From a3b133b0ea0696e42fd876b9a803e28bc6ef5299 Mon Sep 17 00:00:00 2001 From: Yonghee Han Date: Wed, 27 Jul 2016 16:39:12 +0900 Subject: [PATCH] Imported Upstream version 2.3.1 Change-Id: I2161522ea1d7ff10cd1d697609d473243c05e1df --- .gitignore | 10 +- .travis.yml | 3 + LICENSE | 2 +- MAINTAINERS | 112 +- Makefile | 30 +- Makefile.objs | 11 +- Makefile.target | 6 +- VERSION | 2 +- aio-posix.c | 2 +- aio-win32.c | 4 +- arch_init.c | 285 +- async.c | 39 +- audio/audio_template.h | 2 +- backends/hostmem.c | 14 + backends/rng-random.c | 6 +- backends/tpm.c | 2 +- balloon.c | 59 +- block.c | 562 +- block/Makefile.objs | 2 + block/accounting.c | 13 +- block/archipelago.c | 6 +- block/backup.c | 28 + block/blkdebug.c | 105 +- block/block-backend.c | 287 +- block/dmg.c | 502 +- block/iscsi.c | 63 +- block/linux-aio.c | 99 +- block/mirror.c | 42 +- block/nbd-client.c | 129 +- block/nbd-client.h | 35 +- block/nbd.c | 50 +- block/nfs.c | 22 + block/qapi.c | 51 +- block/qcow.c | 2 +- block/qcow2-cache.c | 4 +- block/qcow2-cluster.c | 34 +- block/qcow2-refcount.c | 634 +- block/qcow2-snapshot.c | 2 +- block/qcow2.c | 189 +- block/qcow2.h | 41 +- block/qed.c | 5 + block/qed.h | 1 - block/raw-aio.h | 2 +- block/raw-posix.c | 348 +- block/raw-win32.c | 4 +- block/raw_bsd.c | 78 +- block/rbd.c | 8 +- block/sheepdog.c | 212 +- block/vdi.c | 30 +- block/vhdx.c | 31 +- block/vhdx.h | 3 +- block/vmdk.c | 146 +- block/vpc.c | 165 +- block/vvfat.c | 15 +- block/write-threshold.c | 125 + blockdev-nbd.c | 24 +- blockdev.c | 538 +- bootdevice.c | 113 +- bsd-user/elfload.c | 11 +- bsd-user/main.c | 6 +- configure | 158 +- coroutine-ucontext.c | 69 +- cpu-exec.c | 69 +- cpus.c | 80 +- cputlb.c | 16 +- default-configs/alpha-softmmu.mak | 2 - default-configs/arm-softmmu.mak | 13 + default-configs/i386-softmmu.mak | 6 +- default-configs/lm32-softmmu.mak | 2 +- default-configs/mips-softmmu.mak | 2 - default-configs/mips64-softmmu.mak | 2 - default-configs/mips64el-softmmu.mak | 2 - default-configs/mipsel-softmmu.mak | 2 - default-configs/pci.mak | 6 + default-configs/ppc-softmmu.mak | 4 +- default-configs/ppc64-softmmu.mak | 10 +- default-configs/ppcemb-softmmu.mak | 3 - default-configs/s390x-softmmu.mak | 2 + default-configs/sparc64-softmmu.mak | 2 - default-configs/usb.mak | 1 + default-configs/x86_64-softmmu.mak | 6 +- device-hotplug.c | 9 +- device_tree.c | 19 +- disas/arm-a64.cc | 9 +- disas/arm.c | 128 - disas/cris.c | 13 +- disas/libvixl/README | 2 +- disas/libvixl/a64/assembler-a64.h | 290 +- disas/libvixl/a64/constants-a64.h | 61 +- disas/libvixl/a64/decoder-a64.h | 2 +- disas/libvixl/a64/disasm-a64.cc | 142 +- disas/libvixl/a64/disasm-a64.h | 48 +- disas/libvixl/a64/instructions-a64.cc | 63 + disas/libvixl/a64/instructions-a64.h | 110 +- disas/libvixl/globals.h | 2 +- disas/libvixl/utils.cc | 13 + disas/libvixl/utils.h | 14 +- disas/microblaze.c | 13 +- disas/mips.c | 10 +- disas/s390.c | 35 - disas/sh4.c | 2 +- disas/sparc.c | 32 - docs/memory-hotplug.txt | 76 + docs/memory.txt | 74 +- docs/multiseat.txt | 18 +- docs/rcu.txt | 390 + docs/specs/edu.txt | 110 + docs/specs/fw_cfg.txt | 205 + docs/specs/pci-ids.txt | 2 + docs/xbzrle.txt | 8 + exec.c | 487 +- fpu/softfloat-macros.h | 86 +- fpu/softfloat-specialize.h | 259 +- fpu/softfloat.c | 1787 ++-- fsdev/virtfs-proxy-helper.c | 19 +- gdbstub.c | 23 +- hmp-commands.hx | 200 +- hmp.c | 351 +- hmp.h | 9 +- hw/9pfs/virtio-9p-coth.c | 29 +- hw/9pfs/virtio-9p-coth.h | 4 +- hw/9pfs/virtio-9p-device.c | 2 +- hw/9pfs/virtio-9p-handle.c | 4 +- hw/9pfs/virtio-9p-local.c | 57 +- hw/9pfs/virtio-9p-posix-acl.c | 2 +- hw/9pfs/virtio-9p-proxy.c | 26 +- hw/9pfs/virtio-9p-synth.c | 3 +- hw/9pfs/virtio-9p.c | 3 +- hw/9pfs/virtio-9p.h | 18 +- hw/Makefile.objs | 1 + hw/acpi/Makefile.objs | 2 + hw/acpi/aml-build.c | 894 ++ hw/{i386 => acpi}/bios-linker-loader.c | 6 +- hw/acpi/ich9.c | 114 +- hw/acpi/memory_hotplug.c | 3 +- hw/acpi/pcihp.c | 18 +- hw/acpi/piix4.c | 17 +- hw/alpha/dp264.c | 6 +- hw/alpha/typhoon.c | 7 +- hw/arm/Makefile.objs | 2 + hw/arm/allwinner-a10.c | 2 + hw/arm/armv7m.c | 39 +- hw/arm/boot.c | 120 +- hw/arm/cubieboard.c | 5 +- hw/arm/digic_boards.c | 10 +- hw/arm/exynos4210.c | 13 +- hw/arm/highbank.c | 20 +- hw/arm/integratorcp.c | 130 +- hw/arm/kzm.c | 3 +- hw/arm/musicpal.c | 5 +- hw/arm/netduino2.c | 57 + hw/arm/nseries.c | 6 +- hw/arm/omap1.c | 72 +- hw/arm/omap2.c | 18 +- hw/arm/pxa2xx.c | 10 +- hw/arm/realview.c | 21 +- hw/arm/spitz.c | 3 + hw/arm/stellaris.c | 29 +- hw/arm/stm32f205_soc.c | 160 + hw/arm/strongarm.c | 7 +- hw/arm/versatilepb.c | 39 +- hw/arm/vexpress.c | 172 +- hw/arm/virt.c | 316 +- hw/arm/xilinx_zynq.c | 23 +- hw/audio/ac97.c | 5 +- hw/audio/es1370.c | 5 +- hw/audio/intel-hda.c | 6 +- hw/audio/pcspk.c | 2 +- hw/audio/sb16.c | 4 +- hw/block/block.c | 24 + hw/block/dataplane/virtio-blk.c | 22 +- hw/block/fdc.c | 19 +- hw/block/hd-geometry.c | 10 +- hw/block/m25p80.c | 1 + hw/block/nand.c | 2 +- hw/block/nvme.c | 12 +- hw/block/nvme.h | 2 +- hw/block/onenand.c | 8 +- hw/block/pflash_cfi01.c | 4 +- hw/block/pflash_cfi02.c | 5 +- hw/block/virtio-blk.c | 487 +- hw/block/xen_disk.c | 27 +- hw/bt/sdp.c | 2 +- hw/char/Makefile.objs | 1 + hw/char/cadence_uart.c | 31 +- hw/char/digic-uart.c | 3 + hw/char/etraxfs_ser.c | 3 + hw/char/lm32_juart.c | 3 + hw/char/lm32_uart.c | 3 + hw/char/milkymist-uart.c | 3 + hw/char/omap_uart.c | 3 +- hw/char/parallel.c | 25 + hw/char/pl011.c | 3 + hw/char/serial-isa.c | 23 +- hw/char/serial-pci.c | 22 +- hw/char/serial.c | 95 +- hw/char/spapr_vty.c | 14 +- hw/char/stm32f2xx_usart.c | 232 + hw/char/virtio-serial-bus.c | 88 +- hw/char/xilinx_uartlite.c | 3 + hw/core/Makefile.objs | 2 +- hw/core/fw-path-provider.c | 2 +- hw/core/loader.c | 54 +- hw/core/machine.c | 123 +- hw/core/ptimer.c | 2 +- hw/core/qdev-properties-system.c | 104 +- hw/core/qdev-properties.c | 24 +- hw/core/qdev.c | 95 +- hw/core/sysbus.c | 18 +- hw/cris/axis_dev88.c | 5 +- hw/display/Makefile.objs | 3 +- hw/display/blizzard.c | 1 - hw/display/cirrus_vga.c | 53 +- hw/display/omap_dss.c | 15 +- hw/display/qxl-render.c | 2 + hw/display/qxl.c | 77 +- hw/display/vga-isa.c | 2 +- hw/display/vga-pci.c | 39 +- hw/display/vga.c | 60 +- hw/display/vga_int.h | 3 + hw/display/vmware_vga.c | 6 +- hw/display/xenfb.c | 5 + hw/dma/omap_dma.c | 8 +- hw/dma/pl330.c | 6 +- hw/gpio/omap_gpio.c | 3 +- hw/i2c/smbus_ich9.c | 5 +- hw/i386/Makefile.objs | 5 +- hw/i386/acpi-build.c | 1213 ++- hw/i386/acpi-dsdt-cpu-hotplug.dsl | 16 +- hw/i386/acpi-dsdt-isa.dsl | 11 - hw/i386/acpi-dsdt-mem-hotplug.dsl | 36 +- hw/i386/acpi-dsdt-pci-crs.dsl | 92 - hw/i386/acpi-dsdt.dsl | 46 - hw/i386/acpi-dsdt.hex.generated | 683 +- hw/i386/intel_iommu.c | 3 + hw/i386/kvm/apic.c | 10 +- hw/i386/kvm/clock.c | 3 +- hw/i386/kvm/i8254.c | 2 +- hw/i386/kvm/pci-assign.c | 24 +- hw/i386/multiboot.c | 42 +- hw/i386/pc.c | 189 +- hw/i386/pc_piix.c | 59 +- hw/i386/pc_q35.c | 50 +- hw/i386/pc_sysfw.c | 4 +- hw/i386/q35-acpi-dsdt.dsl | 19 - hw/i386/q35-acpi-dsdt.hex.generated | 627 +- hw/i386/smbios.c | 32 +- hw/i386/ssdt-mem.dsl | 77 - hw/i386/ssdt-mem.hex.generated | 213 - hw/i386/ssdt-misc.dsl | 122 - hw/i386/ssdt-misc.hex.generated | 399 - hw/i386/ssdt-pcihp.dsl | 100 - hw/i386/ssdt-pcihp.hex.generated | 251 - hw/i386/ssdt-proc.dsl | 63 - hw/i386/ssdt-proc.hex.generated | 134 - hw/i386/ssdt-tpm.hex.generated | 6 +- hw/ide/ahci.c | 145 +- hw/ide/ahci.h | 7 +- hw/ide/atapi.c | 132 +- hw/ide/cmd646.c | 8 +- hw/ide/core.c | 159 +- hw/ide/ich.c | 13 +- hw/ide/internal.h | 19 +- hw/ide/isa.c | 7 +- hw/ide/macio.c | 6 - hw/ide/pci.c | 100 +- hw/ide/pci.h | 12 +- hw/ide/piix.c | 20 +- hw/ide/qdev.c | 9 +- hw/ide/via.c | 9 +- hw/input/adb.c | 22 +- hw/input/hid.c | 23 +- hw/input/lm832x.c | 2 +- hw/input/milkymist-softusb.c | 19 +- hw/input/pckbd.c | 10 +- hw/input/ps2.c | 16 + hw/intc/apic_common.c | 19 +- hw/intc/arm_gic.c | 3 +- hw/intc/arm_gic_kvm.c | 34 +- hw/intc/armv7m_nvic.c | 2 +- hw/intc/etraxfs_pic.c | 8 +- hw/intc/i8259.c | 4 +- hw/intc/lm32_pic.c | 4 +- hw/intc/openpic.c | 280 +- hw/intc/openpic_kvm.c | 1 - hw/intc/s390_flic.c | 6 +- hw/intc/xics_kvm.c | 1 - hw/ipack/tpci200.c | 6 +- hw/isa/i82378.c | 9 +- hw/isa/isa-bus.c | 12 +- hw/isa/lpc_ich9.c | 16 +- hw/isa/pc87312.c | 4 + hw/isa/piix4.c | 8 +- hw/isa/vt82c686.c | 72 +- hw/lm32/lm32_boards.c | 10 +- hw/lm32/lm32_hwsetup.h | 3 +- hw/lm32/milkymist-hw.h | 4 +- hw/lm32/milkymist.c | 6 +- hw/m68k/an5206.c | 3 +- hw/m68k/dummy_m68k.c | 10 +- hw/m68k/mcf5208.c | 3 +- hw/mem/pc-dimm.c | 65 + hw/microblaze/boot.c | 2 +- hw/mips/gt64xxx_pci.c | 95 +- hw/mips/mips_fulong2e.c | 15 +- hw/mips/mips_jazz.c | 45 +- hw/mips/mips_malta.c | 12 +- hw/mips/mips_mipssim.c | 5 +- hw/mips/mips_r4k.c | 28 +- hw/misc/Makefile.objs | 7 +- hw/misc/applesmc.c | 5 +- hw/misc/edu.c | 408 + hw/misc/macio/cuda.c | 7 +- hw/misc/macio/macio.c | 27 +- hw/misc/milkymist-pfpu.c | 2 +- hw/misc/omap_gpmc.c | 3 +- hw/misc/omap_l4.c | 3 +- hw/misc/omap_sdrc.c | 3 +- hw/misc/omap_tap.c | 3 +- hw/misc/pci-testdev.c | 6 +- hw/misc/stm32f2xx_syscfg.c | 160 + hw/net/allwinner_emac.c | 8 - hw/net/cadence_gem.c | 9 - hw/net/dp8393x.c | 11 - hw/net/e1000.c | 40 +- hw/net/eepro100.c | 14 +- hw/net/etraxfs_eth.c | 13 - hw/net/fsl_etsec/etsec.c | 11 +- hw/net/lan9118.c | 8 - hw/net/lance.c | 11 +- hw/net/mcf_fec.c | 8 - hw/net/milkymist-minimac2.c | 8 - hw/net/mipsnet.c | 8 - hw/net/ne2000-isa.c | 8 - hw/net/ne2000.c | 14 +- hw/net/opencores_eth.c | 5 - hw/net/pcnet-pci.c | 63 +- hw/net/pcnet.c | 39 +- hw/net/pcnet.h | 3 +- hw/net/rtl8139.c | 468 +- hw/net/smc91c111.c | 8 - hw/net/spapr_llan.c | 14 +- hw/net/stellaris_enet.c | 8 - hw/net/vhost_net.c | 4 +- hw/net/virtio-net.c | 101 +- hw/net/vmxnet3.c | 13 +- hw/net/xen_nic.c | 16 +- hw/net/xgmac.c | 8 - hw/net/xilinx_axienet.c | 9 - hw/net/xilinx_ethlite.c | 9 +- hw/nvram/fw_cfg.c | 225 +- hw/nvram/spapr_nvram.c | 15 +- hw/pci-bridge/Makefile.objs | 5 +- hw/pci-bridge/dec.c | 5 +- hw/pci-bridge/pci_bridge_dev.c | 14 +- hw/pci-host/Makefile.objs | 1 + hw/pci-host/apb.c | 6 +- hw/pci-host/bonito.c | 10 +- hw/pci-host/gpex.c | 154 + hw/pci-host/grackle.c | 5 +- hw/pci-host/piix.c | 15 +- hw/pci-host/ppce500.c | 117 +- hw/pci-host/prep.c | 6 +- hw/pci-host/q35.c | 7 +- hw/pci-host/uninorth.c | 20 +- hw/pci-host/versatile.c | 5 +- hw/pci/Makefile.objs | 2 - hw/pci/pci-hotplug-old.c | 343 - hw/pci/pci-stub.c | 2 +- hw/pci/pci.c | 159 +- hw/pci/pcie.c | 2 +- hw/pci/pcie_aer.c | 14 +- hw/pci/pcie_host.c | 16 +- hw/pci/shpc.c | 13 +- hw/ppc/Makefile.objs | 2 +- hw/ppc/e500.c | 74 +- hw/ppc/e500.h | 5 + hw/ppc/e500plat.c | 5 + hw/ppc/mac_newworld.c | 46 +- hw/ppc/mac_oldworld.c | 10 +- hw/ppc/mpc8544ds.c | 11 + hw/ppc/ppc.c | 165 +- hw/ppc/ppc405_boards.c | 2 +- hw/ppc/prep.c | 165 +- hw/ppc/spapr.c | 235 +- hw/ppc/spapr_events.c | 2 +- hw/ppc/spapr_hcall.c | 2 + hw/ppc/spapr_iommu.c | 33 +- hw/ppc/spapr_pci.c | 321 +- hw/ppc/spapr_pci_vfio.c | 117 +- hw/ppc/spapr_rtas.c | 49 - hw/ppc/spapr_rtc.c | 212 + hw/ppc/spapr_vio.c | 49 +- hw/s390x/Makefile.objs | 1 + hw/s390x/css.c | 65 +- hw/s390x/css.h | 1 + hw/s390x/ipl.c | 220 +- hw/s390x/ipl.h | 25 + hw/s390x/s390-pci-bus.c | 593 ++ hw/s390x/s390-pci-bus.h | 251 + hw/s390x/s390-pci-inst.c | 834 ++ hw/s390x/s390-pci-inst.h | 288 + hw/s390x/s390-virtio-bus.c | 102 +- hw/s390x/s390-virtio-bus.h | 39 +- hw/s390x/s390-virtio-ccw.c | 88 +- hw/s390x/s390-virtio.c | 60 +- hw/s390x/s390-virtio.h | 10 +- hw/s390x/sclp.c | 10 +- hw/s390x/virtio-ccw.c | 213 +- hw/s390x/virtio-ccw.h | 2 +- hw/scsi/esp-pci.c | 28 +- hw/scsi/lsi53c895a.c | 21 +- hw/scsi/megasas.c | 69 +- hw/scsi/scsi-bus.c | 22 +- hw/scsi/scsi-disk.c | 34 + hw/scsi/scsi-generic.c | 6 +- hw/scsi/spapr_vscsi.c | 15 +- hw/scsi/vhost-scsi.c | 47 +- hw/scsi/virtio-scsi-dataplane.c | 16 +- hw/scsi/virtio-scsi.c | 50 +- hw/scsi/vmw_pvscsi.c | 42 +- hw/sd/milkymist-memcard.c | 3 + hw/sd/omap_mmc.c | 3 +- hw/sd/pl181.c | 3 + hw/sd/sdhci.c | 217 +- hw/sd/sdhci.h | 39 +- hw/sd/ssi-sd.c | 1 + hw/sh4/r2d.c | 2 +- hw/sparc/leon3.c | 4 +- hw/sparc/sun4m.c | 26 +- hw/sparc64/sun4u.c | 42 +- hw/ssi/omap_spi.c | 3 +- hw/timer/Makefile.objs | 2 + hw/timer/a9gtimer.c | 6 +- hw/timer/arm_mptimer.c | 2 +- hw/timer/hpet.c | 4 +- hw/timer/i8254.c | 6 + hw/timer/m48t59.c | 359 +- hw/timer/mc146818rtc.c | 50 +- hw/timer/omap_gptimer.c | 2 +- hw/timer/stm32f2xx_timer.c | 328 + hw/tpm/tpm_int.h | 1 + hw/tpm/tpm_passthrough.c | 53 +- hw/tpm/tpm_tis.c | 139 +- hw/tpm/tpm_tis.h | 2 +- hw/unicore32/puv3.c | 6 +- hw/usb/Makefile.objs | 11 +- hw/usb/bus.c | 81 +- hw/usb/desc-msos.c | 2 +- hw/usb/dev-bluetooth.c | 11 +- hw/usb/dev-network.c | 15 +- hw/usb/dev-serial.c | 7 - hw/usb/dev-storage.c | 50 +- hw/usb/hcd-ehci-pci.c | 16 +- hw/usb/hcd-ehci-sysbus.c | 10 + hw/usb/hcd-ehci.c | 33 +- hw/usb/hcd-ehci.h | 1 + hw/usb/hcd-musb.c | 20 +- hw/usb/hcd-ohci.c | 64 +- hw/usb/hcd-uhci.c | 40 +- hw/usb/hcd-xhci.c | 23 +- hw/usb/host-legacy.c | 1 - hw/usb/host-libusb.c | 7 +- hw/usb/host-stub.c | 2 +- hw/usb/redirect.c | 5 +- hw/vfio/Makefile.objs | 4 + hw/vfio/common.c | 961 +++ hw/{misc/vfio.c => vfio/pci.c} | 2021 ++--- hw/virtio/Makefile.objs | 2 +- hw/virtio/dataplane/Makefile.objs | 2 +- hw/virtio/dataplane/vring.c | 63 +- hw/virtio/vhost-backend.c | 2 +- hw/virtio/vhost.c | 15 +- hw/virtio/virtio-balloon.c | 23 +- hw/virtio/virtio-bus.c | 16 +- hw/virtio/virtio-mmio.c | 2 +- hw/virtio/virtio-pci.c | 153 +- hw/virtio/virtio-pci.h | 2 +- hw/virtio/virtio-rng.c | 2 +- hw/virtio/virtio.c | 38 +- hw/watchdog/watchdog.c | 2 +- hw/watchdog/wdt_i6300esb.c | 20 +- hw/watchdog/wdt_ib700.c | 2 +- hw/xen/xen_pt.c | 14 +- hw/xen/xen_pt_config_init.c | 36 +- hw/xtensa/sim.c | 2 +- hw/xtensa/xtfpga.c | 32 +- include/block/accounting.h | 3 + include/block/aio.h | 2 +- include/block/block.h | 48 +- include/block/block_int.h | 47 +- include/block/coroutine.h | 10 - include/block/coroutine_int.h | 1 + include/block/nbd.h | 17 +- include/block/write-threshold.h | 64 + include/elf.h | 2 + include/exec/cpu-all.h | 69 +- include/exec/cpu-common.h | 1 + include/exec/cpu_ldst.h | 202 +- include/exec/cpu_ldst_template.h | 60 +- include/exec/cpu_ldst_useronly_template.h | 81 + include/exec/cputlb.h | 2 +- include/exec/exec-all.h | 11 +- include/exec/gdbstub.h | 6 + include/exec/gen-icount.h | 30 +- include/exec/memory-internal.h | 1 + include/exec/memory.h | 39 + include/exec/ram_addr.h | 22 +- include/fpu/softfloat.h | 451 +- include/glib-compat.h | 4 +- include/hw/acpi/aml-build.h | 191 + {hw/i386 => include/hw/acpi}/bios-linker-loader.h | 0 include/hw/acpi/ich9.h | 8 + include/hw/acpi/pc-hotplug.h | 1 + include/hw/acpi/pcihp.h | 7 +- include/hw/arm/arm.h | 12 +- include/hw/arm/stm32f205_soc.h | 57 + include/hw/block/block.h | 5 +- include/hw/boards.h | 19 +- include/hw/char/serial.h | 2 +- include/hw/char/stm32f2xx_usart.h | 73 + include/hw/elf_ops.h | 82 +- include/hw/hotplug.h | 2 +- include/hw/hw.h | 6 - include/hw/i386/apic.h | 2 +- include/hw/i386/apic_internal.h | 1 + include/hw/i386/pc.h | 22 +- {target-i386 => include/hw/i386}/topology.h | 6 +- include/hw/isa/isa.h | 17 +- include/hw/lm32/lm32_pic.h | 4 +- include/hw/loader.h | 15 +- include/hw/mem/pc-dimm.h | 1 + include/hw/misc/stm32f2xx_syscfg.h | 61 + include/hw/nvram/fw_cfg.h | 6 +- include/hw/pci-host/gpex.h | 56 + include/hw/pci-host/spapr.h | 15 +- include/hw/pci/pci.h | 14 +- include/hw/pci/pci_ids.h | 1 + include/hw/pci/pcie_aer.h | 2 +- include/hw/pci/pcie_host.h | 1 + include/hw/pci/pcie_regs.h | 2 +- include/hw/pci/shpc.h | 1 + include/hw/ppc/spapr.h | 53 +- include/hw/ppc/spapr_vio.h | 6 +- include/hw/qdev-core.h | 11 + include/hw/qdev-properties.h | 12 +- include/hw/s390x/sclp.h | 8 + include/hw/sparc/grlib.h | 12 +- include/hw/sparc/sun4m.h | 4 +- include/hw/timer/m48t59.h | 61 +- include/hw/timer/stm32f2xx_timer.h | 101 + include/hw/usb.h | 14 +- include/hw/vfio/vfio-common.h | 148 + include/hw/{misc => vfio}/vfio.h | 0 include/hw/virtio/dataplane/vring-accessors.h | 75 + include/hw/virtio/dataplane/vring.h | 16 +- include/hw/virtio/vhost-scsi.h | 5 + include/hw/virtio/vhost.h | 2 +- include/hw/virtio/virtio-access.h | 9 + include/hw/virtio/virtio-balloon.h | 35 +- include/hw/virtio/virtio-blk.h | 99 +- include/hw/virtio/virtio-bus.h | 3 - include/hw/virtio/virtio-net.h | 151 +- include/hw/virtio/virtio-rng.h | 4 +- include/hw/virtio/virtio-scsi.h | 137 +- include/hw/virtio/virtio-serial.h | 53 +- include/hw/virtio/virtio.h | 74 +- include/hw/xen/xen.h | 1 - include/hw/xen/xen_common.h | 236 +- include/migration/migration.h | 8 +- include/migration/page_cache.h | 10 +- include/migration/qemu-file.h | 14 +- include/migration/vmstate.h | 50 +- include/monitor/monitor.h | 3 +- include/monitor/qdev.h | 5 +- include/net/net.h | 6 +- include/net/slirp.h | 6 +- include/net/tap.h | 24 +- include/qapi/error.h | 5 + include/qapi/qmp/qerror.h | 22 +- include/qemu-common.h | 10 + include/qemu-io.h | 4 +- include/qemu/atomic.h | 65 +- include/qemu/bitops.h | 2 +- include/qemu/bswap.h | 11 +- include/qemu/log.h | 1 + include/qemu/option.h | 20 +- include/qemu/queue.h | 51 +- include/qemu/rcu.h | 156 + include/qemu/rcu_queue.h | 134 + include/qemu/sockets.h | 7 + include/qemu/thread.h | 7 +- include/qemu/timer.h | 109 +- include/qemu/typedefs.h | 108 +- include/qjson.h | 29 + include/qom/cpu.h | 15 +- include/qom/object.h | 16 +- include/qom/object_interfaces.h | 13 + include/standard-headers/asm-s390/kvm_virtio.h | 64 + include/standard-headers/asm-s390/virtio-ccw.h | 21 + include/standard-headers/linux/if_ether.h | 1 + include/standard-headers/linux/types.h | 2 + include/standard-headers/linux/virtio_9p.h | 44 + include/standard-headers/linux/virtio_balloon.h | 59 + include/standard-headers/linux/virtio_blk.h | 143 + include/standard-headers/linux/virtio_config.h | 64 + include/standard-headers/linux/virtio_console.h | 78 + include/standard-headers/linux/virtio_ids.h | 43 + include/standard-headers/linux/virtio_net.h | 229 + include/standard-headers/linux/virtio_pci.h | 193 + .../linux}/virtio_ring.h | 132 +- include/standard-headers/linux/virtio_rng.h | 8 + include/standard-headers/linux/virtio_scsi.h | 172 + include/standard-headers/linux/virtio_types.h | 46 + include/sysemu/block-backend.h | 30 +- include/sysemu/blockdev.h | 6 +- include/sysemu/device_tree.h | 9 + include/sysemu/kvm.h | 28 +- include/sysemu/numa.h | 25 + include/sysemu/os-win32.h | 1 - include/sysemu/sysemu.h | 54 +- include/sysemu/tpm_backend.h | 2 +- include/ui/console.h | 27 +- include/ui/qemu-pixman.h | 18 + include/ui/qemu-spice.h | 13 +- include/ui/sdl2.h | 34 + include/ui/spice-display.h | 3 +- kvm-all.c | 96 +- libcacard/Makefile | 2 + linux-headers/asm-arm/kvm.h | 2 + linux-headers/asm-arm64/kvm.h | 9 + linux-headers/asm-s390/kvm.h | 37 + linux-headers/asm-x86/hyperv.h | 11 + linux-headers/linux/kvm.h | 20 +- linux-headers/linux/vfio.h | 1 + linux-headers/linux/virtio_config.h | 58 +- linux-headers/linux/virtio_ring.h | 164 +- linux-user/aarch64/target_cpu.h | 2 +- linux-user/alpha/syscall_nr.h | 4 + linux-user/arm/nwfpe/fpopcode.c | 22 - linux-user/arm/target_cpu.h | 15 +- linux-user/elfload.c | 7 +- linux-user/main.c | 106 +- linux-user/mips64/target_signal.h | 2 +- linux-user/signal.c | 65 +- linux-user/syscall.c | 55 +- linux-user/syscall_defs.h | 95 +- linux-user/vm86.c | 57 +- memory.c | 106 +- migration/Makefile.objs | 10 + block-migration.c => migration/block.c | 26 +- migration-exec.c => migration/exec.c | 0 migration-fd.c => migration/fd.c | 24 +- migration.c => migration/migration.c | 169 +- migration/qemu-file-buf.c | 461 ++ migration/qemu-file-internal.h | 53 + qemu-file-stdio.c => migration/qemu-file-stdio.c | 0 qemu-file-unix.c => migration/qemu-file-unix.c | 23 +- qemu-file.c => migration/qemu-file.c | 509 +- migration-rdma.c => migration/rdma.c | 511 +- migration-tcp.c => migration/tcp.c | 0 migration-unix.c => migration/unix.c | 0 vmstate.c => migration/vmstate.c | 217 +- xbzrle.c => migration/xbzrle.c | 0 monitor.c | 280 +- nbd.c | 219 +- net/hub.c | 6 +- net/l2tpv3.c | 9 +- net/net.c | 29 +- net/queue.c | 2 +- net/slirp.c | 8 +- net/socket.c | 4 +- net/tap.c | 23 +- net/vhost-user.c | 20 +- numa.c | 105 +- os-posix.c | 2 + page_cache.c | 43 +- pc-bios/README | 2 +- pc-bios/bios-256k.bin | Bin 262144 -> 262144 bytes pc-bios/bios.bin | Bin 131072 -> 131072 bytes pc-bios/efi-e1000.rom | Bin 194560 -> 197120 bytes pc-bios/efi-eepro100.rom | Bin 196096 -> 197632 bytes pc-bios/efi-ne2k_pci.rom | Bin 194560 -> 195584 bytes pc-bios/efi-pcnet.rom | Bin 194560 -> 195584 bytes pc-bios/efi-rtl8139.rom | Bin 198144 -> 200192 bytes pc-bios/efi-virtio.rom | Bin 192000 -> 194048 bytes pc-bios/keymaps/ru | 2 +- pc-bios/linuxboot.bin | Bin 1024 -> 1024 bytes pc-bios/openbios-ppc | Bin 746588 -> 746588 bytes pc-bios/openbios-sparc32 | Bin 381512 -> 381512 bytes pc-bios/openbios-sparc64 | Bin 1616768 -> 1616768 bytes pc-bios/optionrom/linuxboot.S | 37 +- pc-bios/s390-ccw.img | Bin 17752 -> 13616 bytes pc-bios/s390-ccw/Makefile | 11 +- pc-bios/s390-ccw/bootmap.c | 4 +- pc-bios/s390-ccw/bootmap.h | 2 +- pc-bios/s390-ccw/main.c | 3 +- pc-bios/s390-ccw/s390-ccw.h | 3 + pc-bios/s390-ccw/virtio.c | 7 +- pc-bios/slof.bin | Bin 923896 -> 912192 bytes pc-bios/vgabios-cirrus.bin | Bin 37376 -> 37888 bytes pc-bios/vgabios-qxl.bin | Bin 37376 -> 38400 bytes pc-bios/vgabios-stdvga.bin | Bin 37376 -> 38400 bytes pc-bios/vgabios-vmware.bin | Bin 37376 -> 38400 bytes pc-bios/vgabios.bin | Bin 37376 -> 38400 bytes qapi-schema.json | 157 +- qapi/block-core.json | 160 +- qapi/qmp-dispatch.c | 3 +- qdev-monitor.c | 67 +- qemu-char.c | 107 +- qemu-coroutine-io.c | 2 +- qemu-coroutine.c | 137 +- qemu-doc.texi | 29 +- qemu-img.c | 255 +- qemu-io-cmds.c | 292 +- qemu-io.c | 67 +- qemu-log.c | 4 +- qemu-nbd.c | 82 +- qemu-options.hx | 117 +- qemu-seccomp.c | 4 +- qemu-timer.c | 30 +- qga/commands-posix.c | 479 +- qga/commands-win32.c | 345 +- qga/qapi-schema.json | 155 +- qga/vss-win32/Makefile.objs | 2 +- qjson.c | 129 + qmp-commands.hx | 163 +- qmp.c | 65 +- qobject/qjson.c | 10 +- qom/cpu.c | 6 +- qom/object.c | 81 +- qom/object_interfaces.c | 12 + qtest.c | 11 +- roms/SLOF/VERSION | 2 +- roms/SLOF/board-js2x/slof/Makefile | 1 + roms/SLOF/board-js2x/slof/pci-device_1002_515e.fs | 2 +- roms/SLOF/board-js2x/slof/version.c | 0 roms/SLOF/board-js2x/slof/vga-display.fs | 62 +- roms/SLOF/board-qemu/llfw/startup.S | 2 +- roms/SLOF/board-qemu/slof/Makefile | 1 + roms/SLOF/board-qemu/slof/fdt.fs | 36 +- roms/SLOF/board-qemu/slof/pci-device_1013_00b8.fs | 77 +- roms/SLOF/board-qemu/slof/pci-device_1234_1111.fs | 77 +- roms/SLOF/board-qemu/slof/pci-phb.fs | 9 +- roms/SLOF/board-qemu/slof/rtas.fs | 9 +- roms/SLOF/clients/net-snk/app/biosemu/biosemu.h | 2 + roms/SLOF/clients/net-snk/app/biosemu/debug.h | 2 +- roms/SLOF/clients/net-snk/app/biosemu/device.c | 18 +- roms/SLOF/clients/net-snk/app/biosemu/device.h | 4 +- roms/SLOF/clients/net-snk/app/biosemu/interrupt.c | 23 +- roms/SLOF/clients/net-snk/app/biosemu/interrupt.h | 4 +- roms/SLOF/clients/net-snk/app/biosemu/io.c | 53 +- roms/SLOF/clients/net-snk/app/biosemu/mem.c | 1 + roms/SLOF/clients/net-snk/app/biosemu/vbe.c | 19 +- roms/SLOF/clients/net-snk/app/biosemu/vbe.h | 2 + roms/SLOF/clients/net-snk/app/main.c | 4 +- roms/SLOF/clients/net-snk/app/netapps/netboot.c | 16 +- roms/SLOF/clients/net-snk/app/netapps/ping.c | 6 +- roms/SLOF/clients/net-snk/app/netlib/bootp.c | 4 +- roms/SLOF/clients/net-snk/app/netlib/dhcp.c | 35 +- roms/SLOF/clients/net-snk/app/netlib/dhcp.h | 4 +- roms/SLOF/clients/net-snk/app/netlib/dhcpv6.c | 17 +- roms/SLOF/clients/net-snk/app/netlib/dns.c | 16 +- roms/SLOF/clients/net-snk/app/netlib/dns.h | 2 +- roms/SLOF/clients/net-snk/app/netlib/ethernet.c | 15 +- roms/SLOF/clients/net-snk/app/netlib/ethernet.h | 4 +- roms/SLOF/clients/net-snk/app/netlib/icmpv6.c | 28 +- roms/SLOF/clients/net-snk/app/netlib/icmpv6.h | 6 +- roms/SLOF/clients/net-snk/app/netlib/ipv4.c | 61 +- roms/SLOF/clients/net-snk/app/netlib/ipv4.h | 10 +- roms/SLOF/clients/net-snk/app/netlib/ipv6.c | 31 +- roms/SLOF/clients/net-snk/app/netlib/ipv6.h | 8 +- roms/SLOF/clients/net-snk/app/netlib/tftp.c | 36 +- roms/SLOF/clients/net-snk/app/netlib/tftp.h | 5 +- roms/SLOF/clients/net-snk/app/netlib/udp.c | 12 +- roms/SLOF/clients/net-snk/app/netlib/udp.h | 2 +- roms/SLOF/clients/net-snk/client.lds | 76 +- roms/SLOF/clients/net-snk/include/fileio.h | 36 +- roms/SLOF/clients/net-snk/include/ioctl.h | 19 - roms/SLOF/clients/net-snk/include/kernel.h | 15 +- roms/SLOF/clients/net-snk/include/netdriver_int.h | 191 - roms/SLOF/clients/net-snk/include/of.h | 8 +- roms/SLOF/clients/net-snk/include/pci.h | 9 - roms/SLOF/clients/net-snk/kernel/Makefile | 2 +- roms/SLOF/clients/net-snk/kernel/init.c | 38 +- roms/SLOF/clients/net-snk/kernel/modules.c | 120 - roms/SLOF/clients/net-snk/kernel/modules.h | 17 - roms/SLOF/clients/net-snk/kernel/systemcall.c | 191 +- roms/SLOF/clients/net-snk/kernel/timer.c | 30 - roms/SLOF/clients/net-snk/oflib/Makefile | 2 +- roms/SLOF/clients/net-snk/oflib/ci_device.c | 126 - roms/SLOF/clients/net-snk/oflib/entry.S | 10 +- roms/SLOF/clients/net-snk/oflib/of.c | 277 +- roms/SLOF/clients/net-snk/oflib/pci.c | 31 +- roms/SLOF/clients/net-snk/oflib/rtas.c | 17 +- roms/SLOF/clients/net-snk/sec-client.lds | 7 - roms/SLOF/include/helpers.h | 2 +- roms/SLOF/lib/libhvcall/hvcall.code | 6 + roms/SLOF/lib/libhvcall/hvcall.in | 1 + roms/SLOF/lib/libhvcall/libhvcall.h | 4 +- roms/SLOF/lib/libusb/usb-xhci.c | 39 +- roms/SLOF/lib/libusb/usb-xhci.h | 10 + roms/SLOF/lib/libvirtio/virtio.c | 6 +- roms/SLOF/other-licence/x86emu/x86emu_changes.diff | 30 +- roms/SLOF/slof/fs/graphics.fs | 87 + roms/SLOF/slof/fs/pci-properties.fs | 15 +- roms/SLOF/slof/fs/pci-scan.fs | 13 +- roms/SLOF/slof/fs/root.fs | 2 +- roms/SLOF/slof/fs/term-io.fs | 2 + roms/SLOF/slof/fs/usb/dev-mouse.fs | 1 - roms/SLOF/slof/helpers.c | 2 +- roms/config.seabios-128k | 1 + roms/ipxe/src/Makefile | 6 +- roms/ipxe/src/Makefile.housekeeping | 139 +- roms/ipxe/src/arch/i386/Makefile | 3 +- roms/ipxe/src/arch/i386/Makefile.efi | 4 + roms/ipxe/src/arch/i386/Makefile.pcbios | 18 +- roms/ipxe/src/arch/i386/core/pci_autoboot.c | 6 +- roms/ipxe/src/arch/i386/drivers/net/undinet.c | 9 +- .../src/arch/i386/firmware/pcbios/bios_console.c | 43 + roms/ipxe/src/arch/i386/include/bios.h | 1 + roms/ipxe/src/arch/i386/include/ipxe/msr.h | 38 + .../src/arch/i386/interface/pxeparent/pxeparent.c | 4 +- roms/ipxe/src/arch/i386/prefix/isaromprefix.S | 25 + roms/ipxe/src/arch/i386/prefix/libprefix.S | 35 +- roms/ipxe/src/arch/i386/prefix/lkrnprefix.S | 214 +- roms/ipxe/src/arch/i386/prefix/mromprefix.S | 40 +- roms/ipxe/src/arch/i386/prefix/pciromprefix.S | 25 + roms/ipxe/src/arch/i386/prefix/romprefix.S | 84 +- roms/ipxe/src/arch/i386/transitions/librm_mgmt.c | 44 +- roms/ipxe/src/arch/x86/Makefile | 1 + roms/ipxe/src/arch/x86/Makefile.efi | 4 + roms/ipxe/src/arch/x86/Makefile.linux | 2 +- roms/ipxe/src/arch/x86/drivers/xen/hvm.c | 496 ++ roms/ipxe/src/arch/x86/drivers/xen/hvm.h | 75 + roms/ipxe/src/arch/x86/include/bits/errfile.h | 1 + roms/ipxe/src/arch/x86/include/bits/xen.h | 164 + roms/ipxe/src/arch/x86/include/ipxe/x86_io.h | 5 +- roms/ipxe/src/arch/x86/prefix/efidrvprefix.c | 4 - roms/ipxe/src/arch/x86/prefix/efiprefix.c | 44 + roms/ipxe/src/arch/x86_64/include/ipxe/msr.h | 43 + roms/ipxe/src/config/colour.h | 3 + roms/ipxe/src/config/console.h | 3 + roms/ipxe/src/config/crypto.h | 3 + roms/ipxe/src/config/general.h | 3 + roms/ipxe/src/config/named.h | 26 + roms/ipxe/src/config/serial.h | 3 + roms/ipxe/src/config/settings.h | 3 + roms/ipxe/src/config/sideband.h | 3 + roms/ipxe/src/config/vbox/README | 18 + roms/ipxe/src/config/vbox/colour.h | 0 roms/ipxe/src/config/vbox/console.h | 0 roms/ipxe/src/config/vbox/crypto.h | 0 roms/ipxe/src/config/vbox/general.h | 27 + roms/ipxe/src/config/vbox/serial.h | 0 roms/ipxe/src/config/vbox/settings.h | 0 roms/ipxe/src/config/vbox/sideband.h | 0 roms/ipxe/src/core/debug.c | 17 +- roms/ipxe/src/core/main.c | 4 +- roms/ipxe/src/core/malloc.c | 130 +- roms/ipxe/src/core/pinger.c | 56 +- roms/ipxe/src/core/profile.c | 3 + roms/ipxe/src/core/string.c | 4 +- roms/ipxe/src/core/version.c | 50 +- roms/ipxe/src/crypto/ocsp.c | 11 +- roms/ipxe/src/crypto/x509.c | 57 +- roms/ipxe/src/drivers/block/ibft.c | 30 +- roms/ipxe/src/drivers/block/scsi.c | 34 +- roms/ipxe/src/drivers/block/srp.c | 11 +- roms/ipxe/src/drivers/net/efi/nii.c | 1101 +++ roms/ipxe/src/drivers/net/efi/nii.h | 17 + roms/ipxe/src/drivers/net/efi/snp.c | 113 + roms/ipxe/src/drivers/net/efi/snp.h | 49 - roms/ipxe/src/drivers/net/efi/snpnet.c | 625 +- roms/ipxe/src/drivers/net/efi/snpnet.h | 26 +- roms/ipxe/src/drivers/net/efi/snponly.c | 215 +- roms/ipxe/src/drivers/net/igbvf/igbvf_main.c | 18 +- roms/ipxe/src/drivers/net/intel.c | 51 +- roms/ipxe/src/drivers/net/intel.h | 12 +- roms/ipxe/src/drivers/net/intelx.c | 6 + roms/ipxe/src/drivers/net/myson.c | 5 + roms/ipxe/src/drivers/net/natsemi.c | 5 + roms/ipxe/src/drivers/net/netfront.c | 940 +++ roms/ipxe/src/drivers/net/netfront.h | 153 + roms/ipxe/src/drivers/net/realtek.c | 5 + roms/ipxe/src/drivers/net/skeleton.c | 5 + roms/ipxe/src/drivers/net/smc9000.c | 10 +- roms/ipxe/src/drivers/net/smc9000.h | 2 +- roms/ipxe/src/drivers/net/vmxnet3.c | 44 +- roms/ipxe/src/hci/commands/ping_cmd.c | 11 +- roms/ipxe/src/hci/editstring.c | 36 +- roms/ipxe/src/hci/readline.c | 3 + roms/ipxe/src/image/efi_image.c | 56 +- roms/ipxe/src/include/assert.h | 2 + roms/ipxe/src/include/ipxe/device.h | 6 + roms/ipxe/src/include/ipxe/efi/Base.h | 38 +- .../ipxe/src/include/ipxe/efi/Ia32/ProcessorBind.h | 12 +- .../src/include/ipxe/efi/IndustryStandard/Acpi10.h | 663 ++ .../include/ipxe/efi/IndustryStandard/AcpiAml.h | 177 + .../src/include/ipxe/efi/IndustryStandard/Pci22.h | 39 + .../include/ipxe/efi/IndustryStandard/PeImage.h | 3 +- .../src/include/ipxe/efi/IndustryStandard/Tpm12.h | 2175 +++++ .../ipxe/efi/IndustryStandard/UefiTcgPlatform.h | 172 + roms/ipxe/src/include/ipxe/efi/Library/BaseLib.h | 37 +- roms/ipxe/src/include/ipxe/efi/Pi/PiDxeCis.h | 4 +- .../src/include/ipxe/efi/Pi/PiFirmwareVolume.h | 6 +- roms/ipxe/src/include/ipxe/efi/Pi/PiMultiPhase.h | 31 +- roms/ipxe/src/include/ipxe/efi/Pi/PiStatusCode.h | 9 +- roms/ipxe/src/include/ipxe/efi/Protocol/Arp.h | 387 + .../ipxe/efi/Protocol/BusSpecificDriverOverride.h | 74 + .../src/include/ipxe/efi/Protocol/ComponentName.h | 131 + .../efi/Protocol/ConsoleControl/ConsoleControl.h | 124 + .../src/include/ipxe/efi/Protocol/DebugSupport.h | 99 +- .../src/include/ipxe/efi/Protocol/DevicePath.h | 14 +- roms/ipxe/src/include/ipxe/efi/Protocol/Dhcp4.h | 782 ++ roms/ipxe/src/include/ipxe/efi/Protocol/DiskIo.h | 119 + .../src/include/ipxe/efi/Protocol/FormBrowser2.h | 4 +- .../src/include/ipxe/efi/Protocol/GraphicsOutput.h | 278 + .../include/ipxe/efi/Protocol/HiiConfigAccess.h | 13 +- .../src/include/ipxe/efi/Protocol/HiiDatabase.h | 16 +- roms/ipxe/src/include/ipxe/efi/Protocol/Ip4.h | 614 ++ .../ipxe/src/include/ipxe/efi/Protocol/Ip4Config.h | 184 + .../ipxe/src/include/ipxe/efi/Protocol/LoadFile2.h | 87 + .../src/include/ipxe/efi/Protocol/ManagedNetwork.h | 374 + roms/ipxe/src/include/ipxe/efi/Protocol/Mtftp4.h | 595 ++ .../ipxe/efi/Protocol/NetworkInterfaceIdentifier.h | 14 +- .../src/include/ipxe/efi/Protocol/PxeBaseCode.h | 936 +++ .../include/ipxe/efi/Protocol/SimpleFileSystem.h | 171 +- .../src/include/ipxe/efi/Protocol/TcgService.h | 209 + roms/ipxe/src/include/ipxe/efi/Protocol/Tcp4.h | 579 ++ roms/ipxe/src/include/ipxe/efi/Protocol/Udp4.h | 447 + .../src/include/ipxe/efi/Protocol/VlanConfig.h | 145 + roms/ipxe/src/include/ipxe/efi/Uefi/UefiBaseType.h | 14 + .../ipxe/efi/Uefi/UefiInternalFormRepresentation.h | 9 +- roms/ipxe/src/include/ipxe/efi/Uefi/UefiPxe.h | 12 +- roms/ipxe/src/include/ipxe/efi/Uefi/UefiSpec.h | 144 +- roms/ipxe/src/include/ipxe/efi/X64/ProcessorBind.h | 12 +- roms/ipxe/src/include/ipxe/efi/efi.h | 100 +- roms/ipxe/src/include/ipxe/efi/efi_autoboot.h | 14 + roms/ipxe/src/include/ipxe/efi/efi_download.h | 4 +- roms/ipxe/src/include/ipxe/efi/efi_driver.h | 96 +- roms/ipxe/src/include/ipxe/efi/efi_file.h | 2 +- roms/ipxe/src/include/ipxe/efi/efi_pci.h | 36 +- roms/ipxe/src/include/ipxe/efi/efi_pci_api.h | 3 + roms/ipxe/src/include/ipxe/efi/efi_snp.h | 34 +- roms/ipxe/src/include/ipxe/efi/efi_utils.h | 25 + roms/ipxe/src/include/ipxe/efi/efi_wrap.h | 15 + roms/ipxe/src/include/ipxe/efi/import.pl | 5 +- roms/ipxe/src/include/ipxe/errfile.h | 9 + roms/ipxe/src/include/ipxe/ethernet.h | 3 + roms/ipxe/src/include/ipxe/ibft.h | 8 + roms/ipxe/src/include/ipxe/in.h | 2 +- roms/ipxe/src/include/ipxe/io.h | 6 + roms/ipxe/src/include/ipxe/list.h | 4 + roms/ipxe/src/include/ipxe/netdevice.h | 2 + roms/ipxe/src/include/ipxe/nfs_uri.h | 29 + roms/ipxe/src/include/ipxe/pinger.h | 1 + roms/ipxe/src/include/ipxe/profile.h | 91 +- roms/ipxe/src/include/ipxe/scsi.h | 51 +- roms/ipxe/src/include/ipxe/smbios.h | 17 + roms/ipxe/src/include/ipxe/version.h | 13 +- roms/ipxe/src/include/ipxe/virtio-ring.h | 3 - roms/ipxe/src/include/ipxe/x509.h | 1 + roms/ipxe/src/include/ipxe/xen.h | 75 + roms/ipxe/src/include/ipxe/xenbus.h | 86 + roms/ipxe/src/include/ipxe/xenevent.h | 59 + roms/ipxe/src/include/ipxe/xengrant.h | 232 + roms/ipxe/src/include/ipxe/xenmem.h | 46 + roms/ipxe/src/include/ipxe/xenstore.h | 29 + roms/ipxe/src/include/ipxe/xenver.h | 44 + roms/ipxe/src/include/usr/autoboot.h | 6 +- roms/ipxe/src/include/usr/pingmgmt.h | 3 +- roms/ipxe/src/include/xen/arch-arm.h | 422 + roms/ipxe/src/include/xen/arch-x86/xen-x86_32.h | 173 + roms/ipxe/src/include/xen/arch-x86/xen-x86_64.h | 204 + roms/ipxe/src/include/xen/arch-x86/xen.h | 275 + roms/ipxe/src/include/xen/event_channel.h | 383 + roms/ipxe/src/include/xen/features.h | 111 + roms/ipxe/src/include/xen/grant_table.h | 664 ++ roms/ipxe/src/include/xen/hvm/hvm_op.h | 384 + roms/ipxe/src/include/xen/hvm/params.h | 158 + roms/ipxe/src/include/xen/import.pl | 116 + roms/ipxe/src/include/xen/io/netif.h | 307 + roms/ipxe/src/include/xen/io/ring.h | 314 + roms/ipxe/src/include/xen/io/xenbus.h | 82 + roms/ipxe/src/include/xen/io/xs_wire.h | 140 + roms/ipxe/src/include/xen/memory.h | 540 ++ roms/ipxe/src/include/xen/trace.h | 332 + roms/ipxe/src/include/xen/version.h | 98 + roms/ipxe/src/include/xen/xen-compat.h | 46 + roms/ipxe/src/include/xen/xen.h | 901 ++ roms/ipxe/src/interface/efi/efi_autoboot.c | 67 + roms/ipxe/src/interface/efi/efi_bofm.c | 225 +- roms/ipxe/src/interface/efi/efi_console.c | 66 + roms/ipxe/src/interface/efi/efi_debug.c | 586 +- roms/ipxe/src/interface/efi/efi_download.c | 8 +- roms/ipxe/src/interface/efi/efi_driver.c | 468 +- roms/ipxe/src/interface/efi/efi_file.c | 190 +- roms/ipxe/src/interface/efi/efi_guid.c | 205 + roms/ipxe/src/interface/efi/efi_init.c | 152 +- roms/ipxe/src/interface/efi/efi_pci.c | 479 +- roms/ipxe/src/interface/efi/efi_snp.c | 223 +- roms/ipxe/src/interface/efi/efi_snp_hii.c | 31 +- roms/ipxe/src/interface/efi/efi_utils.c | 218 + roms/ipxe/src/interface/efi/efi_wrap.c | 315 + roms/ipxe/src/interface/smbios/smbios_settings.c | 12 + roms/ipxe/src/interface/xen/xenbus.c | 393 + roms/ipxe/src/interface/xen/xengrant.c | 228 + roms/ipxe/src/interface/xen/xenstore.c | 547 ++ roms/ipxe/src/net/eth_slow.c | 3 +- roms/ipxe/src/net/ethernet.c | 16 + roms/ipxe/src/net/fcp.c | 5 +- roms/ipxe/src/net/ipv6.c | 5 +- roms/ipxe/src/net/ndp.c | 1 - roms/ipxe/src/net/netdevice.c | 57 +- roms/ipxe/src/net/oncrpc/nfs_open.c | 128 +- roms/ipxe/src/net/oncrpc/nfs_uri.c | 148 + roms/ipxe/src/net/tcp.c | 33 +- roms/ipxe/src/net/tcp/iscsi.c | 11 +- roms/ipxe/src/net/tcp/oncrpc.c | 4 +- roms/ipxe/src/net/udp/dhcp.c | 61 +- roms/ipxe/src/net/udp/dhcpv6.c | 1 - roms/ipxe/src/net/udp/syslog.c | 37 +- roms/ipxe/src/tests/ipv6_test.c | 38 + roms/ipxe/src/tests/ocsp_test.c | 411 + roms/ipxe/src/tests/string_test.c | 30 + roms/ipxe/src/tests/x509_test.c | 89 +- roms/ipxe/src/usr/autoboot.c | 72 +- roms/ipxe/src/usr/ifmgmt.c | 4 - roms/ipxe/src/usr/lotest.c | 12 +- roms/ipxe/src/usr/pingmgmt.c | 16 +- roms/ipxe/src/util/.gitignore | 1 + roms/ipxe/src/util/Option/ROM.pm | 5 +- roms/ipxe/src/util/efifatbin.c | 260 + roms/ipxe/src/util/geniso | 180 +- roms/ipxe/src/util/genliso | 74 - roms/openbios/arch/sparc32/boot.c | 5 +- roms/openbios/arch/sparc32/entry.S | 37 + roms/openbios/arch/sparc32/lib.c | 2 +- roms/openbios/arch/sparc32/openbios.c | 7 +- roms/openbios/arch/sparc64/openbios.c | 31 +- roms/openbios/drivers/pci.c | 20 + roms/openbios/forth/device/package.fs | 4 + roms/seabios/.version | 2 +- roms/seabios/Makefile | 22 +- roms/seabios/README | 187 +- roms/seabios/README.CSM | 22 - roms/seabios/TODO | 21 - roms/seabios/docs/Build_overview.md | 80 + roms/seabios/docs/Debugging.md | 106 + roms/seabios/docs/Developer_Documentation.md | 24 + roms/seabios/docs/Developer_links.md | 86 + roms/seabios/docs/Download.md | 25 + roms/seabios/docs/Execution_and_code_flow.md | 178 + roms/seabios/docs/Linking_overview.md | 166 + roms/seabios/docs/Mailinglist.md | 8 + roms/seabios/docs/Memory_Model.md | 253 + roms/seabios/docs/README | 5 + roms/seabios/docs/Releases.md | 377 + roms/seabios/docs/SeaBIOS.md | 15 + roms/seabios/scripts/acpi_extract_preprocess.py | 2 +- roms/seabios/scripts/checkstack.py | 4 +- roms/seabios/scripts/kconfig/Makefile | 4 +- roms/seabios/scripts/kconfig/check.sh | 1 - roms/seabios/scripts/kconfig/conf.c | 2 +- roms/seabios/scripts/kconfig/confdata.c | 5 +- roms/seabios/scripts/kconfig/expr.h | 3 + roms/seabios/scripts/kconfig/gconf.c | 2 +- roms/seabios/scripts/kconfig/lkc.h | 1 + roms/seabios/scripts/kconfig/lxdialog/checklist.c | 4 +- roms/seabios/scripts/kconfig/lxdialog/inputbox.c | 2 +- roms/seabios/scripts/kconfig/lxdialog/menubox.c | 4 +- roms/seabios/scripts/kconfig/lxdialog/util.c | 2 +- roms/seabios/scripts/kconfig/mconf.c | 3 +- roms/seabios/scripts/kconfig/menu.c | 9 +- roms/seabios/scripts/kconfig/nconf.c | 1 - roms/seabios/scripts/kconfig/streamline_config.pl | 9 +- roms/seabios/scripts/kconfig/util.c | 2 - roms/seabios/scripts/kconfig/zconf.gperf | 1 + roms/seabios/scripts/kconfig/zconf.hash.c_shipped | 13 +- roms/seabios/scripts/kconfig/zconf.l | 4 +- roms/seabios/scripts/kconfig/zconf.lex.c_shipped | 4 +- roms/seabios/scripts/kconfig/zconf.tab.c_shipped | 2 +- roms/seabios/scripts/kconfig/zconf.y | 2 +- roms/seabios/scripts/layoutrom.py | 440 +- roms/seabios/scripts/readserial.py | 31 +- roms/seabios/scripts/tarball.sh | 36 + roms/seabios/src/Kconfig | 10 + roms/seabios/src/asm-offsets.c | 1 - roms/seabios/src/block.c | 234 +- roms/seabios/src/block.h | 29 +- roms/seabios/src/boot.c | 7 +- roms/seabios/src/cdrom.c | 107 +- roms/seabios/src/config.h | 7 +- roms/seabios/src/disk.c | 254 +- roms/seabios/src/entryfuncs.S | 80 +- roms/seabios/src/font.c | 2 +- roms/seabios/src/fw/acpi.c | 34 +- roms/seabios/src/fw/csm.c | 6 + roms/seabios/src/fw/dev-piix.h | 29 + roms/seabios/src/fw/dev-q35.h | 3 + roms/seabios/src/fw/pciinit.c | 55 +- roms/seabios/src/fw/shadow.c | 3 +- roms/seabios/src/fw/smm.c | 186 +- roms/seabios/src/fw/smp.c | 113 +- roms/seabios/src/hw/blockcmd.c | 205 +- roms/seabios/src/hw/blockcmd.h | 10 +- roms/seabios/src/hw/floppy.c | 4 +- roms/seabios/src/hw/ps2port.h | 15 +- roms/seabios/src/hw/sdcard.c | 321 + roms/seabios/src/hw/timer.c | 2 +- roms/seabios/src/hw/usb-ehci.c | 239 +- roms/seabios/src/hw/usb-ehci.h | 10 +- roms/seabios/src/hw/usb-hid.c | 6 +- roms/seabios/src/hw/usb-hub.c | 90 +- roms/seabios/src/hw/usb-hub.h | 6 +- roms/seabios/src/hw/usb-msc.c | 10 +- roms/seabios/src/hw/usb-ohci.c | 154 +- roms/seabios/src/hw/usb-ohci.h | 11 +- roms/seabios/src/hw/usb-uas.c | 8 +- roms/seabios/src/hw/usb-uhci.c | 135 +- roms/seabios/src/hw/usb-uhci.h | 10 +- roms/seabios/src/hw/usb-xhci.c | 973 ++- roms/seabios/src/hw/usb-xhci.h | 13 +- roms/seabios/src/hw/usb.c | 176 +- roms/seabios/src/hw/usb.h | 31 +- roms/seabios/src/kbd.c | 8 +- roms/seabios/src/misc.c | 23 +- roms/seabios/src/mouse.c | 21 +- roms/seabios/src/post.c | 1 + roms/seabios/src/romlayout.S | 269 +- roms/seabios/src/serial.c | 3 - roms/seabios/src/stacks.c | 532 +- roms/seabios/src/stacks.h | 24 +- roms/seabios/src/std/LegacyBios.h | 20 + roms/seabios/src/std/acpi.h | 25 + roms/seabios/src/std/bda.h | 7 +- roms/seabios/src/std/disk.h | 8 +- roms/seabios/src/std/vga.h | 63 + roms/seabios/src/string.c | 13 + roms/seabios/src/string.h | 1 + roms/seabios/src/system.c | 52 +- roms/seabios/src/types.h | 8 +- roms/seabios/src/util.h | 12 +- roms/seabios/src/x86.h | 30 +- roms/seabios/vgasrc/Kconfig | 6 + roms/seabios/vgasrc/bochsvga.c | 27 +- roms/seabios/vgasrc/cbvga.c | 10 +- roms/seabios/vgasrc/stdvga.c | 6 +- roms/seabios/vgasrc/stdvga.h | 2 +- roms/seabios/vgasrc/stdvgamodes.c | 13 +- roms/seabios/vgasrc/vbe.c | 8 +- roms/seabios/vgasrc/vgabios.c | 165 +- roms/seabios/vgasrc/vgabios.h | 52 +- roms/seabios/vgasrc/vgaentry.S | 64 +- roms/seabios/vgasrc/vgafb.c | 206 +- roms/seabios/vgasrc/vgainit.c | 68 +- roms/seabios/vgasrc/vgalayout.lds.S | 3 +- rules.mak | 23 +- savevm.c | 138 +- scripts/analyze-migration.py | 592 ++ scripts/checkpatch.pl | 13 +- scripts/coverity-model.c | 228 +- scripts/dump-guest-memory.py | 8 +- scripts/get_maintainer.pl | 45 +- scripts/kvm/kvm_stat | 69 +- scripts/kvm/kvm_stat.texi | 55 + scripts/make_device_config.sh | 18 +- scripts/qapi-types.py | 19 +- scripts/qmp/qmp.py | 95 +- scripts/qmp/qom-tree | 70 + scripts/qtest.py | 71 + scripts/tracetool/backend/stderr.py | 10 +- scripts/tracetool/format/d.py | 26 +- scripts/update-linux-headers.sh | 52 +- scripts/vmstate-static-checker.py | 2 + softmmu_template.h | 4 +- spice-qemu-char.c | 3 +- stubs/Makefile.objs | 1 - stubs/pci-drive-hot-add.c | 10 - stubs/qmp_pc_dimm_device_list.c | 5 + stubs/qtest.c | 2 +- target-alpha/cpu.h | 11 +- target-alpha/translate.c | 30 +- target-arm/arm-semi.c | 11 +- target-arm/cpu-qom.h | 2 + target-arm/cpu.c | 80 +- target-arm/cpu.h | 511 +- target-arm/cpu64.c | 40 + target-arm/crypto_helper.c | 114 +- target-arm/helper-a64.c | 25 +- target-arm/helper.c | 1833 ++++- target-arm/internals.h | 11 +- target-arm/kvm.c | 159 +- target-arm/kvm32.c | 104 +- target-arm/kvm64.c | 189 +- target-arm/kvm_arm.h | 39 + target-arm/machine.c | 26 +- target-arm/op_helper.c | 10 +- target-arm/translate-a64.c | 84 +- target-arm/translate.c | 148 +- target-arm/translate.h | 8 +- target-cris/cpu.h | 11 +- target-cris/helper.c | 11 +- target-cris/opcode-cris.h | 10 - target-cris/translate.c | 40 +- target-cris/translate_v10.c | 6 +- target-i386/arch_dump.c | 16 +- target-i386/cpu-qom.h | 1 + target-i386/cpu.c | 316 +- target-i386/cpu.h | 97 +- target-i386/fpu_helper.c | 22 +- target-i386/helper.c | 15 +- target-i386/kvm.c | 119 +- target-i386/machine.c | 94 +- target-i386/ops_sse.h | 16 +- target-i386/seg_helper.c | 20 +- target-i386/smm_helper.c | 4 +- target-i386/translate.c | 133 +- target-lm32/cpu.h | 11 +- target-lm32/translate.c | 46 +- target-m68k/cpu.h | 11 +- target-m68k/translate.c | 20 +- target-microblaze/cpu.h | 11 +- target-microblaze/helper.c | 8 +- target-microblaze/translate.c | 36 +- target-mips/cpu-qom.h | 4 + target-mips/cpu.c | 1 + target-mips/cpu.h | 153 +- target-mips/dsp_helper.c | 2 +- target-mips/gdbstub.c | 56 +- target-mips/helper.c | 23 +- target-mips/helper.h | 1 + target-mips/kvm.c | 31 +- target-mips/machine.c | 569 +- target-mips/msa_helper.c | 155 +- target-mips/op_helper.c | 447 +- target-mips/translate.c | 337 +- target-mips/translate_init.c | 140 +- target-moxie/cpu.h | 11 +- target-moxie/machine.c | 1 + target-moxie/mmu.h | 10 +- target-moxie/translate.c | 14 +- target-openrisc/cpu.h | 9 +- target-openrisc/translate.c | 53 +- target-ppc/cpu-models.c | 10 +- target-ppc/cpu-models.h | 3 +- target-ppc/cpu.h | 52 +- target-ppc/fpu_helper.c | 85 +- target-ppc/helper.h | 4 +- target-ppc/kvm.c | 85 +- target-ppc/machine.c | 8 +- target-ppc/mem_helper.c | 22 + target-ppc/misc_helper.c | 7 +- target-ppc/mmu-hash32.c | 18 +- target-ppc/mmu-hash64.c | 60 +- target-ppc/mmu-hash64.h | 3 + target-ppc/mmu_helper.c | 61 +- target-ppc/translate.c | 422 +- target-ppc/translate_init.c | 286 +- target-s390x/Makefile.objs | 2 +- target-s390x/cc_helper.c | 18 +- target-s390x/cpu.c | 2 + target-s390x/cpu.h | 193 +- target-s390x/helper.c | 391 +- target-s390x/helper.h | 3 + target-s390x/insn-data.def | 16 +- target-s390x/ioinst.c | 224 +- target-s390x/ioinst.h | 5 +- target-s390x/kvm.c | 806 +- target-s390x/machine.c | 5 +- target-s390x/mem_helper.c | 44 +- target-s390x/misc_helper.c | 37 +- target-s390x/mmu_helper.c | 472 ++ target-s390x/translate.c | 112 +- target-sh4/cpu.h | 10 +- target-sh4/translate.c | 34 +- target-sparc/cpu.c | 3 +- target-sparc/cpu.h | 11 +- target-sparc/ldst_helper.c | 34 +- target-sparc/mmu_helper.c | 7 +- target-sparc/translate.c | 49 +- target-tricore/cpu.c | 11 +- target-tricore/cpu.h | 18 +- target-tricore/csfr.def | 124 + target-tricore/helper.h | 105 + target-tricore/op_helper.c | 2234 ++++- target-tricore/translate.c | 8683 ++++++++++++++++---- target-tricore/tricore-opcodes.h | 108 +- target-unicore32/cpu.h | 6 +- target-unicore32/helper.c | 10 +- target-unicore32/translate.c | 18 +- target-xtensa/cpu-qom.h | 3 + target-xtensa/cpu.c | 1 + target-xtensa/cpu.h | 23 +- target-xtensa/helper.h | 2 +- target-xtensa/op_helper.c | 43 +- target-xtensa/translate.c | 716 +- tcg/aarch64/tcg-target.c | 15 +- tcg/arm/tcg-target.c | 14 +- tcg/i386/tcg-target.c | 72 +- tcg/ia64/tcg-target.c | 35 +- tcg/mips/tcg-target.c | 24 +- tcg/optimize.c | 310 +- tcg/ppc/tcg-target.c | 20 +- tcg/s390/tcg-target.c | 31 +- tcg/sparc/tcg-target.c | 22 +- tcg/tcg-be-ldst.h | 26 +- tcg/tcg-op.c | 1947 +++++ tcg/tcg-op.h | 2484 +----- tcg/tcg-opc.h | 9 - tcg/tcg.c | 585 +- tcg/tcg.h | 103 +- tcg/tci/tcg-target.c | 13 +- tci.c | 13 - tests/.gitignore | 3 + tests/Makefile | 42 +- tests/acpi-test-data/pc/DSDT | Bin 3592 -> 2970 bytes tests/acpi-test-data/pc/SSDT | Bin 2279 -> 2475 bytes tests/acpi-test-data/pc/SSDT.bridge | Bin 0 -> 4334 bytes tests/acpi-test-data/q35/DSDT | Bin 8182 -> 7608 bytes tests/acpi-test-data/q35/SSDT | Bin 560 -> 680 bytes tests/acpi-test-data/q35/SSDT.bridge | Bin 0 -> 697 bytes tests/ahci-test.c | 1271 +-- tests/bios-tables-test.c | 57 +- tests/drive_del-test.c | 2 +- tests/fdc-test.c | 2 +- tests/fw_cfg-test.c | 26 +- tests/hd-geo-test.c | 4 +- tests/i440fx-test.c | 17 +- tests/ide-test.c | 29 +- tests/libqos/ahci.c | 880 ++ tests/libqos/ahci.h | 554 ++ tests/libqos/libqos-pc.c | 24 + tests/libqos/libqos-pc.h | 9 + tests/libqos/libqos.c | 63 + tests/libqos/libqos.h | 33 + tests/libqos/malloc-generic.c | 39 + tests/libqos/malloc-generic.h | 21 + tests/libqos/malloc-pc.c | 296 +- tests/libqos/malloc-pc.h | 11 +- tests/libqos/malloc.c | 331 + tests/libqos/malloc.h | 31 +- tests/libqos/virtio-mmio.c | 198 + tests/libqos/virtio-mmio.h | 46 + tests/libqos/virtio-pci.c | 66 +- tests/libqos/virtio-pci.h | 24 +- tests/libqos/virtio.c | 8 +- tests/libqos/virtio.h | 16 +- tests/libqtest.c | 7 + tests/libqtest.h | 32 + tests/multiboot/Makefile | 5 +- tests/multiboot/libc.c | 12 + tests/multiboot/libc.h | 1 + tests/multiboot/mmap.out | 37 +- tests/multiboot/module.txt | 1 + tests/multiboot/modules.c | 55 + tests/multiboot/modules.out | 38 + tests/multiboot/run_test.sh | 9 +- tests/nvme-test.c | 2 +- tests/pc-cpu-test.c | 147 + tests/qemu-iotests-quick.sh | 2 +- tests/qemu-iotests/.gitignore | 1 + tests/qemu-iotests/001.out | 2 +- tests/qemu-iotests/002.out | 2 +- tests/qemu-iotests/003.out | 2 +- tests/qemu-iotests/004 | 2 +- tests/qemu-iotests/004.out | 2 +- tests/qemu-iotests/005.out | 2 +- tests/qemu-iotests/006.out | 6 - tests/qemu-iotests/007 | 3 + tests/qemu-iotests/007.out | 2 +- tests/qemu-iotests/008.out | 2 +- tests/qemu-iotests/009.out | 2 +- tests/qemu-iotests/010.out | 2 +- tests/qemu-iotests/011.out | 2 +- tests/qemu-iotests/012.out | 2 +- tests/qemu-iotests/013.out | 2 +- tests/qemu-iotests/014.out | 2 +- tests/qemu-iotests/015 | 2 + tests/qemu-iotests/015.out | 2 +- tests/qemu-iotests/016.out | 23 - tests/qemu-iotests/017.out | 2 +- tests/qemu-iotests/018.out | 2 +- tests/qemu-iotests/019.out | 4 +- tests/qemu-iotests/020.out | 4 +- tests/qemu-iotests/021.out | 2 +- tests/qemu-iotests/022.out | 2 +- tests/qemu-iotests/023.out | 16 +- tests/qemu-iotests/024.out | 6 +- tests/qemu-iotests/025.out | 2 +- tests/qemu-iotests/026 | 7 + tests/qemu-iotests/026.out | 428 +- tests/qemu-iotests/027.out | 2 +- tests/qemu-iotests/028.out | 6 +- tests/qemu-iotests/029 | 2 + tests/qemu-iotests/029.out | 8 +- tests/qemu-iotests/030 | 22 +- tests/qemu-iotests/031.out | 4 +- tests/qemu-iotests/032.out | 2 +- tests/qemu-iotests/033 | 66 +- tests/qemu-iotests/033.out | 58 +- tests/qemu-iotests/034.out | 4 +- tests/qemu-iotests/035.out | 2 +- tests/qemu-iotests/036.out | 6 +- tests/qemu-iotests/037.out | 4 +- tests/qemu-iotests/038.out | 4 +- tests/qemu-iotests/039 | 18 +- tests/qemu-iotests/039.out | 18 +- tests/qemu-iotests/040 | 32 +- tests/qemu-iotests/041 | 66 +- tests/qemu-iotests/042.out | 2 +- tests/qemu-iotests/043.out | 52 +- tests/qemu-iotests/046.out | 4 +- tests/qemu-iotests/047.out | 2 +- tests/qemu-iotests/048 | 2 +- tests/qemu-iotests/048.out | 4 +- tests/qemu-iotests/049.out | 118 +- tests/qemu-iotests/050.out | 6 +- tests/qemu-iotests/051 | 10 + tests/qemu-iotests/051.out | 89 +- tests/qemu-iotests/052.out | 2 +- tests/qemu-iotests/053.out | 2 +- tests/qemu-iotests/054.out | 4 +- tests/qemu-iotests/055 | 232 +- tests/qemu-iotests/055.out | 4 +- tests/qemu-iotests/058 | 14 +- tests/qemu-iotests/059 | 10 +- tests/qemu-iotests/059.out | 4 + tests/qemu-iotests/060 | 17 +- tests/qemu-iotests/060.out | 41 +- tests/qemu-iotests/061.out | 45 +- tests/qemu-iotests/062.out | 2 +- tests/qemu-iotests/064 | 19 +- tests/qemu-iotests/064.out | 34 +- tests/qemu-iotests/065 | 23 +- tests/qemu-iotests/066.out | 2 +- tests/qemu-iotests/067 | 7 +- tests/qemu-iotests/067.out | 816 +- tests/qemu-iotests/068.out | 2 +- tests/qemu-iotests/069.out | 4 +- tests/qemu-iotests/071 | 12 +- tests/qemu-iotests/071.out | 22 +- tests/qemu-iotests/072.out | 2 +- tests/qemu-iotests/073.out | 4 +- tests/qemu-iotests/077 | 2 +- tests/qemu-iotests/077.out | 2 +- tests/qemu-iotests/079 | 10 +- tests/qemu-iotests/079.out | 38 +- tests/qemu-iotests/080 | 4 + tests/qemu-iotests/080.out | 26 +- tests/qemu-iotests/081 | 10 +- tests/qemu-iotests/081.out | 8 +- tests/qemu-iotests/082 | 14 +- tests/qemu-iotests/082.out | 97 +- tests/qemu-iotests/083 | 3 +- tests/qemu-iotests/083.out | 81 +- tests/qemu-iotests/084.out | 4 +- tests/qemu-iotests/085.out | 38 +- tests/qemu-iotests/086.out | 2 +- tests/qemu-iotests/087 | 3 +- tests/qemu-iotests/087.out | 33 +- tests/qemu-iotests/088.out | 2 +- tests/qemu-iotests/089 | 8 +- tests/qemu-iotests/089.out | 8 +- tests/qemu-iotests/090.out | 2 +- tests/qemu-iotests/091.out | 2 +- tests/qemu-iotests/092.out | 8 +- tests/qemu-iotests/093 | 114 + tests/qemu-iotests/093.out | 5 + tests/qemu-iotests/094 | 81 + tests/qemu-iotests/094.out | 11 + tests/qemu-iotests/095.out | 6 +- tests/qemu-iotests/097.out | 24 +- tests/qemu-iotests/098.out | 16 +- tests/qemu-iotests/099 | 20 +- tests/qemu-iotests/099.out | 16 +- tests/qemu-iotests/100 | 12 + tests/qemu-iotests/100.out | 14 +- tests/qemu-iotests/103 | 10 + tests/qemu-iotests/103.out | 7 +- tests/qemu-iotests/104 | 11 +- tests/qemu-iotests/104.out | 4 +- tests/qemu-iotests/107.out | 2 +- tests/qemu-iotests/108 | 2 + tests/qemu-iotests/108.out | 12 +- tests/qemu-iotests/109 | 132 + tests/qemu-iotests/109.out | 231 + tests/qemu-iotests/110 | 94 + tests/qemu-iotests/110.out | 19 + tests/qemu-iotests/112 | 187 + tests/qemu-iotests/112.out | 84 + tests/qemu-iotests/113 | 76 + tests/qemu-iotests/113.out | 15 + tests/qemu-iotests/114 | 61 + tests/qemu-iotests/114.out | 13 + tests/qemu-iotests/115 | 95 + tests/qemu-iotests/115.out | 8 + tests/qemu-iotests/116 | 96 + tests/qemu-iotests/116.out | 37 + tests/qemu-iotests/121 | 102 + tests/qemu-iotests/121.out | 23 + tests/qemu-iotests/{016 => 123} | 40 +- tests/qemu-iotests/123.out | 9 + tests/qemu-iotests/128 | 82 + tests/qemu-iotests/128.out | 5 + tests/qemu-iotests/130 | 95 + tests/qemu-iotests/130.out | 43 + tests/qemu-iotests/132 | 59 + tests/qemu-iotests/132.out | 5 + tests/qemu-iotests/{006 => 135} | 14 +- tests/qemu-iotests/135.out | 5 + tests/qemu-iotests/check | 1 + tests/qemu-iotests/common | 10 +- tests/qemu-iotests/common.config | 4 +- tests/qemu-iotests/common.filter | 13 +- tests/qemu-iotests/common.qemu | 15 +- tests/qemu-iotests/common.rc | 19 +- tests/qemu-iotests/group | 21 +- tests/qemu-iotests/iotests.py | 89 +- tests/qemu-iotests/qcow2.py | 4 + tests/qemu-iotests/sample_images/afl5.img.bz2 | Bin 0 -> 175 bytes tests/qemu-iotests/sample_images/grub_mbr.raw.bz2 | Bin 0 -> 552 bytes tests/rcutorture.c | 463 ++ tests/rtl8139-test.c | 181 + tests/tcg/xtensa/test_mmu.S | 26 +- tests/test-coroutine.c | 28 +- tests/test-qemu-opts.c | 38 +- tests/test-rcu-list.c | 312 + tests/test-vmstate.c | 26 +- tests/test-write-threshold.c | 119 + tests/test-x86-cpuid.c | 2 +- tests/usb-hcd-ohci-test.c | 2 +- tests/usb-hcd-uhci-test.c | 4 +- tests/usb-hcd-xhci-test.c | 4 +- tests/virtio-blk-test.c | 331 +- tests/virtio-scsi-test.c | 4 +- tpm.c | 8 +- trace-events | 214 +- trace/control.c | 2 +- translate-all.c | 32 +- ui/Makefile.objs | 7 +- ui/console.c | 41 +- ui/d3des.c | 9 - ui/d3des.h | 6 - ui/gtk.c | 25 +- ui/input-keymap.c | 4 + ui/input-legacy.c | 6 - ui/input.c | 2 +- ui/keymaps.c | 196 +- ui/qemu-pixman.c | 29 +- ui/sdl.c | 56 +- ui/sdl2-2d.c | 135 + ui/sdl2-input.c | 106 + ui/sdl2.c | 352 +- ui/spice-core.c | 19 +- ui/spice-display.c | 67 +- ui/vnc-auth-sasl.c | 2 +- ui/vnc-auth-vencrypt.c | 1 - ui/vnc-enc-tight.c | 2 +- ui/vnc-jobs.c | 13 - ui/vnc-jobs.h | 1 - ui/vnc-tls.c | 72 +- ui/vnc-tls.h | 7 - ui/vnc-ws.c | 164 +- ui/vnc-ws.h | 11 +- ui/vnc.c | 1037 ++- ui/vnc.h | 22 +- ui/vnc_keysym.h | 1 + ui/x_keymap.c | 4 +- user-exec.c | 16 +- util/Makefile.objs | 1 + util/aes.c | 2 +- util/cutils.c | 19 +- util/envlist.c | 32 +- util/error.c | 14 +- util/hbitmap.c | 4 +- util/iov.c | 4 +- util/oslib-posix.c | 4 +- util/qemu-config.c | 99 +- util/qemu-option.c | 118 +- util/qemu-sockets.c | 68 +- util/qemu-thread-posix.c | 45 +- util/qemu-thread-win32.c | 48 +- util/rcu.c | 328 + util/uri.c | 179 +- vl.c | 772 +- xen-hvm-stub.c | 4 - xen-hvm.c | 184 +- xen-mapcache.c | 94 +- 1590 files changed, 94547 insertions(+), 35262 deletions(-) create mode 100644 block/write-threshold.c create mode 100644 docs/memory-hotplug.txt create mode 100644 docs/rcu.txt create mode 100644 docs/specs/edu.txt create mode 100644 docs/specs/fw_cfg.txt create mode 100644 hw/acpi/aml-build.c rename hw/{i386 => acpi}/bios-linker-loader.c (96%) create mode 100644 hw/arm/netduino2.c create mode 100644 hw/arm/stm32f205_soc.c create mode 100644 hw/char/stm32f2xx_usart.c delete mode 100644 hw/i386/acpi-dsdt-pci-crs.dsl delete mode 100644 hw/i386/ssdt-mem.dsl delete mode 100644 hw/i386/ssdt-mem.hex.generated delete mode 100644 hw/i386/ssdt-misc.dsl delete mode 100644 hw/i386/ssdt-misc.hex.generated delete mode 100644 hw/i386/ssdt-pcihp.dsl delete mode 100644 hw/i386/ssdt-pcihp.hex.generated delete mode 100644 hw/i386/ssdt-proc.dsl delete mode 100644 hw/i386/ssdt-proc.hex.generated create mode 100644 hw/misc/edu.c create mode 100644 hw/misc/stm32f2xx_syscfg.c create mode 100644 hw/pci-host/gpex.c delete mode 100644 hw/pci/pci-hotplug-old.c create mode 100644 hw/ppc/spapr_rtc.c create mode 100644 hw/s390x/ipl.h create mode 100644 hw/s390x/s390-pci-bus.c create mode 100644 hw/s390x/s390-pci-bus.h create mode 100644 hw/s390x/s390-pci-inst.c create mode 100644 hw/s390x/s390-pci-inst.h create mode 100644 hw/timer/stm32f2xx_timer.c create mode 100644 hw/vfio/Makefile.objs create mode 100644 hw/vfio/common.c rename hw/{misc/vfio.c => vfio/pci.c} (64%) create mode 100644 include/block/write-threshold.h create mode 100644 include/exec/cpu_ldst_useronly_template.h create mode 100644 include/hw/acpi/aml-build.h rename {hw/i386 => include/hw/acpi}/bios-linker-loader.h (100%) create mode 100644 include/hw/arm/stm32f205_soc.h create mode 100644 include/hw/char/stm32f2xx_usart.h rename {target-i386 => include/hw/i386}/topology.h (97%) create mode 100644 include/hw/misc/stm32f2xx_syscfg.h create mode 100644 include/hw/pci-host/gpex.h create mode 100644 include/hw/timer/stm32f2xx_timer.h create mode 100644 include/hw/vfio/vfio-common.h rename include/hw/{misc => vfio}/vfio.h (100%) create mode 100644 include/hw/virtio/dataplane/vring-accessors.h create mode 100644 include/qemu/rcu.h create mode 100644 include/qemu/rcu_queue.h create mode 100644 include/qjson.h create mode 100644 include/standard-headers/asm-s390/kvm_virtio.h create mode 100644 include/standard-headers/asm-s390/virtio-ccw.h create mode 100644 include/standard-headers/linux/if_ether.h create mode 100644 include/standard-headers/linux/types.h create mode 100644 include/standard-headers/linux/virtio_9p.h create mode 100644 include/standard-headers/linux/virtio_balloon.h create mode 100644 include/standard-headers/linux/virtio_blk.h create mode 100644 include/standard-headers/linux/virtio_config.h create mode 100644 include/standard-headers/linux/virtio_console.h create mode 100644 include/standard-headers/linux/virtio_ids.h create mode 100644 include/standard-headers/linux/virtio_net.h create mode 100644 include/standard-headers/linux/virtio_pci.h rename include/{hw/virtio => standard-headers/linux}/virtio_ring.h (61%) create mode 100644 include/standard-headers/linux/virtio_rng.h create mode 100644 include/standard-headers/linux/virtio_scsi.h create mode 100644 include/standard-headers/linux/virtio_types.h create mode 100644 include/sysemu/numa.h create mode 100644 include/ui/sdl2.h create mode 100644 migration/Makefile.objs rename block-migration.c => migration/block.c (97%) rename migration-exec.c => migration/exec.c (100%) rename migration-fd.c => migration/fd.c (77%) rename migration.c => migration/migration.c (82%) create mode 100644 migration/qemu-file-buf.c create mode 100644 migration/qemu-file-internal.h rename qemu-file-stdio.c => migration/qemu-file-stdio.c (100%) rename qemu-file-unix.c => migration/qemu-file-unix.c (92%) rename qemu-file.c => migration/qemu-file.c (51%) rename migration-rdma.c => migration/rdma.c (86%) rename migration-tcp.c => migration/tcp.c (100%) rename migration-unix.c => migration/unix.c (100%) rename vmstate.c => migration/vmstate.c (72%) rename xbzrle.c => migration/xbzrle.c (100%) create mode 100644 qjson.c create mode 100644 roms/SLOF/board-js2x/slof/version.c delete mode 100644 roms/SLOF/clients/net-snk/include/ioctl.h delete mode 100644 roms/SLOF/clients/net-snk/include/netdriver_int.h delete mode 100644 roms/SLOF/clients/net-snk/kernel/modules.c delete mode 100644 roms/SLOF/clients/net-snk/kernel/modules.h delete mode 100644 roms/SLOF/clients/net-snk/oflib/ci_device.c create mode 100644 roms/SLOF/slof/fs/graphics.fs create mode 100644 roms/ipxe/src/arch/i386/include/ipxe/msr.h create mode 100644 roms/ipxe/src/arch/i386/prefix/isaromprefix.S create mode 100644 roms/ipxe/src/arch/i386/prefix/pciromprefix.S create mode 100644 roms/ipxe/src/arch/x86/drivers/xen/hvm.c create mode 100644 roms/ipxe/src/arch/x86/drivers/xen/hvm.h create mode 100644 roms/ipxe/src/arch/x86/include/bits/xen.h create mode 100644 roms/ipxe/src/arch/x86_64/include/ipxe/msr.h create mode 100644 roms/ipxe/src/config/named.h create mode 100644 roms/ipxe/src/config/vbox/README create mode 100644 roms/ipxe/src/config/vbox/colour.h create mode 100644 roms/ipxe/src/config/vbox/console.h create mode 100644 roms/ipxe/src/config/vbox/crypto.h create mode 100644 roms/ipxe/src/config/vbox/general.h create mode 100644 roms/ipxe/src/config/vbox/serial.h create mode 100644 roms/ipxe/src/config/vbox/settings.h create mode 100644 roms/ipxe/src/config/vbox/sideband.h create mode 100644 roms/ipxe/src/drivers/net/efi/nii.c create mode 100644 roms/ipxe/src/drivers/net/efi/nii.h create mode 100644 roms/ipxe/src/drivers/net/efi/snp.c delete mode 100644 roms/ipxe/src/drivers/net/efi/snp.h create mode 100644 roms/ipxe/src/drivers/net/netfront.c create mode 100644 roms/ipxe/src/drivers/net/netfront.h create mode 100644 roms/ipxe/src/include/ipxe/efi/IndustryStandard/Acpi10.h create mode 100644 roms/ipxe/src/include/ipxe/efi/IndustryStandard/AcpiAml.h create mode 100644 roms/ipxe/src/include/ipxe/efi/IndustryStandard/Tpm12.h create mode 100644 roms/ipxe/src/include/ipxe/efi/IndustryStandard/UefiTcgPlatform.h create mode 100644 roms/ipxe/src/include/ipxe/efi/Protocol/Arp.h create mode 100644 roms/ipxe/src/include/ipxe/efi/Protocol/BusSpecificDriverOverride.h create mode 100644 roms/ipxe/src/include/ipxe/efi/Protocol/ComponentName.h create mode 100644 roms/ipxe/src/include/ipxe/efi/Protocol/ConsoleControl/ConsoleControl.h create mode 100644 roms/ipxe/src/include/ipxe/efi/Protocol/Dhcp4.h create mode 100644 roms/ipxe/src/include/ipxe/efi/Protocol/DiskIo.h create mode 100644 roms/ipxe/src/include/ipxe/efi/Protocol/GraphicsOutput.h create mode 100644 roms/ipxe/src/include/ipxe/efi/Protocol/Ip4.h create mode 100644 roms/ipxe/src/include/ipxe/efi/Protocol/Ip4Config.h create mode 100644 roms/ipxe/src/include/ipxe/efi/Protocol/LoadFile2.h create mode 100644 roms/ipxe/src/include/ipxe/efi/Protocol/ManagedNetwork.h create mode 100644 roms/ipxe/src/include/ipxe/efi/Protocol/Mtftp4.h create mode 100644 roms/ipxe/src/include/ipxe/efi/Protocol/PxeBaseCode.h create mode 100644 roms/ipxe/src/include/ipxe/efi/Protocol/TcgService.h create mode 100644 roms/ipxe/src/include/ipxe/efi/Protocol/Tcp4.h create mode 100644 roms/ipxe/src/include/ipxe/efi/Protocol/Udp4.h create mode 100644 roms/ipxe/src/include/ipxe/efi/Protocol/VlanConfig.h create mode 100644 roms/ipxe/src/include/ipxe/efi/efi_autoboot.h create mode 100644 roms/ipxe/src/include/ipxe/efi/efi_utils.h create mode 100644 roms/ipxe/src/include/ipxe/efi/efi_wrap.h create mode 100644 roms/ipxe/src/include/ipxe/nfs_uri.h create mode 100644 roms/ipxe/src/include/ipxe/xen.h create mode 100644 roms/ipxe/src/include/ipxe/xenbus.h create mode 100644 roms/ipxe/src/include/ipxe/xenevent.h create mode 100644 roms/ipxe/src/include/ipxe/xengrant.h create mode 100644 roms/ipxe/src/include/ipxe/xenmem.h create mode 100644 roms/ipxe/src/include/ipxe/xenstore.h create mode 100644 roms/ipxe/src/include/ipxe/xenver.h create mode 100644 roms/ipxe/src/include/xen/arch-arm.h create mode 100644 roms/ipxe/src/include/xen/arch-x86/xen-x86_32.h create mode 100644 roms/ipxe/src/include/xen/arch-x86/xen-x86_64.h create mode 100644 roms/ipxe/src/include/xen/arch-x86/xen.h create mode 100644 roms/ipxe/src/include/xen/event_channel.h create mode 100644 roms/ipxe/src/include/xen/features.h create mode 100644 roms/ipxe/src/include/xen/grant_table.h create mode 100644 roms/ipxe/src/include/xen/hvm/hvm_op.h create mode 100644 roms/ipxe/src/include/xen/hvm/params.h create mode 100755 roms/ipxe/src/include/xen/import.pl create mode 100644 roms/ipxe/src/include/xen/io/netif.h create mode 100644 roms/ipxe/src/include/xen/io/ring.h create mode 100644 roms/ipxe/src/include/xen/io/xenbus.h create mode 100644 roms/ipxe/src/include/xen/io/xs_wire.h create mode 100644 roms/ipxe/src/include/xen/memory.h create mode 100644 roms/ipxe/src/include/xen/trace.h create mode 100644 roms/ipxe/src/include/xen/version.h create mode 100644 roms/ipxe/src/include/xen/xen-compat.h create mode 100644 roms/ipxe/src/include/xen/xen.h create mode 100644 roms/ipxe/src/interface/efi/efi_autoboot.c create mode 100644 roms/ipxe/src/interface/efi/efi_guid.c create mode 100644 roms/ipxe/src/interface/efi/efi_utils.c create mode 100644 roms/ipxe/src/interface/efi/efi_wrap.c create mode 100644 roms/ipxe/src/interface/xen/xenbus.c create mode 100644 roms/ipxe/src/interface/xen/xengrant.c create mode 100644 roms/ipxe/src/interface/xen/xenstore.c create mode 100644 roms/ipxe/src/net/oncrpc/nfs_uri.c create mode 100644 roms/ipxe/src/util/efifatbin.c delete mode 100755 roms/ipxe/src/util/genliso delete mode 100644 roms/seabios/README.CSM delete mode 100644 roms/seabios/TODO create mode 100644 roms/seabios/docs/Build_overview.md create mode 100644 roms/seabios/docs/Debugging.md create mode 100644 roms/seabios/docs/Developer_Documentation.md create mode 100644 roms/seabios/docs/Developer_links.md create mode 100644 roms/seabios/docs/Download.md create mode 100644 roms/seabios/docs/Execution_and_code_flow.md create mode 100644 roms/seabios/docs/Linking_overview.md create mode 100644 roms/seabios/docs/Mailinglist.md create mode 100644 roms/seabios/docs/Memory_Model.md create mode 100644 roms/seabios/docs/README create mode 100644 roms/seabios/docs/Releases.md create mode 100644 roms/seabios/docs/SeaBIOS.md create mode 100755 roms/seabios/scripts/tarball.sh create mode 100644 roms/seabios/src/fw/dev-piix.h create mode 100644 roms/seabios/src/hw/sdcard.c create mode 100644 roms/seabios/src/std/vga.h create mode 100755 scripts/analyze-migration.py create mode 100644 scripts/kvm/kvm_stat.texi create mode 100755 scripts/qmp/qom-tree create mode 100644 scripts/qtest.py delete mode 100644 stubs/pci-drive-hot-add.c create mode 100644 target-s390x/mmu_helper.c create mode 100644 target-tricore/csfr.def create mode 100644 tcg/tcg-op.c create mode 100644 tests/acpi-test-data/pc/SSDT.bridge create mode 100644 tests/acpi-test-data/q35/SSDT.bridge create mode 100644 tests/libqos/ahci.c create mode 100644 tests/libqos/ahci.h create mode 100644 tests/libqos/libqos-pc.c create mode 100644 tests/libqos/libqos-pc.h create mode 100644 tests/libqos/libqos.c create mode 100644 tests/libqos/libqos.h create mode 100644 tests/libqos/malloc-generic.c create mode 100644 tests/libqos/malloc-generic.h create mode 100644 tests/libqos/malloc.c create mode 100644 tests/libqos/virtio-mmio.c create mode 100644 tests/libqos/virtio-mmio.h create mode 100644 tests/multiboot/module.txt create mode 100644 tests/multiboot/modules.c create mode 100644 tests/multiboot/modules.out create mode 100644 tests/pc-cpu-test.c delete mode 100644 tests/qemu-iotests/006.out delete mode 100644 tests/qemu-iotests/016.out create mode 100755 tests/qemu-iotests/093 create mode 100644 tests/qemu-iotests/093.out create mode 100755 tests/qemu-iotests/094 create mode 100644 tests/qemu-iotests/094.out create mode 100755 tests/qemu-iotests/109 create mode 100644 tests/qemu-iotests/109.out create mode 100755 tests/qemu-iotests/110 create mode 100644 tests/qemu-iotests/110.out create mode 100755 tests/qemu-iotests/112 create mode 100644 tests/qemu-iotests/112.out create mode 100755 tests/qemu-iotests/113 create mode 100644 tests/qemu-iotests/113.out create mode 100755 tests/qemu-iotests/114 create mode 100644 tests/qemu-iotests/114.out create mode 100755 tests/qemu-iotests/115 create mode 100644 tests/qemu-iotests/115.out create mode 100755 tests/qemu-iotests/116 create mode 100644 tests/qemu-iotests/116.out create mode 100755 tests/qemu-iotests/121 create mode 100644 tests/qemu-iotests/121.out rename tests/qemu-iotests/{016 => 123} (56%) create mode 100644 tests/qemu-iotests/123.out create mode 100755 tests/qemu-iotests/128 create mode 100644 tests/qemu-iotests/128.out create mode 100755 tests/qemu-iotests/130 create mode 100644 tests/qemu-iotests/130.out create mode 100644 tests/qemu-iotests/132 create mode 100644 tests/qemu-iotests/132.out rename tests/qemu-iotests/{006 => 135} (79%) create mode 100644 tests/qemu-iotests/135.out create mode 100644 tests/qemu-iotests/sample_images/afl5.img.bz2 create mode 100644 tests/qemu-iotests/sample_images/grub_mbr.raw.bz2 create mode 100644 tests/rcutorture.c create mode 100644 tests/test-rcu-list.c create mode 100644 tests/test-write-threshold.c create mode 100644 ui/sdl2-2d.c create mode 100644 ui/sdl2-input.c create mode 100644 util/rcu.c diff --git a/.gitignore b/.gitignore index e32a584..aed0e1f 100644 --- a/.gitignore +++ b/.gitignore @@ -37,14 +37,8 @@ /qemu-tech.html /qemu-doc.info /qemu-tech.info -/qemu.1 -/qemu.pod -/qemu-img.1 -/qemu-img.pod /qemu-img /qemu-nbd -/qemu-nbd.8 -/qemu-nbd.pod /qemu-options.def /qemu-options.texi /qemu-img-cmds.texi @@ -56,8 +50,7 @@ /qmp-commands.txt /vscclient /fsdev/virtfs-proxy-helper -/fsdev/virtfs-proxy-helper.1 -/fsdev/virtfs-proxy-helper.pod +*.[1-9] *.a *.aux *.cp @@ -70,6 +63,7 @@ *.ky *.log *.pdf +*.pod *.cps *.fns *.kys diff --git a/.travis.yml b/.travis.yml index ad66e5b..0ac170b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -98,3 +98,6 @@ matrix: EXTRA_PKGS="liblttng-ust-dev liburcu-dev" EXTRA_CONFIG="--enable-trace-backends=ust" compiler: gcc + - env: TARGETS=i386-softmmu,x86_64-softmmu + EXTRA_CONFIG="--enable-modules" + compiler: gcc diff --git a/LICENSE b/LICENSE index da70e94..0e0b4b9 100644 --- a/LICENSE +++ b/LICENSE @@ -11,7 +11,7 @@ option) any later version. As of July 2013, contributions under version 2 of the GNU General Public License (and no later version) are only accepted for the following files -or directories: bsd-user/, linux-user/, hw/misc/vfio.c, hw/xen/xen_pt*. +or directories: bsd-user/, linux-user/, hw/vfio/, hw/xen/xen_pt*. 3) The Tiny Code Generator (TCG) is released under the BSD license (see license headers in files). diff --git a/MAINTAINERS b/MAINTAINERS index bcb69e8..d7e9ba2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -50,14 +50,12 @@ Descriptions of section entries: General Project Administration ------------------------------ -M: Anthony Liguori M: Peter Maydell Responsible Disclosure, Reporting Security Issues ------------------------------ W: http://wiki.qemu.org/SecurityProcess M: Michael S. Tsirkin -M: Anthony Liguori L: secalert@redhat.com Guest CPU cores (TCG): @@ -98,8 +96,12 @@ LM32 M: Michael Walle S: Maintained F: target-lm32/ +F: disas/lm32.c F: hw/lm32/ -F: hw/char/lm32_* +F: hw/*/lm32_* +F: hw/*/milkymist-* +F: include/hw/char/lm32_juart.h +F: include/hw/lm32/ F: tests/tcg/lm32/ M68K @@ -155,6 +157,7 @@ F: hw/sh4/ SPARC M: Blue Swirl +M: Mark Cave-Ayland S: Maintained F: target-sparc/ F: hw/sparc/ @@ -514,11 +517,13 @@ SPARC Machines -------------- Sun4m M: Blue Swirl +M: Mark Cave-Ayland S: Maintained F: hw/sparc/sun4m.c Sun4u M: Blue Swirl +M: Mark Cave-Ayland S: Maintained F: hw/sparc64/sun4u.c @@ -534,6 +539,7 @@ S390 Virtio M: Alexander Graf S: Maintained F: hw/s390x/s390-*.c +X: hw/s390x/*pci*.[hc] S390 Virtio-ccw M: Cornelia Huck @@ -544,6 +550,7 @@ F: hw/s390x/s390-virtio-ccw.c F: hw/s390x/css.[hc] F: hw/s390x/sclp*.[hc] F: hw/s390x/ipl*.[hc] +F: hw/s390x/*pci*.[hc] F: include/hw/s390x/ F: pc-bios/s390-ccw/ T: git git://github.com/cohuck/qemu virtio-ccw-upstr @@ -559,7 +566,6 @@ F: hw/unicore32/ X86 Machines ------------ PC -M: Anthony Liguori M: Michael S. Tsirkin S: Supported F: include/hw/i386/ @@ -593,12 +599,31 @@ F: hw/net/opencores_eth.c Devices ------- +EDU +M: Jiri Slaby +S: Maintained +F: hw/misc/edu.c + IDE -M: Kevin Wolf -M: Stefan Hajnoczi -S: Odd Fixes +M: John Snow +L: qemu-block@nongnu.org +S: Supported F: include/hw/ide.h F: hw/ide/ +F: hw/block/block.c +F: hw/block/cdrom.c +F: hw/block/hd-geometry.c +F: tests/ide-test.c +F: tests/ahci-test.c +T: git git://github.com/jnsnow/qemu.git ide + +Floppy +M: John Snow +L: qemu-block@nongnu.org +S: Supported +F: hw/block/fdc.c +F: include/hw/block/fdc.h +T: git git://github.com/jnsnow/qemu.git ide OMAP M: Peter Maydell @@ -657,7 +682,7 @@ F: hw/usb/dev-serial.c VFIO M: Alex Williamson S: Supported -F: hw/misc/vfio.c +F: hw/vfio/* vhost M: Michael S. Tsirkin @@ -665,7 +690,6 @@ S: Supported F: hw/*/*vhost* virtio -M: Anthony Liguori M: Michael S. Tsirkin S: Supported F: hw/*/virtio* @@ -696,6 +720,14 @@ M: Amit Shah S: Supported F: hw/char/virtio-serial-bus.c F: hw/char/virtio-console.c +F: include/hw/virtio/virtio-serial.h + +virtio-rng +M: Amit Shah +S: Supported +F: hw/virtio/virtio-rng.c +F: include/hw/virtio/virtio-rng.h +F: backends/rng*.c nvme M: Keith Busch @@ -743,6 +775,7 @@ F: aio-*.c F: block* F: block/ F: hw/block/ +F: migration/block* F: qemu-img* F: qemu-io* F: tests/image-fuzzer/ @@ -750,8 +783,19 @@ F: tests/qemu-iotests/ T: git git://repo.or.cz/qemu/kevin.git block T: git git://github.com/stefanha/qemu.git block +Block Jobs +M: Jeff Cody +L: qemu-block@nongnu.org +S: Supported +F: blockjob.c +F: include/block/blockjob.h +F: block/backup.c +F: block/commit.c +F: block/stream.h +F: block/mirror.c +T: git git://github.com/codyprime/qemu-kvm-jtc.git block + Character Devices -M: Anthony Liguori M: Paolo Bonzini S: Maintained F: qemu-char.c @@ -763,6 +807,11 @@ M: Samuel Thibault S: Maintained F: backends/baum.c +Coverity model +M: Markus Armbruster +S: Supported +F: scripts/coverity-model.c + CPU M: Andreas Färber S: Supported @@ -807,7 +856,6 @@ F: audio/spiceaudio.c F: hw/display/qxl* Graphics -M: Anthony Liguori M: Gerd Hoffmann S: Odd Fixes F: ui/ @@ -819,7 +867,6 @@ S: Odd Fixes F: ui/cocoa.m Main loop -M: Anthony Liguori M: Paolo Bonzini S: Maintained F: cpus.c @@ -836,8 +883,8 @@ F: hmp-commands.hx T: git git://repo.or.cz/qemu/qmp-unstable.git queue/qmp Network device layer -M: Anthony Liguori M: Stefan Hajnoczi +M: Jason Wang S: Maintained F: net/ T: git git://github.com/stefanha/qemu.git net @@ -887,7 +934,6 @@ F: qga/ T: git git://github.com/mdroth/qemu.git qga QOM -M: Anthony Liguori M: Andreas Färber S: Supported T: git git://github.com/afaerber/qemu-cpu.git qom-next @@ -928,12 +974,14 @@ F: scripts/checkpatch.pl Migration M: Juan Quintela +M: Amit Shah S: Maintained F: include/migration/ -F: migration* +F: migration/ F: savevm.c F: arch_init.c -F: vmstate.c +F: scripts/vmstate-static-checker.py +F: tests/vmstate-static-checker-data/ Seccomp M: Eduardo Otubo @@ -1051,20 +1099,28 @@ F: block/vmdk.c RBD M: Josh Durgin +M: Jeff Cody +L: qemu-block@nongnu.org S: Supported F: block/rbd.c +T: git git://github.com/codyprime/qemu-kvm-jtc.git block Sheepdog M: Hitoshi Mitake M: Liu Yuan +M: Jeff Cody +L: qemu-block@nongnu.org L: sheepdog@lists.wpkg.org S: Supported F: block/sheepdog.c +T: git git://github.com/codyprime/qemu-kvm-jtc.git block VHDX M: Jeff Cody +L: qemu-block@nongnu.org S: Supported F: block/vhdx* +T: git git://github.com/codyprime/qemu-kvm-jtc.git block VDI M: Stefan Weil @@ -1079,20 +1135,42 @@ S: Supported F: block/iscsi.c NFS +M: Jeff Cody M: Peter Lieven +L: qemu-block@nongnu.org S: Maintained F: block/nfs.c +T: git git://github.com/codyprime/qemu-kvm-jtc.git block SSH M: Richard W.M. Jones +M: Jeff Cody +L: qemu-block@nongnu.org S: Supported F: block/ssh.c +T: git git://github.com/codyprime/qemu-kvm-jtc.git block ARCHIPELAGO -M: Chrysostomos Nanakos M: Chrysostomos Nanakos +M: Jeff Cody +L: qemu-block@nongnu.org S: Maintained F: block/archipelago.c +T: git git://github.com/codyprime/qemu-kvm-jtc.git block + +CURL +M: Jeff Cody +L: qemu-block@nongnu.org +S: Supported +F: block/curl.c +T: git git://github.com/codyprime/qemu-kvm-jtc.git block + +GLUSTER +M: Jeff Cody +L: qemu-block@nongnu.org +S: Supported +F: block/gluster.c +T: git git://github.com/codyprime/qemu-kvm-jtc.git block Bootdevice M: Gonglei diff --git a/Makefile b/Makefile index f505202..93af871 100644 --- a/Makefile +++ b/Makefile @@ -84,6 +84,9 @@ HELPERS-$(CONFIG_LINUX) = qemu-bridge-helper$(EXESUF) ifdef BUILD_DOCS DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1 qemu-nbd.8 qmp-commands.txt +ifdef CONFIG_LINUX +DOCS+=kvm_stat.1 +endif ifdef CONFIG_VIRTFS DOCS+=fsdev/virtfs-proxy-helper.1 endif @@ -109,8 +112,9 @@ endif -include $(SUBDIR_DEVICES_MAK_DEP) %/config-devices.mak: default-configs/%.mak - $(call quiet-command,$(SHELL) $(SRC_PATH)/scripts/make_device_config.sh $@ $<, " GEN $@") - @if test -f $@; then \ + $(call quiet-command, \ + $(SHELL) $(SRC_PATH)/scripts/make_device_config.sh $< $*-config-devices.mak.d $@ > $@.tmp, " GEN $@.tmp") + $(call quiet-command, if test -f $@; then \ if cmp -s $@.old $@; then \ mv $@.tmp $@; \ cp -p $@ $@.old; \ @@ -126,7 +130,7 @@ endif else \ mv $@.tmp $@; \ cp -p $@ $@.old; \ - fi + fi, " GEN $@"); defconfig: rm -f config-all-devices.mak $(SUBDIR_DEVICES_MAK) @@ -197,9 +201,9 @@ ALL_SUBDIRS=$(TARGET_DIRS) $(patsubst %,pc-bios/%, $(ROMS)) recurse-all: $(SUBDIR_RULES) $(ROMSUBDIR_RULES) -$(BUILD_DIR)/version.o: $(SRC_PATH)/version.rc $(BUILD_DIR)/config-host.h | $(BUILD_DIR)/version.lo +$(BUILD_DIR)/version.o: $(SRC_PATH)/version.rc config-host.h | $(BUILD_DIR)/version.lo $(call quiet-command,$(WINDRES) -I$(BUILD_DIR) -o $@ $<," RC version.o") -$(BUILD_DIR)/version.lo: $(SRC_PATH)/version.rc $(BUILD_DIR)/config-host.h +$(BUILD_DIR)/version.lo: $(SRC_PATH)/version.rc config-host.h $(call quiet-command,$(WINDRES) -I$(BUILD_DIR) -o $@ $<," RC version.lo") Makefile: $(version-obj-y) $(version-lobj-y) @@ -313,8 +317,8 @@ qemu-%.tar.bz2: distclean: clean rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi - rm -f config-all-devices.mak config-all-disas.mak - rm -f po/*.mo + rm -f config-all-devices.mak config-all-disas.mak config.status + rm -f po/*.mo tests/qemu-iotests/common.env rm -f roms/seabios/config.mak roms/vgabios/config.mak rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.cps qemu-doc.dvi rm -f qemu-doc.fn qemu-doc.fns qemu-doc.info qemu-doc.ky qemu-doc.kys @@ -327,8 +331,8 @@ distclean: clean rm -rf $$d || exit 1 ; \ done rm -Rf .sdk - if test -f pixman/config.log; then make -C pixman distclean; fi - if test -f dtc/version_gen.h; then make $(DTC_MAKE_ARGS) clean; fi + if test -f pixman/config.log; then $(MAKE) -C pixman distclean; fi + if test -f dtc/version_gen.h; then $(MAKE) $(DTC_MAKE_ARGS) clean; fi KEYMAPS=da en-gb et fr fr-ch is lt modifiers no pt-br sv \ ar de en-us fi fr-be hr it lv nl pl ru th \ @@ -490,6 +494,12 @@ qemu-nbd.8: qemu-nbd.texi $(POD2MAN) --section=8 --center=" " --release=" " qemu-nbd.pod > $@, \ " GEN $@") +kvm_stat.1: scripts/kvm/kvm_stat.texi + $(call quiet-command, \ + perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< kvm_stat.pod && \ + $(POD2MAN) --section=1 --center=" " --release=" " kvm_stat.pod > $@, \ + " GEN $@") + dvi: qemu-doc.dvi qemu-tech.dvi html: qemu-doc.html qemu-tech.html info: qemu-doc.info qemu-tech.info @@ -522,7 +532,7 @@ installer: $(INSTALLER) INSTDIR=/tmp/qemu-nsis $(INSTALLER): $(SRC_PATH)/qemu.nsi - make install prefix=${INSTDIR} + $(MAKE) install prefix=${INSTDIR} ifdef SIGNCODE (cd ${INSTDIR}; \ for i in *.exe; do \ diff --git a/Makefile.objs b/Makefile.objs index 18fd35c..28999d3 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -48,15 +48,10 @@ common-obj-$(CONFIG_POSIX) += os-posix.o common-obj-$(CONFIG_LINUX) += fsdev/ -common-obj-y += migration.o migration-tcp.o -common-obj-y += vmstate.o -common-obj-y += qemu-file.o qemu-file-unix.o qemu-file-stdio.o -common-obj-$(CONFIG_RDMA) += migration-rdma.o +common-obj-y += migration/ common-obj-y += qemu-char.o #aio.o -common-obj-y += block-migration.o -common-obj-y += page_cache.o xbzrle.o - -common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o +common-obj-y += page_cache.o +common-obj-y += qjson.o common-obj-$(CONFIG_SPICE) += spice-qemu-char.o diff --git a/Makefile.target b/Makefile.target index e9ff1ee..2262d89 100644 --- a/Makefile.target +++ b/Makefile.target @@ -83,7 +83,7 @@ all: $(PROGS) stap ######################################################### # cpu emulator library obj-y = exec.o translate-all.o cpu-exec.o -obj-y += tcg/tcg.o tcg/optimize.o +obj-y += tcg/tcg.o tcg/tcg-op.o tcg/optimize.o obj-$(CONFIG_TCG_INTERPRETER) += tci.o obj-$(CONFIG_TCG_INTERPRETER) += disas/tci.o obj-y += fpu/softfloat.o @@ -175,9 +175,11 @@ all-obj-y += $(common-obj-y) all-obj-y += $(target-obj-y) all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) +$(QEMU_PROG_BUILD): config-devices.mak + # build either PROG or PROGW $(QEMU_PROG_BUILD): $(all-obj-y) ../libqemuutil.a ../libqemustub.a - $(call LINK,$^) + $(call LINK, $(filter-out %.mak, $^)) gdbstub-xml.c: $(TARGET_XML_FILES) $(SRC_PATH)/scripts/feature_to_c.sh $(call quiet-command,rm -f $@ && $(SHELL) $(SRC_PATH)/scripts/feature_to_c.sh $@ $(TARGET_XML_FILES)," GEN $(TARGET_DIR)$@") diff --git a/VERSION b/VERSION index ccbccc3..2bf1c1c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.2.0 +2.3.1 diff --git a/aio-posix.c b/aio-posix.c index d3ac06e..cbd4c34 100644 --- a/aio-posix.c +++ b/aio-posix.c @@ -73,7 +73,7 @@ void aio_set_fd_handler(AioContext *ctx, } else { if (node == NULL) { /* Alloc and insert if it's not already there */ - node = g_malloc0(sizeof(AioHandler)); + node = g_new0(AioHandler, 1); node->pfd.fd = fd; QLIST_INSERT_HEAD(&ctx->aio_handlers, node, node); diff --git a/aio-win32.c b/aio-win32.c index d81313b..e6f4ced 100644 --- a/aio-win32.c +++ b/aio-win32.c @@ -67,7 +67,7 @@ void aio_set_fd_handler(AioContext *ctx, if (node == NULL) { /* Alloc and insert if it's not already there */ - node = g_malloc0(sizeof(AioHandler)); + node = g_new0(AioHandler, 1); node->pfd.fd = fd; QLIST_INSERT_HEAD(&ctx->aio_handlers, node, node); } @@ -129,7 +129,7 @@ void aio_set_event_notifier(AioContext *ctx, } else { if (node == NULL) { /* Alloc and insert if it's not already there */ - node = g_malloc0(sizeof(AioHandler)); + node = g_new0(AioHandler, 1); node->e = e; node->pfd.fd = (uintptr_t)event_notifier_get_handle(e); node->pfd.events = G_IO_IN; diff --git a/arch_init.c b/arch_init.c index 7680d28..4c8fcee 100644 --- a/arch_init.c +++ b/arch_init.c @@ -52,6 +52,7 @@ #include "exec/ram_addr.h" #include "hw/acpi/acpi.h" #include "qemu/host-utils.h" +#include "qemu/rcu_queue.h" #ifdef DEBUG_ARCH_INIT #define DPRINTF(fmt, ...) \ @@ -304,15 +305,37 @@ uint64_t xbzrle_mig_pages_overflow(void) return acct_info.xbzrle_overflows; } -static size_t save_block_hdr(QEMUFile *f, RAMBlock *block, ram_addr_t offset, - int cont, int flag) +/* This is the last block that we have visited serching for dirty pages + */ +static RAMBlock *last_seen_block; +/* This is the last block from where we have sent data */ +static RAMBlock *last_sent_block; +static ram_addr_t last_offset; +static unsigned long *migration_bitmap; +static uint64_t migration_dirty_pages; +static uint32_t last_version; +static bool ram_bulk_stage; + +/** + * save_page_header: Write page header to wire + * + * If this is the 1st block, it also writes the block identification + * + * Returns: Number of bytes written + * + * @f: QEMUFile where to send the data + * @block: block that contains the page we want to send + * @offset: offset inside the block for the page + * in the lower bits, it contains flags + */ +static size_t save_page_header(QEMUFile *f, RAMBlock *block, ram_addr_t offset) { size_t size; - qemu_put_be64(f, offset | cont | flag); + qemu_put_be64(f, offset); size = 8; - if (!cont) { + if (!(offset & RAM_SAVE_FLAG_CONTINUE)) { qemu_put_byte(f, strlen(block->idstr)); qemu_put_buffer(f, (uint8_t *)block->idstr, strlen(block->idstr)); @@ -321,17 +344,6 @@ static size_t save_block_hdr(QEMUFile *f, RAMBlock *block, ram_addr_t offset, return size; } -/* This is the last block that we have visited serching for dirty pages - */ -static RAMBlock *last_seen_block; -/* This is the last block from where we have sent data */ -static RAMBlock *last_sent_block; -static ram_addr_t last_offset; -static unsigned long *migration_bitmap; -static uint64_t migration_dirty_pages; -static uint32_t last_version; -static bool ram_bulk_stage; - /* Update the xbzrle cache to reflect a page that's been sent as all 0. * The important thing is that a stale (not-yet-0'd) page be replaced * by the new data. @@ -346,22 +358,40 @@ static void xbzrle_cache_zero_page(ram_addr_t current_addr) /* We don't care if this fails to allocate a new cache page * as long as it updated an old one */ - cache_insert(XBZRLE.cache, current_addr, ZERO_TARGET_PAGE); + cache_insert(XBZRLE.cache, current_addr, ZERO_TARGET_PAGE, + bitmap_sync_count); } #define ENCODING_FLAG_XBZRLE 0x1 +/** + * save_xbzrle_page: compress and send current page + * + * Returns: 1 means that we wrote the page + * 0 means that page is identical to the one already sent + * -1 means that xbzrle would be longer than normal + * + * @f: QEMUFile where to send the data + * @current_data: + * @current_addr: + * @block: block that contains the page we want to send + * @offset: offset inside the block for the page + * @last_stage: if we are at the completion stage + * @bytes_transferred: increase it with the number of transferred bytes + */ static int save_xbzrle_page(QEMUFile *f, uint8_t **current_data, ram_addr_t current_addr, RAMBlock *block, - ram_addr_t offset, int cont, bool last_stage) + ram_addr_t offset, bool last_stage, + uint64_t *bytes_transferred) { - int encoded_len = 0, bytes_sent = -1; + int encoded_len = 0, bytes_xbzrle; uint8_t *prev_cached_page; - if (!cache_is_cached(XBZRLE.cache, current_addr)) { + if (!cache_is_cached(XBZRLE.cache, current_addr, bitmap_sync_count)) { acct_info.xbzrle_cache_miss++; if (!last_stage) { - if (cache_insert(XBZRLE.cache, current_addr, *current_data) == -1) { + if (cache_insert(XBZRLE.cache, current_addr, *current_data, + bitmap_sync_count) == -1) { return -1; } else { /* update *current_data when the page has been @@ -401,15 +431,16 @@ static int save_xbzrle_page(QEMUFile *f, uint8_t **current_data, } /* Send XBZRLE based compressed page */ - bytes_sent = save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_XBZRLE); + bytes_xbzrle = save_page_header(f, block, offset | RAM_SAVE_FLAG_XBZRLE); qemu_put_byte(f, ENCODING_FLAG_XBZRLE); qemu_put_be16(f, encoded_len); qemu_put_buffer(f, XBZRLE.encoded_buf, encoded_len); - bytes_sent += encoded_len + 1 + 2; + bytes_xbzrle += encoded_len + 1 + 2; acct_info.xbzrle_pages++; - acct_info.xbzrle_bytes += bytes_sent; + acct_info.xbzrle_bytes += bytes_xbzrle; + *bytes_transferred += bytes_xbzrle; - return bytes_sent; + return 1; } static inline @@ -485,7 +516,6 @@ static void migration_bitmap_sync_range(ram_addr_t start, ram_addr_t length) } -/* Needs iothread lock! */ /* Fix me: there are too many global variables used in migration process. */ static int64_t start_time; static int64_t bytes_xfer_prev; @@ -498,6 +528,7 @@ static void migration_bitmap_sync_init(void) num_dirty_pages_period = 0; } +/* Called with iothread lock held, to protect ram_list.dirty_memory[] */ static void migration_bitmap_sync(void) { RAMBlock *block; @@ -521,9 +552,12 @@ static void migration_bitmap_sync(void) trace_migration_bitmap_sync_start(); address_space_sync_dirty_bitmap(&address_space_memory); - QTAILQ_FOREACH(block, &ram_list.blocks, next) { - migration_bitmap_sync_range(block->mr->ram_addr, block->length); + rcu_read_lock(); + QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { + migration_bitmap_sync_range(block->mr->ram_addr, block->used_length); } + rcu_read_unlock(); + trace_migration_bitmap_sync_end(migration_dirty_pages - num_dirty_pages_init); num_dirty_pages_period += migration_dirty_pages - num_dirty_pages_init; @@ -569,55 +603,68 @@ static void migration_bitmap_sync(void) } } -/* +/** * ram_save_page: Send the given page to the stream * - * Returns: Number of bytes written. + * Returns: Number of pages written. + * + * @f: QEMUFile where to send the data + * @block: block that contains the page we want to send + * @offset: offset inside the block for the page + * @last_stage: if we are at the completion stage + * @bytes_transferred: increase it with the number of transferred bytes */ static int ram_save_page(QEMUFile *f, RAMBlock* block, ram_addr_t offset, - bool last_stage) + bool last_stage, uint64_t *bytes_transferred) { - int bytes_sent; - int cont; + int pages = -1; + uint64_t bytes_xmit; ram_addr_t current_addr; MemoryRegion *mr = block->mr; uint8_t *p; int ret; bool send_async = true; - cont = (block == last_sent_block) ? RAM_SAVE_FLAG_CONTINUE : 0; - p = memory_region_get_ram_ptr(mr) + offset; /* In doubt sent page as normal */ - bytes_sent = -1; + bytes_xmit = 0; ret = ram_control_save_page(f, block->offset, - offset, TARGET_PAGE_SIZE, &bytes_sent); + offset, TARGET_PAGE_SIZE, &bytes_xmit); + if (bytes_xmit) { + *bytes_transferred += bytes_xmit; + pages = 1; + } XBZRLE_cache_lock(); current_addr = block->offset + offset; + + if (block == last_sent_block) { + offset |= RAM_SAVE_FLAG_CONTINUE; + } if (ret != RAM_SAVE_CONTROL_NOT_SUPP) { if (ret != RAM_SAVE_CONTROL_DELAYED) { - if (bytes_sent > 0) { + if (bytes_xmit > 0) { acct_info.norm_pages++; - } else if (bytes_sent == 0) { + } else if (bytes_xmit == 0) { acct_info.dup_pages++; } } } else if (is_zero_range(p, TARGET_PAGE_SIZE)) { acct_info.dup_pages++; - bytes_sent = save_block_hdr(f, block, offset, cont, - RAM_SAVE_FLAG_COMPRESS); + *bytes_transferred += save_page_header(f, block, + offset | RAM_SAVE_FLAG_COMPRESS); qemu_put_byte(f, 0); - bytes_sent++; + *bytes_transferred += 1; + pages = 1; /* Must let xbzrle know, otherwise a previous (now 0'd) cached * page would be stale */ xbzrle_cache_zero_page(current_addr); } else if (!ram_bulk_stage && migrate_use_xbzrle()) { - bytes_sent = save_xbzrle_page(f, &p, current_addr, block, - offset, cont, last_stage); + pages = save_xbzrle_page(f, &p, current_addr, block, + offset, last_stage, bytes_transferred); if (!last_stage) { /* Can't send this cached data async, since the cache page * might get updated before it gets to the wire @@ -627,39 +674,48 @@ static int ram_save_page(QEMUFile *f, RAMBlock* block, ram_addr_t offset, } /* XBZRLE overflow or normal page */ - if (bytes_sent == -1) { - bytes_sent = save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_PAGE); + if (pages == -1) { + *bytes_transferred += save_page_header(f, block, + offset | RAM_SAVE_FLAG_PAGE); if (send_async) { qemu_put_buffer_async(f, p, TARGET_PAGE_SIZE); } else { qemu_put_buffer(f, p, TARGET_PAGE_SIZE); } - bytes_sent += TARGET_PAGE_SIZE; + *bytes_transferred += TARGET_PAGE_SIZE; + pages = 1; acct_info.norm_pages++; } XBZRLE_cache_unlock(); - return bytes_sent; + return pages; } -/* - * ram_find_and_save_block: Finds a page to send and sends it to f +/** + * ram_find_and_save_block: Finds a dirty page and sends it to f + * + * Called within an RCU critical section. * - * Returns: The number of bytes written. + * Returns: The number of pages written * 0 means no dirty pages + * + * @f: QEMUFile where to send the data + * @last_stage: if we are at the completion stage + * @bytes_transferred: increase it with the number of transferred bytes */ -static int ram_find_and_save_block(QEMUFile *f, bool last_stage) +static int ram_find_and_save_block(QEMUFile *f, bool last_stage, + uint64_t *bytes_transferred) { RAMBlock *block = last_seen_block; ram_addr_t offset = last_offset; bool complete_round = false; - int bytes_sent = 0; + int pages = 0; MemoryRegion *mr; if (!block) - block = QTAILQ_FIRST(&ram_list.blocks); + block = QLIST_FIRST_RCU(&ram_list.blocks); while (true) { mr = block->mr; @@ -668,28 +724,30 @@ static int ram_find_and_save_block(QEMUFile *f, bool last_stage) offset >= last_offset) { break; } - if (offset >= block->length) { + if (offset >= block->used_length) { offset = 0; - block = QTAILQ_NEXT(block, next); + block = QLIST_NEXT_RCU(block, next); if (!block) { - block = QTAILQ_FIRST(&ram_list.blocks); + block = QLIST_FIRST_RCU(&ram_list.blocks); complete_round = true; ram_bulk_stage = false; } } else { - bytes_sent = ram_save_page(f, block, offset, last_stage); + pages = ram_save_page(f, block, offset, last_stage, + bytes_transferred); /* if page is unmodified, continue to the next */ - if (bytes_sent > 0) { + if (pages > 0) { last_sent_block = block; break; } } } + last_seen_block = block; last_offset = offset; - return bytes_sent; + return pages; } static uint64_t bytes_transferred; @@ -726,9 +784,10 @@ uint64_t ram_bytes_total(void) RAMBlock *block; uint64_t total = 0; - QTAILQ_FOREACH(block, &ram_list.blocks, next) - total += block->length; - + rcu_read_lock(); + QLIST_FOREACH_RCU(block, &ram_list.blocks, next) + total += block->used_length; + rcu_read_unlock(); return total; } @@ -774,6 +833,13 @@ static void reset_ram_globals(void) #define MAX_WAIT 50 /* ms, half buffered_file limit */ + +/* Each of ram_save_setup, ram_save_iterate and ram_save_complete has + * long-running RCU critical section. When rcu-reclaims in the code + * start to become numerous it will be necessary to reduce the + * granularity of these critical sections. + */ + static int ram_save_setup(QEMUFile *f, void *opaque) { RAMBlock *block; @@ -814,8 +880,10 @@ static int ram_save_setup(QEMUFile *f, void *opaque) acct_clear(); } + /* iothread lock needed for ram_list.dirty_memory[] */ qemu_mutex_lock_iothread(); qemu_mutex_lock_ramlist(); + rcu_read_lock(); bytes_transferred = 0; reset_ram_globals(); @@ -827,27 +895,22 @@ static int ram_save_setup(QEMUFile *f, void *opaque) * Count the total number of pages used by ram blocks not including any * gaps due to alignment or unplugs. */ - migration_dirty_pages = 0; - QTAILQ_FOREACH(block, &ram_list.blocks, next) { - uint64_t block_pages; - - block_pages = block->length >> TARGET_PAGE_BITS; - migration_dirty_pages += block_pages; - } + migration_dirty_pages = ram_bytes_total() >> TARGET_PAGE_BITS; memory_global_dirty_log_start(); migration_bitmap_sync(); + qemu_mutex_unlock_ramlist(); qemu_mutex_unlock_iothread(); qemu_put_be64(f, ram_bytes_total() | RAM_SAVE_FLAG_MEM_SIZE); - QTAILQ_FOREACH(block, &ram_list.blocks, next) { + QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { qemu_put_byte(f, strlen(block->idstr)); qemu_put_buffer(f, (uint8_t *)block->idstr, strlen(block->idstr)); - qemu_put_be64(f, block->length); + qemu_put_be64(f, block->used_length); } - qemu_mutex_unlock_ramlist(); + rcu_read_unlock(); ram_control_before_iterate(f, RAM_CONTROL_SETUP); ram_control_after_iterate(f, RAM_CONTROL_SETUP); @@ -862,27 +925,29 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) int ret; int i; int64_t t0; - int total_sent = 0; - - qemu_mutex_lock_ramlist(); + int pages_sent = 0; + rcu_read_lock(); if (ram_list.version != last_version) { reset_ram_globals(); } + /* Read version before ram_list.blocks */ + smp_rmb(); + ram_control_before_iterate(f, RAM_CONTROL_ROUND); t0 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); i = 0; while ((ret = qemu_file_rate_limit(f)) == 0) { - int bytes_sent; + int pages; - bytes_sent = ram_find_and_save_block(f, false); - /* no more blocks to sent */ - if (bytes_sent == 0) { + pages = ram_find_and_save_block(f, false, &bytes_transferred); + /* no more pages to sent */ + if (pages == 0) { break; } - total_sent += bytes_sent; + pages_sent += pages; acct_info.iterations++; check_guest_throttling(); /* we want to check in the 1st loop, just in case it was the 1st time @@ -900,8 +965,7 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) } i++; } - - qemu_mutex_unlock_ramlist(); + rcu_read_unlock(); /* * Must occur before EOS (or any QEMUFile operation) @@ -909,12 +973,6 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) */ ram_control_after_iterate(f, RAM_CONTROL_ROUND); - bytes_transferred += total_sent; - - /* - * Do not count these 8 bytes into total_sent, so that we can - * return 0 if no page had been dirtied. - */ qemu_put_be64(f, RAM_SAVE_FLAG_EOS); bytes_transferred += 8; @@ -923,12 +981,14 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) return ret; } - return total_sent; + return pages_sent; } +/* Called with iothread lock */ static int ram_save_complete(QEMUFile *f, void *opaque) { - qemu_mutex_lock_ramlist(); + rcu_read_lock(); + migration_bitmap_sync(); ram_control_before_iterate(f, RAM_CONTROL_FINISH); @@ -937,20 +997,19 @@ static int ram_save_complete(QEMUFile *f, void *opaque) /* flush all remaining blocks regardless of rate limiting */ while (true) { - int bytes_sent; + int pages; - bytes_sent = ram_find_and_save_block(f, true); + pages = ram_find_and_save_block(f, true, &bytes_transferred); /* no more blocks to sent */ - if (bytes_sent == 0) { + if (pages == 0) { break; } - bytes_transferred += bytes_sent; } ram_control_after_iterate(f, RAM_CONTROL_FINISH); migration_end(); - qemu_mutex_unlock_ramlist(); + rcu_read_unlock(); qemu_put_be64(f, RAM_SAVE_FLAG_EOS); return 0; @@ -964,7 +1023,9 @@ static uint64_t ram_save_pending(QEMUFile *f, void *opaque, uint64_t max_size) if (remaining_size < max_size) { qemu_mutex_lock_iothread(); + rcu_read_lock(); migration_bitmap_sync(); + rcu_read_unlock(); qemu_mutex_unlock_iothread(); remaining_size = ram_save_remaining() * TARGET_PAGE_SIZE; } @@ -1006,6 +1067,9 @@ static int load_xbzrle(QEMUFile *f, ram_addr_t addr, void *host) return 0; } +/* Must be called from within a rcu critical section. + * Returns a pointer from within the RCU-protected ram_list. + */ static inline void *host_from_stream_offset(QEMUFile *f, ram_addr_t offset, int flags) @@ -1015,7 +1079,7 @@ static inline void *host_from_stream_offset(QEMUFile *f, uint8_t len; if (flags & RAM_SAVE_FLAG_CONTINUE) { - if (!block || block->length <= offset) { + if (!block || block->max_length <= offset) { error_report("Ack, bad migration stream!"); return NULL; } @@ -1027,8 +1091,9 @@ static inline void *host_from_stream_offset(QEMUFile *f, qemu_get_buffer(f, (uint8_t *)id, len); id[len] = 0; - QTAILQ_FOREACH(block, &ram_list.blocks, next) { - if (!strncmp(id, block->idstr, sizeof(id)) && block->length > offset) { + QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { + if (!strncmp(id, block->idstr, sizeof(id)) && + block->max_length > offset) { return memory_region_get_ram_ptr(block->mr) + offset; } } @@ -1059,6 +1124,12 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) ret = -EINVAL; } + /* This RCU critical section can be very long running. + * When RCU reclaims in the code start to become numerous, + * it will be necessary to reduce the granularity of this + * critical section. + */ + rcu_read_lock(); while (!ret && !(flags & RAM_SAVE_FLAG_EOS)) { ram_addr_t addr, total_ram_bytes; void *host; @@ -1083,13 +1154,15 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) id[len] = 0; length = qemu_get_be64(f); - QTAILQ_FOREACH(block, &ram_list.blocks, next) { + QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { if (!strncmp(id, block->idstr, sizeof(id))) { - if (block->length != length) { - error_report("Length mismatch: %s: 0x" RAM_ADDR_FMT - " in != 0x" RAM_ADDR_FMT, id, length, - block->length); - ret = -EINVAL; + if (length != block->used_length) { + Error *local_err = NULL; + + ret = qemu_ram_resize(block->offset, length, &local_err); + if (local_err) { + error_report_err(local_err); + } } break; } @@ -1111,7 +1184,6 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) ret = -EINVAL; break; } - ch = qemu_get_byte(f); ram_handle_compressed(host, ch, TARGET_PAGE_SIZE); break; @@ -1122,7 +1194,6 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) ret = -EINVAL; break; } - qemu_get_buffer(f, host, TARGET_PAGE_SIZE); break; case RAM_SAVE_FLAG_XBZRLE: @@ -1132,7 +1203,6 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) ret = -EINVAL; break; } - if (load_xbzrle(f, addr, host) < 0) { error_report("Failed to decompress XBZRLE page at " RAM_ADDR_FMT, addr); @@ -1157,6 +1227,7 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) } } + rcu_read_unlock(); DPRINTF("Completed load of VM with exit code %d seq iteration " "%" PRIu64 "\n", ret, seq_iter); return ret; diff --git a/async.c b/async.c index 6e1b282..2b51e87 100644 --- a/async.c +++ b/async.c @@ -44,10 +44,12 @@ struct QEMUBH { QEMUBH *aio_bh_new(AioContext *ctx, QEMUBHFunc *cb, void *opaque) { QEMUBH *bh; - bh = g_malloc0(sizeof(QEMUBH)); - bh->ctx = ctx; - bh->cb = cb; - bh->opaque = opaque; + bh = g_new(QEMUBH, 1); + *bh = (QEMUBH){ + .ctx = ctx, + .cb = cb, + .opaque = opaque, + }; qemu_mutex_lock(&ctx->bh_lock); bh->next = ctx->first_bh; /* Make sure that the members are ready before putting bh into list */ @@ -70,12 +72,13 @@ int aio_bh_poll(AioContext *ctx) /* Make sure that fetching bh happens before accessing its members */ smp_read_barrier_depends(); next = bh->next; - if (!bh->deleted && bh->scheduled) { - bh->scheduled = 0; - /* Paired with write barrier in bh schedule to ensure reading for - * idle & callbacks coming after bh's scheduling. - */ - smp_rmb(); + /* The atomic_xchg is paired with the one in qemu_bh_schedule. The + * implicit memory barrier ensures that the callback sees all writes + * done by the scheduling thread. It also ensures that the scheduling + * thread sees the zero before bh->cb has run, and thus will call + * aio_notify again if necessary. + */ + if (!bh->deleted && atomic_xchg(&bh->scheduled, 0)) { if (!bh->idle) ret = 1; bh->idle = 0; @@ -106,33 +109,28 @@ int aio_bh_poll(AioContext *ctx) void qemu_bh_schedule_idle(QEMUBH *bh) { - if (bh->scheduled) - return; bh->idle = 1; /* Make sure that idle & any writes needed by the callback are done * before the locations are read in the aio_bh_poll. */ - smp_wmb(); - bh->scheduled = 1; + atomic_mb_set(&bh->scheduled, 1); } void qemu_bh_schedule(QEMUBH *bh) { AioContext *ctx; - if (bh->scheduled) - return; ctx = bh->ctx; bh->idle = 0; - /* Make sure that: + /* The memory barrier implicit in atomic_xchg makes sure that: * 1. idle & any writes needed by the callback are done before the * locations are read in the aio_bh_poll. * 2. ctx is loaded before scheduled is set and the callback has a chance * to execute. */ - smp_mb(); - bh->scheduled = 1; - aio_notify(ctx); + if (atomic_xchg(&bh->scheduled, 1) == 0) { + aio_notify(ctx); + } } @@ -300,6 +298,7 @@ AioContext *aio_context_new(Error **errp) error_setg_errno(errp, -ret, "Failed to initialize event notifier"); return NULL; } + g_source_set_can_recurse(&ctx->source, true); aio_set_event_notifier(ctx, &ctx->notifier, (EventNotifierHandler *) event_notifier_test_and_clear); diff --git a/audio/audio_template.h b/audio/audio_template.h index 8173188..584e536 100644 --- a/audio/audio_template.h +++ b/audio/audio_template.h @@ -191,9 +191,9 @@ static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp) audio_detach_capture (hw); #endif QLIST_REMOVE (hw, entries); + glue (hw->pcm_ops->fini_, TYPE) (hw); glue (s->nb_hw_voices_, TYPE) += 1; glue (audio_pcm_hw_free_resources_ ,TYPE) (hw); - glue (hw->pcm_ops->fini_, TYPE) (hw); g_free (hw); *hwp = NULL; } diff --git a/backends/hostmem.c b/backends/hostmem.c index 99e8f99..b7b6cf8 100644 --- a/backends/hostmem.c +++ b/backends/hostmem.c @@ -335,12 +335,26 @@ host_memory_backend_memory_complete(UserCreatable *uc, Error **errp) } } +static bool +host_memory_backend_can_be_deleted(UserCreatable *uc, Error **errp) +{ + MemoryRegion *mr; + + mr = host_memory_backend_get_memory(MEMORY_BACKEND(uc), errp); + if (memory_region_is_mapped(mr)) { + return false; + } else { + return true; + } +} + static void host_memory_backend_class_init(ObjectClass *oc, void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); ucc->complete = host_memory_backend_memory_complete; + ucc->can_be_deleted = host_memory_backend_can_be_deleted; } static const TypeInfo host_memory_backend_info = { diff --git a/backends/rng-random.c b/backends/rng-random.c index 601d9dc..4f85a8e 100644 --- a/backends/rng-random.c +++ b/backends/rng-random.c @@ -88,11 +88,7 @@ static char *rng_random_get_filename(Object *obj, Error **errp) { RndRandom *s = RNG_RANDOM(obj); - if (s->filename) { - return g_strdup(s->filename); - } - - return NULL; + return g_strdup(s->filename); } static void rng_random_set_filename(Object *obj, const char *filename, diff --git a/backends/tpm.c b/backends/tpm.c index 01860c4..4efe367 100644 --- a/backends/tpm.c +++ b/backends/tpm.c @@ -36,7 +36,7 @@ void tpm_backend_destroy(TPMBackend *s) { TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); - return k->ops->destroy(s); + k->ops->destroy(s); } int tpm_backend_init(TPMBackend *s, TPMState *state, diff --git a/balloon.c b/balloon.c index b70da4f..70c00f5 100644 --- a/balloon.c +++ b/balloon.c @@ -36,6 +36,21 @@ static QEMUBalloonEvent *balloon_event_fn; static QEMUBalloonStatus *balloon_stat_fn; static void *balloon_opaque; +static bool have_balloon(Error **errp) +{ + if (kvm_enabled() && !kvm_has_sync_mmu()) { + error_set(errp, ERROR_CLASS_KVM_MISSING_CAP, + "Using KVM without synchronous MMU, balloon unavailable"); + return false; + } + if (!balloon_event_fn) { + error_set(errp, ERROR_CLASS_DEVICE_NOT_ACTIVE, + "No balloon device has been activated"); + return false; + } + return true; +} + int qemu_add_balloon_handler(QEMUBalloonEvent *event_func, QEMUBalloonStatus *stat_func, void *opaque) { @@ -62,58 +77,30 @@ void qemu_remove_balloon_handler(void *opaque) balloon_opaque = NULL; } -static int qemu_balloon(ram_addr_t target) -{ - if (!balloon_event_fn) { - return 0; - } - trace_balloon_event(balloon_opaque, target); - balloon_event_fn(balloon_opaque, target); - return 1; -} - -static int qemu_balloon_status(BalloonInfo *info) -{ - if (!balloon_stat_fn) { - return 0; - } - balloon_stat_fn(balloon_opaque, info); - return 1; -} - BalloonInfo *qmp_query_balloon(Error **errp) { BalloonInfo *info; - if (kvm_enabled() && !kvm_has_sync_mmu()) { - error_set(errp, QERR_KVM_MISSING_CAP, "synchronous MMU", "balloon"); + if (!have_balloon(errp)) { return NULL; } info = g_malloc0(sizeof(*info)); - - if (qemu_balloon_status(info) == 0) { - error_set(errp, QERR_DEVICE_NOT_ACTIVE, "balloon"); - qapi_free_BalloonInfo(info); - return NULL; - } - + balloon_stat_fn(balloon_opaque, info); return info; } -void qmp_balloon(int64_t value, Error **errp) +void qmp_balloon(int64_t target, Error **errp) { - if (kvm_enabled() && !kvm_has_sync_mmu()) { - error_set(errp, QERR_KVM_MISSING_CAP, "synchronous MMU", "balloon"); + if (!have_balloon(errp)) { return; } - if (value <= 0) { + if (target <= 0) { error_set(errp, QERR_INVALID_PARAMETER_VALUE, "target", "a size"); return; } - - if (qemu_balloon(value) == 0) { - error_set(errp, QERR_DEVICE_NOT_ACTIVE, "balloon"); - } + + trace_balloon_event(balloon_opaque, target); + balloon_event_fn(balloon_opaque, target); } diff --git a/block.c b/block.c index a612594..2366b8a 100644 --- a/block.c +++ b/block.c @@ -97,6 +97,8 @@ static QTAILQ_HEAD(, BlockDriverState) graph_bdrv_states = static QLIST_HEAD(, BlockDriver) bdrv_drivers = QLIST_HEAD_INITIALIZER(bdrv_drivers); +static void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector, + int nr_sectors); /* If non-zero, use only whitelisted block drivers */ static int use_bdrv_whitelist; @@ -229,7 +231,7 @@ size_t bdrv_opt_mem_align(BlockDriverState *bs) } /* check if the path starts with ":" */ -static int path_has_protocol(const char *path) +int path_has_protocol(const char *path) { const char *p; @@ -303,15 +305,32 @@ void path_combine(char *dest, int dest_size, } } -void bdrv_get_full_backing_filename(BlockDriverState *bs, char *dest, size_t sz) +void bdrv_get_full_backing_filename_from_filename(const char *backed, + const char *backing, + char *dest, size_t sz, + Error **errp) { - if (bs->backing_file[0] == '\0' || path_has_protocol(bs->backing_file)) { - pstrcpy(dest, sz, bs->backing_file); + if (backing[0] == '\0' || path_has_protocol(backing) || + path_is_absolute(backing)) + { + pstrcpy(dest, sz, backing); + } else if (backed[0] == '\0' || strstart(backed, "json:", NULL)) { + error_setg(errp, "Cannot use relative backing file names for '%s'", + backed); } else { - path_combine(dest, sz, bs->filename, bs->backing_file); + path_combine(dest, sz, backed, backing); } } +void bdrv_get_full_backing_filename(BlockDriverState *bs, char *dest, size_t sz, + Error **errp) +{ + char *backed = bs->exact_filename[0] ? bs->exact_filename : bs->filename; + + bdrv_get_full_backing_filename_from_filename(backed, bs->backing_file, + dest, sz, errp); +} + void bdrv_register(BlockDriver *bdrv) { /* Block drivers without coroutine functions need emulation */ @@ -487,9 +506,8 @@ int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp) Error *local_err = NULL; int ret; - drv = bdrv_find_protocol(filename, true); + drv = bdrv_find_protocol(filename, true, errp); if (drv == NULL) { - error_setg(errp, "Could not find protocol for file '%s'", filename); return -ENOENT; } @@ -548,6 +566,40 @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp) } } +/** + * Try to get @bs's logical and physical block size. + * On success, store them in @bsz struct and return 0. + * On failure return -errno. + * @bs must not be empty. + */ +int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) +{ + BlockDriver *drv = bs->drv; + + if (drv && drv->bdrv_probe_blocksizes) { + return drv->bdrv_probe_blocksizes(bs, bsz); + } + + return -ENOTSUP; +} + +/** + * Try to get @bs's geometry (cyls, heads, sectors). + * On success, store them in @geo struct and return 0. + * On failure return -errno. + * @bs must not be empty. + */ +int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo) +{ + BlockDriver *drv = bs->drv; + + if (drv && drv->bdrv_probe_geometry) { + return drv->bdrv_probe_geometry(bs, geo); + } + + return -ENOTSUP; +} + /* * Create a uniquely-named empty temporary file. * Return 0 upon success, otherwise a negative errno value. @@ -607,7 +659,8 @@ static BlockDriver *find_hdev_driver(const char *filename) } BlockDriver *bdrv_find_protocol(const char *filename, - bool allow_protocol_prefix) + bool allow_protocol_prefix, + Error **errp) { BlockDriver *drv1; char protocol[128]; @@ -629,7 +682,7 @@ BlockDriver *bdrv_find_protocol(const char *filename, } if (!path_has_protocol(filename) || !allow_protocol_prefix) { - return bdrv_find_format("file"); + return &bdrv_file; } p = strchr(filename, ':'); @@ -645,25 +698,54 @@ BlockDriver *bdrv_find_protocol(const char *filename, return drv1; } } + + error_setg(errp, "Unknown protocol '%s'", protocol); return NULL; } +/* + * Guess image format by probing its contents. + * This is not a good idea when your image is raw (CVE-2008-2004), but + * we do it anyway for backward compatibility. + * + * @buf contains the image's first @buf_size bytes. + * @buf_size is the buffer size in bytes (generally BLOCK_PROBE_BUF_SIZE, + * but can be smaller if the image file is smaller) + * @filename is its filename. + * + * For all block drivers, call the bdrv_probe() method to get its + * probing score. + * Return the first block driver with the highest probing score. + */ +BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size, + const char *filename) +{ + int score_max = 0, score; + BlockDriver *drv = NULL, *d; + + QLIST_FOREACH(d, &bdrv_drivers, list) { + if (d->bdrv_probe) { + score = d->bdrv_probe(buf, buf_size, filename); + if (score > score_max) { + score_max = score; + drv = d; + } + } + } + + return drv; +} + static int find_image_format(BlockDriverState *bs, const char *filename, BlockDriver **pdrv, Error **errp) { - int score, score_max; - BlockDriver *drv1, *drv; - uint8_t buf[2048]; + BlockDriver *drv; + uint8_t buf[BLOCK_PROBE_BUF_SIZE]; int ret = 0; /* Return the raw BlockDriver * to scsi-generic devices or empty drives */ if (bs->sg || !bdrv_is_inserted(bs) || bdrv_getlength(bs) == 0) { - drv = bdrv_find_format("raw"); - if (!drv) { - error_setg(errp, "Could not find raw image format"); - ret = -ENOENT; - } - *pdrv = drv; + *pdrv = &bdrv_raw; return ret; } @@ -675,17 +757,7 @@ static int find_image_format(BlockDriverState *bs, const char *filename, return ret; } - score_max = 0; - drv = NULL; - QLIST_FOREACH(drv1, &bdrv_drivers, list) { - if (drv1->bdrv_probe) { - score = drv1->bdrv_probe(buf, ret, filename); - if (score > score_max) { - score_max = score; - drv = drv1; - } - } - } + drv = bdrv_probe_all(buf, ret, filename); if (!drv) { error_setg(errp, "Could not determine image format: No compatible " "driver found"); @@ -932,7 +1004,6 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file, bs->zero_beyond_eof = true; open_flags = bdrv_open_flags(bs, flags); bs->read_only = !(open_flags & BDRV_O_RDWR); - bs->growable = !!(flags & BDRV_O_PROTOCOL); if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) { error_setg(errp, @@ -992,6 +1063,13 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file, goto free_and_fail; } + if (bs->encrypted) { + error_report("Encrypted images are deprecated"); + error_printf("Support for them will be removed in a future release.\n" + "You can use 'qemu-img convert' to convert your image" + " to an unencrypted one.\n"); + } + ret = refresh_total_sectors(bs, bs->total_sectors); if (ret < 0) { error_setg_errno(errp, -ret, "Could not refresh total sector count"); @@ -1098,9 +1176,8 @@ static int bdrv_fill_options(QDict **options, const char **pfilename, int flags, } else { if (!drvname && protocol) { if (filename) { - drv = bdrv_find_protocol(filename, parse_filename); + drv = bdrv_find_protocol(filename, parse_filename, errp); if (!drv) { - error_setg(errp, "Unknown protocol"); return -EINVAL; } @@ -1162,7 +1239,7 @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd) bdrv_op_block_all(bs->backing_hd, bs->backing_blocker); /* Otherwise we won't be able to commit due to check in bdrv_commit */ - bdrv_op_unblock(bs->backing_hd, BLOCK_OP_TYPE_COMMIT, + bdrv_op_unblock(bs->backing_hd, BLOCK_OP_TYPE_COMMIT_TARGET, bs->backing_blocker); out: bdrv_refresh_limits(bs, NULL); @@ -1180,7 +1257,6 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp) { char *backing_filename = g_malloc0(PATH_MAX); int ret = 0; - BlockDriver *back_drv = NULL; BlockDriverState *backing_hd; Error *local_err = NULL; @@ -1201,7 +1277,14 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp) QDECREF(options); goto free_exit; } else { - bdrv_get_full_backing_filename(bs, backing_filename, PATH_MAX); + bdrv_get_full_backing_filename(bs, backing_filename, PATH_MAX, + &local_err); + if (local_err) { + ret = -EINVAL; + error_propagate(errp, local_err); + QDECREF(options); + goto free_exit; + } } if (!bs->drv || !bs->drv->supports_backing) { @@ -1213,14 +1296,14 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp) backing_hd = bdrv_new(); - if (bs->backing_format[0] != '\0') { - back_drv = bdrv_find_format(bs->backing_format); + if (bs->backing_format[0] != '\0' && !qdict_haskey(options, "driver")) { + qdict_put(options, "driver", qstring_from_str(bs->backing_format)); } assert(bs->backing_hd == NULL); ret = bdrv_open(&backing_hd, *backing_filename ? backing_filename : NULL, NULL, options, - bdrv_backing_flags(bs->open_flags), back_drv, &local_err); + bdrv_backing_flags(bs->open_flags), NULL, &local_err); if (ret < 0) { bdrv_unref(backing_hd); backing_hd = NULL; @@ -1294,11 +1377,10 @@ int bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp) /* TODO: extra byte is a hack to ensure MAX_PATH space on Windows. */ char *tmp_filename = g_malloc0(PATH_MAX + 1); int64_t total_size; - BlockDriver *bdrv_qcow2; QemuOpts *opts = NULL; QDict *snapshot_options; BlockDriverState *bs_snapshot; - Error *local_err; + Error *local_err = NULL; int ret; /* if snapshot, we create a temporary backing file and open it @@ -1319,11 +1401,10 @@ int bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp) goto out; } - bdrv_qcow2 = bdrv_find_format("qcow2"); - opts = qemu_opts_create(bdrv_qcow2->create_opts, NULL, 0, + opts = qemu_opts_create(bdrv_qcow2.create_opts, NULL, 0, &error_abort); - qemu_opt_set_number(opts, BLOCK_OPT_SIZE, total_size); - ret = bdrv_create(bdrv_qcow2, tmp_filename, opts, &local_err); + qemu_opt_set_number(opts, BLOCK_OPT_SIZE, total_size, &error_abort); + ret = bdrv_create(&bdrv_qcow2, tmp_filename, opts, &local_err); qemu_opts_del(opts); if (ret < 0) { error_setg_errno(errp, -ret, "Could not create temporary overlay " @@ -1343,7 +1424,7 @@ int bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp) bs_snapshot = bdrv_new(); ret = bdrv_open(&bs_snapshot, NULL, NULL, snapshot_options, - flags, bdrv_qcow2, &local_err); + flags, &bdrv_qcow2, &local_err); if (ret < 0) { error_propagate(errp, local_err); goto out; @@ -1467,6 +1548,7 @@ int bdrv_open(BlockDriverState **pbs, const char *filename, } /* Image format probing */ + bs->probed = !drv; if (!drv && file) { ret = find_image_format(file, filename, &drv, &local_err); if (ret < 0) { @@ -1842,7 +1924,6 @@ void bdrv_close(BlockDriverState *bs) bs->encrypted = 0; bs->valid_key = 0; bs->sg = 0; - bs->growable = 0; bs->zero_beyond_eof = false; QDECREF(bs->options); bs->options = NULL; @@ -2164,7 +2245,6 @@ int bdrv_commit(BlockDriverState *bs) int n, ro, open_flags; int ret = 0; uint8_t *buf = NULL; - char filename[PATH_MAX]; if (!drv) return -ENOMEDIUM; @@ -2173,14 +2253,12 @@ int bdrv_commit(BlockDriverState *bs) return -ENOTSUP; } - if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT, NULL) || - bdrv_op_is_blocked(bs->backing_hd, BLOCK_OP_TYPE_COMMIT, NULL)) { + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT_SOURCE, NULL) || + bdrv_op_is_blocked(bs->backing_hd, BLOCK_OP_TYPE_COMMIT_TARGET, NULL)) { return -EBUSY; } ro = bs->backing_hd->read_only; - /* Use pstrcpy (not strncpy): filename must be NUL-terminated. */ - pstrcpy(filename, sizeof(filename), bs->backing_hd->filename); open_flags = bs->backing_hd->open_flags; if (ro) { @@ -2605,25 +2683,17 @@ exit: static int bdrv_check_byte_request(BlockDriverState *bs, int64_t offset, size_t size) { - int64_t len; - - if (size > INT_MAX) { + if (size > BDRV_REQUEST_MAX_SECTORS << BDRV_SECTOR_BITS) { return -EIO; } - if (!bdrv_is_inserted(bs)) + if (!bdrv_is_inserted(bs)) { return -ENOMEDIUM; + } - if (bs->growable) - return 0; - - len = bdrv_getlength(bs); - - if (offset < 0) - return -EIO; - - if ((offset > len) || (len - offset < size)) + if (offset < 0) { return -EIO; + } return 0; } @@ -2631,7 +2701,7 @@ static int bdrv_check_byte_request(BlockDriverState *bs, int64_t offset, static int bdrv_check_request(BlockDriverState *bs, int64_t sector_num, int nb_sectors) { - if (nb_sectors < 0 || nb_sectors > INT_MAX / BDRV_SECTOR_SIZE) { + if (nb_sectors < 0 || nb_sectors > BDRV_REQUEST_MAX_SECTORS) { return -EIO; } @@ -2718,7 +2788,7 @@ static int bdrv_rw_co(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, .iov_len = nb_sectors * BDRV_SECTOR_SIZE, }; - if (nb_sectors < 0 || nb_sectors > INT_MAX / BDRV_SECTOR_SIZE) { + if (nb_sectors < 0 || nb_sectors > BDRV_REQUEST_MAX_SECTORS) { return -EINVAL; } @@ -2786,13 +2856,10 @@ int bdrv_make_zero(BlockDriverState *bs, BdrvRequestFlags flags) } for (;;) { - nb_sectors = target_sectors - sector_num; + nb_sectors = MIN(target_sectors - sector_num, BDRV_REQUEST_MAX_SECTORS); if (nb_sectors <= 0) { return 0; } - if (nb_sectors > INT_MAX / BDRV_SECTOR_SIZE) { - nb_sectors = INT_MAX / BDRV_SECTOR_SIZE; - } ret = bdrv_get_block_status(bs, sector_num, nb_sectors, &n); if (ret < 0) { error_report("error getting block status at sector %" PRId64 ": %s", @@ -3005,10 +3072,10 @@ static int coroutine_fn bdrv_aligned_preadv(BlockDriverState *bs, } /* Forward the request to the BlockDriver */ - if (!(bs->zero_beyond_eof && bs->growable)) { + if (!bs->zero_beyond_eof) { ret = drv->bdrv_co_readv(bs, sector_num, nb_sectors, qiov); } else { - /* Read zeros after EOF of growable BDSes */ + /* Read zeros after EOF */ int64_t total_sectors, max_nb_sectors; total_sectors = bdrv_nb_sectors(bs); @@ -3019,18 +3086,16 @@ static int coroutine_fn bdrv_aligned_preadv(BlockDriverState *bs, max_nb_sectors = ROUND_UP(MAX(0, total_sectors - sector_num), align >> BDRV_SECTOR_BITS); - if (max_nb_sectors > 0) { + if (nb_sectors < max_nb_sectors) { + ret = drv->bdrv_co_readv(bs, sector_num, nb_sectors, qiov); + } else if (max_nb_sectors > 0) { QEMUIOVector local_qiov; - size_t local_sectors; - - max_nb_sectors = MIN(max_nb_sectors, SIZE_MAX / BDRV_SECTOR_BITS); - local_sectors = MIN(max_nb_sectors, nb_sectors); qemu_iovec_init(&local_qiov, qiov->niov); qemu_iovec_concat(&local_qiov, qiov, 0, - local_sectors * BDRV_SECTOR_SIZE); + max_nb_sectors * BDRV_SECTOR_SIZE); - ret = drv->bdrv_co_readv(bs, sector_num, local_sectors, + ret = drv->bdrv_co_readv(bs, sector_num, max_nb_sectors, &local_qiov); qemu_iovec_destroy(&local_qiov); @@ -3072,8 +3137,10 @@ static int coroutine_fn bdrv_co_do_preadv(BlockDriverState *bs, if (!drv) { return -ENOMEDIUM; } - if (bdrv_check_byte_request(bs, offset, bytes)) { - return -EIO; + + ret = bdrv_check_byte_request(bs, offset, bytes); + if (ret < 0) { + return ret; } if (bs->copy_on_read) { @@ -3129,7 +3196,7 @@ static int coroutine_fn bdrv_co_do_readv(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, BdrvRequestFlags flags) { - if (nb_sectors < 0 || nb_sectors > (UINT_MAX >> BDRV_SECTOR_BITS)) { + if (nb_sectors < 0 || nb_sectors > BDRV_REQUEST_MAX_SECTORS) { return -EINVAL; } @@ -3154,10 +3221,7 @@ int coroutine_fn bdrv_co_copy_on_readv(BlockDriverState *bs, BDRV_REQ_COPY_ON_READ); } -/* if no limit is specified in the BlockLimits use a default - * of 32768 512-byte sectors (16 MiB) per request. - */ -#define MAX_WRITE_ZEROES_DEFAULT 32768 +#define MAX_WRITE_ZEROES_BOUNCE_BUFFER 32768 static int coroutine_fn bdrv_co_do_write_zeroes(BlockDriverState *bs, int64_t sector_num, int nb_sectors, BdrvRequestFlags flags) @@ -3167,8 +3231,8 @@ static int coroutine_fn bdrv_co_do_write_zeroes(BlockDriverState *bs, struct iovec iov = {0}; int ret = 0; - int max_write_zeroes = bs->bl.max_write_zeroes ? - bs->bl.max_write_zeroes : MAX_WRITE_ZEROES_DEFAULT; + int max_write_zeroes = MIN_NON_ZERO(bs->bl.max_write_zeroes, + BDRV_REQUEST_MAX_SECTORS); while (nb_sectors > 0 && !ret) { int num = nb_sectors; @@ -3203,6 +3267,9 @@ static int coroutine_fn bdrv_co_do_write_zeroes(BlockDriverState *bs, if (ret == -ENOTSUP) { /* Fall back to bounce buffer if write zeroes is unsupported */ + int max_xfer_len = MIN_NON_ZERO(bs->bl.max_transfer_length, + MAX_WRITE_ZEROES_BOUNCE_BUFFER); + num = MIN(num, max_xfer_len); iov.iov_len = num * BDRV_SECTOR_SIZE; if (iov.iov_base == NULL) { iov.iov_base = qemu_try_blockalign(bs, num * BDRV_SECTOR_SIZE); @@ -3219,7 +3286,7 @@ static int coroutine_fn bdrv_co_do_write_zeroes(BlockDriverState *bs, /* Keep bounce buffer around if it is big enough for all * all future requests. */ - if (num < max_write_zeroes) { + if (num < max_xfer_len) { qemu_vfree(iov.iov_base); iov.iov_base = NULL; } @@ -3287,13 +3354,101 @@ static int coroutine_fn bdrv_aligned_pwritev(BlockDriverState *bs, block_acct_highest_sector(&bs->stats, sector_num, nb_sectors); - if (bs->growable && ret >= 0) { + if (ret >= 0) { bs->total_sectors = MAX(bs->total_sectors, sector_num + nb_sectors); } return ret; } +static int coroutine_fn bdrv_co_do_zero_pwritev(BlockDriverState *bs, + int64_t offset, + unsigned int bytes, + BdrvRequestFlags flags, + BdrvTrackedRequest *req) +{ + uint8_t *buf = NULL; + QEMUIOVector local_qiov; + struct iovec iov; + uint64_t align = MAX(BDRV_SECTOR_SIZE, bs->request_alignment); + unsigned int head_padding_bytes, tail_padding_bytes; + int ret = 0; + + head_padding_bytes = offset & (align - 1); + tail_padding_bytes = align - ((offset + bytes) & (align - 1)); + + + assert(flags & BDRV_REQ_ZERO_WRITE); + if (head_padding_bytes || tail_padding_bytes) { + buf = qemu_blockalign(bs, align); + iov = (struct iovec) { + .iov_base = buf, + .iov_len = align, + }; + qemu_iovec_init_external(&local_qiov, &iov, 1); + } + if (head_padding_bytes) { + uint64_t zero_bytes = MIN(bytes, align - head_padding_bytes); + + /* RMW the unaligned part before head. */ + mark_request_serialising(req, align); + wait_serialising_requests(req); + BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_HEAD); + ret = bdrv_aligned_preadv(bs, req, offset & ~(align - 1), align, + align, &local_qiov, 0); + if (ret < 0) { + goto fail; + } + BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_AFTER_HEAD); + + memset(buf + head_padding_bytes, 0, zero_bytes); + ret = bdrv_aligned_pwritev(bs, req, offset & ~(align - 1), align, + &local_qiov, + flags & ~BDRV_REQ_ZERO_WRITE); + if (ret < 0) { + goto fail; + } + offset += zero_bytes; + bytes -= zero_bytes; + } + + assert(!bytes || (offset & (align - 1)) == 0); + if (bytes >= align) { + /* Write the aligned part in the middle. */ + uint64_t aligned_bytes = bytes & ~(align - 1); + ret = bdrv_aligned_pwritev(bs, req, offset, aligned_bytes, + NULL, flags); + if (ret < 0) { + goto fail; + } + bytes -= aligned_bytes; + offset += aligned_bytes; + } + + assert(!bytes || (offset & (align - 1)) == 0); + if (bytes) { + assert(align == tail_padding_bytes + bytes); + /* RMW the unaligned part after tail. */ + mark_request_serialising(req, align); + wait_serialising_requests(req); + BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_TAIL); + ret = bdrv_aligned_preadv(bs, req, offset, align, + align, &local_qiov, 0); + if (ret < 0) { + goto fail; + } + BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL); + + memset(buf, 0, bytes); + ret = bdrv_aligned_pwritev(bs, req, offset, align, + &local_qiov, flags & ~BDRV_REQ_ZERO_WRITE); + } +fail: + qemu_vfree(buf); + return ret; + +} + /* * Handle a write request in coroutine context */ @@ -3316,8 +3471,10 @@ static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs, if (bs->read_only) { return -EACCES; } - if (bdrv_check_byte_request(bs, offset, bytes)) { - return -EIO; + + ret = bdrv_check_byte_request(bs, offset, bytes); + if (ret < 0) { + return ret; } /* throttling disk I/O */ @@ -3332,6 +3489,11 @@ static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs, */ tracked_request_begin(&req, bs, offset, bytes, true); + if (!qiov) { + ret = bdrv_co_do_zero_pwritev(bs, offset, bytes, flags, &req); + goto out; + } + if (offset & (align - 1)) { QEMUIOVector head_qiov; struct iovec head_iov; @@ -3405,14 +3567,14 @@ static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs, flags); fail: - tracked_request_end(&req); if (use_local_qiov) { qemu_iovec_destroy(&local_qiov); } qemu_vfree(head_buf); qemu_vfree(tail_buf); - +out: + tracked_request_end(&req); return ret; } @@ -3420,7 +3582,7 @@ static int coroutine_fn bdrv_co_do_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, BdrvRequestFlags flags) { - if (nb_sectors < 0 || nb_sectors > (INT_MAX >> BDRV_SECTOR_BITS)) { + if (nb_sectors < 0 || nb_sectors > BDRV_REQUEST_MAX_SECTORS) { return -EINVAL; } @@ -3678,6 +3840,36 @@ int bdrv_set_key(BlockDriverState *bs, const char *key) return ret; } +/* + * Provide an encryption key for @bs. + * If @key is non-null: + * If @bs is not encrypted, fail. + * Else if the key is invalid, fail. + * Else set @bs's key to @key, replacing the existing key, if any. + * If @key is null: + * If @bs is encrypted and still lacks a key, fail. + * Else do nothing. + * On failure, store an error object through @errp if non-null. + */ +void bdrv_add_key(BlockDriverState *bs, const char *key, Error **errp) +{ + if (key) { + if (!bdrv_is_encrypted(bs)) { + error_setg(errp, "Device '%s' is not encrypted", + bdrv_get_device_name(bs)); + } else if (bdrv_set_key(bs, key) < 0) { + error_set(errp, QERR_INVALID_PASSWORD); + } + } else { + if (bdrv_key_required(bs)) { + error_set(errp, ERROR_CLASS_DEVICE_ENCRYPTED, + "'%s' (%s) is encrypted", + bdrv_get_device_name(bs), + bdrv_get_encrypted_filename(bs)); + } + } +} + const char *bdrv_get_format_name(BlockDriverState *bs) { return bs->drv ? bs->drv->format_name : NULL; @@ -3720,15 +3912,6 @@ void bdrv_iterate_format(void (*it)(void *opaque, const char *name), g_free(formats); } -/* This function is to find block backend bs */ -/* TODO convert callers to blk_by_name(), then remove */ -BlockDriverState *bdrv_find(const char *name) -{ - BlockBackend *blk = blk_by_name(name); - - return blk ? blk_bs(blk) : NULL; -} - /* This function is to find a node in the bs graph */ BlockDriverState *bdrv_find_node(const char *node_name) { @@ -3801,6 +3984,14 @@ bool bdrv_chain_contains(BlockDriverState *top, BlockDriverState *base) return top != NULL; } +BlockDriverState *bdrv_next_node(BlockDriverState *bs) +{ + if (!bs) { + return QTAILQ_FIRST(&graph_bdrv_states); + } + return QTAILQ_NEXT(bs, node_list); +} + BlockDriverState *bdrv_next(BlockDriverState *bs) { if (!bs) { @@ -3809,6 +4000,11 @@ BlockDriverState *bdrv_next(BlockDriverState *bs) return QTAILQ_NEXT(bs, device_list); } +const char *bdrv_get_node_name(const BlockDriverState *bs) +{ + return bs->node_name; +} + /* TODO check what callers really want: bs->node_name or blk_name() */ const char *bdrv_get_device_name(const BlockDriverState *bs) { @@ -4004,28 +4200,54 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs, return ret; } -/* Coroutine wrapper for bdrv_get_block_status() */ -static void coroutine_fn bdrv_get_block_status_co_entry(void *opaque) +static int64_t coroutine_fn bdrv_co_get_block_status_above(BlockDriverState *bs, + BlockDriverState *base, + int64_t sector_num, + int nb_sectors, + int *pnum) +{ + BlockDriverState *p; + int64_t ret = 0; + + assert(bs != base); + for (p = bs; p != base; p = p->backing_hd) { + ret = bdrv_co_get_block_status(p, sector_num, nb_sectors, pnum); + if (ret < 0 || ret & BDRV_BLOCK_ALLOCATED) { + break; + } + /* [sector_num, pnum] unallocated on this layer, which could be only + * the first part of [sector_num, nb_sectors]. */ + nb_sectors = MIN(nb_sectors, *pnum); + } + return ret; +} + +/* Coroutine wrapper for bdrv_get_block_status_above() */ +static void coroutine_fn bdrv_get_block_status_above_co_entry(void *opaque) { BdrvCoGetBlockStatusData *data = opaque; - BlockDriverState *bs = data->bs; - data->ret = bdrv_co_get_block_status(bs, data->sector_num, data->nb_sectors, - data->pnum); + data->ret = bdrv_co_get_block_status_above(data->bs, data->base, + data->sector_num, + data->nb_sectors, + data->pnum); data->done = true; } /* - * Synchronous wrapper around bdrv_co_get_block_status(). + * Synchronous wrapper around bdrv_co_get_block_status_above(). * - * See bdrv_co_get_block_status() for details. + * See bdrv_co_get_block_status_above() for details. */ -int64_t bdrv_get_block_status(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, int *pnum) +int64_t bdrv_get_block_status_above(BlockDriverState *bs, + BlockDriverState *base, + int64_t sector_num, + int nb_sectors, int *pnum) { Coroutine *co; BdrvCoGetBlockStatusData data = { .bs = bs, + .base = base, .sector_num = sector_num, .nb_sectors = nb_sectors, .pnum = pnum, @@ -4034,11 +4256,11 @@ int64_t bdrv_get_block_status(BlockDriverState *bs, int64_t sector_num, if (qemu_in_coroutine()) { /* Fast-path if already in coroutine context */ - bdrv_get_block_status_co_entry(&data); + bdrv_get_block_status_above_co_entry(&data); } else { AioContext *aio_context = bdrv_get_aio_context(bs); - co = qemu_coroutine_create(bdrv_get_block_status_co_entry); + co = qemu_coroutine_create(bdrv_get_block_status_above_co_entry); qemu_coroutine_enter(co, &data); while (!data.done) { aio_poll(aio_context, true); @@ -4047,6 +4269,14 @@ int64_t bdrv_get_block_status(BlockDriverState *bs, int64_t sector_num, return data.ret; } +int64_t bdrv_get_block_status(BlockDriverState *bs, + int64_t sector_num, + int nb_sectors, int *pnum) +{ + return bdrv_get_block_status_above(bs, bs->backing_hd, + sector_num, nb_sectors, pnum); +} + int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum) { @@ -4128,12 +4358,18 @@ int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors) { BlockDriver *drv = bs->drv; - if (!drv) + int ret; + + if (!drv) { return -ENOMEDIUM; - if (!drv->bdrv_write_compressed) + } + if (!drv->bdrv_write_compressed) { return -ENOTSUP; - if (bdrv_check_request(bs, sector_num, nb_sectors)) - return -EIO; + } + ret = bdrv_check_request(bs, sector_num, nb_sectors); + if (ret < 0) { + return ret; + } assert(QLIST_EMPTY(&bs->dirty_bitmaps)); @@ -4508,6 +4744,8 @@ static int multiwrite_merge(BlockDriverState *bs, BlockRequest *reqs, } } + block_acct_merge_done(&bs->stats, BLOCK_ACCT_WRITE, num_reqs - outidx - 1); + return outidx + 1; } @@ -5043,26 +5281,22 @@ static void coroutine_fn bdrv_discard_co_entry(void *opaque) rwco->ret = bdrv_co_discard(rwco->bs, rwco->sector_num, rwco->nb_sectors); } -/* if no limit is specified in the BlockLimits use a default - * of 32768 512-byte sectors (16 MiB) per request. - */ -#define MAX_DISCARD_DEFAULT 32768 - int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors) { - int max_discard; + int max_discard, ret; if (!bs->drv) { return -ENOMEDIUM; - } else if (bdrv_check_request(bs, sector_num, nb_sectors)) { - return -EIO; + } + + ret = bdrv_check_request(bs, sector_num, nb_sectors); + if (ret < 0) { + return ret; } else if (bs->read_only) { return -EROFS; } - bdrv_reset_dirty(bs, sector_num, nb_sectors); - /* Do nothing if disabled. */ if (!(bs->open_flags & BDRV_O_UNMAP)) { return 0; @@ -5072,7 +5306,9 @@ int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num, return 0; } - max_discard = bs->bl.max_discard ? bs->bl.max_discard : MAX_DISCARD_DEFAULT; + bdrv_set_dirty(bs, sector_num, nb_sectors); + + max_discard = MIN_NON_ZERO(bs->bl.max_discard, BDRV_REQUEST_MAX_SECTORS); while (nb_sectors > 0) { int ret; int num = nb_sectors; @@ -5361,20 +5597,24 @@ void bdrv_dirty_iter_init(BlockDriverState *bs, hbitmap_iter_init(hbi, bitmap->bitmap, 0); } -void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector, - int nr_sectors) +void bdrv_set_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap, + int64_t cur_sector, int nr_sectors) { - BdrvDirtyBitmap *bitmap; - QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) { - hbitmap_set(bitmap->bitmap, cur_sector, nr_sectors); - } + hbitmap_set(bitmap->bitmap, cur_sector, nr_sectors); +} + +void bdrv_reset_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap, + int64_t cur_sector, int nr_sectors) +{ + hbitmap_reset(bitmap->bitmap, cur_sector, nr_sectors); } -void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector, int nr_sectors) +static void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector, + int nr_sectors) { BdrvDirtyBitmap *bitmap; QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) { - hbitmap_reset(bitmap->bitmap, cur_sector, nr_sectors); + hbitmap_set(bitmap->bitmap, cur_sector, nr_sectors); } } @@ -5535,9 +5775,20 @@ void bdrv_img_create(const char *filename, const char *fmt, return; } - proto_drv = bdrv_find_protocol(filename, true); + proto_drv = bdrv_find_protocol(filename, true, errp); if (!proto_drv) { - error_setg(errp, "Unknown protocol '%s'", filename); + return; + } + + if (!drv->create_opts) { + error_setg(errp, "Format driver '%s' does not support image creation", + drv->format_name); + return; + } + + if (!proto_drv->create_opts) { + error_setg(errp, "Protocol driver '%s' does not support image creation", + proto_drv->format_name); return; } @@ -5546,18 +5797,22 @@ void bdrv_img_create(const char *filename, const char *fmt, /* Create parameter list with default values */ opts = qemu_opts_create(create_opts, NULL, 0, &error_abort); - qemu_opt_set_number(opts, BLOCK_OPT_SIZE, img_size); + qemu_opt_set_number(opts, BLOCK_OPT_SIZE, img_size, &error_abort); /* Parse -o options */ if (options) { - if (qemu_opts_do_parse(opts, options, NULL) != 0) { + qemu_opts_do_parse(opts, options, NULL, &local_err); + if (local_err) { + error_report_err(local_err); + local_err = NULL; error_setg(errp, "Invalid options for file format '%s'", fmt); goto out; } } if (base_filename) { - if (qemu_opt_set(opts, BLOCK_OPT_BACKING_FILE, base_filename)) { + qemu_opt_set(opts, BLOCK_OPT_BACKING_FILE, base_filename, &local_err); + if (local_err) { error_setg(errp, "Backing file not supported for file format '%s'", fmt); goto out; @@ -5565,7 +5820,8 @@ void bdrv_img_create(const char *filename, const char *fmt, } if (base_fmt) { - if (qemu_opt_set(opts, BLOCK_OPT_BACKING_FMT, base_fmt)) { + qemu_opt_set(opts, BLOCK_OPT_BACKING_FMT, base_fmt, &local_err); + if (local_err) { error_setg(errp, "Backing file format not supported for file " "format '%s'", fmt); goto out; @@ -5597,16 +5853,26 @@ void bdrv_img_create(const char *filename, const char *fmt, if (size == -1) { if (backing_file) { BlockDriverState *bs; + char *full_backing = g_new0(char, PATH_MAX); int64_t size; int back_flags; + bdrv_get_full_backing_filename_from_filename(filename, backing_file, + full_backing, PATH_MAX, + &local_err); + if (local_err) { + g_free(full_backing); + goto out; + } + /* backing files always opened read-only */ back_flags = flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING); bs = NULL; - ret = bdrv_open(&bs, backing_file, NULL, NULL, back_flags, + ret = bdrv_open(&bs, full_backing, NULL, NULL, back_flags, backing_drv, &local_err); + g_free(full_backing); if (ret < 0) { goto out; } @@ -5618,7 +5884,7 @@ void bdrv_img_create(const char *filename, const char *fmt, goto out; } - qemu_opt_set_number(opts, BLOCK_OPT_SIZE, size); + qemu_opt_set_number(opts, BLOCK_OPT_SIZE, size, &error_abort); bdrv_unref(bs); } else { @@ -5628,8 +5894,8 @@ void bdrv_img_create(const char *filename, const char *fmt, } if (!quiet) { - printf("Formatting '%s', fmt=%s ", filename, fmt); - qemu_opts_print(opts); + printf("Formatting '%s', fmt=%s", filename, fmt); + qemu_opts_print(opts, " "); puts(""); } diff --git a/block/Makefile.objs b/block/Makefile.objs index 04b0e43..db2933e 100644 --- a/block/Makefile.objs +++ b/block/Makefile.objs @@ -20,6 +20,7 @@ block-obj-$(CONFIG_GLUSTERFS) += gluster.o block-obj-$(CONFIG_ARCHIPELAGO) += archipelago.o block-obj-$(CONFIG_LIBSSH2) += ssh.o block-obj-y += accounting.o +block-obj-y += write-threshold.o common-obj-y += stream.o common-obj-y += commit.o @@ -36,5 +37,6 @@ gluster.o-libs := $(GLUSTERFS_LIBS) ssh.o-cflags := $(LIBSSH2_CFLAGS) ssh.o-libs := $(LIBSSH2_LIBS) archipelago.o-libs := $(ARCHIPELAGO_LIBS) +dmg.o-libs := $(BZIP2_LIBS) qcow.o-libs := -lz linux-aio.o-libs := -laio diff --git a/block/accounting.c b/block/accounting.c index edbb1cc..01d594f 100644 --- a/block/accounting.c +++ b/block/accounting.c @@ -24,6 +24,7 @@ #include "block/accounting.h" #include "block/block_int.h" +#include "qemu/timer.h" void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie, int64_t bytes, enum BlockAcctType type) @@ -31,7 +32,7 @@ void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie, assert(type < BLOCK_MAX_IOTYPE); cookie->bytes = bytes; - cookie->start_time_ns = get_clock(); + cookie->start_time_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); cookie->type = type; } @@ -41,7 +42,8 @@ void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie) stats->nr_bytes[cookie->type] += cookie->bytes; stats->nr_ops[cookie->type]++; - stats->total_time_ns[cookie->type] += get_clock() - cookie->start_time_ns; + stats->total_time_ns[cookie->type] += + qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - cookie->start_time_ns; } @@ -52,3 +54,10 @@ void block_acct_highest_sector(BlockAcctStats *stats, int64_t sector_num, stats->wr_highest_sector = sector_num + nb_sectors - 1; } } + +void block_acct_merge_done(BlockAcctStats *stats, enum BlockAcctType type, + int num_requests) +{ + assert(type < BLOCK_MAX_IOTYPE); + stats->merged[type] += num_requests; +} diff --git a/block/archipelago.c b/block/archipelago.c index a8114b5..855655c 100644 --- a/block/archipelago.c +++ b/block/archipelago.c @@ -291,7 +291,7 @@ static int qemu_archipelago_init(BDRVArchipelagoState *s) ret = qemu_archipelago_xseg_init(s); if (ret < 0) { - error_report("Cannot initialize XSEG. Aborting...\n"); + error_report("Cannot initialize XSEG. Aborting..."); goto err_exit; } @@ -645,7 +645,7 @@ static int qemu_archipelago_create_volume(Error **errp, const char *volname, target = xseg_get_target(xseg, req); if (!target) { - error_setg(errp, "Cannot get XSEG target.\n"); + error_setg(errp, "Cannot get XSEG target."); goto err_exit; } memcpy(target, volname, targetlen); @@ -889,7 +889,7 @@ static BlockAIOCB *qemu_archipelago_aio_rw(BlockDriverState *bs, return &aio_cb->common; err_exit: - error_report("qemu_archipelago_aio_rw(): I/O Error\n"); + error_report("qemu_archipelago_aio_rw(): I/O Error"); qemu_aio_unref(aio_cb); return NULL; } diff --git a/block/backup.c b/block/backup.c index 792e655..1c535b1 100644 --- a/block/backup.c +++ b/block/backup.c @@ -360,6 +360,7 @@ static void coroutine_fn backup_run(void *opaque) hbitmap_free(job->bitmap); bdrv_iostatus_disable(target); + bdrv_op_unblock_all(target, job->common.blocker); data = g_malloc(sizeof(*data)); data->ret = ret; @@ -379,6 +380,11 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target, assert(target); assert(cb); + if (bs == target) { + error_setg(errp, "Source and target cannot be the same"); + return; + } + if ((on_source_error == BLOCKDEV_ON_ERROR_STOP || on_source_error == BLOCKDEV_ON_ERROR_ENOSPC) && !bdrv_iostatus_is_enabled(bs)) { @@ -386,6 +392,26 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target, return; } + if (!bdrv_is_inserted(bs)) { + error_setg(errp, "Device is not inserted: %s", + bdrv_get_device_name(bs)); + return; + } + + if (!bdrv_is_inserted(target)) { + error_setg(errp, "Device is not inserted: %s", + bdrv_get_device_name(target)); + return; + } + + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) { + return; + } + + if (bdrv_op_is_blocked(target, BLOCK_OP_TYPE_BACKUP_TARGET, errp)) { + return; + } + len = bdrv_getlength(bs); if (len < 0) { error_setg_errno(errp, -len, "unable to get length for '%s'", @@ -399,6 +425,8 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target, return; } + bdrv_op_block_all(target, job->common.blocker); + job->on_source_error = on_source_error; job->on_target_error = on_target_error; job->target = target; diff --git a/block/blkdebug.c b/block/blkdebug.c index 862d93b..63611e0 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -472,12 +472,14 @@ static BlockAIOCB *inject_error(BlockDriverState *bs, int error = rule->options.inject.error; struct BlkdebugAIOCB *acb; QEMUBH *bh; + bool immediately = rule->options.inject.immediately; if (rule->options.inject.once) { - QSIMPLEQ_INIT(&s->active_rules); + QSIMPLEQ_REMOVE(&s->active_rules, rule, BlkdebugRule, active_next); + remove_rule(rule); } - if (rule->options.inject.immediately) { + if (immediately) { return NULL; } @@ -721,93 +723,50 @@ static int64_t blkdebug_getlength(BlockDriverState *bs) static void blkdebug_refresh_filename(BlockDriverState *bs) { - BDRVBlkdebugState *s = bs->opaque; - struct BlkdebugRule *rule; QDict *opts; - QList *inject_error_list = NULL, *set_state_list = NULL; - QList *suspend_list = NULL; - int event; + const QDictEntry *e; + bool force_json = false; + + for (e = qdict_first(bs->options); e; e = qdict_next(bs->options, e)) { + if (strcmp(qdict_entry_key(e), "config") && + strcmp(qdict_entry_key(e), "x-image") && + strcmp(qdict_entry_key(e), "image") && + strncmp(qdict_entry_key(e), "image.", strlen("image."))) + { + force_json = true; + break; + } + } - if (!bs->file->full_open_options) { + if (force_json && !bs->file->full_open_options) { /* The config file cannot be recreated, so creating a plain filename * is impossible */ return; } + if (!force_json && bs->file->exact_filename[0]) { + snprintf(bs->exact_filename, sizeof(bs->exact_filename), + "blkdebug:%s:%s", + qdict_get_try_str(bs->options, "config") ?: "", + bs->file->exact_filename); + } + opts = qdict_new(); qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("blkdebug"))); QINCREF(bs->file->full_open_options); qdict_put_obj(opts, "image", QOBJECT(bs->file->full_open_options)); - for (event = 0; event < BLKDBG_EVENT_MAX; event++) { - QLIST_FOREACH(rule, &s->rules[event], next) { - if (rule->action == ACTION_INJECT_ERROR) { - QDict *inject_error = qdict_new(); - - qdict_put_obj(inject_error, "event", QOBJECT(qstring_from_str( - BlkdebugEvent_lookup[rule->event]))); - qdict_put_obj(inject_error, "state", - QOBJECT(qint_from_int(rule->state))); - qdict_put_obj(inject_error, "errno", QOBJECT(qint_from_int( - rule->options.inject.error))); - qdict_put_obj(inject_error, "sector", QOBJECT(qint_from_int( - rule->options.inject.sector))); - qdict_put_obj(inject_error, "once", QOBJECT(qbool_from_int( - rule->options.inject.once))); - qdict_put_obj(inject_error, "immediately", - QOBJECT(qbool_from_int( - rule->options.inject.immediately))); - - if (!inject_error_list) { - inject_error_list = qlist_new(); - } - - qlist_append_obj(inject_error_list, QOBJECT(inject_error)); - } else if (rule->action == ACTION_SET_STATE) { - QDict *set_state = qdict_new(); - - qdict_put_obj(set_state, "event", QOBJECT(qstring_from_str( - BlkdebugEvent_lookup[rule->event]))); - qdict_put_obj(set_state, "state", - QOBJECT(qint_from_int(rule->state))); - qdict_put_obj(set_state, "new_state", QOBJECT(qint_from_int( - rule->options.set_state.new_state))); - - if (!set_state_list) { - set_state_list = qlist_new(); - } - - qlist_append_obj(set_state_list, QOBJECT(set_state)); - } else if (rule->action == ACTION_SUSPEND) { - QDict *suspend = qdict_new(); - - qdict_put_obj(suspend, "event", QOBJECT(qstring_from_str( - BlkdebugEvent_lookup[rule->event]))); - qdict_put_obj(suspend, "state", - QOBJECT(qint_from_int(rule->state))); - qdict_put_obj(suspend, "tag", QOBJECT(qstring_from_str( - rule->options.suspend.tag))); - - if (!suspend_list) { - suspend_list = qlist_new(); - } - - qlist_append_obj(suspend_list, QOBJECT(suspend)); - } + for (e = qdict_first(bs->options); e; e = qdict_next(bs->options, e)) { + if (strcmp(qdict_entry_key(e), "x-image") && + strcmp(qdict_entry_key(e), "image") && + strncmp(qdict_entry_key(e), "image.", strlen("image."))) + { + qobject_incref(qdict_entry_value(e)); + qdict_put_obj(opts, qdict_entry_key(e), qdict_entry_value(e)); } } - if (inject_error_list) { - qdict_put_obj(opts, "inject-error", QOBJECT(inject_error_list)); - } - if (set_state_list) { - qdict_put_obj(opts, "set-state", QOBJECT(set_state_list)); - } - if (suspend_list) { - qdict_put_obj(opts, "suspend", QOBJECT(suspend_list)); - } - bs->full_open_options = opts; } diff --git a/block/block-backend.c b/block/block-backend.c index d0692b1..48b6e4c 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -31,6 +31,16 @@ struct BlockBackend { void *dev_opaque; }; +typedef struct BlockBackendAIOCB { + BlockAIOCB common; + QEMUBH *bh; + int ret; +} BlockBackendAIOCB; + +static const AIOCBInfo block_backend_aiocb_info = { + .aiocb_size = sizeof(BlockBackendAIOCB), +}; + static void drive_info_del(DriveInfo *dinfo); /* All the BlockBackends (except for hidden ones) */ @@ -91,6 +101,40 @@ BlockBackend *blk_new_with_bs(const char *name, Error **errp) return blk; } +/* + * Calls blk_new_with_bs() and then calls bdrv_open() on the BlockDriverState. + * + * Just as with bdrv_open(), after having called this function the reference to + * @options belongs to the block layer (even on failure). + * + * TODO: Remove @filename and @flags; it should be possible to specify a whole + * BDS tree just by specifying the @options QDict (or @reference, + * alternatively). At the time of adding this function, this is not possible, + * though, so callers of this function have to be able to specify @filename and + * @flags. + */ +BlockBackend *blk_new_open(const char *name, const char *filename, + const char *reference, QDict *options, int flags, + Error **errp) +{ + BlockBackend *blk; + int ret; + + blk = blk_new_with_bs(name, errp); + if (!blk) { + QDECREF(options); + return NULL; + } + + ret = bdrv_open(&blk->bs, filename, reference, options, flags, NULL, errp); + if (ret < 0) { + blk_unref(blk); + return NULL; + } + + return blk; +} + static void blk_delete(BlockBackend *blk) { assert(!blk->refcnt); @@ -101,7 +145,7 @@ static void blk_delete(BlockBackend *blk) bdrv_unref(blk->bs); blk->bs = NULL; } - /* Avoid double-remove after blk_hide_on_behalf_of_do_drive_del() */ + /* Avoid double-remove after blk_hide_on_behalf_of_hmp_drive_del() */ if (blk->name[0]) { QTAILQ_REMOVE(&blk_backends, blk, link); } @@ -162,7 +206,7 @@ BlockBackend *blk_next(BlockBackend *blk) /* * Return @blk's name, a non-null string. * Wart: the name is empty iff @blk has been hidden with - * blk_hide_on_behalf_of_do_drive_del(). + * blk_hide_on_behalf_of_hmp_drive_del(). */ const char *blk_name(BlockBackend *blk) { @@ -238,7 +282,7 @@ BlockBackend *blk_by_legacy_dinfo(DriveInfo *dinfo) * Strictly for use by do_drive_del(). * TODO get rid of it! */ -void blk_hide_on_behalf_of_do_drive_del(BlockBackend *blk) +void blk_hide_on_behalf_of_hmp_drive_del(BlockBackend *blk) { QTAILQ_REMOVE(&blk_backends, blk, link); blk->name[0] = 0; @@ -260,9 +304,6 @@ int blk_attach_dev(BlockBackend *blk, void *dev) blk_ref(blk); blk->dev = dev; bdrv_iostatus_reset(blk->bs); - - /* We're expecting I/O from the device so bump up coroutine pool size */ - qemu_coroutine_adjust_pool_size(COROUTINE_POOL_RESERVATION); return 0; } @@ -290,7 +331,6 @@ void blk_detach_dev(BlockBackend *blk, void *dev) blk->dev_ops = NULL; blk->dev_opaque = NULL; bdrv_set_guest_block_size(blk->bs, 512); - qemu_coroutine_adjust_pool_size(-COROUTINE_POOL_RESERVATION); blk_unref(blk); } @@ -398,39 +438,137 @@ void blk_iostatus_enable(BlockBackend *blk) bdrv_iostatus_enable(blk->bs); } +static int blk_check_byte_request(BlockBackend *blk, int64_t offset, + size_t size) +{ + int64_t len; + + if (size > INT_MAX) { + return -EIO; + } + + if (!blk_is_inserted(blk)) { + return -ENOMEDIUM; + } + + len = blk_getlength(blk); + if (len < 0) { + return len; + } + + if (offset < 0) { + return -EIO; + } + + if (offset > len || len - offset < size) { + return -EIO; + } + + return 0; +} + +static int blk_check_request(BlockBackend *blk, int64_t sector_num, + int nb_sectors) +{ + if (sector_num < 0 || sector_num > INT64_MAX / BDRV_SECTOR_SIZE) { + return -EIO; + } + + if (nb_sectors < 0 || nb_sectors > INT_MAX / BDRV_SECTOR_SIZE) { + return -EIO; + } + + return blk_check_byte_request(blk, sector_num * BDRV_SECTOR_SIZE, + nb_sectors * BDRV_SECTOR_SIZE); +} + int blk_read(BlockBackend *blk, int64_t sector_num, uint8_t *buf, int nb_sectors) { + int ret = blk_check_request(blk, sector_num, nb_sectors); + if (ret < 0) { + return ret; + } + return bdrv_read(blk->bs, sector_num, buf, nb_sectors); } int blk_read_unthrottled(BlockBackend *blk, int64_t sector_num, uint8_t *buf, int nb_sectors) { + int ret = blk_check_request(blk, sector_num, nb_sectors); + if (ret < 0) { + return ret; + } + return bdrv_read_unthrottled(blk->bs, sector_num, buf, nb_sectors); } int blk_write(BlockBackend *blk, int64_t sector_num, const uint8_t *buf, int nb_sectors) { + int ret = blk_check_request(blk, sector_num, nb_sectors); + if (ret < 0) { + return ret; + } + return bdrv_write(blk->bs, sector_num, buf, nb_sectors); } +static void error_callback_bh(void *opaque) +{ + struct BlockBackendAIOCB *acb = opaque; + qemu_bh_delete(acb->bh); + acb->common.cb(acb->common.opaque, acb->ret); + qemu_aio_unref(acb); +} + +static BlockAIOCB *abort_aio_request(BlockBackend *blk, BlockCompletionFunc *cb, + void *opaque, int ret) +{ + struct BlockBackendAIOCB *acb; + QEMUBH *bh; + + acb = blk_aio_get(&block_backend_aiocb_info, blk, cb, opaque); + acb->ret = ret; + + bh = aio_bh_new(blk_get_aio_context(blk), error_callback_bh, acb); + acb->bh = bh; + qemu_bh_schedule(bh); + + return &acb->common; +} + BlockAIOCB *blk_aio_write_zeroes(BlockBackend *blk, int64_t sector_num, int nb_sectors, BdrvRequestFlags flags, BlockCompletionFunc *cb, void *opaque) { + int ret = blk_check_request(blk, sector_num, nb_sectors); + if (ret < 0) { + return abort_aio_request(blk, cb, opaque, ret); + } + return bdrv_aio_write_zeroes(blk->bs, sector_num, nb_sectors, flags, cb, opaque); } int blk_pread(BlockBackend *blk, int64_t offset, void *buf, int count) { + int ret = blk_check_byte_request(blk, offset, count); + if (ret < 0) { + return ret; + } + return bdrv_pread(blk->bs, offset, buf, count); } int blk_pwrite(BlockBackend *blk, int64_t offset, const void *buf, int count) { + int ret = blk_check_byte_request(blk, offset, count); + if (ret < 0) { + return ret; + } + return bdrv_pwrite(blk->bs, offset, buf, count); } @@ -444,10 +582,20 @@ void blk_get_geometry(BlockBackend *blk, uint64_t *nb_sectors_ptr) bdrv_get_geometry(blk->bs, nb_sectors_ptr); } +int64_t blk_nb_sectors(BlockBackend *blk) +{ + return bdrv_nb_sectors(blk->bs); +} + BlockAIOCB *blk_aio_readv(BlockBackend *blk, int64_t sector_num, QEMUIOVector *iov, int nb_sectors, BlockCompletionFunc *cb, void *opaque) { + int ret = blk_check_request(blk, sector_num, nb_sectors); + if (ret < 0) { + return abort_aio_request(blk, cb, opaque, ret); + } + return bdrv_aio_readv(blk->bs, sector_num, iov, nb_sectors, cb, opaque); } @@ -455,6 +603,11 @@ BlockAIOCB *blk_aio_writev(BlockBackend *blk, int64_t sector_num, QEMUIOVector *iov, int nb_sectors, BlockCompletionFunc *cb, void *opaque) { + int ret = blk_check_request(blk, sector_num, nb_sectors); + if (ret < 0) { + return abort_aio_request(blk, cb, opaque, ret); + } + return bdrv_aio_writev(blk->bs, sector_num, iov, nb_sectors, cb, opaque); } @@ -468,6 +621,11 @@ BlockAIOCB *blk_aio_discard(BlockBackend *blk, int64_t sector_num, int nb_sectors, BlockCompletionFunc *cb, void *opaque) { + int ret = blk_check_request(blk, sector_num, nb_sectors); + if (ret < 0) { + return abort_aio_request(blk, cb, opaque, ret); + } + return bdrv_aio_discard(blk->bs, sector_num, nb_sectors, cb, opaque); } @@ -483,6 +641,15 @@ void blk_aio_cancel_async(BlockAIOCB *acb) int blk_aio_multiwrite(BlockBackend *blk, BlockRequest *reqs, int num_reqs) { + int i, ret; + + for (i = 0; i < num_reqs; i++) { + ret = blk_check_request(blk, reqs[i].sector, reqs[i].nb_sectors); + if (ret < 0) { + return ret; + } + } + return bdrv_aio_multiwrite(blk->bs, reqs, num_reqs); } @@ -497,6 +664,21 @@ BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf, return bdrv_aio_ioctl(blk->bs, req, buf, cb, opaque); } +int blk_co_discard(BlockBackend *blk, int64_t sector_num, int nb_sectors) +{ + int ret = blk_check_request(blk, sector_num, nb_sectors); + if (ret < 0) { + return ret; + } + + return bdrv_co_discard(blk->bs, sector_num, nb_sectors); +} + +int blk_co_flush(BlockBackend *blk) +{ + return bdrv_co_flush(blk->bs); +} + int blk_flush(BlockBackend *blk) { return bdrv_flush(blk->bs); @@ -549,6 +731,11 @@ void blk_set_enable_write_cache(BlockBackend *blk, bool wce) bdrv_set_enable_write_cache(blk->bs, wce); } +void blk_invalidate_cache(BlockBackend *blk, Error **errp) +{ + bdrv_invalidate_cache(blk->bs, errp); +} + int blk_is_inserted(BlockBackend *blk) { return bdrv_is_inserted(blk->bs); @@ -569,6 +756,11 @@ int blk_get_flags(BlockBackend *blk) return bdrv_get_flags(blk->bs); } +int blk_get_max_transfer_length(BlockBackend *blk) +{ + return blk->bs->bl.max_transfer_length; +} + void blk_set_guest_block_size(BlockBackend *blk, int align) { bdrv_set_guest_block_size(blk->bs, align); @@ -609,6 +801,29 @@ void blk_set_aio_context(BlockBackend *blk, AioContext *new_context) bdrv_set_aio_context(blk->bs, new_context); } +void blk_add_aio_context_notifier(BlockBackend *blk, + void (*attached_aio_context)(AioContext *new_context, void *opaque), + void (*detach_aio_context)(void *opaque), void *opaque) +{ + bdrv_add_aio_context_notifier(blk->bs, attached_aio_context, + detach_aio_context, opaque); +} + +void blk_remove_aio_context_notifier(BlockBackend *blk, + void (*attached_aio_context)(AioContext *, + void *), + void (*detach_aio_context)(void *), + void *opaque) +{ + bdrv_remove_aio_context_notifier(blk->bs, attached_aio_context, + detach_aio_context, opaque); +} + +void blk_add_close_notifier(BlockBackend *blk, Notifier *notify) +{ + bdrv_add_close_notifier(blk->bs, notify); +} + void blk_io_plug(BlockBackend *blk) { bdrv_io_plug(blk->bs); @@ -629,3 +844,61 @@ void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk, { return qemu_aio_get(aiocb_info, blk_bs(blk), cb, opaque); } + +int coroutine_fn blk_co_write_zeroes(BlockBackend *blk, int64_t sector_num, + int nb_sectors, BdrvRequestFlags flags) +{ + int ret = blk_check_request(blk, sector_num, nb_sectors); + if (ret < 0) { + return ret; + } + + return bdrv_co_write_zeroes(blk->bs, sector_num, nb_sectors, flags); +} + +int blk_write_compressed(BlockBackend *blk, int64_t sector_num, + const uint8_t *buf, int nb_sectors) +{ + int ret = blk_check_request(blk, sector_num, nb_sectors); + if (ret < 0) { + return ret; + } + + return bdrv_write_compressed(blk->bs, sector_num, buf, nb_sectors); +} + +int blk_truncate(BlockBackend *blk, int64_t offset) +{ + return bdrv_truncate(blk->bs, offset); +} + +int blk_discard(BlockBackend *blk, int64_t sector_num, int nb_sectors) +{ + int ret = blk_check_request(blk, sector_num, nb_sectors); + if (ret < 0) { + return ret; + } + + return bdrv_discard(blk->bs, sector_num, nb_sectors); +} + +int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf, + int64_t pos, int size) +{ + return bdrv_save_vmstate(blk->bs, buf, pos, size); +} + +int blk_load_vmstate(BlockBackend *blk, uint8_t *buf, int64_t pos, int size) +{ + return bdrv_load_vmstate(blk->bs, buf, pos, size); +} + +int blk_probe_blocksizes(BlockBackend *blk, BlockSizes *bsz) +{ + return bdrv_probe_blocksizes(blk->bs, bsz); +} + +int blk_probe_geometry(BlockBackend *blk, HDGeometry *geo) +{ + return bdrv_probe_geometry(blk->bs, geo); +} diff --git a/block/dmg.c b/block/dmg.c index e455886..825c49d 100644 --- a/block/dmg.c +++ b/block/dmg.c @@ -26,6 +26,10 @@ #include "qemu/bswap.h" #include "qemu/module.h" #include +#ifdef CONFIG_BZIP2 +#include +#endif +#include enum { /* Limit chunk sizes to prevent unreasonable amounts of memory being used @@ -55,6 +59,9 @@ typedef struct BDRVDMGState { uint8_t *compressed_chunk; uint8_t *uncompressed_chunk; z_stream zstream; +#ifdef CONFIG_BZIP2 + bz_stream bzstream; +#endif } BDRVDMGState; static int dmg_probe(const uint8_t *buf, int buf_size, const char *filename) @@ -100,6 +107,16 @@ static int read_uint32(BlockDriverState *bs, int64_t offset, uint32_t *result) return 0; } +static inline uint64_t buff_read_uint64(const uint8_t *buffer, int64_t offset) +{ + return be64_to_cpu(*(uint64_t *)&buffer[offset]); +} + +static inline uint32_t buff_read_uint32(const uint8_t *buffer, int64_t offset) +{ + return be32_to_cpu(*(uint32_t *)&buffer[offset]); +} + /* Increase max chunk sizes, if necessary. This function is used to calculate * the buffer sizes needed for compressed/uncompressed chunk I/O. */ @@ -112,6 +129,7 @@ static void update_max_chunk_size(BDRVDMGState *s, uint32_t chunk, switch (s->types[chunk]) { case 0x80000005: /* zlib compressed */ + case 0x80000006: /* bzip2 compressed */ compressed_size = s->lengths[chunk]; uncompressed_sectors = s->sectorcounts[chunk]; break; @@ -119,7 +137,9 @@ static void update_max_chunk_size(BDRVDMGState *s, uint32_t chunk, uncompressed_sectors = (s->lengths[chunk] + 511) / 512; break; case 2: /* zero */ - uncompressed_sectors = s->sectorcounts[chunk]; + /* as the all-zeroes block may be large, it is treated specially: the + * sector is not copied from a large buffer, a simple memset is used + * instead. Therefore uncompressed_sectors does not need to be set. */ break; } @@ -131,163 +151,372 @@ static void update_max_chunk_size(BDRVDMGState *s, uint32_t chunk, } } +static int64_t dmg_find_koly_offset(BlockDriverState *file_bs, Error **errp) +{ + int64_t length; + int64_t offset = 0; + uint8_t buffer[515]; + int i, ret; + + /* bdrv_getlength returns a multiple of block size (512), rounded up. Since + * dmg images can have odd sizes, try to look for the "koly" magic which + * marks the begin of the UDIF trailer (512 bytes). This magic can be found + * in the last 511 bytes of the second-last sector or the first 4 bytes of + * the last sector (search space: 515 bytes) */ + length = bdrv_getlength(file_bs); + if (length < 0) { + error_setg_errno(errp, -length, + "Failed to get file size while reading UDIF trailer"); + return length; + } else if (length < 512) { + error_setg(errp, "dmg file must be at least 512 bytes long"); + return -EINVAL; + } + if (length > 511 + 512) { + offset = length - 511 - 512; + } + length = length < 515 ? length : 515; + ret = bdrv_pread(file_bs, offset, buffer, length); + if (ret < 0) { + error_setg_errno(errp, -ret, "Failed while reading UDIF trailer"); + return ret; + } + for (i = 0; i < length - 3; i++) { + if (buffer[i] == 'k' && buffer[i+1] == 'o' && + buffer[i+2] == 'l' && buffer[i+3] == 'y') { + return offset + i; + } + } + error_setg(errp, "Could not locate UDIF trailer in dmg file"); + return -EINVAL; +} + +/* used when building the sector table */ +typedef struct DmgHeaderState { + /* used internally by dmg_read_mish_block to remember offsets of blocks + * across calls */ + uint64_t data_fork_offset; + /* exported for dmg_open */ + uint32_t max_compressed_size; + uint32_t max_sectors_per_chunk; +} DmgHeaderState; + +static bool dmg_is_known_block_type(uint32_t entry_type) +{ + switch (entry_type) { + case 0x00000001: /* uncompressed */ + case 0x00000002: /* zeroes */ + case 0x80000005: /* zlib */ +#ifdef CONFIG_BZIP2 + case 0x80000006: /* bzip2 */ +#endif + return true; + default: + return false; + } +} + +static int dmg_read_mish_block(BDRVDMGState *s, DmgHeaderState *ds, + uint8_t *buffer, uint32_t count) +{ + uint32_t type, i; + int ret; + size_t new_size; + uint32_t chunk_count; + int64_t offset = 0; + uint64_t data_offset; + uint64_t in_offset = ds->data_fork_offset; + uint64_t out_offset; + + type = buff_read_uint32(buffer, offset); + /* skip data that is not a valid MISH block (invalid magic or too small) */ + if (type != 0x6d697368 || count < 244) { + /* assume success for now */ + return 0; + } + + /* chunk offsets are relative to this sector number */ + out_offset = buff_read_uint64(buffer, offset + 8); + + /* location in data fork for (compressed) blob (in bytes) */ + data_offset = buff_read_uint64(buffer, offset + 0x18); + in_offset += data_offset; + + /* move to begin of chunk entries */ + offset += 204; + + chunk_count = (count - 204) / 40; + new_size = sizeof(uint64_t) * (s->n_chunks + chunk_count); + s->types = g_realloc(s->types, new_size / 2); + s->offsets = g_realloc(s->offsets, new_size); + s->lengths = g_realloc(s->lengths, new_size); + s->sectors = g_realloc(s->sectors, new_size); + s->sectorcounts = g_realloc(s->sectorcounts, new_size); + + for (i = s->n_chunks; i < s->n_chunks + chunk_count; i++) { + s->types[i] = buff_read_uint32(buffer, offset); + if (!dmg_is_known_block_type(s->types[i])) { + chunk_count--; + i--; + offset += 40; + continue; + } + + /* sector number */ + s->sectors[i] = buff_read_uint64(buffer, offset + 8); + s->sectors[i] += out_offset; + + /* sector count */ + s->sectorcounts[i] = buff_read_uint64(buffer, offset + 0x10); + + /* all-zeroes sector (type 2) does not need to be "uncompressed" and can + * therefore be unbounded. */ + if (s->types[i] != 2 && s->sectorcounts[i] > DMG_SECTORCOUNTS_MAX) { + error_report("sector count %" PRIu64 " for chunk %" PRIu32 + " is larger than max (%u)", + s->sectorcounts[i], i, DMG_SECTORCOUNTS_MAX); + ret = -EINVAL; + goto fail; + } + + /* offset in (compressed) data fork */ + s->offsets[i] = buff_read_uint64(buffer, offset + 0x18); + s->offsets[i] += in_offset; + + /* length in (compressed) data fork */ + s->lengths[i] = buff_read_uint64(buffer, offset + 0x20); + + if (s->lengths[i] > DMG_LENGTHS_MAX) { + error_report("length %" PRIu64 " for chunk %" PRIu32 + " is larger than max (%u)", + s->lengths[i], i, DMG_LENGTHS_MAX); + ret = -EINVAL; + goto fail; + } + + update_max_chunk_size(s, i, &ds->max_compressed_size, + &ds->max_sectors_per_chunk); + offset += 40; + } + s->n_chunks += chunk_count; + return 0; + +fail: + return ret; +} + +static int dmg_read_resource_fork(BlockDriverState *bs, DmgHeaderState *ds, + uint64_t info_begin, uint64_t info_length) +{ + BDRVDMGState *s = bs->opaque; + int ret; + uint32_t count, rsrc_data_offset; + uint8_t *buffer = NULL; + uint64_t info_end; + uint64_t offset; + + /* read offset from begin of resource fork (info_begin) to resource data */ + ret = read_uint32(bs, info_begin, &rsrc_data_offset); + if (ret < 0) { + goto fail; + } else if (rsrc_data_offset > info_length) { + ret = -EINVAL; + goto fail; + } + + /* read length of resource data */ + ret = read_uint32(bs, info_begin + 8, &count); + if (ret < 0) { + goto fail; + } else if (count == 0 || rsrc_data_offset + count > info_length) { + ret = -EINVAL; + goto fail; + } + + /* begin of resource data (consisting of one or more resources) */ + offset = info_begin + rsrc_data_offset; + + /* end of resource data (there is possibly a following resource map + * which will be ignored). */ + info_end = offset + count; + + /* read offsets (mish blocks) from one or more resources in resource data */ + while (offset < info_end) { + /* size of following resource */ + ret = read_uint32(bs, offset, &count); + if (ret < 0) { + goto fail; + } else if (count == 0 || count > info_end - offset) { + ret = -EINVAL; + goto fail; + } + offset += 4; + + buffer = g_realloc(buffer, count); + ret = bdrv_pread(bs->file, offset, buffer, count); + if (ret < 0) { + goto fail; + } + + ret = dmg_read_mish_block(s, ds, buffer, count); + if (ret < 0) { + goto fail; + } + /* advance offset by size of resource */ + offset += count; + } + ret = 0; + +fail: + g_free(buffer); + return ret; +} + +static int dmg_read_plist_xml(BlockDriverState *bs, DmgHeaderState *ds, + uint64_t info_begin, uint64_t info_length) +{ + BDRVDMGState *s = bs->opaque; + int ret; + uint8_t *buffer = NULL; + char *data_begin, *data_end; + + /* Have at least some length to avoid NULL for g_malloc. Attempt to set a + * safe upper cap on the data length. A test sample had a XML length of + * about 1 MiB. */ + if (info_length == 0 || info_length > 16 * 1024 * 1024) { + ret = -EINVAL; + goto fail; + } + + buffer = g_malloc(info_length + 1); + buffer[info_length] = '\0'; + ret = bdrv_pread(bs->file, info_begin, buffer, info_length); + if (ret != info_length) { + ret = -EINVAL; + goto fail; + } + + /* look for .... The data is 284 (0x11c) bytes after base64 + * decode. The actual data element has 431 (0x1af) bytes which includes tabs + * and line feeds. */ + data_end = (char *)buffer; + while ((data_begin = strstr(data_end, "")) != NULL) { + guchar *mish; + gsize out_len = 0; + + data_begin += 6; + data_end = strstr(data_begin, ""); + /* malformed XML? */ + if (data_end == NULL) { + ret = -EINVAL; + goto fail; + } + *data_end++ = '\0'; + mish = g_base64_decode(data_begin, &out_len); + ret = dmg_read_mish_block(s, ds, mish, (uint32_t)out_len); + g_free(mish); + if (ret < 0) { + goto fail; + } + } + ret = 0; + +fail: + g_free(buffer); + return ret; +} + static int dmg_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { BDRVDMGState *s = bs->opaque; - uint64_t info_begin, info_end, last_in_offset, last_out_offset; - uint32_t count, tmp; - uint32_t max_compressed_size = 1, max_sectors_per_chunk = 1, i; + DmgHeaderState ds; + uint64_t rsrc_fork_offset, rsrc_fork_length; + uint64_t plist_xml_offset, plist_xml_length; int64_t offset; int ret; bs->read_only = 1; s->n_chunks = 0; s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL; + /* used by dmg_read_mish_block to keep track of the current I/O position */ + ds.data_fork_offset = 0; + ds.max_compressed_size = 1; + ds.max_sectors_per_chunk = 1; - /* read offset of info blocks */ - offset = bdrv_getlength(bs->file); + /* locate the UDIF trailer */ + offset = dmg_find_koly_offset(bs->file, errp); if (offset < 0) { ret = offset; goto fail; } - offset -= 0x1d8; - ret = read_uint64(bs, offset, &info_begin); + /* offset of data fork (DataForkOffset) */ + ret = read_uint64(bs, offset + 0x18, &ds.data_fork_offset); if (ret < 0) { goto fail; - } else if (info_begin == 0) { + } else if (ds.data_fork_offset > offset) { ret = -EINVAL; goto fail; } - ret = read_uint32(bs, info_begin, &tmp); + /* offset of resource fork (RsrcForkOffset) */ + ret = read_uint64(bs, offset + 0x28, &rsrc_fork_offset); if (ret < 0) { goto fail; - } else if (tmp != 0x100) { + } + ret = read_uint64(bs, offset + 0x30, &rsrc_fork_length); + if (ret < 0) { + goto fail; + } + if (rsrc_fork_offset >= offset || + rsrc_fork_length > offset - rsrc_fork_offset) { ret = -EINVAL; goto fail; } - - ret = read_uint32(bs, info_begin + 4, &count); + /* offset of property list (XMLOffset) */ + ret = read_uint64(bs, offset + 0xd8, &plist_xml_offset); if (ret < 0) { goto fail; - } else if (count == 0) { + } + ret = read_uint64(bs, offset + 0xe0, &plist_xml_length); + if (ret < 0) { + goto fail; + } + if (plist_xml_offset >= offset || + plist_xml_length > offset - plist_xml_offset) { ret = -EINVAL; goto fail; } - info_end = info_begin + count; - - offset = info_begin + 0x100; - - /* read offsets */ - last_in_offset = last_out_offset = 0; - while (offset < info_end) { - uint32_t type; - - ret = read_uint32(bs, offset, &count); + ret = read_uint64(bs, offset + 0x1ec, (uint64_t *)&bs->total_sectors); + if (ret < 0) { + goto fail; + } + if (bs->total_sectors < 0) { + ret = -EINVAL; + goto fail; + } + if (rsrc_fork_length != 0) { + ret = dmg_read_resource_fork(bs, &ds, + rsrc_fork_offset, rsrc_fork_length); if (ret < 0) { goto fail; - } else if (count == 0) { - ret = -EINVAL; - goto fail; } - offset += 4; - - ret = read_uint32(bs, offset, &type); + } else if (plist_xml_length != 0) { + ret = dmg_read_plist_xml(bs, &ds, plist_xml_offset, plist_xml_length); if (ret < 0) { goto fail; } - - if (type == 0x6d697368 && count >= 244) { - size_t new_size; - uint32_t chunk_count; - - offset += 4; - offset += 200; - - chunk_count = (count - 204) / 40; - new_size = sizeof(uint64_t) * (s->n_chunks + chunk_count); - s->types = g_realloc(s->types, new_size / 2); - s->offsets = g_realloc(s->offsets, new_size); - s->lengths = g_realloc(s->lengths, new_size); - s->sectors = g_realloc(s->sectors, new_size); - s->sectorcounts = g_realloc(s->sectorcounts, new_size); - - for (i = s->n_chunks; i < s->n_chunks + chunk_count; i++) { - ret = read_uint32(bs, offset, &s->types[i]); - if (ret < 0) { - goto fail; - } - offset += 4; - if (s->types[i] != 0x80000005 && s->types[i] != 1 && - s->types[i] != 2) { - if (s->types[i] == 0xffffffff && i > 0) { - last_in_offset = s->offsets[i - 1] + s->lengths[i - 1]; - last_out_offset = s->sectors[i - 1] + - s->sectorcounts[i - 1]; - } - chunk_count--; - i--; - offset += 36; - continue; - } - offset += 4; - - ret = read_uint64(bs, offset, &s->sectors[i]); - if (ret < 0) { - goto fail; - } - s->sectors[i] += last_out_offset; - offset += 8; - - ret = read_uint64(bs, offset, &s->sectorcounts[i]); - if (ret < 0) { - goto fail; - } - offset += 8; - - if (s->sectorcounts[i] > DMG_SECTORCOUNTS_MAX) { - error_report("sector count %" PRIu64 " for chunk %" PRIu32 - " is larger than max (%u)", - s->sectorcounts[i], i, DMG_SECTORCOUNTS_MAX); - ret = -EINVAL; - goto fail; - } - - ret = read_uint64(bs, offset, &s->offsets[i]); - if (ret < 0) { - goto fail; - } - s->offsets[i] += last_in_offset; - offset += 8; - - ret = read_uint64(bs, offset, &s->lengths[i]); - if (ret < 0) { - goto fail; - } - offset += 8; - - if (s->lengths[i] > DMG_LENGTHS_MAX) { - error_report("length %" PRIu64 " for chunk %" PRIu32 - " is larger than max (%u)", - s->lengths[i], i, DMG_LENGTHS_MAX); - ret = -EINVAL; - goto fail; - } - - update_max_chunk_size(s, i, &max_compressed_size, - &max_sectors_per_chunk); - } - s->n_chunks += chunk_count; - } + } else { + ret = -EINVAL; + goto fail; } /* initialize zlib engine */ s->compressed_chunk = qemu_try_blockalign(bs->file, - max_compressed_size + 1); + ds.max_compressed_size + 1); s->uncompressed_chunk = qemu_try_blockalign(bs->file, - 512 * max_sectors_per_chunk); + 512 * ds.max_sectors_per_chunk); if (s->compressed_chunk == NULL || s->uncompressed_chunk == NULL) { ret = -ENOMEM; goto fail; @@ -349,13 +578,16 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) if (!is_sector_in_chunk(s, s->current_chunk, sector_num)) { int ret; uint32_t chunk = search_chunk(s, sector_num); +#ifdef CONFIG_BZIP2 + uint64_t total_out; +#endif if (chunk >= s->n_chunks) { return -1; } s->current_chunk = s->n_chunks; - switch (s->types[chunk]) { + switch (s->types[chunk]) { /* block entry type */ case 0x80000005: { /* zlib compressed */ /* we need to buffer, because only the chunk as whole can be * inflated. */ @@ -379,6 +611,34 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) return -1; } break; } +#ifdef CONFIG_BZIP2 + case 0x80000006: /* bzip2 compressed */ + /* we need to buffer, because only the chunk as whole can be + * inflated. */ + ret = bdrv_pread(bs->file, s->offsets[chunk], + s->compressed_chunk, s->lengths[chunk]); + if (ret != s->lengths[chunk]) { + return -1; + } + + ret = BZ2_bzDecompressInit(&s->bzstream, 0, 0); + if (ret != BZ_OK) { + return -1; + } + s->bzstream.next_in = (char *)s->compressed_chunk; + s->bzstream.avail_in = (unsigned int) s->lengths[chunk]; + s->bzstream.next_out = (char *)s->uncompressed_chunk; + s->bzstream.avail_out = (unsigned int) 512 * s->sectorcounts[chunk]; + ret = BZ2_bzDecompress(&s->bzstream); + total_out = ((uint64_t)s->bzstream.total_out_hi32 << 32) + + s->bzstream.total_out_lo32; + BZ2_bzDecompressEnd(&s->bzstream); + if (ret != BZ_STREAM_END || + total_out != 512 * s->sectorcounts[chunk]) { + return -1; + } + break; +#endif /* CONFIG_BZIP2 */ case 1: /* copy */ ret = bdrv_pread(bs->file, s->offsets[chunk], s->uncompressed_chunk, s->lengths[chunk]); @@ -387,7 +647,8 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) } break; case 2: /* zero */ - memset(s->uncompressed_chunk, 0, 512 * s->sectorcounts[chunk]); + /* see dmg_read, it is treated specially. No buffer needs to be + * pre-filled, the zeroes can be set directly. */ break; } s->current_chunk = chunk; @@ -406,6 +667,13 @@ static int dmg_read(BlockDriverState *bs, int64_t sector_num, if (dmg_read_chunk(bs, sector_num + i) != 0) { return -1; } + /* Special case: current chunk is all zeroes. Do not perform a memcpy as + * s->uncompressed_chunk may be too small to cover the large all-zeroes + * section. dmg_read_chunk is called to find s->current_chunk */ + if (s->types[s->current_chunk] == 2) { /* all zeroes block entry */ + memset(buf + i * 512, 0, 512); + continue; + } sector_offset_in_chunk = sector_num + i - s->sectors[s->current_chunk]; memcpy(buf + i * 512, s->uncompressed_chunk + sector_offset_in_chunk * 512, 512); diff --git a/block/iscsi.c b/block/iscsi.c index ed375fc..be8af46 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -56,6 +56,7 @@ typedef struct IscsiLun { uint64_t num_blocks; int events; QEMUTimer *nop_timer; + QEMUTimer *event_timer; uint8_t lbpme; uint8_t lbprz; uint8_t has_write_same; @@ -65,6 +66,7 @@ typedef struct IscsiLun { unsigned long *allocationmap; int cluster_sectors; bool use_16_for_rw; + bool write_protected; } IscsiLun; typedef struct IscsiTask { @@ -94,6 +96,7 @@ typedef struct IscsiAIOCB { #endif } IscsiAIOCB; +#define EVENT_INTERVAL 250 #define NOP_INTERVAL 5000 #define MAX_NOP_FAILURES 3 #define ISCSI_CMD_RETRIES ARRAY_SIZE(iscsi_retry_times) @@ -255,21 +258,30 @@ static void iscsi_set_events(IscsiLun *iscsilun) { struct iscsi_context *iscsi = iscsilun->iscsi; - int ev; + int ev = iscsi_which_events(iscsi); - /* We always register a read handler. */ - ev = POLLIN; - ev |= iscsi_which_events(iscsi); if (ev != iscsilun->events) { aio_set_fd_handler(iscsilun->aio_context, iscsi_get_fd(iscsi), - iscsi_process_read, + (ev & POLLIN) ? iscsi_process_read : NULL, (ev & POLLOUT) ? iscsi_process_write : NULL, iscsilun); + iscsilun->events = ev; + } + /* newer versions of libiscsi may return zero events. In this + * case start a timer to ensure we are able to return to service + * once this situation changes. */ + if (!ev) { + timer_mod(iscsilun->event_timer, + qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + EVENT_INTERVAL); } +} - iscsilun->events = ev; +static void iscsi_timed_set_events(void *opaque) +{ + IscsiLun *iscsilun = opaque; + iscsi_set_events(iscsilun); } static void @@ -1213,6 +1225,11 @@ static void iscsi_detach_aio_context(BlockDriverState *bs) timer_free(iscsilun->nop_timer); iscsilun->nop_timer = NULL; } + if (iscsilun->event_timer) { + timer_del(iscsilun->event_timer); + timer_free(iscsilun->event_timer); + iscsilun->event_timer = NULL; + } } static void iscsi_attach_aio_context(BlockDriverState *bs, @@ -1229,6 +1246,11 @@ static void iscsi_attach_aio_context(BlockDriverState *bs, iscsi_nop_timed_event, iscsilun); timer_mod(iscsilun->nop_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + NOP_INTERVAL); + + /* Prepare a timer for a delayed call to iscsi_set_events */ + iscsilun->event_timer = aio_timer_new(iscsilun->aio_context, + QEMU_CLOCK_REALTIME, SCALE_MS, + iscsi_timed_set_events, iscsilun); } static bool iscsi_is_write_protected(IscsiLun *iscsilun) @@ -1268,10 +1290,6 @@ out: /* * We support iscsi url's on the form * iscsi://[%@][:]// - * - * Note: flags are currently not used by iscsi_open. If this function - * is changed such that flags are used, please examine iscsi_reopen_prepare() - * to see if needs to be changed as well. */ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) @@ -1286,7 +1304,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, QemuOpts *opts; Error *local_err = NULL; const char *filename; - int i, ret; + int i, ret = 0; if ((BDRV_SECTOR_SIZE % 512) != 0) { error_setg(errp, "iSCSI: Invalid BDRV_SECTOR_SIZE. " @@ -1329,7 +1347,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, goto out; } - if (iscsi_url->user != NULL) { + if (iscsi_url->user[0] != '\0') { ret = iscsi_set_initiator_username_pwd(iscsi, iscsi_url->user, iscsi_url->passwd); if (ret != 0) { @@ -1385,9 +1403,10 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, scsi_free_scsi_task(task); task = NULL; + iscsilun->write_protected = iscsi_is_write_protected(iscsilun); /* Check the write protect flag of the LUN if we want to write */ if (iscsilun->type == TYPE_DISK && (flags & BDRV_O_RDWR) && - iscsi_is_write_protected(iscsilun)) { + iscsilun->write_protected) { error_setg(errp, "Cannot open a write protected LUN as read-write"); ret = -EACCES; goto out; @@ -1482,6 +1501,9 @@ out: if (ret) { if (iscsi != NULL) { + if (iscsi_is_logged_in(iscsi)) { + iscsi_logout_sync(iscsi); + } iscsi_destroy_context(iscsi); } memset(iscsilun, 0, sizeof(IscsiLun)); @@ -1495,6 +1517,9 @@ static void iscsi_close(BlockDriverState *bs) struct iscsi_context *iscsi = iscsilun->iscsi; iscsi_detach_aio_context(bs); + if (iscsi_is_logged_in(iscsi)) { + iscsi_logout_sync(iscsi); + } iscsi_destroy_context(iscsi); g_free(iscsilun->zeroblock); g_free(iscsilun->allocationmap); @@ -1541,13 +1566,17 @@ static void iscsi_refresh_limits(BlockDriverState *bs, Error **errp) sector_limits_lun2qemu(iscsilun->bl.opt_xfer_len, iscsilun); } -/* Since iscsi_open() ignores bdrv_flags, there is nothing to do here in - * prepare. Note that this will not re-establish a connection with an iSCSI - * target - it is effectively a NOP. */ +/* Note that this will not re-establish a connection with an iSCSI target - it + * is effectively a NOP. */ static int iscsi_reopen_prepare(BDRVReopenState *state, BlockReopenQueue *queue, Error **errp) { - /* NOP */ + IscsiLun *iscsilun = state->bs->opaque; + + if (state->flags & BDRV_O_RDWR && iscsilun->write_protected) { + error_setg(errp, "Cannot open a write protected LUN as read-write"); + return -EACCES; + } return 0; } diff --git a/block/linux-aio.c b/block/linux-aio.c index d92513b..c991443 100644 --- a/block/linux-aio.c +++ b/block/linux-aio.c @@ -35,14 +35,14 @@ struct qemu_laiocb { size_t nbytes; QEMUIOVector *qiov; bool is_read; - QLIST_ENTRY(qemu_laiocb) node; + QSIMPLEQ_ENTRY(qemu_laiocb) next; }; typedef struct { - struct iocb *iocbs[MAX_QUEUED_IO]; int plugged; - unsigned int size; - unsigned int idx; + unsigned int n; + bool blocked; + QSIMPLEQ_HEAD(, qemu_laiocb) pending; } LaioQueue; struct qemu_laio_state { @@ -59,6 +59,8 @@ struct qemu_laio_state { int event_max; }; +static void ioq_submit(struct qemu_laio_state *s); + static inline ssize_t io_event_ret(struct io_event *ev) { return (ssize_t)(((uint64_t)ev->res2 << 32) | ev->res); @@ -135,6 +137,10 @@ static void qemu_laio_completion_bh(void *opaque) qemu_laio_process_completion(s, laiocb); } + + if (!s->io_q.plugged && !QSIMPLEQ_EMPTY(&s->io_q.pending)) { + ioq_submit(s); + } } static void qemu_laio_completion_cb(EventNotifier *e) @@ -172,50 +178,41 @@ static const AIOCBInfo laio_aiocb_info = { static void ioq_init(LaioQueue *io_q) { - io_q->size = MAX_QUEUED_IO; - io_q->idx = 0; + QSIMPLEQ_INIT(&io_q->pending); io_q->plugged = 0; + io_q->n = 0; + io_q->blocked = false; } -static int ioq_submit(struct qemu_laio_state *s) +static void ioq_submit(struct qemu_laio_state *s) { - int ret, i = 0; - int len = s->io_q.idx; + int ret, len; + struct qemu_laiocb *aiocb; + struct iocb *iocbs[MAX_QUEUED_IO]; + QSIMPLEQ_HEAD(, qemu_laiocb) completed; do { - ret = io_submit(s->ctx, len, s->io_q.iocbs); - } while (i++ < 3 && ret == -EAGAIN); - - /* empty io queue */ - s->io_q.idx = 0; - - if (ret < 0) { - i = 0; - } else { - i = ret; - } - - for (; i < len; i++) { - struct qemu_laiocb *laiocb = - container_of(s->io_q.iocbs[i], struct qemu_laiocb, iocb); - - laiocb->ret = (ret < 0) ? ret : -EIO; - qemu_laio_process_completion(s, laiocb); - } - return ret; -} - -static void ioq_enqueue(struct qemu_laio_state *s, struct iocb *iocb) -{ - unsigned int idx = s->io_q.idx; + len = 0; + QSIMPLEQ_FOREACH(aiocb, &s->io_q.pending, next) { + iocbs[len++] = &aiocb->iocb; + if (len == MAX_QUEUED_IO) { + break; + } + } - s->io_q.iocbs[idx++] = iocb; - s->io_q.idx = idx; + ret = io_submit(s->ctx, len, iocbs); + if (ret == -EAGAIN) { + break; + } + if (ret < 0) { + abort(); + } - /* submit immediately if queue is full */ - if (idx == s->io_q.size) { - ioq_submit(s); - } + s->io_q.n -= ret; + aiocb = container_of(iocbs[ret - 1], struct qemu_laiocb, iocb); + QSIMPLEQ_SPLIT_AFTER(&s->io_q.pending, aiocb, next, &completed); + } while (ret == len && !QSIMPLEQ_EMPTY(&s->io_q.pending)); + s->io_q.blocked = (s->io_q.n > 0); } void laio_io_plug(BlockDriverState *bs, void *aio_ctx) @@ -225,22 +222,19 @@ void laio_io_plug(BlockDriverState *bs, void *aio_ctx) s->io_q.plugged++; } -int laio_io_unplug(BlockDriverState *bs, void *aio_ctx, bool unplug) +void laio_io_unplug(BlockDriverState *bs, void *aio_ctx, bool unplug) { struct qemu_laio_state *s = aio_ctx; - int ret = 0; assert(s->io_q.plugged > 0 || !unplug); if (unplug && --s->io_q.plugged > 0) { - return 0; + return; } - if (s->io_q.idx > 0) { - ret = ioq_submit(s); + if (!s->io_q.blocked && !QSIMPLEQ_EMPTY(&s->io_q.pending)) { + ioq_submit(s); } - - return ret; } BlockAIOCB *laio_submit(BlockDriverState *bs, void *aio_ctx, int fd, @@ -276,12 +270,11 @@ BlockAIOCB *laio_submit(BlockDriverState *bs, void *aio_ctx, int fd, } io_set_eventfd(&laiocb->iocb, event_notifier_get_fd(&s->e)); - if (!s->io_q.plugged) { - if (io_submit(s->ctx, 1, &iocbs) < 0) { - goto out_free_aiocb; - } - } else { - ioq_enqueue(s, iocbs); + QSIMPLEQ_INSERT_TAIL(&s->io_q.pending, laiocb, next); + s->io_q.n++; + if (!s->io_q.blocked && + (!s->io_q.plugged || s->io_q.n >= MAX_QUEUED_IO)) { + ioq_submit(s); } return &laiocb->common; diff --git a/block/mirror.c b/block/mirror.c index 2c6dd2a..bd079a4 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -57,6 +57,7 @@ typedef struct MirrorBlockJob { int in_flight; int sectors_in_flight; int ret; + bool unmap; } MirrorBlockJob; typedef struct MirrorOp { @@ -128,7 +129,8 @@ static void mirror_write_complete(void *opaque, int ret) BlockDriverState *source = s->common.bs; BlockErrorAction action; - bdrv_set_dirty(source, op->sector_num, op->nb_sectors); + bdrv_set_dirty_bitmap(source, s->dirty_bitmap, op->sector_num, + op->nb_sectors); action = mirror_error_action(s, false, -ret); if (action == BLOCK_ERROR_ACTION_REPORT && s->ret >= 0) { s->ret = ret; @@ -145,7 +147,8 @@ static void mirror_read_complete(void *opaque, int ret) BlockDriverState *source = s->common.bs; BlockErrorAction action; - bdrv_set_dirty(source, op->sector_num, op->nb_sectors); + bdrv_set_dirty_bitmap(source, s->dirty_bitmap, op->sector_num, + op->nb_sectors); action = mirror_error_action(s, true, -ret); if (action == BLOCK_ERROR_ACTION_REPORT && s->ret >= 0) { s->ret = ret; @@ -165,6 +168,8 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) int64_t end, sector_num, next_chunk, next_sector, hbitmap_next_sector; uint64_t delay_ns = 0; MirrorOp *op; + int pnum; + int64_t ret; s->sector_num = hbitmap_iter_next(&s->hbi); if (s->sector_num < 0) { @@ -286,14 +291,29 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) next_sector += sectors_per_chunk; } - bdrv_reset_dirty(source, sector_num, nb_sectors); + bdrv_reset_dirty_bitmap(source, s->dirty_bitmap, sector_num, + nb_sectors); /* Copy the dirty cluster. */ s->in_flight++; s->sectors_in_flight += nb_sectors; trace_mirror_one_iteration(s, sector_num, nb_sectors); - bdrv_aio_readv(source, sector_num, &op->qiov, nb_sectors, - mirror_read_complete, op); + + ret = bdrv_get_block_status_above(source, NULL, sector_num, + nb_sectors, &pnum); + if (ret < 0 || pnum < nb_sectors || + (ret & BDRV_BLOCK_DATA && !(ret & BDRV_BLOCK_ZERO))) { + bdrv_aio_readv(source, sector_num, &op->qiov, nb_sectors, + mirror_read_complete, op); + } else if (ret & BDRV_BLOCK_ZERO) { + bdrv_aio_write_zeroes(s->target, sector_num, op->nb_sectors, + s->unmap ? BDRV_REQ_MAY_UNMAP : 0, + mirror_write_complete, op); + } else { + assert(!(ret & BDRV_BLOCK_DATA)); + bdrv_aio_discard(s->target, sector_num, op->nb_sectors, + mirror_write_complete, op); + } return delay_ns; } @@ -375,7 +395,8 @@ static void coroutine_fn mirror_run(void *opaque) int64_t sector_num, end, sectors_per_chunk, length; uint64_t last_pause_ns; BlockDriverInfo bdi; - char backing_filename[1024]; + char backing_filename[2]; /* we only need 2 characters because we are only + checking for a NULL string */ int ret = 0; int n; @@ -442,7 +463,7 @@ static void coroutine_fn mirror_run(void *opaque) assert(n > 0); if (ret == 1) { - bdrv_set_dirty(bs, sector_num, n); + bdrv_set_dirty_bitmap(bs, s->dirty_bitmap, sector_num, n); sector_num = next; } else { sector_num += n; @@ -656,6 +677,7 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target, int64_t buf_size, BlockdevOnError on_source_error, BlockdevOnError on_target_error, + bool unmap, BlockCompletionFunc *cb, void *opaque, Error **errp, const BlockJobDriver *driver, @@ -698,6 +720,7 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target, s->base = base; s->granularity = granularity; s->buf_size = MAX(buf_size, granularity); + s->unmap = unmap; s->dirty_bitmap = bdrv_create_dirty_bitmap(bs, granularity, errp); if (!s->dirty_bitmap) { @@ -716,6 +739,7 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target, int64_t speed, int64_t granularity, int64_t buf_size, MirrorSyncMode mode, BlockdevOnError on_source_error, BlockdevOnError on_target_error, + bool unmap, BlockCompletionFunc *cb, void *opaque, Error **errp) { @@ -726,7 +750,7 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target, base = mode == MIRROR_SYNC_MODE_TOP ? bs->backing_hd : NULL; mirror_start_job(bs, target, replaces, speed, granularity, buf_size, - on_source_error, on_target_error, cb, opaque, errp, + on_source_error, on_target_error, unmap, cb, opaque, errp, &mirror_job_driver, is_none_mode, base); } @@ -774,7 +798,7 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *base, bdrv_ref(base); mirror_start_job(bs, base, NULL, speed, 0, 0, - on_error, on_error, cb, opaque, &local_err, + on_error, on_error, false, cb, opaque, &local_err, &commit_active_job_driver, false, base); if (local_err) { error_propagate(errp, local_err); diff --git a/block/nbd-client.c b/block/nbd-client.c index 6e1c97c..e1bb919 100644 --- a/block/nbd-client.c +++ b/block/nbd-client.c @@ -43,20 +43,23 @@ static void nbd_recv_coroutines_enter_all(NbdClientSession *s) } } -static void nbd_teardown_connection(NbdClientSession *client) +static void nbd_teardown_connection(BlockDriverState *bs) { + NbdClientSession *client = nbd_get_client_session(bs); + /* finish any pending coroutines */ shutdown(client->sock, 2); nbd_recv_coroutines_enter_all(client); - nbd_client_session_detach_aio_context(client); + nbd_client_detach_aio_context(bs); closesocket(client->sock); client->sock = -1; } static void nbd_reply_ready(void *opaque) { - NbdClientSession *s = opaque; + BlockDriverState *bs = opaque; + NbdClientSession *s = nbd_get_client_session(bs); uint64_t i; int ret; @@ -89,28 +92,40 @@ static void nbd_reply_ready(void *opaque) } fail: - nbd_teardown_connection(s); + nbd_teardown_connection(bs); } static void nbd_restart_write(void *opaque) { - NbdClientSession *s = opaque; + BlockDriverState *bs = opaque; - qemu_coroutine_enter(s->send_coroutine, NULL); + qemu_coroutine_enter(nbd_get_client_session(bs)->send_coroutine, NULL); } -static int nbd_co_send_request(NbdClientSession *s, - struct nbd_request *request, - QEMUIOVector *qiov, int offset) +static int nbd_co_send_request(BlockDriverState *bs, + struct nbd_request *request, + QEMUIOVector *qiov, int offset) { + NbdClientSession *s = nbd_get_client_session(bs); AioContext *aio_context; - int rc, ret; + int rc, ret, i; qemu_co_mutex_lock(&s->send_mutex); + + for (i = 0; i < MAX_NBD_REQUESTS; i++) { + if (s->recv_coroutine[i] == NULL) { + s->recv_coroutine[i] = qemu_coroutine_self(); + break; + } + } + + assert(i < MAX_NBD_REQUESTS); + request->handle = INDEX_TO_HANDLE(s, i); s->send_coroutine = qemu_coroutine_self(); - aio_context = bdrv_get_aio_context(s->bs); + aio_context = bdrv_get_aio_context(bs); + aio_set_fd_handler(aio_context, s->sock, - nbd_reply_ready, nbd_restart_write, s); + nbd_reply_ready, nbd_restart_write, bs); if (qiov) { if (!s->is_unix) { socket_set_cork(s->sock, 1); @@ -129,7 +144,7 @@ static int nbd_co_send_request(NbdClientSession *s, } else { rc = nbd_send_request(s->sock, request); } - aio_set_fd_handler(aio_context, s->sock, nbd_reply_ready, NULL, s); + aio_set_fd_handler(aio_context, s->sock, nbd_reply_ready, NULL, bs); s->send_coroutine = NULL; qemu_co_mutex_unlock(&s->send_mutex); return rc; @@ -164,8 +179,6 @@ static void nbd_co_receive_reply(NbdClientSession *s, static void nbd_coroutine_start(NbdClientSession *s, struct nbd_request *request) { - int i; - /* Poor man semaphore. The free_sema is locked when no other request * can be accepted, and unlocked after receiving one reply. */ if (s->in_flight >= MAX_NBD_REQUESTS - 1) { @@ -174,15 +187,7 @@ static void nbd_coroutine_start(NbdClientSession *s, } s->in_flight++; - for (i = 0; i < MAX_NBD_REQUESTS; i++) { - if (s->recv_coroutine[i] == NULL) { - s->recv_coroutine[i] = qemu_coroutine_self(); - break; - } - } - - assert(i < MAX_NBD_REQUESTS); - request->handle = INDEX_TO_HANDLE(s, i); + /* s->recv_coroutine[i] is set as soon as we get the send_lock. */ } static void nbd_coroutine_end(NbdClientSession *s, @@ -195,10 +200,11 @@ static void nbd_coroutine_end(NbdClientSession *s, } } -static int nbd_co_readv_1(NbdClientSession *client, int64_t sector_num, +static int nbd_co_readv_1(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, int offset) { + NbdClientSession *client = nbd_get_client_session(bs); struct nbd_request request = { .type = NBD_CMD_READ }; struct nbd_reply reply; ssize_t ret; @@ -207,7 +213,7 @@ static int nbd_co_readv_1(NbdClientSession *client, int64_t sector_num, request.len = nb_sectors * 512; nbd_coroutine_start(client, &request); - ret = nbd_co_send_request(client, &request, NULL, 0); + ret = nbd_co_send_request(bs, &request, NULL, 0); if (ret < 0) { reply.error = -ret; } else { @@ -218,15 +224,16 @@ static int nbd_co_readv_1(NbdClientSession *client, int64_t sector_num, } -static int nbd_co_writev_1(NbdClientSession *client, int64_t sector_num, +static int nbd_co_writev_1(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, int offset) { + NbdClientSession *client = nbd_get_client_session(bs); struct nbd_request request = { .type = NBD_CMD_WRITE }; struct nbd_reply reply; ssize_t ret; - if (!bdrv_enable_write_cache(client->bs) && + if (!bdrv_enable_write_cache(bs) && (client->nbdflags & NBD_FLAG_SEND_FUA)) { request.type |= NBD_CMD_FLAG_FUA; } @@ -235,7 +242,7 @@ static int nbd_co_writev_1(NbdClientSession *client, int64_t sector_num, request.len = nb_sectors * 512; nbd_coroutine_start(client, &request); - ret = nbd_co_send_request(client, &request, qiov, offset); + ret = nbd_co_send_request(bs, &request, qiov, offset); if (ret < 0) { reply.error = -ret; } else { @@ -249,14 +256,13 @@ static int nbd_co_writev_1(NbdClientSession *client, int64_t sector_num, * remain aligned to 4K. */ #define NBD_MAX_SECTORS 2040 -int nbd_client_session_co_readv(NbdClientSession *client, int64_t sector_num, - int nb_sectors, QEMUIOVector *qiov) +int nbd_client_co_readv(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov) { int offset = 0; int ret; while (nb_sectors > NBD_MAX_SECTORS) { - ret = nbd_co_readv_1(client, sector_num, - NBD_MAX_SECTORS, qiov, offset); + ret = nbd_co_readv_1(bs, sector_num, NBD_MAX_SECTORS, qiov, offset); if (ret < 0) { return ret; } @@ -264,17 +270,16 @@ int nbd_client_session_co_readv(NbdClientSession *client, int64_t sector_num, sector_num += NBD_MAX_SECTORS; nb_sectors -= NBD_MAX_SECTORS; } - return nbd_co_readv_1(client, sector_num, nb_sectors, qiov, offset); + return nbd_co_readv_1(bs, sector_num, nb_sectors, qiov, offset); } -int nbd_client_session_co_writev(NbdClientSession *client, int64_t sector_num, - int nb_sectors, QEMUIOVector *qiov) +int nbd_client_co_writev(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov) { int offset = 0; int ret; while (nb_sectors > NBD_MAX_SECTORS) { - ret = nbd_co_writev_1(client, sector_num, - NBD_MAX_SECTORS, qiov, offset); + ret = nbd_co_writev_1(bs, sector_num, NBD_MAX_SECTORS, qiov, offset); if (ret < 0) { return ret; } @@ -282,11 +287,12 @@ int nbd_client_session_co_writev(NbdClientSession *client, int64_t sector_num, sector_num += NBD_MAX_SECTORS; nb_sectors -= NBD_MAX_SECTORS; } - return nbd_co_writev_1(client, sector_num, nb_sectors, qiov, offset); + return nbd_co_writev_1(bs, sector_num, nb_sectors, qiov, offset); } -int nbd_client_session_co_flush(NbdClientSession *client) +int nbd_client_co_flush(BlockDriverState *bs) { + NbdClientSession *client = nbd_get_client_session(bs); struct nbd_request request = { .type = NBD_CMD_FLUSH }; struct nbd_reply reply; ssize_t ret; @@ -303,7 +309,7 @@ int nbd_client_session_co_flush(NbdClientSession *client) request.len = 0; nbd_coroutine_start(client, &request); - ret = nbd_co_send_request(client, &request, NULL, 0); + ret = nbd_co_send_request(bs, &request, NULL, 0); if (ret < 0) { reply.error = -ret; } else { @@ -313,9 +319,10 @@ int nbd_client_session_co_flush(NbdClientSession *client) return -reply.error; } -int nbd_client_session_co_discard(NbdClientSession *client, int64_t sector_num, - int nb_sectors) +int nbd_client_co_discard(BlockDriverState *bs, int64_t sector_num, + int nb_sectors) { + NbdClientSession *client = nbd_get_client_session(bs); struct nbd_request request = { .type = NBD_CMD_TRIM }; struct nbd_reply reply; ssize_t ret; @@ -327,7 +334,7 @@ int nbd_client_session_co_discard(NbdClientSession *client, int64_t sector_num, request.len = nb_sectors * 512; nbd_coroutine_start(client, &request); - ret = nbd_co_send_request(client, &request, NULL, 0); + ret = nbd_co_send_request(bs, &request, NULL, 0); if (ret < 0) { reply.error = -ret; } else { @@ -338,51 +345,48 @@ int nbd_client_session_co_discard(NbdClientSession *client, int64_t sector_num, } -void nbd_client_session_detach_aio_context(NbdClientSession *client) +void nbd_client_detach_aio_context(BlockDriverState *bs) { - aio_set_fd_handler(bdrv_get_aio_context(client->bs), client->sock, - NULL, NULL, NULL); + aio_set_fd_handler(bdrv_get_aio_context(bs), + nbd_get_client_session(bs)->sock, NULL, NULL, NULL); } -void nbd_client_session_attach_aio_context(NbdClientSession *client, - AioContext *new_context) +void nbd_client_attach_aio_context(BlockDriverState *bs, + AioContext *new_context) { - aio_set_fd_handler(new_context, client->sock, - nbd_reply_ready, NULL, client); + aio_set_fd_handler(new_context, nbd_get_client_session(bs)->sock, + nbd_reply_ready, NULL, bs); } -void nbd_client_session_close(NbdClientSession *client) +void nbd_client_close(BlockDriverState *bs) { + NbdClientSession *client = nbd_get_client_session(bs); struct nbd_request request = { .type = NBD_CMD_DISC, .from = 0, .len = 0 }; - if (!client->bs) { - return; - } if (client->sock == -1) { return; } nbd_send_request(client->sock, &request); - nbd_teardown_connection(client); - client->bs = NULL; + nbd_teardown_connection(bs); } -int nbd_client_session_init(NbdClientSession *client, BlockDriverState *bs, - int sock, const char *export) +int nbd_client_init(BlockDriverState *bs, int sock, const char *export, + Error **errp) { + NbdClientSession *client = nbd_get_client_session(bs); int ret; /* NBD handshake */ logout("session init %s\n", export); qemu_set_block(sock); ret = nbd_receive_negotiate(sock, export, - &client->nbdflags, &client->size, - &client->blocksize); + &client->nbdflags, &client->size, errp); if (ret < 0) { logout("Failed to negotiate with the NBD server\n"); closesocket(sock); @@ -391,13 +395,12 @@ int nbd_client_session_init(NbdClientSession *client, BlockDriverState *bs, qemu_co_mutex_init(&client->send_mutex); qemu_co_mutex_init(&client->free_sema); - client->bs = bs; client->sock = sock; /* Now that we're connected, set the socket to be non-blocking and * kick the reply mechanism. */ qemu_set_nonblock(sock); - nbd_client_session_attach_aio_context(client, bdrv_get_aio_context(bs)); + nbd_client_attach_aio_context(bs, bdrv_get_aio_context(bs)); logout("Established connection with NBD server\n"); return 0; diff --git a/block/nbd-client.h b/block/nbd-client.h index cd478f3..e841340 100644 --- a/block/nbd-client.h +++ b/block/nbd-client.h @@ -20,7 +20,6 @@ typedef struct NbdClientSession { int sock; uint32_t nbdflags; off_t size; - size_t blocksize; CoMutex send_mutex; CoMutex free_sema; @@ -31,24 +30,24 @@ typedef struct NbdClientSession { struct nbd_reply reply; bool is_unix; - - BlockDriverState *bs; } NbdClientSession; -int nbd_client_session_init(NbdClientSession *client, BlockDriverState *bs, - int sock, const char *export_name); -void nbd_client_session_close(NbdClientSession *client); - -int nbd_client_session_co_discard(NbdClientSession *client, int64_t sector_num, - int nb_sectors); -int nbd_client_session_co_flush(NbdClientSession *client); -int nbd_client_session_co_writev(NbdClientSession *client, int64_t sector_num, - int nb_sectors, QEMUIOVector *qiov); -int nbd_client_session_co_readv(NbdClientSession *client, int64_t sector_num, - int nb_sectors, QEMUIOVector *qiov); - -void nbd_client_session_detach_aio_context(NbdClientSession *client); -void nbd_client_session_attach_aio_context(NbdClientSession *client, - AioContext *new_context); +NbdClientSession *nbd_get_client_session(BlockDriverState *bs); + +int nbd_client_init(BlockDriverState *bs, int sock, const char *export_name, + Error **errp); +void nbd_client_close(BlockDriverState *bs); + +int nbd_client_co_discard(BlockDriverState *bs, int64_t sector_num, + int nb_sectors); +int nbd_client_co_flush(BlockDriverState *bs); +int nbd_client_co_writev(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov); +int nbd_client_co_readv(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov); + +void nbd_client_detach_aio_context(BlockDriverState *bs); +void nbd_client_attach_aio_context(BlockDriverState *bs, + AioContext *new_context); #endif /* NBD_CLIENT_H */ diff --git a/block/nbd.c b/block/nbd.c index 04cc845..2176186 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -215,7 +215,8 @@ static void nbd_config(BDRVNBDState *s, QDict *options, char **export, } if (!qemu_opt_get(s->socket_opts, "port")) { - qemu_opt_set_number(s->socket_opts, "port", NBD_DEFAULT_PORT); + qemu_opt_set_number(s->socket_opts, "port", NBD_DEFAULT_PORT, + &error_abort); } *export = g_strdup(qdict_get_try_str(options, "export")); @@ -224,6 +225,12 @@ static void nbd_config(BDRVNBDState *s, QDict *options, char **export, } } +NbdClientSession *nbd_get_client_session(BlockDriverState *bs) +{ + BDRVNBDState *s = bs->opaque; + return &s->client; +} + static int nbd_establish_connection(BlockDriverState *bs, Error **errp) { BDRVNBDState *s = bs->opaque; @@ -241,7 +248,7 @@ static int nbd_establish_connection(BlockDriverState *bs, Error **errp) /* Failed to establish connection */ if (sock < 0) { logout("Failed to establish connection to NBD server\n"); - return -errno; + return -EIO; } return sock; @@ -267,11 +274,12 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, */ sock = nbd_establish_connection(bs, errp); if (sock < 0) { + g_free(export); return sock; } /* NBD handshake */ - result = nbd_client_session_init(&s->client, bs, sock, export); + result = nbd_client_init(bs, sock, export, errp); g_free(export); return result; } @@ -279,35 +287,30 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, static int nbd_co_readv(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) { - BDRVNBDState *s = bs->opaque; - - return nbd_client_session_co_readv(&s->client, sector_num, - nb_sectors, qiov); + return nbd_client_co_readv(bs, sector_num, nb_sectors, qiov); } static int nbd_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) { - BDRVNBDState *s = bs->opaque; - - return nbd_client_session_co_writev(&s->client, sector_num, - nb_sectors, qiov); + return nbd_client_co_writev(bs, sector_num, nb_sectors, qiov); } static int nbd_co_flush(BlockDriverState *bs) { - BDRVNBDState *s = bs->opaque; + return nbd_client_co_flush(bs); +} - return nbd_client_session_co_flush(&s->client); +static void nbd_refresh_limits(BlockDriverState *bs, Error **errp) +{ + bs->bl.max_discard = UINT32_MAX >> BDRV_SECTOR_BITS; + bs->bl.max_transfer_length = UINT32_MAX >> BDRV_SECTOR_BITS; } static int nbd_co_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors) { - BDRVNBDState *s = bs->opaque; - - return nbd_client_session_co_discard(&s->client, sector_num, - nb_sectors); + return nbd_client_co_discard(bs, sector_num, nb_sectors); } static void nbd_close(BlockDriverState *bs) @@ -315,7 +318,7 @@ static void nbd_close(BlockDriverState *bs) BDRVNBDState *s = bs->opaque; qemu_opts_del(s->socket_opts); - nbd_client_session_close(&s->client); + nbd_client_close(bs); } static int64_t nbd_getlength(BlockDriverState *bs) @@ -327,17 +330,13 @@ static int64_t nbd_getlength(BlockDriverState *bs) static void nbd_detach_aio_context(BlockDriverState *bs) { - BDRVNBDState *s = bs->opaque; - - nbd_client_session_detach_aio_context(&s->client); + nbd_client_detach_aio_context(bs); } static void nbd_attach_aio_context(BlockDriverState *bs, AioContext *new_context) { - BDRVNBDState *s = bs->opaque; - - nbd_client_session_attach_aio_context(&s->client, new_context); + nbd_client_attach_aio_context(bs, new_context); } static void nbd_refresh_filename(BlockDriverState *bs) @@ -396,6 +395,7 @@ static BlockDriver bdrv_nbd = { .bdrv_close = nbd_close, .bdrv_co_flush_to_os = nbd_co_flush, .bdrv_co_discard = nbd_co_discard, + .bdrv_refresh_limits = nbd_refresh_limits, .bdrv_getlength = nbd_getlength, .bdrv_detach_aio_context = nbd_detach_aio_context, .bdrv_attach_aio_context = nbd_attach_aio_context, @@ -413,6 +413,7 @@ static BlockDriver bdrv_nbd_tcp = { .bdrv_close = nbd_close, .bdrv_co_flush_to_os = nbd_co_flush, .bdrv_co_discard = nbd_co_discard, + .bdrv_refresh_limits = nbd_refresh_limits, .bdrv_getlength = nbd_getlength, .bdrv_detach_aio_context = nbd_detach_aio_context, .bdrv_attach_aio_context = nbd_attach_aio_context, @@ -430,6 +431,7 @@ static BlockDriver bdrv_nbd_unix = { .bdrv_close = nbd_close, .bdrv_co_flush_to_os = nbd_co_flush, .bdrv_co_discard = nbd_co_discard, + .bdrv_refresh_limits = nbd_refresh_limits, .bdrv_getlength = nbd_getlength, .bdrv_detach_aio_context = nbd_detach_aio_context, .bdrv_attach_aio_context = nbd_attach_aio_context, diff --git a/block/nfs.c b/block/nfs.c index c76e368..c026ff6 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -35,6 +35,8 @@ #include "sysemu/sysemu.h" #include +#define QEMU_NFS_MAX_READAHEAD_SIZE 1048576 + typedef struct NFSClient { struct nfs_context *context; struct nfsfh *fh; @@ -327,6 +329,11 @@ static int64_t nfs_client_open(NFSClient *client, const char *filename, nfs_set_tcp_syncnt(client->context, val); #ifdef LIBNFS_FEATURE_READAHEAD } else if (!strcmp(qp->p[i].name, "readahead")) { + if (val > QEMU_NFS_MAX_READAHEAD_SIZE) { + error_report("NFS Warning: Truncating NFS readahead" + " size to %d", QEMU_NFS_MAX_READAHEAD_SIZE); + val = QEMU_NFS_MAX_READAHEAD_SIZE; + } nfs_set_readahead(client->context, val); #endif } else { @@ -409,6 +416,19 @@ out: return ret; } +static QemuOptsList nfs_create_opts = { + .name = "nfs-create-opts", + .head = QTAILQ_HEAD_INITIALIZER(nfs_create_opts.head), + .desc = { + { + .name = BLOCK_OPT_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Virtual disk size" + }, + { /* end of list */ } + } +}; + static int nfs_file_create(const char *url, QemuOpts *opts, Error **errp) { int ret = 0; @@ -470,6 +490,8 @@ static BlockDriver bdrv_nfs = { .instance_size = sizeof(NFSClient), .bdrv_needs_filename = true, + .create_opts = &nfs_create_opts, + .bdrv_has_zero_init = nfs_has_zero_init, .bdrv_get_allocated_file_size = nfs_get_allocated_file_size, .bdrv_truncate = nfs_file_truncate, diff --git a/block/qapi.c b/block/qapi.c index a87a34a..8a19aed 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -24,6 +24,7 @@ #include "block/qapi.h" #include "block/block_int.h" +#include "block/write-threshold.h" #include "qmp-commands.h" #include "qapi-visit.h" #include "qapi/qmp-output-visitor.h" @@ -40,6 +41,13 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs) info->encrypted = bs->encrypted; info->encryption_key_missing = bdrv_key_required(bs); + info->cache = g_new(BlockdevCacheInfo, 1); + *info->cache = (BlockdevCacheInfo) { + .writeback = bdrv_enable_write_cache(bs), + .direct = !!(bs->open_flags & BDRV_O_NOCACHE), + .no_flush = !!(bs->open_flags & BDRV_O_NO_FLUSH), + }; + if (bs->node_name[0]) { info->has_node_name = true; info->node_name = g_strdup(bs->node_name); @@ -82,6 +90,8 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs) info->iops_size = cfg.op_size; } + info->write_threshold = bdrv_write_threshold_get(bs); + return info; } @@ -168,7 +178,6 @@ void bdrv_query_image_info(BlockDriverState *bs, { int64_t size; const char *backing_filename; - char backing_filename2[1024]; BlockDriverInfo bdi; int ret; Error *err = NULL; @@ -204,10 +213,16 @@ void bdrv_query_image_info(BlockDriverState *bs, backing_filename = bs->backing_file; if (backing_filename[0] != '\0') { + char *backing_filename2 = g_malloc0(PATH_MAX); info->backing_filename = g_strdup(backing_filename); info->has_backing_filename = true; - bdrv_get_full_backing_filename(bs, backing_filename2, - sizeof(backing_filename2)); + bdrv_get_full_backing_filename(bs, backing_filename2, PATH_MAX, &err); + if (err) { + error_propagate(errp, err); + qapi_free_ImageInfo(info); + g_free(backing_filename2); + return; + } if (strcmp(backing_filename, backing_filename2) != 0) { info->full_backing_filename = @@ -219,6 +234,7 @@ void bdrv_query_image_info(BlockDriverState *bs, info->backing_filename_format = g_strdup(bs->backing_format); info->has_backing_filename_format = true; } + g_free(backing_filename2); } ret = bdrv_query_snapshot_info_list(bs, &info->snapshots, &err); @@ -300,7 +316,8 @@ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info, qapi_free_BlockInfo(info); } -static BlockStats *bdrv_query_stats(const BlockDriverState *bs) +static BlockStats *bdrv_query_stats(const BlockDriverState *bs, + bool query_backing) { BlockStats *s; @@ -311,11 +328,18 @@ static BlockStats *bdrv_query_stats(const BlockDriverState *bs) s->device = g_strdup(bdrv_get_device_name(bs)); } + if (bdrv_get_node_name(bs)[0]) { + s->has_node_name = true; + s->node_name = g_strdup(bdrv_get_node_name(bs)); + } + s->stats = g_malloc0(sizeof(*s->stats)); s->stats->rd_bytes = bs->stats.nr_bytes[BLOCK_ACCT_READ]; s->stats->wr_bytes = bs->stats.nr_bytes[BLOCK_ACCT_WRITE]; s->stats->rd_operations = bs->stats.nr_ops[BLOCK_ACCT_READ]; s->stats->wr_operations = bs->stats.nr_ops[BLOCK_ACCT_WRITE]; + s->stats->rd_merged = bs->stats.merged[BLOCK_ACCT_READ]; + s->stats->wr_merged = bs->stats.merged[BLOCK_ACCT_WRITE]; s->stats->wr_highest_offset = bs->stats.wr_highest_sector * BDRV_SECTOR_SIZE; s->stats->flush_operations = bs->stats.nr_ops[BLOCK_ACCT_FLUSH]; @@ -325,12 +349,12 @@ static BlockStats *bdrv_query_stats(const BlockDriverState *bs) if (bs->file) { s->has_parent = true; - s->parent = bdrv_query_stats(bs->file); + s->parent = bdrv_query_stats(bs->file, query_backing); } - if (bs->backing_hd) { + if (query_backing && bs->backing_hd) { s->has_backing = true; - s->backing = bdrv_query_stats(bs->backing_hd); + s->backing = bdrv_query_stats(bs->backing_hd, query_backing); } return s; @@ -361,17 +385,22 @@ BlockInfoList *qmp_query_block(Error **errp) return NULL; } -BlockStatsList *qmp_query_blockstats(Error **errp) +BlockStatsList *qmp_query_blockstats(bool has_query_nodes, + bool query_nodes, + Error **errp) { BlockStatsList *head = NULL, **p_next = &head; BlockDriverState *bs = NULL; - while ((bs = bdrv_next(bs))) { + /* Just to be safe if query_nodes is not always initialized */ + query_nodes = has_query_nodes && query_nodes; + + while ((bs = query_nodes ? bdrv_next_node(bs) : bdrv_next(bs))) { BlockStatsList *info = g_malloc0(sizeof(*info)); AioContext *ctx = bdrv_get_aio_context(bs); aio_context_acquire(ctx); - info->value = bdrv_query_stats(bs); + info->value = bdrv_query_stats(bs, !query_nodes); aio_context_release(ctx); *p_next = info; @@ -385,7 +414,7 @@ BlockStatsList *qmp_query_blockstats(Error **errp) static char *get_human_readable_size(char *buf, int buf_size, int64_t size) { - static const char suffixes[NB_SUFFIXES] = "KMGT"; + static const char suffixes[NB_SUFFIXES] = {'K', 'M', 'G', 'T'}; int64_t base; int i; diff --git a/block/qcow.c b/block/qcow.c index ece2269..0558969 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -215,7 +215,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, /* read the backing file name */ if (header.backing_file_offset != 0) { len = header.backing_file_size; - if (len > 1023) { + if (len > 1023 || len >= sizeof(bs->backing_file)) { error_setg(errp, "Backing file name too long"); ret = -EINVAL; goto fail; diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c index 904f6b1..b115549 100644 --- a/block/qcow2-cache.c +++ b/block/qcow2-cache.c @@ -253,7 +253,9 @@ static int qcow2_cache_find_entry_to_replace(Qcow2Cache *c) /* Give newer hits priority */ /* TODO Check how to optimize the replacement strategy */ - c->entries[i].cache_hits /= 2; + if (c->entries[i].cache_hits > 1) { + c->entries[i].cache_hits /= 2; + } } if (min_index == -1) { diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index df0b2c9..ed2b44d 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -1263,7 +1263,7 @@ int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, again: start = offset; - remaining = *num << BDRV_SECTOR_BITS; + remaining = (uint64_t)*num << BDRV_SECTOR_BITS; cluster_offset = 0; *host_offset = 0; cur_bytes = 0; @@ -1640,7 +1640,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, for (i = 0; i < l1_size; i++) { uint64_t l2_offset = l1_table[i] & L1E_OFFSET_MASK; bool l2_dirty = false; - int l2_refcount; + uint64_t l2_refcount; if (!l2_offset) { /* unallocated */ @@ -1651,6 +1651,14 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, continue; } + if (offset_into_cluster(s, l2_offset)) { + qcow2_signal_corruption(bs, true, -1, -1, "L2 table offset %#" + PRIx64 " unaligned (L1 index: %#x)", + l2_offset, i); + ret = -EIO; + goto fail; + } + if (is_active_l1) { /* get active L2 tables from cache */ ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset, @@ -1664,9 +1672,9 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, goto fail; } - l2_refcount = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits); - if (l2_refcount < 0) { - ret = l2_refcount; + ret = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits, + &l2_refcount); + if (ret < 0) { goto fail; } @@ -1699,7 +1707,8 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, /* For shared L2 tables, set the refcount accordingly (it is * already 1 and needs to be l2_refcount) */ ret = qcow2_update_cluster_refcount(bs, - offset >> s->cluster_bits, l2_refcount - 1, + offset >> s->cluster_bits, + refcount_diff(1, l2_refcount), false, QCOW2_DISCARD_OTHER); if (ret < 0) { qcow2_free_clusters(bs, offset, s->cluster_size, @@ -1709,6 +1718,19 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, } } + if (offset_into_cluster(s, offset)) { + qcow2_signal_corruption(bs, true, -1, -1, "Data cluster offset " + "%#" PRIx64 " unaligned (L2 offset: %#" + PRIx64 ", L2 index: %#x)", offset, + l2_offset, j); + if (!preallocated) { + qcow2_free_clusters(bs, offset, s->cluster_size, + QCOW2_DISCARD_ALWAYS); + } + ret = -EIO; + goto fail; + } + ret = qcow2_pre_write_overlap_check(bs, 0, offset, s->cluster_size); if (ret < 0) { if (!preallocated) { diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 9afdb40..63c0085 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -29,8 +29,52 @@ static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size); static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, - int64_t offset, int64_t length, - int addend, enum qcow2_discard_type type); + int64_t offset, int64_t length, uint64_t addend, + bool decrease, enum qcow2_discard_type type); + +static uint64_t get_refcount_ro0(const void *refcount_array, uint64_t index); +static uint64_t get_refcount_ro1(const void *refcount_array, uint64_t index); +static uint64_t get_refcount_ro2(const void *refcount_array, uint64_t index); +static uint64_t get_refcount_ro3(const void *refcount_array, uint64_t index); +static uint64_t get_refcount_ro4(const void *refcount_array, uint64_t index); +static uint64_t get_refcount_ro5(const void *refcount_array, uint64_t index); +static uint64_t get_refcount_ro6(const void *refcount_array, uint64_t index); + +static void set_refcount_ro0(void *refcount_array, uint64_t index, + uint64_t value); +static void set_refcount_ro1(void *refcount_array, uint64_t index, + uint64_t value); +static void set_refcount_ro2(void *refcount_array, uint64_t index, + uint64_t value); +static void set_refcount_ro3(void *refcount_array, uint64_t index, + uint64_t value); +static void set_refcount_ro4(void *refcount_array, uint64_t index, + uint64_t value); +static void set_refcount_ro5(void *refcount_array, uint64_t index, + uint64_t value); +static void set_refcount_ro6(void *refcount_array, uint64_t index, + uint64_t value); + + +static Qcow2GetRefcountFunc *const get_refcount_funcs[] = { + &get_refcount_ro0, + &get_refcount_ro1, + &get_refcount_ro2, + &get_refcount_ro3, + &get_refcount_ro4, + &get_refcount_ro5, + &get_refcount_ro6 +}; + +static Qcow2SetRefcountFunc *const set_refcount_funcs[] = { + &set_refcount_ro0, + &set_refcount_ro1, + &set_refcount_ro2, + &set_refcount_ro3, + &set_refcount_ro4, + &set_refcount_ro5, + &set_refcount_ro6 +}; /*********************************************************/ @@ -42,6 +86,11 @@ int qcow2_refcount_init(BlockDriverState *bs) unsigned int refcount_table_size2, i; int ret; + assert(s->refcount_order >= 0 && s->refcount_order <= 6); + + s->get_refcount = get_refcount_funcs[s->refcount_order]; + s->set_refcount = set_refcount_funcs[s->refcount_order]; + assert(s->refcount_table_size <= INT_MAX / sizeof(uint64_t)); refcount_table_size2 = s->refcount_table_size * sizeof(uint64_t); s->refcount_table = g_try_malloc(refcount_table_size2); @@ -72,6 +121,95 @@ void qcow2_refcount_close(BlockDriverState *bs) } +static uint64_t get_refcount_ro0(const void *refcount_array, uint64_t index) +{ + return (((const uint8_t *)refcount_array)[index / 8] >> (index % 8)) & 0x1; +} + +static void set_refcount_ro0(void *refcount_array, uint64_t index, + uint64_t value) +{ + assert(!(value >> 1)); + ((uint8_t *)refcount_array)[index / 8] &= ~(0x1 << (index % 8)); + ((uint8_t *)refcount_array)[index / 8] |= value << (index % 8); +} + +static uint64_t get_refcount_ro1(const void *refcount_array, uint64_t index) +{ + return (((const uint8_t *)refcount_array)[index / 4] >> (2 * (index % 4))) + & 0x3; +} + +static void set_refcount_ro1(void *refcount_array, uint64_t index, + uint64_t value) +{ + assert(!(value >> 2)); + ((uint8_t *)refcount_array)[index / 4] &= ~(0x3 << (2 * (index % 4))); + ((uint8_t *)refcount_array)[index / 4] |= value << (2 * (index % 4)); +} + +static uint64_t get_refcount_ro2(const void *refcount_array, uint64_t index) +{ + return (((const uint8_t *)refcount_array)[index / 2] >> (4 * (index % 2))) + & 0xf; +} + +static void set_refcount_ro2(void *refcount_array, uint64_t index, + uint64_t value) +{ + assert(!(value >> 4)); + ((uint8_t *)refcount_array)[index / 2] &= ~(0xf << (4 * (index % 2))); + ((uint8_t *)refcount_array)[index / 2] |= value << (4 * (index % 2)); +} + +static uint64_t get_refcount_ro3(const void *refcount_array, uint64_t index) +{ + return ((const uint8_t *)refcount_array)[index]; +} + +static void set_refcount_ro3(void *refcount_array, uint64_t index, + uint64_t value) +{ + assert(!(value >> 8)); + ((uint8_t *)refcount_array)[index] = value; +} + +static uint64_t get_refcount_ro4(const void *refcount_array, uint64_t index) +{ + return be16_to_cpu(((const uint16_t *)refcount_array)[index]); +} + +static void set_refcount_ro4(void *refcount_array, uint64_t index, + uint64_t value) +{ + assert(!(value >> 16)); + ((uint16_t *)refcount_array)[index] = cpu_to_be16(value); +} + +static uint64_t get_refcount_ro5(const void *refcount_array, uint64_t index) +{ + return be32_to_cpu(((const uint32_t *)refcount_array)[index]); +} + +static void set_refcount_ro5(void *refcount_array, uint64_t index, + uint64_t value) +{ + assert(!(value >> 32)); + ((uint32_t *)refcount_array)[index] = cpu_to_be32(value); +} + +static uint64_t get_refcount_ro6(const void *refcount_array, uint64_t index) +{ + return be64_to_cpu(((const uint64_t *)refcount_array)[index]); +} + +static void set_refcount_ro6(void *refcount_array, uint64_t index, + uint64_t value) +{ + ((uint64_t *)refcount_array)[index] = cpu_to_be64(value); +} + + static int load_refcount_block(BlockDriverState *bs, int64_t refcount_block_offset, void **refcount_block) @@ -87,26 +225,29 @@ static int load_refcount_block(BlockDriverState *bs, } /* - * Returns the refcount of the cluster given by its index. Any non-negative - * return value is the refcount of the cluster, negative values are -errno - * and indicate an error. + * Retrieves the refcount of the cluster given by its index and stores it in + * *refcount. Returns 0 on success and -errno on failure. */ -int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index) +int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index, + uint64_t *refcount) { BDRVQcowState *s = bs->opaque; uint64_t refcount_table_index, block_index; int64_t refcount_block_offset; int ret; - uint16_t *refcount_block; - uint16_t refcount; + void *refcount_block; refcount_table_index = cluster_index >> s->refcount_block_bits; - if (refcount_table_index >= s->refcount_table_size) + if (refcount_table_index >= s->refcount_table_size) { + *refcount = 0; return 0; + } refcount_block_offset = s->refcount_table[refcount_table_index] & REFT_OFFSET_MASK; - if (!refcount_block_offset) + if (!refcount_block_offset) { + *refcount = 0; return 0; + } if (offset_into_cluster(s, refcount_block_offset)) { qcow2_signal_corruption(bs, true, -1, -1, "Refblock offset %#" PRIx64 @@ -116,21 +257,20 @@ int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index) } ret = qcow2_cache_get(bs, s->refcount_block_cache, refcount_block_offset, - (void**) &refcount_block); + &refcount_block); if (ret < 0) { return ret; } block_index = cluster_index & (s->refcount_block_size - 1); - refcount = be16_to_cpu(refcount_block[block_index]); + *refcount = s->get_refcount(refcount_block, block_index); - ret = qcow2_cache_put(bs, s->refcount_block_cache, - (void**) &refcount_block); + ret = qcow2_cache_put(bs, s->refcount_block_cache, &refcount_block); if (ret < 0) { return ret; } - return refcount; + return 0; } /* @@ -169,7 +309,7 @@ static int in_same_refcount_block(BDRVQcowState *s, uint64_t offset_a, * Returns 0 on success or -errno in error case */ static int alloc_refcount_block(BlockDriverState *bs, - int64_t cluster_index, uint16_t **refcount_block) + int64_t cluster_index, void **refcount_block) { BDRVQcowState *s = bs->opaque; unsigned int refcount_table_index; @@ -196,7 +336,7 @@ static int alloc_refcount_block(BlockDriverState *bs, } return load_refcount_block(bs, refcount_block_offset, - (void**) refcount_block); + refcount_block); } } @@ -246,7 +386,7 @@ static int alloc_refcount_block(BlockDriverState *bs, if (in_same_refcount_block(s, new_block, cluster_index << s->cluster_bits)) { /* Zero the new refcount block before updating it */ ret = qcow2_cache_get_empty(bs, s->refcount_block_cache, new_block, - (void**) refcount_block); + refcount_block); if (ret < 0) { goto fail_block; } @@ -256,11 +396,11 @@ static int alloc_refcount_block(BlockDriverState *bs, /* The block describes itself, need to update the cache */ int block_index = (new_block >> s->cluster_bits) & (s->refcount_block_size - 1); - (*refcount_block)[block_index] = cpu_to_be16(1); + s->set_refcount(*refcount_block, block_index, 1); } else { /* Described somewhere else. This can recurse at most twice before we * arrive at a block that describes itself. */ - ret = update_refcount(bs, new_block, s->cluster_size, 1, + ret = update_refcount(bs, new_block, s->cluster_size, 1, false, QCOW2_DISCARD_NEVER); if (ret < 0) { goto fail_block; @@ -274,7 +414,7 @@ static int alloc_refcount_block(BlockDriverState *bs, /* Initialize the new refcount block only after updating its refcount, * update_refcount uses the refcount cache itself */ ret = qcow2_cache_get_empty(bs, s->refcount_block_cache, new_block, - (void**) refcount_block); + refcount_block); if (ret < 0) { goto fail_block; } @@ -308,7 +448,7 @@ static int alloc_refcount_block(BlockDriverState *bs, return -EAGAIN; } - ret = qcow2_cache_put(bs, s->refcount_block_cache, (void**) refcount_block); + ret = qcow2_cache_put(bs, s->refcount_block_cache, refcount_block); if (ret < 0) { goto fail_block; } @@ -326,8 +466,20 @@ static int alloc_refcount_block(BlockDriverState *bs, */ BLKDBG_EVENT(bs->file, BLKDBG_REFTABLE_GROW); - /* Calculate the number of refcount blocks needed so far */ - uint64_t blocks_used = DIV_ROUND_UP(cluster_index, s->refcount_block_size); + /* Calculate the number of refcount blocks needed so far; this will be the + * basis for calculating the index of the first cluster used for the + * self-describing refcount structures which we are about to create. + * + * Because we reached this point, there cannot be any refcount entries for + * cluster_index or higher indices yet. However, because new_block has been + * allocated to describe that cluster (and it will assume this role later + * on), we cannot use that index; also, new_block may actually have a higher + * cluster index than cluster_index, so it needs to be taken into account + * here (and 1 needs to be added to its value because that cluster is used). + */ + uint64_t blocks_used = DIV_ROUND_UP(MAX(cluster_index + 1, + (new_block >> s->cluster_bits) + 1), + s->refcount_block_size); if (blocks_used > QCOW_MAX_REFTABLE_SIZE / sizeof(uint64_t)) { return -EFBIG; @@ -362,7 +514,7 @@ static int alloc_refcount_block(BlockDriverState *bs, s->cluster_size; uint64_t table_offset = meta_offset + blocks_clusters * s->cluster_size; uint64_t *new_table = g_try_new0(uint64_t, table_size); - uint16_t *new_blocks = g_try_malloc0(blocks_clusters * s->cluster_size); + void *new_blocks = g_try_malloc0(blocks_clusters * s->cluster_size); assert(table_size > 0 && blocks_clusters > 0); if (new_table == NULL || new_blocks == NULL) { @@ -384,7 +536,7 @@ static int alloc_refcount_block(BlockDriverState *bs, uint64_t table_clusters = size_to_clusters(s, table_size * sizeof(uint64_t)); int block = 0; for (i = 0; i < table_clusters + blocks_clusters; i++) { - new_blocks[block++] = cpu_to_be16(1); + s->set_refcount(new_blocks, block++, 1); } /* Write refcount blocks to disk */ @@ -437,7 +589,7 @@ static int alloc_refcount_block(BlockDriverState *bs, qcow2_free_clusters(bs, old_table_offset, old_table_size * sizeof(uint64_t), QCOW2_DISCARD_OTHER); - ret = load_refcount_block(bs, new_block, (void**) refcount_block); + ret = load_refcount_block(bs, new_block, refcount_block); if (ret < 0) { return ret; } @@ -452,7 +604,7 @@ fail_table: g_free(new_table); fail_block: if (*refcount_block != NULL) { - qcow2_cache_put(bs, s->refcount_block_cache, (void**) refcount_block); + qcow2_cache_put(bs, s->refcount_block_cache, refcount_block); } return ret; } @@ -527,18 +679,25 @@ found: } /* XXX: cache several refcount block clusters ? */ +/* @addend is the absolute value of the addend; if @decrease is set, @addend + * will be subtracted from the current refcount, otherwise it will be added */ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, - int64_t offset, int64_t length, int addend, enum qcow2_discard_type type) + int64_t offset, + int64_t length, + uint64_t addend, + bool decrease, + enum qcow2_discard_type type) { BDRVQcowState *s = bs->opaque; int64_t start, last, cluster_offset; - uint16_t *refcount_block = NULL; + void *refcount_block = NULL; int64_t old_table_index = -1; int ret; #ifdef DEBUG_ALLOC2 - fprintf(stderr, "update_refcount: offset=%" PRId64 " size=%" PRId64 " addend=%d\n", - offset, length, addend); + fprintf(stderr, "update_refcount: offset=%" PRId64 " size=%" PRId64 + " addend=%s%" PRIu64 "\n", offset, length, decrease ? "-" : "", + addend); #endif if (length < 0) { return -EINVAL; @@ -546,7 +705,7 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, return 0; } - if (addend < 0) { + if (decrease) { qcow2_cache_set_dependency(bs, s->refcount_block_cache, s->l2_table_cache); } @@ -556,7 +715,8 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, for(cluster_offset = start; cluster_offset <= last; cluster_offset += s->cluster_size) { - int block_index, refcount; + int block_index; + uint64_t refcount; int64_t cluster_index = cluster_offset >> s->cluster_bits; int64_t table_index = cluster_index >> s->refcount_block_bits; @@ -564,7 +724,7 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, if (table_index != old_table_index) { if (refcount_block) { ret = qcow2_cache_put(bs, s->refcount_block_cache, - (void**) &refcount_block); + &refcount_block); if (ret < 0) { goto fail; } @@ -582,16 +742,23 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, /* we can update the count and save it */ block_index = cluster_index & (s->refcount_block_size - 1); - refcount = be16_to_cpu(refcount_block[block_index]); - refcount += addend; - if (refcount < 0 || refcount > 0xffff) { + refcount = s->get_refcount(refcount_block, block_index); + if (decrease ? (refcount - addend > refcount) + : (refcount + addend < refcount || + refcount + addend > s->refcount_max)) + { ret = -EINVAL; goto fail; } + if (decrease) { + refcount -= addend; + } else { + refcount += addend; + } if (refcount == 0 && cluster_index < s->free_cluster_index) { s->free_cluster_index = cluster_index; } - refcount_block[block_index] = cpu_to_be16(refcount); + s->set_refcount(refcount_block, block_index, refcount); if (refcount == 0 && s->discard_passthrough[type]) { update_refcount_discard(bs, cluster_offset, s->cluster_size); @@ -607,8 +774,7 @@ fail: /* Write last changed block to disk */ if (refcount_block) { int wret; - wret = qcow2_cache_put(bs, s->refcount_block_cache, - (void**) &refcount_block); + wret = qcow2_cache_put(bs, s->refcount_block_cache, &refcount_block); if (wret < 0) { return ret < 0 ? ret : wret; } @@ -620,8 +786,8 @@ fail: */ if (ret < 0) { int dummy; - dummy = update_refcount(bs, offset, cluster_offset - offset, -addend, - QCOW2_DISCARD_NEVER); + dummy = update_refcount(bs, offset, cluster_offset - offset, addend, + !decrease, QCOW2_DISCARD_NEVER); (void)dummy; } @@ -631,24 +797,26 @@ fail: /* * Increases or decreases the refcount of a given cluster. * - * If the return value is non-negative, it is the new refcount of the cluster. - * If it is negative, it is -errno and indicates an error. + * @addend is the absolute value of the addend; if @decrease is set, @addend + * will be subtracted from the current refcount, otherwise it will be added. + * + * On success 0 is returned; on failure -errno is returned. */ int qcow2_update_cluster_refcount(BlockDriverState *bs, int64_t cluster_index, - int addend, + uint64_t addend, bool decrease, enum qcow2_discard_type type) { BDRVQcowState *s = bs->opaque; int ret; ret = update_refcount(bs, cluster_index << s->cluster_bits, 1, addend, - type); + decrease, type); if (ret < 0) { return ret; } - return qcow2_get_refcount(bs, cluster_index); + return 0; } @@ -662,17 +830,22 @@ int qcow2_update_cluster_refcount(BlockDriverState *bs, static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size) { BDRVQcowState *s = bs->opaque; - uint64_t i, nb_clusters; - int refcount; + uint64_t i, nb_clusters, refcount; + int ret; + + /* We can't allocate clusters if they may still be queued for discard. */ + if (s->cache_discards) { + qcow2_process_discards(bs, 0); + } nb_clusters = size_to_clusters(s, size); retry: for(i = 0; i < nb_clusters; i++) { uint64_t next_cluster_index = s->free_cluster_index++; - refcount = qcow2_get_refcount(bs, next_cluster_index); + ret = qcow2_get_refcount(bs, next_cluster_index, &refcount); - if (refcount < 0) { - return refcount; + if (ret < 0) { + return ret; } else if (refcount != 0) { goto retry; } @@ -706,7 +879,7 @@ int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size) return offset; } - ret = update_refcount(bs, offset, size, 1, QCOW2_DISCARD_NEVER); + ret = update_refcount(bs, offset, size, 1, false, QCOW2_DISCARD_NEVER); } while (ret == -EAGAIN); if (ret < 0) { @@ -720,9 +893,9 @@ int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, int nb_clusters) { BDRVQcowState *s = bs->opaque; - uint64_t cluster_index; + uint64_t cluster_index, refcount; uint64_t i; - int refcount, ret; + int ret; assert(nb_clusters >= 0); if (nb_clusters == 0) { @@ -733,17 +906,16 @@ int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, /* Check how many clusters there are free */ cluster_index = offset >> s->cluster_bits; for(i = 0; i < nb_clusters; i++) { - refcount = qcow2_get_refcount(bs, cluster_index++); - - if (refcount < 0) { - return refcount; + ret = qcow2_get_refcount(bs, cluster_index++, &refcount); + if (ret < 0) { + return ret; } else if (refcount != 0) { break; } } /* And then allocate them */ - ret = update_refcount(bs, offset, i << s->cluster_bits, 1, + ret = update_refcount(bs, offset, i << s->cluster_bits, 1, false, QCOW2_DISCARD_NEVER); } while (ret == -EAGAIN); @@ -759,54 +931,55 @@ int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) { BDRVQcowState *s = bs->opaque; - int64_t offset, cluster_offset; - int free_in_cluster; + int64_t offset; + size_t free_in_cluster; + int ret; BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC_BYTES); assert(size > 0 && size <= s->cluster_size); - if (s->free_byte_offset == 0) { - offset = qcow2_alloc_clusters(bs, s->cluster_size); - if (offset < 0) { - return offset; + assert(!s->free_byte_offset || offset_into_cluster(s, s->free_byte_offset)); + + offset = s->free_byte_offset; + + if (offset) { + uint64_t refcount; + ret = qcow2_get_refcount(bs, offset >> s->cluster_bits, &refcount); + if (ret < 0) { + return ret; } - s->free_byte_offset = offset; - } - redo: - free_in_cluster = s->cluster_size - - offset_into_cluster(s, s->free_byte_offset); - if (size <= free_in_cluster) { - /* enough space in current cluster */ - offset = s->free_byte_offset; - s->free_byte_offset += size; - free_in_cluster -= size; - if (free_in_cluster == 0) - s->free_byte_offset = 0; - if (offset_into_cluster(s, offset) != 0) - qcow2_update_cluster_refcount(bs, offset >> s->cluster_bits, 1, - QCOW2_DISCARD_NEVER); - } else { - offset = qcow2_alloc_clusters(bs, s->cluster_size); - if (offset < 0) { - return offset; + + if (refcount == s->refcount_max) { + offset = 0; } - cluster_offset = start_of_cluster(s, s->free_byte_offset); - if ((cluster_offset + s->cluster_size) == offset) { - /* we are lucky: contiguous data */ - offset = s->free_byte_offset; - qcow2_update_cluster_refcount(bs, offset >> s->cluster_bits, 1, - QCOW2_DISCARD_NEVER); - s->free_byte_offset += size; - } else { - s->free_byte_offset = offset; - goto redo; + } + + free_in_cluster = s->cluster_size - offset_into_cluster(s, offset); + if (!offset || free_in_cluster < size) { + int64_t new_cluster = alloc_clusters_noref(bs, s->cluster_size); + if (new_cluster < 0) { + return new_cluster; + } + + if (!offset || ROUND_UP(offset, s->cluster_size) != new_cluster) { + offset = new_cluster; } } - /* The cluster refcount was incremented, either by qcow2_alloc_clusters() - * or explicitly by qcow2_update_cluster_refcount(). Refcount blocks must - * be flushed before the caller's L2 table updates. - */ + assert(offset); + ret = update_refcount(bs, offset, size, 1, false, QCOW2_DISCARD_NEVER); + if (ret < 0) { + return ret; + } + + /* The cluster refcount was incremented; refcount blocks must be flushed + * before the caller's L2 table updates. */ qcow2_cache_set_dependency(bs, s->l2_table_cache, s->refcount_block_cache); + + s->free_byte_offset = offset + size; + if (!offset_into_cluster(s, s->free_byte_offset)) { + s->free_byte_offset = 0; + } + return offset; } @@ -817,7 +990,7 @@ void qcow2_free_clusters(BlockDriverState *bs, int ret; BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_FREE); - ret = update_refcount(bs, offset, size, -1, type); + ret = update_refcount(bs, offset, size, 1, true, type); if (ret < 0) { fprintf(stderr, "qcow2_free_clusters failed: %s\n", strerror(-ret)); /* TODO Remember the clusters to free them later and avoid leaking */ @@ -876,12 +1049,14 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, int64_t l1_table_offset, int l1_size, int addend) { BDRVQcowState *s = bs->opaque; - uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2; + uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2, refcount; bool l1_allocated = false; int64_t old_offset, old_l2_offset; - int i, j, l1_modified = 0, nb_csectors, refcount; + int i, j, l1_modified = 0, nb_csectors; int ret; + assert(addend >= -1 && addend <= 1); + l2_table = NULL; l1_table = NULL; l1_size2 = l1_size * sizeof(uint64_t); @@ -946,7 +1121,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, if (addend != 0) { ret = update_refcount(bs, (offset & s->cluster_offset_mask) & ~511, - nb_csectors * 512, addend, + nb_csectors * 512, abs(addend), addend < 0, QCOW2_DISCARD_SNAPSHOT); if (ret < 0) { goto fail; @@ -976,15 +1151,16 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, break; } if (addend != 0) { - refcount = qcow2_update_cluster_refcount(bs, - cluster_index, addend, + ret = qcow2_update_cluster_refcount(bs, + cluster_index, abs(addend), addend < 0, QCOW2_DISCARD_SNAPSHOT); - } else { - refcount = qcow2_get_refcount(bs, cluster_index); + if (ret < 0) { + goto fail; + } } - if (refcount < 0) { - ret = refcount; + ret = qcow2_get_refcount(bs, cluster_index, &refcount); + if (ret < 0) { goto fail; } break; @@ -1017,13 +1193,17 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, if (addend != 0) { - refcount = qcow2_update_cluster_refcount(bs, l2_offset >> - s->cluster_bits, addend, QCOW2_DISCARD_SNAPSHOT); - } else { - refcount = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits); + ret = qcow2_update_cluster_refcount(bs, l2_offset >> + s->cluster_bits, + abs(addend), addend < 0, + QCOW2_DISCARD_SNAPSHOT); + if (ret < 0) { + goto fail; + } } - if (refcount < 0) { - ret = refcount; + ret = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits, + &refcount); + if (ret < 0) { goto fail; } else if (refcount == 1) { l2_offset |= QCOW_OFLAG_COPIED; @@ -1068,6 +1248,63 @@ fail: /* refcount checking functions */ +static size_t refcount_array_byte_size(BDRVQcowState *s, uint64_t entries) +{ + /* This assertion holds because there is no way we can address more than + * 2^(64 - 9) clusters at once (with cluster size 512 = 2^9, and because + * offsets have to be representable in bytes); due to every cluster + * corresponding to one refcount entry, we are well below that limit */ + assert(entries < (UINT64_C(1) << (64 - 9))); + + /* Thanks to the assertion this will not overflow, because + * s->refcount_order < 7. + * (note: x << s->refcount_order == x * s->refcount_bits) */ + return DIV_ROUND_UP(entries << s->refcount_order, 8); +} + +/** + * Reallocates *array so that it can hold new_size entries. *size must contain + * the current number of entries in *array. If the reallocation fails, *array + * and *size will not be modified and -errno will be returned. If the + * reallocation is successful, *array will be set to the new buffer, *size + * will be set to new_size and 0 will be returned. The size of the reallocated + * refcount array buffer will be aligned to a cluster boundary, and the newly + * allocated area will be zeroed. + */ +static int realloc_refcount_array(BDRVQcowState *s, void **array, + int64_t *size, int64_t new_size) +{ + size_t old_byte_size, new_byte_size; + void *new_ptr; + + /* Round to clusters so the array can be directly written to disk */ + old_byte_size = size_to_clusters(s, refcount_array_byte_size(s, *size)) + * s->cluster_size; + new_byte_size = size_to_clusters(s, refcount_array_byte_size(s, new_size)) + * s->cluster_size; + + if (new_byte_size == old_byte_size) { + *size = new_size; + return 0; + } + + assert(new_byte_size > 0); + + new_ptr = g_try_realloc(*array, new_byte_size); + if (!new_ptr) { + return -ENOMEM; + } + + if (new_byte_size > old_byte_size) { + memset((void *)((uintptr_t)new_ptr + old_byte_size), 0, + new_byte_size - old_byte_size); + } + + *array = new_ptr; + *size = new_size; + + return 0; +} /* * Increases the refcount for a range of clusters in a given refcount table. @@ -1078,12 +1315,13 @@ fail: */ static int inc_refcounts(BlockDriverState *bs, BdrvCheckResult *res, - uint16_t **refcount_table, + void **refcount_table, int64_t *refcount_table_size, int64_t offset, int64_t size) { BDRVQcowState *s = bs->opaque; - uint64_t start, last, cluster_offset, k; + uint64_t start, last, cluster_offset, k, refcount; + int ret; if (size <= 0) { return 0; @@ -1095,30 +1333,22 @@ static int inc_refcounts(BlockDriverState *bs, cluster_offset += s->cluster_size) { k = cluster_offset >> s->cluster_bits; if (k >= *refcount_table_size) { - int64_t old_refcount_table_size = *refcount_table_size; - uint16_t *new_refcount_table; - - *refcount_table_size = k + 1; - new_refcount_table = g_try_realloc(*refcount_table, - *refcount_table_size * - sizeof(**refcount_table)); - if (!new_refcount_table) { - *refcount_table_size = old_refcount_table_size; + ret = realloc_refcount_array(s, refcount_table, + refcount_table_size, k + 1); + if (ret < 0) { res->check_errors++; - return -ENOMEM; + return ret; } - *refcount_table = new_refcount_table; - - memset(*refcount_table + old_refcount_table_size, 0, - (*refcount_table_size - old_refcount_table_size) * - sizeof(**refcount_table)); } - if (++(*refcount_table)[k] == 0) { + refcount = s->get_refcount(*refcount_table, k); + if (refcount == s->refcount_max) { fprintf(stderr, "ERROR: overflow cluster offset=0x%" PRIx64 "\n", cluster_offset); res->corruptions++; + continue; } + s->set_refcount(*refcount_table, k, refcount + 1); } return 0; @@ -1138,8 +1368,9 @@ enum { * error occurred. */ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, - uint16_t **refcount_table, int64_t *refcount_table_size, int64_t l2_offset, - int flags) + void **refcount_table, + int64_t *refcount_table_size, int64_t l2_offset, + int flags) { BDRVQcowState *s = bs->opaque; uint64_t *l2_table, l2_entry; @@ -1256,7 +1487,7 @@ fail: */ static int check_refcounts_l1(BlockDriverState *bs, BdrvCheckResult *res, - uint16_t **refcount_table, + void **refcount_table, int64_t *refcount_table_size, int64_t l1_table_offset, int l1_size, int flags) @@ -1341,7 +1572,7 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, BDRVQcowState *s = bs->opaque; uint64_t *l2_table = qemu_blockalign(bs, s->cluster_size); int ret; - int refcount; + uint64_t refcount; int i, j; for (i = 0; i < s->l1_size; i++) { @@ -1353,14 +1584,15 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, continue; } - refcount = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits); - if (refcount < 0) { + ret = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits, + &refcount); + if (ret < 0) { /* don't print message nor increment check_errors */ continue; } if ((refcount == 1) != ((l1_entry & QCOW_OFLAG_COPIED) != 0)) { fprintf(stderr, "%s OFLAG_COPIED L2 cluster: l1_index=%d " - "l1_entry=%" PRIx64 " refcount=%d\n", + "l1_entry=%" PRIx64 " refcount=%" PRIu64 "\n", fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", i, l1_entry, refcount); @@ -1395,15 +1627,16 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, if ((cluster_type == QCOW2_CLUSTER_NORMAL) || ((cluster_type == QCOW2_CLUSTER_ZERO) && (data_offset != 0))) { - refcount = qcow2_get_refcount(bs, - data_offset >> s->cluster_bits); - if (refcount < 0) { + ret = qcow2_get_refcount(bs, + data_offset >> s->cluster_bits, + &refcount); + if (ret < 0) { /* don't print message nor increment check_errors */ continue; } if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) { fprintf(stderr, "%s OFLAG_COPIED data cluster: " - "l2_entry=%" PRIx64 " refcount=%d\n", + "l2_entry=%" PRIx64 " refcount=%" PRIu64 "\n", fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", l2_entry, refcount); @@ -1453,7 +1686,7 @@ fail: */ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix, bool *rebuild, - uint16_t **refcount_table, int64_t *nb_clusters) + void **refcount_table, int64_t *nb_clusters) { BDRVQcowState *s = bs->opaque; int64_t i, size; @@ -1478,8 +1711,7 @@ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res, fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", i); if (fix & BDRV_FIX_ERRORS) { - int64_t old_nb_clusters = *nb_clusters; - uint16_t *new_refcount_table; + int64_t new_nb_clusters; if (offset > INT64_MAX - s->cluster_size) { ret = -EINVAL; @@ -1496,22 +1728,15 @@ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res, goto resize_fail; } - *nb_clusters = size_to_clusters(s, size); - assert(*nb_clusters >= old_nb_clusters); + new_nb_clusters = size_to_clusters(s, size); + assert(new_nb_clusters >= *nb_clusters); - new_refcount_table = g_try_realloc(*refcount_table, - *nb_clusters * - sizeof(**refcount_table)); - if (!new_refcount_table) { - *nb_clusters = old_nb_clusters; + ret = realloc_refcount_array(s, refcount_table, + nb_clusters, new_nb_clusters); + if (ret < 0) { res->check_errors++; - return -ENOMEM; + return ret; } - *refcount_table = new_refcount_table; - - memset(*refcount_table + old_nb_clusters, 0, - (*nb_clusters - old_nb_clusters) * - sizeof(**refcount_table)); if (cluster >= *nb_clusters) { ret = -EINVAL; @@ -1546,9 +1771,10 @@ resize_fail: if (ret < 0) { return ret; } - if ((*refcount_table)[cluster] != 1) { + if (s->get_refcount(*refcount_table, cluster) != 1) { fprintf(stderr, "ERROR refcount block %" PRId64 - " refcount=%d\n", i, (*refcount_table)[cluster]); + " refcount=%" PRIu64 "\n", i, + s->get_refcount(*refcount_table, cluster)); res->corruptions++; *rebuild = true; } @@ -1563,7 +1789,7 @@ resize_fail: */ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix, bool *rebuild, - uint16_t **refcount_table, int64_t *nb_clusters) + void **refcount_table, int64_t *nb_clusters) { BDRVQcowState *s = bs->opaque; int64_t i; @@ -1571,10 +1797,12 @@ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res, int ret; if (!*refcount_table) { - *refcount_table = g_try_new0(uint16_t, *nb_clusters); - if (*nb_clusters && *refcount_table == NULL) { + int64_t old_size = 0; + ret = realloc_refcount_array(s, refcount_table, + &old_size, *nb_clusters); + if (ret < 0) { res->check_errors++; - return -ENOMEM; + return ret; } } @@ -1625,22 +1853,23 @@ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res, static void compare_refcounts(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix, bool *rebuild, int64_t *highest_cluster, - uint16_t *refcount_table, int64_t nb_clusters) + void *refcount_table, int64_t nb_clusters) { BDRVQcowState *s = bs->opaque; int64_t i; - int refcount1, refcount2, ret; + uint64_t refcount1, refcount2; + int ret; for (i = 0, *highest_cluster = 0; i < nb_clusters; i++) { - refcount1 = qcow2_get_refcount(bs, i); - if (refcount1 < 0) { + ret = qcow2_get_refcount(bs, i, &refcount1); + if (ret < 0) { fprintf(stderr, "Can't get refcount for cluster %" PRId64 ": %s\n", - i, strerror(-refcount1)); + i, strerror(-ret)); res->check_errors++; continue; } - refcount2 = refcount_table[i]; + refcount2 = s->get_refcount(refcount_table, i); if (refcount1 > 0 || refcount2 > 0) { *highest_cluster = i; @@ -1657,7 +1886,8 @@ static void compare_refcounts(BlockDriverState *bs, BdrvCheckResult *res, num_fixed = &res->corruptions_fixed; } - fprintf(stderr, "%s cluster %" PRId64 " refcount=%d reference=%d\n", + fprintf(stderr, "%s cluster %" PRId64 " refcount=%" PRIu64 + " reference=%" PRIu64 "\n", num_fixed != NULL ? "Repairing" : refcount1 < refcount2 ? "ERROR" : "Leaked", @@ -1665,7 +1895,8 @@ static void compare_refcounts(BlockDriverState *bs, BdrvCheckResult *res, if (num_fixed) { ret = update_refcount(bs, i << s->cluster_bits, 1, - refcount2 - refcount1, + refcount_diff(refcount1, refcount2), + refcount1 > refcount2, QCOW2_DISCARD_ALWAYS); if (ret >= 0) { (*num_fixed)++; @@ -1697,7 +1928,7 @@ static void compare_refcounts(BlockDriverState *bs, BdrvCheckResult *res, */ static int64_t alloc_clusters_imrt(BlockDriverState *bs, int cluster_count, - uint16_t **refcount_table, + void **refcount_table, int64_t *imrt_nb_clusters, int64_t *first_free_cluster) { @@ -1705,6 +1936,7 @@ static int64_t alloc_clusters_imrt(BlockDriverState *bs, int64_t cluster = *first_free_cluster, i; bool first_gap = true; int contiguous_free_clusters; + int ret; /* Starting at *first_free_cluster, find a range of at least cluster_count * continuously free clusters */ @@ -1713,7 +1945,7 @@ static int64_t alloc_clusters_imrt(BlockDriverState *bs, contiguous_free_clusters < cluster_count; cluster++) { - if (!(*refcount_table)[cluster]) { + if (!s->get_refcount(*refcount_table, cluster)) { contiguous_free_clusters++; if (first_gap) { /* If this is the first free cluster found, update @@ -1734,34 +1966,24 @@ static int64_t alloc_clusters_imrt(BlockDriverState *bs, /* If no such range could be found, grow the in-memory refcount table * accordingly to append free clusters at the end of the image */ if (contiguous_free_clusters < cluster_count) { - int64_t old_imrt_nb_clusters = *imrt_nb_clusters; - uint16_t *new_refcount_table; - /* contiguous_free_clusters clusters are already empty at the image end; * we need cluster_count clusters; therefore, we have to allocate * cluster_count - contiguous_free_clusters new clusters at the end of * the image (which is the current value of cluster; note that cluster * may exceed old_imrt_nb_clusters if *first_free_cluster pointed beyond * the image end) */ - *imrt_nb_clusters = cluster + cluster_count - contiguous_free_clusters; - new_refcount_table = g_try_realloc(*refcount_table, - *imrt_nb_clusters * - sizeof(**refcount_table)); - if (!new_refcount_table) { - *imrt_nb_clusters = old_imrt_nb_clusters; - return -ENOMEM; + ret = realloc_refcount_array(s, refcount_table, imrt_nb_clusters, + cluster + cluster_count + - contiguous_free_clusters); + if (ret < 0) { + return ret; } - *refcount_table = new_refcount_table; - - memset(*refcount_table + old_imrt_nb_clusters, 0, - (*imrt_nb_clusters - old_imrt_nb_clusters) * - sizeof(**refcount_table)); } /* Go back to the first free cluster */ cluster -= contiguous_free_clusters; for (i = 0; i < cluster_count; i++) { - (*refcount_table)[cluster + i] = 1; + s->set_refcount(*refcount_table, cluster + i, 1); } return cluster << s->cluster_bits; @@ -1777,7 +1999,7 @@ static int64_t alloc_clusters_imrt(BlockDriverState *bs, */ static int rebuild_refcount_structure(BlockDriverState *bs, BdrvCheckResult *res, - uint16_t **refcount_table, + void **refcount_table, int64_t *nb_clusters) { BDRVQcowState *s = bs->opaque; @@ -1785,8 +2007,8 @@ static int rebuild_refcount_structure(BlockDriverState *bs, int64_t refblock_offset, refblock_start, refblock_index; uint32_t reftable_size = 0; uint64_t *on_disk_reftable = NULL; - uint16_t *on_disk_refblock; - int i, ret = 0; + void *on_disk_refblock; + int ret = 0; struct { uint64_t reftable_offset; uint32_t reftable_clusters; @@ -1796,7 +2018,7 @@ static int rebuild_refcount_structure(BlockDriverState *bs, write_refblocks: for (; cluster < *nb_clusters; cluster++) { - if (!(*refcount_table)[cluster]) { + if (!s->get_refcount(*refcount_table, cluster)) { continue; } @@ -1869,17 +2091,13 @@ write_refblocks: goto fail; } - on_disk_refblock = qemu_blockalign0(bs->file, s->cluster_size); - for (i = 0; i < s->refcount_block_size && - refblock_start + i < *nb_clusters; i++) - { - on_disk_refblock[i] = - cpu_to_be16((*refcount_table)[refblock_start + i]); - } + /* The size of *refcount_table is always cluster-aligned, therefore the + * write operation will not overflow */ + on_disk_refblock = (void *)((char *) *refcount_table + + refblock_index * s->cluster_size); ret = bdrv_write(bs->file, refblock_offset / BDRV_SECTOR_SIZE, - (void *)on_disk_refblock, s->cluster_sectors); - qemu_vfree(on_disk_refblock); + on_disk_refblock, s->cluster_sectors); if (ret < 0) { fprintf(stderr, "ERROR writing refblock: %s\n", strerror(-ret)); goto fail; @@ -1974,7 +2192,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, BDRVQcowState *s = bs->opaque; BdrvCheckResult pre_compare_res; int64_t size, highest_cluster, nb_clusters; - uint16_t *refcount_table = NULL; + void *refcount_table = NULL; bool rebuild = false; int ret; @@ -2023,7 +2241,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, /* Because the old reftable has been exchanged for a new one the * references have to be recalculated */ rebuild = false; - memset(refcount_table, 0, nb_clusters * sizeof(uint16_t)); + memset(refcount_table, 0, refcount_array_byte_size(s, nb_clusters)); ret = calculate_refcounts(bs, res, 0, &rebuild, &refcount_table, &nb_clusters); if (ret < 0) { diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c index 5b3903c..2aa9dcb 100644 --- a/block/qcow2-snapshot.c +++ b/block/qcow2-snapshot.c @@ -702,7 +702,7 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs, sn = &s->snapshots[snapshot_index]; /* Allocate and read in the snapshot's L1 table */ - if (sn->l1_size > QCOW_MAX_L1_SIZE) { + if (sn->l1_size > QCOW_MAX_L1_SIZE / sizeof(uint64_t)) { error_setg(errp, "Snapshot L1 table too large"); return -EFBIG; } diff --git a/block/qcow2.c b/block/qcow2.c index d120494..316a8db 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -117,7 +117,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, #ifdef DEBUG_EXT printf("ext.magic = 0x%x\n", ext.magic); #endif - if (ext.len > end_offset - offset) { + if (offset > end_offset || ext.len > end_offset - offset) { error_setg(errp, "Header extension too large"); return -EINVAL; } @@ -140,6 +140,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, return 3; } bs->backing_format[ext.len] = '\0'; + s->image_backing_format = g_strdup(bs->backing_format); #ifdef DEBUG_EXT printf("Qcow2: Got format extension %s\n", bs->backing_format); #endif @@ -677,13 +678,16 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, } /* Check support for various header values */ - if (header.refcount_order != 4) { - report_unsupported(bs, errp, "%d bit reference counts", - 1 << header.refcount_order); - ret = -ENOTSUP; + if (header.refcount_order > 6) { + error_setg(errp, "Reference count entry width too large; may not " + "exceed 64 bits"); + ret = -EINVAL; goto fail; } s->refcount_order = header.refcount_order; + s->refcount_bits = 1 << s->refcount_order; + s->refcount_max = UINT64_C(1) << (s->refcount_bits - 1); + s->refcount_max += s->refcount_max - 1; if (header.crypt_method > QCOW_CRYPT_AES) { error_setg(errp, "Unsupported encryption method: %" PRIu32, @@ -739,7 +743,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, } /* read the level 1 table */ - if (header.l1_size > QCOW_MAX_L1_SIZE) { + if (header.l1_size > QCOW_MAX_L1_SIZE / sizeof(uint64_t)) { error_setg(errp, "Active L1 table too large"); ret = -EFBIG; goto fail; @@ -868,7 +872,8 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, /* read the backing file name */ if (header.backing_file_offset != 0) { len = header.backing_file_size; - if (len > MIN(1023, s->cluster_size - header.backing_file_offset)) { + if (len > MIN(1023, s->cluster_size - header.backing_file_offset) || + len >= sizeof(bs->backing_file)) { error_setg(errp, "Backing file name too long"); ret = -EINVAL; goto fail; @@ -880,6 +885,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } bs->backing_file[len] = '\0'; + s->image_backing_file = g_strdup(bs->backing_file); } /* Internal snapshots */ @@ -1428,10 +1434,23 @@ static void qcow2_close(BlockDriverState *bs) s->l1_table = NULL; if (!(bs->open_flags & BDRV_O_INCOMING)) { - qcow2_cache_flush(bs, s->l2_table_cache); - qcow2_cache_flush(bs, s->refcount_block_cache); + int ret1, ret2; - qcow2_mark_clean(bs); + ret1 = qcow2_cache_flush(bs, s->l2_table_cache); + ret2 = qcow2_cache_flush(bs, s->refcount_block_cache); + + if (ret1) { + error_report("Failed to flush the L2 table cache: %s", + strerror(-ret1)); + } + if (ret2) { + error_report("Failed to flush the refcount block cache: %s", + strerror(-ret2)); + } + + if (!ret1 && !ret2) { + qcow2_mark_clean(bs); + } } qcow2_cache_destroy(bs, s->l2_table_cache); @@ -1440,6 +1459,9 @@ static void qcow2_close(BlockDriverState *bs) g_free(s->unknown_header_fields); cleanup_unknown_header_ext(bs); + g_free(s->image_backing_file); + g_free(s->image_backing_format); + g_free(s->cluster_cache); qemu_vfree(s->cluster_data); qcow2_refcount_close(bs); @@ -1605,9 +1627,10 @@ int qcow2_update_header(BlockDriverState *bs) } /* Backing file format header extension */ - if (*bs->backing_format) { + if (s->image_backing_format) { ret = header_ext_add(buf, QCOW2_EXT_MAGIC_BACKING_FORMAT, - bs->backing_format, strlen(bs->backing_format), + s->image_backing_format, + strlen(s->image_backing_format), buflen); if (ret < 0) { goto fail; @@ -1665,8 +1688,8 @@ int qcow2_update_header(BlockDriverState *bs) buflen -= ret; /* Backing file name */ - if (*bs->backing_file) { - size_t backing_file_len = strlen(bs->backing_file); + if (s->image_backing_file) { + size_t backing_file_len = strlen(s->image_backing_file); if (buflen < backing_file_len) { ret = -ENOSPC; @@ -1674,7 +1697,7 @@ int qcow2_update_header(BlockDriverState *bs) } /* Using strncpy is ok here, since buf is not NUL-terminated. */ - strncpy(buf, bs->backing_file, buflen); + strncpy(buf, s->image_backing_file, buflen); header->backing_file_offset = cpu_to_be64(buf - ((char*) header)); header->backing_file_size = cpu_to_be32(backing_file_len); @@ -1695,9 +1718,17 @@ fail: static int qcow2_change_backing_file(BlockDriverState *bs, const char *backing_file, const char *backing_fmt) { + BDRVQcowState *s = bs->opaque; + pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_file ?: ""); pstrcpy(bs->backing_format, sizeof(bs->backing_format), backing_fmt ?: ""); + g_free(s->image_backing_file); + g_free(s->image_backing_format); + + s->image_backing_file = backing_file ? g_strdup(bs->backing_file) : NULL; + s->image_backing_format = backing_fmt ? g_strdup(bs->backing_format) : NULL; + return qcow2_update_header(bs); } @@ -1766,7 +1797,7 @@ static int preallocate(BlockDriverState *bs) static int qcow2_create2(const char *filename, int64_t total_size, const char *backing_file, const char *backing_format, int flags, size_t cluster_size, PreallocMode prealloc, - QemuOpts *opts, int version, + QemuOpts *opts, int version, int refcount_order, Error **errp) { /* Calculate cluster_bits */ @@ -1799,9 +1830,21 @@ static int qcow2_create2(const char *filename, int64_t total_size, int ret; if (prealloc == PREALLOC_MODE_FULL || prealloc == PREALLOC_MODE_FALLOC) { + /* Note: The following calculation does not need to be exact; if it is a + * bit off, either some bytes will be "leaked" (which is fine) or we + * will need to increase the file size by some bytes (which is fine, + * too, as long as the bulk is allocated here). Therefore, using + * floating point arithmetic is fine. */ int64_t meta_size = 0; uint64_t nreftablee, nrefblocke, nl1e, nl2e; int64_t aligned_total_size = align_offset(total_size, cluster_size); + int refblock_bits, refblock_size; + /* refcount entry size in bytes */ + double rces = (1 << refcount_order) / 8.; + + /* see qcow2_open() */ + refblock_bits = cluster_bits - (refcount_order - 3); + refblock_size = 1 << refblock_bits; /* header: 1 cluster */ meta_size += cluster_size; @@ -1826,26 +1869,27 @@ static int qcow2_create2(const char *filename, int64_t total_size, * c = cluster size * y1 = number of refcount blocks entries * y2 = meta size including everything + * rces = refcount entry size in bytes * then, * y1 = (y2 + a)/c - * y2 = y1 * sizeof(u16) + y1 * sizeof(u16) * sizeof(u64) / c + m + * y2 = y1 * rces + y1 * rces * sizeof(u64) / c + m * we can get y1: - * y1 = (a + m) / (c - sizeof(u16) - sizeof(u16) * sizeof(u64) / c) + * y1 = (a + m) / (c - rces - rces * sizeof(u64) / c) */ - nrefblocke = (aligned_total_size + meta_size + cluster_size) / - (cluster_size - sizeof(uint16_t) - - 1.0 * sizeof(uint16_t) * sizeof(uint64_t) / cluster_size); - nrefblocke = align_offset(nrefblocke, cluster_size / sizeof(uint16_t)); - meta_size += nrefblocke * sizeof(uint16_t); + nrefblocke = (aligned_total_size + meta_size + cluster_size) + / (cluster_size - rces - rces * sizeof(uint64_t) + / cluster_size); + meta_size += DIV_ROUND_UP(nrefblocke, refblock_size) * cluster_size; /* total size of refcount tables */ - nreftablee = nrefblocke * sizeof(uint16_t) / cluster_size; + nreftablee = nrefblocke / refblock_size; nreftablee = align_offset(nreftablee, cluster_size / sizeof(uint64_t)); meta_size += nreftablee * sizeof(uint64_t); qemu_opt_set_number(opts, BLOCK_OPT_SIZE, - aligned_total_size + meta_size); - qemu_opt_set(opts, BLOCK_OPT_PREALLOC, PreallocMode_lookup[prealloc]); + aligned_total_size + meta_size, &error_abort); + qemu_opt_set(opts, BLOCK_OPT_PREALLOC, PreallocMode_lookup[prealloc], + &error_abort); } ret = bdrv_create_file(filename, opts, &local_err); @@ -1874,7 +1918,7 @@ static int qcow2_create2(const char *filename, int64_t total_size, .l1_size = cpu_to_be32(0), .refcount_table_offset = cpu_to_be64(cluster_size), .refcount_table_clusters = cpu_to_be32(1), - .refcount_order = cpu_to_be32(4), + .refcount_order = cpu_to_be32(refcount_order), .header_length = cpu_to_be32(sizeof(*header)), }; @@ -1915,10 +1959,9 @@ static int qcow2_create2(const char *filename, int64_t total_size, * refcount of the cluster that is occupied by the header and the refcount * table) */ - BlockDriver* drv = bdrv_find_format("qcow2"); - assert(drv != NULL); ret = bdrv_open(&bs, filename, NULL, NULL, - BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, drv, &local_err); + BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, + &bdrv_qcow2, &local_err); if (ret < 0) { error_propagate(errp, local_err); goto out; @@ -1970,7 +2013,7 @@ static int qcow2_create2(const char *filename, int64_t total_size, /* Reopen the image without BDRV_O_NO_FLUSH to flush it before returning */ ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_BACKING, - drv, &local_err); + &bdrv_qcow2, &local_err); if (local_err) { error_propagate(errp, local_err); goto out; @@ -1994,6 +2037,8 @@ static int qcow2_create(const char *filename, QemuOpts *opts, Error **errp) size_t cluster_size = DEFAULT_CLUSTER_SIZE; PreallocMode prealloc; int version = 3; + uint64_t refcount_bits = 16; + int refcount_order; Error *local_err = NULL; int ret; @@ -2048,8 +2093,28 @@ static int qcow2_create(const char *filename, QemuOpts *opts, Error **errp) goto finish; } + refcount_bits = qemu_opt_get_number_del(opts, BLOCK_OPT_REFCOUNT_BITS, + refcount_bits); + if (refcount_bits > 64 || !is_power_of_2(refcount_bits)) { + error_setg(errp, "Refcount width must be a power of two and may not " + "exceed 64 bits"); + ret = -EINVAL; + goto finish; + } + + if (version < 3 && refcount_bits != 16) { + error_setg(errp, "Different refcount widths than 16 bits require " + "compatibility level 1.1 or above (use compat=1.1 or " + "greater)"); + ret = -EINVAL; + goto finish; + } + + refcount_order = ffs(refcount_bits) - 1; + ret = qcow2_create2(filename, size, backing_file, backing_fmt, flags, - cluster_size, prealloc, opts, version, &local_err); + cluster_size, prealloc, opts, version, refcount_order, + &local_err); if (local_err) { error_propagate(errp, local_err); } @@ -2150,8 +2215,7 @@ static int qcow2_write_compressed(BlockDriverState *bs, int64_t sector_num, /* align end of file to a sector boundary to ease reading with sector based I/Os */ cluster_offset = bdrv_getlength(bs->file); - bdrv_truncate(bs->file, cluster_offset); - return 0; + return bdrv_truncate(bs->file, cluster_offset); } if (nb_sectors != s->cluster_sectors) { @@ -2466,7 +2530,8 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs) }; if (s->qcow_version == 2) { *spec_info->qcow2 = (ImageInfoSpecificQCow2){ - .compat = g_strdup("0.10"), + .compat = g_strdup("0.10"), + .refcount_bits = s->refcount_bits, }; } else if (s->qcow_version == 3) { *spec_info->qcow2 = (ImageInfoSpecificQCow2){ @@ -2477,6 +2542,7 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs) .corrupt = s->incompatible_features & QCOW2_INCOMPAT_CORRUPT, .has_corrupt = true, + .refcount_bits = s->refcount_bits, }; } @@ -2509,15 +2575,12 @@ static int qcow2_save_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, { BDRVQcowState *s = bs->opaque; int64_t total_sectors = bs->total_sectors; - int growable = bs->growable; bool zero_beyond_eof = bs->zero_beyond_eof; int ret; BLKDBG_EVENT(bs->file, BLKDBG_VMSTATE_SAVE); - bs->growable = 1; bs->zero_beyond_eof = false; ret = bdrv_pwritev(bs, qcow2_vm_state_offset(s) + pos, qiov); - bs->growable = growable; bs->zero_beyond_eof = zero_beyond_eof; /* bdrv_co_do_writev will have increased the total_sectors value to include @@ -2532,15 +2595,12 @@ static int qcow2_load_vmstate(BlockDriverState *bs, uint8_t *buf, int64_t pos, int size) { BDRVQcowState *s = bs->opaque; - int growable = bs->growable; bool zero_beyond_eof = bs->zero_beyond_eof; int ret; BLKDBG_EVENT(bs->file, BLKDBG_VMSTATE_LOAD); - bs->growable = 1; bs->zero_beyond_eof = false; ret = bdrv_pread(bs, qcow2_vm_state_offset(s) + pos, buf, size); - bs->growable = growable; bs->zero_beyond_eof = zero_beyond_eof; return ret; @@ -2635,8 +2695,8 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, continue; } - if (!strcmp(desc->name, "compat")) { - compat = qemu_opt_get(opts, "compat"); + if (!strcmp(desc->name, BLOCK_OPT_COMPAT_LEVEL)) { + compat = qemu_opt_get(opts, BLOCK_OPT_COMPAT_LEVEL); if (!compat) { /* preserve default */ } else if (!strcmp(compat, "0.10")) { @@ -2647,33 +2707,37 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, fprintf(stderr, "Unknown compatibility level %s.\n", compat); return -EINVAL; } - } else if (!strcmp(desc->name, "preallocation")) { + } else if (!strcmp(desc->name, BLOCK_OPT_PREALLOC)) { fprintf(stderr, "Cannot change preallocation mode.\n"); return -ENOTSUP; - } else if (!strcmp(desc->name, "size")) { - new_size = qemu_opt_get_size(opts, "size", 0); - } else if (!strcmp(desc->name, "backing_file")) { - backing_file = qemu_opt_get(opts, "backing_file"); - } else if (!strcmp(desc->name, "backing_fmt")) { - backing_format = qemu_opt_get(opts, "backing_fmt"); - } else if (!strcmp(desc->name, "encryption")) { - encrypt = qemu_opt_get_bool(opts, "encryption", s->crypt_method); + } else if (!strcmp(desc->name, BLOCK_OPT_SIZE)) { + new_size = qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 0); + } else if (!strcmp(desc->name, BLOCK_OPT_BACKING_FILE)) { + backing_file = qemu_opt_get(opts, BLOCK_OPT_BACKING_FILE); + } else if (!strcmp(desc->name, BLOCK_OPT_BACKING_FMT)) { + backing_format = qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT); + } else if (!strcmp(desc->name, BLOCK_OPT_ENCRYPT)) { + encrypt = qemu_opt_get_bool(opts, BLOCK_OPT_ENCRYPT, + s->crypt_method); if (encrypt != !!s->crypt_method) { fprintf(stderr, "Changing the encryption flag is not " "supported.\n"); return -ENOTSUP; } - } else if (!strcmp(desc->name, "cluster_size")) { - cluster_size = qemu_opt_get_size(opts, "cluster_size", + } else if (!strcmp(desc->name, BLOCK_OPT_CLUSTER_SIZE)) { + cluster_size = qemu_opt_get_size(opts, BLOCK_OPT_CLUSTER_SIZE, cluster_size); if (cluster_size != s->cluster_size) { fprintf(stderr, "Changing the cluster size is not " "supported.\n"); return -ENOTSUP; } - } else if (!strcmp(desc->name, "lazy_refcounts")) { - lazy_refcounts = qemu_opt_get_bool(opts, "lazy_refcounts", + } else if (!strcmp(desc->name, BLOCK_OPT_LAZY_REFCOUNTS)) { + lazy_refcounts = qemu_opt_get_bool(opts, BLOCK_OPT_LAZY_REFCOUNTS, lazy_refcounts); + } else if (!strcmp(desc->name, BLOCK_OPT_REFCOUNT_BITS)) { + error_report("Cannot change refcount entry width"); + return -ENOTSUP; } else { /* if this assertion fails, this probably means a new option was * added without having it covered here */ @@ -2701,8 +2765,9 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, } if (backing_file || backing_format) { - ret = qcow2_change_backing_file(bs, backing_file ?: bs->backing_file, - backing_format ?: bs->backing_format); + ret = qcow2_change_backing_file(bs, + backing_file ?: s->image_backing_file, + backing_format ?: s->image_backing_format); if (ret < 0) { return ret; } @@ -2843,11 +2908,17 @@ static QemuOptsList qcow2_create_opts = { .help = "Postpone refcount updates", .def_value_str = "off" }, + { + .name = BLOCK_OPT_REFCOUNT_BITS, + .type = QEMU_OPT_NUMBER, + .help = "Width of a reference count entry in bits", + .def_value_str = "16" + }, { /* end of list */ } } }; -static BlockDriver bdrv_qcow2 = { +BlockDriver bdrv_qcow2 = { .format_name = "qcow2", .instance_size = sizeof(BDRVQcowState), .bdrv_probe = qcow2_probe, diff --git a/block/qcow2.h b/block/qcow2.h index 6e39a1b..2f20949 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -62,7 +62,8 @@ #define MIN_CLUSTER_BITS 9 #define MAX_CLUSTER_BITS 21 -#define MIN_L2_CACHE_SIZE 1 /* cluster */ +/* Must be at least 2 to cover COW */ +#define MIN_L2_CACHE_SIZE 2 /* clusters */ /* Must be at least 4 to cover all cases of refcount table growth */ #define MIN_REFCOUNT_CACHE_SIZE 4 /* clusters */ @@ -213,6 +214,11 @@ typedef struct Qcow2DiscardRegion { QTAILQ_ENTRY(Qcow2DiscardRegion) next; } Qcow2DiscardRegion; +typedef uint64_t Qcow2GetRefcountFunc(const void *refcount_array, + uint64_t index); +typedef void Qcow2SetRefcountFunc(void *refcount_array, + uint64_t index, uint64_t value); + typedef struct BDRVQcowState { int cluster_bits; int cluster_size; @@ -258,6 +264,11 @@ typedef struct BDRVQcowState { int qcow_version; bool use_lazy_refcounts; int refcount_order; + int refcount_bits; + uint64_t refcount_max; + + Qcow2GetRefcountFunc *get_refcount; + Qcow2SetRefcountFunc *set_refcount; bool discard_passthrough[QCOW2_DISCARD_MAX]; @@ -273,18 +284,13 @@ typedef struct BDRVQcowState { QLIST_HEAD(, Qcow2UnknownHeaderExtension) unknown_header_ext; QTAILQ_HEAD (, Qcow2DiscardRegion) discards; bool cache_discards; -} BDRVQcowState; -/* XXX: use std qcow open function ? */ -typedef struct QCowCreateState { - int cluster_size; - int cluster_bits; - uint16_t *refcount_block; - uint64_t *refcount_table; - int64_t l1_table_offset; - int64_t refcount_table_offset; - int64_t refcount_block_offset; -} QCowCreateState; + /* Backing file path and format as stored in the image (this is not the + * effective path/format, which may be the result of a runtime option + * override) */ + char *image_backing_file; + char *image_backing_format; +} BDRVQcowState; struct QCowAIOCB; @@ -468,6 +474,11 @@ static inline uint64_t l2meta_cow_end(QCowL2Meta *m) + (m->cow_end.nb_sectors << BDRV_SECTOR_BITS); } +static inline uint64_t refcount_diff(uint64_t r1, uint64_t r2) +{ + return r1 > r2 ? r1 - r2 : r2 - r1; +} + // FIXME Need qcow2_ prefix to global functions /* qcow2.c functions */ @@ -487,10 +498,12 @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset, int qcow2_refcount_init(BlockDriverState *bs); void qcow2_refcount_close(BlockDriverState *bs); -int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index); +int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index, + uint64_t *refcount); int qcow2_update_cluster_refcount(BlockDriverState *bs, int64_t cluster_index, - int addend, enum qcow2_discard_type type); + uint64_t addend, bool decrease, + enum qcow2_discard_type type); int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size); int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, diff --git a/block/qed.c b/block/qed.c index 80f18d8..892b13c 100644 --- a/block/qed.c +++ b/block/qed.c @@ -440,6 +440,11 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, s->l2_mask = s->table_nelems - 1; s->l1_shift = s->l2_shift + ffs(s->table_nelems) - 1; + /* Header size calculation must not overflow uint32_t */ + if (s->header.header_size > UINT32_MAX / s->header.cluster_size) { + return -EINVAL; + } + if ((s->header.features & QED_F_BACKING_FILE)) { if ((uint64_t)s->header.backing_filename_offset + s->header.backing_filename_size > diff --git a/block/qed.h b/block/qed.h index d3934a0..615e676 100644 --- a/block/qed.h +++ b/block/qed.h @@ -133,7 +133,6 @@ typedef struct QEDAIOCB { int bh_ret; /* final return status for completion bh */ QSIMPLEQ_ENTRY(QEDAIOCB) next; /* next request */ int flags; /* QED_AIOCB_* bits ORed together */ - bool *finished; /* signal for cancel completion */ uint64_t end_pos; /* request end on block device, in bytes */ /* User scatter-gather list */ diff --git a/block/raw-aio.h b/block/raw-aio.h index 80681ce..31d791f 100644 --- a/block/raw-aio.h +++ b/block/raw-aio.h @@ -41,7 +41,7 @@ BlockAIOCB *laio_submit(BlockDriverState *bs, void *aio_ctx, int fd, void laio_detach_aio_context(void *s, AioContext *old_context); void laio_attach_aio_context(void *s, AioContext *new_context); void laio_io_plug(BlockDriverState *bs, void *aio_ctx); -int laio_io_unplug(BlockDriverState *bs, void *aio_ctx, bool unplug); +void laio_io_unplug(BlockDriverState *bs, void *aio_ctx, bool unplug); #endif #ifdef _WIN32 diff --git a/block/raw-posix.c b/block/raw-posix.c index b1af77e..24d8582 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -56,11 +56,15 @@ #include #include #include +#include +#ifdef __s390__ +#include +#endif #ifndef FS_NOCOW_FL #define FS_NOCOW_FL 0x00800000 /* Do not cow file */ #endif #endif -#ifdef CONFIG_FALLOCATE_PUNCH_HOLE +#if defined(CONFIG_FALLOCATE_PUNCH_HOLE) || defined(CONFIG_FALLOCATE_ZERO_RANGE) #include #endif #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) @@ -147,6 +151,7 @@ typedef struct BDRVRawState { bool has_discard:1; bool has_write_zeroes:1; bool discard_zeroes:1; + bool has_fallocate; bool needs_alignment; } BDRVRawState; @@ -217,39 +222,100 @@ static int raw_normalize_devicepath(const char **filename) } #endif -static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp) +/* + * Get logical block size via ioctl. On success store it in @sector_size_p. + */ +static int probe_logical_blocksize(int fd, unsigned int *sector_size_p) { - BDRVRawState *s = bs->opaque; - char *buf; unsigned int sector_size; + bool success = false; - /* For /dev/sg devices the alignment is not really used. - With buffered I/O, we don't have any restrictions. */ - if (bs->sg || !s->needs_alignment) { - bs->request_alignment = 1; - s->buf_align = 1; - return; - } + errno = ENOTSUP; /* Try a few ioctls to get the right size */ - bs->request_alignment = 0; - s->buf_align = 0; - #ifdef BLKSSZGET if (ioctl(fd, BLKSSZGET, §or_size) >= 0) { - bs->request_alignment = sector_size; + *sector_size_p = sector_size; + success = true; } #endif #ifdef DKIOCGETBLOCKSIZE if (ioctl(fd, DKIOCGETBLOCKSIZE, §or_size) >= 0) { - bs->request_alignment = sector_size; + *sector_size_p = sector_size; + success = true; } #endif #ifdef DIOCGSECTORSIZE if (ioctl(fd, DIOCGSECTORSIZE, §or_size) >= 0) { - bs->request_alignment = sector_size; + *sector_size_p = sector_size; + success = true; } #endif + + return success ? 0 : -errno; +} + +/** + * Get physical block size of @fd. + * On success, store it in @blk_size and return 0. + * On failure, return -errno. + */ +static int probe_physical_blocksize(int fd, unsigned int *blk_size) +{ +#ifdef BLKPBSZGET + if (ioctl(fd, BLKPBSZGET, blk_size) < 0) { + return -errno; + } + return 0; +#else + return -ENOTSUP; +#endif +} + +/* Check if read is allowed with given memory buffer and length. + * + * This function is used to check O_DIRECT memory buffer and request alignment. + */ +static bool raw_is_io_aligned(int fd, void *buf, size_t len) +{ + ssize_t ret = pread(fd, buf, len, 0); + + if (ret >= 0) { + return true; + } + +#ifdef __linux__ + /* The Linux kernel returns EINVAL for misaligned O_DIRECT reads. Ignore + * other errors (e.g. real I/O error), which could happen on a failed + * drive, since we only care about probing alignment. + */ + if (errno != EINVAL) { + return true; + } +#endif + + return false; +} + +static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp) +{ + BDRVRawState *s = bs->opaque; + char *buf; + + /* For /dev/sg devices the alignment is not really used. + With buffered I/O, we don't have any restrictions. */ + if (bs->sg || !s->needs_alignment) { + bs->request_alignment = 1; + s->buf_align = 1; + return; + } + + bs->request_alignment = 0; + s->buf_align = 0; + /* Let's try to use the logical blocksize for the alignment. */ + if (probe_logical_blocksize(fd, &bs->request_alignment) < 0) { + bs->request_alignment = 0; + } #ifdef CONFIG_XFS if (s->is_xfs) { struct dioattr da; @@ -266,7 +332,7 @@ static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp) size_t align; buf = qemu_memalign(MAX_BLOCKSIZE, 2 * MAX_BLOCKSIZE); for (align = 512; align <= MAX_BLOCKSIZE; align <<= 1) { - if (pread(fd, buf + align, MAX_BLOCKSIZE, 0) >= 0) { + if (raw_is_io_aligned(fd, buf + align, MAX_BLOCKSIZE)) { s->buf_align = align; break; } @@ -278,7 +344,7 @@ static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp) size_t align; buf = qemu_memalign(s->buf_align, MAX_BLOCKSIZE); for (align = 512; align <= MAX_BLOCKSIZE; align <<= 1) { - if (pread(fd, buf, align, 0) >= 0) { + if (raw_is_io_aligned(fd, buf, align)) { bs->request_alignment = align; break; } @@ -437,6 +503,14 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, error_setg_errno(errp, -ret, "Could not set AIO state"); goto fail; } + if (!s->use_aio && (bdrv_flags & BDRV_O_NATIVE_AIO)) { + error_printf("WARNING: aio=native was specified for '%s', but " + "it requires cache.direct=on, which was not " + "specified. Falling back to aio=threads.\n" + " This will become an error condition in " + "future QEMU versions.\n", + bs->filename); + } #endif s->has_discard = true; @@ -446,11 +520,13 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, } if (fstat(s->fd, &st) < 0) { + ret = -errno; error_setg_errno(errp, errno, "Could not stat file"); goto fail; } if (S_ISREG(st.st_mode)) { s->discard_zeroes = true; + s->has_fallocate = true; } if (S_ISBLK(st.st_mode)) { #ifdef BLKDISCARDZEROES @@ -652,6 +728,86 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp) bs->bl.opt_mem_alignment = s->buf_align; } +static int check_for_dasd(int fd) +{ +#ifdef BIODASDINFO2 + struct dasd_information2_t info = {0}; + + return ioctl(fd, BIODASDINFO2, &info); +#else + return -1; +#endif +} + +/** + * Try to get @bs's logical and physical block size. + * On success, store them in @bsz and return zero. + * On failure, return negative errno. + */ +static int hdev_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) +{ + BDRVRawState *s = bs->opaque; + int ret; + + /* If DASD, get blocksizes */ + if (check_for_dasd(s->fd) < 0) { + return -ENOTSUP; + } + ret = probe_logical_blocksize(s->fd, &bsz->log); + if (ret < 0) { + return ret; + } + return probe_physical_blocksize(s->fd, &bsz->phys); +} + +/** + * Try to get @bs's geometry: cyls, heads, sectors. + * On success, store them in @geo and return 0. + * On failure return -errno. + * (Allows block driver to assign default geometry values that guest sees) + */ +#ifdef __linux__ +static int hdev_probe_geometry(BlockDriverState *bs, HDGeometry *geo) +{ + BDRVRawState *s = bs->opaque; + struct hd_geometry ioctl_geo = {0}; + uint32_t blksize; + + /* If DASD, get its geometry */ + if (check_for_dasd(s->fd) < 0) { + return -ENOTSUP; + } + if (ioctl(s->fd, HDIO_GETGEO, &ioctl_geo) < 0) { + return -errno; + } + /* HDIO_GETGEO may return success even though geo contains zeros + (e.g. certain multipath setups) */ + if (!ioctl_geo.heads || !ioctl_geo.sectors || !ioctl_geo.cylinders) { + return -ENOTSUP; + } + /* Do not return a geometry for partition */ + if (ioctl_geo.start != 0) { + return -ENOTSUP; + } + geo->heads = ioctl_geo.heads; + geo->sectors = ioctl_geo.sectors; + if (!probe_physical_blocksize(s->fd, &blksize)) { + /* overwrite cyls: HDIO_GETGEO result is incorrect for big drives */ + geo->cylinders = bdrv_nb_sectors(bs) / (blksize / BDRV_SECTOR_SIZE) + / (geo->heads * geo->sectors); + return 0; + } + geo->cylinders = ioctl_geo.cylinders; + + return 0; +} +#else /* __linux__ */ +static int hdev_probe_geometry(BlockDriverState *bs, HDGeometry *geo) +{ + return -ENOTSUP; +} +#endif + static ssize_t handle_aiocb_ioctl(RawPosixAIOData *aiocb) { int ret; @@ -892,40 +1048,110 @@ static int xfs_discard(BDRVRawState *s, int64_t offset, uint64_t bytes) } #endif -static ssize_t handle_aiocb_write_zeroes(RawPosixAIOData *aiocb) +static int translate_err(int err) { - int ret = -EOPNOTSUPP; + if (err == -ENODEV || err == -ENOSYS || err == -EOPNOTSUPP || + err == -ENOTTY) { + err = -ENOTSUP; + } + return err; +} + +#ifdef CONFIG_FALLOCATE +static int do_fallocate(int fd, int mode, off_t offset, off_t len) +{ + do { + if (fallocate(fd, mode, offset, len) == 0) { + return 0; + } + } while (errno == EINTR); + return translate_err(-errno); +} +#endif + +static ssize_t handle_aiocb_write_zeroes_block(RawPosixAIOData *aiocb) +{ + int ret = -ENOTSUP; BDRVRawState *s = aiocb->bs->opaque; - if (s->has_write_zeroes == 0) { + if (!s->has_write_zeroes) { return -ENOTSUP; } - if (aiocb->aio_type & QEMU_AIO_BLKDEV) { #ifdef BLKZEROOUT - do { - uint64_t range[2] = { aiocb->aio_offset, aiocb->aio_nbytes }; - if (ioctl(aiocb->aio_fildes, BLKZEROOUT, range) == 0) { - return 0; - } - } while (errno == EINTR); + do { + uint64_t range[2] = { aiocb->aio_offset, aiocb->aio_nbytes }; + if (ioctl(aiocb->aio_fildes, BLKZEROOUT, range) == 0) { + return 0; + } + } while (errno == EINTR); - ret = -errno; + ret = translate_err(-errno); #endif - } else { + + if (ret == -ENOTSUP) { + s->has_write_zeroes = false; + } + return ret; +} + +static ssize_t handle_aiocb_write_zeroes(RawPosixAIOData *aiocb) +{ +#if defined(CONFIG_FALLOCATE) || defined(CONFIG_XFS) + BDRVRawState *s = aiocb->bs->opaque; +#endif + + if (aiocb->aio_type & QEMU_AIO_BLKDEV) { + return handle_aiocb_write_zeroes_block(aiocb); + } + #ifdef CONFIG_XFS - if (s->is_xfs) { - return xfs_write_zeroes(s, aiocb->aio_offset, aiocb->aio_nbytes); + if (s->is_xfs) { + return xfs_write_zeroes(s, aiocb->aio_offset, aiocb->aio_nbytes); + } +#endif + +#ifdef CONFIG_FALLOCATE_ZERO_RANGE + if (s->has_write_zeroes) { + int ret = do_fallocate(s->fd, FALLOC_FL_ZERO_RANGE, + aiocb->aio_offset, aiocb->aio_nbytes); + if (ret == 0 || ret != -ENOTSUP) { + return ret; } + s->has_write_zeroes = false; + } #endif + +#ifdef CONFIG_FALLOCATE_PUNCH_HOLE + if (s->has_discard && s->has_fallocate) { + int ret = do_fallocate(s->fd, + FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, + aiocb->aio_offset, aiocb->aio_nbytes); + if (ret == 0) { + ret = do_fallocate(s->fd, 0, aiocb->aio_offset, aiocb->aio_nbytes); + if (ret == 0 || ret != -ENOTSUP) { + return ret; + } + s->has_fallocate = false; + } else if (ret != -ENOTSUP) { + return ret; + } else { + s->has_discard = false; + } } +#endif - if (ret == -ENODEV || ret == -ENOSYS || ret == -EOPNOTSUPP || - ret == -ENOTTY) { - s->has_write_zeroes = false; - ret = -ENOTSUP; +#ifdef CONFIG_FALLOCATE + if (s->has_fallocate && aiocb->aio_offset >= bdrv_getlength(aiocb->bs)) { + int ret = do_fallocate(s->fd, 0, aiocb->aio_offset, aiocb->aio_nbytes); + if (ret == 0 || ret != -ENOTSUP) { + return ret; + } + s->has_fallocate = false; } - return ret; +#endif + + return -ENOTSUP; } static ssize_t handle_aiocb_discard(RawPosixAIOData *aiocb) @@ -956,21 +1182,14 @@ static ssize_t handle_aiocb_discard(RawPosixAIOData *aiocb) #endif #ifdef CONFIG_FALLOCATE_PUNCH_HOLE - do { - if (fallocate(s->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, - aiocb->aio_offset, aiocb->aio_nbytes) == 0) { - return 0; - } - } while (errno == EINTR); - - ret = -errno; + ret = do_fallocate(s->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, + aiocb->aio_offset, aiocb->aio_nbytes); #endif } - if (ret == -ENODEV || ret == -ENOSYS || ret == -EOPNOTSUPP || - ret == -ENOTTY) { + ret = translate_err(ret); + if (ret == -ENOTSUP) { s->has_discard = false; - ret = -ENOTSUP; } return ret; } @@ -983,7 +1202,7 @@ static int aio_worker(void *arg) switch (aiocb->aio_type & QEMU_AIO_TYPE_MASK) { case QEMU_AIO_READ: ret = handle_aiocb_rw(aiocb); - if (ret >= 0 && ret < aiocb->aio_nbytes && aiocb->bs->growable) { + if (ret >= 0 && ret < aiocb->aio_nbytes) { iov_memset(aiocb->aio_iov, aiocb->aio_niov, ret, 0, aiocb->aio_nbytes - ret); @@ -1311,7 +1530,20 @@ again: if (size == 0) #endif #if defined(__APPLE__) && defined(__MACH__) - size = LLONG_MAX; + { + uint64_t sectors = 0; + uint32_t sector_size = 0; + + if (ioctl(fd, DKIOCGETBLOCKCOUNT, §ors) == 0 + && ioctl(fd, DKIOCGETBLOCKSIZE, §or_size) == 0) { + size = sectors * sector_size; + } else { + size = lseek(fd, 0LL, SEEK_END); + if (size < 0) { + return -errno; + } + } + } #else size = lseek(fd, 0LL, SEEK_END); if (size < 0) { @@ -1684,7 +1916,7 @@ static QemuOptsList raw_create_opts = { } }; -static BlockDriver bdrv_file = { +BlockDriver bdrv_file = { .format_name = "file", .protocol_name = "file", .instance_size = sizeof(BDRVRawState), @@ -1922,7 +2154,7 @@ static int fd_open(BlockDriverState *bs) return 0; last_media_present = (s->fd >= 0); if (s->fd >= 0 && - (get_clock() - s->fd_open_time) >= FD_OPEN_TIMEOUT) { + (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - s->fd_open_time) >= FD_OPEN_TIMEOUT) { qemu_close(s->fd); s->fd = -1; #ifdef DEBUG_FLOPPY @@ -1931,7 +2163,7 @@ static int fd_open(BlockDriverState *bs) } if (s->fd < 0) { if (s->fd_got_error && - (get_clock() - s->fd_error_time) < FD_OPEN_TIMEOUT) { + (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - s->fd_error_time) < FD_OPEN_TIMEOUT) { #ifdef DEBUG_FLOPPY printf("No floppy (open delayed)\n"); #endif @@ -1939,7 +2171,7 @@ static int fd_open(BlockDriverState *bs) } s->fd = qemu_open(bs->filename, s->open_flags & ~O_NONBLOCK); if (s->fd < 0) { - s->fd_error_time = get_clock(); + s->fd_error_time = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); s->fd_got_error = 1; if (last_media_present) s->fd_media_changed = 1; @@ -1954,7 +2186,7 @@ static int fd_open(BlockDriverState *bs) } if (!last_media_present) s->fd_media_changed = 1; - s->fd_open_time = get_clock(); + s->fd_open_time = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); s->fd_got_error = 0; return 0; } @@ -2117,6 +2349,8 @@ static BlockDriver bdrv_host_device = { .bdrv_get_info = raw_get_info, .bdrv_get_allocated_file_size = raw_get_allocated_file_size, + .bdrv_probe_blocksizes = hdev_probe_blocksizes, + .bdrv_probe_geometry = hdev_probe_geometry, .bdrv_detach_aio_context = raw_detach_aio_context, .bdrv_attach_aio_context = raw_attach_aio_context, @@ -2161,6 +2395,8 @@ static int floppy_open(BlockDriverState *bs, QDict *options, int flags, s->fd = -1; s->fd_media_changed = 1; + error_report("Host floppy pass-through is deprecated"); + error_printf("Support for it will be removed in a future release.\n"); return 0; } diff --git a/block/raw-win32.c b/block/raw-win32.c index 7b58881..dae5d2f 100644 --- a/block/raw-win32.c +++ b/block/raw-win32.c @@ -101,7 +101,7 @@ static int aio_worker(void *arg) switch (aiocb->aio_type & QEMU_AIO_TYPE_MASK) { case QEMU_AIO_READ: count = handle_aiocb_rw(aiocb); - if (count < aiocb->aio_nbytes && aiocb->bs->growable) { + if (count < aiocb->aio_nbytes) { /* A short read means that we have reached EOF. Pad the buffer * with zeros for bytes after EOF. */ iov_memset(aiocb->aio_iov, aiocb->aio_niov, count, @@ -540,7 +540,7 @@ static QemuOptsList raw_create_opts = { } }; -static BlockDriver bdrv_file = { +BlockDriver bdrv_file = { .format_name = "file", .protocol_name = "file", .instance_size = sizeof(BDRVRawState), diff --git a/block/raw_bsd.c b/block/raw_bsd.c index 401b967..e3d2d04 100644 --- a/block/raw_bsd.c +++ b/block/raw_bsd.c @@ -58,8 +58,58 @@ static int coroutine_fn raw_co_readv(BlockDriverState *bs, int64_t sector_num, static int coroutine_fn raw_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) { + void *buf = NULL; + BlockDriver *drv; + QEMUIOVector local_qiov; + int ret; + + if (bs->probed && sector_num == 0) { + /* As long as these conditions are true, we can't get partial writes to + * the probe buffer and can just directly check the request. */ + QEMU_BUILD_BUG_ON(BLOCK_PROBE_BUF_SIZE != 512); + QEMU_BUILD_BUG_ON(BDRV_SECTOR_SIZE != 512); + + if (nb_sectors == 0) { + /* qemu_iovec_to_buf() would fail, but we want to return success + * instead of -EINVAL in this case. */ + return 0; + } + + buf = qemu_try_blockalign(bs->file, 512); + if (!buf) { + ret = -ENOMEM; + goto fail; + } + + ret = qemu_iovec_to_buf(qiov, 0, buf, 512); + if (ret != 512) { + ret = -EINVAL; + goto fail; + } + + drv = bdrv_probe_all(buf, 512, NULL); + if (drv != bs->drv) { + ret = -EPERM; + goto fail; + } + + /* Use the checked buffer, a malicious guest might be overwriting its + * original buffer in the background. */ + qemu_iovec_init(&local_qiov, qiov->niov + 1); + qemu_iovec_add(&local_qiov, buf, 512); + qemu_iovec_concat(&local_qiov, qiov, 512, qiov->size - 512); + qiov = &local_qiov; + } + BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); - return bdrv_co_writev(bs->file, sector_num, nb_sectors, qiov); + ret = bdrv_co_writev(bs->file, sector_num, nb_sectors, qiov); + +fail: + if (qiov == &local_qiov) { + qemu_iovec_destroy(&local_qiov); + } + qemu_vfree(buf); + return ret; } static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs, @@ -158,6 +208,18 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { bs->sg = bs->file->sg; + + if (bs->probed && !bdrv_is_read_only(bs)) { + fprintf(stderr, + "WARNING: Image format was not specified for '%s' and probing " + "guessed raw.\n" + " Automatically detecting the format is dangerous for " + "raw images, write operations on block 0 will be restricted.\n" + " Specify the 'raw' format explicitly to remove the " + "restrictions.\n", + bs->file->filename); + } + return 0; } @@ -173,7 +235,17 @@ static int raw_probe(const uint8_t *buf, int buf_size, const char *filename) return 1; } -static BlockDriver bdrv_raw = { +static int raw_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) +{ + return bdrv_probe_blocksizes(bs->file, bsz); +} + +static int raw_probe_geometry(BlockDriverState *bs, HDGeometry *geo) +{ + return bdrv_probe_geometry(bs->file, geo); +} + +BlockDriver bdrv_raw = { .format_name = "raw", .bdrv_probe = &raw_probe, .bdrv_reopen_prepare = &raw_reopen_prepare, @@ -190,6 +262,8 @@ static BlockDriver bdrv_raw = { .has_variable_length = true, .bdrv_get_info = &raw_get_info, .bdrv_refresh_limits = &raw_refresh_limits, + .bdrv_probe_blocksizes = &raw_probe_blocksizes, + .bdrv_probe_geometry = &raw_probe_geometry, .bdrv_is_inserted = &raw_is_inserted, .bdrv_media_changed = &raw_media_changed, .bdrv_eject = &raw_eject, diff --git a/block/rbd.c b/block/rbd.c index 5b5a64a..f3ab2dd 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -459,7 +459,7 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, clientname = qemu_rbd_parse_clientname(conf, clientname_buf); r = rados_create(&s->cluster, clientname); if (r < 0) { - error_setg(&local_err, "error initializing"); + error_setg(errp, "error initializing"); goto failed_opts; } @@ -495,19 +495,19 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, r = rados_connect(s->cluster); if (r < 0) { - error_setg(&local_err, "error connecting"); + error_setg(errp, "error connecting"); goto failed_shutdown; } r = rados_ioctx_create(s->cluster, pool, &s->io_ctx); if (r < 0) { - error_setg(&local_err, "error opening pool %s", pool); + error_setg(errp, "error opening pool %s", pool); goto failed_shutdown; } r = rbd_open(s->io_ctx, s->name, &s->image, s->snap); if (r < 0) { - error_setg(&local_err, "error reading header from %s", s->name); + error_setg(errp, "error reading header from %s", s->name); goto failed_open; } diff --git a/block/sheepdog.c b/block/sheepdog.c index be3176f..c14172c 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -37,6 +37,7 @@ #define SD_OP_READ_VDIS 0x15 #define SD_OP_FLUSH_VDI 0x16 #define SD_OP_DEL_VDI 0x17 +#define SD_OP_GET_CLUSTER_DEFAULT 0x18 #define SD_FLAG_CMD_WRITE 0x01 #define SD_FLAG_CMD_COW 0x02 @@ -91,6 +92,7 @@ #define SD_NR_VDIS (1U << 24) #define SD_DATA_OBJ_SIZE (UINT64_C(1) << 22) #define SD_MAX_VDI_SIZE (SD_DATA_OBJ_SIZE * MAX_DATA_OBJS) +#define SD_DEFAULT_BLOCK_SIZE_SHIFT 22 /* * For erasure coding, we use at most SD_EC_MAX_STRIP for data strips and * (SD_EC_MAX_STRIP - 1) for parity strips @@ -167,7 +169,8 @@ typedef struct SheepdogVdiReq { uint32_t base_vdi_id; uint8_t copies; uint8_t copy_policy; - uint8_t reserved[2]; + uint8_t store_policy; + uint8_t block_size_shift; uint32_t snapid; uint32_t type; uint32_t pad[2]; @@ -186,6 +189,21 @@ typedef struct SheepdogVdiRsp { uint32_t pad[5]; } SheepdogVdiRsp; +typedef struct SheepdogClusterRsp { + uint8_t proto_ver; + uint8_t opcode; + uint16_t flags; + uint32_t epoch; + uint32_t id; + uint32_t data_length; + uint32_t result; + uint8_t nr_copies; + uint8_t copy_policy; + uint8_t block_size_shift; + uint8_t __pad1; + uint32_t __pad2[6]; +} SheepdogClusterRsp; + typedef struct SheepdogInode { char name[SD_MAX_VDI_LEN]; char tag[SD_MAX_VDI_TAG_LEN]; @@ -527,6 +545,7 @@ static SheepdogAIOCB *sd_aio_setup(BlockDriverState *bs, QEMUIOVector *qiov, return acb; } +/* Return -EIO in case of error, file descriptor on success */ static int connect_to_sdog(BDRVSheepdogState *s, Error **errp) { int fd; @@ -546,11 +565,14 @@ static int connect_to_sdog(BDRVSheepdogState *s, Error **errp) if (fd >= 0) { qemu_set_nonblock(fd); + } else { + fd = -EIO; } return fd; } +/* Return 0 on success and -errno in case of error */ static coroutine_fn int send_co_req(int sockfd, SheepdogReq *hdr, void *data, unsigned int *wlen) { @@ -559,11 +581,13 @@ static coroutine_fn int send_co_req(int sockfd, SheepdogReq *hdr, void *data, ret = qemu_co_send(sockfd, hdr, sizeof(*hdr)); if (ret != sizeof(*hdr)) { error_report("failed to send a req, %s", strerror(errno)); + ret = -socket_error(); return ret; } ret = qemu_co_send(sockfd, data, *wlen); if (ret != *wlen) { + ret = -socket_error(); error_report("failed to send a req, %s", strerror(errno)); } @@ -638,6 +662,11 @@ out: srco->finished = true; } +/* + * Send the request to the sheep in a synchronous manner. + * + * Return 0 on success, -errno in case of error. + */ static int do_req(int sockfd, AioContext *aio_context, SheepdogReq *hdr, void *data, unsigned int *wlen, unsigned int *rlen) { @@ -726,8 +755,7 @@ static coroutine_fn void reconnect_to_sdog(void *opaque) s->fd = get_sheep_fd(s, &local_err); if (s->fd < 0) { DPRINTF("Wait for connection to be established\n"); - error_report("%s", error_get_pretty(local_err)); - error_free(local_err); + error_report_err(local_err); co_aio_sleep_ns(bdrv_get_aio_context(s->bs), QEMU_CLOCK_REALTIME, 1000000000ULL); } @@ -1283,8 +1311,7 @@ static int reload_inode(BDRVSheepdogState *s, uint32_t snapid, const char *tag) fd = connect_to_sdog(s, &local_err); if (fd < 0) { - error_report("%s", error_get_pretty(local_err));; - error_free(local_err); + error_report_err(local_err); return -EIO; } @@ -1292,8 +1319,7 @@ static int reload_inode(BDRVSheepdogState *s, uint32_t snapid, const char *tag) ret = find_vdi_name(s, s->name, snapid, tag, &vid, false, &local_err); if (ret) { - error_report("%s", error_get_pretty(local_err));; - error_free(local_err); + error_report_err(local_err); goto out; } @@ -1544,6 +1570,7 @@ static int do_sd_create(BDRVSheepdogState *s, uint32_t *vdi_id, int snapshot, hdr.vdi_size = s->inode.vdi_size; hdr.copy_policy = s->inode.copy_policy; hdr.copies = s->inode.nr_copies; + hdr.block_size_shift = s->inode.block_size_shift; ret = do_req(fd, s->aio_context, (SheepdogReq *)&hdr, buf, &wlen, &rlen); @@ -1569,9 +1596,12 @@ static int do_sd_create(BDRVSheepdogState *s, uint32_t *vdi_id, int snapshot, static int sd_prealloc(const char *filename, Error **errp) { BlockDriverState *bs = NULL; + BDRVSheepdogState *base = NULL; + unsigned long buf_size; uint32_t idx, max_idx; + uint32_t object_size; int64_t vdi_size; - void *buf = g_malloc0(SD_DATA_OBJ_SIZE); + void *buf = NULL; int ret; ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, @@ -1585,18 +1615,24 @@ static int sd_prealloc(const char *filename, Error **errp) ret = vdi_size; goto out; } - max_idx = DIV_ROUND_UP(vdi_size, SD_DATA_OBJ_SIZE); + + base = bs->opaque; + object_size = (UINT32_C(1) << base->inode.block_size_shift); + buf_size = MIN(object_size, SD_DATA_OBJ_SIZE); + buf = g_malloc0(buf_size); + + max_idx = DIV_ROUND_UP(vdi_size, buf_size); for (idx = 0; idx < max_idx; idx++) { /* * The created image can be a cloned image, so we need to read * a data from the source image. */ - ret = bdrv_pread(bs, idx * SD_DATA_OBJ_SIZE, buf, SD_DATA_OBJ_SIZE); + ret = bdrv_pread(bs, idx * buf_size, buf, buf_size); if (ret < 0) { goto out; } - ret = bdrv_pwrite(bs, idx * SD_DATA_OBJ_SIZE, buf, SD_DATA_OBJ_SIZE); + ret = bdrv_pwrite(bs, idx * buf_size, buf, buf_size); if (ret < 0) { goto out; } @@ -1669,6 +1705,27 @@ static int parse_redundancy(BDRVSheepdogState *s, const char *opt) return 0; } +static int parse_block_size_shift(BDRVSheepdogState *s, QemuOpts *opt) +{ + struct SheepdogInode *inode = &s->inode; + uint64_t object_size; + int obj_order; + + object_size = qemu_opt_get_size_del(opt, BLOCK_OPT_OBJECT_SIZE, 0); + if (object_size) { + if ((object_size - 1) & object_size) { /* not a power of 2? */ + return -EINVAL; + } + obj_order = ffs(object_size) - 1; + if (obj_order < 20 || obj_order > 31) { + return -EINVAL; + } + inode->block_size_shift = (uint8_t)obj_order; + } + + return 0; +} + static int sd_create(const char *filename, QemuOpts *opts, Error **errp) { @@ -1679,6 +1736,7 @@ static int sd_create(const char *filename, QemuOpts *opts, BDRVSheepdogState *s; char tag[SD_MAX_VDI_TAG_LEN]; uint32_t snapid; + uint64_t max_vdi_size; bool prealloc = false; s = g_new0(BDRVSheepdogState, 1); @@ -1717,10 +1775,11 @@ static int sd_create(const char *filename, QemuOpts *opts, goto out; } } - - if (s->inode.vdi_size > SD_MAX_VDI_SIZE) { - error_setg(errp, "too big image size"); - ret = -EINVAL; + ret = parse_block_size_shift(s, opts); + if (ret < 0) { + error_setg(errp, "Invalid object_size." + " obect_size needs to be power of 2" + " and be limited from 2^20 to 2^31"); goto out; } @@ -1730,7 +1789,7 @@ static int sd_create(const char *filename, QemuOpts *opts, BlockDriver *drv; /* Currently, only Sheepdog backing image is supported. */ - drv = bdrv_find_protocol(backing_file, true); + drv = bdrv_find_protocol(backing_file, true, NULL); if (!drv || strcmp(drv->protocol_name, "sheepdog") != 0) { error_setg(errp, "backing_file must be a sheepdog image"); ret = -EINVAL; @@ -1757,6 +1816,51 @@ static int sd_create(const char *filename, QemuOpts *opts, } s->aio_context = qemu_get_aio_context(); + + /* if block_size_shift is not specified, get cluster default value */ + if (s->inode.block_size_shift == 0) { + SheepdogVdiReq hdr; + SheepdogClusterRsp *rsp = (SheepdogClusterRsp *)&hdr; + Error *local_err = NULL; + int fd; + unsigned int wlen = 0, rlen = 0; + + fd = connect_to_sdog(s, &local_err); + if (fd < 0) { + error_report("%s", error_get_pretty(local_err)); + error_free(local_err); + ret = -EIO; + goto out; + } + + memset(&hdr, 0, sizeof(hdr)); + hdr.opcode = SD_OP_GET_CLUSTER_DEFAULT; + hdr.proto_ver = SD_PROTO_VER; + + ret = do_req(fd, s->aio_context, (SheepdogReq *)&hdr, + NULL, &wlen, &rlen); + closesocket(fd); + if (ret) { + error_setg_errno(errp, -ret, "failed to get cluster default"); + goto out; + } + if (rsp->result == SD_RES_SUCCESS) { + s->inode.block_size_shift = rsp->block_size_shift; + } else { + s->inode.block_size_shift = SD_DEFAULT_BLOCK_SIZE_SHIFT; + } + } + + max_vdi_size = (UINT64_C(1) << s->inode.block_size_shift) * MAX_DATA_OBJS; + + if (s->inode.vdi_size > max_vdi_size) { + error_setg(errp, "An image is too large." + " The maximum image size is %"PRIu64 "GB", + max_vdi_size / 1024 / 1024 / 1024); + ret = -EINVAL; + goto out; + } + ret = do_sd_create(s, &vid, 0, errp); if (ret) { goto out; @@ -1785,8 +1889,7 @@ static void sd_close(BlockDriverState *bs) fd = connect_to_sdog(s, &local_err); if (fd < 0) { - error_report("%s", error_get_pretty(local_err));; - error_free(local_err); + error_report_err(local_err); return; } @@ -1827,19 +1930,20 @@ static int sd_truncate(BlockDriverState *bs, int64_t offset) BDRVSheepdogState *s = bs->opaque; int ret, fd; unsigned int datalen; + uint64_t max_vdi_size; + max_vdi_size = (UINT64_C(1) << s->inode.block_size_shift) * MAX_DATA_OBJS; if (offset < s->inode.vdi_size) { error_report("shrinking is not supported"); return -EINVAL; - } else if (offset > SD_MAX_VDI_SIZE) { + } else if (offset > max_vdi_size) { error_report("too big image size"); return -EINVAL; } fd = connect_to_sdog(s, &local_err); if (fd < 0) { - error_report("%s", error_get_pretty(local_err));; - error_free(local_err); + error_report_err(local_err); return fd; } @@ -1912,8 +2016,7 @@ static bool sd_delete(BDRVSheepdogState *s) fd = connect_to_sdog(s, &local_err); if (fd < 0) { - error_report("%s", error_get_pretty(local_err));; - error_free(local_err); + error_report_err(local_err); return false; } @@ -1960,8 +2063,7 @@ static int sd_create_branch(BDRVSheepdogState *s) deleted = sd_delete(s); ret = do_sd_create(s, &vid, !deleted, &local_err); if (ret) { - error_report("%s", error_get_pretty(local_err));; - error_free(local_err); + error_report_err(local_err); goto out; } @@ -1969,8 +2071,7 @@ static int sd_create_branch(BDRVSheepdogState *s) fd = connect_to_sdog(s, &local_err); if (fd < 0) { - error_report("%s", error_get_pretty(local_err));; - error_free(local_err); + error_report_err(local_err); ret = fd; goto out; } @@ -2013,9 +2114,10 @@ static int coroutine_fn sd_co_rw_vector(void *p) SheepdogAIOCB *acb = p; int ret = 0; unsigned long len, done = 0, total = acb->nb_sectors * BDRV_SECTOR_SIZE; - unsigned long idx = acb->sector_num * BDRV_SECTOR_SIZE / SD_DATA_OBJ_SIZE; + unsigned long idx; + uint32_t object_size; uint64_t oid; - uint64_t offset = (acb->sector_num * BDRV_SECTOR_SIZE) % SD_DATA_OBJ_SIZE; + uint64_t offset; BDRVSheepdogState *s = acb->common.bs->opaque; SheepdogInode *inode = &s->inode; AIOReq *aio_req; @@ -2032,6 +2134,10 @@ static int coroutine_fn sd_co_rw_vector(void *p) } } + object_size = (UINT32_C(1) << inode->block_size_shift); + idx = acb->sector_num * BDRV_SECTOR_SIZE / object_size; + offset = (acb->sector_num * BDRV_SECTOR_SIZE) % object_size; + /* * Make sure we don't free the aiocb before we are done with all requests. * This additional reference is dropped at the end of this function. @@ -2045,7 +2151,7 @@ static int coroutine_fn sd_co_rw_vector(void *p) oid = vid_to_data_oid(inode->data_vdi_id[idx], idx); - len = MIN(total - done, SD_DATA_OBJ_SIZE - offset); + len = MIN(total - done, object_size - offset); switch (acb->aiocb_type) { case AIOCB_READ_UDATA: @@ -2069,7 +2175,7 @@ static int coroutine_fn sd_co_rw_vector(void *p) * We discard the object only when the whole object is * 1) allocated 2) trimmed. Otherwise, simply skip it. */ - if (len != SD_DATA_OBJ_SIZE || inode->data_vdi_id[idx] == 0) { + if (len != object_size || inode->data_vdi_id[idx] == 0) { goto done; } break; @@ -2117,7 +2223,7 @@ static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num, int64_t offset = (sector_num + nb_sectors) * BDRV_SECTOR_SIZE; BDRVSheepdogState *s = bs->opaque; - if (bs->growable && offset > s->inode.vdi_size) { + if (offset > s->inode.vdi_size) { ret = sd_truncate(bs, offset); if (ret < 0) { return ret; @@ -2218,8 +2324,7 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) /* refresh inode. */ fd = connect_to_sdog(s, &local_err); if (fd < 0) { - error_report("%s", error_get_pretty(local_err));; - error_free(local_err); + error_report_err(local_err); ret = fd; goto cleanup; } @@ -2234,10 +2339,8 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) ret = do_sd_create(s, &new_vid, 1, &local_err); if (ret < 0) { - error_report("%s", error_get_pretty(local_err));; - error_free(local_err); - error_report("failed to create inode for snapshot. %s", - strerror(errno)); + error_report("failed to create inode for snapshot: %s", + error_get_pretty(local_err)); goto cleanup; } @@ -2336,8 +2439,7 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab) fd = connect_to_sdog(s, &local_err); if (fd < 0) { - error_report("%s", error_get_pretty(local_err));; - error_free(local_err); + error_report_err(local_err); ret = fd; goto out; } @@ -2366,8 +2468,7 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab) fd = connect_to_sdog(s, &local_err); if (fd < 0) { - error_report("%s", error_get_pretty(local_err));; - error_free(local_err); + error_report_err(local_err); ret = fd; goto out; } @@ -2426,19 +2527,19 @@ static int do_load_save_vmstate(BDRVSheepdogState *s, uint8_t *data, uint64_t offset; uint32_t vdi_index; uint32_t vdi_id = load ? s->inode.parent_vdi_id : s->inode.vdi_id; + uint32_t object_size = (UINT32_C(1) << s->inode.block_size_shift); fd = connect_to_sdog(s, &local_err); if (fd < 0) { - error_report("%s", error_get_pretty(local_err));; - error_free(local_err); + error_report_err(local_err); return fd; } while (remaining) { - vdi_index = pos / SD_DATA_OBJ_SIZE; - offset = pos % SD_DATA_OBJ_SIZE; + vdi_index = pos / object_size; + offset = pos % object_size; - data_len = MIN(remaining, SD_DATA_OBJ_SIZE - offset); + data_len = MIN(remaining, object_size - offset); vmstate_oid = vid_to_vmstate_oid(vdi_id, vdi_index); @@ -2525,10 +2626,11 @@ sd_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors, { BDRVSheepdogState *s = bs->opaque; SheepdogInode *inode = &s->inode; + uint32_t object_size = (UINT32_C(1) << inode->block_size_shift); uint64_t offset = sector_num * BDRV_SECTOR_SIZE; - unsigned long start = offset / SD_DATA_OBJ_SIZE, + unsigned long start = offset / object_size, end = DIV_ROUND_UP((sector_num + nb_sectors) * - BDRV_SECTOR_SIZE, SD_DATA_OBJ_SIZE); + BDRV_SECTOR_SIZE, object_size); unsigned long idx; int64_t ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | offset; @@ -2547,7 +2649,7 @@ sd_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors, } } - *pnum = (idx - start) * SD_DATA_OBJ_SIZE / BDRV_SECTOR_SIZE; + *pnum = (idx - start) * object_size / BDRV_SECTOR_SIZE; if (*pnum > nb_sectors) { *pnum = nb_sectors; } @@ -2558,14 +2660,15 @@ static int64_t sd_get_allocated_file_size(BlockDriverState *bs) { BDRVSheepdogState *s = bs->opaque; SheepdogInode *inode = &s->inode; - unsigned long i, last = DIV_ROUND_UP(inode->vdi_size, SD_DATA_OBJ_SIZE); + uint32_t object_size = (UINT32_C(1) << inode->block_size_shift); + unsigned long i, last = DIV_ROUND_UP(inode->vdi_size, object_size); uint64_t size = 0; for (i = 0; i < last; i++) { if (inode->data_vdi_id[i] == 0) { continue; } - size += SD_DATA_OBJ_SIZE; + size += object_size; } return size; } @@ -2594,6 +2697,11 @@ static QemuOptsList sd_create_opts = { .type = QEMU_OPT_STRING, .help = "Redundancy of the image" }, + { + .name = BLOCK_OPT_OBJECT_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Object size of the image" + }, { /* end of list */ } } }; diff --git a/block/vdi.c b/block/vdi.c index 39070b7..53bd02f 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -53,6 +53,7 @@ #include "block/block_int.h" #include "qemu/module.h" #include "migration/migration.h" +#include "block/coroutine.h" #if defined(CONFIG_UUID) #include @@ -196,6 +197,8 @@ typedef struct { /* VDI header (converted to host endianness). */ VdiHeader header; + CoMutex write_lock; + Error *migration_blocker; } BDRVVdiState; @@ -504,6 +507,8 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, "vdi", bdrv_get_device_name(bs), "live migration"); migrate_add_blocker(s->migration_blocker); + qemu_co_mutex_init(&s->write_lock); + return 0; fail_free_bmap: @@ -639,11 +644,31 @@ static int vdi_co_write(BlockDriverState *bs, buf, n_sectors * SECTOR_SIZE); memset(block + (sector_in_block + n_sectors) * SECTOR_SIZE, 0, (s->block_sectors - n_sectors - sector_in_block) * SECTOR_SIZE); + + /* Note that this coroutine does not yield anywhere from reading the + * bmap entry until here, so in regards to all the coroutines trying + * to write to this cluster, the one doing the allocation will + * always be the first to try to acquire the lock. + * Therefore, it is also the first that will actually be able to + * acquire the lock and thus the padded cluster is written before + * the other coroutines can write to the affected area. */ + qemu_co_mutex_lock(&s->write_lock); ret = bdrv_write(bs->file, offset, block, s->block_sectors); + qemu_co_mutex_unlock(&s->write_lock); } else { uint64_t offset = s->header.offset_data / SECTOR_SIZE + (uint64_t)bmap_entry * s->block_sectors + sector_in_block; + qemu_co_mutex_lock(&s->write_lock); + /* This lock is only used to make sure the following write operation + * is executed after the write issued by the coroutine allocating + * this cluster, therefore we do not need to keep it locked. + * As stated above, the allocating coroutine will always try to lock + * the mutex before all the other concurrent accesses to that + * cluster, therefore at this point we can be absolutely certain + * that that write operation has returned (there may be other writes + * in flight, but they do not concern this very operation). */ + qemu_co_mutex_unlock(&s->write_lock); ret = bdrv_write(bs->file, offset, buf, n_sectors); } @@ -852,11 +877,6 @@ static QemuOptsList vdi_create_opts = { .def_value_str = "off" }, #endif - { - .name = BLOCK_OPT_NOCOW, - .type = QEMU_OPT_BOOL, - .help = "Turn off copy-on-write (valid only on btrfs)" - }, /* TODO: An additional option to set UUID values might be useful. */ { /* end of list */ } } diff --git a/block/vhdx.c b/block/vhdx.c index 12bfe75..bb3ed45 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -1109,8 +1109,9 @@ static coroutine_fn int vhdx_co_readv(BlockDriverState *bs, int64_t sector_num, /* check the payload block state */ switch (s->bat[sinfo.bat_idx] & VHDX_BAT_STATE_BIT_MASK) { case PAYLOAD_BLOCK_NOT_PRESENT: /* fall through */ - case PAYLOAD_BLOCK_UNDEFINED: /* fall through */ - case PAYLOAD_BLOCK_UNMAPPED: /* fall through */ + case PAYLOAD_BLOCK_UNDEFINED: + case PAYLOAD_BLOCK_UNMAPPED: + case PAYLOAD_BLOCK_UNMAPPED_v095: case PAYLOAD_BLOCK_ZERO: /* return zero */ qemu_iovec_memset(&hd_qiov, 0, 0, sinfo.bytes_avail); @@ -1173,7 +1174,18 @@ static void vhdx_update_bat_table_entry(BlockDriverState *bs, BDRVVHDXState *s, { /* The BAT entry is a uint64, with 44 bits for the file offset in units of * 1MB, and 3 bits for the block state. */ - s->bat[sinfo->bat_idx] = sinfo->file_offset; + if ((state == PAYLOAD_BLOCK_ZERO) || + (state == PAYLOAD_BLOCK_UNDEFINED) || + (state == PAYLOAD_BLOCK_NOT_PRESENT) || + (state == PAYLOAD_BLOCK_UNMAPPED)) { + s->bat[sinfo->bat_idx] = 0; /* For PAYLOAD_BLOCK_ZERO, the + FileOffsetMB field is denoted as + 'reserved' in the v1.0 spec. If it is + non-zero, MS Hyper-V will fail to read + the disk image */ + } else { + s->bat[sinfo->bat_idx] = sinfo->file_offset; + } s->bat[sinfo->bat_idx] |= state & VHDX_BAT_STATE_BIT_MASK; @@ -1277,11 +1289,11 @@ static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num, sectors_to_write += iov2.iov_len >> BDRV_SECTOR_BITS; } } - /* fall through */ case PAYLOAD_BLOCK_NOT_PRESENT: /* fall through */ - case PAYLOAD_BLOCK_UNMAPPED: /* fall through */ - case PAYLOAD_BLOCK_UNDEFINED: /* fall through */ + case PAYLOAD_BLOCK_UNMAPPED: + case PAYLOAD_BLOCK_UNMAPPED_v095: + case PAYLOAD_BLOCK_UNDEFINED: bat_prior_offset = sinfo.file_offset; ret = vhdx_allocate_block(bs, s, &sinfo.file_offset); if (ret < 0) { @@ -1773,7 +1785,7 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp) log_size = qemu_opt_get_size_del(opts, VHDX_BLOCK_OPT_LOG_SIZE, 0); block_size = qemu_opt_get_size_del(opts, VHDX_BLOCK_OPT_BLOCK_SIZE, 0); type = qemu_opt_get_del(opts, BLOCK_OPT_SUBFMT); - use_zero_blocks = qemu_opt_get_bool_del(opts, VHDX_BLOCK_OPT_ZERO, false); + use_zero_blocks = qemu_opt_get_bool_del(opts, VHDX_BLOCK_OPT_ZERO, true); if (image_size > VHDX_MAX_IMAGE_SIZE) { error_setg_errno(errp, EINVAL, "Image size too large; max of 64TB"); @@ -1935,7 +1947,9 @@ static QemuOptsList vhdx_create_opts = { { .name = VHDX_BLOCK_OPT_ZERO, .type = QEMU_OPT_BOOL, - .help = "Force use of payload blocks of type 'ZERO'. Non-standard." + .help = "Force use of payload blocks of type 'ZERO'. "\ + "Non-standard, but default. Do not set to 'off' when "\ + "using 'qemu-img convert' with subformat=dynamic." }, { NULL } } @@ -1953,6 +1967,7 @@ static BlockDriver bdrv_vhdx = { .bdrv_create = vhdx_create, .bdrv_get_info = vhdx_get_info, .bdrv_check = vhdx_check, + .bdrv_has_zero_init = bdrv_has_zero_init_1, .create_opts = &vhdx_create_opts, }; diff --git a/block/vhdx.h b/block/vhdx.h index b4a12a0..7003ab7 100644 --- a/block/vhdx.h +++ b/block/vhdx.h @@ -226,7 +226,8 @@ typedef struct QEMU_PACKED VHDXLogDataSector { #define PAYLOAD_BLOCK_NOT_PRESENT 0 #define PAYLOAD_BLOCK_UNDEFINED 1 #define PAYLOAD_BLOCK_ZERO 2 -#define PAYLOAD_BLOCK_UNMAPPED 5 +#define PAYLOAD_BLOCK_UNMAPPED 3 +#define PAYLOAD_BLOCK_UNMAPPED_v095 5 #define PAYLOAD_BLOCK_FULLY_PRESENT 6 #define PAYLOAD_BLOCK_PARTIALLY_PRESENT 7 diff --git a/block/vmdk.c b/block/vmdk.c index 2cbfd3e..4c71cde 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -28,6 +28,7 @@ #include "qemu/module.h" #include "migration/migration.h" #include +#include #define VMDK3_MAGIC (('C' << 24) | ('O' << 16) | ('W' << 8) | 'D') #define VMDK4_MAGIC (('K' << 24) | ('D' << 16) | ('M' << 8) | 'V') @@ -450,7 +451,8 @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent, Error **errp) { int ret; - int l1_size, i; + size_t l1_size; + int i; /* read the L1 table */ l1_size = extent->l1_size * sizeof(uint32_t); @@ -556,8 +558,16 @@ static char *vmdk_read_desc(BlockDriverState *file, uint64_t desc_offset, return NULL; } - size = MIN(size, 1 << 20); /* avoid unbounded allocation */ - buf = g_malloc0(size + 1); + if (size < 4) { + /* Both descriptor file and sparse image must be much larger than 4 + * bytes, also callers of vmdk_read_desc want to compare the first 4 + * bytes with VMDK4_MAGIC, let's error out if less is read. */ + error_setg(errp, "File is too small, not a valid image"); + return NULL; + } + + size = MIN(size, (1 << 20) - 1); /* avoid unbounded allocation */ + buf = g_malloc(size + 1); ret = bdrv_pread(file, desc_offset, buf, size); if (ret < 0) { @@ -565,6 +575,7 @@ static char *vmdk_read_desc(BlockDriverState *file, uint64_t desc_offset, g_free(buf); return NULL; } + buf[ret] = 0; return buf; } @@ -635,6 +646,7 @@ static int vmdk_open_vmdk4(BlockDriverState *bs, bs->file->total_sectors * 512 - 1536, &footer, sizeof(footer)); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to read footer"); return ret; } @@ -646,6 +658,7 @@ static int vmdk_open_vmdk4(BlockDriverState *bs, le32_to_cpu(footer.eos_marker.size) != 0 || le32_to_cpu(footer.eos_marker.type) != MARKER_END_OF_STREAM) { + error_setg(errp, "Invalid footer"); return -EINVAL; } @@ -676,6 +689,7 @@ static int vmdk_open_vmdk4(BlockDriverState *bs, l1_entry_sectors = le32_to_cpu(header.num_gtes_per_gt) * le64_to_cpu(header.granularity); if (l1_entry_sectors == 0) { + error_setg(errp, "L1 entry size is invalid"); return -EINVAL; } l1_size = (le64_to_cpu(header.capacity) + l1_entry_sectors - 1) @@ -772,41 +786,44 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, const char *desc_file_path, Error **errp) { int ret; + int matches; char access[11]; char type[11]; char fname[512]; const char *p = desc; int64_t sectors = 0; int64_t flat_offset; - char extent_path[PATH_MAX]; + char *extent_path; BlockDriverState *extent_file; BDRVVmdkState *s = bs->opaque; VmdkExtent *extent; while (*p) { - /* parse extent line: + /* parse extent line in one of below formats: + * * RW [size in sectors] FLAT "file-name.vmdk" OFFSET - * or * RW [size in sectors] SPARSE "file-name.vmdk" + * RW [size in sectors] VMFS "file-name.vmdk" + * RW [size in sectors] VMFSSPARSE "file-name.vmdk" */ flat_offset = -1; - ret = sscanf(p, "%10s %" SCNd64 " %10s \"%511[^\n\r\"]\" %" SCNd64, - access, §ors, type, fname, &flat_offset); - if (ret < 4 || strcmp(access, "RW")) { + matches = sscanf(p, "%10s %" SCNd64 " %10s \"%511[^\n\r\"]\" %" SCNd64, + access, §ors, type, fname, &flat_offset); + if (matches < 4 || strcmp(access, "RW")) { goto next_line; } else if (!strcmp(type, "FLAT")) { - if (ret != 5 || flat_offset < 0) { + if (matches != 5 || flat_offset < 0) { error_setg(errp, "Invalid extent lines: \n%s", p); return -EINVAL; } } else if (!strcmp(type, "VMFS")) { - if (ret == 4) { + if (matches == 4) { flat_offset = 0; } else { error_setg(errp, "Invalid extent lines:\n%s", p); return -EINVAL; } - } else if (ret != 4) { + } else if (matches != 4) { error_setg(errp, "Invalid extent lines:\n%s", p); return -EINVAL; } @@ -818,11 +835,20 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, goto next_line; } - path_combine(extent_path, sizeof(extent_path), - desc_file_path, fname); + if (!path_is_absolute(fname) && !path_has_protocol(fname) && + !desc_file_path[0]) + { + error_setg(errp, "Cannot use relative extent paths with VMDK " + "descriptor file '%s'", bs->file->filename); + return -EINVAL; + } + + extent_path = g_malloc0(PATH_MAX); + path_combine(extent_path, PATH_MAX, desc_file_path, fname); extent_file = NULL; ret = bdrv_open(&extent_file, extent_path, NULL, NULL, bs->open_flags | BDRV_O_PROTOCOL, NULL, errp); + g_free(extent_path); if (ret) { return ret; } @@ -894,7 +920,7 @@ static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf, } s->create_type = g_strdup(ct); s->desc_offset = 0; - ret = vmdk_parse_extents(buf, bs, bs->file->filename, errp); + ret = vmdk_parse_extents(buf, bs, bs->file->exact_filename, errp); exit: return ret; } @@ -902,7 +928,7 @@ exit: static int vmdk_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { - char *buf = NULL; + char *buf; int ret; BDRVVmdkState *s = bs->opaque; uint32_t magic; @@ -1222,6 +1248,17 @@ static VmdkExtent *find_extent(BDRVVmdkState *s, return NULL; } +static inline uint64_t vmdk_find_index_in_cluster(VmdkExtent *extent, + int64_t sector_num) +{ + uint64_t index_in_cluster, extent_begin_sector, extent_relative_sector_num; + + extent_begin_sector = extent->end_sector - extent->sectors; + extent_relative_sector_num = sector_num - extent_begin_sector; + index_in_cluster = extent_relative_sector_num % extent->cluster_sectors; + return index_in_cluster; +} + static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum) { @@ -1259,7 +1296,7 @@ static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs, break; } - index_in_cluster = sector_num % extent->cluster_sectors; + index_in_cluster = vmdk_find_index_in_cluster(extent, sector_num); n = extent->cluster_sectors - index_in_cluster; if (n > nb_sectors) { n = nb_sectors; @@ -1277,6 +1314,8 @@ static int vmdk_write_extent(VmdkExtent *extent, int64_t cluster_offset, uLongf buf_len; const uint8_t *write_buf = buf; int write_len = nb_sectors * 512; + int64_t write_offset; + int64_t write_end_sector; if (extent->compressed) { if (!extent->has_marker) { @@ -1295,10 +1334,14 @@ static int vmdk_write_extent(VmdkExtent *extent, int64_t cluster_offset, write_buf = (uint8_t *)data; write_len = buf_len + sizeof(VmdkGrainMarker); } - ret = bdrv_pwrite(extent->file, - cluster_offset + offset_in_cluster, - write_buf, - write_len); + write_offset = cluster_offset + offset_in_cluster, + ret = bdrv_pwrite(extent->file, write_offset, write_buf, write_len); + + write_end_sector = DIV_ROUND_UP(write_offset + write_len, BDRV_SECTOR_SIZE); + + extent->next_cluster_sector = MAX(extent->next_cluster_sector, + write_end_sector); + if (ret != write_len) { ret = ret < 0 ? ret : -EIO; goto out; @@ -1381,7 +1424,6 @@ static int vmdk_read(BlockDriverState *bs, int64_t sector_num, BDRVVmdkState *s = bs->opaque; int ret; uint64_t n, index_in_cluster; - uint64_t extent_begin_sector, extent_relative_sector_num; VmdkExtent *extent = NULL; uint64_t cluster_offset; @@ -1393,9 +1435,7 @@ static int vmdk_read(BlockDriverState *bs, int64_t sector_num, ret = get_cluster_offset(bs, extent, NULL, sector_num << 9, false, &cluster_offset, 0, 0); - extent_begin_sector = extent->end_sector - extent->sectors; - extent_relative_sector_num = sector_num - extent_begin_sector; - index_in_cluster = extent_relative_sector_num % extent->cluster_sectors; + index_in_cluster = vmdk_find_index_in_cluster(extent, sector_num); n = extent->cluster_sectors - index_in_cluster; if (n > nb_sectors) { n = nb_sectors; @@ -1457,7 +1497,6 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num, VmdkExtent *extent = NULL; int ret; int64_t index_in_cluster, n; - uint64_t extent_begin_sector, extent_relative_sector_num; uint64_t cluster_offset; VmdkMetaData m_data; @@ -1473,9 +1512,7 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num, if (!extent) { return -EIO; } - extent_begin_sector = extent->end_sector - extent->sectors; - extent_relative_sector_num = sector_num - extent_begin_sector; - index_in_cluster = extent_relative_sector_num % extent->cluster_sectors; + index_in_cluster = vmdk_find_index_in_cluster(extent, sector_num); n = extent->cluster_sectors - index_in_cluster; if (n > nb_sectors) { n = nb_sectors; @@ -1538,7 +1575,7 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num, /* update CID on the first write every time the virtual disk is * opened */ if (!s->cid_updated) { - ret = vmdk_write_cid(bs, time(NULL)); + ret = vmdk_write_cid(bs, g_random_int()); if (ret < 0) { return ret; } @@ -1772,10 +1809,15 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp) int ret = 0; bool flat, split, compress; GString *ext_desc_lines; - char path[PATH_MAX], prefix[PATH_MAX], postfix[PATH_MAX]; + char *path = g_malloc0(PATH_MAX); + char *prefix = g_malloc0(PATH_MAX); + char *postfix = g_malloc0(PATH_MAX); + char *desc_line = g_malloc0(BUF_SIZE); + char *ext_filename = g_malloc0(PATH_MAX); + char *desc_filename = g_malloc0(PATH_MAX); const int64_t split_size = 0x80000000; /* VMDK has constant split size */ const char *desc_extent_line; - char parent_desc_line[BUF_SIZE] = ""; + char *parent_desc_line = g_malloc0(BUF_SIZE); uint32_t parent_cid = 0xffffffff; uint32_t number_heads = 16; bool zeroed_grain = false; @@ -1868,8 +1910,19 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp) } if (backing_file) { BlockDriverState *bs = NULL; - ret = bdrv_open(&bs, backing_file, NULL, NULL, BDRV_O_NO_BACKING, NULL, + char *full_backing = g_new0(char, PATH_MAX); + bdrv_get_full_backing_filename_from_filename(filename, backing_file, + full_backing, PATH_MAX, + &local_err); + if (local_err) { + g_free(full_backing); + error_propagate(errp, local_err); + ret = -ENOENT; + goto exit; + } + ret = bdrv_open(&bs, full_backing, NULL, NULL, BDRV_O_NO_BACKING, NULL, errp); + g_free(full_backing); if (ret != 0) { goto exit; } @@ -1880,33 +1933,27 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp) } parent_cid = vmdk_read_cid(bs, 0); bdrv_unref(bs); - snprintf(parent_desc_line, sizeof(parent_desc_line), + snprintf(parent_desc_line, BUF_SIZE, "parentFileNameHint=\"%s\"", backing_file); } /* Create extents */ filesize = total_size; while (filesize > 0) { - char desc_line[BUF_SIZE]; - char ext_filename[PATH_MAX]; - char desc_filename[PATH_MAX]; int64_t size = filesize; if (split && size > split_size) { size = split_size; } if (split) { - snprintf(desc_filename, sizeof(desc_filename), "%s-%c%03d%s", + snprintf(desc_filename, PATH_MAX, "%s-%c%03d%s", prefix, flat ? 'f' : 's', ++idx, postfix); } else if (flat) { - snprintf(desc_filename, sizeof(desc_filename), "%s-flat%s", - prefix, postfix); + snprintf(desc_filename, PATH_MAX, "%s-flat%s", prefix, postfix); } else { - snprintf(desc_filename, sizeof(desc_filename), "%s%s", - prefix, postfix); + snprintf(desc_filename, PATH_MAX, "%s%s", prefix, postfix); } - snprintf(ext_filename, sizeof(ext_filename), "%s%s", - path, desc_filename); + snprintf(ext_filename, PATH_MAX, "%s%s", path, desc_filename); if (vmdk_create_extent(ext_filename, size, flat, compress, zeroed_grain, opts, errp)) { @@ -1916,13 +1963,13 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp) filesize -= size; /* Format description line */ - snprintf(desc_line, sizeof(desc_line), + snprintf(desc_line, BUF_SIZE, desc_extent_line, size / BDRV_SECTOR_SIZE, desc_filename); g_string_append(ext_desc_lines, desc_line); } /* generate descriptor file */ desc = g_strdup_printf(desc_template, - (uint32_t)time(NULL), + g_random_int(), parent_cid, fmt, parent_desc_line, @@ -1971,6 +2018,13 @@ exit: g_free(backing_file); g_free(fmt); g_free(desc); + g_free(path); + g_free(prefix); + g_free(postfix); + g_free(desc_line); + g_free(ext_filename); + g_free(desc_filename); + g_free(parent_desc_line); g_string_free(ext_desc_lines, true); return ret; } diff --git a/block/vpc.c b/block/vpc.c index 38c4f02..8ab30d6 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -46,6 +46,7 @@ enum vhd_type { #define VHD_TIMESTAMP_BASE 946684800 #define VHD_MAX_SECTORS (65535LL * 255 * 255) +#define VHD_MAX_GEOMETRY (65535LL * 16 * 255) // always big-endian typedef struct vhd_footer { @@ -65,7 +66,7 @@ typedef struct vhd_footer { char creator_os[4]; // "Wi2k" uint64_t orig_size; - uint64_t size; + uint64_t current_size; uint16_t cyls; uint8_t heads; @@ -167,6 +168,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, uint8_t buf[HEADER_SIZE]; uint32_t checksum; uint64_t computed_size; + uint64_t pagetable_size; int disk_type = VHD_DYNAMIC; int ret; @@ -215,13 +217,12 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, bs->total_sectors = (int64_t) be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl; - /* images created with disk2vhd report a far higher virtual size - * than expected with the cyls * heads * sectors_per_cyl formula. - * use the footer->size instead if the image was created with - * disk2vhd. - */ - if (!strncmp(footer->creator_app, "d2v", 4)) { - bs->total_sectors = be64_to_cpu(footer->size) / BDRV_SECTOR_SIZE; + /* Images that have exactly the maximum geometry are probably bigger and + * would be truncated if we adhered to the geometry for them. Rely on + * footer->current_size for them. */ + if (bs->total_sectors == VHD_MAX_GEOMETRY) { + bs->total_sectors = be64_to_cpu(footer->current_size) / + BDRV_SECTOR_SIZE; } /* Allow a maximum disk size of approximately 2 TB */ @@ -269,7 +270,17 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - s->pagetable = qemu_try_blockalign(bs->file, s->max_table_entries * 4); + if (s->max_table_entries > SIZE_MAX / 4 || + s->max_table_entries > (int) INT_MAX / 4) { + error_setg(errp, "Max Table Entries too large (%" PRId32 ")", + s->max_table_entries); + ret = -EINVAL; + goto fail; + } + + pagetable_size = (uint64_t) s->max_table_entries * 4; + + s->pagetable = qemu_try_blockalign(bs->file, pagetable_size); if (s->pagetable == NULL) { ret = -ENOMEM; goto fail; @@ -277,14 +288,13 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, s->bat_offset = be64_to_cpu(dyndisk_header->table_offset); - ret = bdrv_pread(bs->file, s->bat_offset, s->pagetable, - s->max_table_entries * 4); + ret = bdrv_pread(bs->file, s->bat_offset, s->pagetable, pagetable_size); if (ret < 0) { goto fail; } s->free_data_block_offset = - (s->bat_offset + (s->max_table_entries * 4) + 511) & ~511; + ROUND_UP(s->bat_offset + pagetable_size, 512); for (i = 0; i < s->max_table_entries; i++) { be32_to_cpus(&s->pagetable[i]); @@ -376,38 +386,6 @@ static inline int64_t get_sector_offset(BlockDriverState *bs, bdrv_pwrite_sync(bs->file, bitmap_offset, bitmap, s->bitmap_size); } -// printf("sector: %" PRIx64 ", index: %x, offset: %x, bioff: %" PRIx64 ", bloff: %" PRIx64 "\n", -// sector_num, pagetable_index, pageentry_index, -// bitmap_offset, block_offset); - -// disabled by reason -#if 0 -#ifdef CACHE - if (bitmap_offset != s->last_bitmap) - { - lseek(s->fd, bitmap_offset, SEEK_SET); - - s->last_bitmap = bitmap_offset; - - // Scary! Bitmap is stored as big endian 32bit entries, - // while we used to look it up byte by byte - read(s->fd, s->pageentry_u8, 512); - for (i = 0; i < 128; i++) - be32_to_cpus(&s->pageentry_u32[i]); - } - - if ((s->pageentry_u8[pageentry_index / 8] >> (pageentry_index % 8)) & 1) - return -1; -#else - lseek(s->fd, bitmap_offset + (pageentry_index / 8), SEEK_SET); - - read(s->fd, &bitmap_entry, 1); - - if ((bitmap_entry >> (pageentry_index % 8)) & 1) - return -1; // not allocated -#endif -#endif - return block_offset; } @@ -597,6 +575,49 @@ static coroutine_fn int vpc_co_write(BlockDriverState *bs, int64_t sector_num, return ret; } +static int64_t coroutine_fn vpc_co_get_block_status(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, int *pnum) +{ + BDRVVPCState *s = bs->opaque; + VHDFooter *footer = (VHDFooter*) s->footer_buf; + int64_t start, offset; + bool allocated; + int n; + + if (be32_to_cpu(footer->type) == VHD_FIXED) { + *pnum = nb_sectors; + return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | BDRV_BLOCK_DATA | + (sector_num << BDRV_SECTOR_BITS); + } + + offset = get_sector_offset(bs, sector_num, 0); + start = offset; + allocated = (offset != -1); + *pnum = 0; + + do { + /* All sectors in a block are contiguous (without using the bitmap) */ + n = ROUND_UP(sector_num + 1, s->block_size / BDRV_SECTOR_SIZE) + - sector_num; + n = MIN(n, nb_sectors); + + *pnum += n; + sector_num += n; + nb_sectors -= n; + /* *pnum can't be greater than one block for allocated + * sectors since there is always a bitmap in between. */ + if (allocated) { + return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start; + } + if (nb_sectors == 0) { + break; + } + offset = get_sector_offset(bs, sector_num, 0); + } while (offset == -1); + + return 0; +} + /* * Calculates the number of cylinders, heads and sectors per cylinder * based on a given number of sectors. This is the algorithm described @@ -614,26 +635,20 @@ static int calculate_geometry(int64_t total_sectors, uint16_t* cyls, { uint32_t cyls_times_heads; - /* Allow a maximum disk size of approximately 2 TB */ - if (total_sectors > 65535LL * 255 * 255) { - return -EFBIG; - } + total_sectors = MIN(total_sectors, VHD_MAX_GEOMETRY); - if (total_sectors > 65535 * 16 * 63) { + if (total_sectors >= 65535LL * 16 * 63) { *secs_per_cyl = 255; - if (total_sectors > 65535 * 16 * 255) { - *heads = 255; - } else { - *heads = 16; - } + *heads = 16; cyls_times_heads = total_sectors / *secs_per_cyl; } else { *secs_per_cyl = 17; cyls_times_heads = total_sectors / *secs_per_cyl; *heads = (cyls_times_heads + 1023) / 1024; - if (*heads < 4) + if (*heads < 4) { *heads = 4; + } if (cyls_times_heads >= (*heads * 1024) || *heads > 16) { *secs_per_cyl = 31; @@ -789,19 +804,28 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp) * Calculate matching total_size and geometry. Increase the number of * sectors requested until we get enough (or fail). This ensures that * qemu-img convert doesn't truncate images, but rather rounds up. + * + * If the image size can't be represented by a spec conform CHS geometry, + * we set the geometry to 65535 x 16 x 255 (CxHxS) sectors and use + * the image size from the VHD footer to calculate total_sectors. */ - total_sectors = total_size / BDRV_SECTOR_SIZE; + total_sectors = MIN(VHD_MAX_GEOMETRY, total_size / BDRV_SECTOR_SIZE); for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) { - if (calculate_geometry(total_sectors + i, &cyls, &heads, - &secs_per_cyl)) - { + calculate_geometry(total_sectors + i, &cyls, &heads, &secs_per_cyl); + } + + if ((int64_t)cyls * heads * secs_per_cyl == VHD_MAX_GEOMETRY) { + total_sectors = total_size / BDRV_SECTOR_SIZE; + /* Allow a maximum disk size of approximately 2 TB */ + if (total_sectors > VHD_MAX_SECTORS) { ret = -EFBIG; goto out; } + } else { + total_sectors = (int64_t)cyls * heads * secs_per_cyl; + total_size = total_sectors * BDRV_SECTOR_SIZE; } - total_sectors = (int64_t) cyls * heads * secs_per_cyl; - /* Prepare the Hard Disk Footer */ memset(buf, 0, 1024); @@ -822,13 +846,8 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp) /* Version of Virtual PC 2007 */ footer->major = cpu_to_be16(0x0005); footer->minor = cpu_to_be16(0x0003); - if (disk_type == VHD_DYNAMIC) { - footer->orig_size = cpu_to_be64(total_sectors * 512); - footer->size = cpu_to_be64(total_sectors * 512); - } else { - footer->orig_size = cpu_to_be64(total_size); - footer->size = cpu_to_be64(total_size); - } + footer->orig_size = cpu_to_be64(total_size); + footer->current_size = cpu_to_be64(total_size); footer->cyls = cpu_to_be16(cyls); footer->heads = heads; footer->secs_per_cyl = secs_per_cyl; @@ -893,11 +912,6 @@ static QemuOptsList vpc_create_opts = { "Type of virtual hard disk format. Supported formats are " "{dynamic (default) | fixed} " }, - { - .name = BLOCK_OPT_NOCOW, - .type = QEMU_OPT_BOOL, - .help = "Turn off copy-on-write (valid only on btrfs)" - }, { /* end of list */ } } }; @@ -912,8 +926,9 @@ static BlockDriver bdrv_vpc = { .bdrv_reopen_prepare = vpc_reopen_prepare, .bdrv_create = vpc_create, - .bdrv_read = vpc_co_read, - .bdrv_write = vpc_co_write, + .bdrv_read = vpc_co_read, + .bdrv_write = vpc_co_write, + .bdrv_co_get_block_status = vpc_co_get_block_status, .bdrv_get_info = vpc_get_info, diff --git a/block/vvfat.c b/block/vvfat.c index cefe3a4..9be632f 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -2909,17 +2909,24 @@ static int enable_write_target(BDRVVVFATState *s, Error **errp) array_init(&(s->commits), sizeof(commit_t)); - s->qcow_filename = g_malloc(1024); - ret = get_tmp_filename(s->qcow_filename, 1024); + s->qcow_filename = g_malloc(PATH_MAX); + ret = get_tmp_filename(s->qcow_filename, PATH_MAX); if (ret < 0) { error_setg_errno(errp, -ret, "can't create temporary file"); goto err; } bdrv_qcow = bdrv_find_format("qcow"); + if (!bdrv_qcow) { + error_setg(errp, "Failed to locate qcow driver"); + ret = -ENOENT; + goto err; + } + opts = qemu_opts_create(bdrv_qcow->create_opts, NULL, 0, &error_abort); - qemu_opt_set_number(opts, BLOCK_OPT_SIZE, s->sector_count * 512); - qemu_opt_set(opts, BLOCK_OPT_BACKING_FILE, "fat:"); + qemu_opt_set_number(opts, BLOCK_OPT_SIZE, s->sector_count * 512, + &error_abort); + qemu_opt_set(opts, BLOCK_OPT_BACKING_FILE, "fat:", &error_abort); ret = bdrv_create(bdrv_qcow, s->qcow_filename, opts, errp); qemu_opts_del(opts); diff --git a/block/write-threshold.c b/block/write-threshold.c new file mode 100644 index 0000000..a53c1f5 --- /dev/null +++ b/block/write-threshold.c @@ -0,0 +1,125 @@ +/* + * QEMU System Emulator block write threshold notification + * + * Copyright Red Hat, Inc. 2014 + * + * Authors: + * Francesco Romani + * + * 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 "block/block_int.h" +#include "block/coroutine.h" +#include "block/write-threshold.h" +#include "qemu/notify.h" +#include "qapi-event.h" +#include "qmp-commands.h" + + +uint64_t bdrv_write_threshold_get(const BlockDriverState *bs) +{ + return bs->write_threshold_offset; +} + +bool bdrv_write_threshold_is_set(const BlockDriverState *bs) +{ + return bs->write_threshold_offset > 0; +} + +static void write_threshold_disable(BlockDriverState *bs) +{ + if (bdrv_write_threshold_is_set(bs)) { + notifier_with_return_remove(&bs->write_threshold_notifier); + bs->write_threshold_offset = 0; + } +} + +uint64_t bdrv_write_threshold_exceeded(const BlockDriverState *bs, + const BdrvTrackedRequest *req) +{ + if (bdrv_write_threshold_is_set(bs)) { + if (req->offset > bs->write_threshold_offset) { + return (req->offset - bs->write_threshold_offset) + req->bytes; + } + if ((req->offset + req->bytes) > bs->write_threshold_offset) { + return (req->offset + req->bytes) - bs->write_threshold_offset; + } + } + return 0; +} + +static int coroutine_fn before_write_notify(NotifierWithReturn *notifier, + void *opaque) +{ + BdrvTrackedRequest *req = opaque; + BlockDriverState *bs = req->bs; + uint64_t amount = 0; + + amount = bdrv_write_threshold_exceeded(bs, req); + if (amount > 0) { + qapi_event_send_block_write_threshold( + bs->node_name, + amount, + bs->write_threshold_offset, + &error_abort); + + /* autodisable to avoid flooding the monitor */ + write_threshold_disable(bs); + } + + return 0; /* should always let other notifiers run */ +} + +static void write_threshold_register_notifier(BlockDriverState *bs) +{ + bs->write_threshold_notifier.notify = before_write_notify; + notifier_with_return_list_add(&bs->before_write_notifiers, + &bs->write_threshold_notifier); +} + +static void write_threshold_update(BlockDriverState *bs, + int64_t threshold_bytes) +{ + bs->write_threshold_offset = threshold_bytes; +} + +void bdrv_write_threshold_set(BlockDriverState *bs, uint64_t threshold_bytes) +{ + if (bdrv_write_threshold_is_set(bs)) { + if (threshold_bytes > 0) { + write_threshold_update(bs, threshold_bytes); + } else { + write_threshold_disable(bs); + } + } else { + if (threshold_bytes > 0) { + /* avoid multiple registration */ + write_threshold_register_notifier(bs); + write_threshold_update(bs, threshold_bytes); + } + /* discard bogus disable request */ + } +} + +void qmp_block_set_write_threshold(const char *node_name, + uint64_t threshold_bytes, + Error **errp) +{ + BlockDriverState *bs; + AioContext *aio_context; + + bs = bdrv_find_node(node_name); + if (!bs) { + error_setg(errp, "Device '%s' not found", node_name); + return; + } + + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + + bdrv_write_threshold_set(bs, threshold_bytes); + + aio_context_release(aio_context); +} diff --git a/blockdev-nbd.c b/blockdev-nbd.c index 06f901e..85cda4c 100644 --- a/blockdev-nbd.c +++ b/blockdev-nbd.c @@ -10,6 +10,7 @@ */ #include "sysemu/blockdev.h" +#include "sysemu/block-backend.h" #include "hw/block/block.h" #include "monitor/monitor.h" #include "qapi/qmp/qerror.h" @@ -46,8 +47,9 @@ void qmp_nbd_server_start(SocketAddress *addr, Error **errp) } } -/* Hook into the BlockDriverState notifiers to close the export when - * the file is closed. +/* + * Hook into the BlockBackend notifiers to close the export when the + * backend is closed. */ typedef struct NBDCloseNotifier { Notifier n; @@ -73,7 +75,7 @@ static void nbd_close_notifier(Notifier *n, void *data) void qmp_nbd_server_add(const char *device, bool has_writable, bool writable, Error **errp) { - BlockDriverState *bs; + BlockBackend *blk; NBDExport *exp; NBDCloseNotifier *n; @@ -87,12 +89,12 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable, return; } - bs = bdrv_find(device); - if (!bs) { + blk = blk_by_name(device); + if (!blk) { error_set(errp, QERR_DEVICE_NOT_FOUND, device); return; } - if (!bdrv_is_inserted(bs)) { + if (!blk_is_inserted(blk)) { error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); return; } @@ -100,18 +102,22 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable, if (!has_writable) { writable = false; } - if (bdrv_is_read_only(bs)) { + if (blk_is_read_only(blk)) { writable = false; } - exp = nbd_export_new(bs, 0, -1, writable ? 0 : NBD_FLAG_READ_ONLY, NULL); + exp = nbd_export_new(blk, 0, -1, writable ? 0 : NBD_FLAG_READ_ONLY, NULL, + errp); + if (!exp) { + return; + } nbd_export_set_name(exp, device); n = g_new0(NBDCloseNotifier, 1); n->n.notify = nbd_close_notifier; n->exp = exp; - bdrv_add_close_notifier(bs, &n->n); + blk_add_close_notifier(blk, &n->n); QTAILQ_INSERT_TAIL(&close_notifiers, n, next); } diff --git a/blockdev.c b/blockdev.c index 57910b8..dde4061 100644 --- a/blockdev.c +++ b/blockdev.c @@ -180,21 +180,19 @@ QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file, const char *optstr) { QemuOpts *opts; - char buf[32]; opts = drive_def(optstr); if (!opts) { return NULL; } if (type != IF_DEFAULT) { - qemu_opt_set(opts, "if", if_name[type]); + qemu_opt_set(opts, "if", if_name[type], &error_abort); } if (index >= 0) { - snprintf(buf, sizeof(buf), "%d", index); - qemu_opt_set(opts, "index", buf); + qemu_opt_set_number(opts, "index", index, &error_abort); } if (file) - qemu_opt_set(opts, "file", file); + qemu_opt_set(opts, "file", file, &error_abort); return opts; } @@ -354,13 +352,11 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, ThrottleConfig cfg; int snapshot = 0; bool copy_on_read; - int ret; Error *error = NULL; QemuOpts *opts; const char *id; bool has_driver_specific_opts; BlockdevDetectZeroesOptions detect_zeroes; - BlockDriver *drv = NULL; /* Check common options by copying from bs_opts to opts, all other options * stay in bs_opts for processing by bdrv_open(). */ @@ -426,11 +422,11 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, goto early_err; } - drv = bdrv_find_format(buf); - if (!drv) { - error_setg(errp, "'%s' invalid format", buf); + if (qdict_haskey(bs_opts, "driver")) { + error_setg(errp, "Cannot specify both 'driver' and 'format'"); goto early_err; } + qdict_put(bs_opts, "driver", qstring_from_str(buf)); } /* disk I/O throttling */ @@ -505,70 +501,64 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, } /* init */ - blk = blk_new_with_bs(qemu_opts_id(opts), errp); - if (!blk) { - goto early_err; - } - bs = blk_bs(blk); - bs->open_flags = snapshot ? BDRV_O_SNAPSHOT : 0; - bs->read_only = ro; - bs->detect_zeroes = detect_zeroes; - - bdrv_set_on_error(bs, on_read_error, on_write_error); + if ((!file || !*file) && !has_driver_specific_opts) { + blk = blk_new_with_bs(qemu_opts_id(opts), errp); + if (!blk) { + goto early_err; + } - /* disk I/O throttling */ - if (throttle_enabled(&cfg)) { - bdrv_io_limits_enable(bs); - bdrv_set_io_limits(bs, &cfg); - } + bs = blk_bs(blk); + bs->open_flags = snapshot ? BDRV_O_SNAPSHOT : 0; + bs->read_only = ro; - if (!file || !*file) { - if (has_driver_specific_opts) { + QDECREF(bs_opts); + } else { + if (file && !*file) { file = NULL; - } else { - QDECREF(bs_opts); - qemu_opts_del(opts); - return blk; } - } - if (snapshot) { - /* always use cache=unsafe with snapshot */ - bdrv_flags &= ~BDRV_O_CACHE_MASK; - bdrv_flags |= (BDRV_O_SNAPSHOT|BDRV_O_CACHE_WB|BDRV_O_NO_FLUSH); - } - if (copy_on_read) { - bdrv_flags |= BDRV_O_COPY_ON_READ; - } + if (snapshot) { + /* always use cache=unsafe with snapshot */ + bdrv_flags &= ~BDRV_O_CACHE_MASK; + bdrv_flags |= (BDRV_O_SNAPSHOT|BDRV_O_CACHE_WB|BDRV_O_NO_FLUSH); + } + + if (copy_on_read) { + bdrv_flags |= BDRV_O_COPY_ON_READ; + } + + if (runstate_check(RUN_STATE_INMIGRATE)) { + bdrv_flags |= BDRV_O_INCOMING; + } - if (runstate_check(RUN_STATE_INMIGRATE)) { - bdrv_flags |= BDRV_O_INCOMING; + bdrv_flags |= ro ? 0 : BDRV_O_RDWR; + + blk = blk_new_open(qemu_opts_id(opts), file, NULL, bs_opts, bdrv_flags, + errp); + if (!blk) { + goto err_no_bs_opts; + } + bs = blk_bs(blk); } - bdrv_flags |= ro ? 0 : BDRV_O_RDWR; + bs->detect_zeroes = detect_zeroes; - QINCREF(bs_opts); - ret = bdrv_open(&bs, file, NULL, bs_opts, bdrv_flags, drv, &error); - assert(bs == blk_bs(blk)); + bdrv_set_on_error(bs, on_read_error, on_write_error); - if (ret < 0) { - error_setg(errp, "could not open disk image %s: %s", - file ?: blk_name(blk), error_get_pretty(error)); - error_free(error); - goto err; + /* disk I/O throttling */ + if (throttle_enabled(&cfg)) { + bdrv_io_limits_enable(bs); + bdrv_set_io_limits(bs, &cfg); } if (bdrv_key_required(bs)) { autostart = 0; } - QDECREF(bs_opts); +err_no_bs_opts: qemu_opts_del(opts); - return blk; -err: - blk_unref(blk); early_err: qemu_opts_del(opts); err_no_opts: @@ -592,7 +582,7 @@ static void qemu_opt_rename(QemuOpts *opts, const char *from, const char *to, /* rename all items in opts */ while ((value = qemu_opt_get(opts, from))) { - qemu_opt_set(opts, to, value); + qemu_opt_set(opts, to, value, &error_abort); qemu_opt_unset(opts, from); } } @@ -728,8 +718,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) qemu_opt_rename(all_opts, opt_renames[i].from, opt_renames[i].to, &local_err); if (local_err) { - error_report("%s", error_get_pretty(local_err)); - error_free(local_err); + error_report_err(local_err); return NULL; } } @@ -746,15 +735,15 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) /* Specific options take precedence */ if (!qemu_opt_get(all_opts, "cache.writeback")) { qemu_opt_set_bool(all_opts, "cache.writeback", - !!(flags & BDRV_O_CACHE_WB)); + !!(flags & BDRV_O_CACHE_WB), &error_abort); } if (!qemu_opt_get(all_opts, "cache.direct")) { qemu_opt_set_bool(all_opts, "cache.direct", - !!(flags & BDRV_O_NOCACHE)); + !!(flags & BDRV_O_NOCACHE), &error_abort); } if (!qemu_opt_get(all_opts, "cache.no-flush")) { qemu_opt_set_bool(all_opts, "cache.no-flush", - !!(flags & BDRV_O_NO_FLUSH)); + !!(flags & BDRV_O_NO_FLUSH), &error_abort); } qemu_opt_unset(all_opts, "cache"); } @@ -767,8 +756,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) &error_abort); qemu_opts_absorb_qdict(legacy_opts, bs_opts, &local_err); if (local_err) { - error_report("%s", error_get_pretty(local_err)); - error_free(local_err); + error_report_err(local_err); goto fail; } @@ -945,13 +933,14 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) devopts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, &error_abort); if (arch_type == QEMU_ARCH_S390X) { - qemu_opt_set(devopts, "driver", "virtio-blk-s390"); + qemu_opt_set(devopts, "driver", "virtio-blk-s390", &error_abort); } else { - qemu_opt_set(devopts, "driver", "virtio-blk-pci"); + qemu_opt_set(devopts, "driver", "virtio-blk-pci", &error_abort); } - qemu_opt_set(devopts, "drive", qdict_get_str(bs_opts, "id")); + qemu_opt_set(devopts, "drive", qdict_get_str(bs_opts, "id"), + &error_abort); if (devaddr) { - qemu_opt_set(devopts, "addr", devaddr); + qemu_opt_set(devopts, "addr", devaddr, &error_abort); } } @@ -983,8 +972,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) bs_opts = NULL; if (!blk) { if (local_err) { - error_report("%s", error_get_pretty(local_err)); - error_free(local_err); + error_report_err(local_err); } goto fail; } else { @@ -1025,21 +1013,21 @@ fail: return dinfo; } -void do_commit(Monitor *mon, const QDict *qdict) +void hmp_commit(Monitor *mon, const QDict *qdict) { const char *device = qdict_get_str(qdict, "device"); - BlockDriverState *bs; + BlockBackend *blk; int ret; if (!strcmp(device, "all")) { ret = bdrv_commit_all(); } else { - bs = bdrv_find(device); - if (!bs) { + blk = blk_by_name(device); + if (!blk) { monitor_printf(mon, "Device '%s' not found\n", device); return; } - ret = bdrv_commit(bs); + ret = bdrv_commit(blk_bs(blk)); } if (ret < 0) { monitor_printf(mon, "'commit' error for '%s': %s\n", device, @@ -1104,16 +1092,20 @@ SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device, const char *name, Error **errp) { - BlockDriverState *bs = bdrv_find(device); + BlockDriverState *bs; + BlockBackend *blk; + AioContext *aio_context; QEMUSnapshotInfo sn; Error *local_err = NULL; SnapshotInfo *info = NULL; int ret; - if (!bs) { + blk = blk_by_name(device); + if (!blk) { error_set(errp, QERR_DEVICE_NOT_FOUND, device); return NULL; } + bs = blk_bs(blk); if (!has_id) { id = NULL; @@ -1128,25 +1120,34 @@ SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device, return NULL; } + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE, errp)) { + goto out_aio_context; + } + ret = bdrv_snapshot_find_by_id_and_name(bs, id, name, &sn, &local_err); if (local_err) { error_propagate(errp, local_err); - return NULL; + goto out_aio_context; } if (!ret) { error_setg(errp, "Snapshot with id '%s' and name '%s' does not exist on " "device '%s'", STR_OR_NULL(id), STR_OR_NULL(name), device); - return NULL; + goto out_aio_context; } bdrv_snapshot_delete(bs, id, name, &local_err); if (local_err) { error_propagate(errp, local_err); - return NULL; + goto out_aio_context; } + aio_context_release(aio_context); + info = g_new0(SnapshotInfo, 1); info->id = g_strdup(sn.id_str); info->name = g_strdup(sn.name); @@ -1157,9 +1158,13 @@ SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device, info->vm_clock_sec = sn.vm_clock_nsec / 1000000000; return info; + +out_aio_context: + aio_context_release(aio_context); + return NULL; } -/* New and old BlockDriverState structs for group snapshots */ +/* New and old BlockDriverState structs for atomic group operations */ typedef struct BlkTransactionState BlkTransactionState; @@ -1193,6 +1198,7 @@ struct BlkTransactionState { typedef struct InternalSnapshotState { BlkTransactionState common; BlockDriverState *bs; + AioContext *aio_context; QEMUSnapshotInfo sn; } InternalSnapshotState; @@ -1202,6 +1208,7 @@ static void internal_snapshot_prepare(BlkTransactionState *common, Error *local_err = NULL; const char *device; const char *name; + BlockBackend *blk; BlockDriverState *bs; QEMUSnapshotInfo old_sn, *sn; bool ret; @@ -1220,17 +1227,26 @@ static void internal_snapshot_prepare(BlkTransactionState *common, name = internal->name; /* 2. check for validation */ - bs = bdrv_find(device); - if (!bs) { + blk = blk_by_name(device); + if (!blk) { error_set(errp, QERR_DEVICE_NOT_FOUND, device); return; } + bs = blk_bs(blk); + + /* AioContext is released in .clean() */ + state->aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(state->aio_context); if (!bdrv_is_inserted(bs)) { error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); return; } + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, errp)) { + return; + } + if (bdrv_is_read_only(bs)) { error_set(errp, QERR_DEVICE_IS_READ_ONLY, device); return; @@ -1303,11 +1319,22 @@ static void internal_snapshot_abort(BlkTransactionState *common) } } +static void internal_snapshot_clean(BlkTransactionState *common) +{ + InternalSnapshotState *state = DO_UPCAST(InternalSnapshotState, + common, common); + + if (state->aio_context) { + aio_context_release(state->aio_context); + } +} + /* external snapshot private data */ typedef struct ExternalSnapshotState { BlkTransactionState common; BlockDriverState *old_bs; BlockDriverState *new_bs; + AioContext *aio_context; } ExternalSnapshotState; static void external_snapshot_prepare(BlkTransactionState *common, @@ -1374,6 +1401,10 @@ static void external_snapshot_prepare(BlkTransactionState *common, return; } + /* Acquire AioContext now so any threads operating on old_bs stop */ + state->aio_context = bdrv_get_aio_context(state->old_bs); + aio_context_acquire(state->aio_context); + if (!bdrv_is_inserted(state->old_bs)) { error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); return; @@ -1432,6 +1463,8 @@ static void external_snapshot_commit(BlkTransactionState *common) ExternalSnapshotState *state = DO_UPCAST(ExternalSnapshotState, common, common); + bdrv_set_aio_context(state->new_bs, state->aio_context); + /* This removes our old bs and adds the new bs */ bdrv_append(state->new_bs, state->old_bs); /* We don't need (or want) to use the transactional @@ -1439,6 +1472,8 @@ static void external_snapshot_commit(BlkTransactionState *common) * don't want to abort all of them if one of them fails the reopen */ bdrv_reopen(state->new_bs, state->new_bs->open_flags & ~BDRV_O_RDWR, NULL); + + aio_context_release(state->aio_context); } static void external_snapshot_abort(BlkTransactionState *common) @@ -1448,23 +1483,40 @@ static void external_snapshot_abort(BlkTransactionState *common) if (state->new_bs) { bdrv_unref(state->new_bs); } + if (state->aio_context) { + aio_context_release(state->aio_context); + } } typedef struct DriveBackupState { BlkTransactionState common; BlockDriverState *bs; + AioContext *aio_context; BlockJob *job; } DriveBackupState; static void drive_backup_prepare(BlkTransactionState *common, Error **errp) { DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common); + BlockDriverState *bs; + BlockBackend *blk; DriveBackup *backup; Error *local_err = NULL; assert(common->action->kind == TRANSACTION_ACTION_KIND_DRIVE_BACKUP); backup = common->action->drive_backup; + blk = blk_by_name(backup->device); + if (!blk) { + error_set(errp, QERR_DEVICE_NOT_FOUND, backup->device); + return; + } + bs = blk_bs(blk); + + /* AioContext is released in .clean() */ + state->aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(state->aio_context); + qmp_drive_backup(backup->device, backup->target, backup->has_format, backup->format, backup->sync, @@ -1475,12 +1527,10 @@ static void drive_backup_prepare(BlkTransactionState *common, Error **errp) &local_err); if (local_err) { error_propagate(errp, local_err); - state->bs = NULL; - state->job = NULL; return; } - state->bs = bdrv_find(backup->device); + state->bs = bs; state->job = state->bs->job; } @@ -1495,6 +1545,91 @@ static void drive_backup_abort(BlkTransactionState *common) } } +static void drive_backup_clean(BlkTransactionState *common) +{ + DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common); + + if (state->aio_context) { + aio_context_release(state->aio_context); + } +} + +typedef struct BlockdevBackupState { + BlkTransactionState common; + BlockDriverState *bs; + BlockJob *job; + AioContext *aio_context; +} BlockdevBackupState; + +static void blockdev_backup_prepare(BlkTransactionState *common, Error **errp) +{ + BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); + BlockdevBackup *backup; + BlockDriverState *bs, *target; + BlockBackend *blk; + Error *local_err = NULL; + + assert(common->action->kind == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP); + backup = common->action->blockdev_backup; + + blk = blk_by_name(backup->device); + if (!blk) { + error_setg(errp, "Device '%s' not found", backup->device); + return; + } + bs = blk_bs(blk); + + blk = blk_by_name(backup->target); + if (!blk) { + error_setg(errp, "Device '%s' not found", backup->target); + return; + } + target = blk_bs(blk); + + /* AioContext is released in .clean() */ + state->aio_context = bdrv_get_aio_context(bs); + if (state->aio_context != bdrv_get_aio_context(target)) { + state->aio_context = NULL; + error_setg(errp, "Backup between two IO threads is not implemented"); + return; + } + aio_context_acquire(state->aio_context); + + qmp_blockdev_backup(backup->device, backup->target, + backup->sync, + backup->has_speed, backup->speed, + backup->has_on_source_error, backup->on_source_error, + backup->has_on_target_error, backup->on_target_error, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + state->bs = bs; + state->job = state->bs->job; +} + +static void blockdev_backup_abort(BlkTransactionState *common) +{ + BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); + BlockDriverState *bs = state->bs; + + /* Only cancel if it's the job we started */ + if (bs && bs->job && bs->job == state->job) { + block_job_cancel_sync(bs->job); + } +} + +static void blockdev_backup_clean(BlkTransactionState *common) +{ + BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); + + if (state->aio_context) { + aio_context_release(state->aio_context); + } +} + static void abort_prepare(BlkTransactionState *common, Error **errp) { error_setg(errp, "Transaction aborted using Abort action"); @@ -1516,6 +1651,13 @@ static const BdrvActionOps actions[] = { .instance_size = sizeof(DriveBackupState), .prepare = drive_backup_prepare, .abort = drive_backup_abort, + .clean = drive_backup_clean, + }, + [TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP] = { + .instance_size = sizeof(BlockdevBackupState), + .prepare = blockdev_backup_prepare, + .abort = blockdev_backup_abort, + .clean = blockdev_backup_clean, }, [TRANSACTION_ACTION_KIND_ABORT] = { .instance_size = sizeof(BlkTransactionState), @@ -1526,13 +1668,13 @@ static const BdrvActionOps actions[] = { .instance_size = sizeof(InternalSnapshotState), .prepare = internal_snapshot_prepare, .abort = internal_snapshot_abort, + .clean = internal_snapshot_clean, }, }; /* - * 'Atomic' group snapshots. The snapshots are taken as a set, and if any fail - * then we do not pivot any of the devices in the group, and abandon the - * snapshots + * 'Atomic' group operations. The operations are performed as a set, and if + * any fail then we roll back all operations in the group. */ void qmp_transaction(TransactionActionList *dev_list, Error **errp) { @@ -1543,10 +1685,10 @@ void qmp_transaction(TransactionActionList *dev_list, Error **errp) QSIMPLEQ_HEAD(snap_bdrv_states, BlkTransactionState) snap_bdrv_states; QSIMPLEQ_INIT(&snap_bdrv_states); - /* drain all i/o before any snapshots */ + /* drain all i/o before any operations */ bdrv_drain_all(); - /* We don't do anything in this loop that commits us to the snapshot */ + /* We don't do anything in this loop that commits us to the operations */ while (NULL != dev_entry) { TransactionAction *dev_info = NULL; const BdrvActionOps *ops; @@ -1581,10 +1723,7 @@ void qmp_transaction(TransactionActionList *dev_list, Error **errp) goto exit; delete_and_fail: - /* - * failure, and it is all-or-none; abandon each new bs, and keep using - * the original bs for all images - */ + /* failure, and it is all-or-none; roll back all operations */ QSIMPLEQ_FOREACH(state, &snap_bdrv_states, entry) { if (state->ops->abort) { state->ops->abort(state); @@ -1603,14 +1742,18 @@ exit: static void eject_device(BlockBackend *blk, int force, Error **errp) { BlockDriverState *bs = blk_bs(blk); + AioContext *aio_context; + + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_EJECT, errp)) { - return; + goto out; } if (!blk_dev_has_removable_media(blk)) { error_setg(errp, "Device '%s' is not removable", bdrv_get_device_name(bs)); - return; + goto out; } if (blk_dev_is_medium_locked(blk) && !blk_dev_is_tray_open(blk)) { @@ -1618,11 +1761,14 @@ static void eject_device(BlockBackend *blk, int force, Error **errp) if (!force) { error_setg(errp, "Device '%s' is locked", bdrv_get_device_name(bs)); - return; + goto out; } } bdrv_close(bs); + +out: + aio_context_release(aio_context); } void qmp_eject(const char *device, bool has_force, bool force, Error **errp) @@ -1644,7 +1790,7 @@ void qmp_block_passwd(bool has_device, const char *device, { Error *local_err = NULL; BlockDriverState *bs; - int err; + AioContext *aio_context; bs = bdrv_lookup_bs(has_device ? device : NULL, has_node_name ? node_name : NULL, @@ -1654,16 +1800,15 @@ void qmp_block_passwd(bool has_device, const char *device, return; } - err = bdrv_set_key(bs, password); - if (err == -EINVAL) { - error_set(errp, QERR_DEVICE_NOT_ENCRYPTED, bdrv_get_device_name(bs)); - return; - } else if (err < 0) { - error_set(errp, QERR_INVALID_PASSWORD); - return; - } + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + + bdrv_add_key(bs, password, errp); + + aio_context_release(aio_context); } +/* Assumes AioContext is held */ static void qmp_bdrv_open_encrypted(BlockDriverState *bs, const char *filename, int bdrv_flags, BlockDriver *drv, const char *password, Error **errp) @@ -1677,18 +1822,7 @@ static void qmp_bdrv_open_encrypted(BlockDriverState *bs, const char *filename, return; } - if (bdrv_key_required(bs)) { - if (password) { - if (bdrv_set_key(bs, password) < 0) { - error_set(errp, QERR_INVALID_PASSWORD); - } - } else { - error_set(errp, QERR_DEVICE_ENCRYPTED, bdrv_get_device_name(bs), - bdrv_get_encrypted_filename(bs)); - } - } else if (password) { - error_set(errp, QERR_DEVICE_NOT_ENCRYPTED, bdrv_get_device_name(bs)); - } + bdrv_add_key(bs, password, errp); } void qmp_change_blockdev(const char *device, const char *filename, @@ -1696,6 +1830,7 @@ void qmp_change_blockdev(const char *device, const char *filename, { BlockBackend *blk; BlockDriverState *bs; + AioContext *aio_context; BlockDriver *drv = NULL; int bdrv_flags; Error *err = NULL; @@ -1707,24 +1842,30 @@ void qmp_change_blockdev(const char *device, const char *filename, } bs = blk_bs(blk); + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + if (format) { drv = bdrv_find_whitelisted_format(format, bs->read_only); if (!drv) { error_set(errp, QERR_INVALID_BLOCK_FORMAT, format); - return; + goto out; } } eject_device(blk, 0, &err); if (err) { error_propagate(errp, err); - return; + goto out; } bdrv_flags = bdrv_is_read_only(bs) ? 0 : BDRV_O_RDWR; bdrv_flags |= bdrv_is_snapshot(bs) ? BDRV_O_SNAPSHOT : 0; qmp_bdrv_open_encrypted(bs, filename, bdrv_flags, drv, NULL, errp); + +out: + aio_context_release(aio_context); } /* throttling disk I/O limits */ @@ -1750,13 +1891,15 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd, { ThrottleConfig cfg; BlockDriverState *bs; + BlockBackend *blk; AioContext *aio_context; - bs = bdrv_find(device); - if (!bs) { + blk = blk_by_name(device); + if (!blk) { error_set(errp, QERR_DEVICE_NOT_FOUND, device); return; } + bs = blk_bs(blk); memset(&cfg, 0, sizeof(cfg)); cfg.buckets[THROTTLE_BPS_TOTAL].avg = bps; @@ -1810,7 +1953,7 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd, aio_context_release(aio_context); } -int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data) +int hmp_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data) { const char *id = qdict_get_str(qdict, "id"); BlockBackend *blk; @@ -1835,8 +1978,7 @@ int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data) aio_context_acquire(aio_context); if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, &local_err)) { - error_report("%s", error_get_pretty(local_err)); - error_free(local_err); + error_report_err(local_err); aio_context_release(aio_context); return -1; } @@ -1852,7 +1994,7 @@ int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data) * then we can just get rid of the block driver state right here. */ if (blk_get_attached_dev(blk)) { - blk_hide_on_behalf_of_do_drive_del(blk); + blk_hide_on_behalf_of_hmp_drive_del(blk); /* Further I/O must not pause the guest */ bdrv_set_on_error(bs, BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT); @@ -1961,6 +2103,7 @@ void qmp_block_stream(const char *device, bool has_on_error, BlockdevOnError on_error, Error **errp) { + BlockBackend *blk; BlockDriverState *bs; BlockDriverState *base_bs = NULL; AioContext *aio_context; @@ -1971,11 +2114,12 @@ void qmp_block_stream(const char *device, on_error = BLOCKDEV_ON_ERROR_REPORT; } - bs = bdrv_find(device); - if (!bs) { + blk = blk_by_name(device); + if (!blk) { error_set(errp, QERR_DEVICE_NOT_FOUND, device); return; } + bs = blk_bs(blk); aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); @@ -2025,6 +2169,7 @@ void qmp_block_commit(const char *device, bool has_speed, int64_t speed, Error **errp) { + BlockBackend *blk; BlockDriverState *bs; BlockDriverState *base_bs, *top_bs; AioContext *aio_context; @@ -2043,11 +2188,12 @@ void qmp_block_commit(const char *device, * live commit feature versions; for this to work, we must make sure to * perform the device lookup before any generic errors that may occur in a * scenario in which all optional arguments are omitted. */ - bs = bdrv_find(device); - if (!bs) { + blk = blk_by_name(device); + if (!blk) { error_set(errp, QERR_DEVICE_NOT_FOUND, device); return; } + bs = blk_bs(blk); aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); @@ -2055,7 +2201,7 @@ void qmp_block_commit(const char *device, /* drain all i/o before commits */ bdrv_drain_all(); - if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT, errp)) { + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT_SOURCE, errp)) { goto out; } @@ -2088,6 +2234,10 @@ void qmp_block_commit(const char *device, assert(bdrv_get_aio_context(base_bs) == aio_context); + if (bdrv_op_is_blocked(base_bs, BLOCK_OP_TYPE_COMMIT_TARGET, errp)) { + goto out; + } + /* Do not allow attempts to commit an image into itself */ if (top_bs == base_bs) { error_setg(errp, "cannot commit an image into itself"); @@ -2124,6 +2274,7 @@ void qmp_drive_backup(const char *device, const char *target, bool has_on_target_error, BlockdevOnError on_target_error, Error **errp) { + BlockBackend *blk; BlockDriverState *bs; BlockDriverState *target_bs; BlockDriverState *source = NULL; @@ -2147,15 +2298,18 @@ void qmp_drive_backup(const char *device, const char *target, mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS; } - bs = bdrv_find(device); - if (!bs) { + blk = blk_by_name(device); + if (!blk) { error_set(errp, QERR_DEVICE_NOT_FOUND, device); return; } + bs = blk_bs(blk); aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); + /* Although backup_run has this check too, we need to use bs->drv below, so + * do an early check redundantly. */ if (!bdrv_is_inserted(bs)) { error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); goto out; @@ -2172,6 +2326,7 @@ void qmp_drive_backup(const char *device, const char *target, } } + /* Early check to avoid creating target */ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) { goto out; } @@ -2239,6 +2394,60 @@ BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp) return bdrv_named_nodes_list(); } +void qmp_blockdev_backup(const char *device, const char *target, + enum MirrorSyncMode sync, + bool has_speed, int64_t speed, + bool has_on_source_error, + BlockdevOnError on_source_error, + bool has_on_target_error, + BlockdevOnError on_target_error, + Error **errp) +{ + BlockBackend *blk; + BlockDriverState *bs; + BlockDriverState *target_bs; + Error *local_err = NULL; + AioContext *aio_context; + + if (!has_speed) { + speed = 0; + } + if (!has_on_source_error) { + on_source_error = BLOCKDEV_ON_ERROR_REPORT; + } + if (!has_on_target_error) { + on_target_error = BLOCKDEV_ON_ERROR_REPORT; + } + + blk = blk_by_name(device); + if (!blk) { + error_setg(errp, "Device '%s' not found", device); + return; + } + bs = blk_bs(blk); + + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + + blk = blk_by_name(target); + if (!blk) { + error_setg(errp, "Device '%s' not found", target); + goto out; + } + target_bs = blk_bs(blk); + + bdrv_ref(target_bs); + bdrv_set_aio_context(target_bs, aio_context); + backup_start(bs, target_bs, speed, sync, on_source_error, on_target_error, + block_job_cb, bs, &local_err); + if (local_err != NULL) { + bdrv_unref(target_bs); + error_propagate(errp, local_err); + } +out: + aio_context_release(aio_context); +} + #define DEFAULT_MIRROR_BUF_SIZE (10 << 20) void qmp_drive_mirror(const char *device, const char *target, @@ -2252,8 +2461,10 @@ void qmp_drive_mirror(const char *device, const char *target, bool has_buf_size, int64_t buf_size, bool has_on_source_error, BlockdevOnError on_source_error, bool has_on_target_error, BlockdevOnError on_target_error, + bool has_unmap, bool unmap, Error **errp) { + BlockBackend *blk; BlockDriverState *bs; BlockDriverState *source, *target_bs; AioContext *aio_context; @@ -2282,6 +2493,9 @@ void qmp_drive_mirror(const char *device, const char *target, if (!has_buf_size) { buf_size = DEFAULT_MIRROR_BUF_SIZE; } + if (!has_unmap) { + unmap = true; + } if (granularity != 0 && (granularity < 512 || granularity > 1048576 * 64)) { error_set(errp, QERR_INVALID_PARAMETER_VALUE, "granularity", @@ -2293,11 +2507,12 @@ void qmp_drive_mirror(const char *device, const char *target, return; } - bs = bdrv_find(device); - if (!bs) { + blk = blk_by_name(device); + if (!blk) { error_set(errp, QERR_DEVICE_NOT_FOUND, device); return; } + bs = blk_bs(blk); aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); @@ -2420,6 +2635,7 @@ void qmp_drive_mirror(const char *device, const char *target, has_replaces ? replaces : NULL, speed, granularity, buf_size, sync, on_source_error, on_target_error, + unmap, block_job_cb, bs, &local_err); if (local_err != NULL) { bdrv_unref(target_bs); @@ -2432,14 +2648,17 @@ out: } /* Get the block job for a given device name and acquire its AioContext */ -static BlockJob *find_block_job(const char *device, AioContext **aio_context) +static BlockJob *find_block_job(const char *device, AioContext **aio_context, + Error **errp) { + BlockBackend *blk; BlockDriverState *bs; - bs = bdrv_find(device); - if (!bs) { + blk = blk_by_name(device); + if (!blk) { goto notfound; } + bs = blk_bs(blk); *aio_context = bdrv_get_aio_context(bs); aio_context_acquire(*aio_context); @@ -2452,6 +2671,8 @@ static BlockJob *find_block_job(const char *device, AioContext **aio_context) return bs->job; notfound: + error_set(errp, ERROR_CLASS_DEVICE_NOT_ACTIVE, + "No active block job on device '%s'", device); *aio_context = NULL; return NULL; } @@ -2459,10 +2680,9 @@ notfound: void qmp_block_job_set_speed(const char *device, int64_t speed, Error **errp) { AioContext *aio_context; - BlockJob *job = find_block_job(device, &aio_context); + BlockJob *job = find_block_job(device, &aio_context, errp); if (!job) { - error_set(errp, QERR_BLOCK_JOB_NOT_ACTIVE, device); return; } @@ -2474,10 +2694,9 @@ void qmp_block_job_cancel(const char *device, bool has_force, bool force, Error **errp) { AioContext *aio_context; - BlockJob *job = find_block_job(device, &aio_context); + BlockJob *job = find_block_job(device, &aio_context, errp); if (!job) { - error_set(errp, QERR_BLOCK_JOB_NOT_ACTIVE, device); return; } @@ -2500,10 +2719,9 @@ out: void qmp_block_job_pause(const char *device, Error **errp) { AioContext *aio_context; - BlockJob *job = find_block_job(device, &aio_context); + BlockJob *job = find_block_job(device, &aio_context, errp); if (!job) { - error_set(errp, QERR_BLOCK_JOB_NOT_ACTIVE, device); return; } @@ -2515,10 +2733,9 @@ void qmp_block_job_pause(const char *device, Error **errp) void qmp_block_job_resume(const char *device, Error **errp) { AioContext *aio_context; - BlockJob *job = find_block_job(device, &aio_context); + BlockJob *job = find_block_job(device, &aio_context, errp); if (!job) { - error_set(errp, QERR_BLOCK_JOB_NOT_ACTIVE, device); return; } @@ -2530,10 +2747,9 @@ void qmp_block_job_resume(const char *device, Error **errp) void qmp_block_job_complete(const char *device, Error **errp) { AioContext *aio_context; - BlockJob *job = find_block_job(device, &aio_context); + BlockJob *job = find_block_job(device, &aio_context, errp); if (!job) { - error_set(errp, QERR_BLOCK_JOB_NOT_ACTIVE, device); return; } @@ -2547,48 +2763,53 @@ void qmp_change_backing_file(const char *device, const char *backing_file, Error **errp) { + BlockBackend *blk; BlockDriverState *bs = NULL; + AioContext *aio_context; BlockDriverState *image_bs = NULL; Error *local_err = NULL; bool ro; int open_flags; int ret; - /* find the top layer BDS of the chain */ - bs = bdrv_find(device); - if (!bs) { + blk = blk_by_name(device); + if (!blk) { error_set(errp, QERR_DEVICE_NOT_FOUND, device); return; } + bs = blk_bs(blk); + + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); image_bs = bdrv_lookup_bs(NULL, image_node_name, &local_err); if (local_err) { error_propagate(errp, local_err); - return; + goto out; } if (!image_bs) { error_setg(errp, "image file not found"); - return; + goto out; } if (bdrv_find_base(image_bs) == image_bs) { error_setg(errp, "not allowing backing file change on an image " "without a backing file"); - return; + goto out; } /* even though we are not necessarily operating on bs, we need it to * determine if block ops are currently prohibited on the chain */ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_CHANGE, errp)) { - return; + goto out; } /* final sanity check */ if (!bdrv_chain_contains(bs, image_bs)) { error_setg(errp, "'%s' and image file are not in the same chain", device); - return; + goto out; } /* if not r/w, reopen to make r/w */ @@ -2599,7 +2820,7 @@ void qmp_change_backing_file(const char *device, bdrv_reopen(image_bs, open_flags | BDRV_O_RDWR, &local_err); if (local_err) { error_propagate(errp, local_err); - return; + goto out; } } @@ -2619,6 +2840,9 @@ void qmp_change_backing_file(const char *device, error_propagate(errp, local_err); /* will preserve prior errp */ } } + +out: + aio_context_release(aio_context); } void qmp_blockdev_add(BlockdevOptions *options, Error **errp) diff --git a/bootdevice.c b/bootdevice.c index b29970c..3cdc0d7 100644 --- a/bootdevice.c +++ b/bootdevice.c @@ -1,7 +1,7 @@ /* * QEMU Boot Device Implement * - * Copyright (c) 2014 HUAWEI TECHNOLOGIES CO.,LTD. + * Copyright (c) 2014 HUAWEI TECHNOLOGIES CO., 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 @@ -25,6 +25,7 @@ #include "sysemu/sysemu.h" #include "qapi/visitor.h" #include "qemu/error-report.h" +#include "hw/hw.h" typedef struct FWBootEntry FWBootEntry; @@ -37,6 +38,80 @@ struct FWBootEntry { static QTAILQ_HEAD(, FWBootEntry) fw_boot_order = QTAILQ_HEAD_INITIALIZER(fw_boot_order); +static QEMUBootSetHandler *boot_set_handler; +static void *boot_set_opaque; + +void qemu_register_boot_set(QEMUBootSetHandler *func, void *opaque) +{ + boot_set_handler = func; + boot_set_opaque = opaque; +} + +void qemu_boot_set(const char *boot_order, Error **errp) +{ + Error *local_err = NULL; + + if (!boot_set_handler) { + error_setg(errp, "no function defined to set boot device list for" + " this architecture"); + return; + } + + validate_bootdevices(boot_order, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + boot_set_handler(boot_set_opaque, boot_order, errp); +} + +void validate_bootdevices(const char *devices, Error **errp) +{ + /* We just do some generic consistency checks */ + const char *p; + int bitmap = 0; + + for (p = devices; *p != '\0'; p++) { + /* Allowed boot devices are: + * a-b: floppy disk drives + * c-f: IDE disk drives + * g-m: machine implementation dependent drives + * n-p: network devices + * It's up to each machine implementation to check if the given boot + * devices match the actual hardware implementation and firmware + * features. + */ + if (*p < 'a' || *p > 'p') { + error_setg(errp, "Invalid boot device '%c'", *p); + return; + } + if (bitmap & (1 << (*p - 'a'))) { + error_setg(errp, "Boot device '%c' was given twice", *p); + return; + } + bitmap |= 1 << (*p - 'a'); + } +} + +void restore_boot_order(void *opaque) +{ + char *normal_boot_order = opaque; + static int first = 1; + + /* Restore boot order and remove ourselves after the first boot */ + if (first) { + first = 0; + return; + } + + if (boot_set_handler) { + qemu_boot_set(normal_boot_order, &error_abort); + } + + qemu_unregister_reset(restore_boot_order, normal_boot_order); + g_free(normal_boot_order); +} void check_boot_index(int32_t bootindex, Error **errp) { @@ -137,7 +212,9 @@ char *get_boot_devices_list(size_t *size, bool ignore_suffixes) char *list = NULL; QTAILQ_FOREACH(i, &fw_boot_order, link) { - char *devpath = NULL, *bootpath; + char *devpath = NULL, *suffix = NULL; + char *bootpath; + char *d; size_t len; if (i->dev) { @@ -145,21 +222,27 @@ char *get_boot_devices_list(size_t *size, bool ignore_suffixes) assert(devpath); } - if (i->suffix && !ignore_suffixes && devpath) { - size_t bootpathlen = strlen(devpath) + strlen(i->suffix) + 1; - - bootpath = g_malloc(bootpathlen); - snprintf(bootpath, bootpathlen, "%s%s", devpath, i->suffix); - g_free(devpath); - } else if (devpath) { - bootpath = devpath; - } else if (!ignore_suffixes) { - assert(i->suffix); - bootpath = g_strdup(i->suffix); - } else { - bootpath = g_strdup(""); + if (!ignore_suffixes) { + if (i->dev) { + d = qdev_get_own_fw_dev_path_from_handler(i->dev->parent_bus, + i->dev); + if (d) { + assert(!i->suffix); + suffix = d; + } else { + suffix = g_strdup(i->suffix); + } + } else { + suffix = g_strdup(i->suffix); + } } + bootpath = g_strdup_printf("%s%s", + devpath ? devpath : "", + suffix ? suffix : ""); + g_free(devpath); + g_free(suffix); + if (total) { list[total-1] = '\n'; } diff --git a/bsd-user/elfload.c b/bsd-user/elfload.c index 93fd9e4..2bf57eb 100644 --- a/bsd-user/elfload.c +++ b/bsd-user/elfload.c @@ -351,8 +351,10 @@ static inline void init_thread(struct target_pt_regs *_regs, struct image_info * _regs->gpr[1] = infop->start_stack; #if defined(TARGET_PPC64) && !defined(TARGET_ABI32) - entry = ldq_raw(infop->entry) + infop->load_addr; - toc = ldq_raw(infop->entry + 8) + infop->load_addr; + get_user_u64(entry, infop->entry); + entry += infop->load_addr; + get_user_u64(toc, infop->entry + 8); + toc += infop->load_addr; _regs->gpr[2] = toc; infop->entry = entry; #endif @@ -365,8 +367,9 @@ static inline void init_thread(struct target_pt_regs *_regs, struct image_info * get_user_ual(_regs->gpr[3], pos); pos += sizeof(abi_ulong); _regs->gpr[4] = pos; - for (tmp = 1; tmp != 0; pos += sizeof(abi_ulong)) - tmp = ldl(pos); + for (tmp = 1; tmp != 0; pos += sizeof(abi_ulong)) { + get_user_ual(tmp, pos); + } _regs->gpr[5] = pos; } diff --git a/bsd-user/main.c b/bsd-user/main.c index 0e8c26c..1bb2754 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -908,12 +908,12 @@ int main(int argc, char **argv) cpu_exec_init_all(); /* NOTE: we need to init the CPU at this stage to get qemu_host_page_size */ - env = cpu_init(cpu_model); - if (!env) { + cpu = cpu_init(cpu_model); + if (!cpu) { fprintf(stderr, "Unable to find CPU definition\n"); exit(1); } - cpu = ENV_GET_CPU(env); + env = cpu->env_ptr; #if defined(TARGET_SPARC) || defined(TARGET_PPC) cpu_reset(cpu); #endif diff --git a/configure b/configure index 47048f0..6969f6f 100755 --- a/configure +++ b/configure @@ -309,10 +309,11 @@ rbd="" smartcard_nss="" libusb="" usb_redir="" -glx="" +opengl="" zlib="yes" lzo="" snappy="" +bzip2="" guest_agent="" guest_agent_with_vss="no" vss_win32_sdk="" @@ -326,7 +327,7 @@ seccomp="" glusterfs="" glusterfs_discard="no" glusterfs_zerofill="no" -archipelago="" +archipelago="no" gtk="" gtkabi="" vte="" @@ -1026,9 +1027,9 @@ for opt do ;; --enable-vhost-scsi) vhost_scsi="yes" ;; - --disable-glx) glx="no" + --disable-opengl) opengl="no" ;; - --enable-glx) glx="yes" + --enable-opengl) opengl="yes" ;; --disable-rbd) rbd="no" ;; @@ -1060,6 +1061,10 @@ for opt do ;; --enable-snappy) snappy="yes" ;; + --disable-bzip2) bzip2="no" + ;; + --enable-bzip2) bzip2="yes" + ;; --enable-guest-agent) guest_agent="yes" ;; --disable-guest-agent) guest_agent="no" @@ -1374,6 +1379,8 @@ Advanced options (experts only): --enable-usb-redir enable usb network redirection support --enable-lzo enable the support of lzo compression library --enable-snappy enable the support of snappy compression library + --enable-bzip2 enable the support of bzip2 compression library (for + reading bzip2-compressed dmg images) --disable-guest-agent disable building of the QEMU Guest Agent --enable-guest-agent enable building of the QEMU Guest Agent --with-vss-sdk=SDK-path enable Windows VSS support in QEMU Guest Agent @@ -1820,6 +1827,24 @@ EOF fi ########################################## +# bzip2 check + +if test "$bzip2" != "no" ; then + cat > $TMPC << EOF +#include +int main(void) { BZ2_bzlibVersion(); return 0; } +EOF + if compile_prog "" "-lbz2" ; then + bzip2="yes" + else + if test "$bzip2" = "yes"; then + feature_not_found "libbzip2" "Install libbzip2 devel" + fi + bzip2="no" + fi +fi + +########################################## # libseccomp check if test "$seccomp" != "no" ; then @@ -1830,7 +1855,7 @@ if test "$seccomp" != "no" ; then seccomp="yes" else if test "$seccomp" = "yes"; then - feature_not_found "libseccomp" "Install libseccomp devel >= 2.1.0" + feature_not_found "libseccomp" "Install libseccomp devel >= 2.1.1" fi seccomp="no" fi @@ -1877,6 +1902,32 @@ int main(void) { xc_gnttab_open(NULL, 0); xc_domain_add_to_physmap(0, 0, XENMAPSPACE_gmfn, 0, 0); xc_hvm_inject_msi(xc, 0, 0xf0000000, 0x00000000); + xc_hvm_create_ioreq_server(xc, 0, 0, NULL); + return 0; +} +EOF + compile_prog "" "$xen_libs" + then + xen_ctrl_version=450 + xen=yes + + elif + cat > $TMPC < +#include +#include +#include +#if !defined(HVM_MAX_VCPUS) +# error HVM_MAX_VCPUS not defined +#endif +int main(void) { + xc_interface *xc; + xs_daemon_open(); + xc = xc_interface_open(0, 0, 0); + xc_hvm_set_mem_type(0, 0, HVMMEM_ram_ro, 0, 0); + xc_gnttab_open(NULL, 0); + xc_domain_add_to_physmap(0, 0, XENMAPSPACE_gmfn, 0, 0); + xc_hvm_inject_msi(xc, 0, 0xf0000000, 0x00000000); return 0; } EOF @@ -2034,6 +2085,15 @@ if test "$sparse" != "no" ; then fi ########################################## +# X11 probe +x11_cflags= +x11_libs=-lX11 +if $pkg_config --exists "x11"; then + x11_cflags=`$pkg_config --cflags x11` + x11_libs=`$pkg_config --libs x11` +fi + +########################################## # GTK probe if test "$gtkabi" = ""; then @@ -2060,7 +2120,8 @@ if test "$gtk" != "no"; then gtk_cflags=`$pkg_config --cflags $gtkpackage` gtk_libs=`$pkg_config --libs $gtkpackage` if $pkg_config --exists "$gtkx11package >= $gtkversion"; then - gtk_libs="$gtk_libs -lX11" + gtk_cflags="$gtk_cflags $x11_cflags" + gtk_libs="$gtk_libs $x11_libs" fi libs_softmmu="$gtk_libs $libs_softmmu" gtk="yes" @@ -2185,8 +2246,9 @@ if test "$sdl" = "yes" ; then #endif int main(void) { return 0; } EOF - if compile_prog "$sdl_cflags" "$sdl_libs" ; then - sdl_libs="$sdl_libs -lX11" + if compile_prog "$sdl_cflags $x11_cflags" "$sdl_libs $x11_libs" ; then + sdl_cflags="$sdl_cflags $x11_cflags" + sdl_libs="$sdl_libs $x11_libs" fi libs_softmmu="$sdl_libs $libs_softmmu" fi @@ -2727,7 +2789,7 @@ fi if test "$modules" = yes; then shacmd_probe="sha1sum sha1 shasum" for c in $shacmd_probe; do - if which $c >/dev/null 2>&1; then + if has $c; then shacmd="$c" break fi @@ -3056,23 +3118,35 @@ fi libs_softmmu="$libs_softmmu $fdt_libs" ########################################## +# opengl probe (for sdl2, milkymist-tmu2) + # GLX probe, used by milkymist-tmu2 -if test "$glx" != "no" ; then - glx_libs="-lGL -lX11" - cat > $TMPC << EOF +# this is temporary, code will be switched to egl mid-term. +cat > $TMPC << EOF #include #include #include int main(void) { glBegin(0); glXQueryVersion(0,0,0); return 0; } EOF - if compile_prog "" "-lGL -lX11" ; then - glx=yes +if compile_prog "" "-lGL -lX11" ; then + have_glx=yes +else + have_glx=no +fi + +if test "$opengl" != "no" ; then + opengl_pkgs="gl" + if $pkg_config $opengl_pkgs x11 && test "$have_glx" = "yes"; then + opengl_cflags="$($pkg_config --cflags $opengl_pkgs) $x11_cflags" + opengl_libs="$($pkg_config --libs $opengl_pkgs) $x11_libs" + opengl=yes else - if test "$glx" = "yes" ; then - feature_not_found "glx" "Install GL devel (e.g. MESA)" + if test "$opengl" = "yes" ; then + feature_not_found "opengl" "Install GL devel (e.g. MESA)" fi - glx_libs= - glx=no + opengl_cflags="" + opengl_libs="" + opengl=no fi fi @@ -3094,6 +3168,12 @@ EOF archipelago="yes" libs_tools="$archipelago_libs $libs_tools" libs_softmmu="$archipelago_libs $libs_softmmu" + + echo "WARNING: Please check the licenses of QEMU and libxseg carefully." + echo "GPLv3 versions of libxseg may not be compatible with QEMU's" + echo "license and therefore prevent redistribution." + echo + echo "To disable Archipelago, use --disable-archipelago" else if test "$archipelago" = "yes" ; then feature_not_found "Archipelago backend support" "Install libxseg devel" @@ -3309,6 +3389,22 @@ if compile_prog "" "" ; then fallocate_punch_hole=yes fi +# check that fallocate supports range zeroing inside the file +fallocate_zero_range=no +cat > $TMPC << EOF +#include +#include + +int main(void) +{ + fallocate(0, FALLOC_FL_ZERO_RANGE, 0, 0); + return 0; +} +EOF +if compile_prog "" "" ; then + fallocate_zero_range=yes +fi + # check for posix_fallocate posix_fallocate=no cat > $TMPC << EOF @@ -4283,6 +4379,9 @@ if test -n "$sparc_cpu"; then echo "Target Sparc Arch $sparc_cpu" fi echo "xen support $xen" +if test "$xen" = "yes" ; then + echo "xen ctrl version $xen_ctrl_version" +fi echo "brlapi support $brlapi" echo "bluez support $bluez" echo "Documentation $docs" @@ -4320,7 +4419,7 @@ echo "xfsctl support $xfs" echo "nss used $smartcard_nss" echo "libusb $libusb" echo "usb net redir $usb_redir" -echo "GLX support $glx" +echo "OpenGL support $opengl" echo "libiscsi support $libiscsi" echo "libnfs support $libnfs" echo "build guest agent $guest_agent" @@ -4340,6 +4439,7 @@ echo "vhdx $vhdx" echo "Quorum $quorum" echo "lzo support $lzo" echo "snappy support $snappy" +echo "bzip2 support $bzip2" echo "NUMA host support $numa" if test "$sdl_too_old" = "yes"; then @@ -4538,6 +4638,9 @@ fi if test "$fallocate_punch_hole" = "yes" ; then echo "CONFIG_FALLOCATE_PUNCH_HOLE=y" >> $config_host_mak fi +if test "$fallocate_zero_range" = "yes" ; then + echo "CONFIG_FALLOCATE_ZERO_RANGE=y" >> $config_host_mak +fi if test "$posix_fallocate" = "yes" ; then echo "CONFIG_POSIX_FALLOCATE=y" >> $config_host_mak fi @@ -4682,9 +4785,10 @@ if test "$usb_redir" = "yes" ; then echo "CONFIG_USB_REDIR=y" >> $config_host_mak fi -if test "$glx" = "yes" ; then - echo "CONFIG_GLX=y" >> $config_host_mak - echo "GLX_LIBS=$glx_libs" >> $config_host_mak +if test "$opengl" = "yes" ; then + echo "CONFIG_OPENGL=y" >> $config_host_mak + echo "OPENGL_CFLAGS=$opengl_cflags" >> $config_host_mak + echo "OPENGL_LIBS=$opengl_libs" >> $config_host_mak fi if test "$lzo" = "yes" ; then @@ -4695,6 +4799,11 @@ if test "$snappy" = "yes" ; then echo "CONFIG_SNAPPY=y" >> $config_host_mak fi +if test "$bzip2" = "yes" ; then + echo "CONFIG_BZIP2=y" >> $config_host_mak + echo "BZIP2_LIBS=-lbz2" >> $config_host_mak +fi + if test "$libiscsi" = "yes" ; then echo "CONFIG_LIBISCSI=m" >> $config_host_mak echo "LIBISCSI_CFLAGS=$libiscsi_cflags" >> $config_host_mak @@ -4909,6 +5018,7 @@ echo "QEMU_CFLAGS=$QEMU_CFLAGS" >> $config_host_mak echo "QEMU_INCLUDES=$QEMU_INCLUDES" >> $config_host_mak if test "$sparse" = "yes" ; then echo "CC := REAL_CC=\"\$(CC)\" cgcc" >> $config_host_mak + echo "CPP := REAL_CC=\"\$(CPP)\" cgcc" >> $config_host_mak echo "CXX := REAL_CC=\"\$(CXX)\" cgcc" >> $config_host_mak echo "HOST_CC := REAL_CC=\"\$(HOST_CC)\" cgcc" >> $config_host_mak echo "QEMU_CFLAGS += -Wbitwise -Wno-transparent-union -Wno-old-initializer -Wno-non-pointer-null" >> $config_host_mak @@ -5164,7 +5274,9 @@ case "$target_name" in \( "$target_name" = "ppcemb" -a "$cpu" = "ppc64" \) -o \ \( "$target_name" = "mipsel" -a "$cpu" = "mips" \) -o \ \( "$target_name" = "x86_64" -a "$cpu" = "i386" \) -o \ - \( "$target_name" = "i386" -a "$cpu" = "x86_64" \) \) ; then + \( "$target_name" = "i386" -a "$cpu" = "x86_64" \) -o \ + \( "$target_name" = "x86_64" -a "$cpu" = "x32" \) -o \ + \( "$target_name" = "i386" -a "$cpu" = "x32" \) \) ; then echo "CONFIG_KVM=y" >> $config_target_mak if test "$vhost_net" = "yes" ; then echo "CONFIG_VHOST_NET=y" >> $config_target_mak diff --git a/coroutine-ucontext.c b/coroutine-ucontext.c index 4bf2cde..259fcb4 100644 --- a/coroutine-ucontext.c +++ b/coroutine-ucontext.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include "qemu-common.h" #include "block/coroutine_int.h" @@ -48,15 +47,8 @@ typedef struct { /** * Per-thread coroutine bookkeeping */ -typedef struct { - /** Currently executing coroutine */ - Coroutine *current; - - /** The default coroutine */ - CoroutineUContext leader; -} CoroutineThreadState; - -static pthread_key_t thread_state_key; +static __thread CoroutineUContext leader; +static __thread Coroutine *current; /* * va_args to makecontext() must be type 'int', so passing @@ -68,36 +60,6 @@ union cc_arg { int i[2]; }; -static CoroutineThreadState *coroutine_get_thread_state(void) -{ - CoroutineThreadState *s = pthread_getspecific(thread_state_key); - - if (!s) { - s = g_malloc0(sizeof(*s)); - s->current = &s->leader.base; - pthread_setspecific(thread_state_key, s); - } - return s; -} - -static void qemu_coroutine_thread_cleanup(void *opaque) -{ - CoroutineThreadState *s = opaque; - - g_free(s); -} - -static void __attribute__((constructor)) coroutine_init(void) -{ - int ret; - - ret = pthread_key_create(&thread_state_key, qemu_coroutine_thread_cleanup); - if (ret != 0) { - fprintf(stderr, "unable to create leader key: %s\n", strerror(errno)); - abort(); - } -} - static void coroutine_trampoline(int i0, int i1) { union cc_arg arg; @@ -193,15 +155,23 @@ void qemu_coroutine_delete(Coroutine *co_) g_free(co); } -CoroutineAction qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, - CoroutineAction action) +/* This function is marked noinline to prevent GCC from inlining it + * into coroutine_trampoline(). If we allow it to do that then it + * hoists the code to get the address of the TLS variable "current" + * out of the while() loop. This is an invalid transformation because + * the sigsetjmp() call may be called when running thread A but + * return in thread B, and so we might be in a different thread + * context each time round the loop. + */ +CoroutineAction __attribute__((noinline)) +qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, + CoroutineAction action) { CoroutineUContext *from = DO_UPCAST(CoroutineUContext, base, from_); CoroutineUContext *to = DO_UPCAST(CoroutineUContext, base, to_); - CoroutineThreadState *s = coroutine_get_thread_state(); int ret; - s->current = to_; + current = to_; ret = sigsetjmp(from->env, 0); if (ret == 0) { @@ -212,14 +182,13 @@ CoroutineAction qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, Coroutine *qemu_coroutine_self(void) { - CoroutineThreadState *s = coroutine_get_thread_state(); - - return s->current; + if (!current) { + current = &leader.base; + } + return current; } bool qemu_in_coroutine(void) { - CoroutineThreadState *s = pthread_getspecific(thread_state_key); - - return s && s->current->caller; + return current && current->caller; } diff --git a/cpu-exec.c b/cpu-exec.c index 3913de0..2ffeb6e 100644 --- a/cpu-exec.c +++ b/cpu-exec.c @@ -24,6 +24,9 @@ #include "qemu/atomic.h" #include "sysemu/qtest.h" #include "qemu/timer.h" +#include "exec/address-spaces.h" +#include "exec/memory-internal.h" +#include "qemu/rcu.h" /* -icount align implementation. */ @@ -61,8 +64,7 @@ static void align_clocks(SyncClocks *sc, const CPUState *cpu) sleep_delay.tv_sec = sc->diff_clk / 1000000000LL; sleep_delay.tv_nsec = sc->diff_clk % 1000000000LL; if (nanosleep(&sleep_delay, &rem_delay) < 0) { - sc->diff_clk -= (sleep_delay.tv_sec - rem_delay.tv_sec) * 1000000000LL; - sc->diff_clk -= sleep_delay.tv_nsec - rem_delay.tv_nsec; + sc->diff_clk = rem_delay.tv_sec * 1000000000LL + rem_delay.tv_nsec; } else { sc->diff_clk = 0; } @@ -101,10 +103,8 @@ static void init_delay_params(SyncClocks *sc, if (!icount_align_option) { return; } - sc->realtime_clock = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); - sc->diff_clk = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - - sc->realtime_clock + - cpu_get_clock_offset(); + sc->realtime_clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT); + sc->diff_clk = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - sc->realtime_clock; sc->last_cpu_icount = cpu->icount_extra + cpu->icount_decr.u16.low; if (sc->diff_clk < max_delay) { max_delay = sc->diff_clk; @@ -144,6 +144,33 @@ void cpu_resume_from_signal(CPUState *cpu, void *puc) cpu->exception_index = -1; siglongjmp(cpu->jmp_env, 1); } + +void cpu_reload_memory_map(CPUState *cpu) +{ + AddressSpaceDispatch *d; + + if (qemu_in_vcpu_thread()) { + /* Do not let the guest prolong the critical section as much as it + * as it desires. + * + * Currently, this is prevented by the I/O thread's periodinc kicking + * of the VCPU thread (iothread_requesting_mutex, qemu_cpu_kick_thread) + * but this will go away once TCG's execution moves out of the global + * mutex. + * + * This pair matches cpu_exec's rcu_read_lock()/rcu_read_unlock(), which + * only protects cpu->as->dispatch. Since we reload it below, we can + * split the critical section. + */ + rcu_read_unlock(); + rcu_read_lock(); + } + + /* The CPU and TLB are protected by the iothread lock. */ + d = atomic_rcu_read(&cpu->as->dispatch); + cpu->memory_dispatch = d; + tlb_flush(cpu, 1); +} #endif /* Execute a TB, and fix up the CPU state afterwards if necessary */ @@ -168,7 +195,9 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, uint8_t *tb_ptr) } #endif /* DEBUG_DISAS */ + cpu->can_do_io = 0; next_tb = tcg_qemu_tb_exec(env, tb_ptr); + cpu->can_do_io = 1; trace_exec_tb_exit((void *) (next_tb & ~TB_EXIT_MASK), next_tb & TB_EXIT_MASK); @@ -202,14 +231,19 @@ static void cpu_exec_nocache(CPUArchState *env, int max_cycles, { CPUState *cpu = ENV_GET_CPU(env); TranslationBlock *tb; + target_ulong pc = orig_tb->pc; + target_ulong cs_base = orig_tb->cs_base; + uint64_t flags = orig_tb->flags; /* Should never happen. We only end up here when an existing TB is too long. */ if (max_cycles > CF_COUNT_MASK) max_cycles = CF_COUNT_MASK; - tb = tb_gen_code(cpu, orig_tb->pc, orig_tb->cs_base, orig_tb->flags, - max_cycles); + /* tb_gen_code can flush our orig_tb, invalidate it now */ + tb_phys_invalidate(orig_tb, -1); + tb = tb_gen_code(cpu, pc, cs_base, flags, + max_cycles | CF_NOCACHE); cpu->current_tb = tb; /* execute the generated code */ trace_exec_tb_nocache(tb, tb->pc); @@ -348,12 +382,13 @@ int cpu_exec(CPUArchState *env) * an instruction scheduling constraint on modern architectures. */ smp_mb(); + rcu_read_lock(); + if (unlikely(exit_request)) { cpu->exit_request = 1; } cc->cpu_exec_enter(cpu); - cpu->exception_index = -1; /* Calculate difference between guest clock and host clock. * This delay includes the delay of the last cycle, so @@ -373,6 +408,7 @@ int cpu_exec(CPUArchState *env) if (ret == EXCP_DEBUG) { cpu_handle_debug_exception(env); } + cpu->exception_index = -1; break; } else { #if defined(CONFIG_USER_ONLY) @@ -383,6 +419,7 @@ int cpu_exec(CPUArchState *env) cc->do_interrupt(cpu); #endif ret = cpu->exception_index; + cpu->exception_index = -1; break; #else cc->do_interrupt(cpu); @@ -489,28 +526,22 @@ int cpu_exec(CPUArchState *env) * interrupt_request) which we will handle * next time around the loop. */ - tb = (TranslationBlock *)(next_tb & ~TB_EXIT_MASK); next_tb = 0; break; case TB_EXIT_ICOUNT_EXPIRED: { /* Instruction counter expired. */ - int insns_left; - tb = (TranslationBlock *)(next_tb & ~TB_EXIT_MASK); - insns_left = cpu->icount_decr.u32; + int insns_left = cpu->icount_decr.u32; if (cpu->icount_extra && insns_left >= 0) { /* Refill decrementer and continue execution. */ cpu->icount_extra += insns_left; - if (cpu->icount_extra > 0xffff) { - insns_left = 0xffff; - } else { - insns_left = cpu->icount_extra; - } + insns_left = MIN(0xffff, cpu->icount_extra); cpu->icount_extra -= insns_left; cpu->icount_decr.u16.low = insns_left; } else { if (insns_left > 0) { /* Execute remaining instructions. */ + tb = (TranslationBlock *)(next_tb & ~TB_EXIT_MASK); cpu_exec_nocache(env, insns_left, tb); align_clocks(&sc, cpu); } @@ -537,6 +568,7 @@ int cpu_exec(CPUArchState *env) cpu = current_cpu; env = cpu->env_ptr; cc = CPU_GET_CLASS(cpu); + cpu->can_do_io = 1; #ifdef TARGET_I386 x86_cpu = X86_CPU(cpu); #endif @@ -548,6 +580,7 @@ int cpu_exec(CPUArchState *env) } /* for(;;) */ cc->cpu_exec_exit(cpu); + rcu_read_unlock(); /* fail safe : never use current_cpu outside cpu_exec() */ current_cpu = NULL; diff --git a/cpus.c b/cpus.c index 0c33458..e6dcae3 100644 --- a/cpus.c +++ b/cpus.c @@ -136,8 +136,7 @@ typedef struct TimersState { static TimersState timers_state; -/* Return the virtual CPU time, based on the instruction counter. */ -static int64_t cpu_get_icount_locked(void) +int64_t cpu_get_icount_raw(void) { int64_t icount; CPUState *cpu = current_cpu; @@ -145,10 +144,18 @@ static int64_t cpu_get_icount_locked(void) icount = timers_state.qemu_icount; if (cpu) { if (!cpu_can_do_io(cpu)) { - fprintf(stderr, "Bad clock read\n"); + fprintf(stderr, "Bad icount read\n"); + exit(1); } icount -= (cpu->icount_decr.u16.low + cpu->icount_extra); } + return icount; +} + +/* Return the virtual CPU time, based on the instruction counter. */ +static int64_t cpu_get_icount_locked(void) +{ + int64_t icount = cpu_get_icount_raw(); return timers_state.qemu_icount_bias + cpu_icount_to_ns(icount); } @@ -222,23 +229,6 @@ int64_t cpu_get_clock(void) return ti; } -/* return the offset between the host clock and virtual CPU clock */ -int64_t cpu_get_clock_offset(void) -{ - int64_t ti; - unsigned start; - - do { - start = seqlock_read_begin(&timers_state.vm_clock_seqlock); - ti = timers_state.cpu_clock_offset; - if (!timers_state.cpu_ticks_enabled) { - ti -= get_clock(); - } - } while (seqlock_read_retry(&timers_state.vm_clock_seqlock, start)); - - return -ti; -} - /* enable cpu_get_ticks() * Caller must hold BQL which server as mutex for vm_clock_seqlock. */ @@ -317,7 +307,7 @@ static void icount_adjust(void) static void icount_adjust_rt(void *opaque) { timer_mod(icount_rt_timer, - qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 1000); + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_RT) + 1000); icount_adjust(); } @@ -345,7 +335,7 @@ static void icount_warp_rt(void *opaque) seqlock_write_lock(&timers_state.vm_clock_seqlock); if (runstate_is_running()) { - int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); + int64_t clock = cpu_get_clock_locked(); int64_t warp_delta; warp_delta = clock - vm_clock_warp_start; @@ -354,9 +344,8 @@ static void icount_warp_rt(void *opaque) * In adaptive mode, do not let QEMU_CLOCK_VIRTUAL run too * far ahead of real time. */ - int64_t cur_time = cpu_get_clock_locked(); int64_t cur_icount = cpu_get_icount_locked(); - int64_t delta = cur_time - cur_icount; + int64_t delta = clock - cur_icount; warp_delta = MIN(warp_delta, delta); } timers_state.qemu_icount_bias += warp_delta; @@ -372,15 +361,19 @@ static void icount_warp_rt(void *opaque) void qtest_clock_warp(int64_t dest) { int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + AioContext *aio_context; assert(qtest_enabled()); + aio_context = qemu_get_aio_context(); while (clock < dest) { int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL); int64_t warp = qemu_soonest_timeout(dest - clock, deadline); + seqlock_write_lock(&timers_state.vm_clock_seqlock); timers_state.qemu_icount_bias += warp; seqlock_write_unlock(&timers_state.vm_clock_seqlock); qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL); + timerlist_run_timers(aio_context->tlg.tl[QEMU_CLOCK_VIRTUAL]); clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); } qemu_clock_notify(QEMU_CLOCK_VIRTUAL); @@ -419,7 +412,7 @@ void qemu_clock_warp(QEMUClockType type) } /* We want to use the earliest deadline from ALL vm_clocks */ - clock = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); + clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT); deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL); if (deadline < 0) { return; @@ -437,8 +430,8 @@ void qemu_clock_warp(QEMUClockType type) * sleep in icount mode if there is a pending QEMU_CLOCK_VIRTUAL * timer; rather time could just advance to the next QEMU_CLOCK_VIRTUAL * event. Instead, we do stop VCPUs and only advance QEMU_CLOCK_VIRTUAL - * after some e"real" time, (related to the time left until the next - * event) has passed. The QEMU_CLOCK_REALTIME timer will do this. + * after some "real" time, (related to the time left until the next + * event) has passed. The QEMU_CLOCK_VIRTUAL_RT clock will do this. * This avoids that the warps are visible externally; for example, * you will not be sending network packets continuously instead of * every 100ms. @@ -512,8 +505,8 @@ void configure_icount(QemuOpts *opts, Error **errp) return; } icount_align_option = qemu_opt_get_bool(opts, "align", false); - icount_warp_timer = timer_new_ns(QEMU_CLOCK_REALTIME, - icount_warp_rt, NULL); + icount_warp_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT, + icount_warp_rt, NULL); if (strcmp(option, "auto") != 0) { errno = 0; icount_time_shift = strtol(option, &rem_str, 0); @@ -537,10 +530,10 @@ void configure_icount(QemuOpts *opts, Error **errp) the virtual time trigger catches emulated time passing too fast. Realtime triggers occur even when idle, so use them less frequently than VM triggers. */ - icount_rt_timer = timer_new_ms(QEMU_CLOCK_REALTIME, - icount_adjust_rt, NULL); + icount_rt_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL_RT, + icount_adjust_rt, NULL); timer_mod(icount_rt_timer, - qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 1000); + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_RT) + 1000); icount_vm_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, icount_adjust_vm, NULL); timer_mod(icount_vm_timer, @@ -785,7 +778,7 @@ static void qemu_tcg_init_cpu_signals(void) static QemuMutex qemu_global_mutex; static QemuCond qemu_io_proceeded_cond; -static bool iothread_requesting_mutex; +static unsigned iothread_requesting_mutex; static QemuThread io_thread; @@ -934,6 +927,7 @@ static void *qemu_kvm_cpu_thread_fn(void *arg) qemu_mutex_lock(&qemu_global_mutex); qemu_thread_get_self(cpu->thread); cpu->thread_id = qemu_get_thread_id(); + cpu->can_do_io = 1; current_cpu = cpu; r = kvm_init_vcpu(cpu); @@ -974,6 +968,7 @@ static void *qemu_dummy_cpu_thread_fn(void *arg) qemu_mutex_lock_iothread(); qemu_thread_get_self(cpu->thread); cpu->thread_id = qemu_get_thread_id(); + cpu->can_do_io = 1; sigemptyset(&waitset); sigaddset(&waitset, SIG_IPI); @@ -1016,6 +1011,7 @@ static void *qemu_tcg_cpu_thread_fn(void *arg) CPU_FOREACH(cpu) { cpu->thread_id = qemu_get_thread_id(); cpu->created = true; + cpu->can_do_io = 1; } qemu_cond_signal(&qemu_cpu_cond); @@ -1029,6 +1025,9 @@ static void *qemu_tcg_cpu_thread_fn(void *arg) } } + /* process any pending work */ + exit_request = 1; + while (1) { tcg_exec_all(); @@ -1112,22 +1111,23 @@ bool qemu_cpu_is_self(CPUState *cpu) return qemu_thread_is_self(cpu->thread); } -static bool qemu_in_vcpu_thread(void) +bool qemu_in_vcpu_thread(void) { return current_cpu && qemu_cpu_is_self(current_cpu); } void qemu_mutex_lock_iothread(void) { - if (!tcg_enabled()) { + atomic_inc(&iothread_requesting_mutex); + if (!tcg_enabled() || !first_cpu || !first_cpu->thread) { qemu_mutex_lock(&qemu_global_mutex); + atomic_dec(&iothread_requesting_mutex); } else { - iothread_requesting_mutex = true; if (qemu_mutex_trylock(&qemu_global_mutex)) { qemu_cpu_kick_thread(first_cpu); qemu_mutex_lock(&qemu_global_mutex); } - iothread_requesting_mutex = false; + atomic_dec(&iothread_requesting_mutex); qemu_cond_broadcast(&qemu_io_proceeded_cond); } } @@ -1353,7 +1353,7 @@ static int tcg_cpu_exec(CPUArchState *env) } ret = cpu_exec(env); #ifdef CONFIG_PROFILER - qemu_time += profile_getclock() - ti; + tcg_time += profile_getclock() - ti; #endif if (use_icount) { /* Fold pending instructions back into the @@ -1474,6 +1474,7 @@ void qmp_memsave(int64_t addr, int64_t size, const char *filename, uint32_t l; CPUState *cpu; uint8_t buf[1024]; + int64_t orig_addr = addr, orig_size = size; if (!has_cpu) { cpu_index = 0; @@ -1497,7 +1498,8 @@ void qmp_memsave(int64_t addr, int64_t size, const char *filename, if (l > size) l = size; if (cpu_memory_rw_debug(cpu, addr, buf, l, 0) != 0) { - error_setg(errp, "Invalid addr 0x%016" PRIx64 "specified", addr); + error_setg(errp, "Invalid addr 0x%016" PRIx64 "/size %" PRId64 + " specified", orig_addr, orig_size); goto exit; } if (fwrite(buf, 1, l, f) != l) { diff --git a/cputlb.c b/cputlb.c index a55518a..38f2151 100644 --- a/cputlb.c +++ b/cputlb.c @@ -243,8 +243,12 @@ static void tlb_add_large_page(CPUArchState *env, target_ulong vaddr, } /* Add a new TLB entry. At most one entry for a given virtual address - is permitted. Only a single TARGET_PAGE_SIZE region is mapped, the - supplied size is only used by tlb_flush_page. */ + * is permitted. Only a single TARGET_PAGE_SIZE region is mapped, the + * supplied size is only used by tlb_flush_page. + * + * Called from TCG-generated code, which is under an RCU read-side + * critical section. + */ void tlb_set_page(CPUState *cpu, target_ulong vaddr, hwaddr paddr, int prot, int mmu_idx, target_ulong size) @@ -265,12 +269,12 @@ void tlb_set_page(CPUState *cpu, target_ulong vaddr, } sz = size; - section = address_space_translate_for_iotlb(cpu->as, paddr, - &xlat, &sz); + section = address_space_translate_for_iotlb(cpu, paddr, &xlat, &sz); assert(sz >= TARGET_PAGE_SIZE); #if defined(DEBUG_TLB) - printf("tlb_set_page: vaddr=" TARGET_FMT_lx " paddr=0x" TARGET_FMT_plx + qemu_log_mask(CPU_LOG_MMU, + "tlb_set_page: vaddr=" TARGET_FMT_lx " paddr=0x" TARGET_FMT_plx " prot=%x idx=%d\n", vaddr, paddr, prot, mmu_idx); #endif @@ -346,7 +350,7 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr) cpu_ldub_code(env1, addr); } pd = env1->iotlb[mmu_idx][page_index] & ~TARGET_PAGE_MASK; - mr = iotlb_to_region(cpu->as, pd); + mr = iotlb_to_region(cpu, pd); if (memory_region_is_unassigned(mr)) { CPUClass *cc = CPU_GET_CLASS(cpu); diff --git a/default-configs/alpha-softmmu.mak b/default-configs/alpha-softmmu.mak index bc07600..7f6161e 100644 --- a/default-configs/alpha-softmmu.mak +++ b/default-configs/alpha-softmmu.mak @@ -5,8 +5,6 @@ include usb.mak CONFIG_SERIAL=y CONFIG_I8254=y CONFIG_PCKBD=y -CONFIG_VGA=y -CONFIG_VGA_PCI=y CONFIG_VGA_CIRRUS=y CONFIG_IDE_CORE=y CONFIG_IDE_QDEV=y diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index f3513fa..a767e4b 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -32,7 +32,10 @@ CONFIG_DS1338=y CONFIG_PFLASH_CFI01=y CONFIG_PFLASH_CFI02=y CONFIG_MICRODRIVE=y +CONFIG_USB=y CONFIG_USB_MUSB=y +CONFIG_USB_EHCI_SYSBUS=y +CONFIG_PLATFORM_BUS=y CONFIG_ARM11MPCORE=y CONFIG_A9MPCORE=y @@ -78,13 +81,23 @@ CONFIG_NSERIES=y CONFIG_REALVIEW=y CONFIG_ZAURUS=y CONFIG_ZYNQ=y +CONFIG_STM32F2XX_TIMER=y +CONFIG_STM32F2XX_USART=y +CONFIG_STM32F2XX_SYSCFG=y +CONFIG_STM32F205_SOC=y CONFIG_VERSATILE_PCI=y CONFIG_VERSATILE_I2C=y +CONFIG_PCI_GENERIC=y + CONFIG_SDHCI=y CONFIG_INTEGRATOR_DEBUG=y CONFIG_ALLWINNER_A10_PIT=y CONFIG_ALLWINNER_A10_PIC=y CONFIG_ALLWINNER_A10=y + +CONFIG_XIO3130=y +CONFIG_IOH3420=y +CONFIG_I82801B11=y diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak index 8e08841..6a74e00 100644 --- a/default-configs/i386-softmmu.mak +++ b/default-configs/i386-softmmu.mak @@ -3,9 +3,7 @@ include pci.mak include sound.mak include usb.mak -CONFIG_VGA=y CONFIG_QXL=$(CONFIG_SPICE) -CONFIG_VGA_PCI=y CONFIG_VGA_ISA=y CONFIG_VGA_CIRRUS=y CONFIG_VMWARE_VGA=y @@ -28,7 +26,6 @@ CONFIG_APPLESMC=y CONFIG_I8259=y CONFIG_PFLASH_CFI01=y CONFIG_TPM_TIS=$(CONFIG_TPM) -CONFIG_PCI_HOTPLUG_OLD=y CONFIG_MC146818RTC=y CONFIG_PAM=y CONFIG_PCI_PIIX=y @@ -45,3 +42,6 @@ CONFIG_IOAPIC=y CONFIG_ICC_BUS=y CONFIG_PVPANIC=y CONFIG_MEM_HOTPLUG=y +CONFIG_XIO3130=y +CONFIG_IOH3420=y +CONFIG_I82801B11=y diff --git a/default-configs/lm32-softmmu.mak b/default-configs/lm32-softmmu.mak index 7df58c8..4889348 100644 --- a/default-configs/lm32-softmmu.mak +++ b/default-configs/lm32-softmmu.mak @@ -2,7 +2,7 @@ CONFIG_LM32=y CONFIG_MILKYMIST=y -CONFIG_MILKYMIST_TMU2=$(CONFIG_GLX) +CONFIG_MILKYMIST_TMU2=$(CONFIG_OPENGL) CONFIG_FRAMEBUFFER=y CONFIG_PTIMER=y CONFIG_PFLASH_CFI01=y diff --git a/default-configs/mips-softmmu.mak b/default-configs/mips-softmmu.mak index 2a80b04..cce2c81 100644 --- a/default-configs/mips-softmmu.mak +++ b/default-configs/mips-softmmu.mak @@ -4,8 +4,6 @@ include pci.mak include sound.mak include usb.mak CONFIG_ESP=y -CONFIG_VGA=y -CONFIG_VGA_PCI=y CONFIG_VGA_ISA=y CONFIG_VGA_ISA_MM=y CONFIG_VGA_CIRRUS=y diff --git a/default-configs/mips64-softmmu.mak b/default-configs/mips64-softmmu.mak index f1f933b..7a88a08 100644 --- a/default-configs/mips64-softmmu.mak +++ b/default-configs/mips64-softmmu.mak @@ -4,8 +4,6 @@ include pci.mak include sound.mak include usb.mak CONFIG_ESP=y -CONFIG_VGA=y -CONFIG_VGA_PCI=y CONFIG_VGA_ISA=y CONFIG_VGA_ISA_MM=y CONFIG_VGA_CIRRUS=y diff --git a/default-configs/mips64el-softmmu.mak b/default-configs/mips64el-softmmu.mak index 317b151..095de43 100644 --- a/default-configs/mips64el-softmmu.mak +++ b/default-configs/mips64el-softmmu.mak @@ -4,8 +4,6 @@ include pci.mak include sound.mak include usb.mak CONFIG_ESP=y -CONFIG_VGA=y -CONFIG_VGA_PCI=y CONFIG_VGA_ISA=y CONFIG_VGA_ISA_MM=y CONFIG_VGA_CIRRUS=y diff --git a/default-configs/mipsel-softmmu.mak b/default-configs/mipsel-softmmu.mak index 7708185..0e25108 100644 --- a/default-configs/mipsel-softmmu.mak +++ b/default-configs/mipsel-softmmu.mak @@ -4,8 +4,6 @@ include pci.mak include sound.mak include usb.mak CONFIG_ESP=y -CONFIG_VGA=y -CONFIG_VGA_PCI=y CONFIG_VGA_ISA=y CONFIG_VGA_ISA_MM=y CONFIG_VGA_CIRRUS=y diff --git a/default-configs/pci.mak b/default-configs/pci.mak index 91b1e92..58a2c0a 100644 --- a/default-configs/pci.mak +++ b/default-configs/pci.mak @@ -30,3 +30,9 @@ CONFIG_IPACK=y CONFIG_WDT_IB6300ESB=y CONFIG_PCI_TESTDEV=y CONFIG_NVME_PCI=y +CONFIG_SD=y +CONFIG_SDHCI=y +CONFIG_EDU=y +CONFIG_VGA=y +CONFIG_VGA_PCI=y +CONFIG_IVSHMEM=$(CONFIG_KVM) diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak index d725b23..4befde3 100644 --- a/default-configs/ppc-softmmu.mak +++ b/default-configs/ppc-softmmu.mak @@ -6,8 +6,6 @@ include usb.mak CONFIG_ISA_MMIO=y CONFIG_ESCC=y CONFIG_M48T59=y -CONFIG_VGA=y -CONFIG_VGA_PCI=y CONFIG_SERIAL=y CONFIG_PARALLEL=y CONFIG_I8254=y @@ -40,11 +38,11 @@ CONFIG_PTIMER=y CONFIG_I8259=y CONFIG_XILINX=y CONFIG_XILINX_ETHLITE=y -CONFIG_OPENPIC=y CONFIG_PREP=y CONFIG_MAC=y CONFIG_E500=y CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM)) +CONFIG_PLATFORM_BUS=y CONFIG_ETSEC=y CONFIG_LIBDECNUMBER=y # For PReP diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak index bd30d69..ab62cc7 100644 --- a/default-configs/ppc64-softmmu.mak +++ b/default-configs/ppc64-softmmu.mak @@ -6,8 +6,6 @@ include usb.mak CONFIG_ISA_MMIO=y CONFIG_ESCC=y CONFIG_M48T59=y -CONFIG_VGA=y -CONFIG_VGA_PCI=y CONFIG_SERIAL=y CONFIG_PARALLEL=y CONFIG_I8254=y @@ -40,23 +38,17 @@ CONFIG_PTIMER=y CONFIG_I8259=y CONFIG_XILINX=y CONFIG_XILINX_ETHLITE=y -CONFIG_OPENPIC=y CONFIG_PSERIES=y CONFIG_PREP=y CONFIG_MAC=y CONFIG_E500=y CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM)) +CONFIG_PLATFORM_BUS=y CONFIG_ETSEC=y CONFIG_LIBDECNUMBER=y # For pSeries CONFIG_XICS=$(CONFIG_PSERIES) CONFIG_XICS_KVM=$(and $(CONFIG_PSERIES),$(CONFIG_KVM)) # For PReP -CONFIG_I82378=y -CONFIG_I8259=y -CONFIG_I8254=y -CONFIG_PCSPK=y -CONFIG_I82374=y -CONFIG_I8257=y CONFIG_MC146818RTC=y CONFIG_ISA_TESTDEV=y diff --git a/default-configs/ppcemb-softmmu.mak b/default-configs/ppcemb-softmmu.mak index e032761..54acc4d 100644 --- a/default-configs/ppcemb-softmmu.mak +++ b/default-configs/ppcemb-softmmu.mak @@ -4,8 +4,6 @@ include pci.mak include sound.mak include usb.mak CONFIG_M48T59=y -CONFIG_VGA=y -CONFIG_VGA_PCI=y CONFIG_SERIAL=y CONFIG_I8257=y CONFIG_OPENPIC=y @@ -15,5 +13,4 @@ CONFIG_PTIMER=y CONFIG_I8259=y CONFIG_XILINX=y CONFIG_XILINX_ETHLITE=y -CONFIG_OPENPIC=y CONFIG_LIBDECNUMBER=y diff --git a/default-configs/s390x-softmmu.mak b/default-configs/s390x-softmmu.mak index 126d88d..f9e13f1 100644 --- a/default-configs/s390x-softmmu.mak +++ b/default-configs/s390x-softmmu.mak @@ -1,3 +1,5 @@ +CONFIG_PCI=y +CONFIG_VIRTIO_PCI=y CONFIG_VIRTIO=y CONFIG_SCLPCONSOLE=y CONFIG_S390_FLIC=y diff --git a/default-configs/sparc64-softmmu.mak b/default-configs/sparc64-softmmu.mak index 299c97b..123bb99 100644 --- a/default-configs/sparc64-softmmu.mak +++ b/default-configs/sparc64-softmmu.mak @@ -5,8 +5,6 @@ include usb.mak CONFIG_ISA_MMIO=y CONFIG_M48T59=y CONFIG_PTIMER=y -CONFIG_VGA=y -CONFIG_VGA_PCI=y CONFIG_SERIAL=y CONFIG_PARALLEL=y CONFIG_PCKBD=y diff --git a/default-configs/usb.mak b/default-configs/usb.mak index 73d8489..f4b8568 100644 --- a/default-configs/usb.mak +++ b/default-configs/usb.mak @@ -1,3 +1,4 @@ +CONFIG_USB=y CONFIG_USB_TABLET_WACOM=y CONFIG_USB_STORAGE_BOT=y CONFIG_USB_STORAGE_UAS=y diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak index 66557ac..46b87dd 100644 --- a/default-configs/x86_64-softmmu.mak +++ b/default-configs/x86_64-softmmu.mak @@ -3,9 +3,7 @@ include pci.mak include sound.mak include usb.mak -CONFIG_VGA=y CONFIG_QXL=$(CONFIG_SPICE) -CONFIG_VGA_PCI=y CONFIG_VGA_ISA=y CONFIG_VGA_CIRRUS=y CONFIG_VMWARE_VGA=y @@ -28,7 +26,6 @@ CONFIG_APPLESMC=y CONFIG_I8259=y CONFIG_PFLASH_CFI01=y CONFIG_TPM_TIS=$(CONFIG_TPM) -CONFIG_PCI_HOTPLUG_OLD=y CONFIG_MC146818RTC=y CONFIG_PAM=y CONFIG_PCI_PIIX=y @@ -45,3 +42,6 @@ CONFIG_IOAPIC=y CONFIG_ICC_BUS=y CONFIG_PVPANIC=y CONFIG_MEM_HOTPLUG=y +CONFIG_XIO3130=y +CONFIG_IOH3420=y +CONFIG_I82801B11=y diff --git a/device-hotplug.c b/device-hotplug.c index 9e38cc4..68b9496 100644 --- a/device-hotplug.c +++ b/device-hotplug.c @@ -30,7 +30,7 @@ #include "sysemu/sysemu.h" #include "monitor/monitor.h" -DriveInfo *add_init_drive(const char *optstr) +static DriveInfo *add_init_drive(const char *optstr) { DriveInfo *dinfo; QemuOpts *opts; @@ -50,7 +50,7 @@ DriveInfo *add_init_drive(const char *optstr) return dinfo; } -void drive_hot_add(Monitor *mon, const QDict *qdict) +void hmp_drive_add(Monitor *mon, const QDict *qdict) { DriveInfo *dinfo = NULL; const char *opts = qdict_get_str(qdict, "opts"); @@ -69,9 +69,8 @@ void drive_hot_add(Monitor *mon, const QDict *qdict) monitor_printf(mon, "OK\n"); break; default: - if (pci_drive_hot_add(mon, qdict, dinfo)) { - goto err; - } + monitor_printf(mon, "Can't hot-add drive to type %d\n", dinfo->type); + goto err; } return; diff --git a/device_tree.c b/device_tree.c index df9eed9..3d119ef 100644 --- a/device_tree.c +++ b/device_tree.c @@ -24,7 +24,7 @@ #include "sysemu/device_tree.h" #include "sysemu/sysemu.h" #include "hw/loader.h" -#include "qemu/option.h" +#include "hw/boards.h" #include "qemu/config-file.h" #include @@ -245,8 +245,7 @@ uint32_t qemu_fdt_alloc_phandle(void *fdt) * which phandle id to start allocting phandles. */ if (!phandle) { - phandle = qemu_opt_get_number(qemu_get_machine_opts(), - "phandle_start", 0); + phandle = machine_phandle_start(current_machine); } if (!phandle) { @@ -324,6 +323,7 @@ int qemu_fdt_setprop_sized_cells_from_array(void *fdt, uint64_t value; int cellnum, vnum, ncells; uint32_t hival; + int ret; propcells = g_new0(uint32_t, numvalues * 2); @@ -331,18 +331,23 @@ int qemu_fdt_setprop_sized_cells_from_array(void *fdt, for (vnum = 0; vnum < numvalues; vnum++) { ncells = values[vnum * 2]; if (ncells != 1 && ncells != 2) { - return -1; + ret = -1; + goto out; } value = values[vnum * 2 + 1]; hival = cpu_to_be32(value >> 32); if (ncells > 1) { propcells[cellnum++] = hival; } else if (hival != 0) { - return -1; + ret = -1; + goto out; } propcells[cellnum++] = cpu_to_be32(value); } - return qemu_fdt_setprop(fdt, node_path, property, propcells, - cellnum * sizeof(uint32_t)); + ret = qemu_fdt_setprop(fdt, node_path, property, propcells, + cellnum * sizeof(uint32_t)); +out: + g_free(propcells); + return ret; } diff --git a/disas/arm-a64.cc b/disas/arm-a64.cc index ca29f6f..e04f946 100644 --- a/disas/arm-a64.cc +++ b/disas/arm-a64.cc @@ -67,7 +67,8 @@ static void vixl_init(FILE *f) { int print_insn_arm_a64(uint64_t addr, disassemble_info *info) { uint8_t bytes[INSN_SIZE]; - uint32_t instr; + uint32_t instrval; + const Instruction *instr; int status; status = info->read_memory_func(addr, bytes, INSN_SIZE, info); @@ -80,8 +81,10 @@ int print_insn_arm_a64(uint64_t addr, disassemble_info *info) vixl_init(info->stream); } - instr = bytes[0] | bytes[1] << 8 | bytes[2] << 16 | bytes[3] << 24; - vixl_decoder->Decode(reinterpret_cast(&instr)); + instrval = bytes[0] | bytes[1] << 8 | bytes[2] << 16 | bytes[3] << 24; + instr = reinterpret_cast(&instrval); + vixl_disasm->MapCodeAddress(addr, instr); + vixl_decoder->Decode(instr); return INSN_SIZE; } diff --git a/disas/arm.c b/disas/arm.c index 76e97a8..6165246 100644 --- a/disas/arm.c +++ b/disas/arm.c @@ -1549,10 +1549,6 @@ enum map_type { MAP_DATA }; -enum map_type last_type; -int last_mapping_sym = -1; -bfd_vma last_mapping_addr = 0; - /* Decode a bitfield of the form matching regexp (N(-N)?,)*N(-N)?. Returns pointer to following character of the format string and fills in *VALUEP and *WIDTHP with the extracted value and number of @@ -3878,135 +3874,11 @@ print_insn_arm (bfd_vma pc, struct disassemble_info *info) int is_data = false; unsigned int size = 4; void (*printer) (bfd_vma, struct disassemble_info *, long); -#if 0 - bfd_boolean found = false; - - if (info->disassembler_options) - { - parse_disassembler_options (info->disassembler_options); - - /* To avoid repeated parsing of these options, we remove them here. */ - info->disassembler_options = NULL; - } - - /* First check the full symtab for a mapping symbol, even if there - are no usable non-mapping symbols for this address. */ - if (info->symtab != NULL - && bfd_asymbol_flavour (*info->symtab) == bfd_target_elf_flavour) - { - bfd_vma addr; - int n; - int last_sym = -1; - enum map_type type = MAP_ARM; - - if (pc <= last_mapping_addr) - last_mapping_sym = -1; - is_thumb = (last_type == MAP_THUMB); - found = false; - /* Start scanning at the start of the function, or wherever - we finished last time. */ - n = info->symtab_pos + 1; - if (n < last_mapping_sym) - n = last_mapping_sym; - - /* Scan up to the location being disassembled. */ - for (; n < info->symtab_size; n++) - { - addr = bfd_asymbol_value (info->symtab[n]); - if (addr > pc) - break; - if ((info->section == NULL - || info->section == info->symtab[n]->section) - && get_sym_code_type (info, n, &type)) - { - last_sym = n; - found = true; - } - } - - if (!found) - { - n = info->symtab_pos; - if (n < last_mapping_sym - 1) - n = last_mapping_sym - 1; - - /* No mapping symbol found at this address. Look backwards - for a preceding one. */ - for (; n >= 0; n--) - { - if (get_sym_code_type (info, n, &type)) - { - last_sym = n; - found = true; - break; - } - } - } - - last_mapping_sym = last_sym; - last_type = type; - is_thumb = (last_type == MAP_THUMB); - is_data = (last_type == MAP_DATA); - - /* Look a little bit ahead to see if we should print out - two or four bytes of data. If there's a symbol, - mapping or otherwise, after two bytes then don't - print more. */ - if (is_data) - { - size = 4 - (pc & 3); - for (n = last_sym + 1; n < info->symtab_size; n++) - { - addr = bfd_asymbol_value (info->symtab[n]); - if (addr > pc) - { - if (addr - pc < size) - size = addr - pc; - break; - } - } - /* If the next symbol is after three bytes, we need to - print only part of the data, so that we can use either - .byte or .short. */ - if (size == 3) - size = (pc & 1) ? 1 : 2; - } - } - - if (info->symbols != NULL) - { - if (bfd_asymbol_flavour (*info->symbols) == bfd_target_coff_flavour) - { - coff_symbol_type * cs; - - cs = coffsymbol (*info->symbols); - is_thumb = ( cs->native->u.syment.n_sclass == C_THUMBEXT - || cs->native->u.syment.n_sclass == C_THUMBSTAT - || cs->native->u.syment.n_sclass == C_THUMBLABEL - || cs->native->u.syment.n_sclass == C_THUMBEXTFUNC - || cs->native->u.syment.n_sclass == C_THUMBSTATFUNC); - } - else if (bfd_asymbol_flavour (*info->symbols) == bfd_target_elf_flavour - && !found) - { - /* If no mapping symbol has been found then fall back to the type - of the function symbol. */ - elf_symbol_type * es; - unsigned int type; - - es = *(elf_symbol_type **)(info->symbols); - type = ELF_ST_TYPE (es->internal_elf_sym.st_info); - - is_thumb = (type == STT_ARM_TFUNC) || (type == STT_ARM_16BIT); - } - } -#else int little; little = (info->endian == BFD_ENDIAN_LITTLE); is_thumb |= (pc & 1); pc &= ~(bfd_vma)1; -#endif if (force_thumb) is_thumb = true; diff --git a/disas/cris.c b/disas/cris.c index 9dfb4e3..e6cff7a 100644 --- a/disas/cris.c +++ b/disas/cris.c @@ -1210,21 +1210,10 @@ cris_cc_strings[] = "le", "a", /* This is a placeholder. In v0, this would be "ext". In v32, this - is "sb". See cris_conds15. */ + is "sb". */ "wf" }; -/* Different names and semantics for condition 1111 (0xf). */ -const struct cris_cond15 cris_cond15s[] = -{ - /* FIXME: In what version did condition "ext" disappear? */ - {"ext", cris_ver_v0_3}, - {"wf", cris_ver_v10}, - {"sb", cris_ver_v32p}, - {NULL, 0} -}; - - /* * Local variables: * eval: (c-set-style "gnu") diff --git a/disas/libvixl/README b/disas/libvixl/README index cba31b4..58db41c 100644 --- a/disas/libvixl/README +++ b/disas/libvixl/README @@ -2,7 +2,7 @@ The code in this directory is a subset of libvixl: https://github.com/armvixl/vixl (specifically, it is the set of files needed for disassembly only, -taken from libvixl 1.6). +taken from libvixl 1.7). Bugfixes should preferably be sent upstream initially. The disassembler does not currently support the entire A64 instruction diff --git a/disas/libvixl/a64/assembler-a64.h b/disas/libvixl/a64/assembler-a64.h index 16a704b..35aaf20 100644 --- a/disas/libvixl/a64/assembler-a64.h +++ b/disas/libvixl/a64/assembler-a64.h @@ -151,21 +151,21 @@ class CPURegister { return Aliases(other) && (size_ == other.size_); } - inline bool IsZero() const { + bool IsZero() const { VIXL_ASSERT(IsValid()); return IsRegister() && (code_ == kZeroRegCode); } - inline bool IsSP() const { + bool IsSP() const { VIXL_ASSERT(IsValid()); return IsRegister() && (code_ == kSPRegInternalCode); } - inline bool IsRegister() const { + bool IsRegister() const { return type_ == kRegister; } - inline bool IsFPRegister() const { + bool IsFPRegister() const { return type_ == kFPRegister; } @@ -179,7 +179,7 @@ class CPURegister { const FPRegister& S() const; const FPRegister& D() const; - inline bool IsSameSizeAndType(const CPURegister& other) const { + bool IsSameSizeAndType(const CPURegister& other) const { return (size_ == other.size_) && (type_ == other.type_); } @@ -198,7 +198,7 @@ class CPURegister { class Register : public CPURegister { public: Register() : CPURegister() {} - inline explicit Register(const CPURegister& other) + explicit Register(const CPURegister& other) : CPURegister(other.code(), other.size(), other.type()) { VIXL_ASSERT(IsValidRegister()); } @@ -213,10 +213,6 @@ class Register : public CPURegister { static const Register& WRegFromCode(unsigned code); static const Register& XRegFromCode(unsigned code); - // V8 compatibility. - static const int kNumRegisters = kNumberOfRegisters; - static const int kNumAllocatableRegisters = kNumberOfRegisters - 1; - private: static const Register wregisters[]; static const Register xregisters[]; @@ -225,12 +221,12 @@ class Register : public CPURegister { class FPRegister : public CPURegister { public: - inline FPRegister() : CPURegister() {} - inline explicit FPRegister(const CPURegister& other) + FPRegister() : CPURegister() {} + explicit FPRegister(const CPURegister& other) : CPURegister(other.code(), other.size(), other.type()) { VIXL_ASSERT(IsValidFPRegister()); } - inline FPRegister(unsigned code, unsigned size) + FPRegister(unsigned code, unsigned size) : CPURegister(code, size, kFPRegister) {} bool IsValid() const { @@ -241,10 +237,6 @@ class FPRegister : public CPURegister { static const FPRegister& SRegFromCode(unsigned code); static const FPRegister& DRegFromCode(unsigned code); - // V8 compatibility. - static const int kNumRegisters = kNumberOfFPRegisters; - static const int kNumAllocatableRegisters = kNumberOfFPRegisters - 1; - private: static const FPRegister sregisters[]; static const FPRegister dregisters[]; @@ -312,23 +304,23 @@ bool AreSameSizeAndType(const CPURegister& reg1, // Lists of registers. class CPURegList { public: - inline explicit CPURegList(CPURegister reg1, - CPURegister reg2 = NoCPUReg, - CPURegister reg3 = NoCPUReg, - CPURegister reg4 = NoCPUReg) + explicit CPURegList(CPURegister reg1, + CPURegister reg2 = NoCPUReg, + CPURegister reg3 = NoCPUReg, + CPURegister reg4 = NoCPUReg) : list_(reg1.Bit() | reg2.Bit() | reg3.Bit() | reg4.Bit()), size_(reg1.size()), type_(reg1.type()) { VIXL_ASSERT(AreSameSizeAndType(reg1, reg2, reg3, reg4)); VIXL_ASSERT(IsValid()); } - inline CPURegList(CPURegister::RegisterType type, unsigned size, RegList list) + CPURegList(CPURegister::RegisterType type, unsigned size, RegList list) : list_(list), size_(size), type_(type) { VIXL_ASSERT(IsValid()); } - inline CPURegList(CPURegister::RegisterType type, unsigned size, - unsigned first_reg, unsigned last_reg) + CPURegList(CPURegister::RegisterType type, unsigned size, + unsigned first_reg, unsigned last_reg) : size_(size), type_(type) { VIXL_ASSERT(((type == CPURegister::kRegister) && (last_reg < kNumberOfRegisters)) || @@ -340,7 +332,7 @@ class CPURegList { VIXL_ASSERT(IsValid()); } - inline CPURegister::RegisterType type() const { + CPURegister::RegisterType type() const { VIXL_ASSERT(IsValid()); return type_; } @@ -366,13 +358,13 @@ class CPURegList { } // Variants of Combine and Remove which take a single register. - inline void Combine(const CPURegister& other) { + void Combine(const CPURegister& other) { VIXL_ASSERT(other.type() == type_); VIXL_ASSERT(other.size() == size_); Combine(other.code()); } - inline void Remove(const CPURegister& other) { + void Remove(const CPURegister& other) { VIXL_ASSERT(other.type() == type_); VIXL_ASSERT(other.size() == size_); Remove(other.code()); @@ -380,24 +372,51 @@ class CPURegList { // Variants of Combine and Remove which take a single register by its code; // the type and size of the register is inferred from this list. - inline void Combine(int code) { + void Combine(int code) { VIXL_ASSERT(IsValid()); VIXL_ASSERT(CPURegister(code, size_, type_).IsValid()); list_ |= (UINT64_C(1) << code); } - inline void Remove(int code) { + void Remove(int code) { VIXL_ASSERT(IsValid()); VIXL_ASSERT(CPURegister(code, size_, type_).IsValid()); list_ &= ~(UINT64_C(1) << code); } - inline RegList list() const { + static CPURegList Union(const CPURegList& list_1, const CPURegList& list_2) { + VIXL_ASSERT(list_1.type_ == list_2.type_); + VIXL_ASSERT(list_1.size_ == list_2.size_); + return CPURegList(list_1.type_, list_1.size_, list_1.list_ | list_2.list_); + } + static CPURegList Union(const CPURegList& list_1, + const CPURegList& list_2, + const CPURegList& list_3); + static CPURegList Union(const CPURegList& list_1, + const CPURegList& list_2, + const CPURegList& list_3, + const CPURegList& list_4); + + static CPURegList Intersection(const CPURegList& list_1, + const CPURegList& list_2) { + VIXL_ASSERT(list_1.type_ == list_2.type_); + VIXL_ASSERT(list_1.size_ == list_2.size_); + return CPURegList(list_1.type_, list_1.size_, list_1.list_ & list_2.list_); + } + static CPURegList Intersection(const CPURegList& list_1, + const CPURegList& list_2, + const CPURegList& list_3); + static CPURegList Intersection(const CPURegList& list_1, + const CPURegList& list_2, + const CPURegList& list_3, + const CPURegList& list_4); + + RegList list() const { VIXL_ASSERT(IsValid()); return list_; } - inline void set_list(RegList new_list) { + void set_list(RegList new_list) { VIXL_ASSERT(IsValid()); list_ = new_list; } @@ -417,38 +436,38 @@ class CPURegList { static CPURegList GetCallerSaved(unsigned size = kXRegSize); static CPURegList GetCallerSavedFP(unsigned size = kDRegSize); - inline bool IsEmpty() const { + bool IsEmpty() const { VIXL_ASSERT(IsValid()); return list_ == 0; } - inline bool IncludesAliasOf(const CPURegister& other) const { + bool IncludesAliasOf(const CPURegister& other) const { VIXL_ASSERT(IsValid()); return (type_ == other.type()) && ((other.Bit() & list_) != 0); } - inline bool IncludesAliasOf(int code) const { + bool IncludesAliasOf(int code) const { VIXL_ASSERT(IsValid()); return ((code & list_) != 0); } - inline int Count() const { + int Count() const { VIXL_ASSERT(IsValid()); return CountSetBits(list_, kRegListSizeInBits); } - inline unsigned RegisterSizeInBits() const { + unsigned RegisterSizeInBits() const { VIXL_ASSERT(IsValid()); return size_; } - inline unsigned RegisterSizeInBytes() const { + unsigned RegisterSizeInBytes() const { int size_in_bits = RegisterSizeInBits(); VIXL_ASSERT((size_in_bits % 8) == 0); return size_in_bits / 8; } - inline unsigned TotalSizeInBytes() const { + unsigned TotalSizeInBytes() const { VIXL_ASSERT(IsValid()); return RegisterSizeInBytes() * Count(); } @@ -587,8 +606,10 @@ class Label { VIXL_ASSERT(!IsLinked() || IsBound()); } - inline bool IsBound() const { return location_ >= 0; } - inline bool IsLinked() const { return !links_.empty(); } + bool IsBound() const { return location_ >= 0; } + bool IsLinked() const { return !links_.empty(); } + + ptrdiff_t location() const { return location_; } private: // The list of linked instructions is stored in a stack-like structure. We @@ -647,22 +668,20 @@ class Label { std::stack * links_extended_; }; - inline ptrdiff_t location() const { return location_; } - - inline void Bind(ptrdiff_t location) { + void Bind(ptrdiff_t location) { // Labels can only be bound once. VIXL_ASSERT(!IsBound()); location_ = location; } - inline void AddLink(ptrdiff_t instruction) { + void AddLink(ptrdiff_t instruction) { // If a label is bound, the assembler already has the information it needs // to write the instruction, so there is no need to add it to links_. VIXL_ASSERT(!IsBound()); links_.push(instruction); } - inline ptrdiff_t GetAndRemoveNextLink() { + ptrdiff_t GetAndRemoveNextLink() { VIXL_ASSERT(IsLinked()); ptrdiff_t link = links_.top(); links_.pop(); @@ -845,14 +864,14 @@ class Assembler { // Return the address of an offset in the buffer. template - inline T GetOffsetAddress(ptrdiff_t offset) { + T GetOffsetAddress(ptrdiff_t offset) { VIXL_STATIC_ASSERT(sizeof(T) >= sizeof(uintptr_t)); return buffer_->GetOffsetAddress(offset); } // Return the address of a bound label. template - inline T GetLabelAddress(const Label * label) { + T GetLabelAddress(const Label * label) { VIXL_ASSERT(label->IsBound()); VIXL_STATIC_ASSERT(sizeof(T) >= sizeof(uintptr_t)); return GetOffsetAddress(label->location()); @@ -860,14 +879,14 @@ class Assembler { // Return the address of the cursor. template - inline T GetCursorAddress() { + T GetCursorAddress() { VIXL_STATIC_ASSERT(sizeof(T) >= sizeof(uintptr_t)); return GetOffsetAddress(CursorOffset()); } // Return the address of the start of the buffer. template - inline T GetStartAddress() { + T GetStartAddress() { VIXL_STATIC_ASSERT(sizeof(T) >= sizeof(uintptr_t)); return GetOffsetAddress(0); } @@ -1074,20 +1093,20 @@ class Assembler { // Bfm aliases. // Bitfield insert. - inline void bfi(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { + void bfi(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { VIXL_ASSERT(width >= 1); VIXL_ASSERT(lsb + width <= rn.size()); bfm(rd, rn, (rd.size() - lsb) & (rd.size() - 1), width - 1); } // Bitfield extract and insert low. - inline void bfxil(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { + void bfxil(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { VIXL_ASSERT(width >= 1); VIXL_ASSERT(lsb + width <= rn.size()); bfm(rd, rn, lsb, lsb + width - 1); @@ -1095,92 +1114,92 @@ class Assembler { // Sbfm aliases. // Arithmetic shift right. - inline void asr(const Register& rd, const Register& rn, unsigned shift) { + void asr(const Register& rd, const Register& rn, unsigned shift) { VIXL_ASSERT(shift < rd.size()); sbfm(rd, rn, shift, rd.size() - 1); } // Signed bitfield insert with zero at right. - inline void sbfiz(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { + void sbfiz(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { VIXL_ASSERT(width >= 1); VIXL_ASSERT(lsb + width <= rn.size()); sbfm(rd, rn, (rd.size() - lsb) & (rd.size() - 1), width - 1); } // Signed bitfield extract. - inline void sbfx(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { + void sbfx(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { VIXL_ASSERT(width >= 1); VIXL_ASSERT(lsb + width <= rn.size()); sbfm(rd, rn, lsb, lsb + width - 1); } // Signed extend byte. - inline void sxtb(const Register& rd, const Register& rn) { + void sxtb(const Register& rd, const Register& rn) { sbfm(rd, rn, 0, 7); } // Signed extend halfword. - inline void sxth(const Register& rd, const Register& rn) { + void sxth(const Register& rd, const Register& rn) { sbfm(rd, rn, 0, 15); } // Signed extend word. - inline void sxtw(const Register& rd, const Register& rn) { + void sxtw(const Register& rd, const Register& rn) { sbfm(rd, rn, 0, 31); } // Ubfm aliases. // Logical shift left. - inline void lsl(const Register& rd, const Register& rn, unsigned shift) { + void lsl(const Register& rd, const Register& rn, unsigned shift) { unsigned reg_size = rd.size(); VIXL_ASSERT(shift < reg_size); ubfm(rd, rn, (reg_size - shift) % reg_size, reg_size - shift - 1); } // Logical shift right. - inline void lsr(const Register& rd, const Register& rn, unsigned shift) { + void lsr(const Register& rd, const Register& rn, unsigned shift) { VIXL_ASSERT(shift < rd.size()); ubfm(rd, rn, shift, rd.size() - 1); } // Unsigned bitfield insert with zero at right. - inline void ubfiz(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { + void ubfiz(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { VIXL_ASSERT(width >= 1); VIXL_ASSERT(lsb + width <= rn.size()); ubfm(rd, rn, (rd.size() - lsb) & (rd.size() - 1), width - 1); } // Unsigned bitfield extract. - inline void ubfx(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { + void ubfx(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { VIXL_ASSERT(width >= 1); VIXL_ASSERT(lsb + width <= rn.size()); ubfm(rd, rn, lsb, lsb + width - 1); } // Unsigned extend byte. - inline void uxtb(const Register& rd, const Register& rn) { + void uxtb(const Register& rd, const Register& rn) { ubfm(rd, rn, 0, 7); } // Unsigned extend halfword. - inline void uxth(const Register& rd, const Register& rn) { + void uxth(const Register& rd, const Register& rn) { ubfm(rd, rn, 0, 15); } // Unsigned extend word. - inline void uxtw(const Register& rd, const Register& rn) { + void uxtw(const Register& rd, const Register& rn) { ubfm(rd, rn, 0, 31); } @@ -1230,7 +1249,7 @@ class Assembler { void cneg(const Register& rd, const Register& rn, Condition cond); // Rotate right. - inline void ror(const Register& rd, const Register& rs, unsigned shift) { + void ror(const Register& rd, const Register& rs, unsigned shift) { extr(rd, rs, rs, shift); } @@ -1495,6 +1514,19 @@ class Assembler { // Load-acquire register. void ldar(const Register& rt, const MemOperand& src); + // Prefetch memory. + void prfm(PrefetchOperation op, const MemOperand& addr, + LoadStoreScalingOption option = PreferScaledOffset); + + // Prefetch memory (with unscaled offset). + void prfum(PrefetchOperation op, const MemOperand& addr, + LoadStoreScalingOption option = PreferUnscaledOffset); + + // Prefetch memory in the literal pool. + void prfm(PrefetchOperation op, RawLiteral* literal); + + // Prefetch from pc + imm19 << 2. + void prfm(PrefetchOperation op, int imm19); // Move instructions. The default shift of -1 indicates that the move // instruction will calculate an appropriate 16-bit immediate and left shift @@ -1638,12 +1670,21 @@ class Assembler { // FP round to integer (nearest with ties to away). void frinta(const FPRegister& fd, const FPRegister& fn); + // FP round to integer (implicit rounding). + void frinti(const FPRegister& fd, const FPRegister& fn); + // FP round to integer (toward minus infinity). void frintm(const FPRegister& fd, const FPRegister& fn); // FP round to integer (nearest with ties to even). void frintn(const FPRegister& fd, const FPRegister& fn); + // FP round to integer (toward plus infinity). + void frintp(const FPRegister& fd, const FPRegister& fn); + + // FP round to integer (exact, implicit rounding). + void frintx(const FPRegister& fd, const FPRegister& fn); + // FP round to integer (towards zero). void frintz(const FPRegister& fd, const FPRegister& fn); @@ -1705,16 +1746,16 @@ class Assembler { // Emit generic instructions. // Emit raw instructions into the instruction stream. - inline void dci(Instr raw_inst) { Emit(raw_inst); } + void dci(Instr raw_inst) { Emit(raw_inst); } // Emit 32 bits of data into the instruction stream. - inline void dc32(uint32_t data) { + void dc32(uint32_t data) { VIXL_ASSERT(buffer_monitor_ > 0); buffer_->Emit32(data); } // Emit 64 bits of data into the instruction stream. - inline void dc64(uint64_t data) { + void dc64(uint64_t data) { VIXL_ASSERT(buffer_monitor_ > 0); buffer_->Emit64(data); } @@ -1849,14 +1890,14 @@ class Assembler { } } - static inline Instr ImmS(unsigned imms, unsigned reg_size) { + static Instr ImmS(unsigned imms, unsigned reg_size) { VIXL_ASSERT(((reg_size == kXRegSize) && is_uint6(imms)) || ((reg_size == kWRegSize) && is_uint5(imms))); USE(reg_size); return imms << ImmS_offset; } - static inline Instr ImmR(unsigned immr, unsigned reg_size) { + static Instr ImmR(unsigned immr, unsigned reg_size) { VIXL_ASSERT(((reg_size == kXRegSize) && is_uint6(immr)) || ((reg_size == kWRegSize) && is_uint5(immr))); USE(reg_size); @@ -1864,7 +1905,7 @@ class Assembler { return immr << ImmR_offset; } - static inline Instr ImmSetBits(unsigned imms, unsigned reg_size) { + static Instr ImmSetBits(unsigned imms, unsigned reg_size) { VIXL_ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); VIXL_ASSERT(is_uint6(imms)); VIXL_ASSERT((reg_size == kXRegSize) || is_uint6(imms + 3)); @@ -1872,7 +1913,7 @@ class Assembler { return imms << ImmSetBits_offset; } - static inline Instr ImmRotate(unsigned immr, unsigned reg_size) { + static Instr ImmRotate(unsigned immr, unsigned reg_size) { VIXL_ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); VIXL_ASSERT(((reg_size == kXRegSize) && is_uint6(immr)) || ((reg_size == kWRegSize) && is_uint5(immr))); @@ -1880,12 +1921,12 @@ class Assembler { return immr << ImmRotate_offset; } - static inline Instr ImmLLiteral(int imm19) { + static Instr ImmLLiteral(int imm19) { VIXL_ASSERT(is_int19(imm19)); return truncate_to_int19(imm19) << ImmLLiteral_offset; } - static inline Instr BitN(unsigned bitn, unsigned reg_size) { + static Instr BitN(unsigned bitn, unsigned reg_size) { VIXL_ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); VIXL_ASSERT((reg_size == kXRegSize) || (bitn == 0)); USE(reg_size); @@ -1943,6 +1984,11 @@ class Assembler { return shift_amount << ImmShiftLS_offset; } + static Instr ImmPrefetchOperation(int imm5) { + VIXL_ASSERT(is_uint5(imm5)); + return imm5 << ImmPrefetchOperation_offset; + } + static Instr ImmException(int imm16) { VIXL_ASSERT(is_uint16(imm16)); return imm16 << ImmException_offset; @@ -2003,12 +2049,32 @@ class Assembler { return scale << FPScale_offset; } + // Immediate field checking helpers. + static bool IsImmAddSub(int64_t immediate); + static bool IsImmConditionalCompare(int64_t immediate); + static bool IsImmFP32(float imm); + static bool IsImmFP64(double imm); + static bool IsImmLogical(uint64_t value, + unsigned width, + unsigned* n = NULL, + unsigned* imm_s = NULL, + unsigned* imm_r = NULL); + static bool IsImmLSPair(int64_t offset, LSDataSize size); + static bool IsImmLSScaled(int64_t offset, LSDataSize size); + static bool IsImmLSUnscaled(int64_t offset); + static bool IsImmMovn(uint64_t imm, unsigned reg_size); + static bool IsImmMovz(uint64_t imm, unsigned reg_size); + // Size of the code generated since label to the current position. size_t SizeOfCodeGeneratedSince(Label* label) const { VIXL_ASSERT(label->IsBound()); return buffer_->OffsetFrom(label->location()); } + size_t SizeOfCodeGenerated() const { + return buffer_->CursorOffset(); + } + size_t BufferCapacity() const { return buffer_->capacity(); } size_t RemainingBufferSpace() const { return buffer_->RemainingBytes(); } @@ -2025,7 +2091,7 @@ class Assembler { } } -#ifdef DEBUG +#ifdef VIXL_DEBUG void AcquireBuffer() { VIXL_ASSERT(buffer_monitor_ >= 0); buffer_monitor_++; @@ -2037,16 +2103,16 @@ class Assembler { } #endif - inline PositionIndependentCodeOption pic() { + PositionIndependentCodeOption pic() const { return pic_; } - inline bool AllowPageOffsetDependentCode() { + bool AllowPageOffsetDependentCode() const { return (pic() == PageOffsetDependentCode) || (pic() == PositionDependentCode); } - static inline const Register& AppropriateZeroRegFor(const CPURegister& reg) { + static const Register& AppropriateZeroRegFor(const CPURegister& reg) { return reg.Is64Bits() ? xzr : wzr; } @@ -2056,14 +2122,15 @@ class Assembler { const MemOperand& addr, LoadStoreOp op, LoadStoreScalingOption option = PreferScaledOffset); - static bool IsImmLSUnscaled(int64_t offset); - static bool IsImmLSScaled(int64_t offset, LSDataSize size); void LoadStorePair(const CPURegister& rt, const CPURegister& rt2, const MemOperand& addr, LoadStorePairOp op); - static bool IsImmLSPair(int64_t offset, LSDataSize size); + + void Prefetch(PrefetchOperation op, + const MemOperand& addr, + LoadStoreScalingOption option = PreferScaledOffset); // TODO(all): The third parameter should be passed by reference but gcc 4.8.2 // reports a bogus uninitialised warning then. @@ -2077,18 +2144,12 @@ class Assembler { unsigned imm_s, unsigned imm_r, LogicalOp op); - static bool IsImmLogical(uint64_t value, - unsigned width, - unsigned* n = NULL, - unsigned* imm_s = NULL, - unsigned* imm_r = NULL); void ConditionalCompare(const Register& rn, const Operand& operand, StatusFlags nzcv, Condition cond, ConditionalCompareOp op); - static bool IsImmConditionalCompare(int64_t immediate); void AddSubWithCarry(const Register& rd, const Register& rn, @@ -2096,8 +2157,6 @@ class Assembler { FlagsUpdate S, AddSubWithCarryOp op); - static bool IsImmFP32(float imm); - static bool IsImmFP64(double imm); // Functions for emulating operands not directly supported by the instruction // set. @@ -2115,7 +2174,6 @@ class Assembler { const Operand& operand, FlagsUpdate S, AddSubOp op); - static bool IsImmAddSub(int64_t immediate); // Find an appropriate LoadStoreOp or LoadStorePairOp for the specified // registers. Only simple loads are supported; sign- and zero-extension (such @@ -2180,6 +2238,12 @@ class Assembler { const FPRegister& fa, FPDataProcessing3SourceOp op); + // Encode the specified MemOperand for the specified access size and scaling + // preference. + Instr LoadStoreMemOperand(const MemOperand& addr, + LSDataSize size, + LoadStoreScalingOption option); + // Link the current (not-yet-emitted) instruction to the specified label, then // return an offset to be encoded in the instruction. If the label is not yet // bound, an offset of 0 is returned. @@ -2205,7 +2269,7 @@ class Assembler { CodeBuffer* buffer_; PositionIndependentCodeOption pic_; -#ifdef DEBUG +#ifdef VIXL_DEBUG int64_t buffer_monitor_; #endif }; @@ -2239,7 +2303,7 @@ class CodeBufferCheckScope { AssertPolicy assert_policy = kMaximumSize) : assm_(assm) { if (check_policy == kCheck) assm->EnsureSpaceFor(size); -#ifdef DEBUG +#ifdef VIXL_DEBUG assm->bind(&start_); size_ = size; assert_policy_ = assert_policy; @@ -2251,7 +2315,7 @@ class CodeBufferCheckScope { // This is a shortcut for CodeBufferCheckScope(assm, 0, kNoCheck, kNoAssert). explicit CodeBufferCheckScope(Assembler* assm) : assm_(assm) { -#ifdef DEBUG +#ifdef VIXL_DEBUG size_ = 0; assert_policy_ = kNoAssert; assm->AcquireBuffer(); @@ -2259,7 +2323,7 @@ class CodeBufferCheckScope { } ~CodeBufferCheckScope() { -#ifdef DEBUG +#ifdef VIXL_DEBUG assm_->ReleaseBuffer(); switch (assert_policy_) { case kNoAssert: break; @@ -2277,7 +2341,7 @@ class CodeBufferCheckScope { protected: Assembler* assm_; -#ifdef DEBUG +#ifdef VIXL_DEBUG Label start_; size_t size_; AssertPolicy assert_policy_; diff --git a/disas/libvixl/a64/constants-a64.h b/disas/libvixl/a64/constants-a64.h index 7a14f85..bc1a2c4 100644 --- a/disas/libvixl/a64/constants-a64.h +++ b/disas/libvixl/a64/constants-a64.h @@ -31,12 +31,6 @@ namespace vixl { const unsigned kNumberOfRegisters = 32; const unsigned kNumberOfFPRegisters = 32; -// Callee saved registers are x21-x30(lr). -const int kNumberOfCalleeSavedRegisters = 10; -const int kFirstCalleeSavedRegisterIndex = 21; -// Callee saved FP registers are d8-d15. -const int kNumberOfCalleeSavedFPRegisters = 8; -const int kFirstCalleeSavedFPRegisterIndex = 8; #define REGISTER_CODE_LIST(R) \ R(0) R(1) R(2) R(3) R(4) R(5) R(6) R(7) \ @@ -53,7 +47,6 @@ V_(Ra, 14, 10, Bits) /* Third source register. */ \ V_(Rt, 4, 0, Bits) /* Load/store register. */ \ V_(Rt2, 14, 10, Bits) /* Load/store second register. */ \ V_(Rs, 20, 16, Bits) /* Exclusive access status. */ \ -V_(PrefetchMode, 4, 0, Bits) \ \ /* Common bits */ \ V_(SixtyFourBits, 31, 31, Bits) \ @@ -109,6 +102,10 @@ V_(ImmLSUnsigned, 21, 10, Bits) \ V_(ImmLSPair, 21, 15, SignedBits) \ V_(SizeLS, 31, 30, Bits) \ V_(ImmShiftLS, 12, 12, Bits) \ +V_(ImmPrefetchOperation, 4, 0, Bits) \ +V_(PrefetchHint, 4, 3, Bits) \ +V_(PrefetchTarget, 2, 1, Bits) \ +V_(PrefetchStream, 0, 0, Bits) \ \ /* Other immediates */ \ V_(ImmUncondBranch, 25, 0, SignedBits) \ @@ -269,6 +266,29 @@ enum BarrierType { BarrierAll = 3 }; +enum PrefetchOperation { + PLDL1KEEP = 0x00, + PLDL1STRM = 0x01, + PLDL2KEEP = 0x02, + PLDL2STRM = 0x03, + PLDL3KEEP = 0x04, + PLDL3STRM = 0x05, + + PLIL1KEEP = 0x08, + PLIL1STRM = 0x09, + PLIL2KEEP = 0x0a, + PLIL2STRM = 0x0b, + PLIL3KEEP = 0x0c, + PLIL3STRM = 0x0d, + + PSTL1KEEP = 0x10, + PSTL1STRM = 0x11, + PSTL2KEEP = 0x12, + PSTL2STRM = 0x13, + PSTL3KEEP = 0x14, + PSTL3STRM = 0x15 +}; + // System/special register names. // This information is not encoded as one field but as the concatenation of // multiple fields (Op0<0>, Op1, Crn, Crm, Op2). @@ -605,6 +625,12 @@ enum LoadStoreAnyOp { LoadStoreAnyFixed = 0x08000000 }; +// Any load pair or store pair. +enum LoadStorePairAnyOp { + LoadStorePairAnyFMask = 0x3a000000, + LoadStorePairAnyFixed = 0x28000000 +}; + #define LOAD_STORE_PAIR_OP_LIST(V) \ V(STP, w, 0x00000000), \ V(LDP, w, 0x00400000), \ @@ -703,27 +729,28 @@ enum LoadLiteralOp { V(LD, R, d, 0xC4400000) +// Load/store (post, pre, offset and unsigned.) +enum LoadStoreOp { + LoadStoreOpMask = 0xC4C00000, + #define LOAD_STORE(A, B, C, D) \ + A##B##_##C = D + LOAD_STORE_OP_LIST(LOAD_STORE), + #undef LOAD_STORE + PRFM = 0xC0800000 +}; + // Load/store unscaled offset. enum LoadStoreUnscaledOffsetOp { LoadStoreUnscaledOffsetFixed = 0x38000000, LoadStoreUnscaledOffsetFMask = 0x3B200C00, LoadStoreUnscaledOffsetMask = 0xFFE00C00, + PRFUM = LoadStoreUnscaledOffsetFixed | PRFM, #define LOAD_STORE_UNSCALED(A, B, C, D) \ A##U##B##_##C = LoadStoreUnscaledOffsetFixed | D LOAD_STORE_OP_LIST(LOAD_STORE_UNSCALED) #undef LOAD_STORE_UNSCALED }; -// Load/store (post, pre, offset and unsigned.) -enum LoadStoreOp { - LoadStoreOpMask = 0xC4C00000, - #define LOAD_STORE(A, B, C, D) \ - A##B##_##C = D - LOAD_STORE_OP_LIST(LOAD_STORE), - #undef LOAD_STORE - PRFM = 0xC0800000 -}; - // Load/store post index. enum LoadStorePostIndex { LoadStorePostIndexFixed = 0x38000400, diff --git a/disas/libvixl/a64/decoder-a64.h b/disas/libvixl/a64/decoder-a64.h index 172594c..fd08d6c 100644 --- a/disas/libvixl/a64/decoder-a64.h +++ b/disas/libvixl/a64/decoder-a64.h @@ -108,7 +108,7 @@ class DecoderVisitor { } private: - VisitorConstness constness_; + const VisitorConstness constness_; }; diff --git a/disas/libvixl/a64/disasm-a64.cc b/disas/libvixl/a64/disasm-a64.cc index e4a74aa..f7bc246 100644 --- a/disas/libvixl/a64/disasm-a64.cc +++ b/disas/libvixl/a64/disasm-a64.cc @@ -34,6 +34,7 @@ Disassembler::Disassembler() { buffer_ = reinterpret_cast(malloc(buffer_size_)); buffer_pos_ = 0; own_buffer_ = true; + code_address_offset_ = 0; } @@ -42,6 +43,7 @@ Disassembler::Disassembler(char* text_buffer, int buffer_size) { buffer_ = text_buffer; buffer_pos_ = 0; own_buffer_ = false; + code_address_offset_ = 0; } @@ -739,9 +741,25 @@ void Disassembler::VisitMoveWideImmediate(const Instruction* instr) { // shift calculation. switch (instr->Mask(MoveWideImmediateMask)) { case MOVN_w: - case MOVN_x: mnemonic = "movn"; break; + case MOVN_x: + if ((instr->ImmMoveWide()) || (instr->ShiftMoveWide() == 0)) { + if ((instr->SixtyFourBits() == 0) && (instr->ImmMoveWide() == 0xffff)) { + mnemonic = "movn"; + } else { + mnemonic = "mov"; + form = "'Rd, 'IMoveNeg"; + } + } else { + mnemonic = "movn"; + } + break; case MOVZ_w: - case MOVZ_x: mnemonic = "movz"; break; + case MOVZ_x: + if ((instr->ImmMoveWide()) || (instr->ShiftMoveWide() == 0)) + mnemonic = "mov"; + else + mnemonic = "movz"; + break; case MOVK_w: case MOVK_x: mnemonic = "movk"; form = "'Rd, 'IMoveLSL"; break; default: VIXL_UNREACHABLE(); @@ -806,7 +824,7 @@ void Disassembler::VisitLoadStoreUnsignedOffset(const Instruction* instr) { case A##_unsigned: mnemonic = B; form = C ", ['Xns'ILU]"; break; LOAD_STORE_LIST(LS_UNSIGNEDOFFSET) #undef LS_UNSIGNEDOFFSET - case PRFM_unsigned: mnemonic = "prfm"; form = "'PrefOp, ['Xn'ILU]"; + case PRFM_unsigned: mnemonic = "prfm"; form = "'PrefOp, ['Xns'ILU]"; } Format(instr, mnemonic, form); } @@ -833,6 +851,7 @@ void Disassembler::VisitLoadStoreUnscaledOffset(const Instruction* instr) { const char *form_x = "'Xt, ['Xns'ILS]"; const char *form_s = "'St, ['Xns'ILS]"; const char *form_d = "'Dt, ['Xns'ILS]"; + const char *form_prefetch = "'PrefOp, ['Xns'ILS]"; switch (instr->Mask(LoadStoreUnscaledOffsetMask)) { case STURB_w: mnemonic = "sturb"; break; @@ -852,6 +871,7 @@ void Disassembler::VisitLoadStoreUnscaledOffset(const Instruction* instr) { case LDURSH_x: form = form_x; // Fall through. case LDURSH_w: mnemonic = "ldursh"; break; case LDURSW_x: mnemonic = "ldursw"; form = form_x; break; + case PRFUM: mnemonic = "prfum"; form = form_prefetch; break; default: form = "(LoadStoreUnscaledOffset)"; } Format(instr, mnemonic, form); @@ -872,6 +892,11 @@ void Disassembler::VisitLoadLiteral(const Instruction* instr) { form = "'Xt, 'ILLiteral 'LValue"; break; } + case PRFM_lit: { + mnemonic = "prfm"; + form = "'PrefOp, 'ILLiteral 'LValue"; + break; + } default: mnemonic = "unimplemented"; } Format(instr, mnemonic, form); @@ -1344,7 +1369,7 @@ void Disassembler::AppendPCRelativeOffsetToOutput(const Instruction* instr, void Disassembler::AppendAddressToOutput(const Instruction* instr, const void* addr) { USE(instr); - AppendToOutput("(addr %p)", addr); + AppendToOutput("(addr 0x%" PRIxPTR ")", reinterpret_cast(addr)); } @@ -1360,6 +1385,40 @@ void Disassembler::AppendDataAddressToOutput(const Instruction* instr, } +void Disassembler::AppendCodeRelativeAddressToOutput(const Instruction* instr, + const void* addr) { + USE(instr); + int64_t rel_addr = CodeRelativeAddress(addr); + if (rel_addr >= 0) { + AppendToOutput("(addr 0x%" PRIx64 ")", rel_addr); + } else { + AppendToOutput("(addr -0x%" PRIx64 ")", -rel_addr); + } +} + + +void Disassembler::AppendCodeRelativeCodeAddressToOutput( + const Instruction* instr, const void* addr) { + AppendCodeRelativeAddressToOutput(instr, addr); +} + + +void Disassembler::AppendCodeRelativeDataAddressToOutput( + const Instruction* instr, const void* addr) { + AppendCodeRelativeAddressToOutput(instr, addr); +} + + +void Disassembler::MapCodeAddress(int64_t base_address, + const Instruction* instr_address) { + set_code_address_offset( + base_address - reinterpret_cast(instr_address)); +} +int64_t Disassembler::CodeRelativeAddress(const void* addr) { + return reinterpret_cast(addr) + code_address_offset(); +} + + void Disassembler::Format(const Instruction* instr, const char* mnemonic, const char* format) { VIXL_ASSERT(mnemonic != NULL); @@ -1486,16 +1545,20 @@ int Disassembler::SubstituteImmediateField(const Instruction* instr, VIXL_ASSERT(format[0] == 'I'); switch (format[1]) { - case 'M': { // IMoveImm or IMoveLSL. - if (format[5] == 'I') { - uint64_t imm = instr->ImmMoveWide() << (16 * instr->ShiftMoveWide()); - AppendToOutput("#0x%" PRIx64, imm); - } else { - VIXL_ASSERT(format[5] == 'L'); + case 'M': { // IMoveImm, IMoveNeg or IMoveLSL. + if (format[5] == 'L') { AppendToOutput("#0x%" PRIx64, instr->ImmMoveWide()); if (instr->ShiftMoveWide() > 0) { AppendToOutput(", lsl #%" PRId64, 16 * instr->ShiftMoveWide()); } + } else { + VIXL_ASSERT((format[5] == 'I') || (format[5] == 'N')); + uint64_t imm = instr->ImmMoveWide() << (16 * instr->ShiftMoveWide()); + if (format[5] == 'N') + imm = ~imm; + if (!instr->SixtyFourBits()) + imm &= UINT64_C(0xffffffff); + AppendToOutput("#0x%" PRIx64, imm); } return 8; } @@ -1634,14 +1697,31 @@ int Disassembler::SubstituteLiteralField(const Instruction* instr, VIXL_ASSERT(strncmp(format, "LValue", 6) == 0); USE(format); + const void * address = instr->LiteralAddress(); switch (instr->Mask(LoadLiteralMask)) { case LDR_w_lit: case LDR_x_lit: case LDRSW_x_lit: case LDR_s_lit: case LDR_d_lit: - AppendDataAddressToOutput(instr, instr->LiteralAddress()); + AppendCodeRelativeDataAddressToOutput(instr, address); break; + case PRFM_lit: { + // Use the prefetch hint to decide how to print the address. + switch (instr->PrefetchHint()) { + case 0x0: // PLD: prefetch for load. + case 0x2: // PST: prepare for store. + AppendCodeRelativeDataAddressToOutput(instr, address); + break; + case 0x1: // PLI: preload instructions. + AppendCodeRelativeCodeAddressToOutput(instr, address); + break; + case 0x3: // Unallocated hint. + AppendCodeRelativeAddressToOutput(instr, address); + break; + } + break; + } default: VIXL_UNREACHABLE(); } @@ -1701,17 +1781,22 @@ int Disassembler::SubstitutePCRelAddressField(const Instruction* instr, (strcmp(format, "AddrPCRelPage") == 0)); // Used by `adrp`. int64_t offset = instr->ImmPCRel(); - const Instruction * base = instr; + // Compute the target address based on the effective address (after applying + // code_address_offset). This is required for correct behaviour of adrp. + const Instruction* base = instr + code_address_offset(); if (format[9] == 'P') { offset *= kPageSize; base = AlignDown(base, kPageSize); } + // Strip code_address_offset before printing, so we can use the + // semantically-correct AppendCodeRelativeAddressToOutput. + const void* target = + reinterpret_cast(base + offset - code_address_offset()); - const void* target = reinterpret_cast(base + offset); AppendPCRelativeOffsetToOutput(instr, offset); AppendToOutput(" "); - AppendAddressToOutput(instr, target); + AppendCodeRelativeAddressToOutput(instr, target); return 13; } @@ -1738,7 +1823,7 @@ int Disassembler::SubstituteBranchTargetField(const Instruction* instr, AppendPCRelativeOffsetToOutput(instr, offset); AppendToOutput(" "); - AppendCodeAddressToOutput(instr, target_address); + AppendCodeRelativeCodeAddressToOutput(instr, target_address); return 8; } @@ -1805,13 +1890,26 @@ int Disassembler::SubstitutePrefetchField(const Instruction* instr, VIXL_ASSERT(format[0] == 'P'); USE(format); - int prefetch_mode = instr->PrefetchMode(); - - const char* ls = (prefetch_mode & 0x10) ? "st" : "ld"; - int level = (prefetch_mode >> 1) + 1; - const char* ks = (prefetch_mode & 1) ? "strm" : "keep"; - - AppendToOutput("p%sl%d%s", ls, level, ks); + static const char* hints[] = {"ld", "li", "st"}; + static const char* stream_options[] = {"keep", "strm"}; + + unsigned hint = instr->PrefetchHint(); + unsigned target = instr->PrefetchTarget() + 1; + unsigned stream = instr->PrefetchStream(); + + if ((hint >= (sizeof(hints) / sizeof(hints[0]))) || (target > 3)) { + // Unallocated prefetch operations. + int prefetch_mode = instr->ImmPrefetchOperation(); + AppendToOutput("#0b%c%c%c%c%c", + (prefetch_mode & (1 << 4)) ? '1' : '0', + (prefetch_mode & (1 << 3)) ? '1' : '0', + (prefetch_mode & (1 << 2)) ? '1' : '0', + (prefetch_mode & (1 << 1)) ? '1' : '0', + (prefetch_mode & (1 << 0)) ? '1' : '0'); + } else { + VIXL_ASSERT(stream < (sizeof(stream_options) / sizeof(stream_options[0]))); + AppendToOutput("p%sl%d%s", hints[hint], target, stream_options[stream]); + } return 6; } diff --git a/disas/libvixl/a64/disasm-a64.h b/disas/libvixl/a64/disasm-a64.h index db04337..ddfe98b 100644 --- a/disas/libvixl/a64/disasm-a64.h +++ b/disas/libvixl/a64/disasm-a64.h @@ -43,7 +43,7 @@ class Disassembler: public DecoderVisitor { char* GetOutput(); // Declare all Visitor functions. - #define DECLARE(A) void Visit##A(const Instruction* instr); + #define DECLARE(A) virtual void Visit##A(const Instruction* instr); VISITOR_LIST(DECLARE) #undef DECLARE @@ -65,23 +65,45 @@ class Disassembler: public DecoderVisitor { // Prints an address, in the general case. It can be code or data. This is // used for example to print the target address of an ADR instruction. - virtual void AppendAddressToOutput(const Instruction* instr, - const void* addr); + virtual void AppendCodeRelativeAddressToOutput(const Instruction* instr, + const void* addr); // Prints the address of some code. // This is used for example to print the target address of a branch to an // immediate offset. // A sub-class can for example override this method to lookup the address and // print an appropriate name. - virtual void AppendCodeAddressToOutput(const Instruction* instr, - const void* addr); + virtual void AppendCodeRelativeCodeAddressToOutput(const Instruction* instr, + const void* addr); // Prints the address of some data. // This is used for example to print the source address of a load literal // instruction. + virtual void AppendCodeRelativeDataAddressToOutput(const Instruction* instr, + const void* addr); + + // Same as the above, but for addresses that are not relative to the code + // buffer. They are currently not used by VIXL. + virtual void AppendAddressToOutput(const Instruction* instr, + const void* addr); + virtual void AppendCodeAddressToOutput(const Instruction* instr, + const void* addr); virtual void AppendDataAddressToOutput(const Instruction* instr, const void* addr); + public: + // Get/Set the offset that should be added to code addresses when printing + // code-relative addresses in the AppendCodeRelativeAddressToOutput() + // helpers. + // Below is an example of how a branch immediate instruction in memory at + // address 0xb010200 would disassemble with different offsets. + // Base address | Disassembly + // 0x0 | 0xb010200: b #+0xcc (addr 0xb0102cc) + // 0x10000 | 0xb000200: b #+0xcc (addr 0xb0002cc) + // 0xb010200 | 0x0: b #+0xcc (addr 0xcc) + void MapCodeAddress(int64_t base_address, const Instruction* instr_address); + int64_t CodeRelativeAddress(const void* instr); + private: void Format( const Instruction* instr, const char* mnemonic, const char* format); @@ -101,32 +123,40 @@ class Disassembler: public DecoderVisitor { int SubstitutePrefetchField(const Instruction* instr, const char* format); int SubstituteBarrierField(const Instruction* instr, const char* format); - inline bool RdIsZROrSP(const Instruction* instr) const { + bool RdIsZROrSP(const Instruction* instr) const { return (instr->Rd() == kZeroRegCode); } - inline bool RnIsZROrSP(const Instruction* instr) const { + bool RnIsZROrSP(const Instruction* instr) const { return (instr->Rn() == kZeroRegCode); } - inline bool RmIsZROrSP(const Instruction* instr) const { + bool RmIsZROrSP(const Instruction* instr) const { return (instr->Rm() == kZeroRegCode); } - inline bool RaIsZROrSP(const Instruction* instr) const { + bool RaIsZROrSP(const Instruction* instr) const { return (instr->Ra() == kZeroRegCode); } bool IsMovzMovnImm(unsigned reg_size, uint64_t value); + int64_t code_address_offset() const { return code_address_offset_; } + protected: void ResetOutput(); void AppendToOutput(const char* string, ...) PRINTF_CHECK(2, 3); + void set_code_address_offset(int64_t code_address_offset) { + code_address_offset_ = code_address_offset; + } + char* buffer_; uint32_t buffer_pos_; uint32_t buffer_size_; bool own_buffer_; + + int64_t code_address_offset_; }; diff --git a/disas/libvixl/a64/instructions-a64.cc b/disas/libvixl/a64/instructions-a64.cc index 1f08c78..b091886 100644 --- a/disas/libvixl/a64/instructions-a64.cc +++ b/disas/libvixl/a64/instructions-a64.cc @@ -30,6 +30,20 @@ namespace vixl { +// Floating-point infinity values. +const float kFP32PositiveInfinity = rawbits_to_float(0x7f800000); +const float kFP32NegativeInfinity = rawbits_to_float(0xff800000); +const double kFP64PositiveInfinity = + rawbits_to_double(UINT64_C(0x7ff0000000000000)); +const double kFP64NegativeInfinity = + rawbits_to_double(UINT64_C(0xfff0000000000000)); + + +// The default NaN values (for FPCR.DN=1). +const double kFP64DefaultNaN = rawbits_to_double(UINT64_C(0x7ff8000000000000)); +const float kFP32DefaultNaN = rawbits_to_float(0x7fc00000); + + static uint64_t RotateRight(uint64_t value, unsigned int rotate, unsigned int width) { @@ -54,6 +68,55 @@ static uint64_t RepeatBitsAcrossReg(unsigned reg_size, } +bool Instruction::IsLoad() const { + if (Mask(LoadStoreAnyFMask) != LoadStoreAnyFixed) { + return false; + } + + if (Mask(LoadStorePairAnyFMask) == LoadStorePairAnyFixed) { + return Mask(LoadStorePairLBit) != 0; + } else { + LoadStoreOp op = static_cast(Mask(LoadStoreOpMask)); + switch (op) { + case LDRB_w: + case LDRH_w: + case LDR_w: + case LDR_x: + case LDRSB_w: + case LDRSB_x: + case LDRSH_w: + case LDRSH_x: + case LDRSW_x: + case LDR_s: + case LDR_d: return true; + default: return false; + } + } +} + + +bool Instruction::IsStore() const { + if (Mask(LoadStoreAnyFMask) != LoadStoreAnyFixed) { + return false; + } + + if (Mask(LoadStorePairAnyFMask) == LoadStorePairAnyFixed) { + return Mask(LoadStorePairLBit) == 0; + } else { + LoadStoreOp op = static_cast(Mask(LoadStoreOpMask)); + switch (op) { + case STRB_w: + case STRH_w: + case STR_w: + case STR_x: + case STR_s: + case STR_d: return true; + default: return false; + } + } +} + + // Logical immediates can't encode zero, so a return value of zero is used to // indicate a failure case. Specifically, where the constraints on imm_s are // not met. diff --git a/disas/libvixl/a64/instructions-a64.h b/disas/libvixl/a64/instructions-a64.h index 29f9722..f1d883c 100644 --- a/disas/libvixl/a64/instructions-a64.h +++ b/disas/libvixl/a64/instructions-a64.h @@ -96,6 +96,17 @@ const unsigned kDoubleExponentBits = 11; const unsigned kFloatMantissaBits = 23; const unsigned kFloatExponentBits = 8; +// Floating-point infinity values. +extern const float kFP32PositiveInfinity; +extern const float kFP32NegativeInfinity; +extern const double kFP64PositiveInfinity; +extern const double kFP64NegativeInfinity; + +// The default NaN values (for FPCR.DN=1). +extern const double kFP64DefaultNaN; +extern const float kFP32DefaultNaN; + + enum LSDataSize { LSByte = 0, LSHalfword = 1, @@ -140,33 +151,33 @@ enum Reg31Mode { class Instruction { public: - inline Instr InstructionBits() const { + Instr InstructionBits() const { return *(reinterpret_cast(this)); } - inline void SetInstructionBits(Instr new_instr) { + void SetInstructionBits(Instr new_instr) { *(reinterpret_cast(this)) = new_instr; } - inline int Bit(int pos) const { + int Bit(int pos) const { return (InstructionBits() >> pos) & 1; } - inline uint32_t Bits(int msb, int lsb) const { + uint32_t Bits(int msb, int lsb) const { return unsigned_bitextract_32(msb, lsb, InstructionBits()); } - inline int32_t SignedBits(int msb, int lsb) const { + int32_t SignedBits(int msb, int lsb) const { int32_t bits = *(reinterpret_cast(this)); return signed_bitextract_32(msb, lsb, bits); } - inline Instr Mask(uint32_t mask) const { + Instr Mask(uint32_t mask) const { return InstructionBits() & mask; } #define DEFINE_GETTER(Name, HighBit, LowBit, Func) \ - inline int64_t Name() const { return Func(HighBit, LowBit); } + int64_t Name() const { return Func(HighBit, LowBit); } INSTRUCTION_FIELDS_LIST(DEFINE_GETTER) #undef DEFINE_GETTER @@ -182,56 +193,64 @@ class Instruction { float ImmFP32() const; double ImmFP64() const; - inline LSDataSize SizeLSPair() const { + LSDataSize SizeLSPair() const { return CalcLSPairDataSize( static_cast(Mask(LoadStorePairMask))); } // Helpers. - inline bool IsCondBranchImm() const { + bool IsCondBranchImm() const { return Mask(ConditionalBranchFMask) == ConditionalBranchFixed; } - inline bool IsUncondBranchImm() const { + bool IsUncondBranchImm() const { return Mask(UnconditionalBranchFMask) == UnconditionalBranchFixed; } - inline bool IsCompareBranch() const { + bool IsCompareBranch() const { return Mask(CompareBranchFMask) == CompareBranchFixed; } - inline bool IsTestBranch() const { + bool IsTestBranch() const { return Mask(TestBranchFMask) == TestBranchFixed; } - inline bool IsPCRelAddressing() const { + bool IsPCRelAddressing() const { return Mask(PCRelAddressingFMask) == PCRelAddressingFixed; } - inline bool IsLogicalImmediate() const { + bool IsLogicalImmediate() const { return Mask(LogicalImmediateFMask) == LogicalImmediateFixed; } - inline bool IsAddSubImmediate() const { + bool IsAddSubImmediate() const { return Mask(AddSubImmediateFMask) == AddSubImmediateFixed; } - inline bool IsAddSubExtended() const { + bool IsAddSubExtended() const { return Mask(AddSubExtendedFMask) == AddSubExtendedFixed; } - inline bool IsLoadOrStore() const { + bool IsLoadOrStore() const { return Mask(LoadStoreAnyFMask) == LoadStoreAnyFixed; } - inline bool IsMovn() const { + bool IsLoad() const; + bool IsStore() const; + + bool IsLoadLiteral() const { + // This includes PRFM_lit. + return Mask(LoadLiteralFMask) == LoadLiteralFixed; + } + + bool IsMovn() const { return (Mask(MoveWideImmediateMask) == MOVN_x) || (Mask(MoveWideImmediateMask) == MOVN_w); } // Indicate whether Rd can be the stack pointer or the zero register. This // does not check that the instruction actually has an Rd field. - inline Reg31Mode RdMode() const { + Reg31Mode RdMode() const { // The following instructions use sp or wsp as Rd: // Add/sub (immediate) when not setting the flags. // Add/sub (extended) when not setting the flags. @@ -260,7 +279,7 @@ class Instruction { // Indicate whether Rn can be the stack pointer or the zero register. This // does not check that the instruction actually has an Rn field. - inline Reg31Mode RnMode() const { + Reg31Mode RnMode() const { // The following instructions use sp or wsp as Rn: // All loads and stores. // Add/sub (immediate). @@ -272,7 +291,7 @@ class Instruction { return Reg31IsZeroRegister; } - inline ImmBranchType BranchType() const { + ImmBranchType BranchType() const { if (IsCondBranchImm()) { return CondBranchType; } else if (IsUncondBranchImm()) { @@ -296,55 +315,66 @@ class Instruction { // Patch a literal load instruction to load from 'source'. void SetImmLLiteral(const Instruction* source); - inline uint8_t* LiteralAddress() const { - int offset = ImmLLiteral() << kLiteralEntrySizeLog2; - const uint8_t* address = reinterpret_cast(this) + offset; - // Note that the result is safely mutable only if the backing buffer is - // safely mutable. - return const_cast(address); + // Calculate the address of a literal referred to by a load-literal + // instruction, and return it as the specified type. + // + // The literal itself is safely mutable only if the backing buffer is safely + // mutable. + template + T LiteralAddress() const { + uint64_t base_raw = reinterpret_cast(this); + ptrdiff_t offset = ImmLLiteral() << kLiteralEntrySizeLog2; + uint64_t address_raw = base_raw + offset; + + // Cast the address using a C-style cast. A reinterpret_cast would be + // appropriate, but it can't cast one integral type to another. + T address = (T)(address_raw); + + // Assert that the address can be represented by the specified type. + VIXL_ASSERT((uint64_t)(address) == address_raw); + + return address; } - inline uint32_t Literal32() const { + uint32_t Literal32() const { uint32_t literal; - memcpy(&literal, LiteralAddress(), sizeof(literal)); - + memcpy(&literal, LiteralAddress(), sizeof(literal)); return literal; } - inline uint64_t Literal64() const { + uint64_t Literal64() const { uint64_t literal; - memcpy(&literal, LiteralAddress(), sizeof(literal)); - + memcpy(&literal, LiteralAddress(), sizeof(literal)); return literal; } - inline float LiteralFP32() const { + float LiteralFP32() const { return rawbits_to_float(Literal32()); } - inline double LiteralFP64() const { + double LiteralFP64() const { return rawbits_to_double(Literal64()); } - inline const Instruction* NextInstruction() const { + const Instruction* NextInstruction() const { return this + kInstructionSize; } - inline const Instruction* InstructionAtOffset(int64_t offset) const { + const Instruction* InstructionAtOffset(int64_t offset) const { VIXL_ASSERT(IsWordAligned(this + offset)); return this + offset; } - template static inline Instruction* Cast(T src) { + template static Instruction* Cast(T src) { return reinterpret_cast(src); } - template static inline const Instruction* CastConst(T src) { + template static const Instruction* CastConst(T src) { return reinterpret_cast(src); } private: - inline int ImmBranch() const; + int ImmBranch() const; void SetPCRelImmTarget(const Instruction* target); void SetBranchImmTarget(const Instruction* target); diff --git a/disas/libvixl/globals.h b/disas/libvixl/globals.h index e28dc66..0c24931 100644 --- a/disas/libvixl/globals.h +++ b/disas/libvixl/globals.h @@ -58,7 +58,7 @@ const int KBytes = 1024; const int MBytes = 1024 * KBytes; #define VIXL_ABORT() printf("in %s, line %i", __FILE__, __LINE__); abort() -#ifdef DEBUG +#ifdef VIXL_DEBUG #define VIXL_ASSERT(condition) assert(condition) #define VIXL_CHECK(condition) VIXL_ASSERT(condition) #define VIXL_UNIMPLEMENTED() printf("UNIMPLEMENTED\t"); VIXL_ABORT() diff --git a/disas/libvixl/utils.cc b/disas/libvixl/utils.cc index 21965d7..80b132a 100644 --- a/disas/libvixl/utils.cc +++ b/disas/libvixl/utils.cc @@ -135,4 +135,17 @@ bool IsPowerOf2(int64_t value) { return (value != 0) && ((value & (value - 1)) == 0); } + +unsigned CountClearHalfWords(uint64_t imm, unsigned reg_size) { + VIXL_ASSERT((reg_size % 8) == 0); + int count = 0; + for (unsigned i = 0; i < (reg_size / 16); i++) { + if ((imm & 0xffff) == 0) { + count++; + } + imm >>= 16; + } + return count; +} + } // namespace vixl diff --git a/disas/libvixl/utils.h b/disas/libvixl/utils.h index 1540c30..b440626 100644 --- a/disas/libvixl/utils.h +++ b/disas/libvixl/utils.h @@ -166,6 +166,8 @@ int CountSetBits(uint64_t value, int width); uint64_t LowestSetBit(uint64_t value); bool IsPowerOf2(int64_t value); +unsigned CountClearHalfWords(uint64_t imm, unsigned reg_size); + // Pointer alignment // TODO: rename/refactor to make it specific to instructions. template @@ -174,14 +176,14 @@ bool IsWordAligned(T pointer) { return ((intptr_t)(pointer) & 3) == 0; } -// Increment a pointer until it has the specified alignment. +// Increment a pointer (up to 64 bits) until it has the specified alignment. template T AlignUp(T pointer, size_t alignment) { // Use C-style casts to get static_cast behaviour for integral types (T), and // reinterpret_cast behaviour for other types. - uintptr_t pointer_raw = (uintptr_t)pointer; - VIXL_STATIC_ASSERT(sizeof(pointer) == sizeof(pointer_raw)); + uint64_t pointer_raw = (uint64_t)pointer; + VIXL_STATIC_ASSERT(sizeof(pointer) <= sizeof(pointer_raw)); size_t align_step = (alignment - pointer_raw) % alignment; VIXL_ASSERT((pointer_raw + align_step) % alignment == 0); @@ -189,14 +191,14 @@ T AlignUp(T pointer, size_t alignment) { return (T)(pointer_raw + align_step); } -// Decrement a pointer until it has the specified alignment. +// Decrement a pointer (up to 64 bits) until it has the specified alignment. template T AlignDown(T pointer, size_t alignment) { // Use C-style casts to get static_cast behaviour for integral types (T), and // reinterpret_cast behaviour for other types. - uintptr_t pointer_raw = (uintptr_t)pointer; - VIXL_STATIC_ASSERT(sizeof(pointer) == sizeof(pointer_raw)); + uint64_t pointer_raw = (uint64_t)pointer; + VIXL_STATIC_ASSERT(sizeof(pointer) <= sizeof(pointer_raw)); size_t align_step = pointer_raw % alignment; VIXL_ASSERT((pointer_raw - align_step) % alignment == 0); diff --git a/disas/microblaze.c b/disas/microblaze.c index ec91af3..c14ab89 100644 --- a/disas/microblaze.c +++ b/disas/microblaze.c @@ -275,7 +275,7 @@ enum microblaze_instr_type { #define MAX_OPCODES 280 -struct op_code_struct { +static struct op_code_struct { const char *name; short inst_type; /* registers and immediate values involved */ short inst_offset_type; /* immediate vals offset from PC? (= 1 for branches) */ @@ -567,10 +567,9 @@ struct op_code_struct { }; /* prefix for register names */ -char register_prefix[] = "r"; -char special_register_prefix[] = "spr"; -char fsl_register_prefix[] = "rfsl"; -char pvr_register_prefix[] = "rpvr"; +static const char register_prefix[] = "r"; +static const char fsl_register_prefix[] = "rfsl"; +static const char pvr_register_prefix[] = "rpvr"; /* #defines for valid immediate range */ @@ -738,7 +737,9 @@ get_field_special (long instr, struct op_code_struct * op) default : { if ( ((((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask) & 0xE000) == REG_PVR_MASK) { - sprintf(tmpstr, "%spvr%d", register_prefix, (unsigned short)(((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask) ^ REG_PVR_MASK); + sprintf(tmpstr, "%s%u", pvr_register_prefix, + (unsigned short)(((instr & IMM_MASK) >> IMM_LOW) ^ + op->immval_mask) ^ REG_PVR_MASK); return(strdup(tmpstr)); } else { strcpy(spr, "pc"); diff --git a/disas/mips.c b/disas/mips.c index 2614c52..1afe0c5 100644 --- a/disas/mips.c +++ b/disas/mips.c @@ -3511,6 +3511,7 @@ struct mips_cp0sel_name const char * const name; }; +#if 0 /* The mips16 registers. */ static const unsigned int mips16_to_32_reg_map[] = { @@ -3518,7 +3519,7 @@ static const unsigned int mips16_to_32_reg_map[] = }; #define mips16_reg_names(rn) mips_gpr_names[mips16_to_32_reg_map[rn]] - +#endif static const char * const mips_gpr_names_numeric[32] = { @@ -3801,13 +3802,6 @@ static const char * const mips_hwr_names_mips3264r2[32] = "$24", "$25", "$26", "$27", "$28", "$29", "$30", "$31" }; -static const char * const mips_msa_control_names_numeric[32] = { - "$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" -}; - static const char * const mips_msa_control_names_mips3264r2[32] = { "MSAIR", "MSACSR", "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", diff --git a/disas/s390.c b/disas/s390.c index 25499ba..974460c 100644 --- a/disas/s390.c +++ b/disas/s390.c @@ -106,10 +106,6 @@ struct s390_opcode static const struct s390_opcode s390_opcodes[]; static const int s390_num_opcodes; -/* A opcode format table for the .insn pseudo mnemonic. */ -static const struct s390_opcode s390_opformats[]; -static const int s390_num_opformats; - /* Values defined for the flags field of a struct powerpc_opcode. */ /* The operands table is an array of struct s390_operand. */ @@ -844,37 +840,6 @@ static const struct s390_operand s390_operands[] = #define MASK_SIY_DRI { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } /* QEMU-END */ -/* The opcode formats table (blueprints for .insn pseudo mnemonic). */ - -static const struct s390_opcode s390_opformats[] = - { - { "e", OP8(0x00LL), MASK_E, INSTR_E, 3, 0 }, - { "ri", OP8(0x00LL), MASK_RI_RI, INSTR_RI_RI, 3, 0 }, - { "rie", OP8(0x00LL), MASK_RIE_RRP, INSTR_RIE_RRP, 3, 0 }, - { "ril", OP8(0x00LL), MASK_RIL_RP, INSTR_RIL_RP, 3, 0 }, - { "rilu", OP8(0x00LL), MASK_RIL_RU, INSTR_RIL_RU, 3, 0 }, - { "rr", OP8(0x00LL), MASK_RR_RR, INSTR_RR_RR, 3, 0 }, - { "rre", OP8(0x00LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0 }, - { "rrf", OP8(0x00LL), MASK_RRF_RURR, INSTR_RRF_RURR, 3, 0 }, - { "rs", OP8(0x00LL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0 }, - { "rse", OP8(0x00LL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 3, 0 }, - { "rsi", OP8(0x00LL), MASK_RSI_RRP, INSTR_RSI_RRP, 3, 0 }, - { "rsy", OP8(0x00LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 3, 3 }, - { "rx", OP8(0x00LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0 }, - { "rxe", OP8(0x00LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 3, 0 }, - { "rxf", OP8(0x00LL), MASK_RXF_RRRDR, INSTR_RXF_RRRDR,3, 0 }, - { "rxy", OP8(0x00LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 3, 3 }, - { "s", OP8(0x00LL), MASK_S_RD, INSTR_S_RD, 3, 0 }, - { "si", OP8(0x00LL), MASK_SI_URD, INSTR_SI_URD, 3, 0 }, - { "siy", OP8(0x00LL), MASK_SIY_URD, INSTR_SIY_URD, 3, 3 }, - { "ss", OP8(0x00LL), MASK_SS_RRRDRD, INSTR_SS_RRRDRD,3, 0 }, - { "sse", OP8(0x00LL), MASK_SSE_RDRD, INSTR_SSE_RDRD, 3, 0 }, - { "ssf", OP8(0x00LL), MASK_SSF_RRDRD, INSTR_SSF_RRDRD,3, 0 }, -}; - -static const int s390_num_opformats = - sizeof (s390_opformats) / sizeof (s390_opformats[0]); - /* include "s390-opc.tab" generated from opcodes/s390-opc.txt rev 1.17 */ /* The opcode table. This file was generated by s390-mkopc. diff --git a/disas/sh4.c b/disas/sh4.c index f6cadd5..020f5eb 100644 --- a/disas/sh4.c +++ b/disas/sh4.c @@ -332,7 +332,7 @@ typedef struct #ifdef DEFINE_TABLE -const sh_opcode_info sh_table[] = +static const sh_opcode_info sh_table[] = { /* 0111nnnni8*1.... add #, */{"add",{A_IMM,A_REG_N},{HEX_7,REG_N,IMM0_8}, arch_sh1_up}, diff --git a/disas/sparc.c b/disas/sparc.c index 8e755d1..f4e3565 100644 --- a/disas/sparc.c +++ b/disas/sparc.c @@ -80,19 +80,6 @@ typedef struct sparc_opcode_arch short supported; } sparc_opcode_arch; -static const struct sparc_opcode_arch sparc_opcode_archs[]; - -/* Return the bitmask of supported architectures for ARCH. */ -#define SPARC_OPCODE_SUPPORTED(ARCH) (sparc_opcode_archs[ARCH].supported) - -/* Non-zero if ARCH1 conflicts with ARCH2. - IE: ARCH1 as a supported bit set that ARCH2 doesn't, and vice versa. */ -#define SPARC_OPCODE_CONFLICT_P(ARCH1, ARCH2) \ - (((SPARC_OPCODE_SUPPORTED (ARCH1) & SPARC_OPCODE_SUPPORTED (ARCH2)) \ - != SPARC_OPCODE_SUPPORTED (ARCH1)) \ - && ((SPARC_OPCODE_SUPPORTED (ARCH1) & SPARC_OPCODE_SUPPORTED (ARCH2)) \ - != SPARC_OPCODE_SUPPORTED (ARCH2))) - /* Structure of an opcode table entry. */ typedef struct sparc_opcode @@ -301,25 +288,6 @@ static const char *sparc_decode_sparclet_cpreg (int); otherwise. */ #define v9notv9a (MASK_V9) -/* Table of opcode architectures. - The order is defined in opcode/sparc.h. */ - -static const struct sparc_opcode_arch sparc_opcode_archs[] = -{ - { "v6", MASK_V6 }, - { "v7", MASK_V6 | MASK_V7 }, - { "v8", MASK_V6 | MASK_V7 | MASK_V8 }, - { "sparclet", MASK_V6 | MASK_V7 | MASK_V8 | MASK_SPARCLET }, - { "sparclite", MASK_V6 | MASK_V7 | MASK_V8 | MASK_SPARCLITE }, - /* ??? Don't some v8 privileged insns conflict with v9? */ - { "v9", MASK_V6 | MASK_V7 | MASK_V8 | MASK_V9 }, - /* v9 with ultrasparc additions */ - { "v9a", MASK_V6 | MASK_V7 | MASK_V8 | MASK_V9 | MASK_V9A }, - /* v9 with cheetah additions */ - { "v9b", MASK_V6 | MASK_V7 | MASK_V8 | MASK_V9 | MASK_V9A | MASK_V9B }, - { NULL, 0 } -}; - /* Branch condition field. */ #define COND(x) (((x) & 0xf) << 25) diff --git a/docs/memory-hotplug.txt b/docs/memory-hotplug.txt new file mode 100644 index 0000000..f70571d --- /dev/null +++ b/docs/memory-hotplug.txt @@ -0,0 +1,76 @@ +QEMU memory hotplug +=================== + +This document explains how to use the memory hotplug feature in QEMU, +which is present since v2.1.0. + +Please, note that memory hotunplug is not supported yet. This means +that you're able to add memory, but you're not able to remove it. +Also, proper guest support is required for memory hotplug to work. + +Basic RAM hotplug +----------------- + +In order to be able to hotplug memory, QEMU has to be told how many +hotpluggable memory slots to create and what is the maximum amount of +memory the guest can grow. This is done at startup time by means of +the -m command-line option, which has the following format: + + -m [size=]megs[,slots=n,maxmem=size] + +Where, + + - "megs" is the startup RAM. It is the RAM the guest will boot with + - "slots" is the number of hotpluggable memory slots + - "maxmem" is the maximum RAM size the guest can have + +For example, the following command-line: + + qemu [...] 1G,slots=3,maxmem=4G + +Creates a guest with 1GB of memory and three hotpluggable memory slots. +The hotpluggable memory slots are empty when the guest is booted, so all +memory the guest will see after boot is 1GB. The maximum memory the +guest can reach is 4GB. This means that three additional gigabytes can be +hotplugged by using any combination of the available memory slots. + +Two monitor commands are used to hotplug memory: + + - "object_add": creates a memory backend object + - "device_add": creates a front-end pc-dimm device and inserts it + into the first empty slot + +For example, the following commands add another 1GB to the guest +discussed earlier: + + (qemu) object_add memory-backend-ram,id=mem1,size=1G + (qemu) device_add pc-dimm,id=dimm1,memdev=mem1 + +Using the file backend +---------------------- + +Besides basic RAM hotplug, QEMU also supports using files as a memory +backend. This is useful for using hugetlbfs in Linux, which provides +access to bigger page sizes. + +For example, assuming that the host has 1GB hugepages available in +the /mnt/hugepages-1GB directory, a 1GB hugepage could be hotplugged +into the guest from the previous section with the following commands: + + (qemu) object_add memory-backend-file,id=mem1,size=1G,mem-path=/mnt/hugepages-1GB + (qemu) device_add pc-dimm,id=dimm1,memdev=mem1 + +It's also possible to start a guest with memory cold-plugged into the +hotpluggable memory slots. This might seem counterintuitive at first, +but this allows for a lot of flexibility when using the file backend. + +In the following command-line example, a 8GB guest is created where 6GB +comes from regular RAM, 1GB is a 1GB hugepage page and 256MB is from +2MB pages. Also, the guest has additional memory slots to hotplug more +2GB if needed: + + qemu [...] -m 6GB,slots=4,maxmem=10G \ + -object memory-backend-file,id=mem1,size=1G,mem-path=/mnt/hugepages-1G \ + -device pc-dimm,id=dimm1,memdev=mem1 \ + -object memory-backend-file,id=mem2,size=256M,mem-path=/mnt/hugepages-2MB \ + -device pc-dimm,id=dimm2,memdev=mem2 diff --git a/docs/memory.txt b/docs/memory.txt index b12f1f0..2ceb348 100644 --- a/docs/memory.txt +++ b/docs/memory.txt @@ -73,17 +73,66 @@ stability. Region lifecycle ---------------- -A region is created by one of the constructor functions (memory_region_init*()) -and attached to an object. It is then destroyed by object_unparent() or simply -when the parent object dies. +A region is created by one of the memory_region_init*() functions and +attached to an object, which acts as its owner or parent. QEMU ensures +that the owner object remains alive as long as the region is visible to +the guest, or as long as the region is in use by a virtual CPU or another +device. For example, the owner object will not die between an +address_space_map operation and the corresponding address_space_unmap. -In between, a region can be added to an address space -by using memory_region_add_subregion() and removed using -memory_region_del_subregion(). Destroying the region implicitly -removes the region from the address space. +After creation, a region can be added to an address space or a +container with memory_region_add_subregion(), and removed using +memory_region_del_subregion(). + +Various region attributes (read-only, dirty logging, coalesced mmio, +ioeventfd) can be changed during the region lifecycle. They take effect +as soon as the region is made visible. This can be immediately, later, +or never. + +Destruction of a memory region happens automatically when the owner +object dies. + +If however the memory region is part of a dynamically allocated data +structure, you should call object_unparent() to destroy the memory region +before the data structure is freed. For an example see VFIOMSIXInfo +and VFIOQuirk in hw/vfio/pci.c. + +You must not destroy a memory region as long as it may be in use by a +device or CPU. In order to do this, as a general rule do not create or +destroy memory regions dynamically during a device's lifetime, and only +call object_unparent() in the memory region owner's instance_finalize +callback. The dynamically allocated data structure that contains the +memory region then should obviously be freed in the instance_finalize +callback as well. + +If you break this rule, the following situation can happen: + +- the memory region's owner had a reference taken via memory_region_ref + (for example by address_space_map) + +- the region is unparented, and has no owner anymore + +- when address_space_unmap is called, the reference to the memory region's + owner is leaked. + + +There is an exception to the above rule: it is okay to call +object_unparent at any time for an alias or a container region. It is +therefore also okay to create or destroy alias and container regions +dynamically during a device's lifetime. + +This exceptional usage is valid because aliases and containers only help +QEMU building the guest's memory map; they are never accessed directly. +memory_region_ref and memory_region_unref are never called on aliases +or containers, and the above situation then cannot happen. Exploiting +this exception is rarely necessary, and therefore it is discouraged, +but nevertheless it is used in a few places. + +For regions that "have no owner" (NULL is passed at creation time), the +machine object is actually used as the owner. Since instance_finalize is +never called for the machine object, you must never call object_unparent +on regions that have no owner, unless they are aliases or containers. -Region attributes may be changed at any point; they take effect once -the region becomes exposed to the guest. Overlapping regions and priority -------------------------------- @@ -215,13 +264,6 @@ BAR containing MMIO registers is mapped after it. Note that if the guest maps a BAR outside the PCI hole, it would not be visible as the pci-hole alias clips it to a 0.5GB range. -Attributes ----------- - -Various region attributes (read-only, dirty logging, coalesced mmio, ioeventfd) -can be changed during the region lifecycle. They take effect once the region -is made visible (which can be immediately, later, or never). - MMIO Operations --------------- diff --git a/docs/multiseat.txt b/docs/multiseat.txt index 67151e0..b963665 100644 --- a/docs/multiseat.txt +++ b/docs/multiseat.txt @@ -7,7 +7,7 @@ host side First you must compile qemu with a user interface supporting multihead/multiseat and input event routing. Right now this -list includes sdl2 and gtk (both 2+3): +list includes sdl2, gtk (both 2+3) and vnc: ./configure --enable-sdl --with-sdlabi=2.0 @@ -16,16 +16,16 @@ or ./configure --enable-gtk -Next put together the qemu command line: +Next put together the qemu command line (sdk/gtk): qemu -enable-kvm -usb $memory $disk $whatever \ -display [ sdl | gtk ] \ -vga std \ -device usb-tablet -That is it for the first head, which will use the standard vga, the +That is it for the first seat, which will use the standard vga, the standard ps/2 keyboard (implicitly there) and the usb-tablet. Now the -additional switches for the second head: +additional switches for the second seat: -device pci-bridge,addr=12.0,chassis_nr=2,id=head.2 \ -device secondary-vga,bus=head.2,addr=02.0,id=video.2 \ @@ -47,6 +47,16 @@ in a separate tab. You can either simply switch tabs to switch heads, or use the "View / Detach tab" menu item to move one of the displays to its own window so you can see both display devices side-by-side. +For vnc some additional configuration on the command line is needed. +We'll create two vnc server instances, and bind the second one to the +second seat, simliar to input devices: + + -display vnc=:1,id=primary \ + -display vnc=:2,id=secondary,display=video.2 + +Connecting to vnc display :1 gives you access to the first seat, and +likewise connecting to vnc display :2 shows the second seat. + Note on spice: Spice handles multihead just fine. But it can't do multiseat. For tablet events the event source is sent to the spice agent. But qemu can't figure it, so it can't do input routing. diff --git a/docs/rcu.txt b/docs/rcu.txt new file mode 100644 index 0000000..21ecb81 --- /dev/null +++ b/docs/rcu.txt @@ -0,0 +1,390 @@ +Using RCU (Read-Copy-Update) for synchronization +================================================ + +Read-copy update (RCU) is a synchronization mechanism that is used to +protect read-mostly data structures. RCU is very efficient and scalable +on the read side (it is wait-free), and thus can make the read paths +extremely fast. + +RCU supports concurrency between a single writer and multiple readers, +thus it is not used alone. Typically, the write-side will use a lock to +serialize multiple updates, but other approaches are possible (e.g., +restricting updates to a single task). In QEMU, when a lock is used, +this will often be the "iothread mutex", also known as the "big QEMU +lock" (BQL). Also, restricting updates to a single task is done in +QEMU using the "bottom half" API. + +RCU is fundamentally a "wait-to-finish" mechanism. The read side marks +sections of code with "critical sections", and the update side will wait +for the execution of all *currently running* critical sections before +proceeding, or before asynchronously executing a callback. + +The key point here is that only the currently running critical sections +are waited for; critical sections that are started _after_ the beginning +of the wait do not extend the wait, despite running concurrently with +the updater. This is the reason why RCU is more scalable than, +for example, reader-writer locks. It is so much more scalable that +the system will have a single instance of the RCU mechanism; a single +mechanism can be used for an arbitrary number of "things", without +having to worry about things such as contention or deadlocks. + +How is this possible? The basic idea is to split updates in two phases, +"removal" and "reclamation". During removal, we ensure that subsequent +readers will not be able to get a reference to the old data. After +removal has completed, a critical section will not be able to access +the old data. Therefore, critical sections that begin after removal +do not matter; as soon as all previous critical sections have finished, +there cannot be any readers who hold references to the data structure, +and these can now be safely reclaimed (e.g., freed or unref'ed). + +Here is a picutre: + + thread 1 thread 2 thread 3 + ------------------- ------------------------ ------------------- + enter RCU crit.sec. + | finish removal phase + | begin wait + | | enter RCU crit.sec. + exit RCU crit.sec | | + complete wait | + begin reclamation phase | + exit RCU crit.sec. + + +Note how thread 3 is still executing its critical section when thread 2 +starts reclaiming data. This is possible, because the old version of the +data structure was not accessible at the time thread 3 began executing +that critical section. + + +RCU API +======= + +The core RCU API is small: + + void rcu_read_lock(void); + + Used by a reader to inform the reclaimer that the reader is + entering an RCU read-side critical section. + + void rcu_read_unlock(void); + + Used by a reader to inform the reclaimer that the reader is + exiting an RCU read-side critical section. Note that RCU + read-side critical sections may be nested and/or overlapping. + + void synchronize_rcu(void); + + Blocks until all pre-existing RCU read-side critical sections + on all threads have completed. This marks the end of the removal + phase and the beginning of reclamation phase. + + Note that it would be valid for another update to come while + synchronize_rcu is running. Because of this, it is better that + the updater releases any locks it may hold before calling + synchronize_rcu. If this is not possible (for example, because + the updater is protected by the BQL), you can use call_rcu. + + void call_rcu1(struct rcu_head * head, + void (*func)(struct rcu_head *head)); + + This function invokes func(head) after all pre-existing RCU + read-side critical sections on all threads have completed. This + marks the end of the removal phase, with func taking care + asynchronously of the reclamation phase. + + The foo struct needs to have an rcu_head structure added, + perhaps as follows: + + struct foo { + struct rcu_head rcu; + int a; + char b; + long c; + }; + + so that the reclaimer function can fetch the struct foo address + and free it: + + call_rcu1(&foo.rcu, foo_reclaim); + + void foo_reclaim(struct rcu_head *rp) + { + struct foo *fp = container_of(rp, struct foo, rcu); + g_free(fp); + } + + For the common case where the rcu_head member is the first of the + struct, you can use the following macro. + + void call_rcu(T *p, + void (*func)(T *p), + field-name); + void g_free_rcu(T *p, + field-name); + + call_rcu1 is typically used through these macro, in the common case + where the "struct rcu_head" is the first field in the struct. If + the callback function is g_free, in particular, g_free_rcu can be + used. In the above case, one could have written simply: + + g_free_rcu(foo_reclaim, rcu); + + typeof(*p) atomic_rcu_read(p); + + atomic_rcu_read() is similar to atomic_mb_read(), but it makes + some assumptions on the code that calls it. This allows a more + optimized implementation. + + atomic_rcu_read assumes that whenever a single RCU critical + section reads multiple shared data, these reads are either + data-dependent or need no ordering. This is almost always the + case when using RCU, because read-side critical sections typically + navigate one or more pointers (the pointers that are changed on + every update) until reaching a data structure of interest, + and then read from there. + + RCU read-side critical sections must use atomic_rcu_read() to + read data, unless concurrent writes are presented by another + synchronization mechanism. + + Furthermore, RCU read-side critical sections should traverse the + data structure in a single direction, opposite to the direction + in which the updater initializes it. + + void atomic_rcu_set(p, typeof(*p) v); + + atomic_rcu_set() is also similar to atomic_mb_set(), and it also + makes assumptions on the code that calls it in order to allow a more + optimized implementation. + + In particular, atomic_rcu_set() suffices for synchronization + with readers, if the updater never mutates a field within a + data item that is already accessible to readers. This is the + case when initializing a new copy of the RCU-protected data + structure; just ensure that initialization of *p is carried out + before atomic_rcu_set() makes the data item visible to readers. + If this rule is observed, writes will happen in the opposite + order as reads in the RCU read-side critical sections (or if + there is just one update), and there will be no need for other + synchronization mechanism to coordinate the accesses. + +The following APIs must be used before RCU is used in a thread: + + void rcu_register_thread(void); + + Mark a thread as taking part in the RCU mechanism. Such a thread + will have to report quiescent points regularly, either manually + or through the QemuCond/QemuSemaphore/QemuEvent APIs. + + void rcu_unregister_thread(void); + + Mark a thread as not taking part anymore in the RCU mechanism. + It is not a problem if such a thread reports quiescent points, + either manually or by using the QemuCond/QemuSemaphore/QemuEvent + APIs. + +Note that these APIs are relatively heavyweight, and should _not_ be +nested. + + +DIFFERENCES WITH LINUX +====================== + +- Waiting on a mutex is possible, though discouraged, within an RCU critical + section. This is because spinlocks are rarely (if ever) used in userspace + programming; not allowing this would prevent upgrading an RCU read-side + critical section to become an updater. + +- atomic_rcu_read and atomic_rcu_set replace rcu_dereference and + rcu_assign_pointer. They take a _pointer_ to the variable being accessed. + +- call_rcu is a macro that has an extra argument (the name of the first + field in the struct, which must be a struct rcu_head), and expects the + type of the callback's argument to be the type of the first argument. + call_rcu1 is the same as Linux's call_rcu. + + +RCU PATTERNS +============ + +Many patterns using read-writer locks translate directly to RCU, with +the advantages of higher scalability and deadlock immunity. + +In general, RCU can be used whenever it is possible to create a new +"version" of a data structure every time the updater runs. This may +sound like a very strict restriction, however: + +- the updater does not mean "everything that writes to a data structure", + but rather "everything that involves a reclamation step". See the + array example below + +- in some cases, creating a new version of a data structure may actually + be very cheap. For example, modifying the "next" pointer of a singly + linked list is effectively creating a new version of the list. + +Here are some frequently-used RCU idioms that are worth noting. + + +RCU list processing +------------------- + +TBD (not yet used in QEMU) + + +RCU reference counting +---------------------- + +Because grace periods are not allowed to complete while there is an RCU +read-side critical section in progress, the RCU read-side primitives +may be used as a restricted reference-counting mechanism. For example, +consider the following code fragment: + + rcu_read_lock(); + p = atomic_rcu_read(&foo); + /* do something with p. */ + rcu_read_unlock(); + +The RCU read-side critical section ensures that the value of "p" remains +valid until after the rcu_read_unlock(). In some sense, it is acquiring +a reference to p that is later released when the critical section ends. +The write side looks simply like this (with appropriate locking): + + qemu_mutex_lock(&foo_mutex); + old = foo; + atomic_rcu_set(&foo, new); + qemu_mutex_unlock(&foo_mutex); + synchronize_rcu(); + free(old); + +If the processing cannot be done purely within the critical section, it +is possible to combine this idiom with a "real" reference count: + + rcu_read_lock(); + p = atomic_rcu_read(&foo); + foo_ref(p); + rcu_read_unlock(); + /* do something with p. */ + foo_unref(p); + +The write side can be like this: + + qemu_mutex_lock(&foo_mutex); + old = foo; + atomic_rcu_set(&foo, new); + qemu_mutex_unlock(&foo_mutex); + synchronize_rcu(); + foo_unref(old); + +or with call_rcu: + + qemu_mutex_lock(&foo_mutex); + old = foo; + atomic_rcu_set(&foo, new); + qemu_mutex_unlock(&foo_mutex); + call_rcu(foo_unref, old, rcu); + +In both cases, the write side only performs removal. Reclamation +happens when the last reference to a "foo" object is dropped. +Using synchronize_rcu() is undesirably expensive, because the +last reference may be dropped on the read side. Hence you can +use call_rcu() instead: + + foo_unref(struct foo *p) { + if (atomic_fetch_dec(&p->refcount) == 1) { + call_rcu(foo_destroy, p, rcu); + } + } + + +Note that the same idioms would be possible with reader/writer +locks: + + read_lock(&foo_rwlock); write_mutex_lock(&foo_rwlock); + p = foo; p = foo; + /* do something with p. */ foo = new; + read_unlock(&foo_rwlock); free(p); + write_mutex_unlock(&foo_rwlock); + free(p); + + ------------------------------------------------------------------ + + read_lock(&foo_rwlock); write_mutex_lock(&foo_rwlock); + p = foo; old = foo; + foo_ref(p); foo = new; + read_unlock(&foo_rwlock); foo_unref(old); + /* do something with p. */ write_mutex_unlock(&foo_rwlock); + read_lock(&foo_rwlock); + foo_unref(p); + read_unlock(&foo_rwlock); + +foo_unref could use a mechanism such as bottom halves to move deallocation +out of the write-side critical section. + + +RCU resizable arrays +-------------------- + +Resizable arrays can be used with RCU. The expensive RCU synchronization +(or call_rcu) only needs to take place when the array is resized. +The two items to take care of are: + +- ensuring that the old version of the array is available between removal + and reclamation; + +- avoiding mismatches in the read side between the array data and the + array size. + +The first problem is avoided simply by not using realloc. Instead, +each resize will allocate a new array and copy the old data into it. +The second problem would arise if the size and the data pointers were +two members of a larger struct: + + struct mystuff { + ... + int data_size; + int data_alloc; + T *data; + ... + }; + +Instead, we store the size of the array with the array itself: + + struct arr { + int size; + int alloc; + T data[]; + }; + struct arr *global_array; + + read side: + rcu_read_lock(); + struct arr *array = atomic_rcu_read(&global_array); + x = i < array->size ? array->data[i] : -1; + rcu_read_unlock(); + return x; + + write side (running under a lock): + if (global_array->size == global_array->alloc) { + /* Creating a new version. */ + new_array = g_malloc(sizeof(struct arr) + + global_array->alloc * 2 * sizeof(T)); + new_array->size = global_array->size; + new_array->alloc = global_array->alloc * 2; + memcpy(new_array->data, global_array->data, + global_array->alloc * sizeof(T)); + + /* Removal phase. */ + old_array = global_array; + atomic_rcu_set(&new_array->data, new_array); + synchronize_rcu(); + + /* Reclamation phase. */ + free(old_array); + } + + +SOURCES +======= + +* Documentation/RCU/ from the Linux kernel diff --git a/docs/specs/edu.txt b/docs/specs/edu.txt new file mode 100644 index 0000000..7f81467 --- /dev/null +++ b/docs/specs/edu.txt @@ -0,0 +1,110 @@ + +EDU device +========== + +Copyright (c) 2014-2015 Jiri Slaby + +This document is licensed under the GPLv2 (or later). + +This is an educational device for writing (kernel) drivers. Its original +intention was to support the Linux kernel lectures taught at the Masaryk +University. Students are given this virtual device and are expected to write a +driver with I/Os, IRQs, DMAs and such. + +The devices behaves very similar to the PCI bridge present in the COMBO6 cards +developed under the Liberouter wings. Both PCI device ID and PCI space is +inherited from that device. + +Command line switches: + -device edu[,dma_mask=mask] + + dma_mask makes the virtual device work with DMA addresses with the given + mask. For educational purposes, the device supports only 28 bits (256 MiB) + by default. Students shall set dma_mask for the device in the OS driver + properly. + +PCI specs +--------- + +PCI ID: 1234:11e8 + +PCI Region 0: + I/O memory, 1 MB in size. Users are supposed to communicate with the card + through this memory. + +MMIO area spec +-------------- + +Only size == 4 accesses are allowed for addresses < 0x80. size == 4 or +size == 8 for the rest. + +0x00 (RO) : identification (0xRRrr00edu) + RR -- major version + rr -- minor version + +0x04 (RW) : card liveness check + It is a simple value inversion (~ C operator). + +0x08 (RW) : factorial computation + The stored value is taken and factorial of it is put back here. + This happens only after factorial bit in the status register (0x20 + below) is cleared. + +0x20 (RW) : status register, bitwise OR + 0x01 -- computing factorial (RO) + 0x80 -- raise interrupt 0x01 after finishing factorial computation + +0x24 (RO) : interrupt status register + It contains values which raised the interrupt (see interrupt raise + register below). + +0x60 (WO) : interrupt raise register + Raise an interrupt. The value will be put to the interrupt status + register (using bitwise OR). + +0x64 (WO) : interrupt acknowledge register + Clear an interrupt. The value will be cleared from the interrupt + status register. This needs to be done from the ISR to stop + generating interrupts. + +0x80 (RW) : DMA source address + Where to perform the DMA from. + +0x88 (RW) : DMA destination address + Where to perform the DMA to. + +0x90 (RW) : DMA transfer count + The size of the area to perform the DMA on. + +0x98 (RW) : DMA command register, bitwise OR + 0x01 -- start transfer + 0x02 -- direction (0: from RAM to EDU, 1: from EDU to RAM) + 0x04 -- raise interrupt 0x100 after finishing the DMA + +IRQ controller +-------------- +An IRQ is generated when written to the interrupt raise register. The value +appears in interrupt status register when the interrupt is raised and has to +be written to the interrupt acknowledge register to lower it. + +DMA controller +-------------- +One has to specify, source, destination, size, and start the transfer. One +4096 bytes long buffer at offset 0x40000 is available in the EDU device. I.e. +one can perform DMA to/from this space when programmed properly. + +Example of transferring a 100 byte block to and from the buffer using a given +PCI address 'addr': +addr -> DMA source address +0x40000 -> DMA destination address +100 -> DMA transfer count +1 -> DMA command register +while (DMA command register & 1) + ; + +0x40000 -> DMA source address +addr+100 -> DMA destination address +100 -> DMA transfer count +3 -> DMA command register +while (DMA command register & 1) + ; diff --git a/docs/specs/fw_cfg.txt b/docs/specs/fw_cfg.txt new file mode 100644 index 0000000..6accd92 --- /dev/null +++ b/docs/specs/fw_cfg.txt @@ -0,0 +1,205 @@ +QEMU Firmware Configuration (fw_cfg) Device +=========================================== + += Guest-side Hardware Interface = + +This hardware interface allows the guest to retrieve various data items +(blobs) that can influence how the firmware configures itself, or may +contain tables to be installed for the guest OS. Examples include device +boot order, ACPI and SMBIOS tables, virtual machine UUID, SMP and NUMA +information, kernel/initrd images for direct (Linux) kernel booting, etc. + +== Selector (Control) Register == + +* Write only +* Location: platform dependent (IOport or MMIO) +* Width: 16-bit +* Endianness: little-endian (if IOport), or big-endian (if MMIO) + +A write to this register sets the index of a firmware configuration +item which can subsequently be accessed via the data register. + +Setting the selector register will cause the data offset to be set +to zero. The data offset impacts which data is accessed via the data +register, and is explained below. + +Bit14 of the selector register indicates whether the configuration +setting is being written. A value of 0 means the item is only being +read, and all write access to the data port will be ignored. A value +of 1 means the item's data can be overwritten by writes to the data +register. In other words, configuration write mode is enabled when +the selector value is between 0x4000-0x7fff or 0xc000-0xffff. + +NOTE: As of QEMU v2.4, writes to the fw_cfg data register are no + longer supported, and will be ignored (treated as no-ops)! + +Bit15 of the selector register indicates whether the configuration +setting is architecture specific. A value of 0 means the item is a +generic configuration item. A value of 1 means the item is specific +to a particular architecture. In other words, generic configuration +items are accessed with a selector value between 0x0000-0x7fff, and +architecture specific configuration items are accessed with a selector +value between 0x8000-0xffff. + +== Data Register == + +* Read/Write (writes ignored as of QEMU v2.4) +* Location: platform dependent (IOport [*] or MMIO) +* Width: 8-bit (if IOport), 8/16/32/64-bit (if MMIO) +* Endianness: string-preserving + +[*] On platforms where the data register is exposed as an IOport, its +port number will always be one greater than the port number of the +selector register. In other words, the two ports overlap, and can not +be mapped separately. + +The data register allows access to an array of bytes for each firmware +configuration data item. The specific item is selected by writing to +the selector register, as described above. + +Initially following a write to the selector register, the data offset +will be set to zero. Each successful access to the data register will +increment the data offset by the appropriate access width. + +Each firmware configuration item has a maximum length of data +associated with the item. After the data offset has passed the +end of this maximum data length, then any reads will return a data +value of 0x00, and all writes will be ignored. + +An N-byte wide read of the data register will return the next available +N bytes of the selected firmware configuration item, as a substring, in +increasing address order, similar to memcpy(). + +== Register Locations == + +=== x86, x86_64 Register Locations === + +Selector Register IOport: 0x510 +Data Register IOport: 0x511 + +== Firmware Configuration Items == + +=== Signature (Key 0x0000, FW_CFG_SIGNATURE) === + +The presence of the fw_cfg selector and data registers can be verified +by selecting the "signature" item using key 0x0000 (FW_CFG_SIGNATURE), +and reading four bytes from the data register. If the fw_cfg device is +present, the four bytes read will contain the characters "QEMU". + +=== Revision (Key 0x0001, FW_CFG_ID) === + +A 32-bit little-endian unsigned int, this item is used as an interface +revision number, and is currently set to 1 by QEMU when fw_cfg is +initialized. + +=== File Directory (Key 0x0019, FW_CFG_FILE_DIR) === + +Firmware configuration items stored at selector keys 0x0020 or higher +(FW_CFG_FILE_FIRST or higher) have an associated entry in a directory +structure, which makes it easier for guest-side firmware to identify +and retrieve them. The format of this file directory (from fw_cfg.h in +the QEMU source tree) is shown here, slightly annotated for clarity: + +struct FWCfgFiles { /* the entire file directory fw_cfg item */ + uint32_t count; /* number of entries, in big-endian format */ + struct FWCfgFile f[]; /* array of file entries, see below */ +}; + +struct FWCfgFile { /* an individual file entry, 64 bytes total */ + uint32_t size; /* size of referenced fw_cfg item, big-endian */ + uint16_t select; /* selector key of fw_cfg item, big-endian */ + uint16_t reserved; + char name[56]; /* fw_cfg item name, NUL-terminated ascii */ +}; + +=== All Other Data Items === + +Please consult the QEMU source for the most up-to-date and authoritative +list of selector keys and their respective items' purpose and format. + +=== Ranges === + +Theoretically, there may be up to 0x4000 generic firmware configuration +items, and up to 0x4000 architecturally specific ones. + +Selector Reg. Range Usage +--------------- ----------- +0x0000 - 0x3fff Generic (0x0000 - 0x3fff, RO) +0x4000 - 0x7fff Generic (0x0000 - 0x3fff, RW, ignored in QEMU v2.4+) +0x8000 - 0xbfff Arch. Specific (0x0000 - 0x3fff, RO) +0xc000 - 0xffff Arch. Specific (0x0000 - 0x3fff, RW, ignored in v2.4+) + +In practice, the number of allowed firmware configuration items is given +by the value of FW_CFG_MAX_ENTRY (see fw_cfg.h). + += Host-side API = + +The following functions are available to the QEMU programmer for adding +data to a fw_cfg device during guest initialization (see fw_cfg.h for +each function's complete prototype): + +== fw_cfg_add_bytes() == + +Given a selector key value, starting pointer, and size, create an item +as a raw "blob" of the given size, available by selecting the given key. +The data referenced by the starting pointer is only linked, NOT copied, +into the data structure of the fw_cfg device. + +== fw_cfg_add_string() == + +Instead of a starting pointer and size, this function accepts a pointer +to a NUL-terminated ascii string, and inserts a newly allocated copy of +the string (including the NUL terminator) into the fw_cfg device data +structure. + +== fw_cfg_add_iXX() == + +Insert an XX-bit item, where XX may be 16, 32, or 64. These functions +will convert a 16-, 32-, or 64-bit integer to little-endian, then add +a dynamically allocated copy of the appropriately sized item to fw_cfg +under the given selector key value. + +== fw_cfg_add_file() == + +Given a filename (i.e., fw_cfg item name), starting pointer, and size, +create an item as a raw "blob" of the given size. Unlike fw_cfg_add_bytes() +above, the next available selector key (above 0x0020, FW_CFG_FILE_FIRST) +will be used, and a new entry will be added to the file directory structure +(at key 0x0019), containing the item name, blob size, and automatically +assigned selector key value. The data referenced by the starting pointer +is only linked, NOT copied, into the fw_cfg data structure. + +== fw_cfg_add_file_callback() == + +Like fw_cfg_add_file(), but additionally sets pointers to a callback +function (and opaque argument), which will be executed host-side by +QEMU each time a byte is read by the guest from this particular item. + +NOTE: The callback function is given the opaque argument set by +fw_cfg_add_file_callback(), but also the current data offset, +allowing it the option of only acting upon specific offset values +(e.g., 0, before the first data byte of the selected item is +returned to the guest). + +== fw_cfg_modify_file() == + +Given a filename (i.e., fw_cfg item name), starting pointer, and size, +completely replace the configuration item referenced by the given item +name with the new given blob. If an existing blob is found, its +callback information is removed, and a pointer to the old data is +returned to allow the caller to free it, helping avoid memory leaks. +If a configuration item does not already exist under the given item +name, a new item will be created as with fw_cfg_add_file(), and NULL +is returned to the caller. In any case, the data referenced by the +starting pointer is only linked, NOT copied, into the fw_cfg data +structure. + +== fw_cfg_add_callback() == + +Like fw_cfg_add_bytes(), but additionally sets pointers to a callback +function (and opaque argument), which will be executed host-side by +QEMU each time a guest-side write operation to this particular item +completes fully overwriting the item's data. + +NOTE: This function is deprecated, and will be completely removed +starting with QEMU v2.4. diff --git a/docs/specs/pci-ids.txt b/docs/specs/pci-ids.txt index 3c65e1a..c6732fe 100644 --- a/docs/specs/pci-ids.txt +++ b/docs/specs/pci-ids.txt @@ -44,6 +44,8 @@ PCI devices (other than virtio): 1b36:0002 PCI serial port (16550A) adapter (docs/specs/pci-serial.txt) 1b36:0003 PCI Dual-port 16550A adapter (docs/specs/pci-serial.txt) 1b36:0004 PCI Quad-port 16550A adapter (docs/specs/pci-serial.txt) +1b36:0005 PCI test device (docs/specs/pci-testdev.txt) +1b36:0007 PCI SD Card Host Controller Interface (SDHCI) All these devices are documented in docs/specs. diff --git a/docs/xbzrle.txt b/docs/xbzrle.txt index cc3a26a..52c8511 100644 --- a/docs/xbzrle.txt +++ b/docs/xbzrle.txt @@ -71,6 +71,14 @@ encoded buffer: encoded length 24 e9 07 0f 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 03 01 67 01 01 69 +Cache update strategy +===================== +Keeping the hot pages in the cache is effective for decreased cache +misses. XBZRLE uses a counter as the age of each page. The counter will +increase after each ram dirty bitmap sync. When a cache conflict is +detected, XBZRLE will only evict pages in the cache that are older than +a threshold. + Usage ====================== 1. Verify the destination QEMU version is able to decode the new format. diff --git a/exec.c b/exec.c index 71ac104..874ecfc 100644 --- a/exec.c +++ b/exec.c @@ -26,6 +26,9 @@ #include "cpu.h" #include "tcg.h" #include "hw/hw.h" +#if !defined(CONFIG_USER_ONLY) +#include "hw/boards.h" +#endif #include "hw/qdev.h" #include "qemu/osdep.h" #include "sysemu/kvm.h" @@ -44,7 +47,7 @@ #include "trace.h" #endif #include "exec/cpu-all.h" - +#include "qemu/rcu_queue.h" #include "exec/cputlb.h" #include "translate-all.h" @@ -58,7 +61,10 @@ #if !defined(CONFIG_USER_ONLY) static bool in_migration; -RAMList ram_list = { .blocks = QTAILQ_HEAD_INITIALIZER(ram_list.blocks) }; +/* ram_list is read under rcu_read_lock()/rcu_read_unlock(). Writes + * are protected by the ramlist lock. + */ +RAMList ram_list = { .blocks = QLIST_HEAD_INITIALIZER(ram_list.blocks) }; static MemoryRegion *system_memory; static MemoryRegion *system_io; @@ -75,6 +81,11 @@ static MemoryRegion io_mem_unassigned; /* RAM is mmap-ed with MAP_SHARED */ #define RAM_SHARED (1 << 1) +/* Only a portion of RAM (used_length) is actually used, and migrated. + * This used_length size can change across reboots. + */ +#define RAM_RESIZEABLE (1 << 2) + #endif struct CPUTailQ cpus = QTAILQ_HEAD_INITIALIZER(cpus); @@ -110,6 +121,8 @@ struct PhysPageEntry { typedef PhysPageEntry Node[P_L2_SIZE]; typedef struct PhysPageMap { + struct rcu_head rcu; + unsigned sections_nb; unsigned sections_nb_alloc; unsigned nodes_nb; @@ -119,6 +132,8 @@ typedef struct PhysPageMap { } PhysPageMap; struct AddressSpaceDispatch { + struct rcu_head rcu; + /* This is a multi-level map on the physical address space. * The bottom level has pointers to MemoryRegionSections. */ @@ -310,6 +325,7 @@ bool memory_region_is_unassigned(MemoryRegion *mr) && mr != &io_mem_watch; } +/* Called from RCU critical section */ static MemoryRegionSection *address_space_lookup_region(AddressSpaceDispatch *d, hwaddr addr, bool resolve_subpage) @@ -325,6 +341,7 @@ static MemoryRegionSection *address_space_lookup_region(AddressSpaceDispatch *d, return section; } +/* Called from RCU critical section */ static MemoryRegionSection * address_space_translate_internal(AddressSpaceDispatch *d, hwaddr addr, hwaddr *xlat, hwaddr *plen, bool resolve_subpage) @@ -365,8 +382,10 @@ MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr, MemoryRegion *mr; hwaddr len = *plen; + rcu_read_lock(); for (;;) { - section = address_space_translate_internal(as->dispatch, addr, &addr, plen, true); + AddressSpaceDispatch *d = atomic_rcu_read(&as->dispatch); + section = address_space_translate_internal(d, addr, &addr, plen, true); mr = section->mr; if (!mr->iommu_ops) { @@ -392,15 +411,18 @@ MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr, *plen = len; *xlat = addr; + rcu_read_unlock(); return mr; } +/* Called from RCU critical section */ MemoryRegionSection * -address_space_translate_for_iotlb(AddressSpace *as, hwaddr addr, hwaddr *xlat, - hwaddr *plen) +address_space_translate_for_iotlb(CPUState *cpu, hwaddr addr, + hwaddr *xlat, hwaddr *plen) { MemoryRegionSection *section; - section = address_space_translate_internal(as->dispatch, addr, xlat, plen, false); + section = address_space_translate_internal(cpu->memory_dispatch, + addr, xlat, plen, false); assert(!section->mr->iommu_ops); return section; @@ -434,7 +456,7 @@ static int cpu_common_pre_load(void *opaque) { CPUState *cpu = opaque; - cpu->exception_index = 0; + cpu->exception_index = -1; return 0; } @@ -443,7 +465,7 @@ static bool cpu_common_exception_index_needed(void *opaque) { CPUState *cpu = opaque; - return cpu->exception_index != 0; + return tcg_enabled() && cpu->exception_index != -1; } static const VMStateDescription vmstate_cpu_common_exception_index = { @@ -529,6 +551,7 @@ void cpu_exec_init(CPUArchState *env) #ifndef CONFIG_USER_ONLY cpu->as = &address_space_memory; cpu->thread_id = qemu_get_thread_id(); + cpu_reload_memory_map(cpu); #endif QTAILQ_INSERT_TAIL(&cpus, cpu, node); #if defined(CONFIG_USER_ONLY) @@ -548,7 +571,6 @@ void cpu_exec_init(CPUArchState *env) } } -#if defined(TARGET_HAS_ICE) #if defined(CONFIG_USER_ONLY) static void breakpoint_invalidate(CPUState *cpu, target_ulong pc) { @@ -564,7 +586,6 @@ static void breakpoint_invalidate(CPUState *cpu, target_ulong pc) } } #endif -#endif /* TARGET_HAS_ICE */ #if defined(CONFIG_USER_ONLY) void cpu_watchpoint_remove_all(CPUState *cpu, int mask) @@ -684,7 +705,6 @@ static inline bool cpu_watchpoint_address_matches(CPUWatchpoint *wp, int cpu_breakpoint_insert(CPUState *cpu, vaddr pc, int flags, CPUBreakpoint **breakpoint) { -#if defined(TARGET_HAS_ICE) CPUBreakpoint *bp; bp = g_malloc(sizeof(*bp)); @@ -705,15 +725,11 @@ int cpu_breakpoint_insert(CPUState *cpu, vaddr pc, int flags, *breakpoint = bp; } return 0; -#else - return -ENOSYS; -#endif } /* Remove a specific breakpoint. */ int cpu_breakpoint_remove(CPUState *cpu, vaddr pc, int flags) { -#if defined(TARGET_HAS_ICE) CPUBreakpoint *bp; QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) { @@ -723,27 +739,21 @@ int cpu_breakpoint_remove(CPUState *cpu, vaddr pc, int flags) } } return -ENOENT; -#else - return -ENOSYS; -#endif } /* Remove a specific breakpoint by reference. */ void cpu_breakpoint_remove_by_ref(CPUState *cpu, CPUBreakpoint *breakpoint) { -#if defined(TARGET_HAS_ICE) QTAILQ_REMOVE(&cpu->breakpoints, breakpoint, entry); breakpoint_invalidate(cpu, breakpoint->pc); g_free(breakpoint); -#endif } /* Remove all matching breakpoints. */ void cpu_breakpoint_remove_all(CPUState *cpu, int mask) { -#if defined(TARGET_HAS_ICE) CPUBreakpoint *bp, *next; QTAILQ_FOREACH_SAFE(bp, &cpu->breakpoints, entry, next) { @@ -751,14 +761,12 @@ void cpu_breakpoint_remove_all(CPUState *cpu, int mask) cpu_breakpoint_remove_by_ref(cpu, bp); } } -#endif } /* enable or disable single step mode. EXCP_DEBUG is returned by the CPU loop after each instruction */ void cpu_single_step(CPUState *cpu, int enabled) { -#if defined(TARGET_HAS_ICE) if (cpu->singlestep_enabled != enabled) { cpu->singlestep_enabled = enabled; if (kvm_enabled()) { @@ -770,7 +778,6 @@ void cpu_single_step(CPUState *cpu, int enabled) tb_flush(env); } } -#endif } void cpu_abort(CPUState *cpu, const char *fmt, ...) @@ -806,17 +813,17 @@ void cpu_abort(CPUState *cpu, const char *fmt, ...) } #if !defined(CONFIG_USER_ONLY) +/* Called from RCU critical section */ static RAMBlock *qemu_get_ram_block(ram_addr_t addr) { RAMBlock *block; - /* The list is protected by the iothread lock here. */ - block = ram_list.mru_block; - if (block && addr - block->offset < block->length) { + block = atomic_rcu_read(&ram_list.mru_block); + if (block && addr - block->offset < block->max_length) { goto found; } - QTAILQ_FOREACH(block, &ram_list.blocks, next) { - if (addr - block->offset < block->length) { + QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { + if (addr - block->offset < block->max_length) { goto found; } } @@ -825,6 +832,22 @@ static RAMBlock *qemu_get_ram_block(ram_addr_t addr) abort(); found: + /* It is safe to write mru_block outside the iothread lock. This + * is what happens: + * + * mru_block = xxx + * rcu_read_unlock() + * xxx removed from list + * rcu_read_lock() + * read mru_block + * mru_block = NULL; + * call_rcu(reclaim_ramblock, xxx); + * rcu_read_unlock() + * + * atomic_rcu_set is not needed here. The block was already published + * when it was placed into the list. Here we're just making an extra + * copy of the pointer. + */ ram_list.mru_block = block; return block; } @@ -838,10 +861,12 @@ static void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t length) end = TARGET_PAGE_ALIGN(start + length); start &= TARGET_PAGE_MASK; + rcu_read_lock(); block = qemu_get_ram_block(start); assert(block == qemu_get_ram_block(end - 1)); - start1 = (uintptr_t)block->host + (start - block->offset); + start1 = (uintptr_t)ramblock_ptr(block, start - block->offset); cpu_tlb_reset_dirty_all(start1, length); + rcu_read_unlock(); } /* Note: start and end must be within the same ram block. */ @@ -850,7 +875,7 @@ void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t length, { if (length == 0) return; - cpu_physical_memory_clear_dirty_range(start, length, client); + cpu_physical_memory_clear_dirty_range_type(start, length, client); if (tcg_enabled()) { tlb_reset_dirty_range_all(start, length); @@ -862,6 +887,7 @@ static void cpu_physical_memory_set_dirty_tracking(bool enable) in_migration = enable; } +/* Called from RCU critical section */ hwaddr memory_region_section_get_iotlb(CPUState *cpu, MemoryRegionSection *section, target_ulong vaddr, @@ -1166,13 +1192,14 @@ static void *file_ram_alloc(RAMBlock *block, error: if (mem_prealloc) { - error_report("%s\n", error_get_pretty(*errp)); + error_report("%s", error_get_pretty(*errp)); exit(1); } return NULL; } #endif +/* Called with the ramlist lock held. */ static ram_addr_t find_ram_offset(ram_addr_t size) { RAMBlock *block, *next_block; @@ -1180,15 +1207,16 @@ static ram_addr_t find_ram_offset(ram_addr_t size) assert(size != 0); /* it would hand out same offset multiple times */ - if (QTAILQ_EMPTY(&ram_list.blocks)) + if (QLIST_EMPTY_RCU(&ram_list.blocks)) { return 0; + } - QTAILQ_FOREACH(block, &ram_list.blocks, next) { + QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { ram_addr_t end, next = RAM_ADDR_MAX; - end = block->offset + block->length; + end = block->offset + block->max_length; - QTAILQ_FOREACH(next_block, &ram_list.blocks, next) { + QLIST_FOREACH_RCU(next_block, &ram_list.blocks, next) { if (next_block->offset >= end) { next = MIN(next, next_block->offset); } @@ -1213,9 +1241,11 @@ ram_addr_t last_ram_offset(void) RAMBlock *block; ram_addr_t last = 0; - QTAILQ_FOREACH(block, &ram_list.blocks, next) - last = MAX(last, block->offset + block->length); - + rcu_read_lock(); + QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { + last = MAX(last, block->offset + block->max_length); + } + rcu_read_unlock(); return last; } @@ -1224,8 +1254,7 @@ static void qemu_ram_setup_dump(void *addr, ram_addr_t size) int ret; /* Use MADV_DONTDUMP, if user doesn't want the guest memory in the core */ - if (!qemu_opt_get_bool(qemu_get_machine_opts(), - "dump-guest-core", true)) { + if (!machine_dump_guest_core(current_machine)) { ret = qemu_madvise(addr, size, QEMU_MADV_DONTDUMP); if (ret) { perror("qemu_madvise"); @@ -1235,11 +1264,14 @@ static void qemu_ram_setup_dump(void *addr, ram_addr_t size) } } +/* Called within an RCU critical section, or while the ramlist lock + * is held. + */ static RAMBlock *find_ram_block(ram_addr_t addr) { RAMBlock *block; - QTAILQ_FOREACH(block, &ram_list.blocks, next) { + QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { if (block->offset == addr) { return block; } @@ -1248,11 +1280,13 @@ static RAMBlock *find_ram_block(ram_addr_t addr) return NULL; } +/* Called with iothread lock held. */ void qemu_ram_set_idstr(ram_addr_t addr, const char *name, DeviceState *dev) { - RAMBlock *new_block = find_ram_block(addr); - RAMBlock *block; + RAMBlock *new_block, *block; + rcu_read_lock(); + new_block = find_ram_block(addr); assert(new_block); assert(!new_block->idstr[0]); @@ -1265,30 +1299,37 @@ void qemu_ram_set_idstr(ram_addr_t addr, const char *name, DeviceState *dev) } pstrcat(new_block->idstr, sizeof(new_block->idstr), name); - /* This assumes the iothread lock is taken here too. */ - qemu_mutex_lock_ramlist(); - QTAILQ_FOREACH(block, &ram_list.blocks, next) { + QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { if (block != new_block && !strcmp(block->idstr, new_block->idstr)) { fprintf(stderr, "RAMBlock \"%s\" already registered, abort!\n", new_block->idstr); abort(); } } - qemu_mutex_unlock_ramlist(); + rcu_read_unlock(); } +/* Called with iothread lock held. */ void qemu_ram_unset_idstr(ram_addr_t addr) { - RAMBlock *block = find_ram_block(addr); + RAMBlock *block; + + /* FIXME: arch_init.c assumes that this is not called throughout + * migration. Ignore the problem since hot-unplug during migration + * does not work anyway. + */ + rcu_read_lock(); + block = find_ram_block(addr); if (block) { memset(block->idstr, 0, sizeof(block->idstr)); } + rcu_read_unlock(); } static int memory_try_enable_merging(void *addr, size_t len) { - if (!qemu_opt_get_bool(qemu_get_machine_opts(), "mem-merge", true)) { + if (!machine_mem_merge(current_machine)) { /* disabled by the user */ return 0; } @@ -1296,22 +1337,68 @@ static int memory_try_enable_merging(void *addr, size_t len) return qemu_madvise(addr, len, QEMU_MADV_MERGEABLE); } +/* Only legal before guest might have detected the memory size: e.g. on + * incoming migration, or right after reset. + * + * As memory core doesn't know how is memory accessed, it is up to + * resize callback to update device state and/or add assertions to detect + * misuse, if necessary. + */ +int qemu_ram_resize(ram_addr_t base, ram_addr_t newsize, Error **errp) +{ + RAMBlock *block = find_ram_block(base); + + assert(block); + + newsize = TARGET_PAGE_ALIGN(newsize); + + if (block->used_length == newsize) { + return 0; + } + + if (!(block->flags & RAM_RESIZEABLE)) { + error_setg_errno(errp, EINVAL, + "Length mismatch: %s: 0x" RAM_ADDR_FMT + " in != 0x" RAM_ADDR_FMT, block->idstr, + newsize, block->used_length); + return -EINVAL; + } + + if (block->max_length < newsize) { + error_setg_errno(errp, EINVAL, + "Length too large: %s: 0x" RAM_ADDR_FMT + " > 0x" RAM_ADDR_FMT, block->idstr, + newsize, block->max_length); + return -EINVAL; + } + + cpu_physical_memory_clear_dirty_range(block->offset, block->used_length); + block->used_length = newsize; + cpu_physical_memory_set_dirty_range(block->offset, block->used_length); + memory_region_set_size(block->mr, newsize); + if (block->resized) { + block->resized(block->idstr, newsize, block->host); + } + return 0; +} + static ram_addr_t ram_block_add(RAMBlock *new_block, Error **errp) { RAMBlock *block; + RAMBlock *last_block = NULL; ram_addr_t old_ram_size, new_ram_size; old_ram_size = last_ram_offset() >> TARGET_PAGE_BITS; - /* This assumes the iothread lock is taken here too. */ qemu_mutex_lock_ramlist(); - new_block->offset = find_ram_offset(new_block->length); + new_block->offset = find_ram_offset(new_block->max_length); if (!new_block->host) { if (xen_enabled()) { - xen_ram_alloc(new_block->offset, new_block->length, new_block->mr); + xen_ram_alloc(new_block->offset, new_block->max_length, + new_block->mr); } else { - new_block->host = phys_mem_alloc(new_block->length, + new_block->host = phys_mem_alloc(new_block->max_length, &new_block->mr->align); if (!new_block->host) { error_setg_errno(errp, errno, @@ -1320,23 +1407,31 @@ static ram_addr_t ram_block_add(RAMBlock *new_block, Error **errp) qemu_mutex_unlock_ramlist(); return -1; } - memory_try_enable_merging(new_block->host, new_block->length); + memory_try_enable_merging(new_block->host, new_block->max_length); } } - /* Keep the list sorted from biggest to smallest block. */ - QTAILQ_FOREACH(block, &ram_list.blocks, next) { - if (block->length < new_block->length) { + /* Keep the list sorted from biggest to smallest block. Unlike QTAILQ, + * QLIST (which has an RCU-friendly variant) does not have insertion at + * tail, so save the last element in last_block. + */ + QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { + last_block = block; + if (block->max_length < new_block->max_length) { break; } } if (block) { - QTAILQ_INSERT_BEFORE(block, new_block, next); - } else { - QTAILQ_INSERT_TAIL(&ram_list.blocks, new_block, next); + QLIST_INSERT_BEFORE_RCU(block, new_block, next); + } else if (last_block) { + QLIST_INSERT_AFTER_RCU(last_block, new_block, next); + } else { /* list is empty */ + QLIST_INSERT_HEAD_RCU(&ram_list.blocks, new_block, next); } ram_list.mru_block = NULL; + /* Write list before version */ + smp_wmb(); ram_list.version++; qemu_mutex_unlock_ramlist(); @@ -1344,20 +1439,24 @@ static ram_addr_t ram_block_add(RAMBlock *new_block, Error **errp) if (new_ram_size > old_ram_size) { int i; + + /* ram_list.dirty_memory[] is protected by the iothread lock. */ for (i = 0; i < DIRTY_MEMORY_NUM; i++) { ram_list.dirty_memory[i] = bitmap_zero_extend(ram_list.dirty_memory[i], old_ram_size, new_ram_size); } } - cpu_physical_memory_set_dirty_range(new_block->offset, new_block->length); - - qemu_ram_setup_dump(new_block->host, new_block->length); - qemu_madvise(new_block->host, new_block->length, QEMU_MADV_HUGEPAGE); - qemu_madvise(new_block->host, new_block->length, QEMU_MADV_DONTFORK); + cpu_physical_memory_set_dirty_range(new_block->offset, + new_block->used_length); - if (kvm_enabled()) { - kvm_setup_guest_memory(new_block->host, new_block->length); + if (new_block->host) { + qemu_ram_setup_dump(new_block->host, new_block->max_length); + qemu_madvise(new_block->host, new_block->max_length, QEMU_MADV_HUGEPAGE); + qemu_madvise(new_block->host, new_block->max_length, QEMU_MADV_DONTFORK); + if (kvm_enabled()) { + kvm_setup_guest_memory(new_block->host, new_block->max_length); + } } return new_block->offset; @@ -1391,7 +1490,8 @@ ram_addr_t qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr, size = TARGET_PAGE_ALIGN(size); new_block = g_malloc0(sizeof(*new_block)); new_block->mr = mr; - new_block->length = size; + new_block->used_length = size; + new_block->max_length = size; new_block->flags = share ? RAM_SHARED : 0; new_block->host = file_ram_alloc(new_block, size, mem_path, errp); @@ -1410,7 +1510,12 @@ ram_addr_t qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr, } #endif -ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, +static +ram_addr_t qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size, + void (*resized)(const char*, + uint64_t length, + void *host), + void *host, bool resizeable, MemoryRegion *mr, Error **errp) { RAMBlock *new_block; @@ -1418,14 +1523,21 @@ ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, Error *local_err = NULL; size = TARGET_PAGE_ALIGN(size); + max_size = TARGET_PAGE_ALIGN(max_size); new_block = g_malloc0(sizeof(*new_block)); new_block->mr = mr; - new_block->length = size; + new_block->resized = resized; + new_block->used_length = size; + new_block->max_length = max_size; + assert(max_size >= size); new_block->fd = -1; new_block->host = host; if (host) { new_block->flags |= RAM_PREALLOC; } + if (resizeable) { + new_block->flags |= RAM_RESIZEABLE; + } addr = ram_block_add(new_block, &local_err); if (local_err) { g_free(new_block); @@ -1435,58 +1547,79 @@ ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, return addr; } +ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, + MemoryRegion *mr, Error **errp) +{ + return qemu_ram_alloc_internal(size, size, NULL, host, false, mr, errp); +} + ram_addr_t qemu_ram_alloc(ram_addr_t size, MemoryRegion *mr, Error **errp) { - return qemu_ram_alloc_from_ptr(size, NULL, mr, errp); + return qemu_ram_alloc_internal(size, size, NULL, NULL, false, mr, errp); +} + +ram_addr_t qemu_ram_alloc_resizeable(ram_addr_t size, ram_addr_t maxsz, + void (*resized)(const char*, + uint64_t length, + void *host), + MemoryRegion *mr, Error **errp) +{ + return qemu_ram_alloc_internal(size, maxsz, resized, NULL, true, mr, errp); } void qemu_ram_free_from_ptr(ram_addr_t addr) { RAMBlock *block; - /* This assumes the iothread lock is taken here too. */ qemu_mutex_lock_ramlist(); - QTAILQ_FOREACH(block, &ram_list.blocks, next) { + QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { if (addr == block->offset) { - QTAILQ_REMOVE(&ram_list.blocks, block, next); + QLIST_REMOVE_RCU(block, next); ram_list.mru_block = NULL; + /* Write list before version */ + smp_wmb(); ram_list.version++; - g_free(block); + g_free_rcu(block, rcu); break; } } qemu_mutex_unlock_ramlist(); } +static void reclaim_ramblock(RAMBlock *block) +{ + if (block->flags & RAM_PREALLOC) { + ; + } else if (xen_enabled()) { + xen_invalidate_map_cache_entry(block->host); +#ifndef _WIN32 + } else if (block->fd >= 0) { + munmap(block->host, block->max_length); + close(block->fd); +#endif + } else { + qemu_anon_ram_free(block->host, block->max_length); + } + g_free(block); +} + void qemu_ram_free(ram_addr_t addr) { RAMBlock *block; - /* This assumes the iothread lock is taken here too. */ qemu_mutex_lock_ramlist(); - QTAILQ_FOREACH(block, &ram_list.blocks, next) { + QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { if (addr == block->offset) { - QTAILQ_REMOVE(&ram_list.blocks, block, next); + QLIST_REMOVE_RCU(block, next); ram_list.mru_block = NULL; + /* Write list before version */ + smp_wmb(); ram_list.version++; - if (block->flags & RAM_PREALLOC) { - ; - } else if (xen_enabled()) { - xen_invalidate_map_cache_entry(block->host); -#ifndef _WIN32 - } else if (block->fd >= 0) { - munmap(block->host, block->length); - close(block->fd); -#endif - } else { - qemu_anon_ram_free(block->host, block->length); - } - g_free(block); + call_rcu(block, reclaim_ramblock, rcu); break; } } qemu_mutex_unlock_ramlist(); - } #ifndef _WIN32 @@ -1497,17 +1630,16 @@ void qemu_ram_remap(ram_addr_t addr, ram_addr_t length) int flags; void *area, *vaddr; - QTAILQ_FOREACH(block, &ram_list.blocks, next) { + QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { offset = addr - block->offset; - if (offset < block->length) { - vaddr = block->host + offset; + if (offset < block->max_length) { + vaddr = ramblock_ptr(block, offset); if (block->flags & RAM_PREALLOC) { ; } else if (xen_enabled()) { abort(); } else { flags = MAP_FIXED; - munmap(vaddr, length); if (block->fd >= 0) { flags |= (block->flags & RAM_SHARED ? MAP_SHARED : MAP_PRIVATE); @@ -1534,7 +1666,6 @@ void qemu_ram_remap(ram_addr_t addr, ram_addr_t length) memory_try_enable_merging(vaddr, length); qemu_ram_setup_dump(vaddr, length); } - return; } } } @@ -1542,49 +1673,78 @@ void qemu_ram_remap(ram_addr_t addr, ram_addr_t length) int qemu_get_ram_fd(ram_addr_t addr) { - RAMBlock *block = qemu_get_ram_block(addr); + RAMBlock *block; + int fd; - return block->fd; + rcu_read_lock(); + block = qemu_get_ram_block(addr); + fd = block->fd; + rcu_read_unlock(); + return fd; } void *qemu_get_ram_block_host_ptr(ram_addr_t addr) { - RAMBlock *block = qemu_get_ram_block(addr); + RAMBlock *block; + void *ptr; - return block->host; + rcu_read_lock(); + block = qemu_get_ram_block(addr); + ptr = ramblock_ptr(block, 0); + rcu_read_unlock(); + return ptr; } /* Return a host pointer to ram allocated with qemu_ram_alloc. - With the exception of the softmmu code in this file, this should - only be used for local memory (e.g. video ram) that the device owns, - and knows it isn't going to access beyond the end of the block. - - It should not be used for general purpose DMA. - Use cpu_physical_memory_map/cpu_physical_memory_rw instead. + * This should not be used for general purpose DMA. Use address_space_map + * or address_space_rw instead. For local memory (e.g. video ram) that the + * device owns, use memory_region_get_ram_ptr. + * + * By the time this function returns, the returned pointer is not protected + * by RCU anymore. If the caller is not within an RCU critical section and + * does not hold the iothread lock, it must have other means of protecting the + * pointer, such as a reference to the region that includes the incoming + * ram_addr_t. */ void *qemu_get_ram_ptr(ram_addr_t addr) { - RAMBlock *block = qemu_get_ram_block(addr); + RAMBlock *block; + void *ptr; - if (xen_enabled()) { + rcu_read_lock(); + block = qemu_get_ram_block(addr); + + if (xen_enabled() && block->host == NULL) { /* We need to check if the requested address is in the RAM * because we don't want to map the entire memory in QEMU. * In that case just map until the end of the page. */ if (block->offset == 0) { - return xen_map_cache(addr, 0, 0); - } else if (block->host == NULL) { - block->host = - xen_map_cache(block->offset, block->length, 1); + ptr = xen_map_cache(addr, 0, 0); + goto unlock; } + + block->host = xen_map_cache(block->offset, block->max_length, 1); } - return block->host + (addr - block->offset); + ptr = ramblock_ptr(block, addr - block->offset); + +unlock: + rcu_read_unlock(); + return ptr; } /* Return a host pointer to guest's ram. Similar to qemu_get_ram_ptr - * but takes a size argument */ + * but takes a size argument. + * + * By the time this function returns, the returned pointer is not protected + * by RCU anymore. If the caller is not within an RCU critical section and + * does not hold the iothread lock, it must have other means of protecting the + * pointer, such as a reference to the region that includes the incoming + * ram_addr_t. + */ static void *qemu_ram_ptr_length(ram_addr_t addr, hwaddr *size) { + void *ptr; if (*size == 0) { return NULL; } @@ -1592,12 +1752,14 @@ static void *qemu_ram_ptr_length(ram_addr_t addr, hwaddr *size) return xen_map_cache(addr, *size, 1); } else { RAMBlock *block; - - QTAILQ_FOREACH(block, &ram_list.blocks, next) { - if (addr - block->offset < block->length) { - if (addr - block->offset + *size > block->length) - *size = block->length - addr + block->offset; - return block->host + (addr - block->offset); + rcu_read_lock(); + QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { + if (addr - block->offset < block->max_length) { + if (addr - block->offset + *size > block->max_length) + *size = block->max_length - addr + block->offset; + ptr = ramblock_ptr(block, addr - block->offset); + rcu_read_unlock(); + return ptr; } } @@ -1607,37 +1769,52 @@ static void *qemu_ram_ptr_length(ram_addr_t addr, hwaddr *size) } /* Some of the softmmu routines need to translate from a host pointer - (typically a TLB entry) back to a ram offset. */ + * (typically a TLB entry) back to a ram offset. + * + * By the time this function returns, the returned pointer is not protected + * by RCU anymore. If the caller is not within an RCU critical section and + * does not hold the iothread lock, it must have other means of protecting the + * pointer, such as a reference to the region that includes the incoming + * ram_addr_t. + */ MemoryRegion *qemu_ram_addr_from_host(void *ptr, ram_addr_t *ram_addr) { RAMBlock *block; uint8_t *host = ptr; + MemoryRegion *mr; if (xen_enabled()) { + rcu_read_lock(); *ram_addr = xen_ram_addr_from_mapcache(ptr); - return qemu_get_ram_block(*ram_addr)->mr; + mr = qemu_get_ram_block(*ram_addr)->mr; + rcu_read_unlock(); + return mr; } - block = ram_list.mru_block; - if (block && block->host && host - block->host < block->length) { + rcu_read_lock(); + block = atomic_rcu_read(&ram_list.mru_block); + if (block && block->host && host - block->host < block->max_length) { goto found; } - QTAILQ_FOREACH(block, &ram_list.blocks, next) { + QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { /* This case append when the block is not mapped. */ if (block->host == NULL) { continue; } - if (host - block->host < block->length) { + if (host - block->host < block->max_length) { goto found; } } + rcu_read_unlock(); return NULL; found: *ram_addr = block->offset + (host - block->host); - return block->mr; + mr = block->mr; + rcu_read_unlock(); + return mr; } static void notdirty_mem_write(void *opaque, hwaddr ram_addr, @@ -1768,7 +1945,7 @@ static uint64_t subpage_read(void *opaque, hwaddr addr, unsigned len) { subpage_t *subpage = opaque; - uint8_t buf[4]; + uint8_t buf[8]; #if defined(DEBUG_SUBPAGE) printf("%s: subpage %p len %u addr " TARGET_FMT_plx "\n", __func__, @@ -1782,6 +1959,8 @@ static uint64_t subpage_read(void *opaque, hwaddr addr, return lduw_p(buf); case 4: return ldl_p(buf); + case 8: + return ldq_p(buf); default: abort(); } @@ -1791,7 +1970,7 @@ static void subpage_write(void *opaque, hwaddr addr, uint64_t value, unsigned len) { subpage_t *subpage = opaque; - uint8_t buf[4]; + uint8_t buf[8]; #if defined(DEBUG_SUBPAGE) printf("%s: subpage %p len %u addr " TARGET_FMT_plx @@ -1808,6 +1987,9 @@ static void subpage_write(void *opaque, hwaddr addr, case 4: stl_p(buf, value); break; + case 8: + stq_p(buf, value); + break; default: abort(); } @@ -1830,6 +2012,10 @@ static bool subpage_accepts(void *opaque, hwaddr addr, static const MemoryRegionOps subpage_ops = { .read = subpage_read, .write = subpage_write, + .impl.min_access_size = 1, + .impl.max_access_size = 8, + .valid.min_access_size = 1, + .valid.max_access_size = 8, .valid.accepts = subpage_accepts, .endianness = DEVICE_NATIVE_ENDIAN, }; @@ -1889,9 +2075,12 @@ static uint16_t dummy_section(PhysPageMap *map, AddressSpace *as, return phys_section_add(map, §ion); } -MemoryRegion *iotlb_to_region(AddressSpace *as, hwaddr index) +MemoryRegion *iotlb_to_region(CPUState *cpu, hwaddr index) { - return as->dispatch->map.sections[index & ~TARGET_PAGE_MASK].mr; + AddressSpaceDispatch *d = atomic_rcu_read(&cpu->memory_dispatch); + MemoryRegionSection *sections = d->map.sections; + + return sections[index & ~TARGET_PAGE_MASK].mr; } static void io_mem_init(void) @@ -1925,6 +2114,12 @@ static void mem_begin(MemoryListener *listener) as->next_dispatch = d; } +static void address_space_dispatch_free(AddressSpaceDispatch *d) +{ + phys_sections_free(&d->map); + g_free(d); +} + static void mem_commit(MemoryListener *listener) { AddressSpace *as = container_of(listener, AddressSpace, dispatch_listener); @@ -1933,11 +2128,9 @@ static void mem_commit(MemoryListener *listener) phys_page_compact_all(next, next->map.nodes_nb); - as->dispatch = next; - + atomic_rcu_set(&as->dispatch, next); if (cur) { - phys_sections_free(&cur->map); - g_free(cur); + call_rcu(cur, address_space_dispatch_free, rcu); } } @@ -1954,7 +2147,7 @@ static void tcg_commit(MemoryListener *listener) if (cpu->tcg_as_listener != listener) { continue; } - tlb_flush(cpu, 1); + cpu_reload_memory_map(cpu); } } @@ -1987,13 +2180,19 @@ void address_space_init_dispatch(AddressSpace *as) memory_listener_register(&as->dispatch_listener, as); } +void address_space_unregister(AddressSpace *as) +{ + memory_listener_unregister(&as->dispatch_listener); +} + void address_space_destroy_dispatch(AddressSpace *as) { AddressSpaceDispatch *d = as->dispatch; - memory_listener_unregister(&as->dispatch_listener); - g_free(d); - as->dispatch = NULL; + atomic_rcu_set(&as->dispatch, NULL); + if (d) { + call_rcu(d, address_space_dispatch_free, rcu); + } } static void memory_map_init(void) @@ -2872,8 +3071,10 @@ void qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque) { RAMBlock *block; - QTAILQ_FOREACH(block, &ram_list.blocks, next) { - func(block->host, block->offset, block->length, opaque); + rcu_read_lock(); + QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { + func(block->host, block->offset, block->used_length, opaque); } + rcu_read_unlock(); } #endif diff --git a/fpu/softfloat-macros.h b/fpu/softfloat-macros.h index 0dcda93..5e030cd 100644 --- a/fpu/softfloat-macros.h +++ b/fpu/softfloat-macros.h @@ -1,13 +1,24 @@ /* * QEMU float support macros * - * Derived from SoftFloat. + * The code in this source file is derived from release 2a of the SoftFloat + * IEC/IEEE Floating-point Arithmetic Package. Those parts of the code (and + * some later contributions) are provided under that license, as detailed below. + * It has subsequently been modified by contributors to the QEMU Project, + * so some portions are provided under: + * the SoftFloat-2a license + * the BSD license + * GPL-v2-or-later + * + * Any future contributions to this file after December 1st 2014 will be + * taken to be licensed under the Softfloat-2a license unless specifically + * indicated otherwise. */ -/*============================================================================ - +/* +=============================================================================== This C source fragment is part of the SoftFloat IEC/IEEE Floating-point -Arithmetic Package, Release 2b. +Arithmetic Package, Release 2a. Written by John R. Hauser. This work was made possible in part by the International Computer Science Institute, located at Suite 600, 1947 Center @@ -16,24 +27,57 @@ National Science Foundation under grant MIP-9311980. The original version of this code was written as part of a project to build a fixed-point vector processor in collaboration with the University of California at Berkeley, overseen by Profs. Nelson Morgan and John Wawrzynek. More information -is available through the Web page `http://www.cs.berkeley.edu/~jhauser/ +is available through the Web page `http://HTTP.CS.Berkeley.EDU/~jhauser/ arithmetic/SoftFloat.html'. -THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort has -been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES -RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS -AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES, -COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE -EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE -INSTITUTE (possibly via similar legal notice) AGAINST ALL LOSSES, COSTS, OR -OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE. +THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort +has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT +TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO +PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY +AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE. Derivative works are acceptable, even for commercial purposes, so long as -(1) the source code for the derivative work includes prominent notice that -the work is derivative, and (2) the source code includes prominent notice with -these four paragraphs for those parts of this code that are retained. +(1) they include prominent notice that the work is derivative, and (2) they +include prominent notice akin to these four paragraphs for those parts of +this code that are retained. + +=============================================================================== +*/ + +/* BSD licensing: + * Copyright (c) 2006, Fabrice Bellard + * 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. + * + * 3. Neither the name of the copyright holder 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 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. + */ -=============================================================================*/ +/* Portions of this work are licensed under the terms of the GNU GPL, + * version 2 or later. See the COPYING file in the top-level directory. + */ /*---------------------------------------------------------------------------- | This macro tests for minimum version of the GNU C compiler. @@ -107,10 +151,10 @@ static inline void shift64RightJamming(uint64_t a, int_fast16_t count, uint64_t | 63 bits of the extra result are all zero if and only if _all_but_the_last_ | bits shifted off were all zero. This extra result is stored in the location | pointed to by `z1Ptr'. The value of `count' can be arbitrarily large. -| (This routine makes more sense if `a0' and `a1' are considered to form -| a fixed-point value with binary point between `a0' and `a1'. This fixed- -| point value is shifted right by the number of bits given in `count', and -| the integer part of the result is returned at the location pointed to by +| (This routine makes more sense if `a0' and `a1' are considered to form a +| fixed-point value with binary point between `a0' and `a1'. This fixed-point +| value is shifted right by the number of bits given in `count', and the +| integer part of the result is returned at the location pointed to by | `z0Ptr'. The fractional part of the result may be slightly corrupted as | described above, and is returned at the location pointed to by `z1Ptr'.) *----------------------------------------------------------------------------*/ diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h index 518f694..fa1214a 100644 --- a/fpu/softfloat-specialize.h +++ b/fpu/softfloat-specialize.h @@ -1,13 +1,24 @@ /* * QEMU float support * - * Derived from SoftFloat. + * The code in this source file is derived from release 2a of the SoftFloat + * IEC/IEEE Floating-point Arithmetic Package. Those parts of the code (and + * some later contributions) are provided under that license, as detailed below. + * It has subsequently been modified by contributors to the QEMU Project, + * so some portions are provided under: + * the SoftFloat-2a license + * the BSD license + * GPL-v2-or-later + * + * Any future contributions to this file after December 1st 2014 will be + * taken to be licensed under the Softfloat-2a license unless specifically + * indicated otherwise. */ -/*============================================================================ - +/* +=============================================================================== This C source fragment is part of the SoftFloat IEC/IEEE Floating-point -Arithmetic Package, Release 2b. +Arithmetic Package, Release 2a. Written by John R. Hauser. This work was made possible in part by the International Computer Science Institute, located at Suite 600, 1947 Center @@ -16,29 +27,66 @@ National Science Foundation under grant MIP-9311980. The original version of this code was written as part of a project to build a fixed-point vector processor in collaboration with the University of California at Berkeley, overseen by Profs. Nelson Morgan and John Wawrzynek. More information -is available through the Web page `http://www.cs.berkeley.edu/~jhauser/ +is available through the Web page `http://HTTP.CS.Berkeley.EDU/~jhauser/ arithmetic/SoftFloat.html'. -THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort has -been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES -RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS -AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES, -COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE -EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE -INSTITUTE (possibly via similar legal warning) AGAINST ALL LOSSES, COSTS, OR -OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE. +THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort +has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT +TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO +PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY +AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE. Derivative works are acceptable, even for commercial purposes, so long as -(1) the source code for the derivative work includes prominent notice that -the work is derivative, and (2) the source code includes prominent notice with -these four paragraphs for those parts of this code that are retained. +(1) they include prominent notice that the work is derivative, and (2) they +include prominent notice akin to these four paragraphs for those parts of +this code that are retained. -=============================================================================*/ +=============================================================================== +*/ + +/* BSD licensing: + * Copyright (c) 2006, Fabrice Bellard + * 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. + * + * 3. Neither the name of the copyright holder 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 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. + */ +/* Portions of this work are licensed under the terms of the GNU GPL, + * version 2 or later. See the COPYING file in the top-level directory. + */ + +/* Does the target distinguish signaling NaNs from non-signaling NaNs + * by setting the most significant bit of the mantissa for a signaling NaN? + * (The more common choice is to have it be zero for SNaN and one for QNaN.) + */ #if defined(TARGET_MIPS) || defined(TARGET_SH4) || defined(TARGET_UNICORE32) -#define SNAN_BIT_IS_ONE 1 +#define SNAN_BIT_IS_ONE 1 #else -#define SNAN_BIT_IS_ONE 0 +#define SNAN_BIT_IS_ONE 0 #endif #if defined(TARGET_XTENSA) @@ -81,7 +129,7 @@ const float64 float64_default_nan = const_float64(LIT64( 0x7FFFFFFFFFFFFFFF )); #elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) const float64 float64_default_nan = const_float64(LIT64( 0x7FF8000000000000 )); #elif SNAN_BIT_IS_ONE -const float64 float64_default_nan = const_float64(LIT64( 0x7FF7FFFFFFFFFFFF )); +const float64 float64_default_nan = const_float64(LIT64(0x7FF7FFFFFFFFFFFF)); #else const float64 float64_default_nan = const_float64(LIT64( 0xFFF8000000000000 )); #endif @@ -91,7 +139,7 @@ const float64 float64_default_nan = const_float64(LIT64( 0xFFF8000000000000 )); *----------------------------------------------------------------------------*/ #if SNAN_BIT_IS_ONE #define floatx80_default_nan_high 0x7FFF -#define floatx80_default_nan_low LIT64( 0xBFFFFFFFFFFFFFFF ) +#define floatx80_default_nan_low LIT64(0xBFFFFFFFFFFFFFFF) #else #define floatx80_default_nan_high 0xFFFF #define floatx80_default_nan_low LIT64( 0xC000000000000000 ) @@ -105,8 +153,8 @@ const floatx80 floatx80_default_nan | `low' values hold the most- and least-significant bits, respectively. *----------------------------------------------------------------------------*/ #if SNAN_BIT_IS_ONE -#define float128_default_nan_high LIT64( 0x7FFF7FFFFFFFFFFF ) -#define float128_default_nan_low LIT64( 0xFFFFFFFFFFFFFFFF ) +#define float128_default_nan_high LIT64(0x7FFF7FFFFFFFFFFF) +#define float128_default_nan_low LIT64(0xFFFFFFFFFFFFFFFF) #else #define float128_default_nan_high LIT64( 0xFFFF800000000000 ) #define float128_default_nan_low LIT64( 0x0000000000000000 ) @@ -122,9 +170,9 @@ const float128 float128_default_nan | should be simply `float_exception_flags |= flags;'. *----------------------------------------------------------------------------*/ -void float_raise( int8 flags STATUS_PARAM ) +void float_raise(int8 flags, float_status *status) { - STATUS(float_exception_flags) |= flags; + status->float_exception_flags |= flags; } /*---------------------------------------------------------------------------- @@ -205,11 +253,13 @@ float16 float16_maybe_silence_nan(float16 a_) | exception is raised. *----------------------------------------------------------------------------*/ -static commonNaNT float16ToCommonNaN( float16 a STATUS_PARAM ) +static commonNaNT float16ToCommonNaN(float16 a, float_status *status) { commonNaNT z; - if ( float16_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR ); + if (float16_is_signaling_nan(a)) { + float_raise(float_flag_invalid, status); + } z.sign = float16_val(a) >> 15; z.low = 0; z.high = ((uint64_t) float16_val(a))<<54; @@ -221,11 +271,11 @@ static commonNaNT float16ToCommonNaN( float16 a STATUS_PARAM ) | precision floating-point format. *----------------------------------------------------------------------------*/ -static float16 commonNaNToFloat16(commonNaNT a STATUS_PARAM) +static float16 commonNaNToFloat16(commonNaNT a, float_status *status) { uint16_t mantissa = a.high>>54; - if (STATUS(default_nan_mode)) { + if (status->default_nan_mode) { return float16_default_nan; } @@ -257,9 +307,9 @@ int float32_is_quiet_nan( float32 a_ ) { uint32_t a = float32_val(a_); #if SNAN_BIT_IS_ONE - return ( ( ( a>>22 ) & 0x1FF ) == 0x1FE ) && ( a & 0x003FFFFF ); + return (((a >> 22) & 0x1ff) == 0x1fe) && (a & 0x003fffff); #else - return ( 0xFF800000 <= (uint32_t) ( a<<1 ) ); + return ((uint32_t)(a << 1) >= 0xff800000); #endif } @@ -272,7 +322,7 @@ int float32_is_signaling_nan( float32 a_ ) { uint32_t a = float32_val(a_); #if SNAN_BIT_IS_ONE - return ( 0xFF800000 <= (uint32_t) ( a<<1 ) ); + return ((uint32_t)(a << 1) >= 0xff800000); #else return ( ( ( a>>22 ) & 0x1FF ) == 0x1FE ) && ( a & 0x003FFFFF ); #endif @@ -308,11 +358,13 @@ float32 float32_maybe_silence_nan( float32 a_ ) | exception is raised. *----------------------------------------------------------------------------*/ -static commonNaNT float32ToCommonNaN( float32 a STATUS_PARAM ) +static commonNaNT float32ToCommonNaN(float32 a, float_status *status) { commonNaNT z; - if ( float32_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR ); + if (float32_is_signaling_nan(a)) { + float_raise(float_flag_invalid, status); + } z.sign = float32_val(a)>>31; z.low = 0; z.high = ( (uint64_t) float32_val(a) )<<41; @@ -324,11 +376,11 @@ static commonNaNT float32ToCommonNaN( float32 a STATUS_PARAM ) | precision floating-point format. *----------------------------------------------------------------------------*/ -static float32 commonNaNToFloat32( commonNaNT a STATUS_PARAM) +static float32 commonNaNToFloat32(commonNaNT a, float_status *status) { uint32_t mantissa = a.high>>41; - if ( STATUS(default_nan_mode) ) { + if (status->default_nan_mode) { return float32_default_nan; } @@ -459,13 +511,14 @@ static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, *----------------------------------------------------------------------------*/ #if defined(TARGET_ARM) static int pickNaNMulAdd(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, - flag cIsQNaN, flag cIsSNaN, flag infzero STATUS_PARAM) + flag cIsQNaN, flag cIsSNaN, flag infzero, + float_status *status) { /* For ARM, the (inf,zero,qnan) case sets InvalidOp and returns * the default NaN */ if (infzero && cIsQNaN) { - float_raise(float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return 3; } @@ -488,13 +541,14 @@ static int pickNaNMulAdd(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, } #elif defined(TARGET_MIPS) static int pickNaNMulAdd(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, - flag cIsQNaN, flag cIsSNaN, flag infzero STATUS_PARAM) + flag cIsQNaN, flag cIsSNaN, flag infzero, + float_status *status) { /* For MIPS, the (inf,zero,qnan) case sets InvalidOp and returns * the default NaN */ if (infzero) { - float_raise(float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return 3; } @@ -515,14 +569,15 @@ static int pickNaNMulAdd(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, } #elif defined(TARGET_PPC) static int pickNaNMulAdd(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, - flag cIsQNaN, flag cIsSNaN, flag infzero STATUS_PARAM) + flag cIsQNaN, flag cIsSNaN, flag infzero, + float_status *status) { /* For PPC, the (inf,zero,qnan) case sets InvalidOp, but we prefer * to return an input NaN if we have one (ie c) rather than generating * a default NaN */ if (infzero) { - float_raise(float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return 2; } @@ -542,7 +597,8 @@ static int pickNaNMulAdd(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, * This is unlikely to actually match any real implementation. */ static int pickNaNMulAdd(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, - flag cIsQNaN, flag cIsSNaN, flag infzero STATUS_PARAM) + flag cIsQNaN, flag cIsSNaN, flag infzero, + float_status *status) { if (aIsSNaN || aIsQNaN) { return 0; @@ -560,7 +616,7 @@ static int pickNaNMulAdd(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, | signaling NaN, the invalid exception is raised. *----------------------------------------------------------------------------*/ -static float32 propagateFloat32NaN( float32 a, float32 b STATUS_PARAM) +static float32 propagateFloat32NaN(float32 a, float32 b, float_status *status) { flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN; flag aIsLargerSignificand; @@ -573,9 +629,11 @@ static float32 propagateFloat32NaN( float32 a, float32 b STATUS_PARAM) av = float32_val(a); bv = float32_val(b); - if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR); + if (aIsSignalingNaN | bIsSignalingNaN) { + float_raise(float_flag_invalid, status); + } - if ( STATUS(default_nan_mode) ) + if (status->default_nan_mode) return float32_default_nan; if ((uint32_t)(av<<1) < (uint32_t)(bv<<1)) { @@ -604,7 +662,8 @@ static float32 propagateFloat32NaN( float32 a, float32 b STATUS_PARAM) *----------------------------------------------------------------------------*/ static float32 propagateFloat32MulAddNaN(float32 a, float32 b, - float32 c, flag infzero STATUS_PARAM) + float32 c, flag infzero, + float_status *status) { flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, cIsQuietNaN, cIsSignalingNaN; @@ -618,14 +677,14 @@ static float32 propagateFloat32MulAddNaN(float32 a, float32 b, cIsSignalingNaN = float32_is_signaling_nan(c); if (aIsSignalingNaN | bIsSignalingNaN | cIsSignalingNaN) { - float_raise(float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); } which = pickNaNMulAdd(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, - cIsQuietNaN, cIsSignalingNaN, infzero STATUS_VAR); + cIsQuietNaN, cIsSignalingNaN, infzero, status); - if (STATUS(default_nan_mode)) { + if (status->default_nan_mode) { /* Note that this check is after pickNaNMulAdd so that function * has an opportunity to set the Invalid flag. */ @@ -665,11 +724,10 @@ int float64_is_quiet_nan( float64 a_ ) { uint64_t a = float64_val(a_); #if SNAN_BIT_IS_ONE - return - ( ( ( a>>51 ) & 0xFFF ) == 0xFFE ) - && ( a & LIT64( 0x0007FFFFFFFFFFFF ) ); + return (((a >> 51) & 0xfff) == 0xffe) + && (a & 0x0007ffffffffffffULL); #else - return ( LIT64( 0xFFF0000000000000 ) <= (uint64_t) ( a<<1 ) ); + return ((a << 1) >= 0xfff0000000000000ULL); #endif } @@ -682,7 +740,7 @@ int float64_is_signaling_nan( float64 a_ ) { uint64_t a = float64_val(a_); #if SNAN_BIT_IS_ONE - return ( LIT64( 0xFFF0000000000000 ) <= (uint64_t) ( a<<1 ) ); + return ((a << 1) >= 0xfff0000000000000ULL); #else return ( ( ( a>>51 ) & 0xFFF ) == 0xFFE ) @@ -720,11 +778,13 @@ float64 float64_maybe_silence_nan( float64 a_ ) | exception is raised. *----------------------------------------------------------------------------*/ -static commonNaNT float64ToCommonNaN( float64 a STATUS_PARAM) +static commonNaNT float64ToCommonNaN(float64 a, float_status *status) { commonNaNT z; - if ( float64_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR); + if (float64_is_signaling_nan(a)) { + float_raise(float_flag_invalid, status); + } z.sign = float64_val(a)>>63; z.low = 0; z.high = float64_val(a)<<12; @@ -736,11 +796,11 @@ static commonNaNT float64ToCommonNaN( float64 a STATUS_PARAM) | precision floating-point format. *----------------------------------------------------------------------------*/ -static float64 commonNaNToFloat64( commonNaNT a STATUS_PARAM) +static float64 commonNaNToFloat64(commonNaNT a, float_status *status) { uint64_t mantissa = a.high>>12; - if ( STATUS(default_nan_mode) ) { + if (status->default_nan_mode) { return float64_default_nan; } @@ -759,7 +819,7 @@ static float64 commonNaNToFloat64( commonNaNT a STATUS_PARAM) | signaling NaN, the invalid exception is raised. *----------------------------------------------------------------------------*/ -static float64 propagateFloat64NaN( float64 a, float64 b STATUS_PARAM) +static float64 propagateFloat64NaN(float64 a, float64 b, float_status *status) { flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN; flag aIsLargerSignificand; @@ -772,9 +832,11 @@ static float64 propagateFloat64NaN( float64 a, float64 b STATUS_PARAM) av = float64_val(a); bv = float64_val(b); - if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR); + if (aIsSignalingNaN | bIsSignalingNaN) { + float_raise(float_flag_invalid, status); + } - if ( STATUS(default_nan_mode) ) + if (status->default_nan_mode) return float64_default_nan; if ((uint64_t)(av<<1) < (uint64_t)(bv<<1)) { @@ -803,7 +865,8 @@ static float64 propagateFloat64NaN( float64 a, float64 b STATUS_PARAM) *----------------------------------------------------------------------------*/ static float64 propagateFloat64MulAddNaN(float64 a, float64 b, - float64 c, flag infzero STATUS_PARAM) + float64 c, flag infzero, + float_status *status) { flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, cIsQuietNaN, cIsSignalingNaN; @@ -817,14 +880,14 @@ static float64 propagateFloat64MulAddNaN(float64 a, float64 b, cIsSignalingNaN = float64_is_signaling_nan(c); if (aIsSignalingNaN | bIsSignalingNaN | cIsSignalingNaN) { - float_raise(float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); } which = pickNaNMulAdd(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, - cIsQuietNaN, cIsSignalingNaN, infzero STATUS_VAR); + cIsQuietNaN, cIsSignalingNaN, infzero, status); - if (STATUS(default_nan_mode)) { + if (status->default_nan_mode) { /* Note that this check is after pickNaNMulAdd so that function * has an opportunity to set the Invalid flag. */ @@ -866,11 +929,10 @@ int floatx80_is_quiet_nan( floatx80 a ) #if SNAN_BIT_IS_ONE uint64_t aLow; - aLow = a.low & ~ LIT64( 0x4000000000000000 ); - return - ( ( a.high & 0x7FFF ) == 0x7FFF ) - && (uint64_t) ( aLow<<1 ) - && ( a.low == aLow ); + aLow = a.low & ~0x4000000000000000ULL; + return ((a.high & 0x7fff) == 0x7fff) + && (aLow << 1) + && (a.low == aLow); #else return ( ( a.high & 0x7FFF ) == 0x7FFF ) && (LIT64( 0x8000000000000000 ) <= ((uint64_t) ( a.low<<1 ))); @@ -886,8 +948,8 @@ int floatx80_is_quiet_nan( floatx80 a ) int floatx80_is_signaling_nan( floatx80 a ) { #if SNAN_BIT_IS_ONE - return ( ( a.high & 0x7FFF ) == 0x7FFF ) - && (LIT64( 0x8000000000000000 ) <= ((uint64_t) ( a.low<<1 ))); + return ((a.high & 0x7fff) == 0x7fff) + && ((a.low << 1) >= 0x8000000000000000ULL); #else uint64_t aLow; @@ -929,11 +991,13 @@ floatx80 floatx80_maybe_silence_nan( floatx80 a ) | invalid exception is raised. *----------------------------------------------------------------------------*/ -static commonNaNT floatx80ToCommonNaN( floatx80 a STATUS_PARAM) +static commonNaNT floatx80ToCommonNaN(floatx80 a, float_status *status) { commonNaNT z; - if ( floatx80_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR); + if (floatx80_is_signaling_nan(a)) { + float_raise(float_flag_invalid, status); + } if ( a.low >> 63 ) { z.sign = a.high >> 15; z.low = 0; @@ -951,11 +1015,11 @@ static commonNaNT floatx80ToCommonNaN( floatx80 a STATUS_PARAM) | double-precision floating-point format. *----------------------------------------------------------------------------*/ -static floatx80 commonNaNToFloatx80( commonNaNT a STATUS_PARAM) +static floatx80 commonNaNToFloatx80(commonNaNT a, float_status *status) { floatx80 z; - if ( STATUS(default_nan_mode) ) { + if (status->default_nan_mode) { z.low = floatx80_default_nan_low; z.high = floatx80_default_nan_high; return z; @@ -978,7 +1042,8 @@ static floatx80 commonNaNToFloatx80( commonNaNT a STATUS_PARAM) | `b' is a signaling NaN, the invalid exception is raised. *----------------------------------------------------------------------------*/ -static floatx80 propagateFloatx80NaN( floatx80 a, floatx80 b STATUS_PARAM) +static floatx80 propagateFloatx80NaN(floatx80 a, floatx80 b, + float_status *status) { flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN; flag aIsLargerSignificand; @@ -988,9 +1053,11 @@ static floatx80 propagateFloatx80NaN( floatx80 a, floatx80 b STATUS_PARAM) bIsQuietNaN = floatx80_is_quiet_nan( b ); bIsSignalingNaN = floatx80_is_signaling_nan( b ); - if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR); + if (aIsSignalingNaN | bIsSignalingNaN) { + float_raise(float_flag_invalid, status); + } - if ( STATUS(default_nan_mode) ) { + if (status->default_nan_mode) { a.low = floatx80_default_nan_low; a.high = floatx80_default_nan_high; return a; @@ -1031,13 +1098,12 @@ int float128_is_signaling_nan(float128 a_) int float128_is_quiet_nan( float128 a ) { #if SNAN_BIT_IS_ONE - return - ( ( ( a.high>>47 ) & 0xFFFF ) == 0xFFFE ) - && ( a.low || ( a.high & LIT64( 0x00007FFFFFFFFFFF ) ) ); + return (((a.high >> 47) & 0xffff) == 0xfffe) + && (a.low || (a.high & 0x00007fffffffffffULL)); #else return - ( LIT64( 0xFFFE000000000000 ) <= (uint64_t) ( a.high<<1 ) ) - && ( a.low || ( a.high & LIT64( 0x0000FFFFFFFFFFFF ) ) ); + ((a.high << 1) >= 0xffff000000000000ULL) + && (a.low || (a.high & 0x0000ffffffffffffULL)); #endif } @@ -1050,8 +1116,8 @@ int float128_is_signaling_nan( float128 a ) { #if SNAN_BIT_IS_ONE return - ( LIT64( 0xFFFE000000000000 ) <= (uint64_t) ( a.high<<1 ) ) - && ( a.low || ( a.high & LIT64( 0x0000FFFFFFFFFFFF ) ) ); + ((a.high << 1) >= 0xffff000000000000ULL) + && (a.low || (a.high & 0x0000ffffffffffffULL)); #else return ( ( ( a.high>>47 ) & 0xFFFF ) == 0xFFFE ) @@ -1089,11 +1155,13 @@ float128 float128_maybe_silence_nan( float128 a ) | exception is raised. *----------------------------------------------------------------------------*/ -static commonNaNT float128ToCommonNaN( float128 a STATUS_PARAM) +static commonNaNT float128ToCommonNaN(float128 a, float_status *status) { commonNaNT z; - if ( float128_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR); + if (float128_is_signaling_nan(a)) { + float_raise(float_flag_invalid, status); + } z.sign = a.high>>63; shortShift128Left( a.high, a.low, 16, &z.high, &z.low ); return z; @@ -1104,11 +1172,11 @@ static commonNaNT float128ToCommonNaN( float128 a STATUS_PARAM) | precision floating-point format. *----------------------------------------------------------------------------*/ -static float128 commonNaNToFloat128( commonNaNT a STATUS_PARAM) +static float128 commonNaNToFloat128(commonNaNT a, float_status *status) { float128 z; - if ( STATUS(default_nan_mode) ) { + if (status->default_nan_mode) { z.low = float128_default_nan_low; z.high = float128_default_nan_high; return z; @@ -1125,7 +1193,8 @@ static float128 commonNaNToFloat128( commonNaNT a STATUS_PARAM) | `b' is a signaling NaN, the invalid exception is raised. *----------------------------------------------------------------------------*/ -static float128 propagateFloat128NaN( float128 a, float128 b STATUS_PARAM) +static float128 propagateFloat128NaN(float128 a, float128 b, + float_status *status) { flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN; flag aIsLargerSignificand; @@ -1135,9 +1204,11 @@ static float128 propagateFloat128NaN( float128 a, float128 b STATUS_PARAM) bIsQuietNaN = float128_is_quiet_nan( b ); bIsSignalingNaN = float128_is_signaling_nan( b ); - if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR); + if (aIsSignalingNaN | bIsSignalingNaN) { + float_raise(float_flag_invalid, status); + } - if ( STATUS(default_nan_mode) ) { + if (status->default_nan_mode) { a.low = float128_default_nan_low; a.high = float128_default_nan_high; return a; diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 16b21eb..f1170fe 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -1,13 +1,24 @@ /* * QEMU float support * - * Derived from SoftFloat. + * The code in this source file is derived from release 2a of the SoftFloat + * IEC/IEEE Floating-point Arithmetic Package. Those parts of the code (and + * some later contributions) are provided under that license, as detailed below. + * It has subsequently been modified by contributors to the QEMU Project, + * so some portions are provided under: + * the SoftFloat-2a license + * the BSD license + * GPL-v2-or-later + * + * Any future contributions to this file after December 1st 2014 will be + * taken to be licensed under the Softfloat-2a license unless specifically + * indicated otherwise. */ -/*============================================================================ - -This C source file is part of the SoftFloat IEC/IEEE Floating-point Arithmetic -Package, Release 2b. +/* +=============================================================================== +This C source file is part of the SoftFloat IEC/IEEE Floating-point +Arithmetic Package, Release 2a. Written by John R. Hauser. This work was made possible in part by the International Computer Science Institute, located at Suite 600, 1947 Center @@ -16,24 +27,57 @@ National Science Foundation under grant MIP-9311980. The original version of this code was written as part of a project to build a fixed-point vector processor in collaboration with the University of California at Berkeley, overseen by Profs. Nelson Morgan and John Wawrzynek. More information -is available through the Web page `http://www.cs.berkeley.edu/~jhauser/ +is available through the Web page `http://HTTP.CS.Berkeley.EDU/~jhauser/ arithmetic/SoftFloat.html'. -THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort has -been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES -RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS -AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES, -COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE -EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE -INSTITUTE (possibly via similar legal warning) AGAINST ALL LOSSES, COSTS, OR -OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE. +THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort +has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT +TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO +PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY +AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE. Derivative works are acceptable, even for commercial purposes, so long as -(1) the source code for the derivative work includes prominent notice that -the work is derivative, and (2) the source code includes prominent notice with -these four paragraphs for those parts of this code that are retained. +(1) they include prominent notice that the work is derivative, and (2) they +include prominent notice akin to these four paragraphs for those parts of +this code that are retained. + +=============================================================================== +*/ + +/* BSD licensing: + * Copyright (c) 2006, Fabrice Bellard + * 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. + * + * 3. Neither the name of the copyright holder 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 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. + */ -=============================================================================*/ +/* Portions of this work are licensed under the terms of the GNU GPL, + * version 2 or later. See the COPYING file in the top-level directory. + */ /* softfloat (and in particular the code in softfloat-specialize.h) is * target-dependent and needs the TARGET_* macros. @@ -100,14 +144,14 @@ static inline flag extractFloat16Sign(float16 a) | positive or negative integer is returned. *----------------------------------------------------------------------------*/ -static int32 roundAndPackInt32( flag zSign, uint64_t absZ STATUS_PARAM) +static int32 roundAndPackInt32(flag zSign, uint64_t absZ, float_status *status) { int8 roundingMode; flag roundNearestEven; int8 roundIncrement, roundBits; int32_t z; - roundingMode = STATUS(float_rounding_mode); + roundingMode = status->float_rounding_mode; roundNearestEven = ( roundingMode == float_round_nearest_even ); switch (roundingMode) { case float_round_nearest_even: @@ -132,10 +176,12 @@ static int32 roundAndPackInt32( flag zSign, uint64_t absZ STATUS_PARAM) z = absZ; if ( zSign ) z = - z; if ( ( absZ>>32 ) || ( z && ( ( z < 0 ) ^ zSign ) ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return zSign ? (int32_t) 0x80000000 : 0x7FFFFFFF; } - if ( roundBits ) STATUS(float_exception_flags) |= float_flag_inexact; + if (roundBits) { + status->float_exception_flags |= float_flag_inexact; + } return z; } @@ -152,13 +198,14 @@ static int32 roundAndPackInt32( flag zSign, uint64_t absZ STATUS_PARAM) | returned. *----------------------------------------------------------------------------*/ -static int64 roundAndPackInt64( flag zSign, uint64_t absZ0, uint64_t absZ1 STATUS_PARAM) +static int64 roundAndPackInt64(flag zSign, uint64_t absZ0, uint64_t absZ1, + float_status *status) { int8 roundingMode; flag roundNearestEven, increment; int64_t z; - roundingMode = STATUS(float_rounding_mode); + roundingMode = status->float_rounding_mode; roundNearestEven = ( roundingMode == float_round_nearest_even ); switch (roundingMode) { case float_round_nearest_even: @@ -186,12 +233,14 @@ static int64 roundAndPackInt64( flag zSign, uint64_t absZ0, uint64_t absZ1 STATU if ( zSign ) z = - z; if ( z && ( ( z < 0 ) ^ zSign ) ) { overflow: - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return zSign ? (int64_t) LIT64( 0x8000000000000000 ) : LIT64( 0x7FFFFFFFFFFFFFFF ); } - if ( absZ1 ) STATUS(float_exception_flags) |= float_flag_inexact; + if (absZ1) { + status->float_exception_flags |= float_flag_inexact; + } return z; } @@ -207,12 +256,12 @@ static int64 roundAndPackInt64( flag zSign, uint64_t absZ0, uint64_t absZ1 STATU *----------------------------------------------------------------------------*/ static int64 roundAndPackUint64(flag zSign, uint64_t absZ0, - uint64_t absZ1 STATUS_PARAM) + uint64_t absZ1, float_status *status) { int8 roundingMode; flag roundNearestEven, increment; - roundingMode = STATUS(float_rounding_mode); + roundingMode = status->float_rounding_mode; roundNearestEven = (roundingMode == float_round_nearest_even); switch (roundingMode) { case float_round_nearest_even: @@ -234,19 +283,19 @@ static int64 roundAndPackUint64(flag zSign, uint64_t absZ0, if (increment) { ++absZ0; if (absZ0 == 0) { - float_raise(float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return LIT64(0xFFFFFFFFFFFFFFFF); } absZ0 &= ~(((uint64_t)(absZ1<<1) == 0) & roundNearestEven); } if (zSign && absZ0) { - float_raise(float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return 0; } if (absZ1) { - STATUS(float_exception_flags) |= float_flag_inexact; + status->float_exception_flags |= float_flag_inexact; } return absZ0; } @@ -288,11 +337,11 @@ static inline flag extractFloat32Sign( float32 a ) | If `a' is denormal and we are in flush-to-zero mode then set the | input-denormal exception and return zero. Otherwise just return the value. *----------------------------------------------------------------------------*/ -float32 float32_squash_input_denormal(float32 a STATUS_PARAM) +float32 float32_squash_input_denormal(float32 a, float_status *status) { - if (STATUS(flush_inputs_to_zero)) { + if (status->flush_inputs_to_zero) { if (extractFloat32Exp(a) == 0 && extractFloat32Frac(a) != 0) { - float_raise(float_flag_input_denormal STATUS_VAR); + float_raise(float_flag_input_denormal, status); return make_float32(float32_val(a) & 0x80000000); } } @@ -358,14 +407,15 @@ static inline float32 packFloat32(flag zSign, int_fast16_t zExp, uint32_t zSig) | Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -static float32 roundAndPackFloat32(flag zSign, int_fast16_t zExp, uint32_t zSig STATUS_PARAM) +static float32 roundAndPackFloat32(flag zSign, int_fast16_t zExp, uint32_t zSig, + float_status *status) { int8 roundingMode; flag roundNearestEven; int8 roundIncrement, roundBits; flag isTiny; - roundingMode = STATUS(float_rounding_mode); + roundingMode = status->float_rounding_mode; roundNearestEven = ( roundingMode == float_round_nearest_even ); switch (roundingMode) { case float_round_nearest_even: @@ -391,25 +441,30 @@ static float32 roundAndPackFloat32(flag zSign, int_fast16_t zExp, uint32_t zSig || ( ( zExp == 0xFD ) && ( (int32_t) ( zSig + roundIncrement ) < 0 ) ) ) { - float_raise( float_flag_overflow | float_flag_inexact STATUS_VAR); + float_raise(float_flag_overflow | float_flag_inexact, status); return packFloat32( zSign, 0xFF, - ( roundIncrement == 0 )); } if ( zExp < 0 ) { - if (STATUS(flush_to_zero)) { - float_raise(float_flag_output_denormal STATUS_VAR); + if (status->flush_to_zero) { + float_raise(float_flag_output_denormal, status); return packFloat32(zSign, 0, 0); } isTiny = - ( STATUS(float_detect_tininess) == float_tininess_before_rounding ) + (status->float_detect_tininess + == float_tininess_before_rounding) || ( zExp < -1 ) || ( zSig + roundIncrement < 0x80000000 ); shift32RightJamming( zSig, - zExp, &zSig ); zExp = 0; roundBits = zSig & 0x7F; - if ( isTiny && roundBits ) float_raise( float_flag_underflow STATUS_VAR); + if (isTiny && roundBits) { + float_raise(float_flag_underflow, status); + } } } - if ( roundBits ) STATUS(float_exception_flags) |= float_flag_inexact; + if (roundBits) { + status->float_exception_flags |= float_flag_inexact; + } zSig = ( zSig + roundIncrement )>>7; zSig &= ~ ( ( ( roundBits ^ 0x40 ) == 0 ) & roundNearestEven ); if ( zSig == 0 ) zExp = 0; @@ -427,12 +482,14 @@ static float32 roundAndPackFloat32(flag zSign, int_fast16_t zExp, uint32_t zSig *----------------------------------------------------------------------------*/ static float32 - normalizeRoundAndPackFloat32(flag zSign, int_fast16_t zExp, uint32_t zSig STATUS_PARAM) + normalizeRoundAndPackFloat32(flag zSign, int_fast16_t zExp, uint32_t zSig, + float_status *status) { int8 shiftCount; shiftCount = countLeadingZeros32( zSig ) - 1; - return roundAndPackFloat32( zSign, zExp - shiftCount, zSig<flush_inputs_to_zero) { if (extractFloat64Exp(a) == 0 && extractFloat64Frac(a) != 0) { - float_raise(float_flag_input_denormal STATUS_VAR); + float_raise(float_flag_input_denormal, status); return make_float64(float64_val(a) & (1ULL << 63)); } } @@ -529,9 +586,9 @@ static inline float64 packFloat64(flag zSign, int_fast16_t zExp, uint64_t zSig) | the inexact exception raised if the abstract input cannot be represented | exactly. However, if the abstract value is too large, the overflow and | inexact exceptions are raised and an infinity or maximal finite value is -| returned. If the abstract value is too small, the input value is rounded -| to a subnormal number, and the underflow and inexact exceptions are raised -| if the abstract input cannot be represented exactly as a subnormal double- +| returned. If the abstract value is too small, the input value is rounded to +| a subnormal number, and the underflow and inexact exceptions are raised if +| the abstract input cannot be represented exactly as a subnormal double- | precision floating-point number. | The input significand `zSig' has its binary point between bits 62 | and 61, which is 10 bits to the left of the usual location. This shifted @@ -543,14 +600,15 @@ static inline float64 packFloat64(flag zSign, int_fast16_t zExp, uint64_t zSig) | Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -static float64 roundAndPackFloat64(flag zSign, int_fast16_t zExp, uint64_t zSig STATUS_PARAM) +static float64 roundAndPackFloat64(flag zSign, int_fast16_t zExp, uint64_t zSig, + float_status *status) { int8 roundingMode; flag roundNearestEven; int_fast16_t roundIncrement, roundBits; flag isTiny; - roundingMode = STATUS(float_rounding_mode); + roundingMode = status->float_rounding_mode; roundNearestEven = ( roundingMode == float_round_nearest_even ); switch (roundingMode) { case float_round_nearest_even: @@ -575,25 +633,30 @@ static float64 roundAndPackFloat64(flag zSign, int_fast16_t zExp, uint64_t zSig || ( ( zExp == 0x7FD ) && ( (int64_t) ( zSig + roundIncrement ) < 0 ) ) ) { - float_raise( float_flag_overflow | float_flag_inexact STATUS_VAR); + float_raise(float_flag_overflow | float_flag_inexact, status); return packFloat64( zSign, 0x7FF, - ( roundIncrement == 0 )); } if ( zExp < 0 ) { - if (STATUS(flush_to_zero)) { - float_raise(float_flag_output_denormal STATUS_VAR); + if (status->flush_to_zero) { + float_raise(float_flag_output_denormal, status); return packFloat64(zSign, 0, 0); } isTiny = - ( STATUS(float_detect_tininess) == float_tininess_before_rounding ) + (status->float_detect_tininess + == float_tininess_before_rounding) || ( zExp < -1 ) || ( zSig + roundIncrement < LIT64( 0x8000000000000000 ) ); shift64RightJamming( zSig, - zExp, &zSig ); zExp = 0; roundBits = zSig & 0x3FF; - if ( isTiny && roundBits ) float_raise( float_flag_underflow STATUS_VAR); + if (isTiny && roundBits) { + float_raise(float_flag_underflow, status); + } } } - if ( roundBits ) STATUS(float_exception_flags) |= float_flag_inexact; + if (roundBits) { + status->float_exception_flags |= float_flag_inexact; + } zSig = ( zSig + roundIncrement )>>10; zSig &= ~ ( ( ( roundBits ^ 0x200 ) == 0 ) & roundNearestEven ); if ( zSig == 0 ) zExp = 0; @@ -611,12 +674,14 @@ static float64 roundAndPackFloat64(flag zSign, int_fast16_t zExp, uint64_t zSig *----------------------------------------------------------------------------*/ static float64 - normalizeRoundAndPackFloat64(flag zSign, int_fast16_t zExp, uint64_t zSig STATUS_PARAM) + normalizeRoundAndPackFloat64(flag zSign, int_fast16_t zExp, uint64_t zSig, + float_status *status) { int8 shiftCount; shiftCount = countLeadingZeros64( zSig ) - 1; - return roundAndPackFloat64( zSign, zExp - shiftCount, zSig<float_rounding_mode; roundNearestEven = ( roundingMode == float_round_nearest_even ); if ( roundingPrecision == 80 ) goto precision80; if ( roundingPrecision == 64 ) { @@ -761,19 +825,24 @@ static floatx80 goto overflow; } if ( zExp <= 0 ) { - if (STATUS(flush_to_zero)) { - float_raise(float_flag_output_denormal STATUS_VAR); + if (status->flush_to_zero) { + float_raise(float_flag_output_denormal, status); return packFloatx80(zSign, 0, 0); } isTiny = - ( STATUS(float_detect_tininess) == float_tininess_before_rounding ) + (status->float_detect_tininess + == float_tininess_before_rounding) || ( zExp < 0 ) || ( zSig0 <= zSig0 + roundIncrement ); shift64RightJamming( zSig0, 1 - zExp, &zSig0 ); zExp = 0; roundBits = zSig0 & roundMask; - if ( isTiny && roundBits ) float_raise( float_flag_underflow STATUS_VAR); - if ( roundBits ) STATUS(float_exception_flags) |= float_flag_inexact; + if (isTiny && roundBits) { + float_raise(float_flag_underflow, status); + } + if (roundBits) { + status->float_exception_flags |= float_flag_inexact; + } zSig0 += roundIncrement; if ( (int64_t) zSig0 < 0 ) zExp = 1; roundIncrement = roundMask + 1; @@ -784,7 +853,9 @@ static floatx80 return packFloatx80( zSign, zExp, zSig0 ); } } - if ( roundBits ) STATUS(float_exception_flags) |= float_flag_inexact; + if (roundBits) { + status->float_exception_flags |= float_flag_inexact; + } zSig0 += roundIncrement; if ( zSig0 < roundIncrement ) { ++zExp; @@ -824,7 +895,7 @@ static floatx80 ) { roundMask = 0; overflow: - float_raise( float_flag_overflow | float_flag_inexact STATUS_VAR); + float_raise(float_flag_overflow | float_flag_inexact, status); if ( ( roundingMode == float_round_to_zero ) || ( zSign && ( roundingMode == float_round_up ) ) || ( ! zSign && ( roundingMode == float_round_down ) ) @@ -835,14 +906,19 @@ static floatx80 } if ( zExp <= 0 ) { isTiny = - ( STATUS(float_detect_tininess) == float_tininess_before_rounding ) + (status->float_detect_tininess + == float_tininess_before_rounding) || ( zExp < 0 ) || ! increment || ( zSig0 < LIT64( 0xFFFFFFFFFFFFFFFF ) ); shift64ExtraRightJamming( zSig0, zSig1, 1 - zExp, &zSig0, &zSig1 ); zExp = 0; - if ( isTiny && zSig1 ) float_raise( float_flag_underflow STATUS_VAR); - if ( zSig1 ) STATUS(float_exception_flags) |= float_flag_inexact; + if (isTiny && zSig1) { + float_raise(float_flag_underflow, status); + } + if (zSig1) { + status->float_exception_flags |= float_flag_inexact; + } switch (roundingMode) { case float_round_nearest_even: case float_round_ties_away: @@ -869,7 +945,9 @@ static floatx80 return packFloatx80( zSign, zExp, zSig0 ); } } - if ( zSig1 ) STATUS(float_exception_flags) |= float_flag_inexact; + if (zSig1) { + status->float_exception_flags |= float_flag_inexact; + } if ( increment ) { ++zSig0; if ( zSig0 == 0 ) { @@ -896,10 +974,10 @@ static floatx80 | normalized. *----------------------------------------------------------------------------*/ -static floatx80 - normalizeRoundAndPackFloatx80( - int8 roundingPrecision, flag zSign, int32 zExp, uint64_t zSig0, uint64_t zSig1 - STATUS_PARAM) +static floatx80 normalizeRoundAndPackFloatx80(int8 roundingPrecision, + flag zSign, int32 zExp, + uint64_t zSig0, uint64_t zSig1, + float_status *status) { int8 shiftCount; @@ -911,8 +989,8 @@ static floatx80 shiftCount = countLeadingZeros64( zSig0 ); shortShift128Left( zSig0, zSig1, shiftCount, &zSig0, &zSig1 ); zExp -= shiftCount; - return - roundAndPackFloatx80( roundingPrecision, zSign, zExp, zSig0, zSig1 STATUS_VAR); + return roundAndPackFloatx80(roundingPrecision, zSign, zExp, + zSig0, zSig1, status); } @@ -1049,14 +1127,14 @@ static inline float128 | overflow follows the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -static float128 - roundAndPackFloat128( - flag zSign, int32 zExp, uint64_t zSig0, uint64_t zSig1, uint64_t zSig2 STATUS_PARAM) +static float128 roundAndPackFloat128(flag zSign, int32 zExp, + uint64_t zSig0, uint64_t zSig1, + uint64_t zSig2, float_status *status) { int8 roundingMode; flag roundNearestEven, increment, isTiny; - roundingMode = STATUS(float_rounding_mode); + roundingMode = status->float_rounding_mode; roundNearestEven = ( roundingMode == float_round_nearest_even ); switch (roundingMode) { case float_round_nearest_even: @@ -1087,7 +1165,7 @@ static float128 && increment ) ) { - float_raise( float_flag_overflow | float_flag_inexact STATUS_VAR); + float_raise(float_flag_overflow | float_flag_inexact, status); if ( ( roundingMode == float_round_to_zero ) || ( zSign && ( roundingMode == float_round_up ) ) || ( ! zSign && ( roundingMode == float_round_down ) ) @@ -1103,12 +1181,13 @@ static float128 return packFloat128( zSign, 0x7FFF, 0, 0 ); } if ( zExp < 0 ) { - if (STATUS(flush_to_zero)) { - float_raise(float_flag_output_denormal STATUS_VAR); + if (status->flush_to_zero) { + float_raise(float_flag_output_denormal, status); return packFloat128(zSign, 0, 0, 0); } isTiny = - ( STATUS(float_detect_tininess) == float_tininess_before_rounding ) + (status->float_detect_tininess + == float_tininess_before_rounding) || ( zExp < -1 ) || ! increment || lt128( @@ -1120,7 +1199,9 @@ static float128 shift128ExtraRightJamming( zSig0, zSig1, zSig2, - zExp, &zSig0, &zSig1, &zSig2 ); zExp = 0; - if ( isTiny && zSig2 ) float_raise( float_flag_underflow STATUS_VAR); + if (isTiny && zSig2) { + float_raise(float_flag_underflow, status); + } switch (roundingMode) { case float_round_nearest_even: case float_round_ties_away: @@ -1140,7 +1221,9 @@ static float128 } } } - if ( zSig2 ) STATUS(float_exception_flags) |= float_flag_inexact; + if (zSig2) { + status->float_exception_flags |= float_flag_inexact; + } if ( increment ) { add128( zSig0, zSig1, 0, 1, &zSig0, &zSig1 ); zSig1 &= ~ ( ( zSig2 + zSig2 == 0 ) & roundNearestEven ); @@ -1162,9 +1245,9 @@ static float128 | point exponent. *----------------------------------------------------------------------------*/ -static float128 - normalizeRoundAndPackFloat128( - flag zSign, int32 zExp, uint64_t zSig0, uint64_t zSig1 STATUS_PARAM) +static float128 normalizeRoundAndPackFloat128(flag zSign, int32 zExp, + uint64_t zSig0, uint64_t zSig1, + float_status *status) { int8 shiftCount; uint64_t zSig2; @@ -1184,7 +1267,7 @@ static float128 zSig0, zSig1, 0, - shiftCount, &zSig0, &zSig1, &zSig2 ); } zExp -= shiftCount; - return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 STATUS_VAR); + return roundAndPackFloat128(zSign, zExp, zSig0, zSig1, zSig2, status); } @@ -1194,15 +1277,14 @@ static float128 | according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -float32 int32_to_float32(int32_t a STATUS_PARAM) +float32 int32_to_float32(int32_t a, float_status *status) { flag zSign; if ( a == 0 ) return float32_zero; if ( a == (int32_t) 0x80000000 ) return packFloat32( 1, 0x9E, 0 ); zSign = ( a < 0 ); - return normalizeRoundAndPackFloat32( zSign, 0x9C, zSign ? - a : a STATUS_VAR ); - + return normalizeRoundAndPackFloat32(zSign, 0x9C, zSign ? -a : a, status); } /*---------------------------------------------------------------------------- @@ -1211,7 +1293,7 @@ float32 int32_to_float32(int32_t a STATUS_PARAM) | according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -float64 int32_to_float64(int32_t a STATUS_PARAM) +float64 int32_to_float64(int32_t a, float_status *status) { flag zSign; uint32 absA; @@ -1234,7 +1316,7 @@ float64 int32_to_float64(int32_t a STATUS_PARAM) | Arithmetic. *----------------------------------------------------------------------------*/ -floatx80 int32_to_floatx80(int32_t a STATUS_PARAM) +floatx80 int32_to_floatx80(int32_t a, float_status *status) { flag zSign; uint32 absA; @@ -1256,7 +1338,7 @@ floatx80 int32_to_floatx80(int32_t a STATUS_PARAM) | according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -float128 int32_to_float128(int32_t a STATUS_PARAM) +float128 int32_to_float128(int32_t a, float_status *status) { flag zSign; uint32 absA; @@ -1278,7 +1360,7 @@ float128 int32_to_float128(int32_t a STATUS_PARAM) | according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -float32 int64_to_float32(int64_t a STATUS_PARAM) +float32 int64_to_float32(int64_t a, float_status *status) { flag zSign; uint64 absA; @@ -1299,39 +1381,18 @@ float32 int64_to_float32(int64_t a STATUS_PARAM) else { absA <<= shiftCount; } - return roundAndPackFloat32( zSign, 0x9C - shiftCount, absA STATUS_VAR ); + return roundAndPackFloat32(zSign, 0x9C - shiftCount, absA, status); } } -float32 uint64_to_float32(uint64_t a STATUS_PARAM) -{ - int8 shiftCount; - - if ( a == 0 ) return float32_zero; - shiftCount = countLeadingZeros64( a ) - 40; - if ( 0 <= shiftCount ) { - return packFloat32(0, 0x95 - shiftCount, a<= 0) { + return packFloat32(0, 0x95 - shiftcount, a << shiftcount); + } + /* Otherwise we need to do a round-and-pack. roundAndPackFloat32() + * expects the binary point between bits 30 and 29, hence the + 7. + */ + shiftcount += 7; + if (shiftcount < 0) { + shift64RightJamming(a, -shiftcount, &a); + } else { + a <<= shiftcount; + } + + return roundAndPackFloat32(0, 0x9c - shiftcount, a, status); +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the 64-bit unsigned integer `a' +| to the double-precision floating-point format. The conversion is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float64 uint64_to_float64(uint64_t a, float_status *status) +{ + int exp = 0x43C; + int shiftcount; + + if (a == 0) { + return float64_zero; + } + + shiftcount = countLeadingZeros64(a) - 1; + if (shiftcount < 0) { + shift64RightJamming(a, -shiftcount, &a); + } else { + a <<= shiftcount; + } + return roundAndPackFloat64(0, exp - shiftcount, a, status); +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the 64-bit unsigned integer `a' +| to the quadruple-precision floating-point format. The conversion is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float128 uint64_to_float128(uint64_t a, float_status *status) { if (a == 0) { return float128_zero; } - return normalizeRoundAndPackFloat128(0, 0x406E, a, 0 STATUS_VAR); + return normalizeRoundAndPackFloat128(0, 0x406E, a, 0, status); } /*---------------------------------------------------------------------------- @@ -1430,14 +1541,14 @@ float128 uint64_to_float128(uint64_t a STATUS_PARAM) | largest integer with the same sign as `a' is returned. *----------------------------------------------------------------------------*/ -int32 float32_to_int32( float32 a STATUS_PARAM ) +int32 float32_to_int32(float32 a, float_status *status) { flag aSign; int_fast16_t aExp, shiftCount; uint32_t aSig; uint64_t aSig64; - a = float32_squash_input_denormal(a STATUS_VAR); + a = float32_squash_input_denormal(a, status); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); aSign = extractFloat32Sign( a ); @@ -1447,7 +1558,7 @@ int32 float32_to_int32( float32 a STATUS_PARAM ) aSig64 = aSig; aSig64 <<= 32; if ( 0 < shiftCount ) shift64RightJamming( aSig64, shiftCount, &aSig64 ); - return roundAndPackInt32( aSign, aSig64 STATUS_VAR ); + return roundAndPackInt32(aSign, aSig64, status); } @@ -1461,13 +1572,13 @@ int32 float32_to_int32( float32 a STATUS_PARAM ) | returned. *----------------------------------------------------------------------------*/ -int32 float32_to_int32_round_to_zero( float32 a STATUS_PARAM ) +int32 float32_to_int32_round_to_zero(float32 a, float_status *status) { flag aSign; int_fast16_t aExp, shiftCount; uint32_t aSig; int32_t z; - a = float32_squash_input_denormal(a STATUS_VAR); + a = float32_squash_input_denormal(a, status); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); @@ -1475,19 +1586,21 @@ int32 float32_to_int32_round_to_zero( float32 a STATUS_PARAM ) shiftCount = aExp - 0x9E; if ( 0 <= shiftCount ) { if ( float32_val(a) != 0xCF000000 ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); if ( ! aSign || ( ( aExp == 0xFF ) && aSig ) ) return 0x7FFFFFFF; } return (int32_t) 0x80000000; } else if ( aExp <= 0x7E ) { - if ( aExp | aSig ) STATUS(float_exception_flags) |= float_flag_inexact; + if (aExp | aSig) { + status->float_exception_flags |= float_flag_inexact; + } return 0; } aSig = ( aSig | 0x00800000 )<<8; z = aSig>>( - shiftCount ); if ( (uint32_t) ( aSig<<( shiftCount & 31 ) ) ) { - STATUS(float_exception_flags) |= float_flag_inexact; + status->float_exception_flags |= float_flag_inexact; } if ( aSign ) z = - z; return z; @@ -1504,7 +1617,7 @@ int32 float32_to_int32_round_to_zero( float32 a STATUS_PARAM ) | returned. *----------------------------------------------------------------------------*/ -int_fast16_t float32_to_int16_round_to_zero(float32 a STATUS_PARAM) +int_fast16_t float32_to_int16_round_to_zero(float32 a, float_status *status) { flag aSign; int_fast16_t aExp, shiftCount; @@ -1517,7 +1630,7 @@ int_fast16_t float32_to_int16_round_to_zero(float32 a STATUS_PARAM) shiftCount = aExp - 0x8E; if ( 0 <= shiftCount ) { if ( float32_val(a) != 0xC7000000 ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); if ( ! aSign || ( ( aExp == 0xFF ) && aSig ) ) { return 0x7FFF; } @@ -1526,7 +1639,7 @@ int_fast16_t float32_to_int16_round_to_zero(float32 a STATUS_PARAM) } else if ( aExp <= 0x7E ) { if ( aExp | aSig ) { - STATUS(float_exception_flags) |= float_flag_inexact; + status->float_exception_flags |= float_flag_inexact; } return 0; } @@ -1534,7 +1647,7 @@ int_fast16_t float32_to_int16_round_to_zero(float32 a STATUS_PARAM) aSig = ( aSig | 0x00800000 )<<8; z = aSig>>( - shiftCount ); if ( (uint32_t) ( aSig<<( shiftCount & 31 ) ) ) { - STATUS(float_exception_flags) |= float_flag_inexact; + status->float_exception_flags |= float_flag_inexact; } if ( aSign ) { z = - z; @@ -1553,20 +1666,20 @@ int_fast16_t float32_to_int16_round_to_zero(float32 a STATUS_PARAM) | largest integer with the same sign as `a' is returned. *----------------------------------------------------------------------------*/ -int64 float32_to_int64( float32 a STATUS_PARAM ) +int64 float32_to_int64(float32 a, float_status *status) { flag aSign; int_fast16_t aExp, shiftCount; uint32_t aSig; uint64_t aSig64, aSigExtra; - a = float32_squash_input_denormal(a STATUS_VAR); + a = float32_squash_input_denormal(a, status); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); aSign = extractFloat32Sign( a ); shiftCount = 0xBE - aExp; if ( shiftCount < 0 ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); if ( ! aSign || ( ( aExp == 0xFF ) && aSig ) ) { return LIT64( 0x7FFFFFFFFFFFFFFF ); } @@ -1576,7 +1689,7 @@ int64 float32_to_int64( float32 a STATUS_PARAM ) aSig64 = aSig; aSig64 <<= 40; shift64ExtraRightJamming( aSig64, 0, shiftCount, &aSig64, &aSigExtra ); - return roundAndPackInt64( aSign, aSig64, aSigExtra STATUS_VAR ); + return roundAndPackInt64(aSign, aSig64, aSigExtra, status); } @@ -1592,19 +1705,19 @@ int64 float32_to_int64( float32 a STATUS_PARAM ) | raise the inexact exception flag. *----------------------------------------------------------------------------*/ -uint64 float32_to_uint64(float32 a STATUS_PARAM) +uint64 float32_to_uint64(float32 a, float_status *status) { flag aSign; int_fast16_t aExp, shiftCount; uint32_t aSig; uint64_t aSig64, aSigExtra; - a = float32_squash_input_denormal(a STATUS_VAR); + a = float32_squash_input_denormal(a, status); aSig = extractFloat32Frac(a); aExp = extractFloat32Exp(a); aSign = extractFloat32Sign(a); if ((aSign) && (aExp > 126)) { - float_raise(float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); if (float32_is_any_nan(a)) { return LIT64(0xFFFFFFFFFFFFFFFF); } else { @@ -1616,14 +1729,14 @@ uint64 float32_to_uint64(float32 a STATUS_PARAM) aSig |= 0x00800000; } if (shiftCount < 0) { - float_raise(float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return LIT64(0xFFFFFFFFFFFFFFFF); } aSig64 = aSig; aSig64 <<= 40; shift64ExtraRightJamming(aSig64, 0, shiftCount, &aSig64, &aSigExtra); - return roundAndPackUint64(aSign, aSig64, aSigExtra STATUS_VAR); + return roundAndPackUint64(aSign, aSig64, aSigExtra, status); } /*---------------------------------------------------------------------------- @@ -1637,12 +1750,12 @@ uint64 float32_to_uint64(float32 a STATUS_PARAM) | not round to zero will raise the inexact flag. *----------------------------------------------------------------------------*/ -uint64 float32_to_uint64_round_to_zero(float32 a STATUS_PARAM) +uint64 float32_to_uint64_round_to_zero(float32 a, float_status *status) { - signed char current_rounding_mode = STATUS(float_rounding_mode); - set_float_rounding_mode(float_round_to_zero STATUS_VAR); - int64_t v = float32_to_uint64(a STATUS_VAR); - set_float_rounding_mode(current_rounding_mode STATUS_VAR); + signed char current_rounding_mode = status->float_rounding_mode; + set_float_rounding_mode(float_round_to_zero, status); + int64_t v = float32_to_uint64(a, status); + set_float_rounding_mode(current_rounding_mode, status); return v; } @@ -1656,14 +1769,14 @@ uint64 float32_to_uint64_round_to_zero(float32 a STATUS_PARAM) | returned. *----------------------------------------------------------------------------*/ -int64 float32_to_int64_round_to_zero( float32 a STATUS_PARAM ) +int64 float32_to_int64_round_to_zero(float32 a, float_status *status) { flag aSign; int_fast16_t aExp, shiftCount; uint32_t aSig; uint64_t aSig64; int64 z; - a = float32_squash_input_denormal(a STATUS_VAR); + a = float32_squash_input_denormal(a, status); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); @@ -1671,7 +1784,7 @@ int64 float32_to_int64_round_to_zero( float32 a STATUS_PARAM ) shiftCount = aExp - 0xBE; if ( 0 <= shiftCount ) { if ( float32_val(a) != 0xDF000000 ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); if ( ! aSign || ( ( aExp == 0xFF ) && aSig ) ) { return LIT64( 0x7FFFFFFFFFFFFFFF ); } @@ -1679,14 +1792,16 @@ int64 float32_to_int64_round_to_zero( float32 a STATUS_PARAM ) return (int64_t) LIT64( 0x8000000000000000 ); } else if ( aExp <= 0x7E ) { - if ( aExp | aSig ) STATUS(float_exception_flags) |= float_flag_inexact; + if (aExp | aSig) { + status->float_exception_flags |= float_flag_inexact; + } return 0; } aSig64 = aSig | 0x00800000; aSig64 <<= 40; z = aSig64>>( - shiftCount ); if ( (uint64_t) ( aSig64<<( shiftCount & 63 ) ) ) { - STATUS(float_exception_flags) |= float_flag_inexact; + status->float_exception_flags |= float_flag_inexact; } if ( aSign ) z = - z; return z; @@ -1700,18 +1815,20 @@ int64 float32_to_int64_round_to_zero( float32 a STATUS_PARAM ) | Arithmetic. *----------------------------------------------------------------------------*/ -float64 float32_to_float64( float32 a STATUS_PARAM ) +float64 float32_to_float64(float32 a, float_status *status) { flag aSign; int_fast16_t aExp; uint32_t aSig; - a = float32_squash_input_denormal(a STATUS_VAR); + a = float32_squash_input_denormal(a, status); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); aSign = extractFloat32Sign( a ); if ( aExp == 0xFF ) { - if ( aSig ) return commonNaNToFloat64( float32ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); + if (aSig) { + return commonNaNToFloat64(float32ToCommonNaN(a, status), status); + } return packFloat64( aSign, 0x7FF, 0 ); } if ( aExp == 0 ) { @@ -1730,18 +1847,20 @@ float64 float32_to_float64( float32 a STATUS_PARAM ) | Arithmetic. *----------------------------------------------------------------------------*/ -floatx80 float32_to_floatx80( float32 a STATUS_PARAM ) +floatx80 float32_to_floatx80(float32 a, float_status *status) { flag aSign; int_fast16_t aExp; uint32_t aSig; - a = float32_squash_input_denormal(a STATUS_VAR); + a = float32_squash_input_denormal(a, status); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); aSign = extractFloat32Sign( a ); if ( aExp == 0xFF ) { - if ( aSig ) return commonNaNToFloatx80( float32ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); + if (aSig) { + return commonNaNToFloatx80(float32ToCommonNaN(a, status), status); + } return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); } if ( aExp == 0 ) { @@ -1760,18 +1879,20 @@ floatx80 float32_to_floatx80( float32 a STATUS_PARAM ) | Arithmetic. *----------------------------------------------------------------------------*/ -float128 float32_to_float128( float32 a STATUS_PARAM ) +float128 float32_to_float128(float32 a, float_status *status) { flag aSign; int_fast16_t aExp; uint32_t aSig; - a = float32_squash_input_denormal(a STATUS_VAR); + a = float32_squash_input_denormal(a, status); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); aSign = extractFloat32Sign( a ); if ( aExp == 0xFF ) { - if ( aSig ) return commonNaNToFloat128( float32ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); + if (aSig) { + return commonNaNToFloat128(float32ToCommonNaN(a, status), status); + } return packFloat128( aSign, 0x7FFF, 0, 0 ); } if ( aExp == 0 ) { @@ -1790,26 +1911,26 @@ float128 float32_to_float128( float32 a STATUS_PARAM ) | Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -float32 float32_round_to_int( float32 a STATUS_PARAM) +float32 float32_round_to_int(float32 a, float_status *status) { flag aSign; int_fast16_t aExp; uint32_t lastBitMask, roundBitsMask; uint32_t z; - a = float32_squash_input_denormal(a STATUS_VAR); + a = float32_squash_input_denormal(a, status); aExp = extractFloat32Exp( a ); if ( 0x96 <= aExp ) { if ( ( aExp == 0xFF ) && extractFloat32Frac( a ) ) { - return propagateFloat32NaN( a, a STATUS_VAR ); + return propagateFloat32NaN(a, a, status); } return a; } if ( aExp <= 0x7E ) { if ( (uint32_t) ( float32_val(a)<<1 ) == 0 ) return a; - STATUS(float_exception_flags) |= float_flag_inexact; + status->float_exception_flags |= float_flag_inexact; aSign = extractFloat32Sign( a ); - switch ( STATUS(float_rounding_mode) ) { + switch (status->float_rounding_mode) { case float_round_nearest_even: if ( ( aExp == 0x7E ) && extractFloat32Frac( a ) ) { return packFloat32( aSign, 0x7F, 0 ); @@ -1831,7 +1952,7 @@ float32 float32_round_to_int( float32 a STATUS_PARAM) lastBitMask <<= 0x96 - aExp; roundBitsMask = lastBitMask - 1; z = float32_val(a); - switch (STATUS(float_rounding_mode)) { + switch (status->float_rounding_mode) { case float_round_nearest_even: z += lastBitMask>>1; if ((z & roundBitsMask) == 0) { @@ -1857,7 +1978,9 @@ float32 float32_round_to_int( float32 a STATUS_PARAM) abort(); } z &= ~ roundBitsMask; - if ( z != float32_val(a) ) STATUS(float_exception_flags) |= float_flag_inexact; + if (z != float32_val(a)) { + status->float_exception_flags |= float_flag_inexact; + } return make_float32(z); } @@ -1870,7 +1993,8 @@ float32 float32_round_to_int( float32 a STATUS_PARAM) | Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -static float32 addFloat32Sigs( float32 a, float32 b, flag zSign STATUS_PARAM) +static float32 addFloat32Sigs(float32 a, float32 b, flag zSign, + float_status *status) { int_fast16_t aExp, bExp, zExp; uint32_t aSig, bSig, zSig; @@ -1885,7 +2009,9 @@ static float32 addFloat32Sigs( float32 a, float32 b, flag zSign STATUS_PARAM) bSig <<= 6; if ( 0 < expDiff ) { if ( aExp == 0xFF ) { - if ( aSig ) return propagateFloat32NaN( a, b STATUS_VAR ); + if (aSig) { + return propagateFloat32NaN(a, b, status); + } return a; } if ( bExp == 0 ) { @@ -1899,7 +2025,9 @@ static float32 addFloat32Sigs( float32 a, float32 b, flag zSign STATUS_PARAM) } else if ( expDiff < 0 ) { if ( bExp == 0xFF ) { - if ( bSig ) return propagateFloat32NaN( a, b STATUS_VAR ); + if (bSig) { + return propagateFloat32NaN(a, b, status); + } return packFloat32( zSign, 0xFF, 0 ); } if ( aExp == 0 ) { @@ -1913,13 +2041,15 @@ static float32 addFloat32Sigs( float32 a, float32 b, flag zSign STATUS_PARAM) } else { if ( aExp == 0xFF ) { - if ( aSig | bSig ) return propagateFloat32NaN( a, b STATUS_VAR ); + if (aSig | bSig) { + return propagateFloat32NaN(a, b, status); + } return a; } if ( aExp == 0 ) { - if (STATUS(flush_to_zero)) { + if (status->flush_to_zero) { if (aSig | bSig) { - float_raise(float_flag_output_denormal STATUS_VAR); + float_raise(float_flag_output_denormal, status); } return packFloat32(zSign, 0, 0); } @@ -1937,7 +2067,7 @@ static float32 addFloat32Sigs( float32 a, float32 b, flag zSign STATUS_PARAM) ++zExp; } roundAndPack: - return roundAndPackFloat32( zSign, zExp, zSig STATUS_VAR ); + return roundAndPackFloat32(zSign, zExp, zSig, status); } @@ -1949,7 +2079,8 @@ static float32 addFloat32Sigs( float32 a, float32 b, flag zSign STATUS_PARAM) | Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -static float32 subFloat32Sigs( float32 a, float32 b, flag zSign STATUS_PARAM) +static float32 subFloat32Sigs(float32 a, float32 b, flag zSign, + float_status *status) { int_fast16_t aExp, bExp, zExp; uint32_t aSig, bSig, zSig; @@ -1965,8 +2096,10 @@ static float32 subFloat32Sigs( float32 a, float32 b, flag zSign STATUS_PARAM) if ( 0 < expDiff ) goto aExpBigger; if ( expDiff < 0 ) goto bExpBigger; if ( aExp == 0xFF ) { - if ( aSig | bSig ) return propagateFloat32NaN( a, b STATUS_VAR ); - float_raise( float_flag_invalid STATUS_VAR); + if (aSig | bSig) { + return propagateFloat32NaN(a, b, status); + } + float_raise(float_flag_invalid, status); return float32_default_nan; } if ( aExp == 0 ) { @@ -1975,10 +2108,12 @@ static float32 subFloat32Sigs( float32 a, float32 b, flag zSign STATUS_PARAM) } if ( bSig < aSig ) goto aBigger; if ( aSig < bSig ) goto bBigger; - return packFloat32( STATUS(float_rounding_mode) == float_round_down, 0, 0 ); + return packFloat32(status->float_rounding_mode == float_round_down, 0, 0); bExpBigger: if ( bExp == 0xFF ) { - if ( bSig ) return propagateFloat32NaN( a, b STATUS_VAR ); + if (bSig) { + return propagateFloat32NaN(a, b, status); + } return packFloat32( zSign ^ 1, 0xFF, 0 ); } if ( aExp == 0 ) { @@ -1996,7 +2131,9 @@ static float32 subFloat32Sigs( float32 a, float32 b, flag zSign STATUS_PARAM) goto normalizeRoundAndPack; aExpBigger: if ( aExp == 0xFF ) { - if ( aSig ) return propagateFloat32NaN( a, b STATUS_VAR ); + if (aSig) { + return propagateFloat32NaN(a, b, status); + } return a; } if ( bExp == 0 ) { @@ -2012,7 +2149,7 @@ static float32 subFloat32Sigs( float32 a, float32 b, flag zSign STATUS_PARAM) zExp = aExp; normalizeRoundAndPack: --zExp; - return normalizeRoundAndPackFloat32( zSign, zExp, zSig STATUS_VAR ); + return normalizeRoundAndPackFloat32(zSign, zExp, zSig, status); } @@ -2022,19 +2159,19 @@ static float32 subFloat32Sigs( float32 a, float32 b, flag zSign STATUS_PARAM) | Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -float32 float32_add( float32 a, float32 b STATUS_PARAM ) +float32 float32_add(float32 a, float32 b, float_status *status) { flag aSign, bSign; - a = float32_squash_input_denormal(a STATUS_VAR); - b = float32_squash_input_denormal(b STATUS_VAR); + a = float32_squash_input_denormal(a, status); + b = float32_squash_input_denormal(b, status); aSign = extractFloat32Sign( a ); bSign = extractFloat32Sign( b ); if ( aSign == bSign ) { - return addFloat32Sigs( a, b, aSign STATUS_VAR); + return addFloat32Sigs(a, b, aSign, status); } else { - return subFloat32Sigs( a, b, aSign STATUS_VAR ); + return subFloat32Sigs(a, b, aSign, status); } } @@ -2045,19 +2182,19 @@ float32 float32_add( float32 a, float32 b STATUS_PARAM ) | for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -float32 float32_sub( float32 a, float32 b STATUS_PARAM ) +float32 float32_sub(float32 a, float32 b, float_status *status) { flag aSign, bSign; - a = float32_squash_input_denormal(a STATUS_VAR); - b = float32_squash_input_denormal(b STATUS_VAR); + a = float32_squash_input_denormal(a, status); + b = float32_squash_input_denormal(b, status); aSign = extractFloat32Sign( a ); bSign = extractFloat32Sign( b ); if ( aSign == bSign ) { - return subFloat32Sigs( a, b, aSign STATUS_VAR ); + return subFloat32Sigs(a, b, aSign, status); } else { - return addFloat32Sigs( a, b, aSign STATUS_VAR ); + return addFloat32Sigs(a, b, aSign, status); } } @@ -2068,7 +2205,7 @@ float32 float32_sub( float32 a, float32 b STATUS_PARAM ) | for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -float32 float32_mul( float32 a, float32 b STATUS_PARAM ) +float32 float32_mul(float32 a, float32 b, float_status *status) { flag aSign, bSign, zSign; int_fast16_t aExp, bExp, zExp; @@ -2076,8 +2213,8 @@ float32 float32_mul( float32 a, float32 b STATUS_PARAM ) uint64_t zSig64; uint32_t zSig; - a = float32_squash_input_denormal(a STATUS_VAR); - b = float32_squash_input_denormal(b STATUS_VAR); + a = float32_squash_input_denormal(a, status); + b = float32_squash_input_denormal(b, status); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); @@ -2088,18 +2225,20 @@ float32 float32_mul( float32 a, float32 b STATUS_PARAM ) zSign = aSign ^ bSign; if ( aExp == 0xFF ) { if ( aSig || ( ( bExp == 0xFF ) && bSig ) ) { - return propagateFloat32NaN( a, b STATUS_VAR ); + return propagateFloat32NaN(a, b, status); } if ( ( bExp | bSig ) == 0 ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return float32_default_nan; } return packFloat32( zSign, 0xFF, 0 ); } if ( bExp == 0xFF ) { - if ( bSig ) return propagateFloat32NaN( a, b STATUS_VAR ); + if (bSig) { + return propagateFloat32NaN(a, b, status); + } if ( ( aExp | aSig ) == 0 ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return float32_default_nan; } return packFloat32( zSign, 0xFF, 0 ); @@ -2121,7 +2260,7 @@ float32 float32_mul( float32 a, float32 b STATUS_PARAM ) zSig <<= 1; --zExp; } - return roundAndPackFloat32( zSign, zExp, zSig STATUS_VAR ); + return roundAndPackFloat32(zSign, zExp, zSig, status); } @@ -2131,13 +2270,13 @@ float32 float32_mul( float32 a, float32 b STATUS_PARAM ) | IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -float32 float32_div( float32 a, float32 b STATUS_PARAM ) +float32 float32_div(float32 a, float32 b, float_status *status) { flag aSign, bSign, zSign; int_fast16_t aExp, bExp, zExp; uint32_t aSig, bSig, zSig; - a = float32_squash_input_denormal(a STATUS_VAR); - b = float32_squash_input_denormal(b STATUS_VAR); + a = float32_squash_input_denormal(a, status); + b = float32_squash_input_denormal(b, status); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); @@ -2147,25 +2286,31 @@ float32 float32_div( float32 a, float32 b STATUS_PARAM ) bSign = extractFloat32Sign( b ); zSign = aSign ^ bSign; if ( aExp == 0xFF ) { - if ( aSig ) return propagateFloat32NaN( a, b STATUS_VAR ); + if (aSig) { + return propagateFloat32NaN(a, b, status); + } if ( bExp == 0xFF ) { - if ( bSig ) return propagateFloat32NaN( a, b STATUS_VAR ); - float_raise( float_flag_invalid STATUS_VAR); + if (bSig) { + return propagateFloat32NaN(a, b, status); + } + float_raise(float_flag_invalid, status); return float32_default_nan; } return packFloat32( zSign, 0xFF, 0 ); } if ( bExp == 0xFF ) { - if ( bSig ) return propagateFloat32NaN( a, b STATUS_VAR ); + if (bSig) { + return propagateFloat32NaN(a, b, status); + } return packFloat32( zSign, 0, 0 ); } if ( bExp == 0 ) { if ( bSig == 0 ) { if ( ( aExp | aSig ) == 0 ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return float32_default_nan; } - float_raise( float_flag_divbyzero STATUS_VAR); + float_raise(float_flag_divbyzero, status); return packFloat32( zSign, 0xFF, 0 ); } normalizeFloat32Subnormal( bSig, &bExp, &bSig ); @@ -2185,7 +2330,7 @@ float32 float32_div( float32 a, float32 b STATUS_PARAM ) if ( ( zSig & 0x3F ) == 0 ) { zSig |= ( (uint64_t) bSig * zSig != ( (uint64_t) aSig )<<32 ); } - return roundAndPackFloat32( zSign, zExp, zSig STATUS_VAR ); + return roundAndPackFloat32(zSign, zExp, zSig, status); } @@ -2195,7 +2340,7 @@ float32 float32_div( float32 a, float32 b STATUS_PARAM ) | according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -float32 float32_rem( float32 a, float32 b STATUS_PARAM ) +float32 float32_rem(float32 a, float32 b, float_status *status) { flag aSign, zSign; int_fast16_t aExp, bExp, expDiff; @@ -2204,8 +2349,8 @@ float32 float32_rem( float32 a, float32 b STATUS_PARAM ) uint64_t aSig64, bSig64, q64; uint32_t alternateASig; int32_t sigMean; - a = float32_squash_input_denormal(a STATUS_VAR); - b = float32_squash_input_denormal(b STATUS_VAR); + a = float32_squash_input_denormal(a, status); + b = float32_squash_input_denormal(b, status); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); @@ -2214,18 +2359,20 @@ float32 float32_rem( float32 a, float32 b STATUS_PARAM ) bExp = extractFloat32Exp( b ); if ( aExp == 0xFF ) { if ( aSig || ( ( bExp == 0xFF ) && bSig ) ) { - return propagateFloat32NaN( a, b STATUS_VAR ); + return propagateFloat32NaN(a, b, status); } - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return float32_default_nan; } if ( bExp == 0xFF ) { - if ( bSig ) return propagateFloat32NaN( a, b STATUS_VAR ); + if (bSig) { + return propagateFloat32NaN(a, b, status); + } return a; } if ( bExp == 0 ) { if ( bSig == 0 ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return float32_default_nan; } normalizeFloat32Subnormal( bSig, &bExp, &bSig ); @@ -2286,8 +2433,7 @@ float32 float32_rem( float32 a, float32 b STATUS_PARAM ) } zSign = ( (int32_t) aSig < 0 ); if ( zSign ) aSig = - aSig; - return normalizeRoundAndPackFloat32( aSign ^ zSign, bExp, aSig STATUS_VAR ); - + return normalizeRoundAndPackFloat32(aSign ^ zSign, bExp, aSig, status); } /*---------------------------------------------------------------------------- @@ -2301,7 +2447,8 @@ float32 float32_rem( float32 a, float32 b STATUS_PARAM ) | externally will flip the sign bit on NaNs.) *----------------------------------------------------------------------------*/ -float32 float32_muladd(float32 a, float32 b, float32 c, int flags STATUS_PARAM) +float32 float32_muladd(float32 a, float32 b, float32 c, int flags, + float_status *status) { flag aSign, bSign, cSign, zSign; int_fast16_t aExp, bExp, cExp, pExp, zExp, expDiff; @@ -2312,9 +2459,9 @@ float32 float32_muladd(float32 a, float32 b, float32 c, int flags STATUS_PARAM) int shiftcount; flag signflip, infzero; - a = float32_squash_input_denormal(a STATUS_VAR); - b = float32_squash_input_denormal(b STATUS_VAR); - c = float32_squash_input_denormal(c STATUS_VAR); + a = float32_squash_input_denormal(a, status); + b = float32_squash_input_denormal(b, status); + c = float32_squash_input_denormal(c, status); aSig = extractFloat32Frac(a); aExp = extractFloat32Exp(a); aSign = extractFloat32Sign(a); @@ -2336,11 +2483,11 @@ float32 float32_muladd(float32 a, float32 b, float32 c, int flags STATUS_PARAM) if (((aExp == 0xff) && aSig) || ((bExp == 0xff) && bSig) || ((cExp == 0xff) && cSig)) { - return propagateFloat32MulAddNaN(a, b, c, infzero STATUS_VAR); + return propagateFloat32MulAddNaN(a, b, c, infzero, status); } if (infzero) { - float_raise(float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return float32_default_nan; } @@ -2361,7 +2508,7 @@ float32 float32_muladd(float32 a, float32 b, float32 c, int flags STATUS_PARAM) if (cExp == 0xff) { if (pInf && (pSign ^ cSign)) { /* addition of opposite-signed infinities => InvalidOperation */ - float_raise(float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return float32_default_nan; } /* Otherwise generate an infinity of the same sign */ @@ -2378,7 +2525,7 @@ float32 float32_muladd(float32 a, float32 b, float32 c, int flags STATUS_PARAM) /* Adding two exact zeroes */ if (pSign == cSign) { zSign = pSign; - } else if (STATUS(float_rounding_mode) == float_round_down) { + } else if (status->float_rounding_mode == float_round_down) { zSign = 1; } else { zSign = 0; @@ -2386,8 +2533,8 @@ float32 float32_muladd(float32 a, float32 b, float32 c, int flags STATUS_PARAM) return packFloat32(zSign ^ signflip, 0, 0); } /* Exact zero plus a denorm */ - if (STATUS(flush_to_zero)) { - float_raise(float_flag_output_denormal STATUS_VAR); + if (status->flush_to_zero) { + float_raise(float_flag_output_denormal, status); return packFloat32(cSign ^ signflip, 0, 0); } } @@ -2401,7 +2548,7 @@ float32 float32_muladd(float32 a, float32 b, float32 c, int flags STATUS_PARAM) */ cExp -= 2; cSig = (cSig | 0x00800000) << 7; - return roundAndPackFloat32(cSign ^ signflip, cExp, cSig STATUS_VAR); + return roundAndPackFloat32(cSign ^ signflip, cExp, cSig, status); } return packFloat32(cSign ^ signflip, cExp, cSig); } @@ -2443,7 +2590,7 @@ float32 float32_muladd(float32 a, float32 b, float32 c, int flags STATUS_PARAM) pExp--; } return roundAndPackFloat32(zSign, pExp - 1, - pSig STATUS_VAR); + pSig, status); } normalizeFloat32Subnormal(cSig, &cExp, &cSig); } @@ -2494,7 +2641,7 @@ float32 float32_muladd(float32 a, float32 b, float32 c, int flags STATUS_PARAM) } else { /* Exact zero */ zSign = signflip; - if (STATUS(float_rounding_mode) == float_round_down) { + if (status->float_rounding_mode == float_round_down) { zSign ^= 1; } return packFloat32(zSign, 0, 0); @@ -2511,7 +2658,7 @@ float32 float32_muladd(float32 a, float32 b, float32 c, int flags STATUS_PARAM) } shift64RightJamming(zSig64, 32, &zSig64); - return roundAndPackFloat32(zSign, zExp, zSig64 STATUS_VAR); + return roundAndPackFloat32(zSign, zExp, zSig64, status); } @@ -2521,26 +2668,28 @@ float32 float32_muladd(float32 a, float32 b, float32 c, int flags STATUS_PARAM) | Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -float32 float32_sqrt( float32 a STATUS_PARAM ) +float32 float32_sqrt(float32 a, float_status *status) { flag aSign; int_fast16_t aExp, zExp; uint32_t aSig, zSig; uint64_t rem, term; - a = float32_squash_input_denormal(a STATUS_VAR); + a = float32_squash_input_denormal(a, status); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); aSign = extractFloat32Sign( a ); if ( aExp == 0xFF ) { - if ( aSig ) return propagateFloat32NaN( a, float32_zero STATUS_VAR ); + if (aSig) { + return propagateFloat32NaN(a, float32_zero, status); + } if ( ! aSign ) return a; - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return float32_default_nan; } if ( aSign ) { if ( ( aExp | aSig ) == 0 ) return a; - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return float32_default_nan; } if ( aExp == 0 ) { @@ -2566,7 +2715,7 @@ float32 float32_sqrt( float32 a STATUS_PARAM ) } shift32RightJamming( zSig, 1, &zSig ); roundAndPack: - return roundAndPackFloat32( 0, zExp, zSig STATUS_VAR ); + return roundAndPackFloat32(0, zExp, zSig, status); } @@ -2607,44 +2756,46 @@ static const float64 float32_exp2_coefficients[15] = const_float64( 0x3d6ae7f3e733b81fll ), /* 15 */ }; -float32 float32_exp2( float32 a STATUS_PARAM ) +float32 float32_exp2(float32 a, float_status *status) { flag aSign; int_fast16_t aExp; uint32_t aSig; float64 r, x, xn; int i; - a = float32_squash_input_denormal(a STATUS_VAR); + a = float32_squash_input_denormal(a, status); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); aSign = extractFloat32Sign( a ); if ( aExp == 0xFF) { - if ( aSig ) return propagateFloat32NaN( a, float32_zero STATUS_VAR ); + if (aSig) { + return propagateFloat32NaN(a, float32_zero, status); + } return (aSign) ? float32_zero : a; } if (aExp == 0) { if (aSig == 0) return float32_one; } - float_raise( float_flag_inexact STATUS_VAR); + float_raise(float_flag_inexact, status); /* ******************************* */ /* using float64 for approximation */ /* ******************************* */ - x = float32_to_float64(a STATUS_VAR); - x = float64_mul(x, float64_ln2 STATUS_VAR); + x = float32_to_float64(a, status); + x = float64_mul(x, float64_ln2, status); xn = x; r = float64_one; for (i = 0 ; i < 15 ; i++) { float64 f; - f = float64_mul(xn, float32_exp2_coefficients[i] STATUS_VAR); - r = float64_add(r, f STATUS_VAR); + f = float64_mul(xn, float32_exp2_coefficients[i], status); + r = float64_add(r, f, status); - xn = float64_mul(xn, x STATUS_VAR); + xn = float64_mul(xn, x, status); } return float64_to_float32(r, status); @@ -2655,13 +2806,13 @@ float32 float32_exp2( float32 a STATUS_PARAM ) | The operation is performed according to the IEC/IEEE Standard for Binary | Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -float32 float32_log2( float32 a STATUS_PARAM ) +float32 float32_log2(float32 a, float_status *status) { flag aSign, zSign; int_fast16_t aExp; uint32_t aSig, zSig, i; - a = float32_squash_input_denormal(a STATUS_VAR); + a = float32_squash_input_denormal(a, status); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); aSign = extractFloat32Sign( a ); @@ -2671,11 +2822,13 @@ float32 float32_log2( float32 a STATUS_PARAM ) normalizeFloat32Subnormal( aSig, &aExp, &aSig ); } if ( aSign ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return float32_default_nan; } if ( aExp == 0xFF ) { - if ( aSig ) return propagateFloat32NaN( a, float32_zero STATUS_VAR ); + if (aSig) { + return propagateFloat32NaN(a, float32_zero, status); + } return a; } @@ -2695,7 +2848,7 @@ float32 float32_log2( float32 a STATUS_PARAM ) if ( zSign ) zSig = -zSig; - return normalizeRoundAndPackFloat32( zSign, 0x85, zSig STATUS_VAR ); + return normalizeRoundAndPackFloat32(zSign, 0x85, zSig, status); } /*---------------------------------------------------------------------------- @@ -2705,16 +2858,16 @@ float32 float32_log2( float32 a STATUS_PARAM ) | according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int float32_eq( float32 a, float32 b STATUS_PARAM ) +int float32_eq(float32 a, float32 b, float_status *status) { uint32_t av, bv; - a = float32_squash_input_denormal(a STATUS_VAR); - b = float32_squash_input_denormal(b STATUS_VAR); + a = float32_squash_input_denormal(a, status); + b = float32_squash_input_denormal(b, status); if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) ) || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return 0; } av = float32_val(a); @@ -2729,17 +2882,17 @@ int float32_eq( float32 a, float32 b STATUS_PARAM ) | according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int float32_le( float32 a, float32 b STATUS_PARAM ) +int float32_le(float32 a, float32 b, float_status *status) { flag aSign, bSign; uint32_t av, bv; - a = float32_squash_input_denormal(a STATUS_VAR); - b = float32_squash_input_denormal(b STATUS_VAR); + a = float32_squash_input_denormal(a, status); + b = float32_squash_input_denormal(b, status); if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) ) || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return 0; } aSign = extractFloat32Sign( a ); @@ -2758,17 +2911,17 @@ int float32_le( float32 a, float32 b STATUS_PARAM ) | to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int float32_lt( float32 a, float32 b STATUS_PARAM ) +int float32_lt(float32 a, float32 b, float_status *status) { flag aSign, bSign; uint32_t av, bv; - a = float32_squash_input_denormal(a STATUS_VAR); - b = float32_squash_input_denormal(b STATUS_VAR); + a = float32_squash_input_denormal(a, status); + b = float32_squash_input_denormal(b, status); if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) ) || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return 0; } aSign = extractFloat32Sign( a ); @@ -2787,15 +2940,15 @@ int float32_lt( float32 a, float32 b STATUS_PARAM ) | Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int float32_unordered( float32 a, float32 b STATUS_PARAM ) +int float32_unordered(float32 a, float32 b, float_status *status) { - a = float32_squash_input_denormal(a STATUS_VAR); - b = float32_squash_input_denormal(b STATUS_VAR); + a = float32_squash_input_denormal(a, status); + b = float32_squash_input_denormal(b, status); if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) ) || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return 1; } return 0; @@ -2808,16 +2961,16 @@ int float32_unordered( float32 a, float32 b STATUS_PARAM ) | for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int float32_eq_quiet( float32 a, float32 b STATUS_PARAM ) +int float32_eq_quiet(float32 a, float32 b, float_status *status) { - a = float32_squash_input_denormal(a STATUS_VAR); - b = float32_squash_input_denormal(b STATUS_VAR); + a = float32_squash_input_denormal(a, status); + b = float32_squash_input_denormal(b, status); if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) ) || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) ) { if ( float32_is_signaling_nan( a ) || float32_is_signaling_nan( b ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); } return 0; } @@ -2832,18 +2985,18 @@ int float32_eq_quiet( float32 a, float32 b STATUS_PARAM ) | IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int float32_le_quiet( float32 a, float32 b STATUS_PARAM ) +int float32_le_quiet(float32 a, float32 b, float_status *status) { flag aSign, bSign; uint32_t av, bv; - a = float32_squash_input_denormal(a STATUS_VAR); - b = float32_squash_input_denormal(b STATUS_VAR); + a = float32_squash_input_denormal(a, status); + b = float32_squash_input_denormal(b, status); if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) ) || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) ) { if ( float32_is_signaling_nan( a ) || float32_is_signaling_nan( b ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); } return 0; } @@ -2863,18 +3016,18 @@ int float32_le_quiet( float32 a, float32 b STATUS_PARAM ) | Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int float32_lt_quiet( float32 a, float32 b STATUS_PARAM ) +int float32_lt_quiet(float32 a, float32 b, float_status *status) { flag aSign, bSign; uint32_t av, bv; - a = float32_squash_input_denormal(a STATUS_VAR); - b = float32_squash_input_denormal(b STATUS_VAR); + a = float32_squash_input_denormal(a, status); + b = float32_squash_input_denormal(b, status); if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) ) || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) ) { if ( float32_is_signaling_nan( a ) || float32_is_signaling_nan( b ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); } return 0; } @@ -2894,16 +3047,16 @@ int float32_lt_quiet( float32 a, float32 b STATUS_PARAM ) | Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int float32_unordered_quiet( float32 a, float32 b STATUS_PARAM ) +int float32_unordered_quiet(float32 a, float32 b, float_status *status) { - a = float32_squash_input_denormal(a STATUS_VAR); - b = float32_squash_input_denormal(b STATUS_VAR); + a = float32_squash_input_denormal(a, status); + b = float32_squash_input_denormal(b, status); if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) ) || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) ) { if ( float32_is_signaling_nan( a ) || float32_is_signaling_nan( b ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); } return 1; } @@ -2920,12 +3073,12 @@ int float32_unordered_quiet( float32 a, float32 b STATUS_PARAM ) | largest integer with the same sign as `a' is returned. *----------------------------------------------------------------------------*/ -int32 float64_to_int32( float64 a STATUS_PARAM ) +int32 float64_to_int32(float64 a, float_status *status) { flag aSign; int_fast16_t aExp, shiftCount; uint64_t aSig; - a = float64_squash_input_denormal(a STATUS_VAR); + a = float64_squash_input_denormal(a, status); aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); @@ -2934,7 +3087,7 @@ int32 float64_to_int32( float64 a STATUS_PARAM ) if ( aExp ) aSig |= LIT64( 0x0010000000000000 ); shiftCount = 0x42C - aExp; if ( 0 < shiftCount ) shift64RightJamming( aSig, shiftCount, &aSig ); - return roundAndPackInt32( aSign, aSig STATUS_VAR ); + return roundAndPackInt32(aSign, aSig, status); } @@ -2948,13 +3101,13 @@ int32 float64_to_int32( float64 a STATUS_PARAM ) | returned. *----------------------------------------------------------------------------*/ -int32 float64_to_int32_round_to_zero( float64 a STATUS_PARAM ) +int32 float64_to_int32_round_to_zero(float64 a, float_status *status) { flag aSign; int_fast16_t aExp, shiftCount; uint64_t aSig, savedASig; int32_t z; - a = float64_squash_input_denormal(a STATUS_VAR); + a = float64_squash_input_denormal(a, status); aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); @@ -2964,7 +3117,9 @@ int32 float64_to_int32_round_to_zero( float64 a STATUS_PARAM ) goto invalid; } else if ( aExp < 0x3FF ) { - if ( aExp || aSig ) STATUS(float_exception_flags) |= float_flag_inexact; + if (aExp || aSig) { + status->float_exception_flags |= float_flag_inexact; + } return 0; } aSig |= LIT64( 0x0010000000000000 ); @@ -2975,11 +3130,11 @@ int32 float64_to_int32_round_to_zero( float64 a STATUS_PARAM ) if ( aSign ) z = - z; if ( ( z < 0 ) ^ aSign ) { invalid: - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return aSign ? (int32_t) 0x80000000 : 0x7FFFFFFF; } if ( ( aSig<float_exception_flags |= float_flag_inexact; } return z; @@ -2995,7 +3150,7 @@ int32 float64_to_int32_round_to_zero( float64 a STATUS_PARAM ) | returned. *----------------------------------------------------------------------------*/ -int_fast16_t float64_to_int16_round_to_zero(float64 a STATUS_PARAM) +int_fast16_t float64_to_int16_round_to_zero(float64 a, float_status *status) { flag aSign; int_fast16_t aExp, shiftCount; @@ -3013,7 +3168,7 @@ int_fast16_t float64_to_int16_round_to_zero(float64 a STATUS_PARAM) } else if ( aExp < 0x3FF ) { if ( aExp || aSig ) { - STATUS(float_exception_flags) |= float_flag_inexact; + status->float_exception_flags |= float_flag_inexact; } return 0; } @@ -3027,11 +3182,11 @@ int_fast16_t float64_to_int16_round_to_zero(float64 a STATUS_PARAM) } if ( ( (int16_t)z < 0 ) ^ aSign ) { invalid: - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return aSign ? (int32_t) 0xffff8000 : 0x7FFF; } if ( ( aSig<float_exception_flags |= float_flag_inexact; } return z; } @@ -3046,12 +3201,12 @@ int_fast16_t float64_to_int16_round_to_zero(float64 a STATUS_PARAM) | largest integer with the same sign as `a' is returned. *----------------------------------------------------------------------------*/ -int64 float64_to_int64( float64 a STATUS_PARAM ) +int64 float64_to_int64(float64 a, float_status *status) { flag aSign; int_fast16_t aExp, shiftCount; uint64_t aSig, aSigExtra; - a = float64_squash_input_denormal(a STATUS_VAR); + a = float64_squash_input_denormal(a, status); aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); @@ -3060,7 +3215,7 @@ int64 float64_to_int64( float64 a STATUS_PARAM ) shiftCount = 0x433 - aExp; if ( shiftCount <= 0 ) { if ( 0x43E < aExp ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); if ( ! aSign || ( ( aExp == 0x7FF ) && ( aSig != LIT64( 0x0010000000000000 ) ) ) @@ -3075,7 +3230,7 @@ int64 float64_to_int64( float64 a STATUS_PARAM ) else { shift64ExtraRightJamming( aSig, 0, shiftCount, &aSig, &aSigExtra ); } - return roundAndPackInt64( aSign, aSig, aSigExtra STATUS_VAR ); + return roundAndPackInt64(aSign, aSig, aSigExtra, status); } @@ -3089,13 +3244,13 @@ int64 float64_to_int64( float64 a STATUS_PARAM ) | returned. *----------------------------------------------------------------------------*/ -int64 float64_to_int64_round_to_zero( float64 a STATUS_PARAM ) +int64 float64_to_int64_round_to_zero(float64 a, float_status *status) { flag aSign; int_fast16_t aExp, shiftCount; uint64_t aSig; int64 z; - a = float64_squash_input_denormal(a STATUS_VAR); + a = float64_squash_input_denormal(a, status); aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); @@ -3105,7 +3260,7 @@ int64 float64_to_int64_round_to_zero( float64 a STATUS_PARAM ) if ( 0 <= shiftCount ) { if ( 0x43E <= aExp ) { if ( float64_val(a) != LIT64( 0xC3E0000000000000 ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); if ( ! aSign || ( ( aExp == 0x7FF ) && ( aSig != LIT64( 0x0010000000000000 ) ) ) @@ -3119,12 +3274,14 @@ int64 float64_to_int64_round_to_zero( float64 a STATUS_PARAM ) } else { if ( aExp < 0x3FE ) { - if ( aExp | aSig ) STATUS(float_exception_flags) |= float_flag_inexact; + if (aExp | aSig) { + status->float_exception_flags |= float_flag_inexact; + } return 0; } z = aSig>>( - shiftCount ); if ( (uint64_t) ( aSig<<( shiftCount & 63 ) ) ) { - STATUS(float_exception_flags) |= float_flag_inexact; + status->float_exception_flags |= float_flag_inexact; } } if ( aSign ) z = - z; @@ -3139,19 +3296,21 @@ int64 float64_to_int64_round_to_zero( float64 a STATUS_PARAM ) | Arithmetic. *----------------------------------------------------------------------------*/ -float32 float64_to_float32( float64 a STATUS_PARAM ) +float32 float64_to_float32(float64 a, float_status *status) { flag aSign; int_fast16_t aExp; uint64_t aSig; uint32_t zSig; - a = float64_squash_input_denormal(a STATUS_VAR); + a = float64_squash_input_denormal(a, status); aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); aSign = extractFloat64Sign( a ); if ( aExp == 0x7FF ) { - if ( aSig ) return commonNaNToFloat32( float64ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); + if (aSig) { + return commonNaNToFloat32(float64ToCommonNaN(a, status), status); + } return packFloat32( aSign, 0xFF, 0 ); } shift64RightJamming( aSig, 22, &aSig ); @@ -3160,7 +3319,7 @@ float32 float64_to_float32( float64 a STATUS_PARAM ) zSig |= 0x40000000; aExp -= 0x381; } - return roundAndPackFloat32( aSign, aExp, zSig STATUS_VAR ); + return roundAndPackFloat32(aSign, aExp, zSig, status); } @@ -3210,7 +3369,8 @@ static float16 packFloat16(flag zSign, int_fast16_t zExp, uint16_t zSig) *----------------------------------------------------------------------------*/ static float32 roundAndPackFloat16(flag zSign, int_fast16_t zExp, - uint32_t zSig, flag ieee STATUS_PARAM) + uint32_t zSig, flag ieee, + float_status *status) { int maxexp = ieee ? 29 : 30; uint32_t mask; @@ -3232,7 +3392,7 @@ static float32 roundAndPackFloat16(flag zSign, int_fast16_t zExp, mask = 0x00001fff; } - switch (STATUS(float_rounding_mode)) { + switch (status->float_rounding_mode) { case float_round_nearest_even: increment = (mask + 1) >> 1; if ((zSig & mask) == increment) { @@ -3257,10 +3417,10 @@ static float32 roundAndPackFloat16(flag zSign, int_fast16_t zExp, if (zExp > maxexp || (zExp == maxexp && rounding_bumps_exp)) { if (ieee) { - float_raise(float_flag_overflow | float_flag_inexact STATUS_VAR); + float_raise(float_flag_overflow | float_flag_inexact, status); return packFloat16(zSign, 0x1f, 0); } else { - float_raise(float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return packFloat16(zSign, 0x1f, 0x3ff); } } @@ -3268,14 +3428,14 @@ static float32 roundAndPackFloat16(flag zSign, int_fast16_t zExp, if (zExp < 0) { /* Note that flush-to-zero does not affect half-precision results */ is_tiny = - (STATUS(float_detect_tininess) == float_tininess_before_rounding) + (status->float_detect_tininess == float_tininess_before_rounding) || (zExp < -1) || (!rounding_bumps_exp); } if (zSig & mask) { - float_raise(float_flag_inexact STATUS_VAR); + float_raise(float_flag_inexact, status); if (is_tiny) { - float_raise(float_flag_underflow STATUS_VAR); + float_raise(float_flag_underflow, status); } } @@ -3306,7 +3466,7 @@ static void normalizeFloat16Subnormal(uint32_t aSig, int_fast16_t *zExpPtr, /* Half precision floats come in two formats: standard IEEE and "ARM" format. The latter gains extra exponent range by omitting the NaN/Inf encodings. */ -float32 float16_to_float32(float16 a, flag ieee STATUS_PARAM) +float32 float16_to_float32(float16 a, flag ieee, float_status *status) { flag aSign; int_fast16_t aExp; @@ -3318,7 +3478,7 @@ float32 float16_to_float32(float16 a, flag ieee STATUS_PARAM) if (aExp == 0x1f && ieee) { if (aSig) { - return commonNaNToFloat32(float16ToCommonNaN(a STATUS_VAR) STATUS_VAR); + return commonNaNToFloat32(float16ToCommonNaN(a, status), status); } return packFloat32(aSign, 0xff, 0); } @@ -3333,13 +3493,13 @@ float32 float16_to_float32(float16 a, flag ieee STATUS_PARAM) return packFloat32( aSign, aExp + 0x70, aSig << 13); } -float16 float32_to_float16(float32 a, flag ieee STATUS_PARAM) +float16 float32_to_float16(float32 a, flag ieee, float_status *status) { flag aSign; int_fast16_t aExp; uint32_t aSig; - a = float32_squash_input_denormal(a STATUS_VAR); + a = float32_squash_input_denormal(a, status); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); @@ -3348,15 +3508,15 @@ float16 float32_to_float16(float32 a, flag ieee STATUS_PARAM) if (aSig) { /* Input is a NaN */ if (!ieee) { - float_raise(float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return packFloat16(aSign, 0, 0); } return commonNaNToFloat16( - float32ToCommonNaN(a STATUS_VAR) STATUS_VAR); + float32ToCommonNaN(a, status), status); } /* Infinity */ if (!ieee) { - float_raise(float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return packFloat16(aSign, 0x1f, 0x3ff); } return packFloat16(aSign, 0x1f, 0); @@ -3374,10 +3534,10 @@ float16 float32_to_float16(float32 a, flag ieee STATUS_PARAM) aSig |= 0x00800000; aExp -= 0x71; - return roundAndPackFloat16(aSign, aExp, aSig, ieee STATUS_VAR); + return roundAndPackFloat16(aSign, aExp, aSig, ieee, status); } -float64 float16_to_float64(float16 a, flag ieee STATUS_PARAM) +float64 float16_to_float64(float16 a, flag ieee, float_status *status) { flag aSign; int_fast16_t aExp; @@ -3390,7 +3550,7 @@ float64 float16_to_float64(float16 a, flag ieee STATUS_PARAM) if (aExp == 0x1f && ieee) { if (aSig) { return commonNaNToFloat64( - float16ToCommonNaN(a STATUS_VAR) STATUS_VAR); + float16ToCommonNaN(a, status), status); } return packFloat64(aSign, 0x7ff, 0); } @@ -3405,14 +3565,14 @@ float64 float16_to_float64(float16 a, flag ieee STATUS_PARAM) return packFloat64(aSign, aExp + 0x3f0, ((uint64_t)aSig) << 42); } -float16 float64_to_float16(float64 a, flag ieee STATUS_PARAM) +float16 float64_to_float16(float64 a, flag ieee, float_status *status) { flag aSign; int_fast16_t aExp; uint64_t aSig; uint32_t zSig; - a = float64_squash_input_denormal(a STATUS_VAR); + a = float64_squash_input_denormal(a, status); aSig = extractFloat64Frac(a); aExp = extractFloat64Exp(a); @@ -3421,15 +3581,15 @@ float16 float64_to_float16(float64 a, flag ieee STATUS_PARAM) if (aSig) { /* Input is a NaN */ if (!ieee) { - float_raise(float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return packFloat16(aSign, 0, 0); } return commonNaNToFloat16( - float64ToCommonNaN(a STATUS_VAR) STATUS_VAR); + float64ToCommonNaN(a, status), status); } /* Infinity */ if (!ieee) { - float_raise(float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return packFloat16(aSign, 0x1f, 0x3ff); } return packFloat16(aSign, 0x1f, 0); @@ -3449,7 +3609,7 @@ float16 float64_to_float16(float64 a, flag ieee STATUS_PARAM) zSig |= 0x00800000; aExp -= 0x3F1; - return roundAndPackFloat16(aSign, aExp, zSig, ieee STATUS_VAR); + return roundAndPackFloat16(aSign, aExp, zSig, ieee, status); } /*---------------------------------------------------------------------------- @@ -3459,18 +3619,20 @@ float16 float64_to_float16(float64 a, flag ieee STATUS_PARAM) | Arithmetic. *----------------------------------------------------------------------------*/ -floatx80 float64_to_floatx80( float64 a STATUS_PARAM ) +floatx80 float64_to_floatx80(float64 a, float_status *status) { flag aSign; int_fast16_t aExp; uint64_t aSig; - a = float64_squash_input_denormal(a STATUS_VAR); + a = float64_squash_input_denormal(a, status); aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); aSign = extractFloat64Sign( a ); if ( aExp == 0x7FF ) { - if ( aSig ) return commonNaNToFloatx80( float64ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); + if (aSig) { + return commonNaNToFloatx80(float64ToCommonNaN(a, status), status); + } return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); } if ( aExp == 0 ) { @@ -3490,18 +3652,20 @@ floatx80 float64_to_floatx80( float64 a STATUS_PARAM ) | Arithmetic. *----------------------------------------------------------------------------*/ -float128 float64_to_float128( float64 a STATUS_PARAM ) +float128 float64_to_float128(float64 a, float_status *status) { flag aSign; int_fast16_t aExp; uint64_t aSig, zSig0, zSig1; - a = float64_squash_input_denormal(a STATUS_VAR); + a = float64_squash_input_denormal(a, status); aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); aSign = extractFloat64Sign( a ); if ( aExp == 0x7FF ) { - if ( aSig ) return commonNaNToFloat128( float64ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); + if (aSig) { + return commonNaNToFloat128(float64ToCommonNaN(a, status), status); + } return packFloat128( aSign, 0x7FFF, 0, 0 ); } if ( aExp == 0 ) { @@ -3521,26 +3685,26 @@ float128 float64_to_float128( float64 a STATUS_PARAM ) | Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -float64 float64_round_to_int( float64 a STATUS_PARAM ) +float64 float64_round_to_int(float64 a, float_status *status) { flag aSign; int_fast16_t aExp; uint64_t lastBitMask, roundBitsMask; uint64_t z; - a = float64_squash_input_denormal(a STATUS_VAR); + a = float64_squash_input_denormal(a, status); aExp = extractFloat64Exp( a ); if ( 0x433 <= aExp ) { if ( ( aExp == 0x7FF ) && extractFloat64Frac( a ) ) { - return propagateFloat64NaN( a, a STATUS_VAR ); + return propagateFloat64NaN(a, a, status); } return a; } if ( aExp < 0x3FF ) { if ( (uint64_t) ( float64_val(a)<<1 ) == 0 ) return a; - STATUS(float_exception_flags) |= float_flag_inexact; + status->float_exception_flags |= float_flag_inexact; aSign = extractFloat64Sign( a ); - switch ( STATUS(float_rounding_mode) ) { + switch (status->float_rounding_mode) { case float_round_nearest_even: if ( ( aExp == 0x3FE ) && extractFloat64Frac( a ) ) { return packFloat64( aSign, 0x3FF, 0 ); @@ -3563,7 +3727,7 @@ float64 float64_round_to_int( float64 a STATUS_PARAM ) lastBitMask <<= 0x433 - aExp; roundBitsMask = lastBitMask - 1; z = float64_val(a); - switch (STATUS(float_rounding_mode)) { + switch (status->float_rounding_mode) { case float_round_nearest_even: z += lastBitMask >> 1; if ((z & roundBitsMask) == 0) { @@ -3589,20 +3753,21 @@ float64 float64_round_to_int( float64 a STATUS_PARAM ) abort(); } z &= ~ roundBitsMask; - if ( z != float64_val(a) ) - STATUS(float_exception_flags) |= float_flag_inexact; + if (z != float64_val(a)) { + status->float_exception_flags |= float_flag_inexact; + } return make_float64(z); } -float64 float64_trunc_to_int( float64 a STATUS_PARAM) +float64 float64_trunc_to_int(float64 a, float_status *status) { int oldmode; float64 res; - oldmode = STATUS(float_rounding_mode); - STATUS(float_rounding_mode) = float_round_to_zero; - res = float64_round_to_int(a STATUS_VAR); - STATUS(float_rounding_mode) = oldmode; + oldmode = status->float_rounding_mode; + status->float_rounding_mode = float_round_to_zero; + res = float64_round_to_int(a, status); + status->float_rounding_mode = oldmode; return res; } @@ -3614,7 +3779,8 @@ float64 float64_trunc_to_int( float64 a STATUS_PARAM) | Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -static float64 addFloat64Sigs( float64 a, float64 b, flag zSign STATUS_PARAM ) +static float64 addFloat64Sigs(float64 a, float64 b, flag zSign, + float_status *status) { int_fast16_t aExp, bExp, zExp; uint64_t aSig, bSig, zSig; @@ -3629,7 +3795,9 @@ static float64 addFloat64Sigs( float64 a, float64 b, flag zSign STATUS_PARAM ) bSig <<= 9; if ( 0 < expDiff ) { if ( aExp == 0x7FF ) { - if ( aSig ) return propagateFloat64NaN( a, b STATUS_VAR ); + if (aSig) { + return propagateFloat64NaN(a, b, status); + } return a; } if ( bExp == 0 ) { @@ -3643,7 +3811,9 @@ static float64 addFloat64Sigs( float64 a, float64 b, flag zSign STATUS_PARAM ) } else if ( expDiff < 0 ) { if ( bExp == 0x7FF ) { - if ( bSig ) return propagateFloat64NaN( a, b STATUS_VAR ); + if (bSig) { + return propagateFloat64NaN(a, b, status); + } return packFloat64( zSign, 0x7FF, 0 ); } if ( aExp == 0 ) { @@ -3657,13 +3827,15 @@ static float64 addFloat64Sigs( float64 a, float64 b, flag zSign STATUS_PARAM ) } else { if ( aExp == 0x7FF ) { - if ( aSig | bSig ) return propagateFloat64NaN( a, b STATUS_VAR ); + if (aSig | bSig) { + return propagateFloat64NaN(a, b, status); + } return a; } if ( aExp == 0 ) { - if (STATUS(flush_to_zero)) { + if (status->flush_to_zero) { if (aSig | bSig) { - float_raise(float_flag_output_denormal STATUS_VAR); + float_raise(float_flag_output_denormal, status); } return packFloat64(zSign, 0, 0); } @@ -3681,7 +3853,7 @@ static float64 addFloat64Sigs( float64 a, float64 b, flag zSign STATUS_PARAM ) ++zExp; } roundAndPack: - return roundAndPackFloat64( zSign, zExp, zSig STATUS_VAR ); + return roundAndPackFloat64(zSign, zExp, zSig, status); } @@ -3693,7 +3865,8 @@ static float64 addFloat64Sigs( float64 a, float64 b, flag zSign STATUS_PARAM ) | Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -static float64 subFloat64Sigs( float64 a, float64 b, flag zSign STATUS_PARAM ) +static float64 subFloat64Sigs(float64 a, float64 b, flag zSign, + float_status *status) { int_fast16_t aExp, bExp, zExp; uint64_t aSig, bSig, zSig; @@ -3709,8 +3882,10 @@ static float64 subFloat64Sigs( float64 a, float64 b, flag zSign STATUS_PARAM ) if ( 0 < expDiff ) goto aExpBigger; if ( expDiff < 0 ) goto bExpBigger; if ( aExp == 0x7FF ) { - if ( aSig | bSig ) return propagateFloat64NaN( a, b STATUS_VAR ); - float_raise( float_flag_invalid STATUS_VAR); + if (aSig | bSig) { + return propagateFloat64NaN(a, b, status); + } + float_raise(float_flag_invalid, status); return float64_default_nan; } if ( aExp == 0 ) { @@ -3719,10 +3894,12 @@ static float64 subFloat64Sigs( float64 a, float64 b, flag zSign STATUS_PARAM ) } if ( bSig < aSig ) goto aBigger; if ( aSig < bSig ) goto bBigger; - return packFloat64( STATUS(float_rounding_mode) == float_round_down, 0, 0 ); + return packFloat64(status->float_rounding_mode == float_round_down, 0, 0); bExpBigger: if ( bExp == 0x7FF ) { - if ( bSig ) return propagateFloat64NaN( a, b STATUS_VAR ); + if (bSig) { + return propagateFloat64NaN(a, b, status); + } return packFloat64( zSign ^ 1, 0x7FF, 0 ); } if ( aExp == 0 ) { @@ -3740,7 +3917,9 @@ static float64 subFloat64Sigs( float64 a, float64 b, flag zSign STATUS_PARAM ) goto normalizeRoundAndPack; aExpBigger: if ( aExp == 0x7FF ) { - if ( aSig ) return propagateFloat64NaN( a, b STATUS_VAR ); + if (aSig) { + return propagateFloat64NaN(a, b, status); + } return a; } if ( bExp == 0 ) { @@ -3756,7 +3935,7 @@ static float64 subFloat64Sigs( float64 a, float64 b, flag zSign STATUS_PARAM ) zExp = aExp; normalizeRoundAndPack: --zExp; - return normalizeRoundAndPackFloat64( zSign, zExp, zSig STATUS_VAR ); + return normalizeRoundAndPackFloat64(zSign, zExp, zSig, status); } @@ -3766,19 +3945,19 @@ static float64 subFloat64Sigs( float64 a, float64 b, flag zSign STATUS_PARAM ) | Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -float64 float64_add( float64 a, float64 b STATUS_PARAM ) +float64 float64_add(float64 a, float64 b, float_status *status) { flag aSign, bSign; - a = float64_squash_input_denormal(a STATUS_VAR); - b = float64_squash_input_denormal(b STATUS_VAR); + a = float64_squash_input_denormal(a, status); + b = float64_squash_input_denormal(b, status); aSign = extractFloat64Sign( a ); bSign = extractFloat64Sign( b ); if ( aSign == bSign ) { - return addFloat64Sigs( a, b, aSign STATUS_VAR ); + return addFloat64Sigs(a, b, aSign, status); } else { - return subFloat64Sigs( a, b, aSign STATUS_VAR ); + return subFloat64Sigs(a, b, aSign, status); } } @@ -3789,19 +3968,19 @@ float64 float64_add( float64 a, float64 b STATUS_PARAM ) | for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -float64 float64_sub( float64 a, float64 b STATUS_PARAM ) +float64 float64_sub(float64 a, float64 b, float_status *status) { flag aSign, bSign; - a = float64_squash_input_denormal(a STATUS_VAR); - b = float64_squash_input_denormal(b STATUS_VAR); + a = float64_squash_input_denormal(a, status); + b = float64_squash_input_denormal(b, status); aSign = extractFloat64Sign( a ); bSign = extractFloat64Sign( b ); if ( aSign == bSign ) { - return subFloat64Sigs( a, b, aSign STATUS_VAR ); + return subFloat64Sigs(a, b, aSign, status); } else { - return addFloat64Sigs( a, b, aSign STATUS_VAR ); + return addFloat64Sigs(a, b, aSign, status); } } @@ -3812,14 +3991,14 @@ float64 float64_sub( float64 a, float64 b STATUS_PARAM ) | for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -float64 float64_mul( float64 a, float64 b STATUS_PARAM ) +float64 float64_mul(float64 a, float64 b, float_status *status) { flag aSign, bSign, zSign; int_fast16_t aExp, bExp, zExp; uint64_t aSig, bSig, zSig0, zSig1; - a = float64_squash_input_denormal(a STATUS_VAR); - b = float64_squash_input_denormal(b STATUS_VAR); + a = float64_squash_input_denormal(a, status); + b = float64_squash_input_denormal(b, status); aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); @@ -3830,18 +4009,20 @@ float64 float64_mul( float64 a, float64 b STATUS_PARAM ) zSign = aSign ^ bSign; if ( aExp == 0x7FF ) { if ( aSig || ( ( bExp == 0x7FF ) && bSig ) ) { - return propagateFloat64NaN( a, b STATUS_VAR ); + return propagateFloat64NaN(a, b, status); } if ( ( bExp | bSig ) == 0 ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return float64_default_nan; } return packFloat64( zSign, 0x7FF, 0 ); } if ( bExp == 0x7FF ) { - if ( bSig ) return propagateFloat64NaN( a, b STATUS_VAR ); + if (bSig) { + return propagateFloat64NaN(a, b, status); + } if ( ( aExp | aSig ) == 0 ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return float64_default_nan; } return packFloat64( zSign, 0x7FF, 0 ); @@ -3863,7 +4044,7 @@ float64 float64_mul( float64 a, float64 b STATUS_PARAM ) zSig0 <<= 1; --zExp; } - return roundAndPackFloat64( zSign, zExp, zSig0 STATUS_VAR ); + return roundAndPackFloat64(zSign, zExp, zSig0, status); } @@ -3873,15 +4054,15 @@ float64 float64_mul( float64 a, float64 b STATUS_PARAM ) | the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -float64 float64_div( float64 a, float64 b STATUS_PARAM ) +float64 float64_div(float64 a, float64 b, float_status *status) { flag aSign, bSign, zSign; int_fast16_t aExp, bExp, zExp; uint64_t aSig, bSig, zSig; uint64_t rem0, rem1; uint64_t term0, term1; - a = float64_squash_input_denormal(a STATUS_VAR); - b = float64_squash_input_denormal(b STATUS_VAR); + a = float64_squash_input_denormal(a, status); + b = float64_squash_input_denormal(b, status); aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); @@ -3891,25 +4072,31 @@ float64 float64_div( float64 a, float64 b STATUS_PARAM ) bSign = extractFloat64Sign( b ); zSign = aSign ^ bSign; if ( aExp == 0x7FF ) { - if ( aSig ) return propagateFloat64NaN( a, b STATUS_VAR ); + if (aSig) { + return propagateFloat64NaN(a, b, status); + } if ( bExp == 0x7FF ) { - if ( bSig ) return propagateFloat64NaN( a, b STATUS_VAR ); - float_raise( float_flag_invalid STATUS_VAR); + if (bSig) { + return propagateFloat64NaN(a, b, status); + } + float_raise(float_flag_invalid, status); return float64_default_nan; } return packFloat64( zSign, 0x7FF, 0 ); } if ( bExp == 0x7FF ) { - if ( bSig ) return propagateFloat64NaN( a, b STATUS_VAR ); + if (bSig) { + return propagateFloat64NaN(a, b, status); + } return packFloat64( zSign, 0, 0 ); } if ( bExp == 0 ) { if ( bSig == 0 ) { if ( ( aExp | aSig ) == 0 ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return float64_default_nan; } - float_raise( float_flag_divbyzero STATUS_VAR); + float_raise(float_flag_divbyzero, status); return packFloat64( zSign, 0x7FF, 0 ); } normalizeFloat64Subnormal( bSig, &bExp, &bSig ); @@ -3935,7 +4122,7 @@ float64 float64_div( float64 a, float64 b STATUS_PARAM ) } zSig |= ( rem1 != 0 ); } - return roundAndPackFloat64( zSign, zExp, zSig STATUS_VAR ); + return roundAndPackFloat64(zSign, zExp, zSig, status); } @@ -3945,7 +4132,7 @@ float64 float64_div( float64 a, float64 b STATUS_PARAM ) | according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -float64 float64_rem( float64 a, float64 b STATUS_PARAM ) +float64 float64_rem(float64 a, float64 b, float_status *status) { flag aSign, zSign; int_fast16_t aExp, bExp, expDiff; @@ -3953,8 +4140,8 @@ float64 float64_rem( float64 a, float64 b STATUS_PARAM ) uint64_t q, alternateASig; int64_t sigMean; - a = float64_squash_input_denormal(a STATUS_VAR); - b = float64_squash_input_denormal(b STATUS_VAR); + a = float64_squash_input_denormal(a, status); + b = float64_squash_input_denormal(b, status); aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); aSign = extractFloat64Sign( a ); @@ -3962,18 +4149,20 @@ float64 float64_rem( float64 a, float64 b STATUS_PARAM ) bExp = extractFloat64Exp( b ); if ( aExp == 0x7FF ) { if ( aSig || ( ( bExp == 0x7FF ) && bSig ) ) { - return propagateFloat64NaN( a, b STATUS_VAR ); + return propagateFloat64NaN(a, b, status); } - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return float64_default_nan; } if ( bExp == 0x7FF ) { - if ( bSig ) return propagateFloat64NaN( a, b STATUS_VAR ); + if (bSig) { + return propagateFloat64NaN(a, b, status); + } return a; } if ( bExp == 0 ) { if ( bSig == 0 ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return float64_default_nan; } normalizeFloat64Subnormal( bSig, &bExp, &bSig ); @@ -4021,7 +4210,7 @@ float64 float64_rem( float64 a, float64 b STATUS_PARAM ) } zSign = ( (int64_t) aSig < 0 ); if ( zSign ) aSig = - aSig; - return normalizeRoundAndPackFloat64( aSign ^ zSign, bExp, aSig STATUS_VAR ); + return normalizeRoundAndPackFloat64(aSign ^ zSign, bExp, aSig, status); } @@ -4036,7 +4225,8 @@ float64 float64_rem( float64 a, float64 b STATUS_PARAM ) | externally will flip the sign bit on NaNs.) *----------------------------------------------------------------------------*/ -float64 float64_muladd(float64 a, float64 b, float64 c, int flags STATUS_PARAM) +float64 float64_muladd(float64 a, float64 b, float64 c, int flags, + float_status *status) { flag aSign, bSign, cSign, zSign; int_fast16_t aExp, bExp, cExp, pExp, zExp, expDiff; @@ -4046,9 +4236,9 @@ float64 float64_muladd(float64 a, float64 b, float64 c, int flags STATUS_PARAM) int shiftcount; flag signflip, infzero; - a = float64_squash_input_denormal(a STATUS_VAR); - b = float64_squash_input_denormal(b STATUS_VAR); - c = float64_squash_input_denormal(c STATUS_VAR); + a = float64_squash_input_denormal(a, status); + b = float64_squash_input_denormal(b, status); + c = float64_squash_input_denormal(c, status); aSig = extractFloat64Frac(a); aExp = extractFloat64Exp(a); aSign = extractFloat64Sign(a); @@ -4070,11 +4260,11 @@ float64 float64_muladd(float64 a, float64 b, float64 c, int flags STATUS_PARAM) if (((aExp == 0x7ff) && aSig) || ((bExp == 0x7ff) && bSig) || ((cExp == 0x7ff) && cSig)) { - return propagateFloat64MulAddNaN(a, b, c, infzero STATUS_VAR); + return propagateFloat64MulAddNaN(a, b, c, infzero, status); } if (infzero) { - float_raise(float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return float64_default_nan; } @@ -4095,7 +4285,7 @@ float64 float64_muladd(float64 a, float64 b, float64 c, int flags STATUS_PARAM) if (cExp == 0x7ff) { if (pInf && (pSign ^ cSign)) { /* addition of opposite-signed infinities => InvalidOperation */ - float_raise(float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return float64_default_nan; } /* Otherwise generate an infinity of the same sign */ @@ -4112,7 +4302,7 @@ float64 float64_muladd(float64 a, float64 b, float64 c, int flags STATUS_PARAM) /* Adding two exact zeroes */ if (pSign == cSign) { zSign = pSign; - } else if (STATUS(float_rounding_mode) == float_round_down) { + } else if (status->float_rounding_mode == float_round_down) { zSign = 1; } else { zSign = 0; @@ -4120,8 +4310,8 @@ float64 float64_muladd(float64 a, float64 b, float64 c, int flags STATUS_PARAM) return packFloat64(zSign ^ signflip, 0, 0); } /* Exact zero plus a denorm */ - if (STATUS(flush_to_zero)) { - float_raise(float_flag_output_denormal STATUS_VAR); + if (status->flush_to_zero) { + float_raise(float_flag_output_denormal, status); return packFloat64(cSign ^ signflip, 0, 0); } } @@ -4135,7 +4325,7 @@ float64 float64_muladd(float64 a, float64 b, float64 c, int flags STATUS_PARAM) */ cExp -= 2; cSig = (cSig | 0x0010000000000000ULL) << 10; - return roundAndPackFloat64(cSign ^ signflip, cExp, cSig STATUS_VAR); + return roundAndPackFloat64(cSign ^ signflip, cExp, cSig, status); } return packFloat64(cSign ^ signflip, cExp, cSig); } @@ -4176,7 +4366,7 @@ float64 float64_muladd(float64 a, float64 b, float64 c, int flags STATUS_PARAM) pExp--; } return roundAndPackFloat64(zSign, pExp - 1, - pSig1 STATUS_VAR); + pSig1, status); } normalizeFloat64Subnormal(cSig, &cExp, &cSig); } @@ -4214,7 +4404,7 @@ float64 float64_muladd(float64 a, float64 b, float64 c, int flags STATUS_PARAM) if (flags & float_muladd_halve_result) { zExp--; } - return roundAndPackFloat64(zSign, zExp, zSig1 STATUS_VAR); + return roundAndPackFloat64(zSign, zExp, zSig1, status); } else { /* Subtraction */ if (expDiff > 0) { @@ -4236,7 +4426,7 @@ float64 float64_muladd(float64 a, float64 b, float64 c, int flags STATUS_PARAM) } else { /* Exact zero */ zSign = signflip; - if (STATUS(float_rounding_mode) == float_round_down) { + if (status->float_rounding_mode == float_round_down) { zSign ^= 1; } return packFloat64(zSign, 0, 0); @@ -4267,7 +4457,7 @@ float64 float64_muladd(float64 a, float64 b, float64 c, int flags STATUS_PARAM) if (flags & float_muladd_halve_result) { zExp--; } - return roundAndPackFloat64(zSign, zExp, zSig0 STATUS_VAR); + return roundAndPackFloat64(zSign, zExp, zSig0, status); } } @@ -4277,26 +4467,28 @@ float64 float64_muladd(float64 a, float64 b, float64 c, int flags STATUS_PARAM) | Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -float64 float64_sqrt( float64 a STATUS_PARAM ) +float64 float64_sqrt(float64 a, float_status *status) { flag aSign; int_fast16_t aExp, zExp; uint64_t aSig, zSig, doubleZSig; uint64_t rem0, rem1, term0, term1; - a = float64_squash_input_denormal(a STATUS_VAR); + a = float64_squash_input_denormal(a, status); aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); aSign = extractFloat64Sign( a ); if ( aExp == 0x7FF ) { - if ( aSig ) return propagateFloat64NaN( a, a STATUS_VAR ); + if (aSig) { + return propagateFloat64NaN(a, a, status); + } if ( ! aSign ) return a; - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return float64_default_nan; } if ( aSign ) { if ( ( aExp | aSig ) == 0 ) return a; - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return float64_default_nan; } if ( aExp == 0 ) { @@ -4319,7 +4511,7 @@ float64 float64_sqrt( float64 a STATUS_PARAM ) } zSig |= ( ( rem0 | rem1 ) != 0 ); } - return roundAndPackFloat64( 0, zExp, zSig STATUS_VAR ); + return roundAndPackFloat64(0, zExp, zSig, status); } @@ -4328,12 +4520,12 @@ float64 float64_sqrt( float64 a STATUS_PARAM ) | The operation is performed according to the IEC/IEEE Standard for Binary | Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -float64 float64_log2( float64 a STATUS_PARAM ) +float64 float64_log2(float64 a, float_status *status) { flag aSign, zSign; int_fast16_t aExp; uint64_t aSig, aSig0, aSig1, zSig, i; - a = float64_squash_input_denormal(a STATUS_VAR); + a = float64_squash_input_denormal(a, status); aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); @@ -4344,11 +4536,13 @@ float64 float64_log2( float64 a STATUS_PARAM ) normalizeFloat64Subnormal( aSig, &aExp, &aSig ); } if ( aSign ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return float64_default_nan; } if ( aExp == 0x7FF ) { - if ( aSig ) return propagateFloat64NaN( a, float64_zero STATUS_VAR ); + if (aSig) { + return propagateFloat64NaN(a, float64_zero, status); + } return a; } @@ -4367,7 +4561,7 @@ float64 float64_log2( float64 a STATUS_PARAM ) if ( zSign ) zSig = -zSig; - return normalizeRoundAndPackFloat64( zSign, 0x408, zSig STATUS_VAR ); + return normalizeRoundAndPackFloat64(zSign, 0x408, zSig, status); } /*---------------------------------------------------------------------------- @@ -4377,16 +4571,16 @@ float64 float64_log2( float64 a STATUS_PARAM ) | according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int float64_eq( float64 a, float64 b STATUS_PARAM ) +int float64_eq(float64 a, float64 b, float_status *status) { uint64_t av, bv; - a = float64_squash_input_denormal(a STATUS_VAR); - b = float64_squash_input_denormal(b STATUS_VAR); + a = float64_squash_input_denormal(a, status); + b = float64_squash_input_denormal(b, status); if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) ) || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return 0; } av = float64_val(a); @@ -4402,17 +4596,17 @@ int float64_eq( float64 a, float64 b STATUS_PARAM ) | according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int float64_le( float64 a, float64 b STATUS_PARAM ) +int float64_le(float64 a, float64 b, float_status *status) { flag aSign, bSign; uint64_t av, bv; - a = float64_squash_input_denormal(a STATUS_VAR); - b = float64_squash_input_denormal(b STATUS_VAR); + a = float64_squash_input_denormal(a, status); + b = float64_squash_input_denormal(b, status); if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) ) || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return 0; } aSign = extractFloat64Sign( a ); @@ -4431,17 +4625,17 @@ int float64_le( float64 a, float64 b STATUS_PARAM ) | to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int float64_lt( float64 a, float64 b STATUS_PARAM ) +int float64_lt(float64 a, float64 b, float_status *status) { flag aSign, bSign; uint64_t av, bv; - a = float64_squash_input_denormal(a STATUS_VAR); - b = float64_squash_input_denormal(b STATUS_VAR); + a = float64_squash_input_denormal(a, status); + b = float64_squash_input_denormal(b, status); if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) ) || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return 0; } aSign = extractFloat64Sign( a ); @@ -4460,15 +4654,15 @@ int float64_lt( float64 a, float64 b STATUS_PARAM ) | Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int float64_unordered( float64 a, float64 b STATUS_PARAM ) +int float64_unordered(float64 a, float64 b, float_status *status) { - a = float64_squash_input_denormal(a STATUS_VAR); - b = float64_squash_input_denormal(b STATUS_VAR); + a = float64_squash_input_denormal(a, status); + b = float64_squash_input_denormal(b, status); if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) ) || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return 1; } return 0; @@ -4481,17 +4675,17 @@ int float64_unordered( float64 a, float64 b STATUS_PARAM ) | for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int float64_eq_quiet( float64 a, float64 b STATUS_PARAM ) +int float64_eq_quiet(float64 a, float64 b, float_status *status) { uint64_t av, bv; - a = float64_squash_input_denormal(a STATUS_VAR); - b = float64_squash_input_denormal(b STATUS_VAR); + a = float64_squash_input_denormal(a, status); + b = float64_squash_input_denormal(b, status); if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) ) || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) ) ) { if ( float64_is_signaling_nan( a ) || float64_is_signaling_nan( b ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); } return 0; } @@ -4508,18 +4702,18 @@ int float64_eq_quiet( float64 a, float64 b STATUS_PARAM ) | IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int float64_le_quiet( float64 a, float64 b STATUS_PARAM ) +int float64_le_quiet(float64 a, float64 b, float_status *status) { flag aSign, bSign; uint64_t av, bv; - a = float64_squash_input_denormal(a STATUS_VAR); - b = float64_squash_input_denormal(b STATUS_VAR); + a = float64_squash_input_denormal(a, status); + b = float64_squash_input_denormal(b, status); if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) ) || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) ) ) { if ( float64_is_signaling_nan( a ) || float64_is_signaling_nan( b ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); } return 0; } @@ -4539,18 +4733,18 @@ int float64_le_quiet( float64 a, float64 b STATUS_PARAM ) | Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int float64_lt_quiet( float64 a, float64 b STATUS_PARAM ) +int float64_lt_quiet(float64 a, float64 b, float_status *status) { flag aSign, bSign; uint64_t av, bv; - a = float64_squash_input_denormal(a STATUS_VAR); - b = float64_squash_input_denormal(b STATUS_VAR); + a = float64_squash_input_denormal(a, status); + b = float64_squash_input_denormal(b, status); if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) ) || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) ) ) { if ( float64_is_signaling_nan( a ) || float64_is_signaling_nan( b ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); } return 0; } @@ -4570,16 +4764,16 @@ int float64_lt_quiet( float64 a, float64 b STATUS_PARAM ) | Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int float64_unordered_quiet( float64 a, float64 b STATUS_PARAM ) +int float64_unordered_quiet(float64 a, float64 b, float_status *status) { - a = float64_squash_input_denormal(a STATUS_VAR); - b = float64_squash_input_denormal(b STATUS_VAR); + a = float64_squash_input_denormal(a, status); + b = float64_squash_input_denormal(b, status); if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) ) || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) ) ) { if ( float64_is_signaling_nan( a ) || float64_is_signaling_nan( b ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); } return 1; } @@ -4596,7 +4790,7 @@ int float64_unordered_quiet( float64 a, float64 b STATUS_PARAM ) | overflows, the largest integer with the same sign as `a' is returned. *----------------------------------------------------------------------------*/ -int32 floatx80_to_int32( floatx80 a STATUS_PARAM ) +int32 floatx80_to_int32(floatx80 a, float_status *status) { flag aSign; int32 aExp, shiftCount; @@ -4609,7 +4803,7 @@ int32 floatx80_to_int32( floatx80 a STATUS_PARAM ) shiftCount = 0x4037 - aExp; if ( shiftCount <= 0 ) shiftCount = 1; shift64RightJamming( aSig, shiftCount, &aSig ); - return roundAndPackInt32( aSign, aSig STATUS_VAR ); + return roundAndPackInt32(aSign, aSig, status); } @@ -4623,7 +4817,7 @@ int32 floatx80_to_int32( floatx80 a STATUS_PARAM ) | sign as `a' is returned. *----------------------------------------------------------------------------*/ -int32 floatx80_to_int32_round_to_zero( floatx80 a STATUS_PARAM ) +int32 floatx80_to_int32_round_to_zero(floatx80 a, float_status *status) { flag aSign; int32 aExp, shiftCount; @@ -4638,7 +4832,9 @@ int32 floatx80_to_int32_round_to_zero( floatx80 a STATUS_PARAM ) goto invalid; } else if ( aExp < 0x3FFF ) { - if ( aExp || aSig ) STATUS(float_exception_flags) |= float_flag_inexact; + if (aExp || aSig) { + status->float_exception_flags |= float_flag_inexact; + } return 0; } shiftCount = 0x403E - aExp; @@ -4648,11 +4844,11 @@ int32 floatx80_to_int32_round_to_zero( floatx80 a STATUS_PARAM ) if ( aSign ) z = - z; if ( ( z < 0 ) ^ aSign ) { invalid: - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return aSign ? (int32_t) 0x80000000 : 0x7FFFFFFF; } if ( ( aSig<float_exception_flags |= float_flag_inexact; } return z; @@ -4668,7 +4864,7 @@ int32 floatx80_to_int32_round_to_zero( floatx80 a STATUS_PARAM ) | overflows, the largest integer with the same sign as `a' is returned. *----------------------------------------------------------------------------*/ -int64 floatx80_to_int64( floatx80 a STATUS_PARAM ) +int64 floatx80_to_int64(floatx80 a, float_status *status) { flag aSign; int32 aExp, shiftCount; @@ -4680,7 +4876,7 @@ int64 floatx80_to_int64( floatx80 a STATUS_PARAM ) shiftCount = 0x403E - aExp; if ( shiftCount <= 0 ) { if ( shiftCount ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); if ( ! aSign || ( ( aExp == 0x7FFF ) && ( aSig != LIT64( 0x8000000000000000 ) ) ) @@ -4694,7 +4890,7 @@ int64 floatx80_to_int64( floatx80 a STATUS_PARAM ) else { shift64ExtraRightJamming( aSig, 0, shiftCount, &aSig, &aSigExtra ); } - return roundAndPackInt64( aSign, aSig, aSigExtra STATUS_VAR ); + return roundAndPackInt64(aSign, aSig, aSigExtra, status); } @@ -4708,7 +4904,7 @@ int64 floatx80_to_int64( floatx80 a STATUS_PARAM ) | sign as `a' is returned. *----------------------------------------------------------------------------*/ -int64 floatx80_to_int64_round_to_zero( floatx80 a STATUS_PARAM ) +int64 floatx80_to_int64_round_to_zero(floatx80 a, float_status *status) { flag aSign; int32 aExp, shiftCount; @@ -4722,7 +4918,7 @@ int64 floatx80_to_int64_round_to_zero( floatx80 a STATUS_PARAM ) if ( 0 <= shiftCount ) { aSig &= LIT64( 0x7FFFFFFFFFFFFFFF ); if ( ( a.high != 0xC03E ) || aSig ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); if ( ! aSign || ( ( aExp == 0x7FFF ) && aSig ) ) { return LIT64( 0x7FFFFFFFFFFFFFFF ); } @@ -4730,12 +4926,14 @@ int64 floatx80_to_int64_round_to_zero( floatx80 a STATUS_PARAM ) return (int64_t) LIT64( 0x8000000000000000 ); } else if ( aExp < 0x3FFF ) { - if ( aExp | aSig ) STATUS(float_exception_flags) |= float_flag_inexact; + if (aExp | aSig) { + status->float_exception_flags |= float_flag_inexact; + } return 0; } z = aSig>>( - shiftCount ); if ( (uint64_t) ( aSig<<( shiftCount & 63 ) ) ) { - STATUS(float_exception_flags) |= float_flag_inexact; + status->float_exception_flags |= float_flag_inexact; } if ( aSign ) z = - z; return z; @@ -4749,7 +4947,7 @@ int64 floatx80_to_int64_round_to_zero( floatx80 a STATUS_PARAM ) | Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -float32 floatx80_to_float32( floatx80 a STATUS_PARAM ) +float32 floatx80_to_float32(floatx80 a, float_status *status) { flag aSign; int32 aExp; @@ -4760,13 +4958,13 @@ float32 floatx80_to_float32( floatx80 a STATUS_PARAM ) aSign = extractFloatx80Sign( a ); if ( aExp == 0x7FFF ) { if ( (uint64_t) ( aSig<<1 ) ) { - return commonNaNToFloat32( floatx80ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); + return commonNaNToFloat32(floatx80ToCommonNaN(a, status), status); } return packFloat32( aSign, 0xFF, 0 ); } shift64RightJamming( aSig, 33, &aSig ); if ( aExp || aSig ) aExp -= 0x3F81; - return roundAndPackFloat32( aSign, aExp, aSig STATUS_VAR ); + return roundAndPackFloat32(aSign, aExp, aSig, status); } @@ -4777,7 +4975,7 @@ float32 floatx80_to_float32( floatx80 a STATUS_PARAM ) | Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -float64 floatx80_to_float64( floatx80 a STATUS_PARAM ) +float64 floatx80_to_float64(floatx80 a, float_status *status) { flag aSign; int32 aExp; @@ -4788,13 +4986,13 @@ float64 floatx80_to_float64( floatx80 a STATUS_PARAM ) aSign = extractFloatx80Sign( a ); if ( aExp == 0x7FFF ) { if ( (uint64_t) ( aSig<<1 ) ) { - return commonNaNToFloat64( floatx80ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); + return commonNaNToFloat64(floatx80ToCommonNaN(a, status), status); } return packFloat64( aSign, 0x7FF, 0 ); } shift64RightJamming( aSig, 1, &zSig ); if ( aExp || aSig ) aExp -= 0x3C01; - return roundAndPackFloat64( aSign, aExp, zSig STATUS_VAR ); + return roundAndPackFloat64(aSign, aExp, zSig, status); } @@ -4805,7 +5003,7 @@ float64 floatx80_to_float64( floatx80 a STATUS_PARAM ) | Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -float128 floatx80_to_float128( floatx80 a STATUS_PARAM ) +float128 floatx80_to_float128(floatx80 a, float_status *status) { flag aSign; int_fast16_t aExp; @@ -4815,7 +5013,7 @@ float128 floatx80_to_float128( floatx80 a STATUS_PARAM ) aExp = extractFloatx80Exp( a ); aSign = extractFloatx80Sign( a ); if ( ( aExp == 0x7FFF ) && (uint64_t) ( aSig<<1 ) ) { - return commonNaNToFloat128( floatx80ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); + return commonNaNToFloat128(floatx80ToCommonNaN(a, status), status); } shift128Right( aSig<<1, 0, 16, &zSig0, &zSig1 ); return packFloat128( aSign, aExp, zSig0, zSig1 ); @@ -4829,7 +5027,7 @@ float128 floatx80_to_float128( floatx80 a STATUS_PARAM ) | Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -floatx80 floatx80_round_to_int( floatx80 a STATUS_PARAM ) +floatx80 floatx80_round_to_int(floatx80 a, float_status *status) { flag aSign; int32 aExp; @@ -4839,7 +5037,7 @@ floatx80 floatx80_round_to_int( floatx80 a STATUS_PARAM ) aExp = extractFloatx80Exp( a ); if ( 0x403E <= aExp ) { if ( ( aExp == 0x7FFF ) && (uint64_t) ( extractFloatx80Frac( a )<<1 ) ) { - return propagateFloatx80NaN( a, a STATUS_VAR ); + return propagateFloatx80NaN(a, a, status); } return a; } @@ -4848,9 +5046,9 @@ floatx80 floatx80_round_to_int( floatx80 a STATUS_PARAM ) && ( (uint64_t) ( extractFloatx80Frac( a )<<1 ) == 0 ) ) { return a; } - STATUS(float_exception_flags) |= float_flag_inexact; + status->float_exception_flags |= float_flag_inexact; aSign = extractFloatx80Sign( a ); - switch ( STATUS(float_rounding_mode) ) { + switch (status->float_rounding_mode) { case float_round_nearest_even: if ( ( aExp == 0x3FFE ) && (uint64_t) ( extractFloatx80Frac( a )<<1 ) ) { @@ -4879,7 +5077,7 @@ floatx80 floatx80_round_to_int( floatx80 a STATUS_PARAM ) lastBitMask <<= 0x403E - aExp; roundBitsMask = lastBitMask - 1; z = a; - switch (STATUS(float_rounding_mode)) { + switch (status->float_rounding_mode) { case float_round_nearest_even: z.low += lastBitMask>>1; if ((z.low & roundBitsMask) == 0) { @@ -4909,7 +5107,9 @@ floatx80 floatx80_round_to_int( floatx80 a STATUS_PARAM ) ++z.high; z.low = LIT64( 0x8000000000000000 ); } - if ( z.low != a.low ) STATUS(float_exception_flags) |= float_flag_inexact; + if (z.low != a.low) { + status->float_exception_flags |= float_flag_inexact; + } return z; } @@ -4922,7 +5122,8 @@ floatx80 floatx80_round_to_int( floatx80 a STATUS_PARAM ) | Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -static floatx80 addFloatx80Sigs( floatx80 a, floatx80 b, flag zSign STATUS_PARAM) +static floatx80 addFloatx80Sigs(floatx80 a, floatx80 b, flag zSign, + float_status *status) { int32 aExp, bExp, zExp; uint64_t aSig, bSig, zSig0, zSig1; @@ -4935,7 +5136,9 @@ static floatx80 addFloatx80Sigs( floatx80 a, floatx80 b, flag zSign STATUS_PARAM expDiff = aExp - bExp; if ( 0 < expDiff ) { if ( aExp == 0x7FFF ) { - if ( (uint64_t) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + if ((uint64_t)(aSig << 1)) { + return propagateFloatx80NaN(a, b, status); + } return a; } if ( bExp == 0 ) --expDiff; @@ -4944,7 +5147,9 @@ static floatx80 addFloatx80Sigs( floatx80 a, floatx80 b, flag zSign STATUS_PARAM } else if ( expDiff < 0 ) { if ( bExp == 0x7FFF ) { - if ( (uint64_t) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + if ((uint64_t)(bSig << 1)) { + return propagateFloatx80NaN(a, b, status); + } return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); } if ( aExp == 0 ) ++expDiff; @@ -4954,7 +5159,7 @@ static floatx80 addFloatx80Sigs( floatx80 a, floatx80 b, flag zSign STATUS_PARAM else { if ( aExp == 0x7FFF ) { if ( (uint64_t) ( ( aSig | bSig )<<1 ) ) { - return propagateFloatx80NaN( a, b STATUS_VAR ); + return propagateFloatx80NaN(a, b, status); } return a; } @@ -4974,10 +5179,8 @@ static floatx80 addFloatx80Sigs( floatx80 a, floatx80 b, flag zSign STATUS_PARAM zSig0 |= LIT64( 0x8000000000000000 ); ++zExp; roundAndPack: - return - roundAndPackFloatx80( - STATUS(floatx80_rounding_precision), zSign, zExp, zSig0, zSig1 STATUS_VAR ); - + return roundAndPackFloatx80(status->floatx80_rounding_precision, + zSign, zExp, zSig0, zSig1, status); } /*---------------------------------------------------------------------------- @@ -4988,7 +5191,8 @@ static floatx80 addFloatx80Sigs( floatx80 a, floatx80 b, flag zSign STATUS_PARAM | Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -static floatx80 subFloatx80Sigs( floatx80 a, floatx80 b, flag zSign STATUS_PARAM ) +static floatx80 subFloatx80Sigs(floatx80 a, floatx80 b, flag zSign, + float_status *status) { int32 aExp, bExp, zExp; uint64_t aSig, bSig, zSig0, zSig1; @@ -5004,9 +5208,9 @@ static floatx80 subFloatx80Sigs( floatx80 a, floatx80 b, flag zSign STATUS_PARAM if ( expDiff < 0 ) goto bExpBigger; if ( aExp == 0x7FFF ) { if ( (uint64_t) ( ( aSig | bSig )<<1 ) ) { - return propagateFloatx80NaN( a, b STATUS_VAR ); + return propagateFloatx80NaN(a, b, status); } - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); z.low = floatx80_default_nan_low; z.high = floatx80_default_nan_high; return z; @@ -5018,10 +5222,12 @@ static floatx80 subFloatx80Sigs( floatx80 a, floatx80 b, flag zSign STATUS_PARAM zSig1 = 0; if ( bSig < aSig ) goto aBigger; if ( aSig < bSig ) goto bBigger; - return packFloatx80( STATUS(float_rounding_mode) == float_round_down, 0, 0 ); + return packFloatx80(status->float_rounding_mode == float_round_down, 0, 0); bExpBigger: if ( bExp == 0x7FFF ) { - if ( (uint64_t) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + if ((uint64_t)(bSig << 1)) { + return propagateFloatx80NaN(a, b, status); + } return packFloatx80( zSign ^ 1, 0x7FFF, LIT64( 0x8000000000000000 ) ); } if ( aExp == 0 ) ++expDiff; @@ -5033,7 +5239,9 @@ static floatx80 subFloatx80Sigs( floatx80 a, floatx80 b, flag zSign STATUS_PARAM goto normalizeRoundAndPack; aExpBigger: if ( aExp == 0x7FFF ) { - if ( (uint64_t) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + if ((uint64_t)(aSig << 1)) { + return propagateFloatx80NaN(a, b, status); + } return a; } if ( bExp == 0 ) --expDiff; @@ -5042,10 +5250,8 @@ static floatx80 subFloatx80Sigs( floatx80 a, floatx80 b, flag zSign STATUS_PARAM sub128( aSig, 0, bSig, zSig1, &zSig0, &zSig1 ); zExp = aExp; normalizeRoundAndPack: - return - normalizeRoundAndPackFloatx80( - STATUS(floatx80_rounding_precision), zSign, zExp, zSig0, zSig1 STATUS_VAR ); - + return normalizeRoundAndPackFloatx80(status->floatx80_rounding_precision, + zSign, zExp, zSig0, zSig1, status); } /*---------------------------------------------------------------------------- @@ -5054,17 +5260,17 @@ static floatx80 subFloatx80Sigs( floatx80 a, floatx80 b, flag zSign STATUS_PARAM | Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -floatx80 floatx80_add( floatx80 a, floatx80 b STATUS_PARAM ) +floatx80 floatx80_add(floatx80 a, floatx80 b, float_status *status) { flag aSign, bSign; aSign = extractFloatx80Sign( a ); bSign = extractFloatx80Sign( b ); if ( aSign == bSign ) { - return addFloatx80Sigs( a, b, aSign STATUS_VAR ); + return addFloatx80Sigs(a, b, aSign, status); } else { - return subFloatx80Sigs( a, b, aSign STATUS_VAR ); + return subFloatx80Sigs(a, b, aSign, status); } } @@ -5075,17 +5281,17 @@ floatx80 floatx80_add( floatx80 a, floatx80 b STATUS_PARAM ) | IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -floatx80 floatx80_sub( floatx80 a, floatx80 b STATUS_PARAM ) +floatx80 floatx80_sub(floatx80 a, floatx80 b, float_status *status) { flag aSign, bSign; aSign = extractFloatx80Sign( a ); bSign = extractFloatx80Sign( b ); if ( aSign == bSign ) { - return subFloatx80Sigs( a, b, aSign STATUS_VAR ); + return subFloatx80Sigs(a, b, aSign, status); } else { - return addFloatx80Sigs( a, b, aSign STATUS_VAR ); + return addFloatx80Sigs(a, b, aSign, status); } } @@ -5096,7 +5302,7 @@ floatx80 floatx80_sub( floatx80 a, floatx80 b STATUS_PARAM ) | IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -floatx80 floatx80_mul( floatx80 a, floatx80 b STATUS_PARAM ) +floatx80 floatx80_mul(floatx80 a, floatx80 b, float_status *status) { flag aSign, bSign, zSign; int32 aExp, bExp, zExp; @@ -5113,16 +5319,18 @@ floatx80 floatx80_mul( floatx80 a, floatx80 b STATUS_PARAM ) if ( aExp == 0x7FFF ) { if ( (uint64_t) ( aSig<<1 ) || ( ( bExp == 0x7FFF ) && (uint64_t) ( bSig<<1 ) ) ) { - return propagateFloatx80NaN( a, b STATUS_VAR ); + return propagateFloatx80NaN(a, b, status); } if ( ( bExp | bSig ) == 0 ) goto invalid; return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); } if ( bExp == 0x7FFF ) { - if ( (uint64_t) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + if ((uint64_t)(bSig << 1)) { + return propagateFloatx80NaN(a, b, status); + } if ( ( aExp | aSig ) == 0 ) { invalid: - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); z.low = floatx80_default_nan_low; z.high = floatx80_default_nan_high; return z; @@ -5143,10 +5351,8 @@ floatx80 floatx80_mul( floatx80 a, floatx80 b STATUS_PARAM ) shortShift128Left( zSig0, zSig1, 1, &zSig0, &zSig1 ); --zExp; } - return - roundAndPackFloatx80( - STATUS(floatx80_rounding_precision), zSign, zExp, zSig0, zSig1 STATUS_VAR ); - + return roundAndPackFloatx80(status->floatx80_rounding_precision, + zSign, zExp, zSig0, zSig1, status); } /*---------------------------------------------------------------------------- @@ -5155,7 +5361,7 @@ floatx80 floatx80_mul( floatx80 a, floatx80 b STATUS_PARAM ) | according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -floatx80 floatx80_div( floatx80 a, floatx80 b STATUS_PARAM ) +floatx80 floatx80_div(floatx80 a, floatx80 b, float_status *status) { flag aSign, bSign, zSign; int32 aExp, bExp, zExp; @@ -5171,27 +5377,33 @@ floatx80 floatx80_div( floatx80 a, floatx80 b STATUS_PARAM ) bSign = extractFloatx80Sign( b ); zSign = aSign ^ bSign; if ( aExp == 0x7FFF ) { - if ( (uint64_t) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + if ((uint64_t)(aSig << 1)) { + return propagateFloatx80NaN(a, b, status); + } if ( bExp == 0x7FFF ) { - if ( (uint64_t) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + if ((uint64_t)(bSig << 1)) { + return propagateFloatx80NaN(a, b, status); + } goto invalid; } return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); } if ( bExp == 0x7FFF ) { - if ( (uint64_t) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + if ((uint64_t)(bSig << 1)) { + return propagateFloatx80NaN(a, b, status); + } return packFloatx80( zSign, 0, 0 ); } if ( bExp == 0 ) { if ( bSig == 0 ) { if ( ( aExp | aSig ) == 0 ) { invalid: - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); z.low = floatx80_default_nan_low; z.high = floatx80_default_nan_high; return z; } - float_raise( float_flag_divbyzero STATUS_VAR); + float_raise(float_flag_divbyzero, status); return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); } normalizeFloatx80Subnormal( bSig, &bExp, &bSig ); @@ -5223,10 +5435,8 @@ floatx80 floatx80_div( floatx80 a, floatx80 b STATUS_PARAM ) } zSig1 |= ( ( rem1 | rem2 ) != 0 ); } - return - roundAndPackFloatx80( - STATUS(floatx80_rounding_precision), zSign, zExp, zSig0, zSig1 STATUS_VAR ); - + return roundAndPackFloatx80(status->floatx80_rounding_precision, + zSign, zExp, zSig0, zSig1, status); } /*---------------------------------------------------------------------------- @@ -5235,7 +5445,7 @@ floatx80 floatx80_div( floatx80 a, floatx80 b STATUS_PARAM ) | according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -floatx80 floatx80_rem( floatx80 a, floatx80 b STATUS_PARAM ) +floatx80 floatx80_rem(floatx80 a, floatx80 b, float_status *status) { flag aSign, zSign; int32 aExp, bExp, expDiff; @@ -5251,18 +5461,20 @@ floatx80 floatx80_rem( floatx80 a, floatx80 b STATUS_PARAM ) if ( aExp == 0x7FFF ) { if ( (uint64_t) ( aSig0<<1 ) || ( ( bExp == 0x7FFF ) && (uint64_t) ( bSig<<1 ) ) ) { - return propagateFloatx80NaN( a, b STATUS_VAR ); + return propagateFloatx80NaN(a, b, status); } goto invalid; } if ( bExp == 0x7FFF ) { - if ( (uint64_t) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + if ((uint64_t)(bSig << 1)) { + return propagateFloatx80NaN(a, b, status); + } return a; } if ( bExp == 0 ) { if ( bSig == 0 ) { invalid: - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); z.low = floatx80_default_nan_low; z.high = floatx80_default_nan_high; return z; @@ -5321,7 +5533,7 @@ floatx80 floatx80_rem( floatx80 a, floatx80 b STATUS_PARAM ) } return normalizeRoundAndPackFloatx80( - 80, zSign, bExp + expDiff, aSig0, aSig1 STATUS_VAR ); + 80, zSign, bExp + expDiff, aSig0, aSig1, status); } @@ -5331,7 +5543,7 @@ floatx80 floatx80_rem( floatx80 a, floatx80 b STATUS_PARAM ) | for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -floatx80 floatx80_sqrt( floatx80 a STATUS_PARAM ) +floatx80 floatx80_sqrt(floatx80 a, float_status *status) { flag aSign; int32 aExp, zExp; @@ -5343,14 +5555,16 @@ floatx80 floatx80_sqrt( floatx80 a STATUS_PARAM ) aExp = extractFloatx80Exp( a ); aSign = extractFloatx80Sign( a ); if ( aExp == 0x7FFF ) { - if ( (uint64_t) ( aSig0<<1 ) ) return propagateFloatx80NaN( a, a STATUS_VAR ); + if ((uint64_t)(aSig0 << 1)) { + return propagateFloatx80NaN(a, a, status); + } if ( ! aSign ) return a; goto invalid; } if ( aSign ) { if ( ( aExp | aSig0 ) == 0 ) return a; invalid: - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); z.low = floatx80_default_nan_low; z.high = floatx80_default_nan_high; return z; @@ -5389,10 +5603,8 @@ floatx80 floatx80_sqrt( floatx80 a STATUS_PARAM ) } shortShift128Left( 0, zSig1, 1, &zSig0, &zSig1 ); zSig0 |= doubleZSig0; - return - roundAndPackFloatx80( - STATUS(floatx80_rounding_precision), 0, zExp, zSig0, zSig1 STATUS_VAR ); - + return roundAndPackFloatx80(status->floatx80_rounding_precision, + 0, zExp, zSig0, zSig1, status); } /*---------------------------------------------------------------------------- @@ -5402,7 +5614,7 @@ floatx80 floatx80_sqrt( floatx80 a STATUS_PARAM ) | according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int floatx80_eq( floatx80 a, floatx80 b STATUS_PARAM ) +int floatx80_eq(floatx80 a, floatx80 b, float_status *status) { if ( ( ( extractFloatx80Exp( a ) == 0x7FFF ) @@ -5410,7 +5622,7 @@ int floatx80_eq( floatx80 a, floatx80 b STATUS_PARAM ) || ( ( extractFloatx80Exp( b ) == 0x7FFF ) && (uint64_t) ( extractFloatx80Frac( b )<<1 ) ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return 0; } return @@ -5430,7 +5642,7 @@ int floatx80_eq( floatx80 a, floatx80 b STATUS_PARAM ) | Arithmetic. *----------------------------------------------------------------------------*/ -int floatx80_le( floatx80 a, floatx80 b STATUS_PARAM ) +int floatx80_le(floatx80 a, floatx80 b, float_status *status) { flag aSign, bSign; @@ -5439,7 +5651,7 @@ int floatx80_le( floatx80 a, floatx80 b STATUS_PARAM ) || ( ( extractFloatx80Exp( b ) == 0x7FFF ) && (uint64_t) ( extractFloatx80Frac( b )<<1 ) ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return 0; } aSign = extractFloatx80Sign( a ); @@ -5463,7 +5675,7 @@ int floatx80_le( floatx80 a, floatx80 b STATUS_PARAM ) | according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int floatx80_lt( floatx80 a, floatx80 b STATUS_PARAM ) +int floatx80_lt(floatx80 a, floatx80 b, float_status *status) { flag aSign, bSign; @@ -5472,7 +5684,7 @@ int floatx80_lt( floatx80 a, floatx80 b STATUS_PARAM ) || ( ( extractFloatx80Exp( b ) == 0x7FFF ) && (uint64_t) ( extractFloatx80Frac( b )<<1 ) ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return 0; } aSign = extractFloatx80Sign( a ); @@ -5495,14 +5707,14 @@ int floatx80_lt( floatx80 a, floatx80 b STATUS_PARAM ) | either operand is a NaN. The comparison is performed according to the | IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int floatx80_unordered( floatx80 a, floatx80 b STATUS_PARAM ) +int floatx80_unordered(floatx80 a, floatx80 b, float_status *status) { if ( ( ( extractFloatx80Exp( a ) == 0x7FFF ) && (uint64_t) ( extractFloatx80Frac( a )<<1 ) ) || ( ( extractFloatx80Exp( b ) == 0x7FFF ) && (uint64_t) ( extractFloatx80Frac( b )<<1 ) ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return 1; } return 0; @@ -5515,7 +5727,7 @@ int floatx80_unordered( floatx80 a, floatx80 b STATUS_PARAM ) | Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int floatx80_eq_quiet( floatx80 a, floatx80 b STATUS_PARAM ) +int floatx80_eq_quiet(floatx80 a, floatx80 b, float_status *status) { if ( ( ( extractFloatx80Exp( a ) == 0x7FFF ) @@ -5525,7 +5737,7 @@ int floatx80_eq_quiet( floatx80 a, floatx80 b STATUS_PARAM ) ) { if ( floatx80_is_signaling_nan( a ) || floatx80_is_signaling_nan( b ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); } return 0; } @@ -5545,7 +5757,7 @@ int floatx80_eq_quiet( floatx80 a, floatx80 b STATUS_PARAM ) | to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int floatx80_le_quiet( floatx80 a, floatx80 b STATUS_PARAM ) +int floatx80_le_quiet(floatx80 a, floatx80 b, float_status *status) { flag aSign, bSign; @@ -5556,7 +5768,7 @@ int floatx80_le_quiet( floatx80 a, floatx80 b STATUS_PARAM ) ) { if ( floatx80_is_signaling_nan( a ) || floatx80_is_signaling_nan( b ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); } return 0; } @@ -5581,7 +5793,7 @@ int floatx80_le_quiet( floatx80 a, floatx80 b STATUS_PARAM ) | IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int floatx80_lt_quiet( floatx80 a, floatx80 b STATUS_PARAM ) +int floatx80_lt_quiet(floatx80 a, floatx80 b, float_status *status) { flag aSign, bSign; @@ -5592,7 +5804,7 @@ int floatx80_lt_quiet( floatx80 a, floatx80 b STATUS_PARAM ) ) { if ( floatx80_is_signaling_nan( a ) || floatx80_is_signaling_nan( b ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); } return 0; } @@ -5616,7 +5828,7 @@ int floatx80_lt_quiet( floatx80 a, floatx80 b STATUS_PARAM ) | The comparison is performed according to the IEC/IEEE Standard for Binary | Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int floatx80_unordered_quiet( floatx80 a, floatx80 b STATUS_PARAM ) +int floatx80_unordered_quiet(floatx80 a, floatx80 b, float_status *status) { if ( ( ( extractFloatx80Exp( a ) == 0x7FFF ) && (uint64_t) ( extractFloatx80Frac( a )<<1 ) ) @@ -5625,7 +5837,7 @@ int floatx80_unordered_quiet( floatx80 a, floatx80 b STATUS_PARAM ) ) { if ( floatx80_is_signaling_nan( a ) || floatx80_is_signaling_nan( b ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); } return 1; } @@ -5642,7 +5854,7 @@ int floatx80_unordered_quiet( floatx80 a, floatx80 b STATUS_PARAM ) | largest integer with the same sign as `a' is returned. *----------------------------------------------------------------------------*/ -int32 float128_to_int32( float128 a STATUS_PARAM ) +int32 float128_to_int32(float128 a, float_status *status) { flag aSign; int32 aExp, shiftCount; @@ -5657,7 +5869,7 @@ int32 float128_to_int32( float128 a STATUS_PARAM ) aSig0 |= ( aSig1 != 0 ); shiftCount = 0x4028 - aExp; if ( 0 < shiftCount ) shift64RightJamming( aSig0, shiftCount, &aSig0 ); - return roundAndPackInt32( aSign, aSig0 STATUS_VAR ); + return roundAndPackInt32(aSign, aSig0, status); } @@ -5671,7 +5883,7 @@ int32 float128_to_int32( float128 a STATUS_PARAM ) | returned. *----------------------------------------------------------------------------*/ -int32 float128_to_int32_round_to_zero( float128 a STATUS_PARAM ) +int32 float128_to_int32_round_to_zero(float128 a, float_status *status) { flag aSign; int32 aExp, shiftCount; @@ -5688,7 +5900,9 @@ int32 float128_to_int32_round_to_zero( float128 a STATUS_PARAM ) goto invalid; } else if ( aExp < 0x3FFF ) { - if ( aExp || aSig0 ) STATUS(float_exception_flags) |= float_flag_inexact; + if (aExp || aSig0) { + status->float_exception_flags |= float_flag_inexact; + } return 0; } aSig0 |= LIT64( 0x0001000000000000 ); @@ -5699,11 +5913,11 @@ int32 float128_to_int32_round_to_zero( float128 a STATUS_PARAM ) if ( aSign ) z = - z; if ( ( z < 0 ) ^ aSign ) { invalid: - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return aSign ? (int32_t) 0x80000000 : 0x7FFFFFFF; } if ( ( aSig0<float_exception_flags |= float_flag_inexact; } return z; @@ -5719,7 +5933,7 @@ int32 float128_to_int32_round_to_zero( float128 a STATUS_PARAM ) | largest integer with the same sign as `a' is returned. *----------------------------------------------------------------------------*/ -int64 float128_to_int64( float128 a STATUS_PARAM ) +int64 float128_to_int64(float128 a, float_status *status) { flag aSign; int32 aExp, shiftCount; @@ -5733,7 +5947,7 @@ int64 float128_to_int64( float128 a STATUS_PARAM ) shiftCount = 0x402F - aExp; if ( shiftCount <= 0 ) { if ( 0x403E < aExp ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); if ( ! aSign || ( ( aExp == 0x7FFF ) && ( aSig1 || ( aSig0 != LIT64( 0x0001000000000000 ) ) ) @@ -5748,7 +5962,7 @@ int64 float128_to_int64( float128 a STATUS_PARAM ) else { shift64ExtraRightJamming( aSig0, aSig1, shiftCount, &aSig0, &aSig1 ); } - return roundAndPackInt64( aSign, aSig0, aSig1 STATUS_VAR ); + return roundAndPackInt64(aSign, aSig0, aSig1, status); } @@ -5762,7 +5976,7 @@ int64 float128_to_int64( float128 a STATUS_PARAM ) | returned. *----------------------------------------------------------------------------*/ -int64 float128_to_int64_round_to_zero( float128 a STATUS_PARAM ) +int64 float128_to_int64_round_to_zero(float128 a, float_status *status) { flag aSign; int32 aExp, shiftCount; @@ -5780,10 +5994,12 @@ int64 float128_to_int64_round_to_zero( float128 a STATUS_PARAM ) aSig0 &= LIT64( 0x0000FFFFFFFFFFFF ); if ( ( a.high == LIT64( 0xC03E000000000000 ) ) && ( aSig1 < LIT64( 0x0002000000000000 ) ) ) { - if ( aSig1 ) STATUS(float_exception_flags) |= float_flag_inexact; + if (aSig1) { + status->float_exception_flags |= float_flag_inexact; + } } else { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); if ( ! aSign || ( ( aExp == 0x7FFF ) && ( aSig0 | aSig1 ) ) ) { return LIT64( 0x7FFFFFFFFFFFFFFF ); } @@ -5792,20 +6008,20 @@ int64 float128_to_int64_round_to_zero( float128 a STATUS_PARAM ) } z = ( aSig0<>( ( - shiftCount ) & 63 ) ); if ( (uint64_t) ( aSig1<float_exception_flags |= float_flag_inexact; } } else { if ( aExp < 0x3FFF ) { if ( aExp | aSig0 | aSig1 ) { - STATUS(float_exception_flags) |= float_flag_inexact; + status->float_exception_flags |= float_flag_inexact; } return 0; } z = aSig0>>( - shiftCount ); if ( aSig1 || ( shiftCount && (uint64_t) ( aSig0<<( shiftCount & 63 ) ) ) ) { - STATUS(float_exception_flags) |= float_flag_inexact; + status->float_exception_flags |= float_flag_inexact; } } if ( aSign ) z = - z; @@ -5820,7 +6036,7 @@ int64 float128_to_int64_round_to_zero( float128 a STATUS_PARAM ) | Arithmetic. *----------------------------------------------------------------------------*/ -float32 float128_to_float32( float128 a STATUS_PARAM ) +float32 float128_to_float32(float128 a, float_status *status) { flag aSign; int32 aExp; @@ -5833,7 +6049,7 @@ float32 float128_to_float32( float128 a STATUS_PARAM ) aSign = extractFloat128Sign( a ); if ( aExp == 0x7FFF ) { if ( aSig0 | aSig1 ) { - return commonNaNToFloat32( float128ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); + return commonNaNToFloat32(float128ToCommonNaN(a, status), status); } return packFloat32( aSign, 0xFF, 0 ); } @@ -5844,7 +6060,7 @@ float32 float128_to_float32( float128 a STATUS_PARAM ) zSig |= 0x40000000; aExp -= 0x3F81; } - return roundAndPackFloat32( aSign, aExp, zSig STATUS_VAR ); + return roundAndPackFloat32(aSign, aExp, zSig, status); } @@ -5855,7 +6071,7 @@ float32 float128_to_float32( float128 a STATUS_PARAM ) | Arithmetic. *----------------------------------------------------------------------------*/ -float64 float128_to_float64( float128 a STATUS_PARAM ) +float64 float128_to_float64(float128 a, float_status *status) { flag aSign; int32 aExp; @@ -5867,7 +6083,7 @@ float64 float128_to_float64( float128 a STATUS_PARAM ) aSign = extractFloat128Sign( a ); if ( aExp == 0x7FFF ) { if ( aSig0 | aSig1 ) { - return commonNaNToFloat64( float128ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); + return commonNaNToFloat64(float128ToCommonNaN(a, status), status); } return packFloat64( aSign, 0x7FF, 0 ); } @@ -5877,7 +6093,7 @@ float64 float128_to_float64( float128 a STATUS_PARAM ) aSig0 |= LIT64( 0x4000000000000000 ); aExp -= 0x3C01; } - return roundAndPackFloat64( aSign, aExp, aSig0 STATUS_VAR ); + return roundAndPackFloat64(aSign, aExp, aSig0, status); } @@ -5888,7 +6104,7 @@ float64 float128_to_float64( float128 a STATUS_PARAM ) | Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -floatx80 float128_to_floatx80( float128 a STATUS_PARAM ) +floatx80 float128_to_floatx80(float128 a, float_status *status) { flag aSign; int32 aExp; @@ -5900,7 +6116,7 @@ floatx80 float128_to_floatx80( float128 a STATUS_PARAM ) aSign = extractFloat128Sign( a ); if ( aExp == 0x7FFF ) { if ( aSig0 | aSig1 ) { - return commonNaNToFloatx80( float128ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); + return commonNaNToFloatx80(float128ToCommonNaN(a, status), status); } return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); } @@ -5912,7 +6128,7 @@ floatx80 float128_to_floatx80( float128 a STATUS_PARAM ) aSig0 |= LIT64( 0x0001000000000000 ); } shortShift128Left( aSig0, aSig1, 15, &aSig0, &aSig1 ); - return roundAndPackFloatx80( 80, aSign, aExp, aSig0, aSig1 STATUS_VAR ); + return roundAndPackFloatx80(80, aSign, aExp, aSig0, aSig1, status); } @@ -5923,7 +6139,7 @@ floatx80 float128_to_floatx80( float128 a STATUS_PARAM ) | Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -float128 float128_round_to_int( float128 a STATUS_PARAM ) +float128 float128_round_to_int(float128 a, float_status *status) { flag aSign; int32 aExp; @@ -5936,7 +6152,7 @@ float128 float128_round_to_int( float128 a STATUS_PARAM ) if ( ( aExp == 0x7FFF ) && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) ) { - return propagateFloat128NaN( a, a STATUS_VAR ); + return propagateFloat128NaN(a, a, status); } return a; } @@ -5944,7 +6160,7 @@ float128 float128_round_to_int( float128 a STATUS_PARAM ) lastBitMask = ( lastBitMask<<( 0x406E - aExp ) )<<1; roundBitsMask = lastBitMask - 1; z = a; - switch (STATUS(float_rounding_mode)) { + switch (status->float_rounding_mode) { case float_round_nearest_even: if ( lastBitMask ) { add128( z.high, z.low, 0, lastBitMask>>1, &z.high, &z.low ); @@ -5986,9 +6202,9 @@ float128 float128_round_to_int( float128 a STATUS_PARAM ) else { if ( aExp < 0x3FFF ) { if ( ( ( (uint64_t) ( a.high<<1 ) ) | a.low ) == 0 ) return a; - STATUS(float_exception_flags) |= float_flag_inexact; + status->float_exception_flags |= float_flag_inexact; aSign = extractFloat128Sign( a ); - switch ( STATUS(float_rounding_mode) ) { + switch (status->float_rounding_mode) { case float_round_nearest_even: if ( ( aExp == 0x3FFE ) && ( extractFloat128Frac0( a ) @@ -6018,7 +6234,7 @@ float128 float128_round_to_int( float128 a STATUS_PARAM ) roundBitsMask = lastBitMask - 1; z.low = 0; z.high = a.high; - switch (STATUS(float_rounding_mode)) { + switch (status->float_rounding_mode) { case float_round_nearest_even: z.high += lastBitMask>>1; if ( ( ( z.high & roundBitsMask ) | a.low ) == 0 ) { @@ -6048,7 +6264,7 @@ float128 float128_round_to_int( float128 a STATUS_PARAM ) z.high &= ~ roundBitsMask; } if ( ( z.low != a.low ) || ( z.high != a.high ) ) { - STATUS(float_exception_flags) |= float_flag_inexact; + status->float_exception_flags |= float_flag_inexact; } return z; @@ -6062,7 +6278,8 @@ float128 float128_round_to_int( float128 a STATUS_PARAM ) | Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -static float128 addFloat128Sigs( float128 a, float128 b, flag zSign STATUS_PARAM) +static float128 addFloat128Sigs(float128 a, float128 b, flag zSign, + float_status *status) { int32 aExp, bExp, zExp; uint64_t aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2; @@ -6077,7 +6294,9 @@ static float128 addFloat128Sigs( float128 a, float128 b, flag zSign STATUS_PARAM expDiff = aExp - bExp; if ( 0 < expDiff ) { if ( aExp == 0x7FFF ) { - if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, b STATUS_VAR ); + if (aSig0 | aSig1) { + return propagateFloat128NaN(a, b, status); + } return a; } if ( bExp == 0 ) { @@ -6092,7 +6311,9 @@ static float128 addFloat128Sigs( float128 a, float128 b, flag zSign STATUS_PARAM } else if ( expDiff < 0 ) { if ( bExp == 0x7FFF ) { - if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b STATUS_VAR ); + if (bSig0 | bSig1) { + return propagateFloat128NaN(a, b, status); + } return packFloat128( zSign, 0x7FFF, 0, 0 ); } if ( aExp == 0 ) { @@ -6108,15 +6329,15 @@ static float128 addFloat128Sigs( float128 a, float128 b, flag zSign STATUS_PARAM else { if ( aExp == 0x7FFF ) { if ( aSig0 | aSig1 | bSig0 | bSig1 ) { - return propagateFloat128NaN( a, b STATUS_VAR ); + return propagateFloat128NaN(a, b, status); } return a; } add128( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1 ); if ( aExp == 0 ) { - if (STATUS(flush_to_zero)) { + if (status->flush_to_zero) { if (zSig0 | zSig1) { - float_raise(float_flag_output_denormal STATUS_VAR); + float_raise(float_flag_output_denormal, status); } return packFloat128(zSign, 0, 0, 0); } @@ -6136,7 +6357,7 @@ static float128 addFloat128Sigs( float128 a, float128 b, flag zSign STATUS_PARAM shift128ExtraRightJamming( zSig0, zSig1, zSig2, 1, &zSig0, &zSig1, &zSig2 ); roundAndPack: - return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 STATUS_VAR ); + return roundAndPackFloat128(zSign, zExp, zSig0, zSig1, zSig2, status); } @@ -6148,7 +6369,8 @@ static float128 addFloat128Sigs( float128 a, float128 b, flag zSign STATUS_PARAM | Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -static float128 subFloat128Sigs( float128 a, float128 b, flag zSign STATUS_PARAM) +static float128 subFloat128Sigs(float128 a, float128 b, flag zSign, + float_status *status) { int32 aExp, bExp, zExp; uint64_t aSig0, aSig1, bSig0, bSig1, zSig0, zSig1; @@ -6168,9 +6390,9 @@ static float128 subFloat128Sigs( float128 a, float128 b, flag zSign STATUS_PARAM if ( expDiff < 0 ) goto bExpBigger; if ( aExp == 0x7FFF ) { if ( aSig0 | aSig1 | bSig0 | bSig1 ) { - return propagateFloat128NaN( a, b STATUS_VAR ); + return propagateFloat128NaN(a, b, status); } - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); z.low = float128_default_nan_low; z.high = float128_default_nan_high; return z; @@ -6183,10 +6405,13 @@ static float128 subFloat128Sigs( float128 a, float128 b, flag zSign STATUS_PARAM if ( aSig0 < bSig0 ) goto bBigger; if ( bSig1 < aSig1 ) goto aBigger; if ( aSig1 < bSig1 ) goto bBigger; - return packFloat128( STATUS(float_rounding_mode) == float_round_down, 0, 0, 0 ); + return packFloat128(status->float_rounding_mode == float_round_down, + 0, 0, 0); bExpBigger: if ( bExp == 0x7FFF ) { - if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b STATUS_VAR ); + if (bSig0 | bSig1) { + return propagateFloat128NaN(a, b, status); + } return packFloat128( zSign ^ 1, 0x7FFF, 0, 0 ); } if ( aExp == 0 ) { @@ -6204,7 +6429,9 @@ static float128 subFloat128Sigs( float128 a, float128 b, flag zSign STATUS_PARAM goto normalizeRoundAndPack; aExpBigger: if ( aExp == 0x7FFF ) { - if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, b STATUS_VAR ); + if (aSig0 | aSig1) { + return propagateFloat128NaN(a, b, status); + } return a; } if ( bExp == 0 ) { @@ -6220,7 +6447,8 @@ static float128 subFloat128Sigs( float128 a, float128 b, flag zSign STATUS_PARAM zExp = aExp; normalizeRoundAndPack: --zExp; - return normalizeRoundAndPackFloat128( zSign, zExp - 14, zSig0, zSig1 STATUS_VAR ); + return normalizeRoundAndPackFloat128(zSign, zExp - 14, zSig0, zSig1, + status); } @@ -6230,17 +6458,17 @@ static float128 subFloat128Sigs( float128 a, float128 b, flag zSign STATUS_PARAM | for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -float128 float128_add( float128 a, float128 b STATUS_PARAM ) +float128 float128_add(float128 a, float128 b, float_status *status) { flag aSign, bSign; aSign = extractFloat128Sign( a ); bSign = extractFloat128Sign( b ); if ( aSign == bSign ) { - return addFloat128Sigs( a, b, aSign STATUS_VAR ); + return addFloat128Sigs(a, b, aSign, status); } else { - return subFloat128Sigs( a, b, aSign STATUS_VAR ); + return subFloat128Sigs(a, b, aSign, status); } } @@ -6251,17 +6479,17 @@ float128 float128_add( float128 a, float128 b STATUS_PARAM ) | Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -float128 float128_sub( float128 a, float128 b STATUS_PARAM ) +float128 float128_sub(float128 a, float128 b, float_status *status) { flag aSign, bSign; aSign = extractFloat128Sign( a ); bSign = extractFloat128Sign( b ); if ( aSign == bSign ) { - return subFloat128Sigs( a, b, aSign STATUS_VAR ); + return subFloat128Sigs(a, b, aSign, status); } else { - return addFloat128Sigs( a, b, aSign STATUS_VAR ); + return addFloat128Sigs(a, b, aSign, status); } } @@ -6272,7 +6500,7 @@ float128 float128_sub( float128 a, float128 b STATUS_PARAM ) | Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -float128 float128_mul( float128 a, float128 b STATUS_PARAM ) +float128 float128_mul(float128 a, float128 b, float_status *status) { flag aSign, bSign, zSign; int32 aExp, bExp, zExp; @@ -6291,16 +6519,18 @@ float128 float128_mul( float128 a, float128 b STATUS_PARAM ) if ( aExp == 0x7FFF ) { if ( ( aSig0 | aSig1 ) || ( ( bExp == 0x7FFF ) && ( bSig0 | bSig1 ) ) ) { - return propagateFloat128NaN( a, b STATUS_VAR ); + return propagateFloat128NaN(a, b, status); } if ( ( bExp | bSig0 | bSig1 ) == 0 ) goto invalid; return packFloat128( zSign, 0x7FFF, 0, 0 ); } if ( bExp == 0x7FFF ) { - if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b STATUS_VAR ); + if (bSig0 | bSig1) { + return propagateFloat128NaN(a, b, status); + } if ( ( aExp | aSig0 | aSig1 ) == 0 ) { invalid: - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); z.low = float128_default_nan_low; z.high = float128_default_nan_high; return z; @@ -6326,7 +6556,7 @@ float128 float128_mul( float128 a, float128 b STATUS_PARAM ) zSig0, zSig1, zSig2, 1, &zSig0, &zSig1, &zSig2 ); ++zExp; } - return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 STATUS_VAR ); + return roundAndPackFloat128(zSign, zExp, zSig0, zSig1, zSig2, status); } @@ -6336,7 +6566,7 @@ float128 float128_mul( float128 a, float128 b STATUS_PARAM ) | the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -float128 float128_div( float128 a, float128 b STATUS_PARAM ) +float128 float128_div(float128 a, float128 b, float_status *status) { flag aSign, bSign, zSign; int32 aExp, bExp, zExp; @@ -6354,27 +6584,33 @@ float128 float128_div( float128 a, float128 b STATUS_PARAM ) bSign = extractFloat128Sign( b ); zSign = aSign ^ bSign; if ( aExp == 0x7FFF ) { - if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, b STATUS_VAR ); + if (aSig0 | aSig1) { + return propagateFloat128NaN(a, b, status); + } if ( bExp == 0x7FFF ) { - if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b STATUS_VAR ); + if (bSig0 | bSig1) { + return propagateFloat128NaN(a, b, status); + } goto invalid; } return packFloat128( zSign, 0x7FFF, 0, 0 ); } if ( bExp == 0x7FFF ) { - if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b STATUS_VAR ); + if (bSig0 | bSig1) { + return propagateFloat128NaN(a, b, status); + } return packFloat128( zSign, 0, 0, 0 ); } if ( bExp == 0 ) { if ( ( bSig0 | bSig1 ) == 0 ) { if ( ( aExp | aSig0 | aSig1 ) == 0 ) { invalid: - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); z.low = float128_default_nan_low; z.high = float128_default_nan_high; return z; } - float_raise( float_flag_divbyzero STATUS_VAR); + float_raise(float_flag_divbyzero, status); return packFloat128( zSign, 0x7FFF, 0, 0 ); } normalizeFloat128Subnormal( bSig0, bSig1, &bExp, &bSig0, &bSig1 ); @@ -6410,7 +6646,7 @@ float128 float128_div( float128 a, float128 b STATUS_PARAM ) zSig1 |= ( ( rem1 | rem2 | rem3 ) != 0 ); } shift128ExtraRightJamming( zSig0, zSig1, 0, 15, &zSig0, &zSig1, &zSig2 ); - return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 STATUS_VAR ); + return roundAndPackFloat128(zSign, zExp, zSig0, zSig1, zSig2, status); } @@ -6420,7 +6656,7 @@ float128 float128_div( float128 a, float128 b STATUS_PARAM ) | according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -float128 float128_rem( float128 a, float128 b STATUS_PARAM ) +float128 float128_rem(float128 a, float128 b, float_status *status) { flag aSign, zSign; int32 aExp, bExp, expDiff; @@ -6439,18 +6675,20 @@ float128 float128_rem( float128 a, float128 b STATUS_PARAM ) if ( aExp == 0x7FFF ) { if ( ( aSig0 | aSig1 ) || ( ( bExp == 0x7FFF ) && ( bSig0 | bSig1 ) ) ) { - return propagateFloat128NaN( a, b STATUS_VAR ); + return propagateFloat128NaN(a, b, status); } goto invalid; } if ( bExp == 0x7FFF ) { - if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b STATUS_VAR ); + if (bSig0 | bSig1) { + return propagateFloat128NaN(a, b, status); + } return a; } if ( bExp == 0 ) { if ( ( bSig0 | bSig1 ) == 0 ) { invalid: - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); z.low = float128_default_nan_low; z.high = float128_default_nan_high; return z; @@ -6518,9 +6756,8 @@ float128 float128_rem( float128 a, float128 b STATUS_PARAM ) } zSign = ( (int64_t) aSig0 < 0 ); if ( zSign ) sub128( 0, 0, aSig0, aSig1, &aSig0, &aSig1 ); - return - normalizeRoundAndPackFloat128( aSign ^ zSign, bExp - 4, aSig0, aSig1 STATUS_VAR ); - + return normalizeRoundAndPackFloat128(aSign ^ zSign, bExp - 4, aSig0, aSig1, + status); } /*---------------------------------------------------------------------------- @@ -6529,7 +6766,7 @@ float128 float128_rem( float128 a, float128 b STATUS_PARAM ) | Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -float128 float128_sqrt( float128 a STATUS_PARAM ) +float128 float128_sqrt(float128 a, float_status *status) { flag aSign; int32 aExp, zExp; @@ -6542,14 +6779,16 @@ float128 float128_sqrt( float128 a STATUS_PARAM ) aExp = extractFloat128Exp( a ); aSign = extractFloat128Sign( a ); if ( aExp == 0x7FFF ) { - if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, a STATUS_VAR ); + if (aSig0 | aSig1) { + return propagateFloat128NaN(a, a, status); + } if ( ! aSign ) return a; goto invalid; } if ( aSign ) { if ( ( aExp | aSig0 | aSig1 ) == 0 ) return a; invalid: - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); z.low = float128_default_nan_low; z.high = float128_default_nan_high; return z; @@ -6588,7 +6827,7 @@ float128 float128_sqrt( float128 a STATUS_PARAM ) zSig1 |= ( ( rem1 | rem2 | rem3 ) != 0 ); } shift128ExtraRightJamming( zSig0, zSig1, 0, 14, &zSig0, &zSig1, &zSig2 ); - return roundAndPackFloat128( 0, zExp, zSig0, zSig1, zSig2 STATUS_VAR ); + return roundAndPackFloat128(0, zExp, zSig0, zSig1, zSig2, status); } @@ -6599,7 +6838,7 @@ float128 float128_sqrt( float128 a STATUS_PARAM ) | according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int float128_eq( float128 a, float128 b STATUS_PARAM ) +int float128_eq(float128 a, float128 b, float_status *status) { if ( ( ( extractFloat128Exp( a ) == 0x7FFF ) @@ -6607,7 +6846,7 @@ int float128_eq( float128 a, float128 b STATUS_PARAM ) || ( ( extractFloat128Exp( b ) == 0x7FFF ) && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return 0; } return @@ -6626,7 +6865,7 @@ int float128_eq( float128 a, float128 b STATUS_PARAM ) | according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int float128_le( float128 a, float128 b STATUS_PARAM ) +int float128_le(float128 a, float128 b, float_status *status) { flag aSign, bSign; @@ -6635,7 +6874,7 @@ int float128_le( float128 a, float128 b STATUS_PARAM ) || ( ( extractFloat128Exp( b ) == 0x7FFF ) && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return 0; } aSign = extractFloat128Sign( a ); @@ -6659,7 +6898,7 @@ int float128_le( float128 a, float128 b STATUS_PARAM ) | to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int float128_lt( float128 a, float128 b STATUS_PARAM ) +int float128_lt(float128 a, float128 b, float_status *status) { flag aSign, bSign; @@ -6668,7 +6907,7 @@ int float128_lt( float128 a, float128 b STATUS_PARAM ) || ( ( extractFloat128Exp( b ) == 0x7FFF ) && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return 0; } aSign = extractFloat128Sign( a ); @@ -6692,14 +6931,14 @@ int float128_lt( float128 a, float128 b STATUS_PARAM ) | Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int float128_unordered( float128 a, float128 b STATUS_PARAM ) +int float128_unordered(float128 a, float128 b, float_status *status) { if ( ( ( extractFloat128Exp( a ) == 0x7FFF ) && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) ) || ( ( extractFloat128Exp( b ) == 0x7FFF ) && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return 1; } return 0; @@ -6712,7 +6951,7 @@ int float128_unordered( float128 a, float128 b STATUS_PARAM ) | for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int float128_eq_quiet( float128 a, float128 b STATUS_PARAM ) +int float128_eq_quiet(float128 a, float128 b, float_status *status) { if ( ( ( extractFloat128Exp( a ) == 0x7FFF ) @@ -6722,7 +6961,7 @@ int float128_eq_quiet( float128 a, float128 b STATUS_PARAM ) ) { if ( float128_is_signaling_nan( a ) || float128_is_signaling_nan( b ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); } return 0; } @@ -6742,7 +6981,7 @@ int float128_eq_quiet( float128 a, float128 b STATUS_PARAM ) | IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int float128_le_quiet( float128 a, float128 b STATUS_PARAM ) +int float128_le_quiet(float128 a, float128 b, float_status *status) { flag aSign, bSign; @@ -6753,7 +6992,7 @@ int float128_le_quiet( float128 a, float128 b STATUS_PARAM ) ) { if ( float128_is_signaling_nan( a ) || float128_is_signaling_nan( b ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); } return 0; } @@ -6778,7 +7017,7 @@ int float128_le_quiet( float128 a, float128 b STATUS_PARAM ) | Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int float128_lt_quiet( float128 a, float128 b STATUS_PARAM ) +int float128_lt_quiet(float128 a, float128 b, float_status *status) { flag aSign, bSign; @@ -6789,7 +7028,7 @@ int float128_lt_quiet( float128 a, float128 b STATUS_PARAM ) ) { if ( float128_is_signaling_nan( a ) || float128_is_signaling_nan( b ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); } return 0; } @@ -6814,7 +7053,7 @@ int float128_lt_quiet( float128 a, float128 b STATUS_PARAM ) | Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int float128_unordered_quiet( float128 a, float128 b STATUS_PARAM ) +int float128_unordered_quiet(float128 a, float128 b, float_status *status) { if ( ( ( extractFloat128Exp( a ) == 0x7FFF ) && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) ) @@ -6823,7 +7062,7 @@ int float128_unordered_quiet( float128 a, float128 b STATUS_PARAM ) ) { if ( float128_is_signaling_nan( a ) || float128_is_signaling_nan( b ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); } return 1; } @@ -6831,23 +7070,23 @@ int float128_unordered_quiet( float128 a, float128 b STATUS_PARAM ) } /* misc functions */ -float32 uint32_to_float32(uint32_t a STATUS_PARAM) +float32 uint32_to_float32(uint32_t a, float_status *status) { - return int64_to_float32(a STATUS_VAR); + return int64_to_float32(a, status); } -float64 uint32_to_float64(uint32_t a STATUS_PARAM) +float64 uint32_to_float64(uint32_t a, float_status *status) { - return int64_to_float64(a STATUS_VAR); + return int64_to_float64(a, status); } -uint32 float32_to_uint32( float32 a STATUS_PARAM ) +uint32 float32_to_uint32(float32 a, float_status *status) { int64_t v; uint32 res; int old_exc_flags = get_float_exception_flags(status); - v = float32_to_int64(a STATUS_VAR); + v = float32_to_int64(a, status); if (v < 0) { res = 0; } else if (v > 0xffffffff) { @@ -6856,17 +7095,17 @@ uint32 float32_to_uint32( float32 a STATUS_PARAM ) return v; } set_float_exception_flags(old_exc_flags, status); - float_raise(float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return res; } -uint32 float32_to_uint32_round_to_zero( float32 a STATUS_PARAM ) +uint32 float32_to_uint32_round_to_zero(float32 a, float_status *status) { int64_t v; uint32 res; int old_exc_flags = get_float_exception_flags(status); - v = float32_to_int64_round_to_zero(a STATUS_VAR); + v = float32_to_int64_round_to_zero(a, status); if (v < 0) { res = 0; } else if (v > 0xffffffff) { @@ -6875,17 +7114,17 @@ uint32 float32_to_uint32_round_to_zero( float32 a STATUS_PARAM ) return v; } set_float_exception_flags(old_exc_flags, status); - float_raise(float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return res; } -int_fast16_t float32_to_int16(float32 a STATUS_PARAM) +int_fast16_t float32_to_int16(float32 a, float_status *status) { int32_t v; int_fast16_t res; int old_exc_flags = get_float_exception_flags(status); - v = float32_to_int32(a STATUS_VAR); + v = float32_to_int32(a, status); if (v < -0x8000) { res = -0x8000; } else if (v > 0x7fff) { @@ -6895,17 +7134,17 @@ int_fast16_t float32_to_int16(float32 a STATUS_PARAM) } set_float_exception_flags(old_exc_flags, status); - float_raise(float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return res; } -uint_fast16_t float32_to_uint16(float32 a STATUS_PARAM) +uint_fast16_t float32_to_uint16(float32 a, float_status *status) { int32_t v; uint_fast16_t res; int old_exc_flags = get_float_exception_flags(status); - v = float32_to_int32(a STATUS_VAR); + v = float32_to_int32(a, status); if (v < 0) { res = 0; } else if (v > 0xffff) { @@ -6915,17 +7154,17 @@ uint_fast16_t float32_to_uint16(float32 a STATUS_PARAM) } set_float_exception_flags(old_exc_flags, status); - float_raise(float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return res; } -uint_fast16_t float32_to_uint16_round_to_zero(float32 a STATUS_PARAM) +uint_fast16_t float32_to_uint16_round_to_zero(float32 a, float_status *status) { int64_t v; uint_fast16_t res; int old_exc_flags = get_float_exception_flags(status); - v = float32_to_int64_round_to_zero(a STATUS_VAR); + v = float32_to_int64_round_to_zero(a, status); if (v < 0) { res = 0; } else if (v > 0xffff) { @@ -6934,51 +7173,51 @@ uint_fast16_t float32_to_uint16_round_to_zero(float32 a STATUS_PARAM) return v; } set_float_exception_flags(old_exc_flags, status); - float_raise(float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return res; } -uint32 float64_to_uint32( float64 a STATUS_PARAM ) +uint32 float64_to_uint32(float64 a, float_status *status) { uint64_t v; uint32 res; int old_exc_flags = get_float_exception_flags(status); - v = float64_to_uint64(a STATUS_VAR); + v = float64_to_uint64(a, status); if (v > 0xffffffff) { res = 0xffffffff; } else { return v; } set_float_exception_flags(old_exc_flags, status); - float_raise(float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return res; } -uint32 float64_to_uint32_round_to_zero( float64 a STATUS_PARAM ) +uint32 float64_to_uint32_round_to_zero(float64 a, float_status *status) { uint64_t v; uint32 res; int old_exc_flags = get_float_exception_flags(status); - v = float64_to_uint64_round_to_zero(a STATUS_VAR); + v = float64_to_uint64_round_to_zero(a, status); if (v > 0xffffffff) { res = 0xffffffff; } else { return v; } set_float_exception_flags(old_exc_flags, status); - float_raise(float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return res; } -int_fast16_t float64_to_int16(float64 a STATUS_PARAM) +int_fast16_t float64_to_int16(float64 a, float_status *status) { int64_t v; int_fast16_t res; int old_exc_flags = get_float_exception_flags(status); - v = float64_to_int32(a STATUS_VAR); + v = float64_to_int32(a, status); if (v < -0x8000) { res = -0x8000; } else if (v > 0x7fff) { @@ -6988,17 +7227,17 @@ int_fast16_t float64_to_int16(float64 a STATUS_PARAM) } set_float_exception_flags(old_exc_flags, status); - float_raise(float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return res; } -uint_fast16_t float64_to_uint16(float64 a STATUS_PARAM) +uint_fast16_t float64_to_uint16(float64 a, float_status *status) { int64_t v; uint_fast16_t res; int old_exc_flags = get_float_exception_flags(status); - v = float64_to_int32(a STATUS_VAR); + v = float64_to_int32(a, status); if (v < 0) { res = 0; } else if (v > 0xffff) { @@ -7008,17 +7247,17 @@ uint_fast16_t float64_to_uint16(float64 a STATUS_PARAM) } set_float_exception_flags(old_exc_flags, status); - float_raise(float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return res; } -uint_fast16_t float64_to_uint16_round_to_zero(float64 a STATUS_PARAM) +uint_fast16_t float64_to_uint16_round_to_zero(float64 a, float_status *status) { int64_t v; uint_fast16_t res; int old_exc_flags = get_float_exception_flags(status); - v = float64_to_int64_round_to_zero(a STATUS_VAR); + v = float64_to_int64_round_to_zero(a, status); if (v < 0) { res = 0; } else if (v > 0xffff) { @@ -7027,7 +7266,7 @@ uint_fast16_t float64_to_uint16_round_to_zero(float64 a STATUS_PARAM) return v; } set_float_exception_flags(old_exc_flags, status); - float_raise(float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return res; } @@ -7043,18 +7282,18 @@ uint_fast16_t float64_to_uint16_round_to_zero(float64 a STATUS_PARAM) | will raise the inexact exception. *----------------------------------------------------------------------------*/ -uint64_t float64_to_uint64(float64 a STATUS_PARAM) +uint64_t float64_to_uint64(float64 a, float_status *status) { flag aSign; int_fast16_t aExp, shiftCount; uint64_t aSig, aSigExtra; - a = float64_squash_input_denormal(a STATUS_VAR); + a = float64_squash_input_denormal(a, status); aSig = extractFloat64Frac(a); aExp = extractFloat64Exp(a); aSign = extractFloat64Sign(a); if (aSign && (aExp > 1022)) { - float_raise(float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); if (float64_is_any_nan(a)) { return LIT64(0xFFFFFFFFFFFFFFFF); } else { @@ -7067,7 +7306,7 @@ uint64_t float64_to_uint64(float64 a STATUS_PARAM) shiftCount = 0x433 - aExp; if (shiftCount <= 0) { if (0x43E < aExp) { - float_raise(float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return LIT64(0xFFFFFFFFFFFFFFFF); } aSigExtra = 0; @@ -7075,26 +7314,26 @@ uint64_t float64_to_uint64(float64 a STATUS_PARAM) } else { shift64ExtraRightJamming(aSig, 0, shiftCount, &aSig, &aSigExtra); } - return roundAndPackUint64(aSign, aSig, aSigExtra STATUS_VAR); + return roundAndPackUint64(aSign, aSig, aSigExtra, status); } -uint64_t float64_to_uint64_round_to_zero (float64 a STATUS_PARAM) +uint64_t float64_to_uint64_round_to_zero(float64 a, float_status *status) { - signed char current_rounding_mode = STATUS(float_rounding_mode); - set_float_rounding_mode(float_round_to_zero STATUS_VAR); - int64_t v = float64_to_uint64(a STATUS_VAR); - set_float_rounding_mode(current_rounding_mode STATUS_VAR); + signed char current_rounding_mode = status->float_rounding_mode; + set_float_rounding_mode(float_round_to_zero, status); + int64_t v = float64_to_uint64(a, status); + set_float_rounding_mode(current_rounding_mode, status); return v; } #define COMPARE(s, nan_exp) \ -static inline int float ## s ## _compare_internal( float ## s a, float ## s b, \ - int is_quiet STATUS_PARAM ) \ +static inline int float ## s ## _compare_internal(float ## s a, float ## s b,\ + int is_quiet, float_status *status) \ { \ flag aSign, bSign; \ uint ## s ## _t av, bv; \ - a = float ## s ## _squash_input_denormal(a STATUS_VAR); \ - b = float ## s ## _squash_input_denormal(b STATUS_VAR); \ + a = float ## s ## _squash_input_denormal(a, status); \ + b = float ## s ## _squash_input_denormal(b, status); \ \ if (( ( extractFloat ## s ## Exp( a ) == nan_exp ) && \ extractFloat ## s ## Frac( a ) ) || \ @@ -7103,7 +7342,7 @@ static inline int float ## s ## _compare_internal( float ## s a, float ## s b, if (!is_quiet || \ float ## s ## _is_signaling_nan( a ) || \ float ## s ## _is_signaling_nan( b ) ) { \ - float_raise( float_flag_invalid STATUS_VAR); \ + float_raise(float_flag_invalid, status); \ } \ return float_relation_unordered; \ } \ @@ -7127,21 +7366,22 @@ static inline int float ## s ## _compare_internal( float ## s a, float ## s b, } \ } \ \ -int float ## s ## _compare( float ## s a, float ## s b STATUS_PARAM ) \ +int float ## s ## _compare(float ## s a, float ## s b, float_status *status) \ { \ - return float ## s ## _compare_internal(a, b, 0 STATUS_VAR); \ + return float ## s ## _compare_internal(a, b, 0, status); \ } \ \ -int float ## s ## _compare_quiet( float ## s a, float ## s b STATUS_PARAM ) \ +int float ## s ## _compare_quiet(float ## s a, float ## s b, \ + float_status *status) \ { \ - return float ## s ## _compare_internal(a, b, 1 STATUS_VAR); \ + return float ## s ## _compare_internal(a, b, 1, status); \ } COMPARE(32, 0xff) COMPARE(64, 0x7ff) -static inline int floatx80_compare_internal( floatx80 a, floatx80 b, - int is_quiet STATUS_PARAM ) +static inline int floatx80_compare_internal(floatx80 a, floatx80 b, + int is_quiet, float_status *status) { flag aSign, bSign; @@ -7152,7 +7392,7 @@ static inline int floatx80_compare_internal( floatx80 a, floatx80 b, if (!is_quiet || floatx80_is_signaling_nan( a ) || floatx80_is_signaling_nan( b ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); } return float_relation_unordered; } @@ -7176,18 +7416,18 @@ static inline int floatx80_compare_internal( floatx80 a, floatx80 b, } } -int floatx80_compare( floatx80 a, floatx80 b STATUS_PARAM ) +int floatx80_compare(floatx80 a, floatx80 b, float_status *status) { - return floatx80_compare_internal(a, b, 0 STATUS_VAR); + return floatx80_compare_internal(a, b, 0, status); } -int floatx80_compare_quiet( floatx80 a, floatx80 b STATUS_PARAM ) +int floatx80_compare_quiet(floatx80 a, floatx80 b, float_status *status) { - return floatx80_compare_internal(a, b, 1 STATUS_VAR); + return floatx80_compare_internal(a, b, 1, status); } -static inline int float128_compare_internal( float128 a, float128 b, - int is_quiet STATUS_PARAM ) +static inline int float128_compare_internal(float128 a, float128 b, + int is_quiet, float_status *status) { flag aSign, bSign; @@ -7198,7 +7438,7 @@ static inline int float128_compare_internal( float128 a, float128 b, if (!is_quiet || float128_is_signaling_nan( a ) || float128_is_signaling_nan( b ) ) { - float_raise( float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); } return float_relation_unordered; } @@ -7220,14 +7460,14 @@ static inline int float128_compare_internal( float128 a, float128 b, } } -int float128_compare( float128 a, float128 b STATUS_PARAM ) +int float128_compare(float128 a, float128 b, float_status *status) { - return float128_compare_internal(a, b, 0 STATUS_VAR); + return float128_compare_internal(a, b, 0, status); } -int float128_compare_quiet( float128 a, float128 b STATUS_PARAM ) +int float128_compare_quiet(float128 a, float128 b, float_status *status) { - return float128_compare_internal(a, b, 1 STATUS_VAR); + return float128_compare_internal(a, b, 1, status); } /* min() and max() functions. These can't be implemented as @@ -7247,12 +7487,13 @@ int float128_compare_quiet( float128 a, float128 b STATUS_PARAM ) #define MINMAX(s) \ static inline float ## s float ## s ## _minmax(float ## s a, float ## s b, \ int ismin, int isieee, \ - int ismag STATUS_PARAM) \ + int ismag, \ + float_status *status) \ { \ flag aSign, bSign; \ uint ## s ## _t av, bv, aav, abv; \ - a = float ## s ## _squash_input_denormal(a STATUS_VAR); \ - b = float ## s ## _squash_input_denormal(b STATUS_VAR); \ + a = float ## s ## _squash_input_denormal(a, status); \ + b = float ## s ## _squash_input_denormal(b, status); \ if (float ## s ## _is_any_nan(a) || \ float ## s ## _is_any_nan(b)) { \ if (isieee) { \ @@ -7264,7 +7505,7 @@ static inline float ## s float ## s ## _minmax(float ## s a, float ## s b, \ return a; \ } \ } \ - return propagateFloat ## s ## NaN(a, b STATUS_VAR); \ + return propagateFloat ## s ## NaN(a, b, status); \ } \ aSign = extractFloat ## s ## Sign(a); \ bSign = extractFloat ## s ## Sign(b); \ @@ -7296,34 +7537,40 @@ static inline float ## s float ## s ## _minmax(float ## s a, float ## s b, \ } \ } \ \ -float ## s float ## s ## _min(float ## s a, float ## s b STATUS_PARAM) \ +float ## s float ## s ## _min(float ## s a, float ## s b, \ + float_status *status) \ { \ - return float ## s ## _minmax(a, b, 1, 0, 0 STATUS_VAR); \ + return float ## s ## _minmax(a, b, 1, 0, 0, status); \ } \ \ -float ## s float ## s ## _max(float ## s a, float ## s b STATUS_PARAM) \ +float ## s float ## s ## _max(float ## s a, float ## s b, \ + float_status *status) \ { \ - return float ## s ## _minmax(a, b, 0, 0, 0 STATUS_VAR); \ + return float ## s ## _minmax(a, b, 0, 0, 0, status); \ } \ \ -float ## s float ## s ## _minnum(float ## s a, float ## s b STATUS_PARAM) \ +float ## s float ## s ## _minnum(float ## s a, float ## s b, \ + float_status *status) \ { \ - return float ## s ## _minmax(a, b, 1, 1, 0 STATUS_VAR); \ + return float ## s ## _minmax(a, b, 1, 1, 0, status); \ } \ \ -float ## s float ## s ## _maxnum(float ## s a, float ## s b STATUS_PARAM) \ +float ## s float ## s ## _maxnum(float ## s a, float ## s b, \ + float_status *status) \ { \ - return float ## s ## _minmax(a, b, 0, 1, 0 STATUS_VAR); \ + return float ## s ## _minmax(a, b, 0, 1, 0, status); \ } \ \ -float ## s float ## s ## _minnummag(float ## s a, float ## s b STATUS_PARAM) \ +float ## s float ## s ## _minnummag(float ## s a, float ## s b, \ + float_status *status) \ { \ - return float ## s ## _minmax(a, b, 1, 1, 1 STATUS_VAR); \ + return float ## s ## _minmax(a, b, 1, 1, 1, status); \ } \ \ -float ## s float ## s ## _maxnummag(float ## s a, float ## s b STATUS_PARAM) \ +float ## s float ## s ## _maxnummag(float ## s a, float ## s b, \ + float_status *status) \ { \ - return float ## s ## _minmax(a, b, 0, 1, 1 STATUS_VAR); \ + return float ## s ## _minmax(a, b, 0, 1, 1, status); \ } MINMAX(32) @@ -7331,20 +7578,20 @@ MINMAX(64) /* Multiply A by 2 raised to the power N. */ -float32 float32_scalbn( float32 a, int n STATUS_PARAM ) +float32 float32_scalbn(float32 a, int n, float_status *status) { flag aSign; int16_t aExp; uint32_t aSig; - a = float32_squash_input_denormal(a STATUS_VAR); + a = float32_squash_input_denormal(a, status); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); aSign = extractFloat32Sign( a ); if ( aExp == 0xFF ) { if ( aSig ) { - return propagateFloat32NaN( a, a STATUS_VAR ); + return propagateFloat32NaN(a, a, status); } return a; } @@ -7364,23 +7611,23 @@ float32 float32_scalbn( float32 a, int n STATUS_PARAM ) aExp += n - 1; aSig <<= 7; - return normalizeRoundAndPackFloat32( aSign, aExp, aSig STATUS_VAR ); + return normalizeRoundAndPackFloat32(aSign, aExp, aSig, status); } -float64 float64_scalbn( float64 a, int n STATUS_PARAM ) +float64 float64_scalbn(float64 a, int n, float_status *status) { flag aSign; int16_t aExp; uint64_t aSig; - a = float64_squash_input_denormal(a STATUS_VAR); + a = float64_squash_input_denormal(a, status); aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); aSign = extractFloat64Sign( a ); if ( aExp == 0x7FF ) { if ( aSig ) { - return propagateFloat64NaN( a, a STATUS_VAR ); + return propagateFloat64NaN(a, a, status); } return a; } @@ -7400,10 +7647,10 @@ float64 float64_scalbn( float64 a, int n STATUS_PARAM ) aExp += n - 1; aSig <<= 10; - return normalizeRoundAndPackFloat64( aSign, aExp, aSig STATUS_VAR ); + return normalizeRoundAndPackFloat64(aSign, aExp, aSig, status); } -floatx80 floatx80_scalbn( floatx80 a, int n STATUS_PARAM ) +floatx80 floatx80_scalbn(floatx80 a, int n, float_status *status) { flag aSign; int32_t aExp; @@ -7415,7 +7662,7 @@ floatx80 floatx80_scalbn( floatx80 a, int n STATUS_PARAM ) if ( aExp == 0x7FFF ) { if ( aSig<<1 ) { - return propagateFloatx80NaN( a, a STATUS_VAR ); + return propagateFloatx80NaN(a, a, status); } return a; } @@ -7434,11 +7681,11 @@ floatx80 floatx80_scalbn( floatx80 a, int n STATUS_PARAM ) } aExp += n; - return normalizeRoundAndPackFloatx80( STATUS(floatx80_rounding_precision), - aSign, aExp, aSig, 0 STATUS_VAR ); + return normalizeRoundAndPackFloatx80(status->floatx80_rounding_precision, + aSign, aExp, aSig, 0, status); } -float128 float128_scalbn( float128 a, int n STATUS_PARAM ) +float128 float128_scalbn(float128 a, int n, float_status *status) { flag aSign; int32_t aExp; @@ -7450,7 +7697,7 @@ float128 float128_scalbn( float128 a, int n STATUS_PARAM ) aSign = extractFloat128Sign( a ); if ( aExp == 0x7FFF ) { if ( aSig0 | aSig1 ) { - return propagateFloat128NaN( a, a STATUS_VAR ); + return propagateFloat128NaN(a, a, status); } return a; } @@ -7470,6 +7717,6 @@ float128 float128_scalbn( float128 a, int n STATUS_PARAM ) aExp += n - 1; return normalizeRoundAndPackFloat128( aSign, aExp, aSig0, aSig1 - STATUS_VAR ); + , status); } diff --git a/fsdev/virtfs-proxy-helper.c b/fsdev/virtfs-proxy-helper.c index cd291d3..a698e2d 100644 --- a/fsdev/virtfs-proxy-helper.c +++ b/fsdev/virtfs-proxy-helper.c @@ -117,7 +117,7 @@ error: static int init_capabilities(void) { - /* helper needs following capbabilities only */ + /* helper needs following capabilities only */ cap_value_t cap_list[] = { CAP_CHOWN, CAP_DAC_OVERRIDE, @@ -262,6 +262,9 @@ static int send_status(int sockfd, struct iovec *iovec, int status) */ msg_size = proxy_marshal(iovec, 0, "ddd", header.type, header.size, status); + if (msg_size < 0) { + return msg_size; + } retval = socket_write(sockfd, iovec->iov_base, msg_size); if (retval < 0) { return retval; @@ -735,6 +738,7 @@ static int proxy_socket(const char *path, uid_t uid, gid_t gid) return -1; } + g_assert(strlen(path) < sizeof(proxy.sun_path)); sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0) { do_perror("socket"); @@ -749,24 +753,29 @@ static int proxy_socket(const char *path, uid_t uid, gid_t gid) if (bind(sock, (struct sockaddr *)&proxy, sizeof(struct sockaddr_un)) < 0) { do_perror("bind"); - return -1; + goto error; } if (chown(proxy.sun_path, uid, gid) < 0) { do_perror("chown"); - return -1; + goto error; } if (listen(sock, 1) < 0) { do_perror("listen"); - return -1; + goto error; } size = sizeof(qemu); client = accept(sock, (struct sockaddr *)&qemu, &size); if (client < 0) { do_perror("accept"); - return -1; + goto error; } + close(sock); return client; + +error: + close(sock); + return -1; } static void usage(char *prog) diff --git a/gdbstub.c b/gdbstub.c index 0faca56..8abcb8a 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -317,6 +317,8 @@ static GDBState *gdbserver_state; bool gdb_has_xml; +int semihosting_target = SEMIHOSTING_TARGET_AUTO; + #ifdef CONFIG_USER_ONLY /* XXX: This is not thread safe. Do we care? */ static int gdbserver_fd = -1; @@ -351,10 +353,19 @@ static enum { GDB_SYS_DISABLED, } gdb_syscall_mode; -/* If gdb is connected when the first semihosting syscall occurs then use - remote gdb syscalls. Otherwise use native file IO. */ +/* Decide if either remote gdb syscalls or native file IO should be used. */ int use_gdb_syscalls(void) { + if (semihosting_target == SEMIHOSTING_TARGET_NATIVE) { + /* -semihosting-config target=native */ + return false; + } else if (semihosting_target == SEMIHOSTING_TARGET_GDB) { + /* -semihosting-config target=gdb */ + return true; + } + + /* -semihosting-config target=auto */ + /* On the first call check if gdb is connected and remember. */ if (gdb_syscall_mode == GDB_SYS_UNKNOWN) { gdb_syscall_mode = (gdbserver_state ? GDB_SYS_ENABLED : GDB_SYS_DISABLED); @@ -1432,15 +1443,17 @@ void gdb_exit(CPUArchState *env, int code) if (gdbserver_fd < 0 || s->fd < 0) { return; } +#else + if (!s->chr) { + return; + } #endif snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code); put_packet(s, buf); #ifndef CONFIG_USER_ONLY - if (s->chr) { - qemu_chr_delete(s->chr); - } + qemu_chr_delete(s->chr); #endif } diff --git a/hmp-commands.hx b/hmp-commands.hx index e37bc8b..3089533 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -28,7 +28,7 @@ ETEXI .args_type = "device:B", .params = "device|all", .help = "commit changes to the disk images (if -snapshot is used) or backing files", - .mhandler.cmd = do_commit, + .mhandler.cmd = hmp_commit, }, STEXI @@ -47,7 +47,6 @@ ETEXI .args_type = "", .params = "", .help = "quit the emulator", - .user_print = monitor_user_noop, .mhandler.cmd = hmp_quit, }, @@ -180,7 +179,7 @@ ETEXI .params = "device", .help = "remove host block device", .user_print = monitor_user_noop, - .mhandler.cmd_new = do_drive_del, + .mhandler.cmd_new = hmp_drive_del, }, STEXI @@ -205,7 +204,6 @@ ETEXI STEXI @item change @var{device} @var{setting} @findex change - Change the configuration of a device. @table @option @@ -245,7 +243,7 @@ ETEXI .args_type = "filename:F", .params = "filename", .help = "save screen into PPM image 'filename'", - .mhandler.cmd = hmp_screen_dump, + .mhandler.cmd = hmp_screendump, }, STEXI @@ -259,7 +257,7 @@ ETEXI .args_type = "filename:F", .params = "filename", .help = "output logs to 'filename'", - .mhandler.cmd = do_logfile, + .mhandler.cmd = hmp_logfile, }, STEXI @@ -273,7 +271,7 @@ ETEXI .args_type = "name:s,option:b", .params = "name on|off", .help = "changes status of a specific trace event", - .mhandler.cmd = do_trace_event_set_state, + .mhandler.cmd = hmp_trace_event, }, STEXI @@ -288,7 +286,7 @@ ETEXI .args_type = "op:s?,arg:F?", .params = "on|off|flush|set [arg]", .help = "open, close, or flush trace file, or set a new file name", - .mhandler.cmd = do_trace_file, + .mhandler.cmd = hmp_trace_file, }, STEXI @@ -303,7 +301,7 @@ ETEXI .args_type = "items:s", .params = "item1[,...]", .help = "activate logging of the specified items", - .mhandler.cmd = do_log, + .mhandler.cmd = hmp_log, }, STEXI @@ -317,7 +315,7 @@ ETEXI .args_type = "name:s?", .params = "[tag|id]", .help = "save a VM snapshot. If no tag or id are provided, a new snapshot is created", - .mhandler.cmd = do_savevm, + .mhandler.cmd = hmp_savevm, }, STEXI @@ -334,7 +332,7 @@ ETEXI .args_type = "name:s", .params = "tag|id", .help = "restore a VM snapshot from its tag or id", - .mhandler.cmd = do_loadvm, + .mhandler.cmd = hmp_loadvm, .command_completion = loadvm_completion, }, @@ -350,7 +348,7 @@ ETEXI .args_type = "name:s", .params = "tag|id", .help = "delete a VM snapshot from its tag or id", - .mhandler.cmd = do_delvm, + .mhandler.cmd = hmp_delvm, .command_completion = delvm_completion, }, @@ -365,7 +363,7 @@ ETEXI .args_type = "option:s?", .params = "[on|off]", .help = "run emulation in singlestep mode or switch to normal mode", - .mhandler.cmd = do_singlestep, + .mhandler.cmd = hmp_singlestep, }, STEXI @@ -422,7 +420,7 @@ ETEXI .args_type = "device:s?", .params = "[device]", .help = "start gdbserver on given device (default 'tcp::1234'), stop with 'none'", - .mhandler.cmd = do_gdbserver, + .mhandler.cmd = hmp_gdbserver, }, STEXI @@ -436,7 +434,7 @@ ETEXI .args_type = "fmt:/,addr:l", .params = "/fmt addr", .help = "virtual memory dump starting at 'addr'", - .mhandler.cmd = do_memory_dump, + .mhandler.cmd = hmp_memory_dump, }, STEXI @@ -450,7 +448,7 @@ ETEXI .args_type = "fmt:/,addr:l", .params = "/fmt addr", .help = "physical memory dump starting at 'addr'", - .mhandler.cmd = do_physical_memory_dump, + .mhandler.cmd = hmp_physical_memory_dump, }, STEXI @@ -523,7 +521,6 @@ ETEXI STEXI @item p or print/@var{fmt} @var{expr} @findex print - Print expression value. Only the @var{format} part of @var{fmt} is used. ETEXI @@ -533,10 +530,12 @@ ETEXI .args_type = "fmt:/,addr:i,index:i.", .params = "/fmt addr", .help = "I/O port read", - .mhandler.cmd = do_ioport_read, + .mhandler.cmd = hmp_ioport_read, }, STEXI +@item i/@var{fmt} @var{addr} [.@var{index}] +@findex i Read I/O port. ETEXI @@ -545,10 +544,12 @@ ETEXI .args_type = "fmt:/,addr:i,val:i", .params = "/fmt addr value", .help = "I/O port write", - .mhandler.cmd = do_ioport_write, + .mhandler.cmd = hmp_ioport_write, }, STEXI +@item o/@var{fmt} @var{addr} @var{val} +@findex o Write to I/O port. ETEXI @@ -557,14 +558,13 @@ ETEXI .args_type = "keys:s,hold-time:i?", .params = "keys [hold_ms]", .help = "send keys to the VM (e.g. 'sendkey ctrl-alt-f1', default hold time=100 ms)", - .mhandler.cmd = hmp_send_key, + .mhandler.cmd = hmp_sendkey, .command_completion = sendkey_completion, }, STEXI @item sendkey @var{keys} @findex sendkey - Send @var{keys} to the guest. @var{keys} could be the name of the key or the raw value in hexadecimal format. Use @code{-} to press several keys simultaneously. Example: @@ -587,7 +587,6 @@ ETEXI STEXI @item system_reset @findex system_reset - Reset the system. ETEXI @@ -602,7 +601,6 @@ ETEXI STEXI @item system_powerdown @findex system_powerdown - Power down the system (if supported). ETEXI @@ -611,13 +609,12 @@ ETEXI .args_type = "start:i,size:i", .params = "addr size", .help = "compute the checksum of a memory region", - .mhandler.cmd = do_sum, + .mhandler.cmd = hmp_sum, }, STEXI @item sum @var{addr} @var{size} @findex sum - Compute the checksum of a memory region. ETEXI @@ -626,13 +623,12 @@ ETEXI .args_type = "devname:s", .params = "device", .help = "add USB device (e.g. 'host:bus.addr' or 'host:vendor_id:product_id')", - .mhandler.cmd = do_usb_add, + .mhandler.cmd = hmp_usb_add, }, STEXI @item usb_add @var{devname} @findex usb_add - Add the USB device @var{devname}. For details of available devices see @ref{usb_devices} ETEXI @@ -642,13 +638,12 @@ ETEXI .args_type = "devname:s", .params = "device", .help = "remove USB device 'bus.addr'", - .mhandler.cmd = do_usb_del, + .mhandler.cmd = hmp_usb_del, }, STEXI @item usb_del @var{devname} @findex usb_del - Remove the USB device @var{devname} from the QEMU virtual USB hub. @var{devname} has the syntax @code{bus.addr}. Use the monitor command @code{info usb} to see the devices you can remove. @@ -667,7 +662,6 @@ ETEXI STEXI @item device_add @var{config} @findex device_add - Add device. ETEXI @@ -683,7 +677,6 @@ ETEXI STEXI @item device_del @var{id} @findex device_del - Remove device @var{id}. ETEXI @@ -706,7 +699,7 @@ ETEXI .args_type = "dx_str:s,dy_str:s,dz_str:s?", .params = "dx dy [dz]", .help = "send mouse move events", - .mhandler.cmd = do_mouse_move, + .mhandler.cmd = hmp_mouse_move, }, STEXI @@ -721,7 +714,7 @@ ETEXI .args_type = "button_state:i", .params = "state", .help = "change mouse button state (1=L, 2=M, 4=R)", - .mhandler.cmd = do_mouse_button, + .mhandler.cmd = hmp_mouse_button, }, STEXI @@ -735,7 +728,7 @@ ETEXI .args_type = "index:i", .params = "index", .help = "set which mouse device receives events", - .mhandler.cmd = do_mouse_set, + .mhandler.cmd = hmp_mouse_set, }, STEXI @@ -753,7 +746,7 @@ ETEXI .args_type = "path:F,freq:i?,bits:i?,nchannels:i?", .params = "path [frequency [bits [channels]]]", .help = "capture audio to a wave file (default frequency=44100 bits=16 channels=2)", - .mhandler.cmd = do_wav_capture, + .mhandler.cmd = hmp_wavcapture, }, STEXI @item wavcapture @var{filename} [@var{frequency} [@var{bits} [@var{channels}]]] @@ -774,7 +767,7 @@ ETEXI .args_type = "n:i", .params = "capture index", .help = "stop capture", - .mhandler.cmd = do_stop_capture, + .mhandler.cmd = hmp_stopcapture, }, STEXI @item stopcapture @var{index} @@ -818,13 +811,12 @@ ETEXI .args_type = "bootdevice:s", .params = "bootdevice", .help = "define new values for the boot device list", - .mhandler.cmd = do_boot_set, + .mhandler.cmd = hmp_boot_set, }, STEXI @item boot_set @var{bootdevicelist} @findex boot_set - Define new values for the boot device list. Those values will override the values specified on the command line through the @code{-boot} option. @@ -837,7 +829,7 @@ ETEXI .args_type = "", .params = "", .help = "inject an NMI", - .mhandler.cmd = hmp_inject_nmi, + .mhandler.cmd = hmp_nmi, }, STEXI @item nmi @var{cpu} @@ -922,6 +914,22 @@ Cancel the current VM migration. ETEXI { + .name = "migrate_incoming", + .args_type = "uri:s", + .params = "uri", + .help = "Continue an incoming migration from an -incoming defer", + .mhandler.cmd = hmp_migrate_incoming, + }, + +STEXI +@item migrate_incoming @var{uri} +@findex migrate_incoming +Continue an incoming migration using the @var{uri} (that has the same syntax +as the -incoming option). + +ETEXI + + { .name = "migrate_set_cache_size", .args_type = "value:o", .params = "value", @@ -1140,7 +1148,7 @@ ETEXI "[,snapshot=on|off][,cache=on|off]\n" "[,readonly=on|off][,copy-on-read=on|off]", .help = "add drive to PCI storage controller", - .mhandler.cmd = drive_hot_add, + .mhandler.cmd = hmp_drive_add, }, STEXI @@ -1149,38 +1157,6 @@ STEXI Add drive to PCI storage controller. ETEXI -#if defined(CONFIG_PCI_HOTPLUG_OLD) - { - .name = "pci_add", - .args_type = "pci_addr:s,type:s,opts:s?", - .params = "auto|[[:]:] nic|storage [[vlan=n][,macaddr=addr][,model=type]] [file=file][,if=type][,bus=nr]...", - .help = "hot-add PCI device", - .mhandler.cmd = pci_device_hot_add, - }, -#endif - -STEXI -@item pci_add -@findex pci_add -Hot-add PCI device. -ETEXI - -#if defined(CONFIG_PCI_HOTPLUG_OLD) - { - .name = "pci_del", - .args_type = "pci_addr:s", - .params = "[[:]:]", - .help = "hot remove PCI device", - .mhandler.cmd = do_pci_device_hot_remove, - }, -#endif - -STEXI -@item pci_del -@findex pci_del -Hot remove PCI device. -ETEXI - { .name = "pcie_aer_inject_error", .args_type = "advisory_non_fatal:-a,correctable:-c," @@ -1197,7 +1173,7 @@ ETEXI " = 32bit x 4\n\t\t\t" " = 32bit x 4", .user_print = pcie_aer_inject_error_print, - .mhandler.cmd_new = do_pcie_aer_inject_error, + .mhandler.cmd_new = hmp_pcie_aer_inject_error, }, STEXI @@ -1211,7 +1187,7 @@ ETEXI .args_type = "device:s,opts:s?", .params = "tap|user|socket|vde|netmap|bridge|vhost-user|dump [options]", .help = "add host VLAN client", - .mhandler.cmd = net_host_device_add, + .mhandler.cmd = hmp_host_net_add, .command_completion = host_net_add_completion, }, @@ -1226,7 +1202,7 @@ ETEXI .args_type = "vlan_id:i,device:s", .params = "vlan_id name", .help = "remove host VLAN client", - .mhandler.cmd = net_host_device_remove, + .mhandler.cmd = hmp_host_net_remove, .command_completion = host_net_remove_completion, }, @@ -1302,7 +1278,7 @@ ETEXI .args_type = "arg1:s,arg2:s?,arg3:s?", .params = "[vlan_id name] [tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport", .help = "redirect TCP or UDP connections from host to guest (requires -net user)", - .mhandler.cmd = net_slirp_hostfwd_add, + .mhandler.cmd = hmp_hostfwd_add, }, #endif STEXI @@ -1317,7 +1293,7 @@ ETEXI .args_type = "arg1:s,arg2:s?,arg3:s?", .params = "[vlan_id name] [tcp|udp]:[hostaddr]:hostport", .help = "remove host-to-guest TCP or UDP redirection", - .mhandler.cmd = net_slirp_hostfwd_remove, + .mhandler.cmd = hmp_hostfwd_remove, }, #endif @@ -1361,7 +1337,7 @@ ETEXI .args_type = "action:s", .params = "[reset|shutdown|poweroff|pause|debug|none]", .help = "change watchdog action", - .mhandler.cmd = do_watchdog_action, + .mhandler.cmd = hmp_watchdog_action, .command_completion = watchdog_action_completion, }, @@ -1376,7 +1352,7 @@ ETEXI .args_type = "aclname:s", .params = "aclname", .help = "list rules in the access control list", - .mhandler.cmd = do_acl_show, + .mhandler.cmd = hmp_acl_show, }, STEXI @@ -1393,7 +1369,7 @@ ETEXI .args_type = "aclname:s,policy:s", .params = "aclname allow|deny", .help = "set default access control list policy", - .mhandler.cmd = do_acl_policy, + .mhandler.cmd = hmp_acl_policy, }, STEXI @@ -1409,7 +1385,7 @@ ETEXI .args_type = "aclname:s,match:s,policy:s,index:i?", .params = "aclname match allow|deny [index]", .help = "add a match rule to the access control list", - .mhandler.cmd = do_acl_add, + .mhandler.cmd = hmp_acl_add, }, STEXI @@ -1428,7 +1404,7 @@ ETEXI .args_type = "aclname:s,match:s", .params = "aclname match", .help = "remove a match rule from the access control list", - .mhandler.cmd = do_acl_remove, + .mhandler.cmd = hmp_acl_remove, }, STEXI @@ -1442,7 +1418,7 @@ ETEXI .args_type = "aclname:s", .params = "aclname", .help = "reset the access control list", - .mhandler.cmd = do_acl_reset, + .mhandler.cmd = hmp_acl_reset, }, STEXI @@ -1504,7 +1480,7 @@ ETEXI .args_type = "broadcast:-b,cpu_index:i,bank:i,status:l,mcg_status:l,addr:l,misc:l", .params = "[-b] cpu bank status mcgstatus addr misc", .help = "inject a MCE on the given CPU [and broadcast to other CPUs with -b option]", - .mhandler.cmd = do_inject_mce, + .mhandler.cmd = hmp_mce, }, #endif @@ -1555,9 +1531,9 @@ ETEXI }, STEXI -@item block_set_io_throttle @var{device} @var{bps} @var{bps_rd} @var{bps_wr} @var{iops} @var{iops_rd} @var{iops_wr} -@findex block_set_io_throttle -Change I/O throttle limits for a block drive to @var{bps} @var{bps_rd} @var{bps_wr} @var{iops} @var{iops_rd} @var{iops_wr} +@item block_passwd @var{device} @var{password} +@findex block_passwd +Set the encrypted device @var{device} password to @var{password} ETEXI { @@ -1569,9 +1545,9 @@ ETEXI }, STEXI -@item block_passwd @var{device} @var{password} -@findex block_passwd -Set the encrypted device @var{device} password to @var{password} +@item block_set_io_throttle @var{device} @var{bps} @var{bps_rd} @var{bps_wr} @var{iops} @var{iops_rd} @var{iops_wr} +@findex block_set_io_throttle +Change I/O throttle limits for a block drive to @var{bps} @var{bps_rd} @var{bps_wr} @var{iops} @var{iops_rd} @var{iops_wr} ETEXI { @@ -1585,7 +1561,6 @@ ETEXI STEXI @item set_password [ vnc | spice ] password [ action-if-connected ] @findex set_password - Change spice/vnc password. Use zero to make the password stay valid forever. @var{action-if-connected} specifies what should happen in case a connection is established: @var{fail} makes the password change @@ -1605,7 +1580,6 @@ ETEXI STEXI @item expire_password [ vnc | spice ] expire-time @findex expire_password - Specify when a password for spice/vnc becomes invalid. @var{expire-time} accepts: @@ -1636,9 +1610,8 @@ ETEXI }, STEXI -@item chardev_add args -@findex chardev_add - +@item chardev-add args +@findex chardev-add chardev_add accepts the same parameters as the -chardev command line switch. ETEXI @@ -1653,9 +1626,8 @@ ETEXI }, STEXI -@item chardev_remove id -@findex chardev_remove - +@item chardev-remove id +@findex chardev-remove Removes the chardev @var{id}. ETEXI @@ -1671,7 +1643,6 @@ ETEXI STEXI @item qemu-io @var{device} @var{command} @findex qemu-io - Executes a qemu-io command on the given block device. ETEXI @@ -1686,15 +1657,42 @@ ETEXI STEXI @item cpu-add @var{id} +@findex cpu-add Add CPU with id @var{id} ETEXI { + .name = "qom-list", + .args_type = "path:s?", + .params = "path", + .help = "list QOM properties", + .mhandler.cmd = hmp_qom_list, + }, + +STEXI +@item qom-list [@var{path}] +Print QOM properties of object at location @var{path} +ETEXI + + { + .name = "qom-set", + .args_type = "path:s,property:s,value:s", + .params = "path property value", + .help = "set QOM property", + .mhandler.cmd = hmp_qom_set, + }, + +STEXI +@item qom-set @var{path} @var{property} @var{value} +Set QOM property @var{property} of object at location @var{path} to value @var{value} +ETEXI + + { .name = "info", .args_type = "item:s?", .params = "[subcommand]", .help = "show various information about the system state", - .mhandler.cmd = do_info_help, + .mhandler.cmd = hmp_info_help, .sub_table = info_cmds, }, @@ -1772,6 +1770,8 @@ show balloon information show device tree @item info qdm show qdev device model list +@item info qom-tree +show object composition tree @item info roms show roms @item info tpm diff --git a/hmp.c b/hmp.c index 63d7686..1b9a317 100644 --- a/hmp.c +++ b/hmp.c @@ -16,6 +16,7 @@ #include "hmp.h" #include "net/net.h" #include "sysemu/char.h" +#include "sysemu/block-backend.h" #include "qemu/option.h" #include "qemu/timer.h" #include "qmp-commands.h" @@ -28,6 +29,10 @@ #include "block/qapi.h" #include "qemu-io.h" +#ifdef CONFIG_SPICE +#include +#endif + static void hmp_handle_error(Monitor *mon, Error **errp) { assert(errp); @@ -157,7 +162,8 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict) } if (info->has_status) { - monitor_printf(mon, "Migration status: %s\n", info->status); + monitor_printf(mon, "Migration status: %s\n", + MigrationStatus_lookup[info->status]); monitor_printf(mon, "total time: %" PRIu64 " milliseconds\n", info->total_time); if (info->has_expected_downtime) { @@ -290,14 +296,132 @@ void hmp_info_cpus(Monitor *mon, const QDict *qdict) qapi_free_CpuInfoList(cpu_list); } +static void print_block_info(Monitor *mon, BlockInfo *info, + BlockDeviceInfo *inserted, bool verbose) +{ + ImageInfo *image_info; + + assert(!info || !info->has_inserted || info->inserted == inserted); + + if (info) { + monitor_printf(mon, "%s", info->device); + if (inserted && inserted->has_node_name) { + monitor_printf(mon, " (%s)", inserted->node_name); + } + } else { + assert(inserted); + monitor_printf(mon, "%s", + inserted->has_node_name + ? inserted->node_name + : ""); + } + + if (inserted) { + monitor_printf(mon, ": %s (%s%s%s)\n", + inserted->file, + inserted->drv, + inserted->ro ? ", read-only" : "", + inserted->encrypted ? ", encrypted" : ""); + } else { + monitor_printf(mon, ": [not inserted]\n"); + } + + if (info) { + if (info->has_io_status && info->io_status != BLOCK_DEVICE_IO_STATUS_OK) { + monitor_printf(mon, " I/O status: %s\n", + BlockDeviceIoStatus_lookup[info->io_status]); + } + + if (info->removable) { + monitor_printf(mon, " Removable device: %slocked, tray %s\n", + info->locked ? "" : "not ", + info->tray_open ? "open" : "closed"); + } + } + + + if (!inserted) { + return; + } + + monitor_printf(mon, " Cache mode: %s%s%s\n", + inserted->cache->writeback ? "writeback" : "writethrough", + inserted->cache->direct ? ", direct" : "", + inserted->cache->no_flush ? ", ignore flushes" : ""); + + if (inserted->has_backing_file) { + monitor_printf(mon, + " Backing file: %s " + "(chain depth: %" PRId64 ")\n", + inserted->backing_file, + inserted->backing_file_depth); + } + + if (inserted->detect_zeroes != BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF) { + monitor_printf(mon, " Detect zeroes: %s\n", + BlockdevDetectZeroesOptions_lookup[inserted->detect_zeroes]); + } + + if (inserted->bps || inserted->bps_rd || inserted->bps_wr || + inserted->iops || inserted->iops_rd || inserted->iops_wr) + { + monitor_printf(mon, " I/O throttling: bps=%" PRId64 + " bps_rd=%" PRId64 " bps_wr=%" PRId64 + " bps_max=%" PRId64 + " bps_rd_max=%" PRId64 + " bps_wr_max=%" PRId64 + " iops=%" PRId64 " iops_rd=%" PRId64 + " iops_wr=%" PRId64 + " iops_max=%" PRId64 + " iops_rd_max=%" PRId64 + " iops_wr_max=%" PRId64 + " iops_size=%" PRId64 "\n", + inserted->bps, + inserted->bps_rd, + inserted->bps_wr, + inserted->bps_max, + inserted->bps_rd_max, + inserted->bps_wr_max, + inserted->iops, + inserted->iops_rd, + inserted->iops_wr, + inserted->iops_max, + inserted->iops_rd_max, + inserted->iops_wr_max, + inserted->iops_size); + } + + /* TODO: inserted->image should never be null */ + if (verbose && inserted->image) { + monitor_printf(mon, "\nImages:\n"); + image_info = inserted->image; + while (1) { + bdrv_image_info_dump((fprintf_function)monitor_printf, + mon, image_info); + if (image_info->has_backing_image) { + image_info = image_info->backing_image; + } else { + break; + } + } + } +} + void hmp_info_block(Monitor *mon, const QDict *qdict) { BlockInfoList *block_list, *info; - ImageInfo *image_info; + BlockDeviceInfoList *blockdev_list, *blockdev; const char *device = qdict_get_try_str(qdict, "device"); bool verbose = qdict_get_try_bool(qdict, "verbose", 0); + bool nodes = qdict_get_try_bool(qdict, "nodes", 0); + bool printed = false; - block_list = qmp_query_block(NULL); + /* Print BlockBackend information */ + if (!nodes) { + block_list = qmp_query_block(NULL); + } else { + block_list = NULL; + } for (info = block_list; info; info = info->next) { if (device && strcmp(device, info->value->device)) { @@ -308,102 +432,40 @@ void hmp_info_block(Monitor *mon, const QDict *qdict) monitor_printf(mon, "\n"); } - monitor_printf(mon, "%s", info->value->device); - if (info->value->has_inserted) { - monitor_printf(mon, ": %s (%s%s%s)\n", - info->value->inserted->file, - info->value->inserted->drv, - info->value->inserted->ro ? ", read-only" : "", - info->value->inserted->encrypted ? ", encrypted" : ""); - } else { - monitor_printf(mon, ": [not inserted]\n"); - } - - if (info->value->has_io_status && info->value->io_status != BLOCK_DEVICE_IO_STATUS_OK) { - monitor_printf(mon, " I/O status: %s\n", - BlockDeviceIoStatus_lookup[info->value->io_status]); - } + print_block_info(mon, info->value, info->value->has_inserted + ? info->value->inserted : NULL, + verbose); + printed = true; + } - if (info->value->removable) { - monitor_printf(mon, " Removable device: %slocked, tray %s\n", - info->value->locked ? "" : "not ", - info->value->tray_open ? "open" : "closed"); - } + qapi_free_BlockInfoList(block_list); + if ((!device && !nodes) || printed) { + return; + } - if (!info->value->has_inserted) { + /* Print node information */ + blockdev_list = qmp_query_named_block_nodes(NULL); + for (blockdev = blockdev_list; blockdev; blockdev = blockdev->next) { + assert(blockdev->value->has_node_name); + if (device && strcmp(device, blockdev->value->node_name)) { continue; } - if (info->value->inserted->has_backing_file) { - monitor_printf(mon, - " Backing file: %s " - "(chain depth: %" PRId64 ")\n", - info->value->inserted->backing_file, - info->value->inserted->backing_file_depth); - } - - if (info->value->inserted->detect_zeroes != BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF) { - monitor_printf(mon, " Detect zeroes: %s\n", - BlockdevDetectZeroesOptions_lookup[info->value->inserted->detect_zeroes]); - } - - if (info->value->inserted->bps - || info->value->inserted->bps_rd - || info->value->inserted->bps_wr - || info->value->inserted->iops - || info->value->inserted->iops_rd - || info->value->inserted->iops_wr) - { - monitor_printf(mon, " I/O throttling: bps=%" PRId64 - " bps_rd=%" PRId64 " bps_wr=%" PRId64 - " bps_max=%" PRId64 - " bps_rd_max=%" PRId64 - " bps_wr_max=%" PRId64 - " iops=%" PRId64 " iops_rd=%" PRId64 - " iops_wr=%" PRId64 - " iops_max=%" PRId64 - " iops_rd_max=%" PRId64 - " iops_wr_max=%" PRId64 - " iops_size=%" PRId64 "\n", - info->value->inserted->bps, - info->value->inserted->bps_rd, - info->value->inserted->bps_wr, - info->value->inserted->bps_max, - info->value->inserted->bps_rd_max, - info->value->inserted->bps_wr_max, - info->value->inserted->iops, - info->value->inserted->iops_rd, - info->value->inserted->iops_wr, - info->value->inserted->iops_max, - info->value->inserted->iops_rd_max, - info->value->inserted->iops_wr_max, - info->value->inserted->iops_size); + if (blockdev != blockdev_list) { + monitor_printf(mon, "\n"); } - if (verbose) { - monitor_printf(mon, "\nImages:\n"); - image_info = info->value->inserted->image; - while (1) { - bdrv_image_info_dump((fprintf_function)monitor_printf, - mon, image_info); - if (image_info->has_backing_image) { - image_info = image_info->backing_image; - } else { - break; - } - } - } + print_block_info(mon, NULL, blockdev->value, verbose); } - - qapi_free_BlockInfoList(block_list); + qapi_free_BlockDeviceInfoList(blockdev_list); } void hmp_info_blockstats(Monitor *mon, const QDict *qdict) { BlockStatsList *stats_list, *stats; - stats_list = qmp_query_blockstats(NULL); + stats_list = qmp_query_blockstats(false, false, NULL); for (stats = stats_list; stats; stats = stats->next) { if (!stats->value->has_device) { @@ -419,6 +481,8 @@ void hmp_info_blockstats(Monitor *mon, const QDict *qdict) " wr_total_time_ns=%" PRId64 " rd_total_time_ns=%" PRId64 " flush_total_time_ns=%" PRId64 + " rd_merged=%" PRId64 + " wr_merged=%" PRId64 "\n", stats->value->stats->rd_bytes, stats->value->stats->wr_bytes, @@ -427,7 +491,9 @@ void hmp_info_blockstats(Monitor *mon, const QDict *qdict) stats->value->stats->flush_operations, stats->value->stats->wr_total_time_ns, stats->value->stats->rd_total_time_ns, - stats->value->stats->flush_total_time_ns); + stats->value->stats->flush_total_time_ns, + stats->value->stats->rd_merged, + stats->value->stats->wr_merged); } qapi_free_BlockStatsList(stats_list); @@ -480,10 +546,30 @@ out: qapi_free_VncInfo(info); } +#ifdef CONFIG_SPICE void hmp_info_spice(Monitor *mon, const QDict *qdict) { SpiceChannelList *chan; SpiceInfo *info; + const char *channel_name; + const char * const channel_names[] = { + [SPICE_CHANNEL_MAIN] = "main", + [SPICE_CHANNEL_DISPLAY] = "display", + [SPICE_CHANNEL_INPUTS] = "inputs", + [SPICE_CHANNEL_CURSOR] = "cursor", + [SPICE_CHANNEL_PLAYBACK] = "playback", + [SPICE_CHANNEL_RECORD] = "record", + [SPICE_CHANNEL_TUNNEL] = "tunnel", + [SPICE_CHANNEL_SMARTCARD] = "smartcard", + [SPICE_CHANNEL_USBREDIR] = "usbredir", + [SPICE_CHANNEL_PORT] = "port", +#if 0 + /* minimum spice-protocol is 0.12.3, webdav was added in 0.12.7, + * no easy way to #ifdef (SPICE_CHANNEL_* is a enum). Disable + * as quick fix for build failures with older versions. */ + [SPICE_CHANNEL_WEBDAV] = "webdav", +#endif + }; info = qmp_query_spice(NULL); @@ -520,12 +606,22 @@ void hmp_info_spice(Monitor *mon, const QDict *qdict) chan->value->connection_id); monitor_printf(mon, " channel: %" PRId64 ":%" PRId64 "\n", chan->value->channel_type, chan->value->channel_id); + + channel_name = "unknown"; + if (chan->value->channel_type > 0 && + chan->value->channel_type < ARRAY_SIZE(channel_names) && + channel_names[chan->value->channel_type]) { + channel_name = channel_names[chan->value->channel_type]; + } + + monitor_printf(mon, " channel name: %s\n", channel_name); } } out: qapi_free_SpiceInfo(info); } +#endif void hmp_info_balloon(Monitor *mon, const QDict *qdict) { @@ -862,7 +958,7 @@ void hmp_system_wakeup(Monitor *mon, const QDict *qdict) qmp_system_wakeup(NULL); } -void hmp_inject_nmi(Monitor *mon, const QDict *qdict) +void hmp_nmi(Monitor *mon, const QDict *qdict) { Error *err = NULL; @@ -938,7 +1034,7 @@ void hmp_drive_mirror(Monitor *mon, const QDict *qdict) false, NULL, false, NULL, full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP, true, mode, false, 0, false, 0, false, 0, - false, 0, false, 0, &err); + false, 0, false, 0, false, true, &err); hmp_handle_error(mon, &err); } @@ -1022,6 +1118,16 @@ void hmp_migrate_cancel(Monitor *mon, const QDict *qdict) qmp_migrate_cancel(NULL); } +void hmp_migrate_incoming(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + const char *uri = qdict_get_str(qdict, "uri"); + + qmp_migrate_incoming(uri, &err); + + hmp_handle_error(mon, &err); +} + void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict) { double value = qdict_get_double(qdict, "value"); @@ -1237,21 +1343,21 @@ void hmp_block_job_complete(Monitor *mon, const QDict *qdict) hmp_handle_error(mon, &error); } -typedef struct MigrationStatus +typedef struct HMPMigrationStatus { QEMUTimer *timer; Monitor *mon; bool is_block_migration; -} MigrationStatus; +} HMPMigrationStatus; static void hmp_migrate_status_cb(void *opaque) { - MigrationStatus *status = opaque; + HMPMigrationStatus *status = opaque; MigrationInfo *info; info = qmp_query_migrate(NULL); - if (!info->has_status || strcmp(info->status, "active") == 0 || - strcmp(info->status, "setup") == 0) { + if (!info->has_status || info->status == MIGRATION_STATUS_ACTIVE || + info->status == MIGRATION_STATUS_SETUP) { if (info->has_disk) { int progress; @@ -1294,7 +1400,7 @@ void hmp_migrate(Monitor *mon, const QDict *qdict) } if (!detach) { - MigrationStatus *status; + HMPMigrationStatus *status; if (monitor_suspend(mon) < 0) { monitor_printf(mon, "terminal does not allow synchronous " @@ -1471,7 +1577,7 @@ void hmp_closefd(Monitor *mon, const QDict *qdict) hmp_handle_error(mon, &err); } -void hmp_send_key(Monitor *mon, const QDict *qdict) +void hmp_sendkey(Monitor *mon, const QDict *qdict) { const char *keys = qdict_get_str(qdict, "keys"); KeyValueList *keylist, *head = NULL, *tmp = NULL; @@ -1540,7 +1646,7 @@ err_out: goto out; } -void hmp_screen_dump(Monitor *mon, const QDict *qdict) +void hmp_screendump(Monitor *mon, const QDict *qdict) { const char *filename = qdict_get_str(qdict, "filename"); Error *err = NULL; @@ -1659,14 +1765,14 @@ void hmp_chardev_remove(Monitor *mon, const QDict *qdict) void hmp_qemu_io(Monitor *mon, const QDict *qdict) { - BlockDriverState *bs; + BlockBackend *blk; const char* device = qdict_get_str(qdict, "device"); const char* command = qdict_get_str(qdict, "command"); Error *err = NULL; - bs = bdrv_find(device); - if (bs) { - qemuio_command(bs, command); + blk = blk_by_name(device); + if (blk) { + qemuio_command(blk, command); } else { error_set(&err, QERR_DEVICE_NOT_FOUND, device); } @@ -1758,3 +1864,50 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict) qapi_free_MemoryDeviceInfoList(info_list); } + +void hmp_qom_list(Monitor *mon, const QDict *qdict) +{ + const char *path = qdict_get_try_str(qdict, "path"); + ObjectPropertyInfoList *list; + Error *err = NULL; + + if (path == NULL) { + monitor_printf(mon, "/\n"); + return; + } + + list = qmp_qom_list(path, &err); + if (err == NULL) { + ObjectPropertyInfoList *start = list; + while (list != NULL) { + ObjectPropertyInfo *value = list->value; + + monitor_printf(mon, "%s (%s)\n", + value->name, value->type); + list = list->next; + } + qapi_free_ObjectPropertyInfoList(start); + } + hmp_handle_error(mon, &err); +} + +void hmp_qom_set(Monitor *mon, const QDict *qdict) +{ + const char *path = qdict_get_str(qdict, "path"); + const char *property = qdict_get_str(qdict, "property"); + const char *value = qdict_get_str(qdict, "value"); + Error *err = NULL; + bool ambiguous = false; + Object *obj; + + obj = object_resolve_path(path, &ambiguous); + if (obj == NULL) { + error_set(&err, QERR_DEVICE_NOT_FOUND, path); + } else { + if (ambiguous) { + monitor_printf(mon, "Warning: Path '%s' is ambiguous\n", path); + } + object_property_parse(obj, value, property, &err); + } + hmp_handle_error(mon, &err); +} diff --git a/hmp.h b/hmp.h index 4bb5dca..2b9308b 100644 --- a/hmp.h +++ b/hmp.h @@ -49,7 +49,7 @@ void hmp_ringbuf_write(Monitor *mon, const QDict *qdict); void hmp_ringbuf_read(Monitor *mon, const QDict *qdict); void hmp_cont(Monitor *mon, const QDict *qdict); void hmp_system_wakeup(Monitor *mon, const QDict *qdict); -void hmp_inject_nmi(Monitor *mon, const QDict *qdict); +void hmp_nmi(Monitor *mon, const QDict *qdict); void hmp_set_link(Monitor *mon, const QDict *qdict); void hmp_block_passwd(Monitor *mon, const QDict *qdict); void hmp_balloon(Monitor *mon, const QDict *qdict); @@ -60,6 +60,7 @@ void hmp_snapshot_delete_blkdev_internal(Monitor *mon, const QDict *qdict); void hmp_drive_mirror(Monitor *mon, const QDict *qdict); void hmp_drive_backup(Monitor *mon, const QDict *qdict); void hmp_migrate_cancel(Monitor *mon, const QDict *qdict); +void hmp_migrate_incoming(Monitor *mon, const QDict *qdict); void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict); void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict); void hmp_migrate_set_capability(Monitor *mon, const QDict *qdict); @@ -82,8 +83,8 @@ void hmp_netdev_add(Monitor *mon, const QDict *qdict); void hmp_netdev_del(Monitor *mon, const QDict *qdict); void hmp_getfd(Monitor *mon, const QDict *qdict); void hmp_closefd(Monitor *mon, const QDict *qdict); -void hmp_send_key(Monitor *mon, const QDict *qdict); -void hmp_screen_dump(Monitor *mon, const QDict *qdict); +void hmp_sendkey(Monitor *mon, const QDict *qdict); +void hmp_screendump(Monitor *mon, const QDict *qdict); void hmp_nbd_server_start(Monitor *mon, const QDict *qdict); void hmp_nbd_server_add(Monitor *mon, const QDict *qdict); void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict); @@ -95,6 +96,8 @@ void hmp_object_add(Monitor *mon, const QDict *qdict); void hmp_object_del(Monitor *mon, const QDict *qdict); void hmp_info_memdev(Monitor *mon, const QDict *qdict); void hmp_info_memory_devices(Monitor *mon, const QDict *qdict); +void hmp_qom_list(Monitor *mon, const QDict *qdict); +void hmp_qom_set(Monitor *mon, const QDict *qdict); void object_add_completion(ReadLineState *rs, int nb_args, const char *str); void object_del_completion(ReadLineState *rs, int nb_args, const char *str); void device_add_completion(ReadLineState *rs, int nb_args, const char *str); diff --git a/hw/9pfs/virtio-9p-coth.c b/hw/9pfs/virtio-9p-coth.c index ae6cde8..8185c53 100644 --- a/hw/9pfs/virtio-9p-coth.c +++ b/hw/9pfs/virtio-9p-coth.c @@ -14,6 +14,7 @@ #include "fsdev/qemu-fsdev.h" #include "qemu/thread.h" +#include "qemu/event_notifier.h" #include "block/coroutine.h" #include "virtio-9p-coth.h" @@ -26,15 +27,11 @@ void co_run_in_worker_bh(void *opaque) g_thread_pool_push(v9fs_pool.pool, co, NULL); } -static void v9fs_qemu_process_req_done(void *arg) +static void v9fs_qemu_process_req_done(EventNotifier *e) { - char byte; - ssize_t len; Coroutine *co; - do { - len = read(v9fs_pool.rfd, &byte, sizeof(byte)); - } while (len == -1 && errno == EINTR); + event_notifier_test_and_clear(e); while ((co = g_async_queue_try_pop(v9fs_pool.completed)) != NULL) { qemu_coroutine_enter(co, NULL); @@ -43,22 +40,18 @@ static void v9fs_qemu_process_req_done(void *arg) static void v9fs_thread_routine(gpointer data, gpointer user_data) { - ssize_t len; - char byte = 0; Coroutine *co = data; qemu_coroutine_enter(co, NULL); g_async_queue_push(v9fs_pool.completed, co); - do { - len = write(v9fs_pool.wfd, &byte, sizeof(byte)); - } while (len == -1 && errno == EINTR); + + event_notifier_set(&v9fs_pool.e); } int v9fs_init_worker_threads(void) { int ret = 0; - int notifier_fds[2]; V9fsThPool *p = &v9fs_pool; sigset_t set, oldset; @@ -66,10 +59,6 @@ int v9fs_init_worker_threads(void) /* Leave signal handling to the iothread. */ pthread_sigmask(SIG_SETMASK, &set, &oldset); - if (qemu_pipe(notifier_fds) == -1) { - ret = -1; - goto err_out; - } p->pool = g_thread_pool_new(v9fs_thread_routine, p, -1, FALSE, NULL); if (!p->pool) { ret = -1; @@ -84,13 +73,9 @@ int v9fs_init_worker_threads(void) ret = -1; goto err_out; } - p->rfd = notifier_fds[0]; - p->wfd = notifier_fds[1]; - - fcntl(p->rfd, F_SETFL, O_NONBLOCK); - fcntl(p->wfd, F_SETFL, O_NONBLOCK); + event_notifier_init(&p->e, 0); - qemu_set_fd_handler(p->rfd, v9fs_qemu_process_req_done, NULL, NULL); + event_notifier_set_handler(&p->e, v9fs_qemu_process_req_done); err_out: pthread_sigmask(SIG_SETMASK, &oldset, NULL); return ret; diff --git a/hw/9pfs/virtio-9p-coth.h b/hw/9pfs/virtio-9p-coth.h index 86d5ed4..4f51b25 100644 --- a/hw/9pfs/virtio-9p-coth.h +++ b/hw/9pfs/virtio-9p-coth.h @@ -21,8 +21,8 @@ #include typedef struct V9fsThPool { - int rfd; - int wfd; + EventNotifier e; + GThreadPool *pool; GAsyncQueue *completed; } V9fsThPool; diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c index 2572747..30492ec 100644 --- a/hw/9pfs/virtio-9p-device.c +++ b/hw/9pfs/virtio-9p-device.c @@ -23,7 +23,7 @@ static uint32_t virtio_9p_get_features(VirtIODevice *vdev, uint32_t features) { - features |= 1 << VIRTIO_9P_MOUNT_TAG; + virtio_add_feature(&features, VIRTIO_9P_MOUNT_TAG); return features; } diff --git a/hw/9pfs/virtio-9p-handle.c b/hw/9pfs/virtio-9p-handle.c index 4b79cef..13eabb9 100644 --- a/hw/9pfs/virtio-9p-handle.c +++ b/hw/9pfs/virtio-9p-handle.c @@ -140,7 +140,7 @@ static int handle_opendir(FsContext *ctx, static void handle_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) { - return rewinddir(fs->dir); + rewinddir(fs->dir); } static off_t handle_telldir(FsContext *ctx, V9fsFidOpenState *fs) @@ -157,7 +157,7 @@ static int handle_readdir_r(FsContext *ctx, V9fsFidOpenState *fs, static void handle_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) { - return seekdir(fs->dir, off); + seekdir(fs->dir, off); } static ssize_t handle_preadv(FsContext *ctx, V9fsFidOpenState *fs, diff --git a/hw/9pfs/virtio-9p-local.c b/hw/9pfs/virtio-9p-local.c index a183eee..f1f2e25 100644 --- a/hw/9pfs/virtio-9p-local.c +++ b/hw/9pfs/virtio-9p-local.c @@ -45,19 +45,17 @@ static char *local_mapped_attr_path(FsContext *ctx, const char *path) { - char *dir_name; - char *tmp_path = g_strdup(path); - char *base_name = basename(tmp_path); - char *buffer; - - /* NULL terminate the directory */ - dir_name = tmp_path; - *(base_name - 1) = '\0'; - - buffer = g_strdup_printf("%s/%s/%s/%s", - ctx->fs_root, dir_name, VIRTFS_META_DIR, base_name); - g_free(tmp_path); - return buffer; + 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) @@ -332,7 +330,6 @@ static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path, tsize = read(fd, (void *)buf, bufsz); } while (tsize == -1 && errno == EINTR); close(fd); - return tsize; } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || (fs_ctx->export_flags & V9FS_SM_NONE)) { buffer = rpath(fs_ctx, path); @@ -381,7 +378,7 @@ static int local_opendir(FsContext *ctx, static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) { - return rewinddir(fs->dir); + rewinddir(fs->dir); } static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs) @@ -412,7 +409,7 @@ again: static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) { - return seekdir(fs->dir, off); + seekdir(fs->dir, off); } static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs, @@ -489,7 +486,7 @@ static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path, int err = -1; int serrno = 0; V9fsString fullname; - char *buffer; + char *buffer = NULL; v9fs_string_init(&fullname); v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); @@ -500,7 +497,6 @@ static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path, buffer = rpath(fs_ctx, path); err = mknod(buffer, SM_LOCAL_MODE_BITS|S_IFREG, 0); if (err == -1) { - g_free(buffer); goto out; } err = local_set_xattr(buffer, credp); @@ -513,7 +509,6 @@ static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path, buffer = rpath(fs_ctx, path); err = mknod(buffer, SM_LOCAL_MODE_BITS|S_IFREG, 0); if (err == -1) { - g_free(buffer); goto out; } err = local_set_mapped_file_attr(fs_ctx, path, credp); @@ -526,7 +521,6 @@ static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path, buffer = rpath(fs_ctx, path); err = mknod(buffer, credp->fc_mode, credp->fc_rdev); if (err == -1) { - g_free(buffer); goto out; } err = local_post_create_passthrough(fs_ctx, path, credp); @@ -540,8 +534,8 @@ static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path, err_end: remove(buffer); errno = serrno; - g_free(buffer); out: + g_free(buffer); v9fs_string_free(&fullname); return err; } @@ -553,7 +547,7 @@ static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, int err = -1; int serrno = 0; V9fsString fullname; - char *buffer; + char *buffer = NULL; v9fs_string_init(&fullname); v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); @@ -564,7 +558,6 @@ static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, buffer = rpath(fs_ctx, path); err = mkdir(buffer, SM_LOCAL_DIR_MODE_BITS); if (err == -1) { - g_free(buffer); goto out; } credp->fc_mode = credp->fc_mode|S_IFDIR; @@ -577,7 +570,6 @@ static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, buffer = rpath(fs_ctx, path); err = mkdir(buffer, SM_LOCAL_DIR_MODE_BITS); if (err == -1) { - g_free(buffer); goto out; } credp->fc_mode = credp->fc_mode|S_IFDIR; @@ -591,7 +583,6 @@ static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, buffer = rpath(fs_ctx, path); err = mkdir(buffer, credp->fc_mode); if (err == -1) { - g_free(buffer); goto out; } err = local_post_create_passthrough(fs_ctx, path, credp); @@ -605,8 +596,8 @@ static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, err_end: remove(buffer); errno = serrno; - g_free(buffer); out: + g_free(buffer); v9fs_string_free(&fullname); return err; } @@ -660,7 +651,7 @@ static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, int err = -1; int serrno = 0; V9fsString fullname; - char *buffer; + char *buffer = NULL; /* * Mark all the open to not follow symlinks @@ -676,7 +667,6 @@ static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, buffer = rpath(fs_ctx, path); fd = open(buffer, flags, SM_LOCAL_MODE_BITS); if (fd == -1) { - g_free(buffer); err = fd; goto out; } @@ -691,7 +681,6 @@ static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, buffer = rpath(fs_ctx, path); fd = open(buffer, flags, SM_LOCAL_MODE_BITS); if (fd == -1) { - g_free(buffer); err = fd; goto out; } @@ -707,7 +696,6 @@ static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, buffer = rpath(fs_ctx, path); fd = open(buffer, flags, credp->fc_mode); if (fd == -1) { - g_free(buffer); err = fd; goto out; } @@ -725,8 +713,8 @@ err_end: close(fd); remove(buffer); errno = serrno; - g_free(buffer); out: + g_free(buffer); v9fs_string_free(&fullname); return err; } @@ -739,7 +727,7 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath, int serrno = 0; char *newpath; V9fsString fullname; - char *buffer; + char *buffer = NULL; v9fs_string_init(&fullname); v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); @@ -752,7 +740,6 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath, buffer = rpath(fs_ctx, newpath); fd = open(buffer, O_CREAT|O_EXCL|O_RDWR|O_NOFOLLOW, SM_LOCAL_MODE_BITS); if (fd == -1) { - g_free(buffer); err = fd; goto out; } @@ -782,7 +769,6 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath, buffer = rpath(fs_ctx, newpath); fd = open(buffer, O_CREAT|O_EXCL|O_RDWR|O_NOFOLLOW, SM_LOCAL_MODE_BITS); if (fd == -1) { - g_free(buffer); err = fd; goto out; } @@ -811,7 +797,6 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath, buffer = rpath(fs_ctx, newpath); err = symlink(oldpath, buffer); if (err) { - g_free(buffer); goto out; } err = lchown(buffer, credp->fc_uid, credp->fc_gid); @@ -832,8 +817,8 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath, err_end: remove(buffer); errno = serrno; - g_free(buffer); out: + g_free(buffer); v9fs_string_free(&fullname); return err; } diff --git a/hw/9pfs/virtio-9p-posix-acl.c b/hw/9pfs/virtio-9p-posix-acl.c index 803d9d9..09dad07 100644 --- a/hw/9pfs/virtio-9p-posix-acl.c +++ b/hw/9pfs/virtio-9p-posix-acl.c @@ -114,7 +114,7 @@ static ssize_t mp_dacl_listxattr(FsContext *ctx, const char *path, } /* len includes the trailing NUL */ - memcpy(value, ACL_ACCESS, len); + memcpy(value, ACL_DEFAULT, len); return 0; } diff --git a/hw/9pfs/virtio-9p-proxy.c b/hw/9pfs/virtio-9p-proxy.c index 59c7445..1bc7881 100644 --- a/hw/9pfs/virtio-9p-proxy.c +++ b/hw/9pfs/virtio-9p-proxy.c @@ -669,7 +669,7 @@ static int proxy_opendir(FsContext *ctx, static void proxy_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) { - return rewinddir(fs->dir); + rewinddir(fs->dir); } static off_t proxy_telldir(FsContext *ctx, V9fsFidOpenState *fs) @@ -686,23 +686,23 @@ static int proxy_readdir_r(FsContext *ctx, V9fsFidOpenState *fs, static void proxy_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) { - return seekdir(fs->dir, 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 - return preadv(fs->fd, iov, iovcnt, offset); + ret = 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); + 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, @@ -714,10 +714,8 @@ static ssize_t proxy_pwritev(FsContext *ctx, V9fsFidOpenState *fs, #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 = lseek(fs->fd, offset, SEEK_SET); + if (ret >= 0) { ret = writev(fs->fd, iov, iovcnt); } #endif @@ -1102,6 +1100,10 @@ static int connect_namedsocket(const char *path) int sockfd, size; struct sockaddr_un helper; + if (strlen(path) >= sizeof(helper.sun_path)) { + fprintf(stderr, "Socket name too large\n"); + return -1; + } sockfd = socket(AF_UNIX, SOCK_STREAM, 0); if (sockfd < 0) { fprintf(stderr, "failed to create socket: %s\n", strerror(errno)); diff --git a/hw/9pfs/virtio-9p-synth.c b/hw/9pfs/virtio-9p-synth.c index 71262bc..a0ab9a8 100644 --- a/hw/9pfs/virtio-9p-synth.c +++ b/hw/9pfs/virtio-9p-synth.c @@ -17,7 +17,8 @@ #include "virtio-9p-xattr.h" #include "fsdev/qemu-fsdev.h" #include "virtio-9p-synth.h" - +#include "qemu/rcu.h" +#include "qemu/rcu_queue.h" #include /* Root node for synth file system */ diff --git a/hw/9pfs/virtio-9p.c b/hw/9pfs/virtio-9p.c index 5861a5b..4964da0 100644 --- a/hw/9pfs/virtio-9p.c +++ b/hw/9pfs/virtio-9p.c @@ -1950,7 +1950,8 @@ static void v9fs_write(void *opaque) err = pdu_unmarshal(pdu, offset, "dqd", &fid, &off, &count); if (err < 0) { - return complete_pdu(s, pdu, err); + complete_pdu(s, pdu, err); + return; } offset += err; v9fs_init_qiov_from_pdu(&qiov_full, pdu, offset, count, true); diff --git a/hw/9pfs/virtio-9p.h b/hw/9pfs/virtio-9p.h index 2c3603a..58dafa9 100644 --- a/hw/9pfs/virtio-9p.h +++ b/hw/9pfs/virtio-9p.h @@ -7,16 +7,14 @@ #include #include #include +#include "standard-headers/linux/virtio_9p.h" #include "hw/virtio/virtio.h" +#include "hw/virtio/virtio-9p.h" #include "fsdev/file-op-9p.h" #include "fsdev/virtio-9p-marshal.h" #include "qemu/thread.h" #include "block/coroutine.h" -/* The feature bitmap for virtio 9P */ -/* The mount point is specified in a config variable */ -#define VIRTIO_9P_MOUNT_TAG 0 - enum { P9_TLERROR = 6, P9_RLERROR, @@ -144,10 +142,6 @@ struct V9fsPDU * 1) change user needs to set groups and stuff */ -/* from Linux's linux/virtio_9p.h */ - -/* The ID for virtio console */ -#define VIRTIO_ID_9P 9 #define MAX_REQ 128 #define MAX_TAG_LEN 32 @@ -277,14 +271,6 @@ typedef struct V9fsWriteState { int cnt; } V9fsWriteState; -struct virtio_9p_config -{ - /* number of characters in tag */ - uint16_t tag_len; - /* Variable size tag name */ - uint8_t tag[0]; -} QEMU_PACKED; - typedef struct V9fsMkState { V9fsPDU *pdu; size_t offset; diff --git a/hw/Makefile.objs b/hw/Makefile.objs index 52a1464..73afa41 100644 --- a/hw/Makefile.objs +++ b/hw/Makefile.objs @@ -26,6 +26,7 @@ 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/ diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs index acd2389..b9fefa7 100644 --- a/hw/acpi/Makefile.objs +++ b/hw/acpi/Makefile.objs @@ -1,3 +1,5 @@ common-obj-$(CONFIG_ACPI) += core.o piix4.o ich9.o pcihp.o cpu_hotplug.o common-obj-$(CONFIG_ACPI) += memory_hotplug.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/hw/acpi/aml-build.c b/hw/acpi/aml-build.c new file mode 100644 index 0000000..41ff6a3 --- /dev/null +++ b/hw/acpi/aml-build.c @@ -0,0 +1,894 @@ +/* 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 +#include +#include +#include +#include +#include "hw/acpi/aml-build.h" +#include "qemu/bswap.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) +{ + /* It would be nicer to use g_string_vprintf but it's only there in 2.22 */ + 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) +{ + /* It would be nicer to use g_string_vprintf but it's only there in 2.22 */ + char *s; + int len; + va_list va_len; + char **segs; + char **segs_iter; + int seg_count = 0; + + va_copy(va_len, ap); + len = vsnprintf(NULL, 0, format, va_len); + va_end(va_len); + len += 1; + s = g_new(typeof(*s), len); + + len = vsnprintf(s, len, 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); + } +} + +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 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; +} + +/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefAnd */ +Aml *aml_and(Aml *arg1, Aml *arg2) +{ + Aml *var = aml_opcode(0x7B /* AndOp */); + aml_append(var, arg1); + aml_append(var, arg2); + build_append_byte(var->buf, 0x00 /* NullNameOp */); + return var; +} + +/* 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_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 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: 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.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.2 Named Objects Encoding: DefMethod */ +Aml *aml_method(const char *name, int arg_count) +{ + Aml *var = aml_bundle(0x14 /* MethodOp */, AML_PACKAGE); + build_append_namestring(var->buf, "%s", name); + build_append_byte(var->buf, arg_count); /* 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 */ +Aml *aml_buffer(void) +{ + Aml *var = aml_bundle(0x11 /* BufferOp */, AML_BUFFER); + 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, + uint32_t offset, uint32_t len) +{ + Aml *var = aml_alloc(); + build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */ + build_append_byte(var->buf, 0x80); /* OpRegionOp */ + build_append_namestring(var->buf, "%s", name); + build_append_byte(var->buf, rs); + build_append_int(var->buf, offset); + 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, AmlFieldFlags flags) +{ + Aml *var = aml_bundle(0x81 /* FieldOp */, AML_EXT_PACKAGE); + build_append_namestring(var->buf, "%s", name); + build_append_byte(var->buf, flags); + return var; +} + +/* 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, va_len; + char *s; + int len; + + va_start(ap, name_format); + va_copy(va_len, ap); + len = vsnprintf(NULL, 0, name_format, va_len); + va_end(va_len); + len += 1; + s = g_new0(typeof(*s), len); + + len = vsnprintf(s, len, name_format, ap); + va_end(ap); + + g_array_append_vals(var->buf, s, len); + build_append_byte(var->buf, 0x0); /* NullChar */ + 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 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, AmlCacheble 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, AmlCacheble 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); +} diff --git a/hw/i386/bios-linker-loader.c b/hw/acpi/bios-linker-loader.c similarity index 96% rename from hw/i386/bios-linker-loader.c rename to hw/acpi/bios-linker-loader.c index aa56184..d9382f8 100644 --- a/hw/i386/bios-linker-loader.c +++ b/hw/acpi/bios-linker-loader.c @@ -19,7 +19,7 @@ */ #include "qemu-common.h" -#include "bios-linker-loader.h" +#include "hw/acpi/bios-linker-loader.h" #include "hw/nvram/fw_cfg.h" #include "qemu/bswap.h" @@ -141,6 +141,7 @@ void bios_linker_loader_add_pointer(GArray *linker, uint8_t pointer_size) { BiosLinkerLoaderEntry entry; + size_t offset = (gchar *)pointer - table->data; memset(&entry, 0, sizeof entry); strncpy(entry.pointer.dest_file, dest_file, @@ -148,7 +149,8 @@ void bios_linker_loader_add_pointer(GArray *linker, strncpy(entry.pointer.src_file, src_file, sizeof entry.pointer.src_file - 1); entry.command = cpu_to_le32(BIOS_LINKER_LOADER_COMMAND_ADD_POINTER); - entry.pointer.offset = cpu_to_le32((gchar *)pointer - table->data); + assert(table->len >= offset + pointer_size); + entry.pointer.offset = cpu_to_le32(offset); entry.pointer.size = pointer_size; assert(pointer_size == 1 || pointer_size == 2 || pointer_size == 4 || pointer_size == 8); diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c index ea991a3..5352e19 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c @@ -166,7 +166,7 @@ const VMStateDescription vmstate_ich9_pm = { 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(acpi_regs.tmr.timer, 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), @@ -219,7 +219,7 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, 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, 2); + acpi_pm1_cnt_init(&pm->acpi_regs, &pm->io, 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, @@ -269,10 +269,94 @@ static void ich9_pm_set_memory_hotplug_support(Object *obj, bool value, s->pm.acpi_memory_hotplug.is_enabled = value; } +static void ich9_pm_get_disable_s3(Object *obj, Visitor *v, + void *opaque, const char *name, + Error **errp) +{ + ICH9LPCPMRegs *pm = opaque; + uint8_t value = pm->disable_s3; + + visit_type_uint8(v, &value, name, errp); +} + +static void ich9_pm_set_disable_s3(Object *obj, Visitor *v, + void *opaque, const char *name, + Error **errp) +{ + ICH9LPCPMRegs *pm = opaque; + Error *local_err = NULL; + uint8_t value; + + visit_type_uint8(v, &value, name, &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, + void *opaque, const char *name, + Error **errp) +{ + ICH9LPCPMRegs *pm = opaque; + uint8_t value = pm->disable_s4; + + visit_type_uint8(v, &value, name, errp); +} + +static void ich9_pm_set_disable_s4(Object *obj, Visitor *v, + void *opaque, const char *name, + Error **errp) +{ + ICH9LPCPMRegs *pm = opaque; + Error *local_err = NULL; + uint8_t value; + + visit_type_uint8(v, &value, name, &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, + void *opaque, const char *name, + Error **errp) +{ + ICH9LPCPMRegs *pm = opaque; + uint8_t value = pm->s4_val; + + visit_type_uint8(v, &value, name, errp); +} + +static void ich9_pm_set_s4_val(Object *obj, Visitor *v, + void *opaque, const char *name, + Error **errp) +{ + ICH9LPCPMRegs *pm = opaque; + Error *local_err = NULL; + uint8_t value; + + visit_type_uint8(v, &value, name, &local_err); + if (local_err) { + goto out; + } + pm->s4_val = value; +out: + error_propagate(errp, local_err); +} + 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); @@ -285,6 +369,18 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp) 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); } void ich9_pm_device_plug_cb(ICH9LPCPMRegs *pm, DeviceState *dev, Error **errp) @@ -301,6 +397,20 @@ void ich9_pm_device_plug_cb(ICH9LPCPMRegs *pm, DeviceState *dev, Error **errp) } } +void ich9_pm_device_unplug_request_cb(ICH9LPCPMRegs *pm, DeviceState *dev, + Error **errp) +{ + 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) +{ + 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); diff --git a/hw/acpi/memory_hotplug.c b/hw/acpi/memory_hotplug.c index ed39241..c6580da 100644 --- a/hw/acpi/memory_hotplug.c +++ b/hw/acpi/memory_hotplug.c @@ -168,7 +168,8 @@ void acpi_memory_plug_cb(ACPIREGS *ar, qemu_irq irq, MemHotplugState *mem_st, { MemStatus *mdev; Error *local_err = NULL; - int slot = object_property_get_int(OBJECT(dev), "slot", &local_err); + int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, + &local_err); if (local_err) { error_propagate(errp, local_err); diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c index 34dedf1..612fec0 100644 --- a/hw/acpi/pcihp.c +++ b/hw/acpi/pcihp.c @@ -297,10 +297,11 @@ static const MemoryRegionOps acpi_pcihp_io_ops = { }, }; -void acpi_pcihp_init(AcpiPciHpState *s, PCIBus *root_bus, +void acpi_pcihp_init(Object *owner, AcpiPciHpState *s, PCIBus *root_bus, MemoryRegion *address_space_io, bool bridges_enabled) { - uint16_t io_size = ACPI_PCIHP_SIZE; + s->io_len = ACPI_PCIHP_SIZE; + s->io_base = ACPI_PCIHP_ADDR; s->root= root_bus; s->legacy_piix = !bridges_enabled; @@ -308,16 +309,21 @@ void acpi_pcihp_init(AcpiPciHpState *s, PCIBus *root_bus, if (s->legacy_piix) { unsigned *bus_bsel = g_malloc(sizeof *bus_bsel); - io_size = ACPI_PCIHP_LEGACY_SIZE; + 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, NULL, &acpi_pcihp_io_ops, s, - "acpi-pci-hotplug", io_size); - memory_region_add_subregion(address_space_io, ACPI_PCIHP_ADDR, &s->io); + 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 = { diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index 481a16c..d1f1179 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -285,7 +285,7 @@ static const VMStateDescription vmstate_acpi = { VMSTATE_UINT16(ar.pm1.evt.en, PIIX4PMState), VMSTATE_UINT16(ar.pm1.cnt.cnt, PIIX4PMState), VMSTATE_STRUCT(apm, PIIX4PMState, 0, vmstate_apm, APMState), - VMSTATE_TIMER(ar.tmr.timer, PIIX4PMState), + 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( @@ -370,6 +370,13 @@ static void piix4_device_unplug_request_cb(HotplugHandler *hotplug_dev, } } +static void piix4_device_unplug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + 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; @@ -420,7 +427,7 @@ static void piix4_pm_add_propeties(PIIX4PMState *s) &s->io_base, NULL); } -static int piix4_pm_initfn(PCIDevice *dev) +static void piix4_pm_realize(PCIDevice *dev, Error **errp) { PIIX4PMState *s = PIIX4_PM(dev); uint8_t *pci_conf; @@ -470,7 +477,6 @@ static int piix4_pm_initfn(PCIDevice *dev) piix4_acpi_system_hot_add_init(pci_address_space_io(dev), dev->bus, s); piix4_pm_add_propeties(s); - return 0; } Object *piix4_pm_find(void) @@ -556,7 +562,7 @@ static void piix4_acpi_system_hot_add_init(MemoryRegion *parent, "acpi-gpe0", GPE_LEN); memory_region_add_subregion(parent, GPE_BASE, &s->io_gpe); - acpi_pcihp_init(&s->acpi_pci_hotplug, bus, parent, + 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, @@ -593,7 +599,7 @@ static void piix4_pm_class_init(ObjectClass *klass, void *data) HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(klass); - k->init = piix4_pm_initfn; + 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; @@ -610,6 +616,7 @@ static void piix4_pm_class_init(ObjectClass *klass, void *data) 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; } diff --git a/hw/alpha/dp264.c b/hw/alpha/dp264.c index 84a55e4..e82d61d 100644 --- a/hw/alpha/dp264.c +++ b/hw/alpha/dp264.c @@ -83,11 +83,7 @@ static void clipper_init(MachineState *machine) pci_vga_init(pci_bus); /* Serial code setup. */ - for (i = 0; i < MAX_SERIAL_PORTS; ++i) { - if (serial_hds[i]) { - serial_isa_init(isa_bus, i, serial_hds[i]); - } - } + 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++) { diff --git a/hw/alpha/typhoon.c b/hw/alpha/typhoon.c index 5310006..a6044f2 100644 --- a/hw/alpha/typhoon.c +++ b/hw/alpha/typhoon.c @@ -844,9 +844,8 @@ PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus, /* Main memory region, 0x00.0000.0000. Real hardware supports 32GB, but the address space hole reserved at this point is 8TB. */ - memory_region_init_ram(&s->ram_region, OBJECT(s), "ram", ram_size, - &error_abort); - vmstate_register_ram_global(&s->ram_region); + 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. */ @@ -920,7 +919,7 @@ PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus, { qemu_irq isa_pci_irq, *isa_irqs; - *isa_bus = isa_bus_new(NULL, &s->pchip.reg_io); + *isa_bus = isa_bus_new(NULL, get_system_memory(), &s->pchip.reg_io); isa_pci_irq = *qemu_allocate_irqs(typhoon_set_isa_irq, s, 1); isa_irqs = i8259_init(*isa_bus, isa_pci_irq); isa_bus_irqs(*isa_bus, isa_irqs); diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 6088e53..2577f68 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -3,8 +3,10 @@ obj-$(CONFIG_DIGIC) += digic_boards.o obj-y += integratorcp.o kzm.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-y += netduino2.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_STM32F205_SOC) += stm32f205_soc.o diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c index 01206f2..ff249af 100644 --- a/hw/arm/allwinner-a10.c +++ b/hw/arm/allwinner-a10.c @@ -34,6 +34,7 @@ static void aw_a10_init(Object *obj) 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]); @@ -92,6 +93,7 @@ static void aw_a10_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(sysbusdev, 0, AW_A10_EMAC_BASE); sysbus_connect_irq(sysbusdev, 0, s->irq[55]); + /* 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); } diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c index ef24ca4..c6eab6d 100644 --- a/hw/arm/armv7m.c +++ b/hw/arm/armv7m.c @@ -163,30 +163,23 @@ static void armv7m_reset(void *opaque) } /* Init CPU and memory for a v7-M based board. - flash_size and sram_size are in kb. + mem_size is in bytes. Returns the NVIC array. */ -qemu_irq *armv7m_init(MemoryRegion *system_memory, - int flash_size, int sram_size, +qemu_irq *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; - /* FIXME: make this local state. */ - static qemu_irq pic[64]; + qemu_irq *pic = g_new(qemu_irq, num_irq); int image_size; uint64_t entry; uint64_t lowaddr; int i; int big_endian; - MemoryRegion *sram = g_new(MemoryRegion, 1); - MemoryRegion *flash = g_new(MemoryRegion, 1); MemoryRegion *hack = g_new(MemoryRegion, 1); - flash_size *= 1024; - sram_size *= 1024; - if (cpu_model == NULL) { cpu_model = "cortex-m3"; } @@ -197,35 +190,15 @@ qemu_irq *armv7m_init(MemoryRegion *system_memory, } env = &cpu->env; -#if 0 - /* > 32Mb SRAM gets complicated because it overlaps the bitband area. - We don't have proper commandline options, so allocate half of memory - as SRAM, up to a maximum of 32Mb, and the rest as code. */ - if (ram_size > (512 + 32) * 1024 * 1024) - ram_size = (512 + 32) * 1024 * 1024; - sram_size = (ram_size / 2) & TARGET_PAGE_MASK; - if (sram_size > 32 * 1024 * 1024) - sram_size = 32 * 1024 * 1024; - code_size = ram_size - sram_size; -#endif - - /* Flash programming is done via the SCU, so pretend it is ROM. */ - memory_region_init_ram(flash, NULL, "armv7m.flash", flash_size, - &error_abort); - 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, "armv7m.sram", sram_size, &error_abort); - vmstate_register_ram_global(sram); - memory_region_add_subregion(system_memory, 0x20000000, sram); 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)); - for (i = 0; i < 64; i++) { + for (i = 0; i < num_irq; i++) { pic[i] = qdev_get_gpio_in(nvic, i); } @@ -244,7 +217,7 @@ qemu_irq *armv7m_init(MemoryRegion *system_memory, image_size = load_elf(kernel_filename, NULL, NULL, &entry, &lowaddr, NULL, big_endian, ELF_MACHINE, 1); if (image_size < 0) { - image_size = load_image_targphys(kernel_filename, 0, flash_size); + image_size = load_image_targphys(kernel_filename, 0, mem_size); lowaddr = 0; } if (image_size < 0) { diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 0014c34..a48d1b2 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -329,6 +329,8 @@ static void set_kernel_args_old(const struct arm_boot_info *info) * 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) @@ -352,7 +354,7 @@ static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo, goto fail; } g_free(filename); - } else if (binfo->get_dtb) { + } else { fdt = binfo->get_dtb(binfo, &size); if (!fdt) { fprintf(stderr, "Board was unable to create a dtb blob\n"); @@ -455,6 +457,34 @@ static void do_cpu_reset(void *opaque) env->thumb = info->entry & 1; } } 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) { + if (arm_feature(env, ARM_FEATURE_EL2)) { + env->pstate = PSTATE_MODE_EL2h; + } else { + env->pstate = PSTATE_MODE_EL1h; + } + } + + /* Set to non-secure if not a secure boot */ + if (!info->secure_boot) { + /* Linux expects non-secure state */ + env->cp15.scr_el3 |= SCR_NS; + } + } + if (CPU(cpu) == first_cpu) { if (env->aarch64) { env->pc = info->loader_start; @@ -476,6 +506,55 @@ static void do_cpu_reset(void *opaque) } } +/** + * 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); +} + void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) { CPUState *cs; @@ -498,19 +577,48 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) } /* Load the kernel. */ - if (!info->kernel_filename) { + 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, - * copy it to the base of RAM for a bootloader to pick up. + /* 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 no kernel specified, do nothing; we will start from address 0 - * (typically a boot ROM image) in the same way as hardware. + 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; } diff --git a/hw/arm/cubieboard.c b/hw/arm/cubieboard.c index d1e53be..1582250 100644 --- a/hw/arm/cubieboard.c +++ b/hw/arm/cubieboard.c @@ -63,9 +63,8 @@ static void cubieboard_init(MachineState *machine) exit(1); } - memory_region_init_ram(&s->sdram, NULL, "cubieboard.ram", - machine->ram_size, &error_abort); - vmstate_register_ram_global(&s->sdram); + 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); diff --git a/hw/arm/digic_boards.c b/hw/arm/digic_boards.c index 2a4b872..f8ba9e5 100644 --- a/hw/arm/digic_boards.c +++ b/hw/arm/digic_boards.c @@ -51,9 +51,8 @@ typedef struct DigicBoard { static void digic4_board_setup_ram(DigicBoardState *s, hwaddr ram_size) { - memory_region_init_ram(&s->ram, NULL, "ram", ram_size, &error_abort); + memory_region_allocate_system_memory(&s->ram, NULL, "ram", ram_size); memory_region_add_subregion(get_system_memory(), 0, &s->ram); - vmstate_register_ram_global(&s->ram); } static void digic4_board_init(DigicBoard *board) @@ -65,7 +64,7 @@ static void digic4_board_init(DigicBoard *board) s->digic = DIGIC(object_new(TYPE_DIGIC)); object_property_set_bool(OBJECT(s->digic), true, "realized", &err); if (err != NULL) { - error_report("Couldn't realize DIGIC SoC: %s\n", + error_report("Couldn't realize DIGIC SoC: %s", error_get_pretty(err)); exit(1); } @@ -104,15 +103,16 @@ static void digic_load_rom(DigicBoardState *s, hwaddr addr, char *fn = qemu_find_file(QEMU_FILE_TYPE_BIOS, filename); if (!fn) { - error_report("Couldn't find rom image '%s'.\n", filename); + 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'.\n", filename); + error_report("Couldn't load rom image '%s'.", filename); exit(1); } + g_free(fn); } } diff --git a/hw/arm/exynos4210.c b/hw/arm/exynos4210.c index 582794c..c55fab8 100644 --- a/hw/arm/exynos4210.c +++ b/hw/arm/exynos4210.c @@ -152,12 +152,23 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem, Object *cpuobj = object_new(object_class_get_name(cpu_oc)); Error *err = NULL; + /* By default A9 CPUs have EL3 enabled. This board does not currently + * support EL3 so the CPU EL3 property is disabled before realization. + */ + if (object_property_find(cpuobj, "has_el3", NULL)) { + object_property_set_bool(cpuobj, false, "has_el3", &err); + if (err) { + error_report_err(err); + exit(1); + } + } + s->cpu[n] = ARM_CPU(cpuobj); object_property_set_int(cpuobj, EXYNOS4210_SMP_PRIVATE_BASE_ADDR, "reset-cbar", &error_abort); object_property_set_bool(cpuobj, true, "realized", &err); if (err) { - error_report("%s", error_get_pretty(err)); + error_report_err(err); exit(1); } } diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c index 30f744a..dd2a67b 100644 --- a/hw/arm/highbank.c +++ b/hw/arm/highbank.c @@ -241,13 +241,25 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) cpuobj = object_new(object_class_get_name(oc)); cpu = ARM_CPU(cpuobj); + /* By default A9 and A15 CPUs have EL3 enabled. This board does not + * currently support EL3 so the CPU EL3 property is disabled before + * realization. + */ + if (object_property_find(cpuobj, "has_el3", NULL)) { + object_property_set_bool(cpuobj, false, "has_el3", &err); + if (err) { + error_report_err(err); + exit(1); + } + } + if (object_property_find(cpuobj, "reset-cbar", NULL)) { object_property_set_int(cpuobj, MPCORE_PERIPHBASE, "reset-cbar", &error_abort); } object_property_set_bool(cpuobj, true, "realized", &err); if (err) { - error_report("%s", error_get_pretty(err)); + error_report_err(err); exit(1); } cpu_irq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ); @@ -255,7 +267,7 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) sysmem = get_system_memory(); dram = g_new(MemoryRegion, 1); - memory_region_init_ram(dram, NULL, "highbank.dram", ram_size, &error_abort); + memory_region_allocate_system_memory(dram, NULL, "highbank.dram", ram_size); /* SDRAM at address zero. */ memory_region_add_subregion(sysmem, 0, dram); @@ -266,10 +278,10 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) if (bios_name != NULL) { sysboot_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); if (sysboot_filename != NULL) { - uint32_t filesize = get_image_size(sysboot_filename); - if (load_image_targphys("sysram.bin", 0xfff88000, filesize) < 0) { + if (load_image_targphys(sysboot_filename, 0xfff88000, 0x8000) < 0) { hw_error("Unable to load %s\n", bios_name); } + g_free(sysboot_filename); } else { hw_error("Unable to find %s\n", bios_name); } diff --git a/hw/arm/integratorcp.c b/hw/arm/integratorcp.c index 266ec18..0fbbf99 100644 --- a/hw/arm/integratorcp.c +++ b/hw/arm/integratorcp.c @@ -15,6 +15,7 @@ #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) \ @@ -405,16 +406,39 @@ static int icp_pic_init(SysBusDevice *sbd) /* 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 0; + return s->intreg_state; case 3: /* CP_DECODE */ return 0x11; default: @@ -426,9 +450,14 @@ static uint64_t icp_control_read(void *opaque, hwaddr offset, static void icp_control_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { + ICPCtrlRegsState *s = opaque; + switch (offset >> 2) { - case 1: /* CP_FLASHPROG */ 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; @@ -443,15 +472,41 @@ static const MemoryRegionOps icp_control_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static void icp_control_init(hwaddr base) +static void icp_control_mmc_wprot(void *opaque, int line, int level) { - MemoryRegion *io; + ICPCtrlRegsState *s = opaque; - io = (MemoryRegion *)g_malloc0(sizeof(MemoryRegion)); - memory_region_init_io(io, NULL, &icp_control_ops, NULL, - "control", 0x00800000); - memory_region_add_subregion(get_system_memory(), base, io); - /* ??? Save/restore. */ + 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); } @@ -469,25 +524,51 @@ static void integratorcp_init(MachineState *machine) 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; + DeviceState *dev, *sic, *icp; int i; + Error *err = NULL; if (!cpu_model) { cpu_model = "arm926"; } - cpu = cpu_arm_init(cpu_model); - if (!cpu) { + + cpu_oc = cpu_class_by_name(TYPE_ARM_CPU, cpu_model); + if (!cpu_oc) { fprintf(stderr, "Unable to find CPU definition\n"); exit(1); } - memory_region_init_ram(ram, NULL, "integrator.ram", ram_size, &error_abort); - vmstate_register_ram_global(ram); + 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", &err); + if (err) { + error_report_err(err); + exit(1); + } + } + + object_property_set_bool(cpuobj, true, "realized", &err); + if (err) { + error_report_err(err); + exit(1); + } + + 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*/ @@ -508,17 +589,24 @@ static void integratorcp_init(MachineState *machine) for (i = 0; i < 32; i++) { pic[i] = qdev_get_gpio_in(dev, i); } - sysbus_create_simple(TYPE_INTEGRATOR_PIC, 0xca000000, pic[26]); + 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_control_init(0xcb000000); + 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); - sysbus_create_varargs("pl181", 0x1c000000, pic[23], pic[24], NULL); + + 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]); @@ -579,10 +667,18 @@ static const TypeInfo icp_pic_info = { .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/hw/arm/kzm.c b/hw/arm/kzm.c index 94ceab6..5be0369 100644 --- a/hw/arm/kzm.c +++ b/hw/arm/kzm.c @@ -97,8 +97,7 @@ static void kzm_init(MachineState *machine) /* On a real system, the first 16k is a `secure boot rom' */ - memory_region_init_ram(ram, NULL, "kzm.ram", ram_size, &error_abort); - vmstate_register_ram_global(ram); + memory_region_allocate_system_memory(ram, NULL, "kzm.ram", ram_size); memory_region_add_subregion(address_space_mem, KZM_RAMADDRESS, ram); memory_region_init_alias(ram_alias, NULL, "ram.alias", ram, 0, ram_size); diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c index 3712de6..a3b1314 100644 --- a/hw/arm/musicpal.c +++ b/hw/arm/musicpal.c @@ -1600,9 +1600,8 @@ static void musicpal_init(MachineState *machine) } /* For now we use a fixed - the original - RAM size */ - memory_region_init_ram(ram, NULL, "musicpal.ram", MP_RAM_DEFAULT_SIZE, - &error_abort); - vmstate_register_ram_global(ram); + 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, diff --git a/hw/arm/netduino2.c b/hw/arm/netduino2.c new file mode 100644 index 0000000..8f26780 --- /dev/null +++ b/hw/arm/netduino2.c @@ -0,0 +1,57 @@ +/* + * 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 "hw/boards.h" +#include "qemu/error-report.h" +#include "hw/arm/stm32f205_soc.h" + +static void netduino2_init(MachineState *machine) +{ + DeviceState *dev; + Error *err = NULL; + + dev = qdev_create(NULL, TYPE_STM32F205_SOC); + if (machine->kernel_filename) { + qdev_prop_set_string(dev, "kernel-filename", machine->kernel_filename); + } + qdev_prop_set_string(dev, "cpu-model", "cortex-m3"); + object_property_set_bool(OBJECT(dev), true, "realized", &err); + if (err != NULL) { + error_report("%s", error_get_pretty(err)); + exit(1); + } +} + +static QEMUMachine netduino2_machine = { + .name = "netduino2", + .desc = "Netduino 2 Machine", + .init = netduino2_init, +}; + +static void netduino2_machine_init(void) +{ + qemu_register_machine(&netduino2_machine); +} + +machine_init(netduino2_machine_init); diff --git a/hw/arm/nseries.c b/hw/arm/nseries.c index c7ebaa6..2a5406d 100644 --- a/hw/arm/nseries.c +++ b/hw/arm/nseries.c @@ -1344,7 +1344,7 @@ static void n8x0_init(MachineState *machine, n8x0_dss_setup(s); n8x0_cbus_setup(s); n8x0_uart_setup(s); - if (usb_enabled(false)) { + if (usb_enabled()) { n8x0_usb_setup(s); } @@ -1403,12 +1403,12 @@ static struct arm_boot_info n810_binfo = { static void n800_init(MachineState *machine) { - return n8x0_init(machine, &n800_binfo, 800); + n8x0_init(machine, &n800_binfo, 800); } static void n810_init(MachineState *machine) { - return n8x0_init(machine, &n810_binfo, 810); + n8x0_init(machine, &n810_binfo, 810); } static QEMUMachine n800_machine = { diff --git a/hw/arm/omap1.c b/hw/arm/omap1.c index abb183c..91ffb58 100644 --- a/hw/arm/omap1.c +++ b/hw/arm/omap1.c @@ -16,6 +16,8 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ + +#include "hw/boards.h" #include "hw/hw.h" #include "hw/arm/arm.h" #include "hw/arm/omap.h" @@ -207,7 +209,8 @@ static void omap_mpu_timer_write(void *opaque, hwaddr addr, struct omap_mpu_timer_s *s = (struct omap_mpu_timer_s *) opaque; if (size != 4) { - return omap_badwidth_write32(opaque, addr, value); + omap_badwidth_write32(opaque, addr, value); + return; } switch (addr) { @@ -314,7 +317,8 @@ static void omap_wd_timer_write(void *opaque, hwaddr addr, struct omap_watchdog_timer_s *s = (struct omap_watchdog_timer_s *) opaque; if (size != 2) { - return omap_badwidth_write16(opaque, addr, value); + omap_badwidth_write16(opaque, addr, value); + return; } switch (addr) { @@ -440,7 +444,8 @@ static void omap_os_timer_write(void *opaque, hwaddr addr, int offset = addr & OMAP_MPUI_REG_MASK; if (size != 4) { - return omap_badwidth_write32(opaque, addr, value); + omap_badwidth_write32(opaque, addr, value); + return; } switch (offset) { @@ -585,7 +590,8 @@ static void omap_ulpd_pm_write(void *opaque, hwaddr addr, uint16_t diff; if (size != 2) { - return omap_badwidth_write16(opaque, addr, value); + omap_badwidth_write16(opaque, addr, value); + return; } switch (addr) { @@ -857,7 +863,8 @@ static void omap_pin_cfg_write(void *opaque, hwaddr addr, uint32_t diff; if (size != 4) { - return omap_badwidth_write32(opaque, addr, value); + omap_badwidth_write32(opaque, addr, value); + return; } switch (addr) { @@ -1012,7 +1019,8 @@ static void omap_id_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { if (size != 4) { - return omap_badwidth_write32(opaque, addr, value); + omap_badwidth_write32(opaque, addr, value); + return; } OMAP_BAD_REG(addr); @@ -1081,7 +1089,8 @@ static void omap_mpui_write(void *opaque, hwaddr addr, struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; if (size != 4) { - return omap_badwidth_write32(opaque, addr, value); + omap_badwidth_write32(opaque, addr, value); + return; } switch (addr) { @@ -1175,7 +1184,8 @@ static void omap_tipb_bridge_write(void *opaque, hwaddr addr, struct omap_tipb_bridge_s *s = (struct omap_tipb_bridge_s *) opaque; if (size < 2) { - return omap_badwidth_write16(opaque, addr, value); + omap_badwidth_write16(opaque, addr, value); + return; } switch (addr) { @@ -1284,7 +1294,8 @@ static void omap_tcmi_write(void *opaque, hwaddr addr, struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; if (size != 4) { - return omap_badwidth_write32(opaque, addr, value); + omap_badwidth_write32(opaque, addr, value); + return; } switch (addr) { @@ -1379,7 +1390,8 @@ static void omap_dpll_write(void *opaque, hwaddr addr, int div, mult; if (size != 2) { - return omap_badwidth_write16(opaque, addr, value); + omap_badwidth_write16(opaque, addr, value); + return; } if (addr == 0x00) { /* CTL_REG */ @@ -1647,7 +1659,8 @@ static void omap_clkm_write(void *opaque, hwaddr addr, }; if (size != 2) { - return omap_badwidth_write16(opaque, addr, value); + omap_badwidth_write16(opaque, addr, value); + return; } switch (addr) { @@ -1775,7 +1788,8 @@ static void omap_clkdsp_write(void *opaque, hwaddr addr, uint16_t diff; if (size != 2) { - return omap_badwidth_write16(opaque, addr, value); + omap_badwidth_write16(opaque, addr, value); + return; } switch (addr) { @@ -1982,7 +1996,8 @@ static void omap_mpuio_write(void *opaque, hwaddr addr, int ln; if (size != 2) { - return omap_badwidth_write16(opaque, addr, value); + omap_badwidth_write16(opaque, addr, value); + return; } switch (offset) { @@ -2210,7 +2225,8 @@ static void omap_uwire_write(void *opaque, hwaddr addr, int offset = addr & OMAP_MPUI_REG_MASK; if (size != 2) { - return omap_badwidth_write16(opaque, addr, value); + omap_badwidth_write16(opaque, addr, value); + return; } switch (offset) { @@ -2349,7 +2365,8 @@ static void omap_pwl_write(void *opaque, hwaddr addr, int offset = addr & OMAP_MPUI_REG_MASK; if (size != 1) { - return omap_badwidth_write8(opaque, addr, value); + omap_badwidth_write8(opaque, addr, value); + return; } switch (offset) { @@ -2444,7 +2461,8 @@ static void omap_pwt_write(void *opaque, hwaddr addr, int offset = addr & OMAP_MPUI_REG_MASK; if (size != 1) { - return omap_badwidth_write8(opaque, addr, value); + omap_badwidth_write8(opaque, addr, value); + return; } switch (offset) { @@ -2637,7 +2655,8 @@ static void omap_rtc_write(void *opaque, hwaddr addr, time_t ti[2]; if (size != 1) { - return omap_badwidth_write8(opaque, addr, value); + omap_badwidth_write8(opaque, addr, value); + return; } switch (offset) { @@ -3410,9 +3429,14 @@ static void omap_mcbsp_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { switch (size) { - case 2: return omap_mcbsp_writeh(opaque, addr, value); - case 4: return omap_mcbsp_writew(opaque, addr, value); - default: return omap_badwidth_write16(opaque, addr, value); + 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); } } @@ -3586,7 +3610,8 @@ static void omap_lpg_write(void *opaque, hwaddr addr, int offset = addr & OMAP_MPUI_REG_MASK; if (size != 1) { - return omap_badwidth_write8(opaque, addr, value); + omap_badwidth_write8(opaque, addr, value); + return; } switch (offset) { @@ -3855,9 +3880,8 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *system_memory, omap_clk_init(s); /* Memory-mapped stuff */ - memory_region_init_ram(&s->emiff_ram, NULL, "omap1.dram", s->sdram_size, - &error_abort); - vmstate_register_ram_global(&s->emiff_ram); + 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_abort); diff --git a/hw/arm/omap2.c b/hw/arm/omap2.c index b083ebe..e39b317 100644 --- a/hw/arm/omap2.c +++ b/hw/arm/omap2.c @@ -20,6 +20,7 @@ #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" @@ -447,7 +448,8 @@ static void omap_eac_write(void *opaque, hwaddr addr, struct omap_eac_s *s = (struct omap_eac_s *) opaque; if (size != 2) { - return omap_badwidth_write16(opaque, addr, value); + omap_badwidth_write16(opaque, addr, value); + return; } switch (addr) { @@ -692,7 +694,8 @@ static void omap_sti_write(void *opaque, hwaddr addr, struct omap_sti_s *s = (struct omap_sti_s *) opaque; if (size != 4) { - return omap_badwidth_write32(opaque, addr, value); + omap_badwidth_write32(opaque, addr, value); + return; } switch (addr) { @@ -757,7 +760,8 @@ static void omap_sti_fifo_write(void *opaque, hwaddr addr, uint8_t byte = value; if (size != 1) { - return omap_badwidth_write8(opaque, addr, size); + omap_badwidth_write8(opaque, addr, size); + return; } if (ch == STI_TRACE_CONTROL_CHANNEL) { @@ -1359,7 +1363,8 @@ static void omap_prcm_write(void *opaque, hwaddr addr, struct omap_prcm_s *s = (struct omap_prcm_s *) opaque; if (size != 4) { - return omap_badwidth_write32(opaque, addr, value); + omap_badwidth_write32(opaque, addr, value); + return; } switch (addr) { @@ -2267,9 +2272,8 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sysmem, omap_clk_init(s); /* Memory-mapped stuff */ - memory_region_init_ram(&s->sdram, NULL, "omap2.dram", s->sdram_size, - &error_abort); - vmstate_register_ram_global(&s->sdram); + 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_abort); diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c index 693dfec..165ba2a 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -273,10 +273,10 @@ static void pxa2xx_pwrmode_write(CPUARMState *env, const ARMCPRegInfo *ri, 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.c1_sys = 0; + s->cpu->env.cp15.sctlr_ns = 0; s->cpu->env.cp15.c1_coproc = 0; - s->cpu->env.cp15.ttbr0_el1 = 0; - s->cpu->env.cp15.c3 = 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 */ @@ -2143,7 +2143,7 @@ PXA2xxState *pxa270_init(MemoryRegion *address_space, s->ssp[i] = (SSIBus *)qdev_get_child_bus(dev, "ssi"); } - if (usb_enabled(false)) { + if (usb_enabled()) { sysbus_create_simple("sysbus-ohci", 0x4c000000, qdev_get_gpio_in(s->pic, PXA2XX_PIC_USBH1)); } @@ -2276,7 +2276,7 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size) s->ssp[i] = (SSIBus *)qdev_get_child_bus(dev, "ssi"); } - if (usb_enabled(false)) { + if (usb_enabled()) { sysbus_create_simple("sysbus-ohci", 0x4c000000, qdev_get_gpio_in(s->pic, PXA2XX_PIC_USBH1)); } diff --git a/hw/arm/realview.c b/hw/arm/realview.c index af65aa4..ef2788d 100644 --- a/hw/arm/realview.c +++ b/hw/arm/realview.c @@ -52,7 +52,7 @@ static void realview_init(MachineState *machine, CPUARMState *env; ObjectClass *cpu_oc; MemoryRegion *sysmem = get_system_memory(); - MemoryRegion *ram_lo = g_new(MemoryRegion, 1); + MemoryRegion *ram_lo; MemoryRegion *ram_hi = g_new(MemoryRegion, 1); MemoryRegion *ram_alias = g_new(MemoryRegion, 1); MemoryRegion *ram_hack = g_new(MemoryRegion, 1); @@ -101,17 +101,29 @@ static void realview_init(MachineState *machine, Object *cpuobj = object_new(object_class_get_name(cpu_oc)); Error *err = NULL; + /* By default A9,A15 and ARM1176 CPUs have EL3 enabled. This board + * does not currently support EL3 so the CPU EL3 property is disabled + * before realization. + */ + if (object_property_find(cpuobj, "has_el3", NULL)) { + object_property_set_bool(cpuobj, false, "has_el3", &err); + if (err) { + error_report_err(err); + exit(1); + } + } + if (is_pb && is_mpcore) { object_property_set_int(cpuobj, periphbase, "reset-cbar", &err); if (err) { - error_report("%s", error_get_pretty(err)); + error_report_err(err); exit(1); } } object_property_set_bool(cpuobj, true, "realized", &err); if (err) { - error_report("%s", error_get_pretty(err)); + error_report_err(err); exit(1); } @@ -135,6 +147,7 @@ static void realview_init(MachineState *machine, 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, @@ -248,7 +261,7 @@ static void realview_init(MachineState *machine, 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(false)) { + if (usb_enabled()) { pci_create_simple(pci_bus, -1, "pci-ohci"); } n = drive_get_max_bus(IF_SCSI); diff --git a/hw/arm/spitz.c b/hw/arm/spitz.c index a16831c..5bf032a 100644 --- a/hw/arm/spitz.c +++ b/hw/arm/spitz.c @@ -168,6 +168,7 @@ static int sl_nand_init(SysBusDevice *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); @@ -1035,6 +1036,8 @@ static void sl_nand_class_init(ObjectClass *klass, void *data) 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 = { diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index 64bd4b4..cb515ec 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -29,6 +29,8 @@ #define BP_OLED_SSI 0x02 #define BP_GAMEPAD 0x04 +#define NUM_IRQ_LINES 64 + typedef const struct { const char *name; uint32_t did0; @@ -306,7 +308,7 @@ static const VMStateDescription vmstate_stellaris_gptm = { VMSTATE_UINT32_ARRAY(match_prescale, gptm_state, 2), VMSTATE_UINT32(rtc, gptm_state), VMSTATE_INT64_ARRAY(tick, gptm_state, 2), - VMSTATE_TIMER_ARRAY(timer, gptm_state, 2), + VMSTATE_TIMER_PTR_ARRAY(timer, gptm_state, 2), VMSTATE_END_OF_LIST() } }; @@ -1220,10 +1222,27 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model, int i; int j; - flash_size = ((board->dc0 & 0xffff) + 1) << 1; - sram_size = (board->dc0 >> 18) + 1; - pic = armv7m_init(get_system_memory(), - flash_size, sram_size, kernel_filename, cpu_model); + 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_abort); + vmstate_register_ram_global(flash); + memory_region_set_readonly(flash, true); + memory_region_add_subregion(system_memory, 0, flash); + + memory_region_init_ram(sram, NULL, "stellaris.sram", sram_size, + &error_abort); + vmstate_register_ram_global(sram); + memory_region_add_subregion(system_memory, 0x20000000, sram); + + pic = armv7m_init(system_memory, flash_size, NUM_IRQ_LINES, + kernel_filename, cpu_model); if (board->dc1 & (1 << 16)) { dev = sysbus_create_varargs(TYPE_STELLARIS_ADC, 0x40038000, diff --git a/hw/arm/stm32f205_soc.c b/hw/arm/stm32f205_soc.c new file mode 100644 index 0000000..0f3bdc7 --- /dev/null +++ b/hw/arm/stm32f205_soc.c @@ -0,0 +1,160 @@ +/* + * 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 "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; + SysBusDevice *syscfgbusdev, *usartbusdev, *timerbusdev; + qemu_irq *pic; + 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_abort); + 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_abort); + vmstate_register_ram_global(sram); + memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, sram); + + pic = armv7m_init(get_system_memory(), FLASH_SIZE, 96, + s->kernel_filename, s->cpu_model); + + /* 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, pic[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, pic[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, pic[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/hw/arm/strongarm.c b/hw/arm/strongarm.c index 3206345..1ddea6d 100644 --- a/hw/arm/strongarm.c +++ b/hw/arm/strongarm.c @@ -26,6 +26,8 @@ * Contributions after 2012-01-13 are licensed under the terms of the * GNU GPL, version 2 or (at your option) any later version. */ + +#include "hw/boards.h" #include "hw/sysbus.h" #include "strongarm.h" #include "qemu/error-report.h" @@ -1604,9 +1606,8 @@ StrongARMState *sa1110_init(MemoryRegion *sysmem, exit(1); } - memory_region_init_ram(&s->sdram, NULL, "strongarm.sdram", sdram_size, - &error_abort); - vmstate_register_ram_global(&s->sdram); + 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, diff --git a/hw/arm/versatilepb.c b/hw/arm/versatilepb.c index e6ef0a2..6c69f4e 100644 --- a/hw/arm/versatilepb.c +++ b/hw/arm/versatilepb.c @@ -18,6 +18,7 @@ #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) @@ -175,6 +176,8 @@ 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); @@ -189,18 +192,42 @@ static void versatile_init(MachineState *machine, int board_id) int n; int done_smc = 0; DriveInfo *dinfo; + Error *err = NULL; if (!machine->cpu_model) { machine->cpu_model = "arm926"; } - cpu = cpu_arm_init(machine->cpu_model); - if (!cpu) { + + 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); } - memory_region_init_ram(ram, NULL, "versatile.ram", machine->ram_size, - &error_abort); - vmstate_register_ram_global(ram); + + 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", &err); + if (err) { + error_report_err(err); + exit(1); + } + } + + object_property_set_bool(cpuobj, true, "realized", &err); + if (err) { + error_report_err(err); + exit(1); + } + + 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); @@ -253,7 +280,7 @@ static void versatile_init(MachineState *machine, int board_id) pci_nic_init_nofail(nd, pci_bus, "rtl8139", NULL); } } - if (usb_enabled(false)) { + if (usb_enabled()) { pci_create_simple(pci_bus, -1, "pci-ohci"); } n = drive_get_max_bus(IF_SCSI); diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index 7cbd13f..3989bc5 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -157,7 +157,27 @@ static hwaddr motherboard_aseries_map[] = { typedef struct VEDBoardInfo VEDBoardInfo; -typedef void DBoardInitFn(const VEDBoardInfo *daughterboard, +typedef struct { + MachineClass parent; + VEDBoardInfo *daughterboard; +} VexpressMachineClass; + +typedef struct { + MachineState parent; + bool secure; +} VexpressMachineState; + +#define TYPE_VEXPRESS_MACHINE "vexpress" +#define TYPE_VEXPRESS_A9_MACHINE "vexpress-a9" +#define TYPE_VEXPRESS_A15_MACHINE "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); @@ -176,7 +196,7 @@ struct VEDBoardInfo { }; static void init_cpus(const char *cpu_model, const char *privdev, - hwaddr periphbase, qemu_irq *pic) + hwaddr periphbase, qemu_irq *pic, bool secure) { ObjectClass *cpu_oc = cpu_class_by_name(TYPE_ARM_CPU, cpu_model); DeviceState *dev; @@ -193,13 +213,17 @@ static void init_cpus(const char *cpu_model, const char *privdev, Object *cpuobj = object_new(object_class_get_name(cpu_oc)); Error *err = NULL; + 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", &err); if (err) { - error_report("%s", error_get_pretty(err)); + error_report_err(err); exit(1); } } @@ -232,7 +256,7 @@ static void init_cpus(const char *cpu_model, const char *privdev, } } -static void a9_daughterboard_init(const VEDBoardInfo *daughterboard, +static void a9_daughterboard_init(const VexpressMachineState *vms, ram_addr_t ram_size, const char *cpu_model, qemu_irq *pic) @@ -252,9 +276,8 @@ static void a9_daughterboard_init(const VEDBoardInfo *daughterboard, exit(1); } - memory_region_init_ram(ram, NULL, "vexpress.highmem", ram_size, - &error_abort); - vmstate_register_ram_global(ram); + 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; @@ -268,7 +291,7 @@ static void a9_daughterboard_init(const VEDBoardInfo *daughterboard, memory_region_add_subregion(sysmem, 0x60000000, ram); /* 0x1e000000 A9MPCore (SCU) private memory region */ - init_cpus(cpu_model, "a9mpcore_priv", 0x1e000000, pic); + init_cpus(cpu_model, "a9mpcore_priv", 0x1e000000, pic, vms->secure); /* Daughterboard peripherals : 0x10020000 .. 0x20000000 */ @@ -322,7 +345,7 @@ static VEDBoardInfo a9_daughterboard = { .init = a9_daughterboard_init, }; -static void a15_daughterboard_init(const VEDBoardInfo *daughterboard, +static void a15_daughterboard_init(const VexpressMachineState *vms, ram_addr_t ram_size, const char *cpu_model, qemu_irq *pic) @@ -347,14 +370,13 @@ static void a15_daughterboard_init(const VEDBoardInfo *daughterboard, } } - memory_region_init_ram(ram, NULL, "vexpress.highmem", ram_size, - &error_abort); - vmstate_register_ram_global(ram); + 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); + init_cpus(cpu_model, "a15mpcore_priv", 0x2c000000, pic, vms->secure); /* A15 daughterboard peripherals: */ @@ -491,9 +513,9 @@ static pflash_t *ve_pflash_cfi01_register(hwaddr base, const char *name, { DeviceState *dev = qdev_create(NULL, "cfi.pflash01"); - if (di && qdev_prop_set_drive(dev, "drive", - blk_by_legacy_dinfo(di))) { - abort(); + if (di) { + qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(di), + &error_abort); } qdev_prop_set_uint32(dev, "num-blocks", @@ -513,9 +535,11 @@ static pflash_t *ve_pflash_cfi01_register(hwaddr base, const char *name, return OBJECT_CHECK(pflash_t, (dev), "cfi.pflash01"); } -static void vexpress_common_init(VEDBoardInfo *daughterboard, - MachineState *machine) +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; @@ -530,14 +554,14 @@ static void vexpress_common_init(VEDBoardInfo *daughterboard, const hwaddr *map = daughterboard->motherboard_map; int i; - daughterboard->init(daughterboard, machine->ram_size, machine->cpu_model, - pic); + 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) { - const char *fn; + char *fn; + int image_size; if (drive_get(IF_PFLASH, 0, 0)) { error_report("The contents of the first flash device may be " @@ -546,8 +570,14 @@ static void vexpress_common_init(VEDBoardInfo *daughterboard, exit(1); } fn = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - if (!fn || load_image_targphys(fn, map[VE_NORFLASH0], - VEXPRESS_FLASH_SIZE) < 0) { + 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); } @@ -678,39 +708,99 @@ static void vexpress_common_init(VEDBoardInfo *daughterboard, 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 void vexpress_a9_init(MachineState *machine) +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) { - vexpress_common_init(&a9_daughterboard, machine); + VexpressMachineState *vms = VEXPRESS_MACHINE(obj); + + vms->secure = value; } -static void vexpress_a15_init(MachineState *machine) +static void vexpress_instance_init(Object *obj) { - vexpress_common_init(&a15_daughterboard, machine); + 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 QEMUMachine vexpress_a9_machine = { - .name = "vexpress-a9", - .desc = "ARM Versatile Express for Cortex-A9", - .init = vexpress_a9_init, - .block_default_type = IF_SCSI, - .max_cpus = 4, +static void vexpress_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->name = TYPE_VEXPRESS_MACHINE; + mc->desc = "ARM Versatile Express"; + mc->init = vexpress_common_init; + mc->block_default_type = IF_SCSI; + 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->name = TYPE_VEXPRESS_A9_MACHINE; + 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->name = TYPE_VEXPRESS_A15_MACHINE; + 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 QEMUMachine vexpress_a15_machine = { - .name = "vexpress-a15", - .desc = "ARM Versatile Express for Cortex-A15", - .init = vexpress_a15_init, - .block_default_type = IF_SCSI, - .max_cpus = 4, +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) { - qemu_register_machine(&vexpress_a9_machine); - qemu_register_machine(&vexpress_a15_machine); + type_register_static(&vexpress_info); + type_register_static(&vexpress_a9_info); + type_register_static(&vexpress_a15_info); } machine_init(vexpress_machine_init); diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 314e55b..565f573 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -42,6 +42,7 @@ #include "exec/address-spaces.h" #include "qemu/bitops.h" #include "qemu/error-report.h" +#include "hw/pci-host/gpex.h" #define NUM_VIRTIO_TRANSPORTS 32 @@ -68,6 +69,8 @@ enum { VIRT_UART, VIRT_MMIO, VIRT_RTC, + VIRT_FW_CFG, + VIRT_PCIE, }; typedef struct MemMapEntry { @@ -86,6 +89,24 @@ typedef struct VirtBoardInfo { uint32_t clock_phandle; } VirtBoardInfo; +typedef struct { + MachineClass parent; + VirtBoardInfo *daughterboard; +} VirtMachineClass; + +typedef struct { + MachineState parent; + bool secure; +} VirtMachineState; + +#define TYPE_VIRT_MACHINE "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) + /* Addresses and sizes of our components. * 0..128MB is space for a flash device so we can run bootrom code such as UEFI. * 128MB..256MB is used for miscellaneous device I/O. @@ -107,15 +128,24 @@ static const MemMapEntry a15memmap[] = { [VIRT_GIC_CPU] = { 0x08010000, 0x00010000 }, [VIRT_UART] = { 0x09000000, 0x00001000 }, [VIRT_RTC] = { 0x09010000, 0x00001000 }, + [VIRT_FW_CFG] = { 0x09020000, 0x0000000a }, [VIRT_MMIO] = { 0x0a000000, 0x00000200 }, /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */ - /* 0x10000000 .. 0x40000000 reserved for PCI */ + /* + * PCIE verbose map: + * + * MMIO window { 0x10000000, 0x2eff0000 }, + * PIO window { 0x3eff0000, 0x00010000 }, + * ECAM { 0x3f000000, 0x01000000 }, + */ + [VIRT_PCIE] = { 0x10000000, 0x30000000 }, [VIRT_MEM] = { 0x40000000, 30ULL * 1024 * 1024 * 1024 }, }; static const int a15irqmap[] = { [VIRT_UART] = 1, [VIRT_RTC] = 2, + [VIRT_PCIE] = 3, /* ... to 6 */ [VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */ }; @@ -292,7 +322,7 @@ static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi) } } -static void fdt_add_gic_node(const VirtBoardInfo *vbi) +static uint32_t fdt_add_gic_node(const VirtBoardInfo *vbi) { uint32_t gic_phandle; @@ -311,9 +341,11 @@ static void fdt_add_gic_node(const VirtBoardInfo *vbi) 2, vbi->memmap[VIRT_GIC_CPU].base, 2, vbi->memmap[VIRT_GIC_CPU].size); qemu_fdt_setprop_cell(vbi->fdt, "/intc", "phandle", gic_phandle); + + return gic_phandle; } -static void create_gic(const VirtBoardInfo *vbi, qemu_irq *pic) +static uint32_t create_gic(const VirtBoardInfo *vbi, qemu_irq *pic) { /* We create a standalone GIC v2 */ DeviceState *gicdev; @@ -360,7 +392,7 @@ static void create_gic(const VirtBoardInfo *vbi, qemu_irq *pic) pic[i] = qdev_get_gpio_in(gicdev, i); } - fdt_add_gic_node(vbi); + return fdt_add_gic_node(vbi); } static void create_uart(const VirtBoardInfo *vbi, qemu_irq *pic) @@ -421,10 +453,32 @@ static void create_virtio_devices(const VirtBoardInfo *vbi, qemu_irq *pic) int i; hwaddr size = vbi->memmap[VIRT_MMIO].size; - /* Note that we have to create the transports in forwards order - * so that command line devices are inserted lowest address first, - * and then add dtb nodes in reverse order so that they appear in - * the finished device tree lowest address first. + /* 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; @@ -433,6 +487,13 @@ static void create_virtio_devices(const VirtBoardInfo *vbi, qemu_irq *pic) 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; @@ -461,9 +522,9 @@ static void create_one_flash(const char *name, hwaddr flashbase, DeviceState *dev = qdev_create(NULL, "cfi.pflash01"); const uint64_t sectorlength = 256 * 1024; - if (dinfo && qdev_prop_set_drive(dev, "drive", - blk_by_legacy_dinfo(dinfo))) { - abort(); + if (dinfo) { + qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(dinfo), + &error_abort); } qdev_prop_set_uint32(dev, "num-blocks", flashsize / sectorlength); @@ -491,7 +552,8 @@ static void create_flash(const VirtBoardInfo *vbi) char *nodename; if (bios_name) { - const char *fn; + char *fn; + int image_size; if (drive_get(IF_PFLASH, 0, 0)) { error_report("The contents of the first flash device may be " @@ -500,7 +562,13 @@ static void create_flash(const VirtBoardInfo *vbi) exit(1); } fn = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - if (!fn || load_image_targphys(fn, flashbase, flashsize) < 0) { + if (!fn) { + error_report("Could not find ROM image '%s'", bios_name); + exit(1); + } + image_size = load_image_targphys(fn, flashbase, flashsize); + g_free(fn); + if (image_size < 0) { error_report("Could not load ROM image '%s'", bios_name); exit(1); } @@ -519,6 +587,136 @@ static void create_flash(const VirtBoardInfo *vbi) g_free(nodename); } +static void create_fw_cfg(const VirtBoardInfo *vbi) +{ + 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); + + 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 * 8] = { 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, irq_type, irq_nr, irq_level }; /* GIC irq */ + + /* Convert map to big endian */ + for (i = 0; i < 8; i++) { + irq_map[i] = cpu_to_be32(map[i]); + } + irq_map += 8; + } + } + + 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, + uint32_t gic_phandle) +{ + hwaddr base = vbi->memmap[VIRT_PCIE].base; + hwaddr size = vbi->memmap[VIRT_PCIE].size; + hwaddr end = base + size; + hwaddr size_mmio; + hwaddr size_ioport = 64 * 1024; + int nr_pcie_buses = 16; + hwaddr size_ecam = PCIE_MMCFG_SIZE_MIN * nr_pcie_buses; + hwaddr base_mmio = base; + hwaddr base_ioport; + hwaddr base_ecam; + int irq = vbi->irqmap[VIRT_PCIE]; + MemoryRegion *mmio_alias; + MemoryRegion *mmio_reg; + MemoryRegion *ecam_alias; + MemoryRegion *ecam_reg; + DeviceState *dev; + char *nodename; + int i; + + base_ecam = QEMU_ALIGN_DOWN(end - size_ecam, size_ecam); + base_ioport = QEMU_ALIGN_DOWN(base_ecam - size_ioport, size_ioport); + size_mmio = base_ioport - base; + + 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); + + /* Map IO port space */ + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, base_ioport); + + for (i = 0; i < GPEX_NUM_IRQS; i++) { + sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, pic[irq + i]); + } + + 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); + + qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg", + 2, base_ecam, 2, size_ecam); + qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "ranges", + 1, FDT_PCI_RANGE_IOPORT, 2, 0, + 2, base_ioport, 2, size_ioport, + 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, gic_phandle, irq, nodename); + + g_free(nodename); +} + static void *machvirt_dtb(const struct arm_boot_info *binfo, int *fdt_size) { const VirtBoardInfo *board = (const VirtBoardInfo *)binfo; @@ -529,21 +727,27 @@ static void *machvirt_dtb(const struct arm_boot_info *binfo, int *fdt_size) static void machvirt_init(MachineState *machine) { + VirtMachineState *vms = VIRT_MACHINE(machine); qemu_irq pic[NUM_IRQS]; MemoryRegion *sysmem = get_system_memory(); int n; MemoryRegion *ram = g_new(MemoryRegion, 1); const char *cpu_model = machine->cpu_model; VirtBoardInfo *vbi; + uint32_t gic_phandle; + char **cpustr; if (!cpu_model) { cpu_model = "cortex-a15"; } - vbi = find_machine_info(cpu_model); + /* 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", cpu_model); + error_report("mach-virt: CPU %s not supported", cpustr[0]); exit(1); } @@ -557,8 +761,11 @@ static void machvirt_init(MachineState *machine) create_fdt(vbi); for (n = 0; n < smp_cpus; n++) { - ObjectClass *oc = cpu_class_by_name(TYPE_ARM_CPU, cpu_model); + 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) { fprintf(stderr, "Unable to find CPU definition\n"); @@ -566,6 +773,18 @@ static void machvirt_init(MachineState *machine) } 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); + } + object_property_set_int(cpuobj, QEMU_PSCI_CONDUIT_HVC, "psci-conduit", NULL); @@ -581,29 +800,33 @@ static void machvirt_init(MachineState *machine) object_property_set_bool(cpuobj, true, "realized", NULL); } + g_strfreev(cpustr); fdt_add_timer_nodes(vbi); fdt_add_cpu_nodes(vbi); fdt_add_psci_node(vbi); - memory_region_init_ram(ram, NULL, "mach-virt.ram", machine->ram_size, - &error_abort); - vmstate_register_ram_global(ram); + 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); - create_gic(vbi, pic); + gic_phandle = create_gic(vbi, pic); create_uart(vbi, pic); create_rtc(vbi, pic); + create_pcie(vbi, pic, gic_phandle); + /* 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); + vbi->bootinfo.ram_size = machine->ram_size; vbi->bootinfo.kernel_filename = machine->kernel_filename; vbi->bootinfo.kernel_cmdline = machine->kernel_cmdline; @@ -612,19 +835,60 @@ static void machvirt_init(MachineState *machine) vbi->bootinfo.board_id = -1; vbi->bootinfo.loader_start = vbi->memmap[VIRT_MEM].base; vbi->bootinfo.get_dtb = machvirt_dtb; + vbi->bootinfo.firmware_loaded = bios_name || drive_get(IF_PFLASH, 0, 0); arm_load_kernel(ARM_CPU(first_cpu), &vbi->bootinfo); } -static QEMUMachine machvirt_a15_machine = { - .name = "virt", - .desc = "ARM Virtual Machine", - .init = machvirt_init, - .max_cpus = 8, +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 void virt_instance_init(Object *obj) +{ + VirtMachineState *vms = VIRT_MACHINE(obj); + + /* EL3 is enabled by default on virt */ + vms->secure = true; + 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); +} + +static void virt_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->name = TYPE_VIRT_MACHINE; + mc->desc = "ARM Virtual Machine", + mc->init = machvirt_init; + mc->max_cpus = 8; +} + +static const TypeInfo machvirt_info = { + .name = TYPE_VIRT_MACHINE, + .parent = TYPE_MACHINE, + .instance_size = sizeof(VirtMachineState), + .instance_init = virt_instance_init, + .class_size = sizeof(VirtMachineClass), + .class_init = virt_class_init, }; static void machvirt_machine_init(void) { - qemu_register_machine(&machvirt_a15_machine); + type_register_static(&machvirt_info); } machine_init(machvirt_machine_init); diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index b590392..a4e7b5c 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -126,20 +126,32 @@ static void zynq_init(MachineState *machine) 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", &err); + if (err) { + error_report_err(err); + exit(1); + } + } + object_property_set_int(OBJECT(cpu), ZYNQ_BOARD_MIDR, "midr", &err); if (err) { - error_report("%s", error_get_pretty(err)); + error_report_err(err); exit(1); } object_property_set_int(OBJECT(cpu), MPCORE_PERIPHBASE, "reset-cbar", &err); if (err) { - error_report("%s", error_get_pretty(err)); + error_report_err(err); exit(1); } object_property_set_bool(OBJECT(cpu), true, "realized", &err); if (err) { - error_report("%s", error_get_pretty(err)); + error_report_err(err); exit(1); } @@ -149,9 +161,8 @@ static void zynq_init(MachineState *machine) } /* DDR remapped to address zero. */ - memory_region_init_ram(ext_ram, NULL, "zynq.ext_ram", ram_size, - &error_abort); - vmstate_register_ram_global(ext_ram); + 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 */ diff --git a/hw/audio/ac97.c b/hw/audio/ac97.c index 111ec0e..b173835 100644 --- a/hw/audio/ac97.c +++ b/hw/audio/ac97.c @@ -1337,7 +1337,7 @@ static void ac97_on_reset (DeviceState *dev) mixer_reset (s); } -static int ac97_initfn (PCIDevice *dev) +static void ac97_realize(PCIDevice *dev, Error **errp) { AC97LinkState *s = DO_UPCAST (AC97LinkState, dev, dev); uint8_t *c = s->dev.config; @@ -1384,7 +1384,6 @@ static int ac97_initfn (PCIDevice *dev) 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); - return 0; } static int ac97_init (PCIBus *bus) @@ -1403,7 +1402,7 @@ static void ac97_class_init (ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS (klass); PCIDeviceClass *k = PCI_DEVICE_CLASS (klass); - k->init = ac97_initfn; + k->realize = ac97_realize; k->vendor_id = PCI_VENDOR_ID_INTEL; k->device_id = PCI_DEVICE_ID_INTEL_82801AA_5; k->revision = 0x01; diff --git a/hw/audio/es1370.c b/hw/audio/es1370.c index e67d1ea..8e7bcf5 100644 --- a/hw/audio/es1370.c +++ b/hw/audio/es1370.c @@ -1016,7 +1016,7 @@ static void es1370_on_reset (void *opaque) es1370_reset (s); } -static int es1370_initfn (PCIDevice *dev) +static void es1370_realize(PCIDevice *dev, Error **errp) { ES1370State *s = DO_UPCAST (ES1370State, dev, dev); uint8_t *c = s->dev.config; @@ -1039,7 +1039,6 @@ static int es1370_initfn (PCIDevice *dev) AUD_register_card ("es1370", &s->card); es1370_reset (s); - return 0; } static int es1370_init (PCIBus *bus) @@ -1053,7 +1052,7 @@ static void es1370_class_init (ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS (klass); PCIDeviceClass *k = PCI_DEVICE_CLASS (klass); - k->init = es1370_initfn; + 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; diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c index 2885231..433463e 100644 --- a/hw/audio/intel-hda.c +++ b/hw/audio/intel-hda.c @@ -1126,7 +1126,7 @@ static void intel_hda_reset(DeviceState *dev) intel_hda_update_irq(d); } -static int intel_hda_init(PCIDevice *pci) +static void intel_hda_realize(PCIDevice *pci, Error **errp) { IntelHDAState *d = INTEL_HDA(pci); uint8_t *conf = d->pci.config; @@ -1147,8 +1147,6 @@ static int intel_hda_init(PCIDevice *pci) hda_codec_bus_init(DEVICE(pci), &d->codecs, sizeof(d->codecs), intel_hda_response, intel_hda_xfer); - - return 0; } static void intel_hda_exit(PCIDevice *pci) @@ -1245,7 +1243,7 @@ static void intel_hda_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = intel_hda_init; + 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; diff --git a/hw/audio/pcspk.c b/hw/audio/pcspk.c index 1d81bbe..5266fb5 100644 --- a/hw/audio/pcspk.c +++ b/hw/audio/pcspk.c @@ -167,7 +167,7 @@ static void pcspk_initfn(Object *obj) { PCSpkState *s = PC_SPEAKER(obj); - memory_region_init_io(&s->ioport, OBJECT(s), &pcspk_io_ops, s, "elcr", 1); + memory_region_init_io(&s->ioport, OBJECT(s), &pcspk_io_ops, s, "pcspk", 1); } static void pcspk_realizefn(DeviceState *dev, Error **errp) diff --git a/hw/audio/sb16.c b/hw/audio/sb16.c index bda26d0..444eb9e 100644 --- a/hw/audio/sb16.c +++ b/hw/audio/sb16.c @@ -999,7 +999,7 @@ static IO_READ_PROTO (dsp_read) retval = (!s->out_data_len || s->highspeed) ? 0 : 0x80; if (s->mixer_regs[0x82] & 1) { ack = 1; - s->mixer_regs[0x82] &= 1; + s->mixer_regs[0x82] &= ~1; qemu_irq_lower (s->pic); } break; @@ -1008,7 +1008,7 @@ static IO_READ_PROTO (dsp_read) retval = 0xff; if (s->mixer_regs[0x82] & 2) { ack = 1; - s->mixer_regs[0x82] &= 2; + s->mixer_regs[0x82] &= ~2; qemu_irq_lower (s->pic); } break; diff --git a/hw/block/block.c b/hw/block/block.c index a625773..f7243e5 100644 --- a/hw/block/block.c +++ b/hw/block/block.c @@ -25,6 +25,30 @@ void blkconf_serial(BlockConf *conf, char **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) diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c index 1222a37..3db139b 100644 --- a/hw/block/dataplane/virtio-blk.c +++ b/hw/block/dataplane/virtio-blk.c @@ -16,7 +16,9 @@ #include "qemu/iov.h" #include "qemu/thread.h" #include "qemu/error-report.h" +#include "hw/virtio/virtio-access.h" #include "hw/virtio/dataplane/vring.h" +#include "hw/virtio/dataplane/vring-accessors.h" #include "sysemu/block-backend.h" #include "hw/virtio/virtio-blk.h" #include "virtio-blk.h" @@ -75,8 +77,7 @@ static void complete_request_vring(VirtIOBlockReq *req, unsigned char status) VirtIOBlockDataPlane *s = req->dev->dataplane; stb_p(&req->in->status, status); - vring_push(&req->dev->dataplane->vring, &req->elem, - req->qiov.size + sizeof(*req->in)); + vring_push(s->vdev, &req->dev->dataplane->vring, &req->elem, req->in_len); /* Suppress notification to guest by BH and its scheduled * flag because requests are completed as a batch after io @@ -96,9 +97,7 @@ static void handle_notify(EventNotifier *e) event_notifier_test_and_clear(&s->host_notifier); blk_io_plug(s->conf->conf.blk); for (;;) { - MultiReqBuffer mrb = { - .num_writes = 0, - }; + MultiReqBuffer mrb = {}; int ret; /* Disable guest->host notifies to avoid unnecessary vmexits */ @@ -120,7 +119,9 @@ static void handle_notify(EventNotifier *e) virtio_blk_handle_request(req, &mrb); } - virtio_submit_multiwrite(s->conf->conf.blk, &mrb); + if (mrb.num_reqs) { + virtio_blk_submit_multireq(s->conf->conf.blk, &mrb); + } if (likely(ret == -EAGAIN)) { /* vring emptied */ /* Re-enable guest->host notifies and stop processing the vring. @@ -197,7 +198,14 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf, blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_RESIZE, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_DRIVE_DEL, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_BACKUP_SOURCE, s->blocker); - blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_COMMIT, s->blocker); + blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_CHANGE, s->blocker); + blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_COMMIT_SOURCE, s->blocker); + blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_COMMIT_TARGET, s->blocker); + blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_EJECT, s->blocker); + blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, s->blocker); + blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, s->blocker); + blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE, + s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_MIRROR, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_STREAM, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_REPLACE, s->blocker); diff --git a/hw/block/fdc.c b/hw/block/fdc.c index 739a03e..a9de4ab 100644 --- a/hw/block/fdc.c +++ b/hw/block/fdc.c @@ -791,7 +791,7 @@ static const VMStateDescription vmstate_fdc_result_timer = { .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { - VMSTATE_TIMER(result_timer, FDCtrl), + VMSTATE_TIMER_PTR(result_timer, FDCtrl), VMSTATE_END_OF_LIST() } }; @@ -1512,7 +1512,7 @@ static uint32_t fdctrl_read_data(FDCtrl *fdctrl) { FDrive *cur_drv; uint32_t retval = 0; - int pos; + uint32_t pos; cur_drv = get_cur_drv(fdctrl); fdctrl->dsr &= ~FD_DSR_PWRDOWN; @@ -1521,8 +1521,8 @@ static uint32_t fdctrl_read_data(FDCtrl *fdctrl) return 0; } pos = fdctrl->data_pos; + pos %= FD_SECTOR_LEN; if (fdctrl->msr & FD_MSR_NONDMA) { - pos %= FD_SECTOR_LEN; if (pos == 0) { if (fdctrl->data_pos != 0) if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv)) { @@ -1867,10 +1867,13 @@ static void fdctrl_handle_option(FDCtrl *fdctrl, int direction) static void fdctrl_handle_drive_specification_command(FDCtrl *fdctrl, int direction) { FDrive *cur_drv = get_cur_drv(fdctrl); + uint32_t pos; - if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x80) { + pos = fdctrl->data_pos - 1; + pos %= FD_SECTOR_LEN; + if (fdctrl->fifo[pos] & 0x80) { /* Command parameters done */ - if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x40) { + if (fdctrl->fifo[pos] & 0x40) { fdctrl->fifo[0] = fdctrl->fifo[1]; fdctrl->fifo[2] = 0; fdctrl->fifo[3] = 0; @@ -1970,7 +1973,7 @@ static uint8_t command_to_handler[256]; static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value) { FDrive *cur_drv; - int pos; + uint32_t pos; /* Reset mode */ if (!(fdctrl->dor & FD_DOR_nRESET)) { @@ -2019,7 +2022,9 @@ static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value) } FLOPPY_DPRINTF("%s: %02x\n", __func__, value); - fdctrl->fifo[fdctrl->data_pos++] = value; + pos = fdctrl->data_pos++; + pos %= FD_SECTOR_LEN; + fdctrl->fifo[pos] = value; if (fdctrl->data_pos == fdctrl->data_len) { /* We now have all parameters * and will be able to treat the command diff --git a/hw/block/hd-geometry.c b/hw/block/hd-geometry.c index 6fcf74d..b187878 100644 --- a/hw/block/hd-geometry.c +++ b/hw/block/hd-geometry.c @@ -121,8 +121,16 @@ void hd_geometry_guess(BlockBackend *blk, int *ptrans) { int cylinders, heads, secs, translation; + HDGeometry geo; - if (guess_disk_lchs(blk, &cylinders, &heads, &secs) < 0) { + /* 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); diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index ff1106b..afe243b 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -623,6 +623,7 @@ static int m25p80_init(SSISlave *ss) s->dirty_page = -1; s->storage = blk_blockalign(s->blk, s->size); + /* FIXME use a qdev drive property instead of drive_get_next() */ dinfo = drive_get_next(IF_MTD); if (dinfo) { diff --git a/hw/block/nand.c b/hw/block/nand.c index 1882a0c..61d2cec 100644 --- a/hw/block/nand.c +++ b/hw/block/nand.c @@ -393,7 +393,7 @@ static void nand_realize(DeviceState *dev, Error **errp) nand_init_2048(s); break; default: - error_setg(errp, "Unsupported NAND block size %#x\n", + error_setg(errp, "Unsupported NAND block size %#x", 1 << s->page_shift); return; } diff --git a/hw/block/nvme.c b/hw/block/nvme.c index 1327658..1e07166 100644 --- a/hw/block/nvme.c +++ b/hw/block/nvme.c @@ -222,7 +222,7 @@ static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd, 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 = nlb << data_shift; + 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; @@ -476,7 +476,8 @@ static uint16_t nvme_get_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) switch (dw10) { case NVME_NUMBER_OF_QUEUES: - req->cqe.result = cpu_to_le32(n->num_queues); + req->cqe.result = + cpu_to_le32((n->num_queues - 1) | ((n->num_queues - 1) << 16)); break; default: return NVME_INVALID_FIELD | NVME_DNR; @@ -490,7 +491,8 @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) switch (dw10) { case NVME_NUMBER_OF_QUEUES: - req->cqe.result = cpu_to_le32(n->num_queues); + req->cqe.result = + cpu_to_le32((n->num_queues - 1) | ((n->num_queues - 1) << 16)); break; default: return NVME_INVALID_FIELD | NVME_DNR; @@ -763,6 +765,7 @@ static int nvme_init(PCIDevice *pci_dev) if (!n->serial) { return -1; } + blkconf_blocksizes(&n->conf); pci_conf = pci_dev->config; pci_conf[PCI_INTERRUPT_PIN] = 1; @@ -811,8 +814,9 @@ static int nvme_init(PCIDevice *pci_dev) 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 = 0x00010001; + n->bar.vs = 0x00010100; n->bar.intmc = n->bar.intms = 0; for (i = 0; i < n->num_namespaces; i++) { diff --git a/hw/block/nvme.h b/hw/block/nvme.h index 993c511..b6ccb65 100644 --- a/hw/block/nvme.h +++ b/hw/block/nvme.h @@ -688,7 +688,7 @@ typedef struct NvmeCtrl { NvmeBar bar; BlockConf conf; - uint16_t page_size; + uint32_t page_size; uint16_t page_bits; uint16_t max_prp_ents; uint16_t cqe_size; diff --git a/hw/block/onenand.c b/hw/block/onenand.c index 348630d..1b2c893 100644 --- a/hw/block/onenand.c +++ b/hw/block/onenand.c @@ -346,15 +346,9 @@ static inline int onenand_prog_spare(OneNANDState *s, int sec, int secn, static inline int onenand_erase(OneNANDState *s, int sec, int num) { uint8_t *blankbuf, *tmpbuf; + blankbuf = g_malloc(512); - if (!blankbuf) { - return 1; - } tmpbuf = g_malloc(512); - if (!tmpbuf) { - g_free(blankbuf); - return 1; - } memset(blankbuf, 0xff, 512); for (; num > 0; num--, sec++) { if (s->blk_cur) { diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index 89d380e..d282695 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -969,8 +969,8 @@ pflash_t *pflash_cfi01_register(hwaddr base, { DeviceState *dev = qdev_create(NULL, TYPE_CFI_PFLASH01); - if (blk && qdev_prop_set_drive(dev, "drive", blk)) { - abort(); + 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); diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c index 8513a17..074a005 100644 --- a/hw/block/pflash_cfi02.c +++ b/hw/block/pflash_cfi02.c @@ -744,6 +744,7 @@ static void pflash_cfi02_class_init(ObjectClass *klass, void *data) dc->realize = pflash_cfi02_realize; dc->props = pflash_cfi02_properties; + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } static const TypeInfo pflash_cfi02_info = { @@ -772,8 +773,8 @@ pflash_t *pflash_cfi02_register(hwaddr base, { DeviceState *dev = qdev_create(NULL, TYPE_CFI_PFLASH02); - if (blk && qdev_prop_set_drive(dev, "drive", blk)) { - abort(); + 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); diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index b19b102..9546fd2 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -33,7 +33,9 @@ VirtIOBlockReq *virtio_blk_alloc_request(VirtIOBlock *s) VirtIOBlockReq *req = g_slice_new(VirtIOBlockReq); req->dev = s; req->qiov.size = 0; + req->in_len = 0; req->next = NULL; + req->mr_next = NULL; return req; } @@ -53,7 +55,7 @@ static void virtio_blk_complete_request(VirtIOBlockReq *req, trace_virtio_blk_req_complete(req, status); stb_p(&req->in->status, status); - virtqueue_push(s->vq, &req->elem, req->qiov.size + sizeof(*req->in)); + virtqueue_push(s->vq, &req->elem, req->in_len); virtio_notify(vdev, s->vq); } @@ -84,20 +86,40 @@ static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error, static void virtio_blk_rw_complete(void *opaque, int ret) { - VirtIOBlockReq *req = opaque; + VirtIOBlockReq *next = opaque; - trace_virtio_blk_rw_complete(req, ret); + while (next) { + VirtIOBlockReq *req = next; + next = req->mr_next; + trace_virtio_blk_rw_complete(req, ret); - if (ret) { - int p = virtio_ldl_p(VIRTIO_DEVICE(req->dev), &req->out.type); - bool is_read = !(p & VIRTIO_BLK_T_OUT); - if (virtio_blk_handle_rw_error(req, -ret, is_read)) - return; - } + 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); + } - 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); + 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) @@ -115,6 +137,56 @@ static void virtio_blk_flush_complete(void *opaque, int ret) 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 = virtio_blk_alloc_request(s); @@ -127,16 +199,18 @@ static VirtIOBlockReq *virtio_blk_get_request(VirtIOBlock *s) return req; } -int virtio_blk_handle_scsi_req(VirtIOBlock *blk, - VirtQueueElement *elem) +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(blk); + VirtIODevice *vdev = VIRTIO_DEVICE(req->dev); + VirtQueueElement *elem = &req->elem; + VirtIOBlock *blk = req->dev; #ifdef __linux__ int i; - struct sg_io_hdr hdr; + VirtIOBlockIoctlReq *ioctl_req; + BlockAIOCB *acb; #endif /* @@ -171,71 +245,57 @@ int virtio_blk_handle_scsi_req(VirtIOBlock *blk, } #ifdef __linux__ - memset(&hdr, 0, sizeof(struct sg_io_hdr)); - hdr.interface_id = 'S'; - hdr.cmd_len = elem->out_sg[1].iov_len; - hdr.cmdp = elem->out_sg[1].iov_base; - hdr.dxfer_len = 0; + 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. */ - hdr.dxfer_direction = SG_DXFER_TO_DEV; - hdr.iovec_count = elem->out_num - 2; + ioctl_req->hdr.dxfer_direction = SG_DXFER_TO_DEV; + ioctl_req->hdr.iovec_count = elem->out_num - 2; - for (i = 0; i < hdr.iovec_count; i++) - hdr.dxfer_len += elem->out_sg[i + 2].iov_len; + for (i = 0; i < ioctl_req->hdr.iovec_count; i++) { + ioctl_req->hdr.dxfer_len += elem->out_sg[i + 2].iov_len; + } - hdr.dxferp = elem->out_sg + 2; + 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. */ - hdr.dxfer_direction = SG_DXFER_FROM_DEV; - hdr.iovec_count = elem->in_num - 3; - for (i = 0; i < hdr.iovec_count; i++) - hdr.dxfer_len += elem->in_sg[i].iov_len; + 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; + } - hdr.dxferp = elem->in_sg; + ioctl_req->hdr.dxferp = elem->in_sg; } else { /* * Some SCSI commands don't actually transfer any data. */ - hdr.dxfer_direction = SG_DXFER_NONE; + ioctl_req->hdr.dxfer_direction = SG_DXFER_NONE; } - hdr.sbp = elem->in_sg[elem->in_num - 3].iov_base; - hdr.mx_sb_len = elem->in_sg[elem->in_num - 3].iov_len; + 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; - status = blk_ioctl(blk->blk, SG_IO, &hdr); - if (status) { + 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; } - - /* - * 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); - - return status; + return -EINPROGRESS; #else abort(); #endif @@ -252,29 +312,134 @@ static void virtio_blk_handle_scsi(VirtIOBlockReq *req) { int status; - status = virtio_blk_handle_scsi_req(req->dev, &req->elem); - virtio_blk_req_complete(req, status); - virtio_blk_free_request(req); + status = virtio_blk_handle_scsi_req(req); + if (status != -EINPROGRESS) { + virtio_blk_req_complete(req, status); + virtio_blk_free_request(req); + } } -void virtio_submit_multiwrite(BlockBackend *blk, MultiReqBuffer *mrb) +static inline void submit_requests(BlockBackend *blk, MultiReqBuffer *mrb, + int start, int num_reqs, int niov) { - int i, ret; + 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 (!mrb->num_writes) { + 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; } - ret = blk_aio_multiwrite(blk, mrb->blkreq, mrb->num_writes); - if (ret != 0) { - for (i = 0; i < mrb->num_writes; i++) { - if (mrb->blkreq[i].error) { - virtio_blk_rw_complete(mrb->blkreq[i].opaque, -EIO); + 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) { + bool merge = true; + + /* merge would exceed maximum number of IOVs */ + if (niov + req->qiov.niov > IOV_MAX) { + merge = false; + } + + /* merge would exceed maximum transfer length of backend device */ + if (req->qiov.size / BDRV_SECTOR_SIZE + nb_sectors > max_xfer_len) { + merge = false; + } + + /* requests are not sequential */ + if (sector_num + nb_sectors != req->sector_num) { + merge = false; + } + + if (!merge) { + 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++; } - mrb->num_writes = 0; + submit_requests(blk, mrb, start, num_reqs, niov); + mrb->num_reqs = 0; } static void virtio_blk_handle_flush(VirtIOBlockReq *req, MultiReqBuffer *mrb) @@ -285,7 +450,9 @@ static void virtio_blk_handle_flush(VirtIOBlockReq *req, MultiReqBuffer *mrb) /* * Make sure all outstanding writes are posted to the backing device. */ - virtio_submit_multiwrite(req->dev->blk, mrb); + 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); } @@ -295,6 +462,9 @@ static bool virtio_blk_sect_range_ok(VirtIOBlock *dev, 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; } @@ -308,60 +478,6 @@ static bool virtio_blk_sect_range_ok(VirtIOBlock *dev, return true; } -static void virtio_blk_handle_write(VirtIOBlockReq *req, MultiReqBuffer *mrb) -{ - BlockRequest *blkreq; - uint64_t sector; - - sector = virtio_ldq_p(VIRTIO_DEVICE(req->dev), &req->out.sector); - - trace_virtio_blk_handle_write(req, sector, req->qiov.size / 512); - - if (!virtio_blk_sect_range_ok(req->dev, sector, req->qiov.size)) { - virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR); - virtio_blk_free_request(req); - return; - } - - block_acct_start(blk_get_stats(req->dev->blk), &req->acct, req->qiov.size, - BLOCK_ACCT_WRITE); - - if (mrb->num_writes == 32) { - virtio_submit_multiwrite(req->dev->blk, mrb); - } - - blkreq = &mrb->blkreq[mrb->num_writes]; - blkreq->sector = sector; - blkreq->nb_sectors = req->qiov.size / BDRV_SECTOR_SIZE; - blkreq->qiov = &req->qiov; - blkreq->cb = virtio_blk_rw_complete; - blkreq->opaque = req; - blkreq->error = 0; - - mrb->num_writes++; -} - -static void virtio_blk_handle_read(VirtIOBlockReq *req) -{ - uint64_t sector; - - sector = virtio_ldq_p(VIRTIO_DEVICE(req->dev), &req->out.sector); - - trace_virtio_blk_handle_read(req, sector, req->qiov.size / 512); - - if (!virtio_blk_sect_range_ok(req->dev, sector, req->qiov.size)) { - virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR); - virtio_blk_free_request(req); - return; - } - - block_acct_start(blk_get_stats(req->dev->blk), &req->acct, req->qiov.size, - BLOCK_ACCT_READ); - blk_aio_readv(req->dev->blk, sector, &req->qiov, - req->qiov.size / BDRV_SECTOR_SIZE, - virtio_blk_rw_complete, req); -} - void virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) { uint32_t type; @@ -389,6 +505,8 @@ void virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) 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); @@ -396,11 +514,58 @@ void virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) type = virtio_ldl_p(VIRTIO_DEVICE(req->dev), &req->out.type); - if (type & VIRTIO_BLK_T_FLUSH) { + /* VIRTIO_BLK_T_OUT defines the command direction. VIRTIO_BLK_T_BARRIER + * is an optional flag. Altough 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); + 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); - } else if (type & VIRTIO_BLK_T_SCSI_CMD) { + break; + case VIRTIO_BLK_T_SCSI_CMD: virtio_blk_handle_scsi(req); - } else if (type & VIRTIO_BLK_T_GET_ID) { + break; + case VIRTIO_BLK_T_GET_ID: + { VirtIOBlock *s = req->dev; /* @@ -414,14 +579,9 @@ void virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) iov_from_buf(in_iov, in_num, 0, serial, size); virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); virtio_blk_free_request(req); - } else if (type & VIRTIO_BLK_T_OUT) { - qemu_iovec_init_external(&req->qiov, iov, out_num); - virtio_blk_handle_write(req, mrb); - } else if (type == VIRTIO_BLK_T_IN || type == VIRTIO_BLK_T_BARRIER) { - /* VIRTIO_BLK_T_IN is 0, so we can't just & it. */ - qemu_iovec_init_external(&req->qiov, in_iov, in_num); - virtio_blk_handle_read(req); - } else { + break; + } + default: virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP); virtio_blk_free_request(req); } @@ -431,9 +591,7 @@ static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq) { VirtIOBlock *s = VIRTIO_BLK(vdev); VirtIOBlockReq *req; - MultiReqBuffer mrb = { - .num_writes = 0, - }; + MultiReqBuffer mrb = {}; /* Some guests kick before setting VIRTIO_CONFIG_S_DRIVER_OK so start * dataplane here instead of waiting for .set_status(). @@ -447,22 +605,16 @@ static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq) virtio_blk_handle_request(req, &mrb); } - virtio_submit_multiwrite(s->blk, &mrb); - - /* - * FIXME: Want to check for completions before returning to guest mode, - * so cached reads and writes are reported as quickly as possible. But - * that should be done in the generic block layer. - */ + if (mrb.num_reqs) { + virtio_blk_submit_multireq(s->blk, &mrb); + } } static void virtio_blk_dma_restart_bh(void *opaque) { VirtIOBlock *s = opaque; VirtIOBlockReq *req = s->rq; - MultiReqBuffer mrb = { - .num_writes = 0, - }; + MultiReqBuffer mrb = {}; qemu_bh_delete(s->bh); s->bh = NULL; @@ -475,7 +627,9 @@ static void virtio_blk_dma_restart_bh(void *opaque) req = next; } - virtio_submit_multiwrite(s->blk, &mrb); + if (mrb.num_reqs) { + virtio_blk_submit_multireq(s->blk, &mrb); + } } static void virtio_blk_dma_restart_cb(void *opaque, int running, @@ -524,11 +678,11 @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config) 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.cylinders, conf->cyls); + 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.heads = conf->heads; + 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 @@ -541,9 +695,9 @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config) * per track (cylinder). */ if (blk_getlength(s->blk) / conf->heads / conf->secs % blk_size) { - blkcfg.sectors = conf->secs & ~s->sector_mask; + blkcfg.geometry.sectors = conf->secs & ~s->sector_mask; } else { - blkcfg.sectors = conf->secs; + blkcfg.geometry.sectors = conf->secs; } blkcfg.size_max = 0; blkcfg.physical_block_exp = get_physical_block_exp(conf); @@ -568,20 +722,20 @@ static uint32_t virtio_blk_get_features(VirtIODevice *vdev, uint32_t features) { VirtIOBlock *s = VIRTIO_BLK(vdev); - features |= (1 << VIRTIO_BLK_F_SEG_MAX); - features |= (1 << VIRTIO_BLK_F_GEOMETRY); - features |= (1 << VIRTIO_BLK_F_TOPOLOGY); - features |= (1 << VIRTIO_BLK_F_BLK_SIZE); - features |= (1 << VIRTIO_BLK_F_SCSI); + 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); + virtio_add_feature(&features, VIRTIO_BLK_F_SCSI); if (s->conf.config_wce) { - features |= (1 << VIRTIO_BLK_F_CONFIG_WCE); + virtio_add_feature(&features, VIRTIO_BLK_F_CONFIG_WCE); } if (blk_enable_write_cache(s->blk)) { - features |= (1 << VIRTIO_BLK_F_WCE); + virtio_add_feature(&features, VIRTIO_BLK_F_WCE); } if (blk_is_read_only(s->blk)) { - features |= 1 << VIRTIO_BLK_F_RO; + virtio_add_feature(&features, VIRTIO_BLK_F_RO); } return features; @@ -590,7 +744,6 @@ static uint32_t virtio_blk_get_features(VirtIODevice *vdev, uint32_t features) static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status) { VirtIOBlock *s = VIRTIO_BLK(vdev); - uint32_t features; if (s->dataplane && !(status & (VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK))) { @@ -601,8 +754,6 @@ static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status) return; } - features = vdev->guest_features; - /* 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. @@ -618,10 +769,10 @@ static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status) * * s->blk would erroneously be placed in writethrough mode. */ - if (!(features & (1 << VIRTIO_BLK_F_CONFIG_WCE))) { + if (!virtio_has_feature(vdev, VIRTIO_BLK_F_CONFIG_WCE)) { aio_context_acquire(blk_get_aio_context(s->blk)); blk_set_enable_write_cache(s->blk, - !!(features & (1 << VIRTIO_BLK_F_WCE))); + virtio_has_feature(vdev, VIRTIO_BLK_F_WCE)); aio_context_release(blk_get_aio_context(s->blk)); } } @@ -715,8 +866,7 @@ static void virtio_blk_migration_state_changed(Notifier *notifier, void *data) virtio_blk_data_plane_create(VIRTIO_DEVICE(s), &s->conf, &s->dataplane, &err); if (err != NULL) { - error_report("%s", error_get_pretty(err)); - error_free(err); + error_report_err(err); } } } @@ -745,6 +895,7 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp) error_propagate(errp, err); return; } + blkconf_blocksizes(&conf->conf); virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK, sizeof(struct virtio_blk_config)); @@ -808,6 +959,8 @@ static Property virtio_blk_properties[] = { #ifdef __linux__ DEFINE_PROP_BIT("scsi", VirtIOBlock, conf.scsi, 0, true), #endif + DEFINE_PROP_BIT("request-merging", VirtIOBlock, conf.request_merging, 0, + true), DEFINE_PROP_BIT("x-data-plane", VirtIOBlock, conf.data_plane, 0, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c index 21842a0..267d8a8 100644 --- a/hw/block/xen_disk.c +++ b/hw/block/xen_disk.c @@ -40,6 +40,8 @@ #include "xen_blkif.h" #include "sysemu/blockdev.h" #include "sysemu/block-backend.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qstring.h" /* ------------------------------------------------------------- */ @@ -897,30 +899,23 @@ static int blk_connect(struct XenDevice *xendev) blkdev->dinfo = drive_get(IF_XEN, 0, index); if (!blkdev->dinfo) { Error *local_err = NULL; - BlockBackend *blk; - BlockDriver *drv; - BlockDriverState *bs; + QDict *options = NULL; - /* setup via xenbus -> create new block driver instance */ - xen_be_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n"); - blk = blk_new_with_bs(blkdev->dev, NULL); - if (!blk) { - return -1; + if (strcmp(blkdev->fileproto, "")) { + options = qdict_new(); + qdict_put(options, "driver", qstring_from_str(blkdev->fileproto)); } - blkdev->blk = blk; - bs = blk_bs(blk); - drv = bdrv_find_whitelisted_format(blkdev->fileproto, readonly); - if (bdrv_open(&bs, blkdev->filename, NULL, NULL, qflags, - drv, &local_err) != 0) { + /* setup via xenbus -> create new block driver instance */ + xen_be_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n"); + blkdev->blk = blk_new_open(blkdev->dev, blkdev->filename, NULL, options, + qflags, &local_err); + if (!blkdev->blk) { xen_be_printf(&blkdev->xendev, 0, "error: %s\n", error_get_pretty(local_err)); error_free(local_err); - blk_unref(blk); - blkdev->blk = NULL; return -1; } - assert(bs == blk_bs(blk)); } else { /* setup via qemu cmdline -> already setup for us */ xen_be_printf(&blkdev->xendev, 2, "get configured bdrv (cmdline setup)\n"); diff --git a/hw/bt/sdp.c b/hw/bt/sdp.c index 218e075..c903747 100644 --- a/hw/bt/sdp.c +++ b/hw/bt/sdp.c @@ -707,7 +707,7 @@ static void sdp_service_record_build(struct sdp_service_record_s *record, len += sdp_attr_max_size(&def->attributes[record->attributes ++].data, &record->uuids); } - record->uuids = 1 << ffs(record->uuids - 1); + record->uuids = pow2ceil(record->uuids); record->attribute_list = g_malloc0(record->attributes * sizeof(*record->attribute_list)); record->uuid = diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs index 317385d..5931cc8 100644 --- a/hw/char/Makefile.objs +++ b/hw/char/Makefile.objs @@ -15,6 +15,7 @@ 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 common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o common-obj-$(CONFIG_ISA_DEBUG) += debugcon.o diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c index a5736cb..d145378 100644 --- a/hw/char/cadence_uart.c +++ b/hw/char/cadence_uart.c @@ -476,27 +476,32 @@ static void cadence_uart_reset(DeviceState *dev) uart_update_status(s); } -static int cadence_uart_init(SysBusDevice *dev) +static void cadence_uart_realize(DeviceState *dev, Error **errp) { UartState *s = CADENCE_UART(dev); - memory_region_init_io(&s->iomem, OBJECT(s), &uart_ops, s, "uart", 0x1000); - sysbus_init_mmio(dev, &s->iomem); - sysbus_init_irq(dev, &s->irq); - s->fifo_trigger_handle = timer_new_ns(QEMU_CLOCK_VIRTUAL, - (QEMUTimerCB *)fifo_trigger_update, s); - - s->char_tx_time = (get_ticks_per_sec() / 9600) * 10; + 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); } +} - return 0; +static void cadence_uart_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + UartState *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 = (get_ticks_per_sec() / 9600) * 10; } static int cadence_uart_post_load(void *opaque, int version_id) @@ -520,7 +525,7 @@ static const VMStateDescription vmstate_cadence_uart = { VMSTATE_UINT32(rx_count, UartState), VMSTATE_UINT32(tx_count, UartState), VMSTATE_UINT32(rx_wpos, UartState), - VMSTATE_TIMER(fifo_trigger_handle, UartState), + VMSTATE_TIMER_PTR(fifo_trigger_handle, UartState), VMSTATE_END_OF_LIST() } }; @@ -528,17 +533,19 @@ static const VMStateDescription vmstate_cadence_uart = { static void cadence_uart_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - sdc->init = cadence_uart_init; + 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(UartState), + .instance_init = cadence_uart_init, .class_init = cadence_uart_class_init, }; diff --git a/hw/char/digic-uart.c b/hw/char/digic-uart.c index 8abe944..6d44576 100644 --- a/hw/char/digic-uart.c +++ b/hw/char/digic-uart.c @@ -143,6 +143,7 @@ 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); @@ -176,6 +177,8 @@ static void digic_uart_class_init(ObjectClass *klass, void *data) 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 = { diff --git a/hw/char/etraxfs_ser.c b/hw/char/etraxfs_ser.c index 460094e..857c136 100644 --- a/hw/char/etraxfs_ser.c +++ b/hw/char/etraxfs_ser.c @@ -219,6 +219,7 @@ static int etraxfs_ser_init(SysBusDevice *dev) "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, @@ -235,6 +236,8 @@ static void etraxfs_ser_class_init(ObjectClass *klass, void *data) 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 = { diff --git a/hw/char/lm32_juart.c b/hw/char/lm32_juart.c index 628a86f..62763f2 100644 --- a/hw/char/lm32_juart.c +++ b/hw/char/lm32_juart.c @@ -117,6 +117,7 @@ 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); @@ -144,6 +145,8 @@ static void lm32_juart_class_init(ObjectClass *klass, void *data) 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 = { diff --git a/hw/char/lm32_uart.c b/hw/char/lm32_uart.c index 4f20966..837a46e 100644 --- a/hw/char/lm32_uart.c +++ b/hw/char/lm32_uart.c @@ -258,6 +258,7 @@ static int lm32_uart_init(SysBusDevice *dev) "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); @@ -284,6 +285,8 @@ static void lm32_uart_class_init(ObjectClass *klass, void *data) 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 = { diff --git a/hw/char/milkymist-uart.c b/hw/char/milkymist-uart.c index d05b825..9b89b7e 100644 --- a/hw/char/milkymist-uart.c +++ b/hw/char/milkymist-uart.c @@ -199,6 +199,7 @@ 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); @@ -234,6 +235,8 @@ static void milkymist_uart_class_init(ObjectClass *klass, void *data) 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 = { diff --git a/hw/char/omap_uart.c b/hw/char/omap_uart.c index 0b91693..88f2094 100644 --- a/hw/char/omap_uart.c +++ b/hw/char/omap_uart.c @@ -112,7 +112,8 @@ static void omap_uart_write(void *opaque, hwaddr addr, struct omap_uart_s *s = (struct omap_uart_s *) opaque; if (size == 4) { - return omap_badwidth_write8(opaque, addr, value); + omap_badwidth_write8(opaque, addr, value); + return; } switch (addr) { diff --git a/hw/char/parallel.c b/hw/char/parallel.c index c2b553f..4079554 100644 --- a/hw/char/parallel.c +++ b/hw/char/parallel.c @@ -641,3 +641,28 @@ static void parallel_register_types(void) } type_init(parallel_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/hw/char/pl011.c b/hw/char/pl011.c index 0a45115..eac6fac 100644 --- a/hw/char/pl011.c +++ b/hw/char/pl011.c @@ -293,6 +293,7 @@ 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) { @@ -307,6 +308,8 @@ static void pl011_class_init(ObjectClass *oc, void *data) 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 = { diff --git a/hw/char/serial-isa.c b/hw/char/serial-isa.c index c9fcb27..f3db024 100644 --- a/hw/char/serial-isa.c +++ b/hw/char/serial-isa.c @@ -119,20 +119,27 @@ static void serial_register_types(void) type_init(serial_register_types) -bool serial_isa_init(ISABus *bus, int index, CharDriverState *chr) +static void serial_isa_init(ISABus *bus, int index, CharDriverState *chr) { DeviceState *dev; ISADevice *isadev; - isadev = isa_try_create(bus, TYPE_ISA_SERIAL); - if (!isadev) { - return false; - } + isadev = isa_create(bus, TYPE_ISA_SERIAL); dev = DEVICE(isadev); qdev_prop_set_uint32(dev, "index", index); qdev_prop_set_chr(dev, "chardev", chr); - if (qdev_init(dev) < 0) { - return false; + 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]); + } } - return true; } diff --git a/hw/char/serial-pci.c b/hw/char/serial-pci.c index f05c9b4..467c3b4 100644 --- a/hw/char/serial-pci.c +++ b/hw/char/serial-pci.c @@ -48,7 +48,7 @@ typedef struct PCIMultiSerialState { uint8_t prog_if; } PCIMultiSerialState; -static int serial_pci_init(PCIDevice *dev) +static void serial_pci_realize(PCIDevice *dev, Error **errp) { PCISerialState *pci = DO_UPCAST(PCISerialState, dev, dev); SerialState *s = &pci->state; @@ -57,9 +57,8 @@ static int serial_pci_init(PCIDevice *dev) s->baudbase = 115200; serial_realize_core(s, &err); if (err != NULL) { - qerror_report_err(err); - error_free(err); - return -1; + error_propagate(errp, err); + return; } pci->dev.config[PCI_CLASS_PROG] = pci->prog_if; @@ -68,7 +67,6 @@ static int serial_pci_init(PCIDevice *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); - return 0; } static void multi_serial_irq_mux(void *opaque, int n, int level) @@ -85,7 +83,7 @@ static void multi_serial_irq_mux(void *opaque, int n, int level) pci_set_irq(&pci->dev, pending); } -static int multi_serial_pci_init(PCIDevice *dev) +static void multi_serial_pci_realize(PCIDevice *dev, Error **errp) { PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); PCIMultiSerialState *pci = DO_UPCAST(PCIMultiSerialState, dev, dev); @@ -116,9 +114,8 @@ static int multi_serial_pci_init(PCIDevice *dev) s->baudbase = 115200; serial_realize_core(s, &err); if (err != NULL) { - qerror_report_err(err); - error_free(err); - return -1; + error_propagate(errp, err); + return; } s->irq = pci->irqs[i]; pci->name[i] = g_strdup_printf("uart #%d", i+1); @@ -126,7 +123,6 @@ static int multi_serial_pci_init(PCIDevice *dev) pci->name[i], 8); memory_region_add_subregion(&pci->iobar, 8 * i, &s->io); } - return 0; } static void serial_pci_exit(PCIDevice *dev) @@ -203,7 +199,7 @@ static void serial_pci_class_initfn(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass); - pc->init = serial_pci_init; + 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; @@ -218,7 +214,7 @@ static void multi_2x_serial_pci_class_initfn(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass); - pc->init = multi_serial_pci_init; + 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; @@ -233,7 +229,7 @@ static void multi_4x_serial_pci_class_initfn(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass); - pc->init = multi_serial_pci_init; + 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; diff --git a/hw/char/serial.c b/hw/char/serial.c index ebcacdc..55011cf 100644 --- a/hw/char/serial.c +++ b/hw/char/serial.c @@ -224,21 +224,23 @@ 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) { - if (fifo8_is_empty(&s->xmit_fifo)) { - return FALSE; - } + 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 if ((s->lsr & UART_LSR_THRE)) { - return FALSE; } else { s->tsr = s->thr; s->lsr |= UART_LSR_THRE; - s->lsr &= ~UART_LSR_TEMT; + } + if ((s->lsr & UART_LSR_THRE) && !s->thr_ipending) { + s->thr_ipending = 1; + serial_update_irq(s); } } @@ -256,17 +258,13 @@ static gboolean serial_xmit(GIOChannel *chan, GIOCondition cond, void *opaque) } 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->fcr & UART_FCR_FE) && !fifo8_is_empty(&s->xmit_fifo)); + } while (!(s->lsr & UART_LSR_THRE)); s->last_xmit_ts = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - - if (s->lsr & UART_LSR_THRE) { - s->lsr |= UART_LSR_TEMT; - s->thr_ipending = 1; - serial_update_irq(s); - } + s->lsr |= UART_LSR_TEMT; return FALSE; } @@ -323,10 +321,10 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val, fifo8_pop(&s->xmit_fifo); } fifo8_push(&s->xmit_fifo, s->thr); - s->lsr &= ~UART_LSR_TEMT; } 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); @@ -338,10 +336,12 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val, 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 (s->poll_msl >= 0) { + * 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); @@ -350,8 +350,27 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val, s->poll_msl = 0; } } - if (s->lsr & UART_LSR_THRE) { - s->thr_ipending = 1; + + /* 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); } } @@ -365,12 +384,15 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val, /* 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); } @@ -623,11 +645,20 @@ static int serial_post_load(void *opaque, int version_id) static bool serial_thr_ipending_needed(void *opaque) { SerialState *s = opaque; - bool expected_value = ((s->iir & UART_IIR_ID) == UART_IIR_THRI); - return s->thr_ipending != expected_value; + + 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; + } } -const VMStateDescription vmstate_serial_thr_ipending = { +static const VMStateDescription vmstate_serial_thr_ipending = { .name = "serial/thr_ipending", .version_id = 1, .minimum_version_id = 1, @@ -643,7 +674,7 @@ static bool serial_tsr_needed(void *opaque) return s->tsr_retry != 0; } -const VMStateDescription vmstate_serial_tsr = { +static const VMStateDescription vmstate_serial_tsr = { .name = "serial/tsr", .version_id = 1, .minimum_version_id = 1, @@ -662,7 +693,7 @@ static bool serial_recv_fifo_needed(void *opaque) } -const VMStateDescription vmstate_serial_recv_fifo = { +static const VMStateDescription vmstate_serial_recv_fifo = { .name = "serial/recv_fifo", .version_id = 1, .minimum_version_id = 1, @@ -678,7 +709,7 @@ static bool serial_xmit_fifo_needed(void *opaque) return !fifo8_is_empty(&s->xmit_fifo); } -const VMStateDescription vmstate_serial_xmit_fifo = { +static const VMStateDescription vmstate_serial_xmit_fifo = { .name = "serial/xmit_fifo", .version_id = 1, .minimum_version_id = 1, @@ -694,12 +725,12 @@ static bool serial_fifo_timeout_timer_needed(void *opaque) return timer_pending(s->fifo_timeout_timer); } -const VMStateDescription vmstate_serial_fifo_timeout_timer = { +static const VMStateDescription vmstate_serial_fifo_timeout_timer = { .name = "serial/fifo_timeout_timer", .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { - VMSTATE_TIMER(fifo_timeout_timer, SerialState), + VMSTATE_TIMER_PTR(fifo_timeout_timer, SerialState), VMSTATE_END_OF_LIST() } }; @@ -710,7 +741,7 @@ static bool serial_timeout_ipending_needed(void *opaque) return s->timeout_ipending != 0; } -const VMStateDescription vmstate_serial_timeout_ipending = { +static const VMStateDescription vmstate_serial_timeout_ipending = { .name = "serial/timeout_ipending", .version_id = 1, .minimum_version_id = 1, @@ -726,13 +757,13 @@ static bool serial_poll_needed(void *opaque) return s->poll_msl >= 0; } -const VMStateDescription vmstate_serial_poll = { +static const VMStateDescription vmstate_serial_poll = { .name = "serial/poll", .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { VMSTATE_INT32(poll_msl, SerialState), - VMSTATE_TIMER(modem_status_poll, SerialState), + VMSTATE_TIMER_PTR(modem_status_poll, SerialState), VMSTATE_END_OF_LIST() } }; @@ -875,8 +906,7 @@ SerialState *serial_init(int base, qemu_irq irq, int baudbase, s->chr = chr; serial_realize_core(s, &err); if (err != NULL) { - error_report("%s", error_get_pretty(err)); - error_free(err); + error_report_err(err); exit(1); } @@ -939,8 +969,7 @@ SerialState *serial_mm_init(MemoryRegion *address_space, serial_realize_core(s, &err); if (err != NULL) { - error_report("%s", error_get_pretty(err)); - error_free(err); + error_report_err(err); exit(1); } vmstate_register(NULL, base, &vmstate_serial, s); diff --git a/hw/char/spapr_vty.c b/hw/char/spapr_vty.c index 0adf096..c7f824e 100644 --- a/hw/char/spapr_vty.c +++ b/hw/char/spapr_vty.c @@ -60,19 +60,17 @@ void vty_putchars(VIOsPAPRDevice *sdev, uint8_t *buf, int len) qemu_chr_fe_write(dev->chardev, buf, len); } -static int spapr_vty_init(VIOsPAPRDevice *sdev) +static void spapr_vty_realize(VIOsPAPRDevice *sdev, Error **errp) { VIOsPAPRVTYDevice *dev = VIO_SPAPR_VTY_DEVICE(sdev); if (!dev->chardev) { - fprintf(stderr, "spapr-vty: Can't create vty without a chardev!\n"); - exit(1); + error_setg(errp, "chardev property not set"); + return; } qemu_chr_add_handlers(dev->chardev, vty_can_receive, vty_receive, NULL, dev); - - return 0; } /* Forward declaration */ @@ -163,7 +161,7 @@ static void spapr_vty_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VIOsPAPRDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); - k->init = spapr_vty_init; + k->realize = spapr_vty_realize; k->dt_name = "vty"; k->dt_type = "serial"; k->dt_compatible = "hvterm1"; @@ -230,6 +228,10 @@ VIOsPAPRDevice *vty_lookup(sPAPREnvironment *spapr, target_ulong reg) return spapr_vty_get_default(spapr->vio_bus); } + if (!object_dynamic_cast(OBJECT(sdev), TYPE_VIO_SPAPR_VTY_DEVICE)) { + return NULL; + } + return sdev; } diff --git a/hw/char/stm32f2xx_usart.c b/hw/char/stm32f2xx_usart.c new file mode 100644 index 0000000..c9d3a1b --- /dev/null +++ b/hw/char/stm32f2xx_usart.c @@ -0,0 +1,232 @@ +/* + * 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 "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/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c index a7b1b68..e336bdb 100644 --- a/hw/char/virtio-serial-bus.c +++ b/hw/char/virtio-serial-bus.c @@ -26,7 +26,7 @@ #include "hw/virtio/virtio-serial.h" #include "hw/virtio/virtio-access.h" -struct VirtIOSerialDevices { +static struct VirtIOSerialDevices { QLIST_HEAD(, VirtIOSerial) devices; } vserdevices; @@ -64,7 +64,7 @@ static VirtIOSerialPort *find_port_by_name(char *name) VirtIOSerialPort *port; QTAILQ_FOREACH(port, &vser->ports, next) { - if (!strcmp(port->name, name)) { + if (port->name && !strcmp(port->name, name)) { return port; } } @@ -75,7 +75,7 @@ static VirtIOSerialPort *find_port_by_name(char *name) static bool use_multiport(VirtIOSerial *vser) { VirtIODevice *vdev = VIRTIO_DEVICE(vser); - return vdev->guest_features & (1 << VIRTIO_CONSOLE_F_MULTIPORT); + return virtio_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT); } static size_t write_to_port(VirtIOSerialPort *port, @@ -465,6 +465,37 @@ static void handle_output(VirtIODevice *vdev, VirtQueue *vq) 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 uint32_t get_features(VirtIODevice *vdev, uint32_t features) @@ -474,7 +505,7 @@ static uint32_t get_features(VirtIODevice *vdev, uint32_t features) vser = VIRTIO_SERIAL(vdev); if (vser->bus.max_nr_ports > 1) { - features |= (1 << VIRTIO_CONSOLE_F_MULTIPORT); + virtio_add_feature(&features, VIRTIO_CONSOLE_F_MULTIPORT); } return features; } @@ -482,10 +513,14 @@ static uint32_t get_features(VirtIODevice *vdev, uint32_t features) /* Guest requested config info */ static void get_config(VirtIODevice *vdev, uint8_t *config_data) { - VirtIOSerial *vser; - - vser = VIRTIO_SERIAL(vdev); - memcpy(config_data, &vser->config, sizeof(struct virtio_console_config)); + 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) @@ -533,10 +568,6 @@ static void vser_reset(VirtIODevice *vdev) vser = VIRTIO_SERIAL(vdev); guest_reset(vser); - - /* In case we have switched endianness */ - vser->config.max_nr_ports = - virtio_tswap32(vdev, vser->serial.max_virtserial_ports); } static void virtio_serial_save(QEMUFile *f, void *opaque) @@ -551,15 +582,16 @@ static void virtio_serial_save_device(VirtIODevice *vdev, QEMUFile *f) VirtIOSerialPort *port; uint32_t nr_active_ports; unsigned int i, max_nr_ports; + struct virtio_console_config config; - /* The config space */ - qemu_put_be16s(f, &s->config.cols); - qemu_put_be16s(f, &s->config.rows); - - qemu_put_be32s(f, &s->config.max_nr_ports); + /* 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 = virtio_tswap32(vdev, s->config.max_nr_ports); + 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]); } @@ -715,13 +747,7 @@ static int virtio_serial_load_device(VirtIODevice *vdev, QEMUFile *f, qemu_get_be16s(f, (uint16_t *) &tmp); qemu_get_be32s(f, &tmp); - /* Note: this is the only location where we use tswap32() instead of - * virtio_tswap32() because: - * - virtio_tswap32() only makes sense when the device is fully restored - * - the target endianness that was used to populate s->config is - * necessarly the default one - */ - max_nr_ports = tswap32(s->config.max_nr_ports); + max_nr_ports = s->serial.max_virtserial_ports; for (i = 0; i < (max_nr_ports + 31) / 32; i++) { qemu_get_be32s(f, &ports_map); @@ -784,10 +810,9 @@ static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent) /* This function is only used if a port id is not provided by the user */ static uint32_t find_free_port_id(VirtIOSerial *vser) { - VirtIODevice *vdev = VIRTIO_DEVICE(vser); unsigned int i, max_nr_ports; - max_nr_ports = virtio_tswap32(vdev, vser->config.max_nr_ports); + max_nr_ports = vser->serial.max_virtserial_ports; for (i = 0; i < (max_nr_ports + 31) / 32; i++) { uint32_t map, bit; @@ -848,7 +873,6 @@ 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)); - VirtIODevice *vdev = VIRTIO_DEVICE(bus->vser); int max_nr_ports; bool plugging_port0; Error *err = NULL; @@ -890,7 +914,7 @@ static void virtser_port_device_realize(DeviceState *dev, Error **errp) } } - max_nr_ports = virtio_tswap32(vdev, port->vser->config.max_nr_ports); + 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); @@ -956,8 +980,10 @@ static void virtio_serial_device_realize(DeviceState *dev, Error **errp) 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, - sizeof(struct virtio_console_config)); + 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, @@ -995,8 +1021,6 @@ static void virtio_serial_device_realize(DeviceState *dev, Error **errp) vser->ovqs[i] = virtio_add_queue(vdev, 128, handle_output); } - vser->config.max_nr_ports = - virtio_tswap32(vdev, vser->serial.max_virtserial_ports); vser->ports_map = g_malloc0(((vser->serial.max_virtserial_ports + 31) / 32) * sizeof(vser->ports_map[0])); /* diff --git a/hw/char/xilinx_uartlite.c b/hw/char/xilinx_uartlite.c index f7c3cae..ef883a8 100644 --- a/hw/char/xilinx_uartlite.c +++ b/hw/char/xilinx_uartlite.c @@ -205,6 +205,7 @@ 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); @@ -227,6 +228,8 @@ static void xilinx_uartlite_class_init(ObjectClass *klass, void *data) 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 = { diff --git a/hw/core/Makefile.objs b/hw/core/Makefile.objs index 9dce1bc..abb3560 100644 --- a/hw/core/Makefile.objs +++ b/hw/core/Makefile.objs @@ -14,4 +14,4 @@ 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_SOFTMMU) += platform-bus.o +common-obj-$(CONFIG_PLATFORM_BUS) += platform-bus.o diff --git a/hw/core/fw-path-provider.c b/hw/core/fw-path-provider.c index 1290c3e..7442d32 100644 --- a/hw/core/fw-path-provider.c +++ b/hw/core/fw-path-provider.c @@ -1,5 +1,5 @@ /* - * Firmware patch provider class and helpers. + * 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 diff --git a/hw/core/loader.c b/hw/core/loader.c index 7527fd3..d4c441f 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -267,7 +267,7 @@ int load_aout(const char *filename, hwaddr addr, int max_sz, /* ELF loader */ -static void *load_at(int fd, int offset, int size) +static void *load_at(int fd, off_t offset, size_t size) { void *ptr; if (lseek(fd, offset, SEEK_SET) < 0) @@ -297,6 +297,7 @@ static void *load_at(int fd, int offset, int size) #undef elf_phdr #undef elf_shdr #undef elf_sym +#undef elf_rela #undef elf_note #undef elf_word #undef elf_sword @@ -307,6 +308,7 @@ static void *load_at(int fd, int offset, int size) #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 @@ -614,14 +616,9 @@ int load_ramdisk(const char *filename, hwaddr addr, uint64_t max_sz) NULL, NULL); } -/* This simply prevents g_malloc in the function below from allocating - * a huge amount of memory, by placing a limit on the maximum - * uncompressed image size that load_image_gzipped will read. - */ -#define LOAD_IMAGE_MAX_GUNZIP_BYTES (256 << 20) - -/* Load a gzip-compressed kernel. */ -int load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz) +/* 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; @@ -653,8 +650,11 @@ int load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz) goto out; } - rom_add_blob_fixed(filename, data, bytes, addr); + /* 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); @@ -662,6 +662,20 @@ int load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz) 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. @@ -712,12 +726,22 @@ static void rom_insert(Rom *rom) 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_ram(rom->mr, owner, name, rom->datasize, &error_abort); + memory_region_init_resizeable_ram(rom->mr, owner, name, + rom->datasize, rom->romsize, + fw_cfg_resized, + &error_abort); memory_region_set_readonly(rom->mr, true); vmstate_register_ram_global(rom->mr); @@ -812,7 +836,7 @@ err: } ram_addr_t rom_add_blob(const char *name, const void *blob, size_t len, - hwaddr addr, const char *fw_file_name, + size_t max_len, hwaddr addr, const char *fw_file_name, FWCfgReadCallback fw_callback, void *callback_opaque) { Rom *rom; @@ -821,7 +845,7 @@ ram_addr_t rom_add_blob(const char *name, const void *blob, size_t len, rom = g_malloc0(sizeof(*rom)); rom->name = g_strdup(name); rom->addr = addr; - rom->romsize = len; + rom->romsize = max_len ? max_len : len; rom->datasize = len; rom->data = g_malloc0(rom->datasize); memcpy(rom->data, blob, len); @@ -841,7 +865,7 @@ ram_addr_t rom_add_blob(const char *name, const void *blob, size_t len, fw_cfg_add_file_callback(fw_cfg, fw_file_name, fw_callback, callback_opaque, - data, rom->romsize); + data, rom->datasize); } return ret; } @@ -1040,7 +1064,7 @@ void *rom_ptr(hwaddr addr) return rom->data + (addr - rom->addr); } -void do_info_roms(Monitor *mon, const QDict *qdict) +void hmp_info_roms(Monitor *mon, const QDict *qdict) { Rom *rom; diff --git a/hw/core/machine.c b/hw/core/machine.c index 19d3e3a..25c45e6 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -31,18 +31,12 @@ static void machine_set_accel(Object *obj, const char *value, Error **errp) ms->accel = g_strdup(value); } -static bool machine_get_kernel_irqchip(Object *obj, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - return ms->kernel_irqchip; -} - static void machine_set_kernel_irqchip(Object *obj, bool value, Error **errp) { MachineState *ms = MACHINE(obj); - ms->kernel_irqchip = value; + ms->kernel_irqchip_allowed = value; + ms->kernel_irqchip_required = value; } static void machine_get_kvm_shadow_mem(Object *obj, Visitor *v, @@ -229,6 +223,7 @@ static void machine_set_usb(Object *obj, bool value, Error **errp) MachineState *ms = MACHINE(obj); ms->usb = value; + ms->usb_disabled = !value; } static char *machine_get_firmware(Object *obj, Error **errp) @@ -260,6 +255,20 @@ static void machine_set_iommu(Object *obj, bool value, Error **errp) 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 int error_on_sysbus_device(SysBusDevice *sbdev, void *opaque) { error_report("Option '-device %s' cannot be handled by this machine", @@ -289,50 +298,106 @@ 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_bool(obj, "kernel-irqchip", - machine_get_kernel_irqchip, + NULL, machine_set_kernel_irqchip, NULL); + object_property_set_description(obj, "kernel-irqchip", + "Use 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_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); /* Register notifier when init is done for sysbus sanity checks */ ms->sysbus_notifier.notify = machine_init_notify; @@ -353,6 +418,46 @@ static void machine_finalize(Object *obj) g_free(ms->firmware); } +bool machine_usb(MachineState *machine) +{ + return machine->usb; +} + +bool machine_iommu(MachineState *machine) +{ + return machine->iommu; +} + +bool machine_kernel_irqchip_allowed(MachineState *machine) +{ + return machine->kernel_irqchip_allowed; +} + +bool machine_kernel_irqchip_required(MachineState *machine) +{ + return machine->kernel_irqchip_required; +} + +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, diff --git a/hw/core/ptimer.c b/hw/core/ptimer.c index 466e543..2abad1f 100644 --- a/hw/core/ptimer.c +++ b/hw/core/ptimer.c @@ -214,7 +214,7 @@ const VMStateDescription vmstate_ptimer = { VMSTATE_INT64(period, ptimer_state), VMSTATE_INT64(last_event, ptimer_state), VMSTATE_INT64(next_event, ptimer_state), - VMSTATE_TIMER(timer, ptimer_state), + VMSTATE_TIMER_PTR(timer, ptimer_state), VMSTATE_END_OF_LIST() } }; diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index 65901ef..c413226 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -177,42 +177,69 @@ PropertyInfo qdev_prop_chr = { }; /* --- netdev device --- */ +static void get_netdev(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop); + char *p = g_strdup(peers_ptr->ncs[0] ? peers_ptr->ncs[0]->name : ""); + + visit_type_str(v, &p, name, errp); + g_free(p); +} -static int parse_netdev(DeviceState *dev, const char *str, void **ptr) +static void set_netdev(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) { - NICPeers *peers_ptr = (NICPeers *)ptr; + 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]; - int queues, i = 0; - int ret; + 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, &str, name, &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) { - ret = -ENOENT; - goto err; + err = -ENOENT; + goto out; } if (queues > MAX_QUEUE_NUM) { - ret = -E2BIG; - goto err; + 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) { - ret = -ENOENT; - goto err; + err = -ENOENT; + goto out; } if (peers[i]->peer) { - ret = -EEXIST; - goto err; + err = -EEXIST; + goto out; } if (ncs[i]) { - ret = -EINVAL; - goto err; + err = -EINVAL; + goto out; } ncs[i] = peers[i]; @@ -221,30 +248,9 @@ static int parse_netdev(DeviceState *dev, const char *str, void **ptr) peers_ptr->queues = queues; - return 0; - -err: - return ret; -} - -static char *print_netdev(void *ptr) -{ - NetClientState *netdev = ptr; - const char *val = netdev->name ? netdev->name : ""; - - return g_strdup(val); -} - -static void get_netdev(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) -{ - get_pointer(obj, v, opaque, print_netdev, name, errp); -} - -static void set_netdev(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) -{ - set_pointer(obj, v, opaque, parse_netdev, name, errp); +out: + error_set_from_qdev_prop_error(errp, err, dev, prop, str); + g_free(str); } PropertyInfo qdev_prop_netdev = { @@ -335,27 +341,25 @@ PropertyInfo qdev_prop_vlan = { .set = set_vlan, }; -int qdev_prop_set_drive(DeviceState *dev, const char *name, - BlockBackend *value) +void qdev_prop_set_drive(DeviceState *dev, const char *name, + BlockBackend *value, Error **errp) { - Error *err = NULL; - object_property_set_str(OBJECT(dev), - value ? blk_name(value) : "", name, &err); - if (err) { - qerror_report_err(err); - error_free(err); - return -1; - } - return 0; + object_property_set_str(OBJECT(dev), value ? blk_name(value) : "", + name, errp); } void qdev_prop_set_drive_nofail(DeviceState *dev, const char *name, BlockBackend *value) { - if (qdev_prop_set_drive(dev, name, value) < 0) { + Error *err = NULL; + + qdev_prop_set_drive(dev, name, value, &err); + if (err) { + error_report_err(err); exit(1); } } + void qdev_prop_set_chr(DeviceState *dev, const char *name, CharDriverState *value) { diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 2e47f70..570d5f0 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -580,7 +580,8 @@ static void set_blocksize(Object *obj, Visitor *v, void *opaque, error_propagate(errp, local_err); return; } - if (value < min || value > max) { + /* value of 0 means "unset" */ + if (value && (value < min || value > max)) { error_set(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE, dev->id?:"", name, (int64_t)value, min, max); return; @@ -990,8 +991,8 @@ int qdev_prop_check_globals(void) return ret; } -void qdev_prop_set_globals_for_type(DeviceState *dev, const char *typename, - Error **errp) +static void qdev_prop_set_globals_for_type(DeviceState *dev, + const char *typename) { GlobalProperty *prop; @@ -1004,25 +1005,22 @@ void qdev_prop_set_globals_for_type(DeviceState *dev, const char *typename, prop->used = true; object_property_parse(OBJECT(dev), prop->value, prop->property, &err); if (err != NULL) { - error_propagate(errp, err); + assert(prop->user_provided); + error_report("Warning: global %s.%s=%s ignored (%s)", + prop->driver, prop->property, prop->value, + error_get_pretty(err)); + error_free(err); return; } } } -void qdev_prop_set_globals(DeviceState *dev, Error **errp) +void qdev_prop_set_globals(DeviceState *dev) { ObjectClass *class = object_get_class(OBJECT(dev)); do { - Error *err = NULL; - - qdev_prop_set_globals_for_type(dev, object_class_get_name(class), - &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } + qdev_prop_set_globals_for_type(dev, object_class_get_name(class)); class = object_class_get_parent(class); } while (class); } diff --git a/hw/core/qdev.c b/hw/core/qdev.c index 35fd00d..6e6a65d 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -189,6 +189,56 @@ int qdev_init(DeviceState *dev) return 0; } +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); @@ -323,10 +373,15 @@ void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev, way is somewhat unclean, and best avoided. */ void qdev_init_nofail(DeviceState *dev) { - const char *typename = object_get_typename(OBJECT(dev)); + Error *err = NULL; - if (qdev_init(dev) < 0) { - error_report("Initialization of device %s failed", typename); + assert(!dev->realized); + + object_property_set_bool(OBJECT(dev), true, "realized", &err); + if (err) { + error_report("Initialization of device %s failed: %s", + object_get_typename(OBJECT(dev)), + error_get_pretty(err)); exit(1); } } @@ -446,8 +501,9 @@ void qdev_connect_gpio_out_named(DeviceState *dev, const char *name, int n, * 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(qdev_get_machine(), "non-qdev-gpio[*]", - OBJECT(pin), NULL); + 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); @@ -768,6 +824,13 @@ static char *qdev_get_fw_dev_path_from_handler(BusState *bus, DeviceState *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; @@ -938,7 +1001,12 @@ void qdev_alias_all_properties(DeviceState *target, Object *source) static int qdev_add_hotpluggable_device(Object *obj, void *opaque) { GSList **list = opaque; - DeviceState *dev = DEVICE(obj); + 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); @@ -994,6 +1062,8 @@ static void device_set_realized(Object *obj, bool value, Error **errp) 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); @@ -1035,6 +1105,7 @@ static void device_set_realized(Object *obj, bool value, Error **errp) dc->unrealize(dev, local_errp); } dev->pending_deleted_event = true; + DEVICE_LISTENER_CALL(unrealize, Reverse, dev); } if (local_err != NULL) { @@ -1126,13 +1197,7 @@ static void device_initfn(Object *obj) static void device_post_init(Object *obj) { - Error *err = NULL; - qdev_prop_set_globals(DEVICE(obj), &err); - if (err) { - qerror_report_err(err); - error_free(err); - exit(EXIT_FAILURE); - } + qdev_prop_set_globals(DEVICE(obj)); } /* Unlink device from bus and free the structure. */ @@ -1141,9 +1206,7 @@ static void device_finalize(Object *obj) NamedGPIOList *ngl, *next; DeviceState *dev = DEVICE(obj); - if (dev->opts) { - qemu_opts_del(dev->opts); - } + qemu_opts_del(dev->opts); QLIST_FOREACH_SAFE(ngl, &dev->gpios, node, next) { QLIST_REMOVE(ngl, node); diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c index 84af593..92eced9 100644 --- a/hw/core/sysbus.c +++ b/hw/core/sysbus.c @@ -91,6 +91,8 @@ bool sysbus_has_irq(SysBusDevice *dev, int n) ObjectProperty *r; r = object_property_find(OBJECT(dev), prop, NULL); + g_free(prop); + return (r != NULL); } @@ -279,19 +281,15 @@ static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent) static char *sysbus_get_fw_dev_path(DeviceState *dev) { SysBusDevice *s = SYS_BUS_DEVICE(dev); - char path[40]; - int off; - - off = snprintf(path, sizeof(path), "%s", qdev_fw_name(dev)); if (s->num_mmio) { - snprintf(path + off, sizeof(path) - off, "@"TARGET_FMT_plx, - s->mmio[0].addr); - } else if (s->num_pio) { - snprintf(path + off, sizeof(path) - off, "@i%04x", s->pio[0]); + return g_strdup_printf("%s@" TARGET_FMT_plx, qdev_fw_name(dev), + s->mmio[0].addr); } - - return g_strdup(path); + if (s->num_pio) { + return g_strdup_printf("%s@i%04x", qdev_fw_name(dev), s->pio[0]); + } + return g_strdup(qdev_fw_name(dev)); } void sysbus_add_io(SysBusDevice *dev, hwaddr addr, diff --git a/hw/cris/axis_dev88.c b/hw/cris/axis_dev88.c index 0479196..3cae480 100644 --- a/hw/cris/axis_dev88.c +++ b/hw/cris/axis_dev88.c @@ -270,9 +270,8 @@ void axisdev88_init(MachineState *machine) env = &cpu->env; /* allocate RAM */ - memory_region_init_ram(phys_ram, NULL, "axisdev88.ram", ram_size, - &error_abort); - vmstate_register_ram_global(phys_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 diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs index 7ed76a9..e73cb7d 100644 --- a/hw/display/Makefile.objs +++ b/hw/display/Makefile.objs @@ -20,7 +20,8 @@ common-obj-$(CONFIG_ZAURUS) += tc6393xb.o ifeq ($(CONFIG_MILKYMIST_TMU2),y) common-obj-y += milkymist-tmu2.o -libs_softmmu += $(GLX_LIBS) +milkymist-tmu2.o-cflags := $(OPENGL_CFLAGS) +libs_softmmu += $(OPENGL_LIBS) endif obj-$(CONFIG_OMAP) += omap_dss.o diff --git a/hw/display/blizzard.c b/hw/display/blizzard.c index 92b1fac..5019bbb 100644 --- a/hw/display/blizzard.c +++ b/hw/display/blizzard.c @@ -21,7 +21,6 @@ #include "qemu-common.h" #include "ui/console.h" #include "hw/devices.h" -#include "vga_int.h" #include "ui/pixel_ops.h" typedef void (*blizzard_fn_t)(uint8_t *, const uint8_t *, unsigned int); diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c index 2725264..8765a7e 100644 --- a/hw/display/cirrus_vga.c +++ b/hw/display/cirrus_vga.c @@ -202,8 +202,6 @@ typedef struct CirrusVGAState { uint32_t cirrus_bank_base[2]; uint32_t cirrus_bank_limit[2]; uint8_t cirrus_hidden_palette[48]; - uint32_t hw_cursor_x; - uint32_t hw_cursor_y; int cirrus_blt_pixelwidth; int cirrus_blt_width; int cirrus_blt_height; @@ -1328,7 +1326,7 @@ static void cirrus_vga_write_sr(CirrusVGAState * s, uint32_t val) case 0xd0: case 0xf0: // Graphics Cursor X s->vga.sr[0x10] = val; - s->hw_cursor_x = (val << 3) | (s->vga.sr_index >> 5); + s->vga.hw_cursor_x = (val << 3) | (s->vga.sr_index >> 5); break; case 0x11: case 0x31: @@ -1339,7 +1337,7 @@ static void cirrus_vga_write_sr(CirrusVGAState * s, uint32_t val) case 0xd1: case 0xf1: // Graphics Cursor Y s->vga.sr[0x11] = val; - s->hw_cursor_y = (val << 3) | (s->vga.sr_index >> 5); + s->vga.hw_cursor_y = (val << 3) | (s->vga.sr_index >> 5); break; case 0x07: // Extended Sequencer Mode cirrus_update_memory_access(s); @@ -1351,7 +1349,6 @@ static void cirrus_vga_write_sr(CirrusVGAState * s, uint32_t val) 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 @@ -1370,6 +1367,14 @@ static void cirrus_vga_write_sr(CirrusVGAState * s, uint32_t val) 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); @@ -2188,14 +2193,14 @@ static void cirrus_cursor_invalidate(VGACommonState *s1) } /* invalidate last cursor and new cursor if any change */ if (s->last_hw_cursor_size != size || - s->last_hw_cursor_x != s->hw_cursor_x || - s->last_hw_cursor_y != s->hw_cursor_y) { + 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->hw_cursor_x; - s->last_hw_cursor_y = s->hw_cursor_y; + 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); @@ -2252,14 +2257,15 @@ static void cirrus_cursor_draw_line(VGACommonState *s1, uint8_t *d1, int scr_y) } else { h = 32; } - if (scr_y < s->hw_cursor_y || - scr_y >= (s->hw_cursor_y + h)) + 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->hw_cursor_y) * 16; + src += (scr_y - s->vga.hw_cursor_y) * 16; poffset = 8; content = ((uint32_t *)src)[0] | ((uint32_t *)src)[1] | @@ -2267,7 +2273,7 @@ static void cirrus_cursor_draw_line(VGACommonState *s1, uint8_t *d1, int scr_y) ((uint32_t *)src)[3]; } else { src += (s->vga.sr[0x13] & 0x3f) * 256; - src += (scr_y - s->hw_cursor_y) * 4; + src += (scr_y - s->vga.hw_cursor_y) * 4; poffset = 128; @@ -2279,10 +2285,10 @@ static void cirrus_cursor_draw_line(VGACommonState *s1, uint8_t *d1, int scr_y) return; w = h; - x1 = s->hw_cursor_x; + x1 = s->vga.hw_cursor_x; if (x1 >= s->vga.last_scr_width) return; - x2 = s->hw_cursor_x + w; + x2 = s->vga.hw_cursor_x + w; if (x2 > s->vga.last_scr_width) x2 = s->vga.last_scr_width; w = x2 - x1; @@ -2771,8 +2777,8 @@ static const VMStateDescription vmstate_cirrus_vga = { VMSTATE_INT32(vga.bank_offset, CirrusVGAState), VMSTATE_UINT8(cirrus_hidden_dac_lockindex, CirrusVGAState), VMSTATE_UINT8(cirrus_hidden_dac_data, CirrusVGAState), - VMSTATE_UINT32(hw_cursor_x, CirrusVGAState), - VMSTATE_UINT32(hw_cursor_y, 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() @@ -2901,7 +2907,7 @@ static void cirrus_init_common(CirrusVGAState *s, Object *owner, bank, 1); } memory_region_add_subregion_overlap(system_memory, - isa_mem_base + 0x000a0000, + 0x000a0000, &s->low_mem_container, 1); memory_region_set_coalescing(&s->low_mem); @@ -3000,7 +3006,7 @@ static const TypeInfo isa_cirrus_vga_info = { * ***************************************/ -static int pci_cirrus_vga_initfn(PCIDevice *dev) +static void pci_cirrus_vga_realize(PCIDevice *dev, Error **errp) { PCICirrusVGAState *d = DO_UPCAST(PCICirrusVGAState, dev, dev); CirrusVGAState *s = &d->cirrus_vga; @@ -3011,9 +3017,9 @@ static int pci_cirrus_vga_initfn(PCIDevice *dev) 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_report("Invalid cirrus_vga ram size '%u'", - s->vga.vram_size_mb); - return -1; + 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); @@ -3038,7 +3044,6 @@ static int pci_cirrus_vga_initfn(PCIDevice *dev) if (device_id == CIRRUS_ID_CLGD5446) { pci_register_bar(&d->dev, 1, 0, &s->cirrus_mmio_io); } - return 0; } static Property pci_vga_cirrus_properties[] = { @@ -3052,7 +3057,7 @@ static void cirrus_vga_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = pci_cirrus_vga_initfn; + k->realize = pci_cirrus_vga_realize; k->romfile = VGABIOS_CIRRUS_FILENAME; k->vendor_id = PCI_VENDOR_ID_CIRRUS; k->device_id = CIRRUS_ID_CLGD5446; diff --git a/hw/display/omap_dss.c b/hw/display/omap_dss.c index 24ccbcc..f1fef27 100644 --- a/hw/display/omap_dss.c +++ b/hw/display/omap_dss.c @@ -212,7 +212,8 @@ static void omap_diss_write(void *opaque, hwaddr addr, struct omap_dss_s *s = (struct omap_dss_s *) opaque; if (size != 4) { - return omap_badwidth_write32(opaque, addr, value); + omap_badwidth_write32(opaque, addr, value); + return; } switch (addr) { @@ -377,7 +378,8 @@ static void omap_disc_write(void *opaque, hwaddr addr, struct omap_dss_s *s = (struct omap_dss_s *) opaque; if (size != 4) { - return omap_badwidth_write32(opaque, addr, value); + omap_badwidth_write32(opaque, addr, value); + return; } switch (addr) { @@ -736,7 +738,8 @@ static void omap_rfbi_write(void *opaque, hwaddr addr, struct omap_dss_s *s = (struct omap_dss_s *) opaque; if (size != 4) { - return omap_badwidth_write32(opaque, addr, value); + omap_badwidth_write32(opaque, addr, value); + return; } switch (addr) { @@ -928,7 +931,8 @@ static void omap_venc_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { if (size != 4) { - return omap_badwidth_write32(opaque, addr, size); + omap_badwidth_write32(opaque, addr, size); + return; } switch (addr) { @@ -1016,7 +1020,8 @@ static void omap_im3_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { if (size != 4) { - return omap_badwidth_write32(opaque, addr, value); + omap_badwidth_write32(opaque, addr, value); + return; } switch (addr) { diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c index e812ddd..a542087 100644 --- a/hw/display/qxl-render.c +++ b/hw/display/qxl-render.c @@ -283,12 +283,14 @@ int qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext) 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/hw/display/qxl.c b/hw/display/qxl.c index b540dd6..b6d65b9 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -120,9 +120,12 @@ static QXLMode qxl_modes[] = { 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 */ }; @@ -297,19 +300,6 @@ void qxl_spice_reset_cursor(PCIQXLDevice *qxl) qxl->ssd.cursor = cursor_builtin_hidden(); } - -static inline uint32_t msb_mask(uint32_t val) -{ - uint32_t mask; - - do { - mask = ~(val - 1) & val; - val &= ~mask; - } while (mask < val); - - return mask; -} - static ram_addr_t qxl_rom_size(void) { uint32_t required_rom_size = sizeof(QXLRom) + sizeof(QXLModes) + @@ -367,6 +357,8 @@ static void init_qxl_rom(PCIQXLDevice *d) 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); @@ -1092,6 +1084,7 @@ static void qxl_enter_vga_mode(PCIQXLDevice *d) 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); @@ -1105,6 +1098,7 @@ static void qxl_exit_vga_mode(PCIQXLDevice *d) } 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); } @@ -1861,10 +1855,6 @@ static void display_refresh(DisplayChangeListener *dcl) if (qxl->mode == QXL_MODE_VGA) { qemu_spice_display_refresh(&qxl->ssd); - } else { - qemu_mutex_lock(&qxl->ssd.lock); - qemu_spice_cursor_refresh_unlocked(&qxl->ssd); - qemu_mutex_unlock(&qxl->ssd.lock); } } @@ -1881,6 +1871,12 @@ static void qxl_init_ramsize(PCIQXLDevice *qxl) 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) */ @@ -1911,13 +1907,13 @@ static void qxl_init_ramsize(PCIQXLDevice *qxl) qxl->vram32_size = 4096; qxl->vram_size = 4096; } - qxl->vgamem_size = msb_mask(qxl->vgamem_size * 2 - 1); - qxl->vga.vram_size = msb_mask(qxl->vga.vram_size * 2 - 1); - qxl->vram32_size = msb_mask(qxl->vram32_size * 2 - 1); - qxl->vram_size = msb_mask(qxl->vram_size * 2 - 1); + 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 int qxl_init_common(PCIQXLDevice *qxl) +static void qxl_realize_common(PCIQXLDevice *qxl, Error **errp) { uint8_t* config = qxl->pci.config; uint32_t pci_device_rev; @@ -1946,12 +1942,12 @@ static int qxl_init_common(PCIQXLDevice *qxl) break; case 4: /* qxl-4 */ pci_device_rev = QXL_REVISION_STABLE_V12; - io_size = msb_mask(QXL_IO_RANGE_SIZE * 2 - 1); + io_size = pow2ceil(QXL_IO_RANGE_SIZE); break; default: - error_report("Invalid revision %d for qxl device (max %d)", - qxl->revision, QXL_DEFAULT_REVISION); - return -1; + 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); @@ -2015,9 +2011,9 @@ static int qxl_init_common(PCIQXLDevice *qxl) qxl->ssd.qxl.base.sif = &qxl_interface.base; if (qemu_spice_add_display_interface(&qxl->ssd.qxl, qxl->vga.con) != 0) { - error_report("qxl interface %d.%d not supported by spice-server", - SPICE_INTERFACE_QXL_MAJOR, SPICE_INTERFACE_QXL_MINOR); - return -1; + 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); @@ -2025,15 +2021,14 @@ static int qxl_init_common(PCIQXLDevice *qxl) qxl_reset_state(qxl); qxl->update_area_bh = qemu_bh_new(qxl_render_update_area_bh, qxl); - - return 0; + qxl->ssd.cursor_bh = qemu_bh_new(qemu_spice_cursor_refresh_bh, &qxl->ssd); } -static int qxl_init_primary(PCIDevice *dev) +static void qxl_realize_primary(PCIDevice *dev, Error **errp) { PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, dev); VGACommonState *vga = &qxl->vga; - int rc; + Error *local_err = NULL; qxl->id = 0; qxl_init_ramsize(qxl); @@ -2050,18 +2045,18 @@ static int qxl_init_primary(PCIDevice *dev) vga->con = graphic_console_init(DEVICE(dev), 0, &qxl_ops, qxl); qemu_spice_display_init_common(&qxl->ssd); - rc = qxl_init_common(qxl); - if (rc != 0) { - return rc; + 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); - return rc; } -static int qxl_init_secondary(PCIDevice *dev) +static void qxl_realize_secondary(PCIDevice *dev, Error **errp) { static int device_id = 1; PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, dev); @@ -2074,7 +2069,7 @@ static int qxl_init_secondary(PCIDevice *dev) qxl->vga.vram_ptr = memory_region_get_ram_ptr(&qxl->vga.vram); qxl->vga.con = graphic_console_init(DEVICE(dev), 0, &qxl_ops, qxl); - return qxl_init_common(qxl); + qxl_realize_common(qxl, errp); } static void qxl_pre_save(void *opaque) @@ -2287,7 +2282,7 @@ static void qxl_primary_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = qxl_init_primary; + k->realize = qxl_realize_primary; k->romfile = "vgabios-qxl.bin"; k->vendor_id = REDHAT_PCI_VENDOR_ID; k->device_id = QXL_DEVICE_ID_STABLE; @@ -2312,7 +2307,7 @@ static void qxl_secondary_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = qxl_init_secondary; + k->realize = qxl_realize_secondary; k->vendor_id = REDHAT_PCI_VENDOR_ID; k->device_id = QXL_DEVICE_ID_STABLE; k->class_id = PCI_CLASS_DISPLAY_OTHER; diff --git a/hw/display/vga-isa.c b/hw/display/vga-isa.c index 2b480bd..7f3c989 100644 --- a/hw/display/vga-isa.c +++ b/hw/display/vga-isa.c @@ -64,7 +64,7 @@ static void vga_isa_realizefn(DeviceState *dev, Error **errp) isa_register_portio_list(isadev, 0x1ce, vbe_ports, s, "vbe"); } memory_region_add_subregion_overlap(isa_address_space(isadev), - isa_mem_base + 0x000a0000, + 0x000a0000, vga_io_memory, 1); memory_region_set_coalescing(vga_io_memory); s->con = graphic_console_init(DEVICE(dev), 0, s->hw_ops, s); diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c index db922f1..aabfc23 100644 --- a/hw/display/vga-pci.c +++ b/hw/display/vga-pci.c @@ -181,6 +181,20 @@ static void pci_vga_qext_write(void *ptr, hwaddr addr, } } +static bool vga_get_big_endian_fb(Object *obj, Error **errp) +{ + PCIVGAState *d = DO_UPCAST(PCIVGAState, dev, PCI_DEVICE(obj)); + + return d->vga.big_endian_fb; +} + +static void vga_set_big_endian_fb(Object *obj, bool value, Error **errp) +{ + PCIVGAState *d = DO_UPCAST(PCIVGAState, dev, 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, @@ -189,7 +203,7 @@ static const MemoryRegionOps pci_vga_qext_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static int pci_std_vga_initfn(PCIDevice *dev) +static void pci_std_vga_realize(PCIDevice *dev, Error **errp) { PCIVGAState *d = DO_UPCAST(PCIVGAState, dev, dev); VGACommonState *s = &d->vga; @@ -232,11 +246,16 @@ static int pci_std_vga_initfn(PCIDevice *dev) /* compatibility with pc-0.13 and older */ vga_init_vbe(s, OBJECT(dev), pci_address_space(dev)); } +} - return 0; +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 int pci_secondary_vga_initfn(PCIDevice *dev) +static void pci_secondary_vga_realize(PCIDevice *dev, Error **errp) { PCIVGAState *d = DO_UPCAST(PCIVGAState, dev, dev); VGACommonState *s = &d->vga; @@ -267,8 +286,13 @@ static int pci_secondary_vga_initfn(PCIDevice *dev) 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); +} - return 0; +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) @@ -298,7 +322,7 @@ static void vga_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = pci_std_vga_initfn; + k->realize = pci_std_vga_realize; k->romfile = "vgabios-stdvga.bin"; k->vendor_id = PCI_VENDOR_ID_QEMU; k->device_id = PCI_DEVICE_ID_QEMU_VGA; @@ -314,18 +338,20 @@ static void secondary_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = pci_secondary_vga_initfn; + k->realize = pci_secondary_vga_realize; k->vendor_id = PCI_VENDOR_ID_QEMU; k->device_id = PCI_DEVICE_ID_QEMU_VGA; k->class_id = PCI_CLASS_DISPLAY_OTHER; dc->vmsd = &vmstate_vga_pci; dc->props = secondary_pci_properties; dc->reset = pci_secondary_vga_reset; + set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); } static const TypeInfo vga_info = { .name = "VGA", .parent = TYPE_PCI_DEVICE, + .instance_init = pci_std_vga_init, .instance_size = sizeof(PCIVGAState), .class_init = vga_class_init, }; @@ -333,6 +359,7 @@ static const TypeInfo vga_info = { static const TypeInfo secondary_info = { .name = "secondary-vga", .parent = TYPE_PCI_DEVICE, + .instance_init = pci_secondary_vga_init, .instance_size = sizeof(PCIVGAState), .class_init = secondary_class_init, }; diff --git a/hw/display/vga.c b/hw/display/vga.c index 52eaf05..d1d296c 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -177,7 +177,6 @@ static void vga_update_memory_access(VGACommonState *s) size = 0x8000; break; } - base += isa_mem_base; 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, @@ -1436,6 +1435,8 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) 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 @@ -1479,21 +1480,42 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) } 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) { - if (depth == 32 || (depth == 16 && !byteswap)) { - pixman_format_code_t format = - qemu_default_pixman_format(depth, !byteswap); + 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; @@ -1975,7 +1997,7 @@ static void vga_mem_write(void *opaque, hwaddr addr, { VGACommonState *s = opaque; - return vga_mem_writeb(s, addr, data); + vga_mem_writeb(s, addr, data); } const MemoryRegionOps vga_mem_ops = { @@ -2009,7 +2031,7 @@ static bool vga_endian_state_needed(void *opaque) return s->default_endian_fb != s->big_endian_fb; } -const VMStateDescription vmstate_vga_endian = { +static const VMStateDescription vmstate_vga_endian = { .name = "vga.endian", .version_id = 1, .minimum_version_id = 1, @@ -2072,6 +2094,17 @@ static const GraphicHwOps vga_ops = { .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; @@ -2099,13 +2132,10 @@ void vga_common_init(VGACommonState *s, Object *obj, bool global_vmstate) expand4to8[i] = v; } - /* valid range: 1 MB -> 256 MB */ - s->vram_size = 1024 * 1024; - while (s->vram_size < (s->vram_size_mb << 20) && - s->vram_size < (256 << 20)) { - s->vram_size <<= 1; - } - s->vram_size_mb = s->vram_size >> 20; + 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; } @@ -2195,7 +2225,7 @@ void vga_init(VGACommonState *s, Object *obj, MemoryRegion *address_space, vga_io_memory = vga_init_io(s, obj, &vga_ports, &vbe_ports); memory_region_add_subregion_overlap(address_space, - isa_mem_base + 0x000a0000, + 0x000a0000, vga_io_memory, 1); memory_region_set_coalescing(vga_io_memory); diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h index ed69e06..fcfcc5f 100644 --- a/hw/display/vga_int.h +++ b/hw/display/vga_int.h @@ -151,6 +151,7 @@ typedef struct VGACommonState { 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; @@ -162,6 +163,8 @@ typedef struct VGACommonState { 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 */ diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c index 1751f19..c17ddd1 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -1298,7 +1298,7 @@ static const MemoryRegionOps vmsvga_io_ops = { }, }; -static int pci_vmsvga_initfn(PCIDevice *dev) +static void pci_vmsvga_realize(PCIDevice *dev, Error **errp) { struct pci_vmsvga_state_s *s = VMWARE_SVGA(dev); @@ -1323,8 +1323,6 @@ static int pci_vmsvga_initfn(PCIDevice *dev) /* compatibility with pc-0.13 and older */ vga_init_vbe(&s->chip.vga, OBJECT(dev), pci_address_space(dev)); } - - return 0; } static Property vga_vmware_properties[] = { @@ -1338,7 +1336,7 @@ static void vmsvga_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = pci_vmsvga_initfn; + k->realize = pci_vmsvga_realize; k->romfile = "vgabios-vmware.bin"; k->vendor_id = PCI_VENDOR_ID_VMWARE; k->device_id = SVGA_PCI_DEVICE_ID; diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c index 8a61e95..5e324ef 100644 --- a/hw/display/xenfb.c +++ b/hw/display/xenfb.c @@ -45,6 +45,8 @@ #include #include +#include "trace.h" + #ifndef BTN_LEFT #define BTN_LEFT 0x110 /* from */ #endif @@ -324,6 +326,8 @@ static void xenfb_mouse_event(void *opaque, 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, @@ -380,6 +384,7 @@ static void input_connected(struct XenDevice *xendev) 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"); diff --git a/hw/dma/omap_dma.c b/hw/dma/omap_dma.c index 756a87a..97c57a0 100644 --- a/hw/dma/omap_dma.c +++ b/hw/dma/omap_dma.c @@ -136,7 +136,7 @@ struct omap_dma_s { static inline void omap_dma_interrupts_update(struct omap_dma_s *s) { - return s->intr_update(s); + s->intr_update(s); } static void omap_dma_channel_load(struct omap_dma_channel_s *ch) @@ -1502,7 +1502,8 @@ static void omap_dma_write(void *opaque, hwaddr addr, int reg, ch; if (size != 2) { - return omap_badwidth_write16(opaque, addr, value); + omap_badwidth_write16(opaque, addr, value); + return; } switch (addr) { @@ -1857,7 +1858,8 @@ static void omap_dma4_write(void *opaque, hwaddr addr, struct omap_dma_channel_s *ch; if (size == 1) { - return omap_badwidth_write16(opaque, addr, value); + omap_badwidth_write16(opaque, addr, value); + return; } switch (addr) { diff --git a/hw/dma/pl330.c b/hw/dma/pl330.c index 6b6eaae..5be3df5 100644 --- a/hw/dma/pl330.c +++ b/hw/dma/pl330.c @@ -286,7 +286,7 @@ static const VMStateDescription vmstate_pl330 = { PL330Queue), VMSTATE_STRUCT(write_queue, PL330State, 0, vmstate_pl330_queue, PL330Queue), - VMSTATE_TIMER(timer, PL330State), + VMSTATE_TIMER_PTR(timer, PL330State), VMSTATE_UINT32(inten, PL330State), VMSTATE_UINT32(int_status, PL330State), VMSTATE_UINT32(ev_status, PL330State), @@ -1566,7 +1566,7 @@ static void pl330_realize(DeviceState *dev, Error **errp) s->cfg[1] |= 5; break; default: - error_setg(errp, "Bad value for i-cache_len property: %" PRIx8 "\n", + error_setg(errp, "Bad value for i-cache_len property: %" PRIx8, s->i_cache_len); return; } @@ -1601,7 +1601,7 @@ static void pl330_realize(DeviceState *dev, Error **errp) s->cfg[CFG_CRD] |= 0x4; break; default: - error_setg(errp, "Bad value for data_width property: %" PRIx8 "\n", + error_setg(errp, "Bad value for data_width property: %" PRIx8, s->data_width); return; } diff --git a/hw/gpio/omap_gpio.c b/hw/gpio/omap_gpio.c index 938782a..9a43486 100644 --- a/hw/gpio/omap_gpio.c +++ b/hw/gpio/omap_gpio.c @@ -113,7 +113,8 @@ static void omap_gpio_write(void *opaque, hwaddr addr, int ln; if (size != 2) { - return omap_badwidth_write16(opaque, addr, value); + omap_badwidth_write16(opaque, addr, value); + return; } switch (offset) { diff --git a/hw/i2c/smbus_ich9.c b/hw/i2c/smbus_ich9.c index 0803dc4..91d4d32 100644 --- a/hw/i2c/smbus_ich9.c +++ b/hw/i2c/smbus_ich9.c @@ -71,7 +71,7 @@ static void ich9_smbus_write_config(PCIDevice *d, uint32_t address, } } -static int ich9_smbus_initfn(PCIDevice *d) +static void ich9_smbus_realize(PCIDevice *d, Error **errp) { ICH9SMBState *s = ICH9_SMB_DEVICE(d); @@ -84,7 +84,6 @@ static int ich9_smbus_initfn(PCIDevice *d) pm_smbus_init(&d->qdev, &s->smb); pci_register_bar(d, ICH9_SMB_SMB_BASE_BAR, PCI_BASE_ADDRESS_SPACE_IO, &s->smb.io); - return 0; } static void ich9_smb_class_init(ObjectClass *klass, void *data) @@ -98,7 +97,7 @@ static void ich9_smb_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_SERIAL_SMBUS; dc->vmsd = &vmstate_ich9_smbus; dc->desc = "ICH9 SMBUS Bridge"; - k->init = ich9_smbus_initfn; + k->realize = ich9_smbus_realize; k->config_write = ich9_smbus_write_config; /* * Reason: part of ICH9 southbridge, needs to be wired up by diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs index 9d419ad..e058a39 100644 --- a/hw/i386/Makefile.objs +++ b/hw/i386/Makefile.objs @@ -7,11 +7,8 @@ obj-$(CONFIG_XEN) += ../xenpv/ xen/ obj-y += kvmvapic.o obj-y += acpi-build.o -obj-y += bios-linker-loader.o -hw/i386/acpi-build.o: hw/i386/acpi-build.c hw/i386/acpi-dsdt.hex \ - hw/i386/ssdt-proc.hex hw/i386/ssdt-pcihp.hex hw/i386/ssdt-misc.hex \ +hw/i386/acpi-build.o: hw/i386/acpi-build.c \ hw/i386/acpi-dsdt.hex hw/i386/q35-acpi-dsdt.hex \ - hw/i386/q35-acpi-dsdt.hex hw/i386/ssdt-mem.hex \ hw/i386/ssdt-tpm.hex iasl-option=$(shell if test -z "`$(1) $(2) 2>&1 > /dev/null`" \ diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index b37a397..e761005 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -36,7 +36,7 @@ #include "hw/i386/acpi-defs.h" #include "hw/acpi/acpi.h" #include "hw/nvram/fw_cfg.h" -#include "bios-linker-loader.h" +#include "hw/acpi/bios-linker-loader.h" #include "hw/loader.h" #include "hw/isa/isa.h" #include "hw/acpi/memory_hotplug.h" @@ -54,6 +54,8 @@ #include "hw/i386/q35-acpi-dsdt.hex" #include "hw/i386/acpi-dsdt.hex" +#include "hw/acpi/aml-build.h" + #include "qapi/qmp/qint.h" #include "qom/qom-qobject.h" #include "exec/ram_addr.h" @@ -68,6 +70,17 @@ #define ACPI_BUILD_TABLE_SIZE 0x20000 +/* Reserve RAM space for tables: add another order of magnitude. */ +#define ACPI_BUILD_TABLE_MAX_SIZE 0x200000 + +/* #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 AcpiCpuInfo { DECLARE_BITMAP(found_cpus, ACPI_CPU_HOTPLUG_ID_LIMIT); } AcpiCpuInfo; @@ -88,15 +101,21 @@ typedef struct AcpiPmInfo { 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 has_hpet; bool has_tpm; - DECLARE_BITMAP(slot_hotplug_enable, PCI_SLOT_MAX); const unsigned char *dsdt_code; unsigned dsdt_size; uint16_t pvpanic_port; + uint16_t applesmc_io_base; } AcpiMiscInfo; typedef struct AcpiBuildPciBusHotplugState { @@ -108,7 +127,6 @@ typedef struct AcpiBuildPciBusHotplugState { static void acpi_get_dsdt(AcpiMiscInfo *info) { - uint16_t *applesmc_sta; Object *piix = piix4_pm_find(); Object *lpc = ich9_lpc_find(); assert(!!piix != !!lpc); @@ -116,17 +134,11 @@ static void acpi_get_dsdt(AcpiMiscInfo *info) if (piix) { info->dsdt_code = AcpiDsdtAmlCode; info->dsdt_size = sizeof AcpiDsdtAmlCode; - applesmc_sta = piix_dsdt_applesmc_sta; } if (lpc) { info->dsdt_code = Q35AcpiDsdtAmlCode; info->dsdt_size = sizeof Q35AcpiDsdtAmlCode; - applesmc_sta = q35_dsdt_applesmc_sta; } - - /* Patch in appropriate value for AppleSMC _STA */ - *(uint8_t *)(info->dsdt_code + *applesmc_sta) = - applesmc_find() ? 0x0b : 0x00; } static @@ -161,14 +173,26 @@ static void acpi_get_pm_info(AcpiPmInfo *pm) Object *obj = NULL; QObject *o; + 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) { @@ -217,6 +241,7 @@ static void acpi_get_misc_info(AcpiMiscInfo *info) info->has_hpet = hpet_find(); info->has_tpm = tpm_find(); info->pvpanic_port = pvpanic_port(); + info->applesmc_io_base = applesmc_port(); } static void acpi_get_pci_info(PcPciInfo *info) @@ -246,8 +271,6 @@ static void acpi_get_pci_info(PcPciInfo *info) #define ACPI_BUILD_APPNAME6 "BOCHS " #define ACPI_BUILD_APPNAME4 "BXPC" -#define ACPI_BUILD_DPRINTF(level, fmt, ...) do {} while (0) - #define ACPI_BUILD_TABLE_FILE "etc/acpi/tables" #define ACPI_BUILD_RSDP_FILE "etc/acpi/rsdp" #define ACPI_BUILD_TPMLOG_FILE "etc/tpm/log" @@ -271,203 +294,6 @@ build_header(GArray *linker, GArray *table_data, table_data->data, h, len, &h->checksum); } -static inline GArray *build_alloc_array(void) -{ - return g_array_new(false, true /* clear */, 1); -} - -static inline void build_free_array(GArray *array) -{ - g_array_free(array, true); -} - -static inline void build_prepend_byte(GArray *array, uint8_t val) -{ - g_array_prepend_val(array, val); -} - -static inline void build_append_byte(GArray *array, uint8_t val) -{ - g_array_append_val(array, val); -} - -static inline void build_append_array(GArray *array, GArray *val) -{ - g_array_append_vals(array, val->data, val->len); -} - -static void GCC_FMT_ATTR(2, 3) -build_append_nameseg(GArray *array, const char *format, ...) -{ - /* It would be nicer to use g_string_vprintf but it's only there in 2.22 */ - char s[] = "XXXX"; - int len; - va_list args; - - va_start(args, format); - len = vsnprintf(s, sizeof s, format, args); - va_end(args); - - assert(len == 4); - g_array_append_vals(array, s, len); -} - -/* 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 min_bytes) -{ - uint8_t byte; - unsigned length = package->len; - 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; - } - - /* Force length to at least min_bytes. - * This wastes memory but that's how bios did it. - */ - length_bytes = MAX(length_bytes, min_bytes); - - /* PkgLength is the length of the inclusive length of the data. */ - 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_package(GArray *package, uint8_t op, unsigned min_bytes) -{ - build_prepend_package_length(package, min_bytes); - build_prepend_byte(package, op); -} - -static void build_extop_package(GArray *package, uint8_t op) -{ - build_package(package, op, 1); - build_prepend_byte(package, 0x5B); /* ExtOpPrefix */ -} - -static void build_append_value(GArray *table, uint32_t value, int size) -{ - uint8_t prefix; - int i; - - switch (size) { - case 1: - prefix = 0x0A; /* BytePrefix */ - break; - case 2: - prefix = 0x0B; /* WordPrefix */ - break; - case 4: - prefix = 0x0C; /* DWordPrefix */ - break; - default: - assert(0); - return; - } - build_append_byte(table, prefix); - for (i = 0; i < size; ++i) { - build_append_byte(table, value & 0xFF); - value = value >> 8; - } -} - -static void build_append_int(GArray *table, uint32_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_value(table, value, 1); - } else if (value <= 0xFFFF) { - build_append_value(table, value, 2); - } else { - build_append_value(table, value, 4); - } -} - -static GArray *build_alloc_method(const char *name, uint8_t arg_count) -{ - GArray *method = build_alloc_array(); - - build_append_nameseg(method, "%s", name); - build_append_byte(method, arg_count); /* MethodFlags: ArgCount */ - - return method; -} - -static void build_append_and_cleanup_method(GArray *device, GArray *method) -{ - uint8_t op = 0x14; /* MethodOp */ - - build_package(method, op, 0); - - build_append_array(device, method); - build_free_array(method); -} - -static void build_append_notify_target_ifequal(GArray *method, - GArray *target_name, - uint32_t value, int size) -{ - GArray *notify = build_alloc_array(); - uint8_t op = 0xA0; /* IfOp */ - - build_append_byte(notify, 0x93); /* LEqualOp */ - build_append_byte(notify, 0x68); /* Arg0Op */ - build_append_value(notify, value, size); - build_append_byte(notify, 0x86); /* NotifyOp */ - build_append_array(notify, target_name); - build_append_byte(notify, 0x69); /* Arg1Op */ - - /* Pack it up */ - build_package(notify, op, 1); - - build_append_array(method, notify); - - build_free_array(notify); -} - /* End here */ #define ACPI_PORT_SMI_CMD 0x00b2 /* TODO: this is APM_CNT_IOPORT */ @@ -494,24 +320,6 @@ static void acpi_align_size(GArray *blob, unsigned align) g_array_set_size(blob, ROUND_UP(acpi_data_len(blob), align)); } -/* Set a value within table in a safe manner */ -#define ACPI_BUILD_SET_LE(table, size, off, bits, val) \ - do { \ - uint64_t ACPI_BUILD_SET_LE_val = cpu_to_le64(val); \ - memcpy(acpi_data_get_ptr(table, size, off, \ - (bits) / BITS_PER_BYTE), \ - &ACPI_BUILD_SET_LE_val, \ - (bits) / BITS_PER_BYTE); \ - } while (0) - -static inline void *acpi_data_get_ptr(uint8_t *table_data, unsigned table_size, - unsigned off, unsigned size) -{ - assert(off + size > off); - assert(off + size <= table_size); - return table_data + off; -} - static inline void acpi_add_table(GArray *table_offsets, GArray *table_data) { uint32_t offset = cpu_to_le32(table_data->len); @@ -659,115 +467,8 @@ build_madt(GArray *table_data, GArray *linker, AcpiCpuInfo *cpu, table_data->len - madt_start, 1); } -/* Encode a hex value */ -static inline char acpi_get_hex(uint32_t val) -{ - val &= 0x0f; - return (val <= 9) ? ('0' + val) : ('A' + val - 10); -} - -#include "hw/i386/ssdt-proc.hex" - -/* 0x5B 0x83 ProcessorOp PkgLength NameString ProcID */ -#define ACPI_PROC_OFFSET_CPUHEX (*ssdt_proc_name - *ssdt_proc_start + 2) -#define ACPI_PROC_OFFSET_CPUID1 (*ssdt_proc_name - *ssdt_proc_start + 4) -#define ACPI_PROC_OFFSET_CPUID2 (*ssdt_proc_id - *ssdt_proc_start) -#define ACPI_PROC_SIZEOF (*ssdt_proc_end - *ssdt_proc_start) -#define ACPI_PROC_AML (ssdp_proc_aml + *ssdt_proc_start) - -/* 0x5B 0x82 DeviceOp PkgLength NameString */ -#define ACPI_PCIHP_OFFSET_HEX (*ssdt_pcihp_name - *ssdt_pcihp_start + 1) -#define ACPI_PCIHP_OFFSET_ID (*ssdt_pcihp_id - *ssdt_pcihp_start) -#define ACPI_PCIHP_OFFSET_ADR (*ssdt_pcihp_adr - *ssdt_pcihp_start) -#define ACPI_PCIHP_OFFSET_EJ0 (*ssdt_pcihp_ej0 - *ssdt_pcihp_start) -#define ACPI_PCIHP_SIZEOF (*ssdt_pcihp_end - *ssdt_pcihp_start) -#define ACPI_PCIHP_AML (ssdp_pcihp_aml + *ssdt_pcihp_start) - -#define ACPI_PCINOHP_OFFSET_HEX (*ssdt_pcinohp_name - *ssdt_pcinohp_start + 1) -#define ACPI_PCINOHP_OFFSET_ADR (*ssdt_pcinohp_adr - *ssdt_pcinohp_start) -#define ACPI_PCINOHP_SIZEOF (*ssdt_pcinohp_end - *ssdt_pcinohp_start) -#define ACPI_PCINOHP_AML (ssdp_pcihp_aml + *ssdt_pcinohp_start) - -#define ACPI_PCIVGA_OFFSET_HEX (*ssdt_pcivga_name - *ssdt_pcivga_start + 1) -#define ACPI_PCIVGA_OFFSET_ADR (*ssdt_pcivga_adr - *ssdt_pcivga_start) -#define ACPI_PCIVGA_SIZEOF (*ssdt_pcivga_end - *ssdt_pcivga_start) -#define ACPI_PCIVGA_AML (ssdp_pcihp_aml + *ssdt_pcivga_start) - -#define ACPI_PCIQXL_OFFSET_HEX (*ssdt_pciqxl_name - *ssdt_pciqxl_start + 1) -#define ACPI_PCIQXL_OFFSET_ADR (*ssdt_pciqxl_adr - *ssdt_pciqxl_start) -#define ACPI_PCIQXL_SIZEOF (*ssdt_pciqxl_end - *ssdt_pciqxl_start) -#define ACPI_PCIQXL_AML (ssdp_pcihp_aml + *ssdt_pciqxl_start) - -#include "hw/i386/ssdt-mem.hex" - -/* 0x5B 0x82 DeviceOp PkgLength NameString DimmID */ -#define ACPI_MEM_OFFSET_HEX (*ssdt_mem_name - *ssdt_mem_start + 2) -#define ACPI_MEM_OFFSET_ID (*ssdt_mem_id - *ssdt_mem_start + 7) -#define ACPI_MEM_SIZEOF (*ssdt_mem_end - *ssdt_mem_start) -#define ACPI_MEM_AML (ssdm_mem_aml + *ssdt_mem_start) - -#define ACPI_SSDT_SIGNATURE 0x54445353 /* SSDT */ -#define ACPI_SSDT_HEADER_LENGTH 36 - -#include "hw/i386/ssdt-misc.hex" -#include "hw/i386/ssdt-pcihp.hex" #include "hw/i386/ssdt-tpm.hex" -static void -build_append_notify_method(GArray *device, const char *name, - const char *format, int count) -{ - int i; - GArray *method = build_alloc_method(name, 2); - - for (i = 0; i < count; i++) { - GArray *target = build_alloc_array(); - build_append_nameseg(target, format, i); - assert(i < 256); /* Fits in 1 byte */ - build_append_notify_target_ifequal(method, target, i, 1); - build_free_array(target); - } - - build_append_and_cleanup_method(device, method); -} - -static void patch_pcihp(int slot, uint8_t *ssdt_ptr) -{ - unsigned devfn = PCI_DEVFN(slot, 0); - - ssdt_ptr[ACPI_PCIHP_OFFSET_HEX] = acpi_get_hex(devfn >> 4); - ssdt_ptr[ACPI_PCIHP_OFFSET_HEX + 1] = acpi_get_hex(devfn); - ssdt_ptr[ACPI_PCIHP_OFFSET_ID] = slot; - ssdt_ptr[ACPI_PCIHP_OFFSET_ADR + 2] = slot; -} - -static void patch_pcinohp(int slot, uint8_t *ssdt_ptr) -{ - unsigned devfn = PCI_DEVFN(slot, 0); - - ssdt_ptr[ACPI_PCINOHP_OFFSET_HEX] = acpi_get_hex(devfn >> 4); - ssdt_ptr[ACPI_PCINOHP_OFFSET_HEX + 1] = acpi_get_hex(devfn); - ssdt_ptr[ACPI_PCINOHP_OFFSET_ADR + 2] = slot; -} - -static void patch_pcivga(int slot, uint8_t *ssdt_ptr) -{ - unsigned devfn = PCI_DEVFN(slot, 0); - - ssdt_ptr[ACPI_PCIVGA_OFFSET_HEX] = acpi_get_hex(devfn >> 4); - ssdt_ptr[ACPI_PCIVGA_OFFSET_HEX + 1] = acpi_get_hex(devfn); - ssdt_ptr[ACPI_PCIVGA_OFFSET_ADR + 2] = slot; -} - -static void patch_pciqxl(int slot, uint8_t *ssdt_ptr) -{ - unsigned devfn = PCI_DEVFN(slot, 0); - - ssdt_ptr[ACPI_PCIQXL_OFFSET_HEX] = acpi_get_hex(devfn >> 4); - ssdt_ptr[ACPI_PCIQXL_OFFSET_HEX + 1] = acpi_get_hex(devfn); - ssdt_ptr[ACPI_PCIQXL_OFFSET_ADR + 2] = slot; -} - /* Assign BSEL property to all buses. In the future, this can be changed * to only assign to buses that support hotplug. */ @@ -798,261 +499,155 @@ static void acpi_set_pci_info(void) } } -static void build_pci_bus_state_init(AcpiBuildPciBusHotplugState *state, - AcpiBuildPciBusHotplugState *parent, - bool pcihp_bridge_en) -{ - state->parent = parent; - state->device_table = build_alloc_array(); - state->notify_table = build_alloc_array(); - state->pcihp_bridge_en = pcihp_bridge_en; -} - -static void build_pci_bus_state_cleanup(AcpiBuildPciBusHotplugState *state) +static void build_append_pcihp_notify_entry(Aml *method, int slot) { - build_free_array(state->device_table); - build_free_array(state->notify_table); -} + Aml *if_ctx; + int32_t devfn = PCI_DEVFN(slot, 0); -static void *build_pci_bus_begin(PCIBus *bus, void *parent_state) -{ - AcpiBuildPciBusHotplugState *parent = parent_state; - AcpiBuildPciBusHotplugState *child = g_malloc(sizeof *child); - - build_pci_bus_state_init(child, parent, parent->pcihp_bridge_en); - - return child; + if_ctx = aml_if(aml_and(aml_arg(0), aml_int(0x1U << slot))); + aml_append(if_ctx, aml_notify(aml_name("S%.02X", devfn), aml_arg(1))); + aml_append(method, if_ctx); } -static void build_pci_bus_end(PCIBus *bus, void *bus_state) +static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus, + bool pcihp_bridge_en) { - AcpiBuildPciBusHotplugState *child = bus_state; - AcpiBuildPciBusHotplugState *parent = child->parent; - GArray *bus_table = build_alloc_array(); - DECLARE_BITMAP(slot_hotplug_enable, PCI_SLOT_MAX); - DECLARE_BITMAP(slot_device_present, PCI_SLOT_MAX); - DECLARE_BITMAP(slot_device_system, PCI_SLOT_MAX); - DECLARE_BITMAP(slot_device_vga, PCI_SLOT_MAX); - DECLARE_BITMAP(slot_device_qxl, PCI_SLOT_MAX); - uint8_t op; - int i; + Aml *dev, *notify_method, *method; QObject *bsel; - GArray *method; - bool bus_hotplug_support = false; - - /* - * Skip bridge subtree creation if bridge hotplug is disabled - * to make acpi tables compatible with legacy machine types. - */ - if (!child->pcihp_bridge_en && bus->parent_dev) { - return; - } - - if (bus->parent_dev) { - op = 0x82; /* DeviceOp */ - build_append_nameseg(bus_table, "S%.02X_", - bus->parent_dev->devfn); - build_append_byte(bus_table, 0x08); /* NameOp */ - build_append_nameseg(bus_table, "_SUN"); - build_append_value(bus_table, PCI_SLOT(bus->parent_dev->devfn), 1); - build_append_byte(bus_table, 0x08); /* NameOp */ - build_append_nameseg(bus_table, "_ADR"); - build_append_value(bus_table, (PCI_SLOT(bus->parent_dev->devfn) << 16) | - PCI_FUNC(bus->parent_dev->devfn), 4); - } else { - op = 0x10; /* ScopeOp */; - build_append_nameseg(bus_table, "PCI0"); - } + PCIBus *sec; + int i; bsel = object_property_get_qobject(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, NULL); if (bsel) { - build_append_byte(bus_table, 0x08); /* NameOp */ - build_append_nameseg(bus_table, "BSEL"); - build_append_int(bus_table, qint_get_int(qobject_to_qint(bsel))); - memset(slot_hotplug_enable, 0xff, sizeof slot_hotplug_enable); - } else { - /* No bsel - no slots are hot-pluggable */ - memset(slot_hotplug_enable, 0x00, sizeof slot_hotplug_enable); - } + int64_t bsel_val = qint_get_int(qobject_to_qint(bsel)); - memset(slot_device_present, 0x00, sizeof slot_device_present); - memset(slot_device_system, 0x00, sizeof slot_device_present); - memset(slot_device_vga, 0x00, sizeof slot_device_vga); - memset(slot_device_qxl, 0x00, sizeof slot_device_qxl); + aml_append(parent_scope, aml_name_decl("BSEL", aml_int(bsel_val))); + notify_method = aml_method("DVNT", 2); + } 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_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; } - set_bit(slot, slot_device_present); 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 && child->pcihp_bridge_en; + 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 || bridge_in_acpi) { - set_bit(slot, slot_device_system); + 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) { - set_bit(slot, slot_device_vga); + /* add VGA specific AML methods */ + int s3d; if (object_dynamic_cast(OBJECT(pdev), "qxl-vga")) { - set_bit(slot, slot_device_qxl); + s3d = 3; + } else { + s3d = 0; } - } - if (!dc->hotpluggable || bridge_in_acpi) { - clear_bit(slot, slot_hotplug_enable); - } - } + method = aml_method("_S1D", 0); + aml_append(method, aml_return(aml_int(0))); + aml_append(dev, method); + + method = aml_method("_S2D", 0); + aml_append(method, aml_return(aml_int(0))); + aml_append(dev, method); + + method = aml_method("_S3D", 0); + 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_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)); - /* Append Device object for each slot */ - for (i = 0; i < PCI_SLOT_MAX; i++) { - bool can_eject = test_bit(i, slot_hotplug_enable); - bool present = test_bit(i, slot_device_present); - bool vga = test_bit(i, slot_device_vga); - bool qxl = test_bit(i, slot_device_qxl); - bool system = test_bit(i, slot_device_system); - if (can_eject) { - void *pcihp = acpi_data_push(bus_table, - ACPI_PCIHP_SIZEOF); - memcpy(pcihp, ACPI_PCIHP_AML, ACPI_PCIHP_SIZEOF); - patch_pcihp(i, pcihp); - bus_hotplug_support = true; - } else if (qxl) { - void *pcihp = acpi_data_push(bus_table, - ACPI_PCIQXL_SIZEOF); - memcpy(pcihp, ACPI_PCIQXL_AML, ACPI_PCIQXL_SIZEOF); - patch_pciqxl(i, pcihp); - } else if (vga) { - void *pcihp = acpi_data_push(bus_table, - ACPI_PCIVGA_SIZEOF); - memcpy(pcihp, ACPI_PCIVGA_AML, ACPI_PCIVGA_SIZEOF); - patch_pcivga(i, pcihp); - } else if (system) { - /* Nothing to do: system devices are in DSDT or in SSDT above. */ - } else if (present) { - void *pcihp = acpi_data_push(bus_table, - ACPI_PCINOHP_SIZEOF); - memcpy(pcihp, ACPI_PCINOHP_AML, ACPI_PCINOHP_SIZEOF); - patch_pcinohp(i, pcihp); + 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) { - method = build_alloc_method("DVNT", 2); - - for (i = 0; i < PCI_SLOT_MAX; i++) { - GArray *notify; - uint8_t op; - - if (!test_bit(i, slot_hotplug_enable)) { - continue; - } - - notify = build_alloc_array(); - op = 0xA0; /* IfOp */ - - build_append_byte(notify, 0x7B); /* AndOp */ - build_append_byte(notify, 0x68); /* Arg0Op */ - build_append_int(notify, 0x1U << i); - build_append_byte(notify, 0x00); /* NullName */ - build_append_byte(notify, 0x86); /* NotifyOp */ - build_append_nameseg(notify, "S%.02X_", PCI_DEVFN(i, 0)); - build_append_byte(notify, 0x69); /* Arg1Op */ - - /* Pack it up */ - build_package(notify, op, 0); - - build_append_array(method, notify); - - build_free_array(notify); - } - - build_append_and_cleanup_method(bus_table, method); + 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. */ - if (bus_hotplug_support || child->notify_table->len || !bus->parent_dev) { - method = build_alloc_method("PCNT", 0); - - /* If bus supports hotplug select it and notify about local events */ - if (bsel) { - build_append_byte(method, 0x70); /* StoreOp */ - build_append_int(method, qint_get_int(qobject_to_qint(bsel))); - build_append_nameseg(method, "BNUM"); - build_append_nameseg(method, "DVNT"); - build_append_nameseg(method, "PCIU"); - build_append_int(method, 1); /* Device Check */ - build_append_nameseg(method, "DVNT"); - build_append_nameseg(method, "PCID"); - build_append_int(method, 3); /* Eject Request */ - } + method = aml_method("PCNT", 0); - /* Notify about child bus events in any case */ - build_append_array(method, child->notify_table); - - build_append_and_cleanup_method(bus_table, method); - - /* Append description of child buses */ - build_append_array(bus_table, child->device_table); - - /* Pack it up */ - if (bus->parent_dev) { - build_extop_package(bus_table, op); - } else { - build_package(bus_table, op, 0); - } - - /* Append our bus description to parent table */ - build_append_array(parent->device_table, bus_table); - - /* Also tell parent how to notify us, invoking PCNT method. - * At the moment this is not needed for root as we have a single root. - */ - if (bus->parent_dev) { - build_append_byte(parent->notify_table, '^'); /* ParentPrefixChar */ - build_append_byte(parent->notify_table, 0x2E); /* DualNamePrefix */ - build_append_nameseg(parent->notify_table, "S%.02X_", - bus->parent_dev->devfn); - build_append_nameseg(parent->notify_table, "PCNT"); - } + /* 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 */) + ); } - qobject_decref(bsel); - build_free_array(bus_table); - build_pci_bus_state_cleanup(child); - g_free(child); -} - -static void patch_pci_windows(PcPciInfo *pci, uint8_t *start, unsigned size) -{ - ACPI_BUILD_SET_LE(start, size, acpi_pci32_start[0], 32, pci->w32.begin); - - ACPI_BUILD_SET_LE(start, size, acpi_pci32_end[0], 32, pci->w32.end - 1); + /* 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; - if (pci->w64.end || pci->w64.begin) { - ACPI_BUILD_SET_LE(start, size, acpi_pci64_valid[0], 8, 1); - ACPI_BUILD_SET_LE(start, size, acpi_pci64_start[0], 64, pci->w64.begin); - ACPI_BUILD_SET_LE(start, size, acpi_pci64_end[0], 64, pci->w64.end - 1); - ACPI_BUILD_SET_LE(start, size, acpi_pci64_length[0], 64, pci->w64.end - pci->w64.begin); - } else { - ACPI_BUILD_SET_LE(start, size, acpi_pci64_valid[0], 8, 0); + aml_append(method, aml_name("^S%.02X.PCNT", devfn)); + } } + aml_append(parent_scope, method); } static void @@ -1063,112 +658,337 @@ build_ssdt(GArray *table_data, GArray *linker, MachineState *machine = MACHINE(qdev_get_machine()); uint32_t nr_mem = machine->ram_slots; unsigned acpi_cpus = guest_info->apic_id_limit; - int ssdt_start = table_data->len; - uint8_t *ssdt_ptr; + Aml *ssdt, *sb_scope, *scope, *pkg, *dev, *method, *crs, *field, *ifctx; int i; + ssdt = init_aml_allocator(); /* The current AML generator can cover the APIC ID range [0..255], * inclusive, for VCPU hotplug. */ QEMU_BUILD_BUG_ON(ACPI_CPU_HOTPLUG_ID_LIMIT > 256); g_assert(acpi_cpus <= ACPI_CPU_HOTPLUG_ID_LIMIT); - /* Copy header and patch values in the S3_ / S4_ / S5_ packages */ - ssdt_ptr = acpi_data_push(table_data, sizeof(ssdp_misc_aml)); - memcpy(ssdt_ptr, ssdp_misc_aml, sizeof(ssdp_misc_aml)); - if (pm->s3_disabled) { - ssdt_ptr[acpi_s3_name[0]] = 'X'; + /* Reserve space for header */ + acpi_data_push(ssdt->buf, sizeof(AcpiTableHeader)); + + 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, 0x0000, 0x00FF, 0x0000, 0x0100)); + 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)); + aml_append(crs, + aml_word_io(aml_min_fixed, aml_max_fixed, + aml_pos_decode, aml_entire_range, + 0x0000, 0x0D00, 0xFFFF, 0x0000, 0xF300)); + aml_append(crs, + aml_dword_memory(aml_pos_decode, aml_min_fixed, aml_max_fixed, + aml_cacheable, aml_ReadWrite, + 0, 0x000A0000, 0x000BFFFF, 0, 0x00020000)); + aml_append(crs, + aml_dword_memory(aml_pos_decode, aml_min_fixed, aml_max_fixed, + aml_non_cacheable, aml_ReadWrite, + 0, pci->w32.begin, pci->w32.end - 1, 0, + pci->w32.end - pci->w32.begin)); + if (pci->w64.begin) { + aml_append(crs, + aml_qword_memory(aml_pos_decode, aml_min_fixed, aml_max_fixed, + aml_cacheable, aml_ReadWrite, + 0, pci->w64.begin, pci->w64.end - 1, 0, + pci->w64.end - pci->w64.begin)); } - if (pm->s4_disabled) { - ssdt_ptr[acpi_s4_name[0]] = 'X'; - } else { - ssdt_ptr[acpi_s4_pkg[0] + 1] = ssdt_ptr[acpi_s4_pkg[0] + 3] = - pm->s4_val; + 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); + + /* 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(ssdt, 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)); } - patch_pci_windows(pci, ssdt_ptr, sizeof(ssdp_misc_aml)); + 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(ssdt, 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(ssdt, scope); + } - ACPI_BUILD_SET_LE(ssdt_ptr, sizeof(ssdp_misc_aml), - ssdt_isa_pest[0], 16, misc->pvpanic_port); + if (misc->pvpanic_port) { + scope = aml_scope("\\_SB.PCI0.ISA"); - ACPI_BUILD_SET_LE(ssdt_ptr, sizeof(ssdp_misc_aml), - ssdt_mctrl_nr_slots[0], 32, nr_mem); + dev = aml_device("PEVR"); + aml_append(dev, aml_name_decl("_HID", aml_string("QEMU0001"))); - { - GArray *sb_scope = build_alloc_array(); - uint8_t op = 0x10; /* ScopeOp */ + 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, + misc->pvpanic_port, 1)); + field = aml_field("PEOR", aml_byte_acc); + aml_append(field, aml_named_field("PEPT", 8)); + aml_append(dev, field); + + method = aml_method("RDPT", 0); + aml_append(method, aml_store(aml_name("PEPT"), aml_local(0))); + aml_append(method, aml_return(aml_local(0))); + aml_append(dev, method); - build_append_nameseg(sb_scope, "_SB_"); + method = aml_method("WRPT", 1); + aml_append(method, aml_store(aml_arg(0), aml_name("PEPT"))); + aml_append(dev, method); + + aml_append(scope, dev); + aml_append(ssdt, scope); + } + + sb_scope = aml_scope("_SB"); + { + /* create PCI0.PRES device and its _CRS to reserve CPU hotplug MMIO */ + dev = aml_device("PCI0." stringify(CPU_HOTPLUG_RESOURCE_DEVICE)); + aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A06"))); + aml_append(dev, + aml_name_decl("_UID", aml_string("CPU Hotplug resources")) + ); + /* device present, functioning, decoding, not shown in UI */ + aml_append(dev, aml_name_decl("_STA", aml_int(0xB))); + crs = aml_resource_template(); + aml_append(crs, + aml_io(aml_decode16, pm->cpu_hp_io_base, pm->cpu_hp_io_base, 1, + pm->cpu_hp_io_len) + ); + aml_append(dev, aml_name_decl("_CRS", crs)); + aml_append(sb_scope, dev); + /* declare CPU hotplug MMIO region and PRS field to access it */ + aml_append(sb_scope, aml_operation_region( + "PRST", aml_system_io, pm->cpu_hp_io_base, pm->cpu_hp_io_len)); + field = aml_field("PRST", aml_byte_acc); + aml_append(field, aml_named_field("PRS", 256)); + aml_append(sb_scope, field); /* build Processor object for each processor */ for (i = 0; i < acpi_cpus; i++) { - uint8_t *proc = acpi_data_push(sb_scope, ACPI_PROC_SIZEOF); - memcpy(proc, ACPI_PROC_AML, ACPI_PROC_SIZEOF); - proc[ACPI_PROC_OFFSET_CPUHEX] = acpi_get_hex(i >> 4); - proc[ACPI_PROC_OFFSET_CPUHEX+1] = acpi_get_hex(i); - proc[ACPI_PROC_OFFSET_CPUID1] = i; - proc[ACPI_PROC_OFFSET_CPUID2] = i; + dev = aml_processor(i, 0, 0, "CP%.02X", i); + + method = aml_method("_MAT", 0); + aml_append(method, aml_return(aml_call1("CPMA", aml_int(i)))); + aml_append(dev, method); + + method = aml_method("_STA", 0); + aml_append(method, aml_return(aml_call1("CPST", aml_int(i)))); + aml_append(dev, method); + + method = aml_method("_EJ0", 1); + aml_append(method, + aml_return(aml_call2("CPEJ", aml_int(i), aml_arg(0))) + ); + aml_append(dev, method); + + aml_append(sb_scope, dev); } /* build this code: * Method(NTFY, 2) {If (LEqual(Arg0, 0x00)) {Notify(CP00, Arg1)} ...} */ /* Arg0 = Processor ID = APIC ID */ - build_append_notify_method(sb_scope, "NTFY", "CP%0.02X", acpi_cpus); - - /* build "Name(CPON, Package() { One, One, ..., Zero, Zero, ... })" */ - build_append_byte(sb_scope, 0x08); /* NameOp */ - build_append_nameseg(sb_scope, "CPON"); - - { - GArray *package = build_alloc_array(); - uint8_t op; - - /* - * Note: The ability to create variable-sized packages was first introduced in ACPI 2.0. ACPI 1.0 only - * allowed fixed-size packages with up to 255 elements. - * Windows guests up to win2k8 fail when VarPackageOp is used. - */ - if (acpi_cpus <= 255) { - op = 0x12; /* PackageOp */ - build_append_byte(package, acpi_cpus); /* NumElements */ - } else { - op = 0x13; /* VarPackageOp */ - build_append_int(package, acpi_cpus); /* VarNumElements */ - } - - for (i = 0; i < acpi_cpus; i++) { - uint8_t b = test_bit(i, cpu->found_cpus) ? 0x01 : 0x00; - build_append_byte(package, b); - } - - build_package(package, op, 2); - build_append_array(sb_scope, package); - build_free_array(package); + method = aml_method("NTFY", 2); + for (i = 0; i < acpi_cpus; i++) { + ifctx = aml_if(aml_equal(aml_arg(0), aml_int(i))); + aml_append(ifctx, + aml_notify(aml_name("CP%.02X", i), aml_arg(1)) + ); + aml_append(method, ifctx); } + aml_append(sb_scope, method); + + /* build "Name(CPON, Package() { One, One, ..., Zero, Zero, ... })" + * + * Note: The ability to create variable-sized packages was first + * introduced in ACPI 2.0. ACPI 1.0 only allowed fixed-size packages + * ith up to 255 elements. Windows guests up to win2k8 fail when + * VarPackageOp is used. + */ + pkg = acpi_cpus <= 255 ? aml_package(acpi_cpus) : + aml_varpackage(acpi_cpus); - if (nr_mem) { - assert(nr_mem <= ACPI_MAX_RAM_SLOTS); - /* build memory devices */ - for (i = 0; i < nr_mem; i++) { - char id[3]; - uint8_t *mem = acpi_data_push(sb_scope, ACPI_MEM_SIZEOF); - - snprintf(id, sizeof(id), "%02X", i); - memcpy(mem, ACPI_MEM_AML, ACPI_MEM_SIZEOF); - memcpy(mem + ACPI_MEM_OFFSET_HEX, id, 2); - memcpy(mem + ACPI_MEM_OFFSET_ID, id, 2); - } + for (i = 0; i < acpi_cpus; i++) { + uint8_t b = test_bit(i, cpu->found_cpus) ? 0x01 : 0x00; + aml_append(pkg, aml_int(b)); + } + aml_append(sb_scope, aml_name_decl("CPON", pkg)); + + /* build memory devices */ + assert(nr_mem <= ACPI_MAX_RAM_SLOTS); + scope = aml_scope("\\_SB.PCI0." stringify(MEMORY_HOTPLUG_DEVICE)); + aml_append(scope, + aml_name_decl(stringify(MEMORY_SLOTS_NUMBER), aml_int(nr_mem)) + ); + + crs = aml_resource_template(); + aml_append(crs, + aml_io(aml_decode16, pm->mem_hp_io_base, pm->mem_hp_io_base, 0, + pm->mem_hp_io_len) + ); + aml_append(scope, aml_name_decl("_CRS", crs)); + + aml_append(scope, aml_operation_region( + stringify(MEMORY_HOTPLUG_IO_REGION), aml_system_io, + pm->mem_hp_io_base, pm->mem_hp_io_len) + ); + + field = aml_field(stringify(MEMORY_HOTPLUG_IO_REGION), aml_dword_acc); + aml_append(field, /* read only */ + aml_named_field(stringify(MEMORY_SLOT_ADDR_LOW), 32)); + aml_append(field, /* read only */ + aml_named_field(stringify(MEMORY_SLOT_ADDR_HIGH), 32)); + aml_append(field, /* read only */ + aml_named_field(stringify(MEMORY_SLOT_SIZE_LOW), 32)); + aml_append(field, /* read only */ + aml_named_field(stringify(MEMORY_SLOT_SIZE_HIGH), 32)); + aml_append(field, /* read only */ + aml_named_field(stringify(MEMORY_SLOT_PROXIMITY), 32)); + aml_append(scope, field); + + field = aml_field(stringify(MEMORY_HOTPLUG_IO_REGION), aml_byte_acc); + aml_append(field, aml_reserved_field(160 /* bits, Offset(20) */)); + aml_append(field, /* 1 if enabled, read only */ + aml_named_field(stringify(MEMORY_SLOT_ENABLED), 1)); + aml_append(field, + /*(read) 1 if has a insert event. (write) 1 to clear event */ + aml_named_field(stringify(MEMORY_SLOT_INSERT_EVENT), 1)); + aml_append(scope, field); + + field = aml_field(stringify(MEMORY_HOTPLUG_IO_REGION), aml_dword_acc); + aml_append(field, /* DIMM selector, write only */ + aml_named_field(stringify(MEMORY_SLOT_SLECTOR), 32)); + aml_append(field, /* _OST event code, write only */ + aml_named_field(stringify(MEMORY_SLOT_OST_EVENT), 32)); + aml_append(field, /* _OST status code, write only */ + aml_named_field(stringify(MEMORY_SLOT_OST_STATUS), 32)); + aml_append(scope, field); + + aml_append(sb_scope, scope); + + for (i = 0; i < nr_mem; i++) { + #define BASEPATH "\\_SB.PCI0." stringify(MEMORY_HOTPLUG_DEVICE) "." + const char *s; + + dev = aml_device("MP%02X", i); + aml_append(dev, aml_name_decl("_UID", aml_string("0x%02X", i))); + aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0C80"))); + + method = aml_method("_CRS", 0); + s = BASEPATH stringify(MEMORY_SLOT_CRS_METHOD); + aml_append(method, aml_return(aml_call1(s, aml_name("_UID")))); + aml_append(dev, method); + + method = aml_method("_STA", 0); + s = BASEPATH stringify(MEMORY_SLOT_STATUS_METHOD); + aml_append(method, aml_return(aml_call1(s, aml_name("_UID")))); + aml_append(dev, method); + + method = aml_method("_PXM", 0); + s = BASEPATH stringify(MEMORY_SLOT_PROXIMITY_METHOD); + aml_append(method, aml_return(aml_call1(s, aml_name("_UID")))); + aml_append(dev, method); + + method = aml_method("_OST", 3); + s = BASEPATH stringify(MEMORY_SLOT_OST_METHOD); + aml_append(method, aml_return(aml_call4( + s, aml_name("_UID"), aml_arg(0), aml_arg(1), aml_arg(2) + ))); + aml_append(dev, method); + + aml_append(sb_scope, dev); + } - /* build Method(MEMORY_SLOT_NOTIFY_METHOD, 2) { - * If (LEqual(Arg0, 0x00)) {Notify(MP00, Arg1)} ... - */ - build_append_notify_method(sb_scope, - stringify(MEMORY_SLOT_NOTIFY_METHOD), - "MP%0.02X", nr_mem); + /* build Method(MEMORY_SLOT_NOTIFY_METHOD, 2) { + * If (LEqual(Arg0, 0x00)) {Notify(MP00, Arg1)} ... + */ + method = aml_method(stringify(MEMORY_SLOT_NOTIFY_METHOD), 2); + for (i = 0; i < nr_mem; i++) { + ifctx = aml_if(aml_equal(aml_arg(0), aml_int(i))); + aml_append(ifctx, + aml_notify(aml_name("MP%.02X", i), aml_arg(1)) + ); + aml_append(method, ifctx); } + aml_append(sb_scope, method); { - AcpiBuildPciBusHotplugState hotplug_state; Object *pci_host; PCIBus *bus = NULL; bool ambiguous; @@ -1178,26 +998,22 @@ build_ssdt(GArray *table_data, GArray *linker, bus = PCI_HOST_BRIDGE(pci_host)->bus; } - build_pci_bus_state_init(&hotplug_state, NULL, pm->pcihp_bridge_en); - if (bus) { + Aml *scope = aml_scope("PCI0"); /* Scan all PCI buses. Generate tables to support hotplug. */ - pci_for_each_bus_depth_first(bus, build_pci_bus_begin, - build_pci_bus_end, &hotplug_state); + build_append_pci_bus_devices(scope, bus, pm->pcihp_bridge_en); + aml_append(sb_scope, scope); } - - build_append_array(sb_scope, hotplug_state.device_table); - build_pci_bus_state_cleanup(&hotplug_state); } - - build_package(sb_scope, op, 3); - build_append_array(table_data, sb_scope); - build_free_array(sb_scope); + aml_append(ssdt, sb_scope); } + /* copy AML table into ACPI tables blob and patch header there */ + g_array_append_vals(table_data, ssdt->buf->data, ssdt->buf->len); build_header(linker, table_data, - (void *)(table_data->data + ssdt_start), - "SSDT", table_data->len - ssdt_start, 1); + (void *)(table_data->data + table_data->len - ssdt->buf->len), + "SSDT", ssdt->buf->len, 1); + free_aml_allocator(); } static void @@ -1499,7 +1315,7 @@ static inline 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, mfre); + g_array_free(tables->rsdp, true); g_array_free(tables->table_data, true); g_array_free(tables->tcpalog, mfre); } @@ -1508,10 +1324,12 @@ typedef struct AcpiBuildState { /* Copy of table in RAM (for patching). */ ram_addr_t table_ram; - uint32_t table_size; /* Is table patched? */ uint8_t patched; PcGuestInfo *guest_info; + void *rsdp; + ram_addr_t rsdp_ram; + ram_addr_t linker_ram; } AcpiBuildState; static bool acpi_get_mcfg(AcpiMcfgInfo *mcfg) @@ -1560,6 +1378,7 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables) PcPciInfo pci; uint8_t *u; size_t aml_len = 0; + GArray *tables_blob = tables->table_data; acpi_get_cpu_info(&cpu); acpi_get_pm_info(&pm); @@ -1569,7 +1388,7 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables) table_offsets = g_array_new(false, true /* clear */, sizeof(uint32_t)); - ACPI_BUILD_DPRINTF(3, "init ACPI tables\n"); + ACPI_BUILD_DPRINTF("init ACPI tables\n"); bios_linker_loader_alloc(tables->linker, ACPI_BUILD_TABLE_FILE, 64 /* Ensure FACS is aligned */, @@ -1580,74 +1399,72 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables) * We place it first since it's the only table that has alignment * requirements. */ - facs = tables->table_data->len; - build_facs(tables->table_data, tables->linker, guest_info); + facs = tables_blob->len; + build_facs(tables_blob, tables->linker, guest_info); /* DSDT is pointed to by FADT */ - dsdt = tables->table_data->len; - build_dsdt(tables->table_data, tables->linker, &misc); + dsdt = tables_blob->len; + build_dsdt(tables_blob, tables->linker, &misc); /* Count the size of the DSDT and SSDT, we will need it for legacy * sizing of ACPI tables. */ - aml_len += tables->table_data->len - dsdt; + aml_len += tables_blob->len - dsdt; /* ACPI tables pointed to by RSDT */ - acpi_add_table(table_offsets, tables->table_data); - build_fadt(tables->table_data, tables->linker, &pm, facs, dsdt); + acpi_add_table(table_offsets, tables_blob); + build_fadt(tables_blob, tables->linker, &pm, facs, dsdt); - ssdt = tables->table_data->len; - acpi_add_table(table_offsets, tables->table_data); - build_ssdt(tables->table_data, tables->linker, &cpu, &pm, &misc, &pci, + ssdt = tables_blob->len; + acpi_add_table(table_offsets, tables_blob); + build_ssdt(tables_blob, tables->linker, &cpu, &pm, &misc, &pci, guest_info); - aml_len += tables->table_data->len - ssdt; + aml_len += tables_blob->len - ssdt; - acpi_add_table(table_offsets, tables->table_data); - build_madt(tables->table_data, tables->linker, &cpu, guest_info); + acpi_add_table(table_offsets, tables_blob); + build_madt(tables_blob, tables->linker, &cpu, guest_info); if (misc.has_hpet) { - acpi_add_table(table_offsets, tables->table_data); - build_hpet(tables->table_data, tables->linker); + acpi_add_table(table_offsets, tables_blob); + build_hpet(tables_blob, tables->linker); } if (misc.has_tpm) { - acpi_add_table(table_offsets, tables->table_data); - build_tpm_tcpa(tables->table_data, tables->linker, tables->tcpalog); + acpi_add_table(table_offsets, tables_blob); + build_tpm_tcpa(tables_blob, tables->linker, tables->tcpalog); - acpi_add_table(table_offsets, tables->table_data); - build_tpm_ssdt(tables->table_data, tables->linker); + acpi_add_table(table_offsets, tables_blob); + build_tpm_ssdt(tables_blob, tables->linker); } if (guest_info->numa_nodes) { - acpi_add_table(table_offsets, tables->table_data); - build_srat(tables->table_data, tables->linker, guest_info); + acpi_add_table(table_offsets, tables_blob); + build_srat(tables_blob, tables->linker, guest_info); } if (acpi_get_mcfg(&mcfg)) { - acpi_add_table(table_offsets, tables->table_data); - build_mcfg_q35(tables->table_data, tables->linker, &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->table_data); - build_dmar_q35(tables->table_data, tables->linker); + acpi_add_table(table_offsets, tables_blob); + build_dmar_q35(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->table_data); - g_array_append_vals(tables->table_data, u, len); + acpi_add_table(table_offsets, tables_blob); + g_array_append_vals(tables_blob, u, len); } /* RSDT is pointed to by RSDP */ - rsdt = tables->table_data->len; - build_rsdt(tables->table_data, tables->linker, table_offsets); + rsdt = tables_blob->len; + build_rsdt(tables_blob, tables->linker, table_offsets); /* 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. - * RSDP is small so it's easy to keep it immutable, no need to - * bother with alignment. * * 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 @@ -1671,23 +1488,23 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables) guest_info->legacy_acpi_table_size + ACPI_BUILD_LEGACY_CPU_AML_SIZE * max_cpus; int legacy_table_size = - ROUND_UP(tables->table_data->len - aml_len + legacy_aml_len, + ROUND_UP(tables_blob->len - aml_len + legacy_aml_len, ACPI_BUILD_ALIGN_SIZE); - if (tables->table_data->len > legacy_table_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->table_data, legacy_table_size); + 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->table_data->len > ACPI_BUILD_TABLE_SIZE / 2) { + 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->table_data, ACPI_BUILD_TABLE_SIZE); + acpi_align_size(tables_blob, ACPI_BUILD_TABLE_SIZE); } acpi_align_size(tables->linker, ACPI_BUILD_ALIGN_SIZE); @@ -1696,6 +1513,17 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables) g_array_free(table_offsets, true); } +static void acpi_ram_update(ram_addr_t ram, GArray *data) +{ + uint32_t size = acpi_data_len(data); + + /* Make sure RAM size is correct - in case it got changed e.g. by migration */ + qemu_ram_resize(ram, size, &error_abort); + + memcpy(qemu_get_ram_ptr(ram), data->data, size); + cpu_physical_memory_set_dirty_range_nocode(ram, size); +} + static void acpi_build_update(void *build_opaque, uint32_t offset) { AcpiBuildState *build_state = build_opaque; @@ -1711,13 +1539,15 @@ static void acpi_build_update(void *build_opaque, uint32_t offset) acpi_build(build_state->guest_info, &tables); - assert(acpi_data_len(tables.table_data) == build_state->table_size); - memcpy(qemu_get_ram_ptr(build_state->table_ram), tables.table_data->data, - build_state->table_size); + acpi_ram_update(build_state->table_ram, tables.table_data); - cpu_physical_memory_set_dirty_range_nocode(build_state->table_ram, - build_state->table_size); + if (build_state->rsdp) { + memcpy(build_state->rsdp, tables.rsdp->data, acpi_data_len(tables.rsdp)); + } else { + acpi_ram_update(build_state->rsdp_ram, tables.rsdp); + } + acpi_ram_update(build_state->linker_ram, tables.linker); acpi_build_tables_cleanup(&tables, true); } @@ -1728,10 +1558,10 @@ static void acpi_build_reset(void *build_opaque) } static ram_addr_t acpi_add_rom_blob(AcpiBuildState *build_state, GArray *blob, - const char *name) + const char *name, uint64_t max_size) { - return rom_add_blob(name, blob->data, acpi_data_len(blob), -1, name, - acpi_build_update, build_state); + 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 = { @@ -1750,17 +1580,17 @@ void acpi_setup(PcGuestInfo *guest_info) AcpiBuildState *build_state; if (!guest_info->fw_cfg) { - ACPI_BUILD_DPRINTF(3, "No fw cfg. Bailing out.\n"); + ACPI_BUILD_DPRINTF("No fw cfg. Bailing out.\n"); return; } if (!guest_info->has_acpi_build) { - ACPI_BUILD_DPRINTF(3, "ACPI build disabled. Bailing out.\n"); + ACPI_BUILD_DPRINTF("ACPI build disabled. Bailing out.\n"); return; } if (!acpi_enabled) { - ACPI_BUILD_DPRINTF(3, "ACPI disabled. Bailing out.\n"); + ACPI_BUILD_DPRINTF("ACPI disabled. Bailing out.\n"); return; } @@ -1775,21 +1605,34 @@ void acpi_setup(PcGuestInfo *guest_info) /* Now expose it all to Guest */ build_state->table_ram = acpi_add_rom_blob(build_state, tables.table_data, - ACPI_BUILD_TABLE_FILE); + ACPI_BUILD_TABLE_FILE, + ACPI_BUILD_TABLE_MAX_SIZE); assert(build_state->table_ram != RAM_ADDR_MAX); - build_state->table_size = acpi_data_len(tables.table_data); - acpi_add_rom_blob(NULL, tables.linker, "etc/table-loader"); + build_state->linker_ram = + 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)); - /* - * RSDP is small so it's easy to keep it immutable, no need to - * bother with ROM blobs. - */ - fw_cfg_add_file(guest_info->fw_cfg, ACPI_BUILD_RSDP_FILE, - tables.rsdp->data, acpi_data_len(tables.rsdp)); + if (!guest_info->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(guest_info->fw_cfg, ACPI_BUILD_RSDP_FILE, + acpi_build_update, build_state, + build_state->rsdp, rsdp_size); + build_state->rsdp_ram = (ram_addr_t)-1; + } else { + build_state->rsdp = NULL; + build_state->rsdp_ram = 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); diff --git a/hw/i386/acpi-dsdt-cpu-hotplug.dsl b/hw/i386/acpi-dsdt-cpu-hotplug.dsl index 34aab5a..1aff746 100644 --- a/hw/i386/acpi-dsdt-cpu-hotplug.dsl +++ b/hw/i386/acpi-dsdt-cpu-hotplug.dsl @@ -16,12 +16,12 @@ /**************************************************************** * CPU hotplug ****************************************************************/ -#define CPU_HOTPLUG_RESOURCE_DEVICE PRES Scope(\_SB) { /* Objects filled in by run-time generated SSDT */ External(NTFY, MethodObj) External(CPON, PkgObj) + External(PRS, FieldUnitObj) /* Methods called by run-time generated SSDT Processor objects */ Method(CPMA, 1, NotSerialized) { @@ -54,10 +54,6 @@ Scope(\_SB) { } #define CPU_STATUS_LEN ACPI_GPE_PROC_LEN - OperationRegion(PRST, SystemIO, CPU_STATUS_BASE, CPU_STATUS_LEN) - Field(PRST, ByteAcc, NoLock, Preserve) { - PRS, 256 - } Method(PRSC, 0) { // Local5 = active cpu bitmap Store(PRS, Local5) @@ -91,14 +87,4 @@ Scope(\_SB) { Increment(Local0) } } - - Device(CPU_HOTPLUG_RESOURCE_DEVICE) { - Name(_HID, EisaId("PNP0A06")) - - Name(_CRS, ResourceTemplate() { - IO(Decode16, CPU_STATUS_BASE, CPU_STATUS_BASE, 0, CPU_STATUS_LEN) - }) - - Name(_STA, 0xB) /* present, functioning, decoding, not shown in UI */ - } } diff --git a/hw/i386/acpi-dsdt-isa.dsl b/hw/i386/acpi-dsdt-isa.dsl index deb37de..89caa16 100644 --- a/hw/i386/acpi-dsdt-isa.dsl +++ b/hw/i386/acpi-dsdt-isa.dsl @@ -16,17 +16,6 @@ /* Common legacy ISA style devices. */ Scope(\_SB.PCI0.ISA) { - Device (SMC) { - Name(_HID, EisaId("APP0001")) - /* _STA will be patched to 0x0B if AppleSMC is present */ - ACPI_EXTRACT_NAME_BYTE_CONST DSDT_APPLESMC_STA - Name(_STA, 0xF0) - Name(_CRS, ResourceTemplate () { - IO (Decode16, 0x0300, 0x0300, 0x01, 0x20) - IRQNoFlags() { 6 } - }) - } - Device(RTC) { Name(_HID, EisaId("PNP0B00")) Name(_CRS, ResourceTemplate() { diff --git a/hw/i386/acpi-dsdt-mem-hotplug.dsl b/hw/i386/acpi-dsdt-mem-hotplug.dsl index 2a36c47..1e9ec39 100644 --- a/hw/i386/acpi-dsdt-mem-hotplug.dsl +++ b/hw/i386/acpi-dsdt-mem-hotplug.dsl @@ -22,14 +22,16 @@ External(MEMORY_SLOTS_NUMBER, IntObj) /* Memory hotplug IO registers */ - OperationRegion(MEMORY_HOTPLUG_IO_REGION, SystemIO, - ACPI_MEMORY_HOTPLUG_BASE, - ACPI_MEMORY_HOTPLUG_IO_LEN) - - Name(_CRS, ResourceTemplate() { - IO(Decode16, ACPI_MEMORY_HOTPLUG_BASE, ACPI_MEMORY_HOTPLUG_BASE, - 0, ACPI_MEMORY_HOTPLUG_IO_LEN, IO) - }) + External(MEMORY_SLOT_ADDR_LOW, FieldUnitObj) // read only + External(MEMORY_SLOT_ADDR_HIGH, FieldUnitObj) // read only + External(MEMORY_SLOT_SIZE_LOW, FieldUnitObj) // read only + External(MEMORY_SLOT_SIZE_HIGH, FieldUnitObj) // read only + External(MEMORY_SLOT_PROXIMITY, FieldUnitObj) // read only + External(MEMORY_SLOT_ENABLED, FieldUnitObj) // 1 if enabled, read only + External(MEMORY_SLOT_INSERT_EVENT, FieldUnitObj) // (read) 1 if has a insert event. (write) 1 to clear event + External(MEMORY_SLOT_SLECTOR, FieldUnitObj) // DIMM selector, write only + External(MEMORY_SLOT_OST_EVENT, FieldUnitObj) // _OST event code, write only + External(MEMORY_SLOT_OST_STATUS, FieldUnitObj) // _OST status code, write only Method(_STA, 0) { If (LEqual(MEMORY_SLOTS_NUMBER, Zero)) { @@ -39,25 +41,7 @@ Return(0xB) } - Field(MEMORY_HOTPLUG_IO_REGION, DWordAcc, NoLock, Preserve) { - MEMORY_SLOT_ADDR_LOW, 32, // read only - MEMORY_SLOT_ADDR_HIGH, 32, // read only - MEMORY_SLOT_SIZE_LOW, 32, // read only - MEMORY_SLOT_SIZE_HIGH, 32, // read only - MEMORY_SLOT_PROXIMITY, 32, // read only - } - Field(MEMORY_HOTPLUG_IO_REGION, ByteAcc, NoLock, Preserve) { - Offset(20), - MEMORY_SLOT_ENABLED, 1, // 1 if enabled, read only - MEMORY_SLOT_INSERT_EVENT, 1, // (read) 1 if has a insert event. (write) 1 to clear event - } - Mutex (MEMORY_SLOT_LOCK, 0) - Field (MEMORY_HOTPLUG_IO_REGION, DWordAcc, NoLock, Preserve) { - MEMORY_SLOT_SLECTOR, 32, // DIMM selector, write only - MEMORY_SLOT_OST_EVENT, 32, // _OST event code, write only - MEMORY_SLOT_OST_STATUS, 32, // _OST status code, write only - } Method(MEMORY_SLOT_SCAN_METHOD, 0) { If (LEqual(MEMORY_SLOTS_NUMBER, Zero)) { diff --git a/hw/i386/acpi-dsdt-pci-crs.dsl b/hw/i386/acpi-dsdt-pci-crs.dsl deleted file mode 100644 index 4648e90..0000000 --- a/hw/i386/acpi-dsdt-pci-crs.dsl +++ /dev/null @@ -1,92 +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 . - */ - -/* PCI CRS (current resources) definition. */ -Scope(\_SB.PCI0) { - - Name(CRES, ResourceTemplate() { - WordBusNumber(ResourceProducer, MinFixed, MaxFixed, PosDecode, - 0x0000, // Address Space Granularity - 0x0000, // Address Range Minimum - 0x00FF, // Address Range Maximum - 0x0000, // Address Translation Offset - 0x0100, // Address Length - ,, ) - IO(Decode16, - 0x0CF8, // Address Range Minimum - 0x0CF8, // Address Range Maximum - 0x01, // Address Alignment - 0x08, // Address Length - ) - BOARD_SPECIFIC_PCI_RESOURSES - DWordMemory(ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite, - 0x00000000, // Address Space Granularity - 0x000A0000, // Address Range Minimum - 0x000BFFFF, // Address Range Maximum - 0x00000000, // Address Translation Offset - 0x00020000, // Address Length - ,, , AddressRangeMemory, TypeStatic) - DWordMemory(ResourceProducer, PosDecode, MinFixed, MaxFixed, NonCacheable, ReadWrite, - 0x00000000, // Address Space Granularity - 0xE0000000, // Address Range Minimum - 0xFEBFFFFF, // Address Range Maximum - 0x00000000, // Address Translation Offset - 0x1EC00000, // Address Length - ,, PW32, AddressRangeMemory, TypeStatic) - }) - - Name(CR64, ResourceTemplate() { - QWordMemory(ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite, - 0x00000000, // Address Space Granularity - 0x8000000000, // Address Range Minimum - 0xFFFFFFFFFF, // Address Range Maximum - 0x00000000, // Address Translation Offset - 0x8000000000, // Address Length - ,, PW64, AddressRangeMemory, TypeStatic) - }) - - Method(_CRS, 0) { - /* Fields provided by dynamically created ssdt */ - External(P0S, IntObj) - External(P0E, IntObj) - External(P1V, IntObj) - External(P1S, BuffObj) - External(P1E, BuffObj) - External(P1L, BuffObj) - - /* fixup 32bit pci io window */ - CreateDWordField(CRES, \_SB.PCI0.PW32._MIN, PS32) - CreateDWordField(CRES, \_SB.PCI0.PW32._MAX, PE32) - CreateDWordField(CRES, \_SB.PCI0.PW32._LEN, PL32) - Store(P0S, PS32) - Store(P0E, PE32) - Store(Add(Subtract(P0E, P0S), 1), PL32) - - If (LEqual(P1V, Zero)) { - Return (CRES) - } - - /* fixup 64bit pci io window */ - CreateQWordField(CR64, \_SB.PCI0.PW64._MIN, PS64) - CreateQWordField(CR64, \_SB.PCI0.PW64._MAX, PE64) - CreateQWordField(CR64, \_SB.PCI0.PW64._LEN, PL64) - Store(P1S, PS64) - Store(P1E, PE64) - Store(P1L, PL64) - /* add window and return result */ - ConcatenateResTemplate(CRES, CR64, Local0) - Return (Local0) - } -} diff --git a/hw/i386/acpi-dsdt.dsl b/hw/i386/acpi-dsdt.dsl index a611e07..a2d84ec 100644 --- a/hw/i386/acpi-dsdt.dsl +++ b/hw/i386/acpi-dsdt.dsl @@ -31,50 +31,6 @@ DefinitionBlock ( #include "acpi-dsdt-dbug.dsl" - -/**************************************************************** - * PCI Bus definition - ****************************************************************/ -#define BOARD_SPECIFIC_PCI_RESOURSES \ - WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, \ - 0x0000, \ - 0x0000, \ - 0x0CF7, \ - 0x0000, \ - 0x0CF8, \ - ,, , TypeStatic) \ - WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, \ - 0x0000, \ - 0x0D00, \ - 0xADFF, \ - 0x0000, \ - 0xA100, \ - ,, , TypeStatic) \ - /* 0xae00-0xae0e hole for PCI hotplug, hw/acpi/piix4.c:PCI_HOTPLUG_ADDR */ \ - WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, \ - 0x0000, \ - 0xAE0F, \ - 0xAEFF, \ - 0x0000, \ - 0x00F1, \ - ,, , TypeStatic) \ - /* 0xaf00-0xaf1f hole for CPU hotplug, hw/acpi/piix4.c:PIIX4_PROC_BASE */ \ - WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, \ - 0x0000, \ - 0xAF20, \ - 0xAFDF, \ - 0x0000, \ - 0x00C0, \ - ,, , TypeStatic) \ - /* 0xafe0-0xafe3 hole for ACPI.GPE0, hw/acpi/piix4.c:GPE_BASE */ \ - WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, \ - 0x0000, \ - 0xAFE4, \ - 0xFFFF, \ - 0x0000, \ - 0x501C, \ - ,, , TypeStatic) - Scope(\_SB) { Device(PCI0) { Name(_HID, EisaId("PNP0A03")) @@ -85,7 +41,6 @@ DefinitionBlock ( } } -#include "acpi-dsdt-pci-crs.dsl" #include "acpi-dsdt-hpet.dsl" @@ -130,7 +85,6 @@ DefinitionBlock ( } } -#define DSDT_APPLESMC_STA piix_dsdt_applesmc_sta #include "acpi-dsdt-isa.dsl" diff --git a/hw/i386/acpi-dsdt.hex.generated b/hw/i386/acpi-dsdt.hex.generated index 875570e..ecaa4a5 100644 --- a/hw/i386/acpi-dsdt.hex.generated +++ b/hw/i386/acpi-dsdt.hex.generated @@ -3,12 +3,12 @@ static unsigned char AcpiDsdtAmlCode[] = { 0x53, 0x44, 0x54, -0x8, -0xe, +0x9a, +0xb, 0x0, 0x0, 0x1, -0xfc, +0xf8, 0x42, 0x58, 0x50, @@ -31,8 +31,8 @@ static unsigned char AcpiDsdtAmlCode[] = { 0x4e, 0x54, 0x4c, -0x28, -0x8, +0x7, +0x11, 0x14, 0x20, 0x10, @@ -127,422 +127,23 @@ static unsigned char AcpiDsdtAmlCode[] = { 0x48, 0x49, 0x44, -0xc, -0x41, -0xd0, -0xa, -0x3, -0x8, -0x5f, -0x41, -0x44, -0x52, -0x0, -0x8, -0x5f, -0x55, -0x49, -0x44, -0x1, -0x10, -0x4e, -0x18, -0x2e, -0x5f, -0x53, -0x42, -0x5f, -0x50, -0x43, -0x49, -0x30, -0x8, -0x43, -0x52, -0x45, -0x53, -0x11, -0x42, -0xa, -0xa, -0x9e, -0x88, -0xd, -0x0, -0x2, -0xc, -0x0, -0x0, -0x0, -0x0, -0x0, -0xff, -0x0, -0x0, -0x0, -0x0, -0x1, -0x47, -0x1, -0xf8, -0xc, -0xf8, -0xc, -0x1, -0x8, -0x88, -0xd, -0x0, -0x1, -0xc, -0x3, -0x0, -0x0, -0x0, -0x0, -0xf7, -0xc, -0x0, -0x0, -0xf8, -0xc, -0x88, -0xd, -0x0, -0x1, -0xc, -0x3, -0x0, -0x0, -0x0, -0xd, -0xff, -0xad, -0x0, -0x0, -0x0, -0xa1, -0x88, -0xd, -0x0, -0x1, -0xc, -0x3, -0x0, -0x0, -0xf, -0xae, -0xff, -0xae, -0x0, -0x0, -0xf1, -0x0, -0x88, -0xd, -0x0, -0x1, -0xc, -0x3, -0x0, -0x0, -0x20, -0xaf, -0xdf, -0xaf, -0x0, -0x0, -0xc0, -0x0, -0x88, -0xd, -0x0, -0x1, -0xc, -0x3, -0x0, -0x0, -0xe4, -0xaf, -0xff, -0xff, -0x0, -0x0, -0x1c, -0x50, -0x87, -0x17, -0x0, -0x0, -0xc, -0x3, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0xa, -0x0, -0xff, -0xff, -0xb, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x2, -0x0, -0x87, -0x17, -0x0, -0x0, -0xc, -0x1, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0xe0, -0xff, -0xff, -0xbf, -0xfe, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0xc0, -0x1e, -0x79, -0x0, -0x8, -0x43, -0x52, -0x36, -0x34, -0x11, -0x33, -0xa, -0x30, -0x8a, -0x2b, -0x0, -0x0, -0xc, -0x3, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x80, -0x0, -0x0, -0x0, -0xff, -0xff, -0xff, -0xff, -0xff, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x80, -0x0, -0x0, -0x0, -0x79, -0x0, -0x14, -0x41, -0xa, -0x5f, -0x43, -0x52, -0x53, -0x0, -0x8a, -0x43, -0x52, -0x45, -0x53, -0xa, -0x8c, -0x50, -0x53, -0x33, -0x32, -0x8a, -0x43, -0x52, -0x45, -0x53, -0xa, -0x90, -0x50, -0x45, -0x33, -0x32, -0x8a, -0x43, -0x52, -0x45, -0x53, -0xa, -0x98, -0x50, -0x4c, -0x33, -0x32, -0x70, -0x50, -0x30, -0x53, -0x5f, -0x50, -0x53, -0x33, -0x32, -0x70, -0x50, -0x30, -0x45, -0x5f, -0x50, -0x45, -0x33, -0x32, -0x70, -0x72, -0x74, -0x50, -0x30, -0x45, -0x5f, -0x50, -0x30, -0x53, -0x5f, -0x0, -0x1, -0x0, -0x50, -0x4c, -0x33, -0x32, -0xa0, -0xc, -0x93, -0x50, -0x31, -0x56, -0x5f, -0x0, -0xa4, -0x43, -0x52, -0x45, -0x53, -0x8f, -0x43, -0x52, -0x36, -0x34, -0xa, -0xe, -0x50, -0x53, -0x36, -0x34, -0x8f, -0x43, -0x52, -0x36, -0x34, -0xa, -0x16, -0x50, -0x45, -0x36, -0x34, -0x8f, -0x43, -0x52, -0x36, -0x34, -0xa, -0x26, -0x50, -0x4c, -0x36, -0x34, -0x70, -0x50, -0x31, -0x53, -0x5f, -0x50, -0x53, -0x36, -0x34, -0x70, -0x50, -0x31, -0x45, -0x5f, -0x50, -0x45, -0x36, -0x34, -0x70, -0x50, -0x31, -0x4c, -0x5f, -0x50, -0x4c, -0x36, -0x34, -0x84, -0x43, -0x52, -0x45, -0x53, -0x43, +0xc, +0x41, +0xd0, +0xa, +0x3, +0x8, +0x5f, +0x41, +0x44, 0x52, -0x36, -0x34, -0x60, -0xa4, -0x60, +0x0, +0x8, +0x5f, +0x55, +0x49, +0x44, +0x1, 0x10, 0x4d, 0x8, @@ -811,8 +412,8 @@ static unsigned char AcpiDsdtAmlCode[] = { 0x4e, 0x1, 0x10, -0x4a, -0x1e, +0x4c, +0x1b, 0x2f, 0x3, 0x5f, @@ -829,52 +430,6 @@ static unsigned char AcpiDsdtAmlCode[] = { 0x5f, 0x5b, 0x82, -0x2c, -0x53, -0x4d, -0x43, -0x5f, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x6, -0x10, -0x0, -0x1, -0x8, -0x5f, -0x53, -0x54, -0x41, -0xa, -0xf0, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0x10, -0xa, -0xd, -0x47, -0x1, -0x0, -0x3, -0x0, -0x3, -0x1, -0x20, -0x22, -0x40, -0x0, -0x79, -0x0, -0x5b, -0x82, 0x2d, 0x52, 0x54, @@ -2318,8 +1873,8 @@ static unsigned char AcpiDsdtAmlCode[] = { 0x53, 0x1, 0x10, -0x42, -0x11, +0x4d, +0xc, 0x5f, 0x53, 0x42, @@ -2416,32 +1971,6 @@ static unsigned char AcpiDsdtAmlCode[] = { 0x22, 0xa, 0xc8, -0x5b, -0x80, -0x50, -0x52, -0x53, -0x54, -0x1, -0xb, -0x0, -0xaf, -0xa, -0x20, -0x5b, -0x81, -0xc, -0x50, -0x52, -0x53, -0x54, -0x1, -0x50, -0x52, -0x53, -0x5f, -0x40, -0x10, 0x14, 0x4a, 0x6, @@ -2549,52 +2078,9 @@ static unsigned char AcpiDsdtAmlCode[] = { 0x3, 0x75, 0x60, -0x5b, -0x82, -0x29, -0x50, -0x52, -0x45, -0x53, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xa, -0x6, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0xd, -0xa, -0xa, -0x47, -0x1, -0x0, -0xaf, -0x0, -0xaf, -0x0, -0x20, -0x79, -0x0, -0x8, -0x5f, -0x53, -0x54, -0x41, -0xa, -0xb, 0x10, -0x40, -0x31, +0x44, +0x2a, 0x2e, 0x5f, 0x53, @@ -2606,8 +2092,8 @@ static unsigned char AcpiDsdtAmlCode[] = { 0x30, 0x5b, 0x82, -0x43, -0x30, +0x47, +0x29, 0x4d, 0x48, 0x50, @@ -2657,37 +2143,6 @@ static unsigned char AcpiDsdtAmlCode[] = { 0x65, 0x73, 0x0, -0x5b, -0x80, -0x48, -0x50, -0x4d, -0x52, -0x1, -0xb, -0x0, -0xa, -0xa, -0x18, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0xd, -0xa, -0xa, -0x47, -0x1, -0x0, -0xa, -0x0, -0xa, -0x0, -0x18, -0x79, -0x0, 0x14, 0x13, 0x5f, @@ -2709,89 +2164,12 @@ static unsigned char AcpiDsdtAmlCode[] = { 0xa, 0xb, 0x5b, -0x81, -0x1f, -0x48, -0x50, -0x4d, -0x52, -0x3, -0x4d, -0x52, -0x42, -0x4c, -0x20, -0x4d, -0x52, -0x42, -0x48, -0x20, -0x4d, -0x52, -0x4c, -0x4c, -0x20, -0x4d, -0x52, -0x4c, -0x48, -0x20, -0x4d, -0x50, -0x58, -0x5f, -0x20, -0x5b, -0x81, -0x13, -0x48, -0x50, -0x4d, -0x52, -0x1, -0x0, -0x40, -0xa, -0x4d, -0x45, -0x53, -0x5f, -0x1, -0x4d, -0x49, -0x4e, -0x53, -0x1, -0x5b, 0x1, 0x4d, 0x4c, 0x43, 0x4b, 0x0, -0x5b, -0x81, -0x15, -0x48, -0x50, -0x4d, -0x52, -0x3, -0x4d, -0x53, -0x45, -0x4c, -0x20, -0x4d, -0x4f, -0x45, -0x56, -0x20, -0x4d, -0x4f, -0x53, -0x43, -0x20, 0x14, 0x4a, 0x4, @@ -3592,6 +2970,3 @@ static unsigned char AcpiDsdtAmlCode[] = { 0x46, 0x0 }; -static unsigned short piix_dsdt_applesmc_sta[] = { -0x353 -}; diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 0a4282a..7da70ff 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -745,6 +745,9 @@ static inline bool vtd_is_interrupt_addr(hwaddr addr) /* 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 diff --git a/hw/i386/kvm/apic.c b/hw/i386/kvm/apic.c index 271e97f..5b47056 100644 --- a/hw/i386/kvm/apic.c +++ b/hw/i386/kvm/apic.c @@ -171,12 +171,15 @@ static const MemoryRegionOps kvm_apic_io_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static void kvm_apic_realize(DeviceState *dev, Error **errp) +static void kvm_apic_reset(APICCommonState *s) { - APICCommonState *s = APIC_COMMON(dev); - /* 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); @@ -191,6 +194,7 @@ 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; diff --git a/hw/i386/kvm/clock.c b/hw/i386/kvm/clock.c index 58be2bd..efdf165 100644 --- a/hw/i386/kvm/clock.c +++ b/hw/i386/kvm/clock.c @@ -88,7 +88,7 @@ static void kvmclock_vm_state_change(void *opaque, int running, int ret; if (running) { - struct kvm_clock_data data; + struct kvm_clock_data data = {}; uint64_t time_at_migration = kvmclock_current_nsec(s); s->clock_valid = false; @@ -99,7 +99,6 @@ static void kvmclock_vm_state_change(void *opaque, int running, } data.clock = s->clock; - data.flags = 0; ret = kvm_vm_ioctl(kvm_state, KVM_SET_CLOCK, &data); if (ret < 0) { fprintf(stderr, "KVM_SET_CLOCK failed: %s\n", strerror(ret)); diff --git a/hw/i386/kvm/i8254.c b/hw/i386/kvm/i8254.c index 472af81..90eea10 100644 --- a/hw/i386/kvm/i8254.c +++ b/hw/i386/kvm/i8254.c @@ -138,7 +138,7 @@ static void kvm_pit_get(PITCommonState *pit) static void kvm_pit_put(PITCommonState *pit) { KVMPITState *s = KVM_PIT(pit); - struct kvm_pit_state2 kpit; + struct kvm_pit_state2 kpit = {}; struct kvm_pit_channel_state *kchan; struct PITChannelState *sc; int i, ret; diff --git a/hw/i386/kvm/pci-assign.c b/hw/i386/kvm/pci-assign.c index bb206da..9db7c77 100644 --- a/hw/i386/kvm/pci-assign.c +++ b/hw/i386/kvm/pci-assign.c @@ -552,9 +552,8 @@ static void get_real_device(AssignedDevice *pci_dev, Error **errp) snprintf(name, sizeof(name), "%sconfig", dir); if (pci_dev->configfd_name && *pci_dev->configfd_name) { - dev->config_fd = monitor_handle_fd_param2(cur_mon, - pci_dev->configfd_name, - &local_err); + dev->config_fd = monitor_fd_param(cur_mon, pci_dev->configfd_name, + &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -953,8 +952,7 @@ static void assigned_dev_update_irq_routing(PCIDevice *dev) r = assign_intx(assigned_dev, &err); if (r < 0) { - error_report("%s", error_get_pretty(err)); - error_free(err); + error_report_err(err); err = NULL; qdev_unplug(&dev->qdev, &err); assert(!err); @@ -1010,8 +1008,7 @@ static void assigned_dev_update_msi(PCIDevice *pci_dev) assign_intx(assigned_dev, &local_err); if (local_err) { - error_report("%s", error_get_pretty(local_err)); - error_free(local_err); + error_report_err(local_err); } } } @@ -1158,8 +1155,7 @@ static void assigned_dev_update_msix(PCIDevice *pci_dev) assign_intx(assigned_dev, &local_err); if (local_err) { - error_report("%s", error_get_pretty(local_err)); - error_free(local_err); + error_report_err(local_err); } } } @@ -1742,7 +1738,7 @@ static void reset_assigned_device(DeviceState *dev) assigned_dev_pci_write_config(pci_dev, PCI_COMMAND, 0, 1); } -static int assigned_initfn(struct PCIDevice *pci_dev) +static void assigned_realize(struct PCIDevice *pci_dev, Error **errp) { AssignedDevice *dev = DO_UPCAST(AssignedDevice, dev, pci_dev); uint8_t e_intx; @@ -1825,7 +1821,7 @@ static int assigned_initfn(struct PCIDevice *pci_dev) assigned_dev_load_option_rom(dev); - return 0; + return; assigned_out: deassign_device(dev); @@ -1835,9 +1831,7 @@ out: exit_with_error: assert(local_err); - qerror_report_err(local_err); - error_free(local_err); - return -1; + error_propagate(errp, local_err); } static void assigned_exitfn(struct PCIDevice *pci_dev) @@ -1873,7 +1867,7 @@ static void assign_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - k->init = assigned_initfn; + k->realize = assigned_realize; k->exit = assigned_exitfn; k->config_read = assigned_dev_pci_read_config; k->config_write = assigned_dev_pci_write_config; diff --git a/hw/i386/multiboot.c b/hw/i386/multiboot.c index 985ca1e..1adbe9e 100644 --- a/hw/i386/multiboot.c +++ b/hw/i386/multiboot.c @@ -54,6 +54,7 @@ enum { MBI_MODS_COUNT = 20, MBI_MODS_ADDR = 24, MBI_MMAP_ADDR = 48, + MBI_BOOTLOADER = 64, MBI_SIZE = 88, @@ -74,6 +75,7 @@ enum { MULTIBOOT_FLAGS_CMDLINE = 1 << 2, MULTIBOOT_FLAGS_MODULES = 1 << 3, MULTIBOOT_FLAGS_MMAP = 1 << 6, + MULTIBOOT_FLAGS_BOOTLOADER = 1 << 9, }; typedef struct { @@ -87,6 +89,8 @@ typedef struct { 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 */ @@ -95,6 +99,8 @@ typedef struct { 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; @@ -105,6 +111,16 @@ static uint32_t mb_add_cmdline(MultibootState *s, const char *cmdline) 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) @@ -140,6 +156,7 @@ int load_multiboot(FWCfgState *fw_cfg, 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. */ @@ -241,25 +258,29 @@ int load_multiboot(FWCfgState *fw_cfg, mbs.mb_buf_size = TARGET_PAGE_ALIGN(mb_kernel_size); mbs.offset_mbinfo = mbs.mb_buf_size; - /* Calculate space for cmdlines and mb_mods */ - mbs.mb_buf_size += strlen(kernel_filename) + 1; - mbs.mb_buf_size += strlen(kernel_cmdline) + 1; + /* 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; - mbs.mb_buf_size += strlen(r) + 1; + 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 += MB_MOD_SIZE * mbs.mb_mods_avail; } + 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 and 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; + /* 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; @@ -306,6 +327,8 @@ int load_multiboot(FWCfgState *fw_cfg, 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 */ @@ -314,7 +337,8 @@ int load_multiboot(FWCfgState *fw_cfg, | MULTIBOOT_FLAGS_BOOT_DEVICE | MULTIBOOT_FLAGS_CMDLINE | MULTIBOOT_FLAGS_MODULES - | MULTIBOOT_FLAGS_MMAP); + | 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); diff --git a/hw/i386/pc.c b/hw/i386/pc.c index f31d55e..a8e6be1 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -25,6 +25,8 @@ #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" @@ -41,7 +43,9 @@ #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" @@ -282,7 +286,7 @@ static int boot_device2nibble(char boot_device) return 0; } -static int set_boot_dev(ISADevice *s, const char *boot_device) +static void set_boot_dev(ISADevice *s, const char *boot_device, Error **errp) { #define PC_MAX_BOOT_DEVICES 3 int nbds, bds[3] = { 0, }; @@ -290,25 +294,24 @@ static int set_boot_dev(ISADevice *s, const char *boot_device) nbds = strlen(boot_device); if (nbds > PC_MAX_BOOT_DEVICES) { - error_report("Too many boot devices for PC"); - return(1); + 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_report("Invalid boot device for PC: '%c'", - boot_device[i]); - return(1); + 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)); - return(0); } -static int pc_boot_set(void *opaque, const char *boot_device) +static void pc_boot_set(void *opaque, const char *boot_device, Error **errp) { - return set_boot_dev(opaque, boot_device); + set_boot_dev(opaque, boot_device, errp); } typedef struct pc_cmos_init_late_arg { @@ -365,6 +368,7 @@ void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size, FDriveType fd_type[2] = { FDRIVE_DRV_NONE, FDRIVE_DRV_NONE }; static pc_cmos_init_late_arg arg; PCMachineState *pc_machine = PC_MACHINE(machine); + Error *local_err = NULL; /* various important CMOS locations needed by PC/Bochs bios */ @@ -412,7 +416,9 @@ void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size, object_property_set_link(OBJECT(machine), OBJECT(s), "rtc_state", &error_abort); - if (set_boot_dev(s, boot_device)) { + set_boot_dev(s, boot_device, &local_err); + if (local_err) { + error_report_err(local_err); exit(1); } @@ -602,8 +608,7 @@ int e820_add_entry(uint64_t address, uint64_t length, uint32_t type) } /* new "etc/e820" file -- include ram too */ - e820_table = g_realloc(e820_table, - sizeof(struct e820_entry) * (e820_entries+1)); + 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); @@ -627,6 +632,39 @@ bool e820_get_entry(int idx, uint32_t type, uint64_t *address, uint64_t *length) 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; + } +} + /* Calculates the limit to CPU APIC ID values * * This function returns the limit for the APIC ID value, so that all @@ -648,7 +686,7 @@ static FWCfgState *bochs_bios_init(void) int i, j; unsigned int apic_id_limit = pc_apic_id_limit(max_cpus); - fw_cfg = fw_cfg_init(BIOS_CFG_IOPORT, BIOS_CFG_IOPORT + 1, 0, 0); + fw_cfg = fw_cfg_init_io(BIOS_CFG_IOPORT); /* FW_CFG_MAX_CPUS is a bit confusing/problematic on x86: * * SeaBIOS needs FW_CFG_MAX_CPUS for CPU hotplug, but the CPU hotplug @@ -664,7 +702,6 @@ static FWCfgState *bochs_bios_init(void) * the APIC ID, not the "CPU index" */ fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)apic_id_limit); - fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1); 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); @@ -955,18 +992,26 @@ void pc_acpi_smi_interrupt(void *opaque, int irq, int level) static X86CPU *pc_new_cpu(const char *cpu_model, int64_t apic_id, DeviceState *icc_bridge, Error **errp) { - X86CPU *cpu; + X86CPU *cpu = NULL; Error *local_err = NULL; - cpu = cpu_x86_create(cpu_model, icc_bridge, &local_err); + if (icc_bridge == NULL) { + error_setg(&local_err, "Invalid icc-bridge value"); + goto out; + } + + cpu = cpu_x86_create(cpu_model, &local_err); if (local_err != NULL) { - error_propagate(errp, local_err); - return NULL; + goto out; } + qdev_set_parent_bus(DEVICE(cpu), qdev_get_child_bus(icc_bridge, "icc")); + object_unref(OBJECT(cpu)); + 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)); @@ -1039,8 +1084,7 @@ void pc_cpus_init(const char *cpu_model, DeviceState *icc_bridge) cpu = pc_new_cpu(cpu_model, x86_cpu_apic_id_from_index(i), icc_bridge, &error); if (error) { - error_report("%s", error_get_pretty(error)); - error_free(error); + error_report_err(error); exit(1); } } @@ -1137,15 +1181,11 @@ void pc_acpi_init(const char *default_dsdt) if (filename == NULL) { fprintf(stderr, "WARNING: failed to find %s\n", default_dsdt); } else { - char *arg; - QemuOpts *opts; + QemuOpts *opts = qemu_opts_create(qemu_find_opts("acpi"), NULL, 0, + &error_abort); Error *err = NULL; - arg = g_strdup_printf("file=%s", filename); - - /* creates a deep copy of "arg" */ - opts = qemu_opts_parse(qemu_find_opts("acpi"), arg, 0); - g_assert(opts != NULL); + qemu_opt_set(opts, "file", filename, &error_abort); acpi_table_add_builtin(opts, &err); if (err) { @@ -1153,7 +1193,6 @@ void pc_acpi_init(const char *default_dsdt) error_get_pretty(err)); error_free(err); } - g_free(arg); g_free(filename); } } @@ -1169,7 +1208,7 @@ FWCfgState *xen_load_linux(const char *kernel_filename, assert(kernel_filename != NULL); - fw_cfg = fw_cfg_init(BIOS_CFG_IOPORT, BIOS_CFG_IOPORT + 1, 0, 0); + fw_cfg = fw_cfg_init_io(BIOS_CFG_IOPORT); rom_set_fw(fw_cfg); load_linux(fw_cfg, kernel_filename, initrd_filename, @@ -1245,6 +1284,13 @@ FWCfgState *pc_memory_init(MachineState *machine, 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 + above_4g_mem_size, 1ULL << 30); @@ -1418,17 +1464,8 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi, pcspk_init(isa_bus, pit); } - for(i = 0; i < MAX_SERIAL_PORTS; i++) { - if (serial_hds[i]) { - serial_isa_init(isa_bus, i, serial_hds[i]); - } - } - - for(i = 0; i < MAX_PARALLEL_PORTS; i++) { - if (parallel_hds[i]) { - parallel_init(isa_bus, i, parallel_hds[i]); - } - } + 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"); @@ -1551,37 +1588,6 @@ void qemu_register_pc_machine(QEMUMachine *m) g_free(name); } -static int pc_dimm_count(Object *obj, void *opaque) -{ - int *count = opaque; - - if (object_dynamic_cast(obj, TYPE_PC_DIMM)) { - (*count)++; - } - - object_child_foreach(obj, pc_dimm_count, opaque); - return 0; -} - -static int pc_existing_dimms_capacity(Object *obj, void *opaque) -{ - Error *local_err = NULL; - uint64_t *size = opaque; - - if (object_dynamic_cast(obj, TYPE_PC_DIMM)) { - (*size) += object_property_get_int(obj, PC_DIMM_SIZE_PROP, &local_err); - - if (local_err) { - qerror_report_err(local_err); - error_free(local_err); - return 1; - } - } - - object_child_foreach(obj, pc_dimm_count, opaque); - return 0; -} - static void pc_dimm_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { @@ -1614,16 +1620,17 @@ static void pc_dimm_plug(HotplugHandler *hotplug_dev, goto out; } - if (pc_existing_dimms_capacity(OBJECT(machine), &existing_dimms_capacity)) { - error_setg(&local_err, "failed to get total size of existing DIMMs"); + 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 0x" RAM_ADDR_FMT, - existing_dimms_capacity, machine->maxram_size); + " in use of total hot pluggable 0x" RAM_ADDR_FMT, + existing_dimms_capacity, + machine->maxram_size - machine->ram_size); goto out; } @@ -1709,6 +1716,20 @@ static void pc_machine_device_plug_cb(HotplugHandler *hotplug_dev, } } +static void pc_machine_device_unplug_request_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + 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) +{ + 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) { @@ -1804,17 +1825,24 @@ static void pc_machine_initfn(Object *obj) object_property_add(obj, PC_MACHINE_MEMHP_REGION_SIZE, "int", pc_machine_get_hotplug_memory_region_size, NULL, NULL, NULL, NULL); + pcms->max_ram_below_4g = 1ULL << 32; /* 4G */ object_property_add(obj, PC_MACHINE_MAX_RAM_BELOW_4G, "size", pc_machine_get_max_ram_below_4g, pc_machine_set_max_ram_below_4g, NULL, NULL, NULL); + object_property_set_description(obj, PC_MACHINE_MAX_RAM_BELOW_4G, + "Maximum ram below the 4G boundary (32bit boundary)", + NULL); pcms->vmport = ON_OFF_AUTO_AUTO; object_property_add(obj, PC_MACHINE_VMPORT, "OnOffAuto", pc_machine_get_vmport, pc_machine_set_vmport, NULL, NULL, NULL); + object_property_set_description(obj, PC_MACHINE_VMPORT, + "Enable vmport (pc & q35)", + NULL); pcms->enforce_aligned_dimm = true; object_property_add_bool(obj, PC_MACHINE_ENFORCE_ALIGNED_DIMM, @@ -1822,6 +1850,14 @@ static void pc_machine_initfn(Object *obj) NULL, NULL); } +static unsigned pc_cpu_index_to_socket_id(unsigned cpu_index) +{ + unsigned pkg_id, core_id, smt_id; + x86_topo_ids_from_idx(smp_cores, smp_threads, cpu_index, + &pkg_id, &core_id, &smt_id); + return pkg_id; +} + static void pc_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -1830,7 +1866,10 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) pcmc->get_hotplug_handler = mc->get_hotplug_handler; mc->get_hotplug_handler = pc_get_hotpug_handler; + mc->cpu_index_to_socket_id = pc_cpu_index_to_socket_id; 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 = { diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 85ed3c8..1fe7bfb 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -60,6 +60,7 @@ static const int ide_iobase2[MAX_IDE_BUS] = { 0x3f6, 0x376 }; static const int ide_irq[MAX_IDE_BUS] = { 14, 15 }; static bool has_acpi_build = true; +static bool rsdp_in_ram = true; static int legacy_acpi_table_size; static bool smbios_defaults = true; static bool smbios_legacy_mode; @@ -168,6 +169,7 @@ static void pc_init1(MachineState *machine, guest_info->isapc_ram_fw = !pci_enabled; guest_info->has_reserved_memory = has_reserved_memory; + guest_info->rsdp_in_ram = rsdp_in_ram; if (smbios_defaults) { MachineClass *mc = MACHINE_GET_CLASS(machine); @@ -208,7 +210,7 @@ static void pc_init1(MachineState *machine, } else { pci_bus = NULL; i440fx_state = NULL; - isa_bus = isa_bus_new(NULL, system_io); + isa_bus = isa_bus_new(NULL, get_system_memory(), system_io); no_hpet = 1; } isa_bus_irqs(isa_bus, gsi); @@ -274,7 +276,7 @@ static void pc_init1(MachineState *machine, pc_cmos_init(below_4g_mem_size, above_4g_mem_size, machine->boot_order, machine, floppy, idebus[0], idebus[1], rtc_state); - if (pci_enabled && usb_enabled(false)) { + if (pci_enabled && usb_enabled()) { pci_create_simple(pci_bus, piix3_devfn + 2, "piix3-usb-uhci"); } @@ -308,9 +310,35 @@ static void pc_init_pci(MachineState *machine) pc_init1(machine, 1, 1); } +static void pc_compat_2_2(MachineState *machine) +{ + rsdp_in_ram = false; + x86_cpu_compat_set_features("kvm64", FEAT_1_EDX, 0, CPUID_VME); + x86_cpu_compat_set_features("kvm32", FEAT_1_EDX, 0, CPUID_VME); + x86_cpu_compat_set_features("Conroe", FEAT_1_EDX, 0, CPUID_VME); + x86_cpu_compat_set_features("Penryn", FEAT_1_EDX, 0, CPUID_VME); + x86_cpu_compat_set_features("Nehalem", FEAT_1_EDX, 0, CPUID_VME); + x86_cpu_compat_set_features("Westmere", FEAT_1_EDX, 0, CPUID_VME); + x86_cpu_compat_set_features("SandyBridge", FEAT_1_EDX, 0, CPUID_VME); + x86_cpu_compat_set_features("Haswell", FEAT_1_EDX, 0, CPUID_VME); + x86_cpu_compat_set_features("Broadwell", FEAT_1_EDX, 0, CPUID_VME); + x86_cpu_compat_set_features("Opteron_G1", FEAT_1_EDX, 0, CPUID_VME); + x86_cpu_compat_set_features("Opteron_G2", FEAT_1_EDX, 0, CPUID_VME); + x86_cpu_compat_set_features("Opteron_G3", FEAT_1_EDX, 0, CPUID_VME); + x86_cpu_compat_set_features("Opteron_G4", FEAT_1_EDX, 0, CPUID_VME); + x86_cpu_compat_set_features("Opteron_G5", FEAT_1_EDX, 0, CPUID_VME); + x86_cpu_compat_set_features("Haswell", FEAT_1_ECX, 0, CPUID_EXT_F16C); + x86_cpu_compat_set_features("Haswell", FEAT_1_ECX, 0, CPUID_EXT_RDRAND); + x86_cpu_compat_set_features("Broadwell", FEAT_1_ECX, 0, CPUID_EXT_F16C); + x86_cpu_compat_set_features("Broadwell", FEAT_1_ECX, 0, CPUID_EXT_RDRAND); + machine->suppress_vmdesc = true; +} + static void pc_compat_2_1(MachineState *machine) { PCMachineState *pcms = PC_MACHINE(machine); + + pc_compat_2_2(machine); smbios_uuid_encoded = false; x86_cpu_compat_set_features("coreduo", FEAT_1_ECX, CPUID_EXT_VMX, 0); x86_cpu_compat_set_features("core2duo", FEAT_1_ECX, CPUID_EXT_VMX, 0); @@ -382,7 +410,13 @@ static void pc_compat_1_3(MachineState *machine) static void pc_compat_1_2(MachineState *machine) { pc_compat_1_3(machine); - x86_cpu_compat_kvm_no_autoenable(FEAT_KVM, KVM_FEATURE_PV_EOI); + x86_cpu_compat_kvm_no_autoenable(FEAT_KVM, 1 << KVM_FEATURE_PV_EOI); +} + +static void pc_init_pci_2_2(MachineState *machine) +{ + pc_compat_2_2(machine); + pc_init_pci(machine); } static void pc_init_pci_2_1(MachineState *machine) @@ -453,7 +487,7 @@ static void pc_init_isa(MachineState *machine) if (!machine->cpu_model) { machine->cpu_model = "486"; } - x86_cpu_compat_kvm_no_autoenable(FEAT_KVM, KVM_FEATURE_PV_EOI); + x86_cpu_compat_kvm_no_autoenable(FEAT_KVM, 1 << KVM_FEATURE_PV_EOI); enable_compat_apic_id_mode(); pc_init1(machine, 0, 1); } @@ -478,19 +512,27 @@ static void pc_xen_hvm_init(MachineState *machine) .desc = "Standard PC (i440FX + PIIX, 1996)", \ .hot_add_cpu = pc_hot_add_cpu -#define PC_I440FX_2_2_MACHINE_OPTIONS \ +#define PC_I440FX_2_3_MACHINE_OPTIONS \ PC_I440FX_MACHINE_OPTIONS, \ .default_machine_opts = "firmware=bios-256k.bin", \ .default_display = "std" -static QEMUMachine pc_i440fx_machine_v2_2 = { - PC_I440FX_2_2_MACHINE_OPTIONS, - .name = "pc-i440fx-2.2", +static QEMUMachine pc_i440fx_machine_v2_3 = { + PC_I440FX_2_3_MACHINE_OPTIONS, + .name = "pc-i440fx-2.3", .alias = "pc", .init = pc_init_pci, .is_default = 1, }; +#define PC_I440FX_2_2_MACHINE_OPTIONS PC_I440FX_2_3_MACHINE_OPTIONS + +static QEMUMachine pc_i440fx_machine_v2_2 = { + PC_I440FX_2_2_MACHINE_OPTIONS, + .name = "pc-i440fx-2.2", + .init = pc_init_pci_2_2, +}; + #define PC_I440FX_2_1_MACHINE_OPTIONS \ PC_I440FX_MACHINE_OPTIONS, \ .default_machine_opts = "firmware=bios-256k.bin" @@ -928,6 +970,7 @@ static QEMUMachine xenfv_machine = { static void pc_machine_init(void) { + qemu_register_pc_machine(&pc_i440fx_machine_v2_3); qemu_register_pc_machine(&pc_i440fx_machine_v2_2); qemu_register_pc_machine(&pc_i440fx_machine_v2_1); qemu_register_pc_machine(&pc_i440fx_machine_v2_0); diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 0262b5e..dcc17c0 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -50,6 +50,7 @@ #define MAX_SATA_PORTS 6 static bool has_acpi_build = true; +static bool rsdp_in_ram = true; static bool smbios_defaults = true; static bool smbios_legacy_mode; static bool smbios_uuid_encoded = true; @@ -154,6 +155,7 @@ static void pc_q35_init(MachineState *machine) guest_info->isapc_ram_fw = false; guest_info->has_acpi_build = has_acpi_build; guest_info->has_reserved_memory = has_reserved_memory; + guest_info->rsdp_in_ram = rsdp_in_ram; /* Migration was not supported in 2.0 for Q35, so do not bother * with this hack (see hw/i386/acpi-build.c). @@ -265,7 +267,7 @@ static void pc_q35_init(MachineState *machine) ide_drive_get(hd, ICH_AHCI(ahci)->ahci.ports); ahci_ide_create_devs(ahci, hd); - if (usb_enabled(false)) { + if (usb_enabled()) { /* Should we create 6 UHCI according to ich9 spec? */ ehci_create_ich9_with_companions(host_bus, 0x1d); } @@ -287,10 +289,35 @@ static void pc_q35_init(MachineState *machine) } } +static void pc_compat_2_2(MachineState *machine) +{ + rsdp_in_ram = false; + x86_cpu_compat_set_features("kvm64", FEAT_1_EDX, 0, CPUID_VME); + x86_cpu_compat_set_features("kvm32", FEAT_1_EDX, 0, CPUID_VME); + x86_cpu_compat_set_features("Conroe", FEAT_1_EDX, 0, CPUID_VME); + x86_cpu_compat_set_features("Penryn", FEAT_1_EDX, 0, CPUID_VME); + x86_cpu_compat_set_features("Nehalem", FEAT_1_EDX, 0, CPUID_VME); + x86_cpu_compat_set_features("Westmere", FEAT_1_EDX, 0, CPUID_VME); + x86_cpu_compat_set_features("SandyBridge", FEAT_1_EDX, 0, CPUID_VME); + x86_cpu_compat_set_features("Haswell", FEAT_1_EDX, 0, CPUID_VME); + x86_cpu_compat_set_features("Broadwell", FEAT_1_EDX, 0, CPUID_VME); + x86_cpu_compat_set_features("Opteron_G1", FEAT_1_EDX, 0, CPUID_VME); + x86_cpu_compat_set_features("Opteron_G2", FEAT_1_EDX, 0, CPUID_VME); + x86_cpu_compat_set_features("Opteron_G3", FEAT_1_EDX, 0, CPUID_VME); + x86_cpu_compat_set_features("Opteron_G4", FEAT_1_EDX, 0, CPUID_VME); + x86_cpu_compat_set_features("Opteron_G5", FEAT_1_EDX, 0, CPUID_VME); + x86_cpu_compat_set_features("Haswell", FEAT_1_ECX, 0, CPUID_EXT_F16C); + x86_cpu_compat_set_features("Haswell", FEAT_1_ECX, 0, CPUID_EXT_RDRAND); + x86_cpu_compat_set_features("Broadwell", FEAT_1_ECX, 0, CPUID_EXT_F16C); + x86_cpu_compat_set_features("Broadwell", FEAT_1_ECX, 0, CPUID_EXT_RDRAND); + machine->suppress_vmdesc = true; +} + static void pc_compat_2_1(MachineState *machine) { PCMachineState *pcms = PC_MACHINE(machine); + pc_compat_2_2(machine); pcms->enforce_aligned_dimm = false; smbios_uuid_encoded = false; x86_cpu_compat_set_features("coreduo", FEAT_1_ECX, CPUID_EXT_VMX, 0); @@ -334,6 +361,12 @@ static void pc_compat_1_4(MachineState *machine) x86_cpu_compat_set_features("Westmere", FEAT_1_ECX, 0, CPUID_EXT_PCLMULQDQ); } +static void pc_q35_init_2_2(MachineState *machine) +{ + pc_compat_2_2(machine); + pc_q35_init(machine); +} + static void pc_q35_init_2_1(MachineState *machine) { pc_compat_2_1(machine); @@ -377,16 +410,24 @@ static void pc_q35_init_1_4(MachineState *machine) .hot_add_cpu = pc_hot_add_cpu, \ .units_per_default_bus = 1 -#define PC_Q35_2_2_MACHINE_OPTIONS \ +#define PC_Q35_2_3_MACHINE_OPTIONS \ PC_Q35_MACHINE_OPTIONS, \ .default_machine_opts = "firmware=bios-256k.bin", \ .default_display = "std" +static QEMUMachine pc_q35_machine_v2_3 = { + PC_Q35_2_3_MACHINE_OPTIONS, + .name = "pc-q35-2.3", + .alias = "q35", + .init = pc_q35_init, +}; + +#define PC_Q35_2_2_MACHINE_OPTIONS PC_Q35_2_3_MACHINE_OPTIONS + static QEMUMachine pc_q35_machine_v2_2 = { PC_Q35_2_2_MACHINE_OPTIONS, .name = "pc-q35-2.2", - .alias = "q35", - .init = pc_q35_init, + .init = pc_q35_init_2_2, }; #define PC_Q35_2_1_MACHINE_OPTIONS \ @@ -465,6 +506,7 @@ static QEMUMachine pc_q35_machine_v1_4 = { static void pc_q35_machine_init(void) { + qemu_register_pc_machine(&pc_q35_machine_v2_3); qemu_register_pc_machine(&pc_q35_machine_v2_2); qemu_register_pc_machine(&pc_q35_machine_v2_1); qemu_register_pc_machine(&pc_q35_machine_v2_0); diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c index 75913c5..662d997 100644 --- a/hw/i386/pc_sysfw.c +++ b/hw/i386/pc_sysfw.c @@ -204,9 +204,7 @@ static void old_pc_system_rom_init(MemoryRegion *rom_memory, bool isapc_ram_fw) fprintf(stderr, "qemu: could not load PC BIOS '%s'\n", bios_name); exit(1); } - if (filename) { - g_free(filename); - } + g_free(filename); /* map the last 128KB of the BIOS in ISA space */ isa_bios_size = bios_size; diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl index e1cee5d..16eaca3 100644 --- a/hw/i386/q35-acpi-dsdt.dsl +++ b/hw/i386/q35-acpi-dsdt.dsl @@ -48,23 +48,6 @@ DefinitionBlock ( /**************************************************************** * PCI Bus definition ****************************************************************/ -#define BOARD_SPECIFIC_PCI_RESOURSES \ - WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, \ - 0x0000, \ - 0x0000, \ - 0x0CD7, \ - 0x0000, \ - 0x0CD8, \ - ,, , TypeStatic) \ - /* 0xcd8-0xcf7 hole for CPU hotplug, hw/acpi/ich9.c:ICH9_PROC_BASE */ \ - WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, \ - 0x0000, \ - 0x0D00, \ - 0xFFFF, \ - 0x0000, \ - 0xF300, \ - ,, , TypeStatic) - Scope(\_SB) { Device(PCI0) { Name(_HID, EisaId("PNP0A08")) @@ -131,7 +114,6 @@ DefinitionBlock ( } } -#include "acpi-dsdt-pci-crs.dsl" #include "acpi-dsdt-hpet.dsl" @@ -168,7 +150,6 @@ DefinitionBlock ( } } -#define DSDT_APPLESMC_STA q35_dsdt_applesmc_sta #include "acpi-dsdt-isa.dsl" diff --git a/hw/i386/q35-acpi-dsdt.hex.generated b/hw/i386/q35-acpi-dsdt.hex.generated index 4807bdf..ed9a2cc 100644 --- a/hw/i386/q35-acpi-dsdt.hex.generated +++ b/hw/i386/q35-acpi-dsdt.hex.generated @@ -3,12 +3,12 @@ static unsigned char Q35AcpiDsdtAmlCode[] = { 0x53, 0x44, 0x54, -0xf6, -0x1f, +0xb8, +0x1d, 0x0, 0x0, 0x1, -0x91, +0x35, 0x42, 0x58, 0x50, @@ -31,8 +31,8 @@ static unsigned char Q35AcpiDsdtAmlCode[] = { 0x4e, 0x54, 0x4c, -0x28, -0x8, +0x7, +0x11, 0x14, 0x20, 0x10, @@ -344,372 +344,21 @@ static unsigned char Q35AcpiDsdtAmlCode[] = { 0x44, 0x57, 0x33, -0xa1, -0xc, -0x7d, -0x43, -0x44, -0x57, -0x31, -0xa, -0x4, -0x43, -0x44, -0x57, -0x31, -0xa4, -0x6b, -0x10, -0x4e, -0x15, -0x2e, -0x5f, -0x53, -0x42, -0x5f, -0x50, -0x43, -0x49, -0x30, -0x8, -0x43, -0x52, -0x45, -0x53, -0x11, -0x42, -0x7, -0xa, -0x6e, -0x88, -0xd, -0x0, -0x2, -0xc, -0x0, -0x0, -0x0, -0x0, -0x0, -0xff, -0x0, -0x0, -0x0, -0x0, -0x1, -0x47, -0x1, -0xf8, -0xc, -0xf8, -0xc, -0x1, -0x8, -0x88, -0xd, -0x0, -0x1, -0xc, -0x3, -0x0, -0x0, -0x0, -0x0, -0xd7, -0xc, -0x0, -0x0, -0xd8, -0xc, -0x88, -0xd, -0x0, -0x1, -0xc, -0x3, -0x0, -0x0, -0x0, -0xd, -0xff, -0xff, -0x0, -0x0, -0x0, -0xf3, -0x87, -0x17, -0x0, -0x0, -0xc, -0x3, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0xa, -0x0, -0xff, -0xff, -0xb, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x2, -0x0, -0x87, -0x17, -0x0, -0x0, -0xc, -0x1, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0xe0, -0xff, -0xff, -0xbf, -0xfe, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0xc0, -0x1e, -0x79, -0x0, -0x8, -0x43, -0x52, -0x36, -0x34, -0x11, -0x33, -0xa, -0x30, -0x8a, -0x2b, -0x0, -0x0, -0xc, -0x3, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x80, -0x0, -0x0, -0x0, -0xff, -0xff, -0xff, -0xff, -0xff, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x80, -0x0, -0x0, -0x0, -0x79, -0x0, -0x14, -0x41, -0xa, -0x5f, -0x43, -0x52, -0x53, -0x0, -0x8a, -0x43, -0x52, -0x45, -0x53, -0xa, -0x5c, -0x50, -0x53, -0x33, -0x32, -0x8a, -0x43, -0x52, -0x45, -0x53, -0xa, -0x60, -0x50, -0x45, -0x33, -0x32, -0x8a, -0x43, -0x52, -0x45, -0x53, -0xa, -0x68, -0x50, -0x4c, -0x33, -0x32, -0x70, -0x50, -0x30, -0x53, -0x5f, -0x50, -0x53, -0x33, -0x32, -0x70, -0x50, -0x30, -0x45, -0x5f, -0x50, -0x45, -0x33, -0x32, -0x70, -0x72, -0x74, -0x50, -0x30, -0x45, -0x5f, -0x50, -0x30, -0x53, -0x5f, -0x0, -0x1, -0x0, -0x50, -0x4c, -0x33, -0x32, -0xa0, -0xc, -0x93, -0x50, -0x31, -0x56, -0x5f, -0x0, -0xa4, -0x43, -0x52, -0x45, -0x53, -0x8f, -0x43, -0x52, -0x36, -0x34, -0xa, -0xe, -0x50, -0x53, -0x36, -0x34, -0x8f, -0x43, -0x52, -0x36, -0x34, -0xa, -0x16, -0x50, -0x45, -0x36, -0x34, -0x8f, -0x43, -0x52, -0x36, -0x34, -0xa, -0x26, -0x50, -0x4c, -0x36, -0x34, -0x70, -0x50, -0x31, -0x53, -0x5f, -0x50, -0x53, -0x36, -0x34, -0x70, -0x50, -0x31, -0x45, -0x5f, -0x50, -0x45, -0x36, -0x34, -0x70, -0x50, -0x31, -0x4c, -0x5f, -0x50, -0x4c, -0x36, -0x34, -0x84, +0xa1, +0xc, +0x7d, 0x43, -0x52, -0x45, -0x53, +0x44, +0x57, +0x31, +0xa, +0x4, 0x43, -0x52, -0x36, -0x34, -0x60, +0x44, +0x57, +0x31, 0xa4, -0x60, +0x6b, 0x10, 0x4d, 0x8, @@ -978,8 +627,8 @@ static unsigned char Q35AcpiDsdtAmlCode[] = { 0x4e, 0x1, 0x10, -0x4a, -0x1e, +0x4c, +0x1b, 0x2f, 0x3, 0x5f, @@ -996,52 +645,6 @@ static unsigned char Q35AcpiDsdtAmlCode[] = { 0x5f, 0x5b, 0x82, -0x2c, -0x53, -0x4d, -0x43, -0x5f, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x6, -0x10, -0x0, -0x1, -0x8, -0x5f, -0x53, -0x54, -0x41, -0xa, -0xf0, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0x10, -0xa, -0xd, -0x47, -0x1, -0x0, -0x3, -0x0, -0x3, -0x1, -0x20, -0x22, -0x40, -0x0, -0x79, -0x0, -0x5b, -0x82, 0x2d, 0x52, 0x54, @@ -6959,8 +6562,8 @@ static unsigned char Q35AcpiDsdtAmlCode[] = { 0x53, 0x1, 0x10, -0x42, -0x11, +0x4d, +0xc, 0x5f, 0x53, 0x42, @@ -7057,32 +6660,6 @@ static unsigned char Q35AcpiDsdtAmlCode[] = { 0x22, 0xa, 0xc8, -0x5b, -0x80, -0x50, -0x52, -0x53, -0x54, -0x1, -0xb, -0xd8, -0xc, -0xa, -0x20, -0x5b, -0x81, -0xc, -0x50, -0x52, -0x53, -0x54, -0x1, -0x50, -0x52, -0x53, -0x5f, -0x40, -0x10, 0x14, 0x4a, 0x6, @@ -7190,52 +6767,9 @@ static unsigned char Q35AcpiDsdtAmlCode[] = { 0x3, 0x75, 0x60, -0x5b, -0x82, -0x29, -0x50, -0x52, -0x45, -0x53, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xa, -0x6, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0xd, -0xa, -0xa, -0x47, -0x1, -0xd8, -0xc, -0xd8, -0xc, -0x0, -0x20, -0x79, -0x0, -0x8, -0x5f, -0x53, -0x54, -0x41, -0xa, -0xb, 0x10, -0x40, -0x31, +0x44, +0x2a, 0x2e, 0x5f, 0x53, @@ -7247,8 +6781,8 @@ static unsigned char Q35AcpiDsdtAmlCode[] = { 0x30, 0x5b, 0x82, -0x43, -0x30, +0x47, +0x29, 0x4d, 0x48, 0x50, @@ -7298,37 +6832,6 @@ static unsigned char Q35AcpiDsdtAmlCode[] = { 0x65, 0x73, 0x0, -0x5b, -0x80, -0x48, -0x50, -0x4d, -0x52, -0x1, -0xb, -0x0, -0xa, -0xa, -0x18, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0xd, -0xa, -0xa, -0x47, -0x1, -0x0, -0xa, -0x0, -0xa, -0x0, -0x18, -0x79, -0x0, 0x14, 0x13, 0x5f, @@ -7350,89 +6853,12 @@ static unsigned char Q35AcpiDsdtAmlCode[] = { 0xa, 0xb, 0x5b, -0x81, -0x1f, -0x48, -0x50, -0x4d, -0x52, -0x3, -0x4d, -0x52, -0x42, -0x4c, -0x20, -0x4d, -0x52, -0x42, -0x48, -0x20, -0x4d, -0x52, -0x4c, -0x4c, -0x20, -0x4d, -0x52, -0x4c, -0x48, -0x20, -0x4d, -0x50, -0x58, -0x5f, -0x20, -0x5b, -0x81, -0x13, -0x48, -0x50, -0x4d, -0x52, -0x1, -0x0, -0x40, -0xa, -0x4d, -0x45, -0x53, -0x5f, -0x1, -0x4d, -0x49, -0x4e, -0x53, -0x1, -0x5b, 0x1, 0x4d, 0x4c, 0x43, 0x4b, 0x0, -0x5b, -0x81, -0x15, -0x48, -0x50, -0x4d, -0x52, -0x3, -0x4d, -0x53, -0x45, -0x4c, -0x20, -0x4d, -0x4f, -0x45, -0x56, -0x20, -0x4d, -0x4f, -0x53, -0x43, -0x20, 0x14, 0x4a, 0x4, @@ -8182,6 +7608,3 @@ static unsigned char Q35AcpiDsdtAmlCode[] = { 0x46, 0x0 }; -static unsigned short q35_dsdt_applesmc_sta[] = { -0x3fa -}; diff --git a/hw/i386/smbios.c b/hw/i386/smbios.c index 024e594..1341e02 100644 --- a/hw/i386/smbios.c +++ b/hw/i386/smbios.c @@ -91,6 +91,7 @@ static struct { static struct { const char *loc_pfx, *bank, *manufacturer, *serial, *asset, *part; + uint16_t speed; } type17; static QemuOptsList qemu_smbios_opts = { @@ -304,6 +305,10 @@ static const QemuOptDesc qemu_smbios_type17_opts[] = { .name = "part", .type = QEMU_OPT_STRING, .help = "part number", + },{ + .name = "speed", + .type = QEMU_OPT_NUMBER, + .help = "maximum capable speed", }, { /* end of list */ } }; @@ -618,8 +623,9 @@ static void smbios_build_type_4_table(unsigned instance) SMBIOS_TABLE_SET_STR(4, processor_version_str, type4.version); t->voltage = 0; t->external_clock = cpu_to_le16(0); /* Unknown */ - t->max_speed = cpu_to_le16(0); /* Unknown */ - t->current_speed = 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 */ @@ -696,13 +702,13 @@ static void smbios_build_type_17_table(unsigned instance, uint64_t size) 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(0); /* Unknown */ + 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 = cpu_to_le16(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 */ @@ -850,7 +856,8 @@ void smbios_get_tables(uint8_t **tables, size_t *tables_len, } #define MAX_DIMM_SZ (16ll * ONE_GB) -#define GET_DIMM_SZ ((i < dimm_cnt - 1) ? MAX_DIMM_SZ : ram_size % MAX_DIMM_SZ) +#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; @@ -906,7 +913,7 @@ void smbios_entry_add(QemuOpts *opts) qemu_opts_validate(opts, qemu_smbios_file_opts, &local_err); if (local_err) { - error_report("%s", error_get_pretty(local_err)); + error_report_err(local_err); exit(1); } @@ -992,7 +999,7 @@ void smbios_entry_add(QemuOpts *opts) case 0: qemu_opts_validate(opts, qemu_smbios_type0_opts, &local_err); if (local_err) { - error_report("%s", error_get_pretty(local_err)); + error_report_err(local_err); exit(1); } save_opt(&type0.vendor, opts, "vendor"); @@ -1012,7 +1019,7 @@ void smbios_entry_add(QemuOpts *opts) case 1: qemu_opts_validate(opts, qemu_smbios_type1_opts, &local_err); if (local_err) { - error_report("%s", error_get_pretty(local_err)); + error_report_err(local_err); exit(1); } save_opt(&type1.manufacturer, opts, "manufacturer"); @@ -1034,7 +1041,7 @@ void smbios_entry_add(QemuOpts *opts) case 2: qemu_opts_validate(opts, qemu_smbios_type2_opts, &local_err); if (local_err) { - error_report("%s", error_get_pretty(local_err)); + error_report_err(local_err); exit(1); } save_opt(&type2.manufacturer, opts, "manufacturer"); @@ -1047,7 +1054,7 @@ void smbios_entry_add(QemuOpts *opts) case 3: qemu_opts_validate(opts, qemu_smbios_type3_opts, &local_err); if (local_err) { - error_report("%s", error_get_pretty(local_err)); + error_report_err(local_err); exit(1); } save_opt(&type3.manufacturer, opts, "manufacturer"); @@ -1059,7 +1066,7 @@ void smbios_entry_add(QemuOpts *opts) case 4: qemu_opts_validate(opts, qemu_smbios_type4_opts, &local_err); if (local_err) { - error_report("%s", error_get_pretty(local_err)); + error_report_err(local_err); exit(1); } save_opt(&type4.sock_pfx, opts, "sock_pfx"); @@ -1072,7 +1079,7 @@ void smbios_entry_add(QemuOpts *opts) case 17: qemu_opts_validate(opts, qemu_smbios_type17_opts, &local_err); if (local_err) { - error_report("%s", error_get_pretty(local_err)); + error_report_err(local_err); exit(1); } save_opt(&type17.loc_pfx, opts, "loc_pfx"); @@ -1081,6 +1088,7 @@ void smbios_entry_add(QemuOpts *opts) 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", diff --git a/hw/i386/ssdt-mem.dsl b/hw/i386/ssdt-mem.dsl deleted file mode 100644 index 22ff5dd..0000000 --- a/hw/i386/ssdt-mem.dsl +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Memory hotplug ACPI DSDT static objects definitions - * - * Copyright ProfitBricks GmbH 2012 - * Copyright (C) 2013-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 - */ - -/* This file is the basis for the ssdt_mem[] variable in src/acpi.c. - * It defines the contents of the memory device object. At - * runtime, a dynamically generated SSDT will contain one copy of this - * AML snippet for every possible memory device in the system. The - * objects will be placed in the \_SB_ namespace. - * - * In addition to the aml code generated from this file, the - * src/acpi.c file creates a MTFY method with an entry for each memdevice: - * Method(MTFY, 2) { - * If (LEqual(Arg0, 0x00)) { Notify(MP00, Arg1) } - * If (LEqual(Arg0, 0x01)) { Notify(MP01, Arg1) } - * ... - * } - */ -#include "hw/acpi/pc-hotplug.h" - -ACPI_EXTRACT_ALL_CODE ssdm_mem_aml - -DefinitionBlock ("ssdt-mem.aml", "SSDT", 0x02, "BXPC", "CSSDT", 0x1) -{ - - External(\_SB.PCI0.MEMORY_HOTPLUG_DEVICE.MEMORY_SLOT_CRS_METHOD, MethodObj) - External(\_SB.PCI0.MEMORY_HOTPLUG_DEVICE.MEMORY_SLOT_STATUS_METHOD, MethodObj) - External(\_SB.PCI0.MEMORY_HOTPLUG_DEVICE.MEMORY_SLOT_OST_METHOD, MethodObj) - External(\_SB.PCI0.MEMORY_HOTPLUG_DEVICE.MEMORY_SLOT_PROXIMITY_METHOD, MethodObj) - - Scope(\_SB) { -/* v------------------ DO NOT EDIT ------------------v */ - ACPI_EXTRACT_DEVICE_START ssdt_mem_start - ACPI_EXTRACT_DEVICE_END ssdt_mem_end - ACPI_EXTRACT_DEVICE_STRING ssdt_mem_name - Device(MPAA) { - ACPI_EXTRACT_NAME_STRING ssdt_mem_id - Name(_UID, "0xAA") -/* ^------------------ DO NOT EDIT ------------------^ - * Don't change the above without also updating the C code. - */ - Name(_HID, EISAID("PNP0C80")) - - Method(_CRS, 0) { - Return(\_SB.PCI0.MEMORY_HOTPLUG_DEVICE.MEMORY_SLOT_CRS_METHOD(_UID)) - } - - Method(_STA, 0) { - Return(\_SB.PCI0.MEMORY_HOTPLUG_DEVICE.MEMORY_SLOT_STATUS_METHOD(_UID)) - } - - Method(_PXM, 0) { - Return(\_SB.PCI0.MEMORY_HOTPLUG_DEVICE.MEMORY_SLOT_PROXIMITY_METHOD(_UID)) - } - - Method(_OST, 3) { - \_SB.PCI0.MEMORY_HOTPLUG_DEVICE.MEMORY_SLOT_OST_METHOD(_UID, Arg0, Arg1, Arg2) - } - } - } -} diff --git a/hw/i386/ssdt-mem.hex.generated b/hw/i386/ssdt-mem.hex.generated deleted file mode 100644 index b3bfbbd..0000000 --- a/hw/i386/ssdt-mem.hex.generated +++ /dev/null @@ -1,213 +0,0 @@ -static unsigned char ssdt_mem_id[] = { -0x35 -}; -static unsigned char ssdm_mem_aml[] = { -0x53, -0x53, -0x44, -0x54, -0xc7, -0x0, -0x0, -0x0, -0x2, -0x66, -0x42, -0x58, -0x50, -0x43, -0x0, -0x0, -0x43, -0x53, -0x53, -0x44, -0x54, -0x0, -0x0, -0x0, -0x1, -0x0, -0x0, -0x0, -0x49, -0x4e, -0x54, -0x4c, -0x28, -0x8, -0x14, -0x20, -0x10, -0x42, -0xa, -0x5c, -0x5f, -0x53, -0x42, -0x5f, -0x5b, -0x82, -0x49, -0x9, -0x4d, -0x50, -0x41, -0x41, -0x8, -0x5f, -0x55, -0x49, -0x44, -0xd, -0x30, -0x78, -0x41, -0x41, -0x0, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xc, -0x41, -0xd0, -0xc, -0x80, -0x14, -0x1e, -0x5f, -0x43, -0x52, -0x53, -0x0, -0xa4, -0x5c, -0x2f, -0x4, -0x5f, -0x53, -0x42, -0x5f, -0x50, -0x43, -0x49, -0x30, -0x4d, -0x48, -0x50, -0x44, -0x4d, -0x43, -0x52, -0x53, -0x5f, -0x55, -0x49, -0x44, -0x14, -0x1e, -0x5f, -0x53, -0x54, -0x41, -0x0, -0xa4, -0x5c, -0x2f, -0x4, -0x5f, -0x53, -0x42, -0x5f, -0x50, -0x43, -0x49, -0x30, -0x4d, -0x48, -0x50, -0x44, -0x4d, -0x52, -0x53, -0x54, -0x5f, -0x55, -0x49, -0x44, -0x14, -0x1e, -0x5f, -0x50, -0x58, -0x4d, -0x0, -0xa4, -0x5c, -0x2f, -0x4, -0x5f, -0x53, -0x42, -0x5f, -0x50, -0x43, -0x49, -0x30, -0x4d, -0x48, -0x50, -0x44, -0x4d, -0x50, -0x58, -0x4d, -0x5f, -0x55, -0x49, -0x44, -0x14, -0x20, -0x5f, -0x4f, -0x53, -0x54, -0x3, -0x5c, -0x2f, -0x4, -0x5f, -0x53, -0x42, -0x5f, -0x50, -0x43, -0x49, -0x30, -0x4d, -0x48, -0x50, -0x44, -0x4d, -0x4f, -0x53, -0x54, -0x5f, -0x55, -0x49, -0x44, -0x68, -0x69, -0x6a -}; -static unsigned char ssdt_mem_start[] = { -0x2c -}; -static unsigned char ssdt_mem_end[] = { -0xc7 -}; -static unsigned char ssdt_mem_name[] = { -0x30 -}; diff --git a/hw/i386/ssdt-misc.dsl b/hw/i386/ssdt-misc.dsl deleted file mode 100644 index 1e3baae..0000000 --- a/hw/i386/ssdt-misc.dsl +++ /dev/null @@ -1,122 +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 "hw/acpi/pc-hotplug.h" - -ACPI_EXTRACT_ALL_CODE ssdp_misc_aml - -DefinitionBlock ("ssdt-misc.aml", "SSDT", 0x01, "BXPC", "BXSSDTSUSP", 0x1) -{ - -/**************************************************************** - * PCI memory ranges - ****************************************************************/ - - Scope(\) { - ACPI_EXTRACT_NAME_DWORD_CONST acpi_pci32_start - Name(P0S, 0x12345678) - ACPI_EXTRACT_NAME_DWORD_CONST acpi_pci32_end - Name(P0E, 0x12345678) - ACPI_EXTRACT_NAME_BYTE_CONST acpi_pci64_valid - Name(P1V, 0x12) - ACPI_EXTRACT_NAME_BUFFER8 acpi_pci64_start - Name(P1S, Buffer() { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }) - ACPI_EXTRACT_NAME_BUFFER8 acpi_pci64_end - Name(P1E, Buffer() { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }) - ACPI_EXTRACT_NAME_BUFFER8 acpi_pci64_length - Name(P1L, Buffer() { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }) - ACPI_EXTRACT_NAME_DWORD_CONST ssdt_mctrl_nr_slots - Name(MEMORY_SLOTS_NUMBER, 0x12345678) - } - - -/**************************************************************** - * Suspend - ****************************************************************/ - - Scope(\) { - /* - * S3 (suspend-to-ram), S4 (suspend-to-disk) and S5 (power-off) type codes: - * must match piix4 emulation. - */ - - ACPI_EXTRACT_NAME_STRING acpi_s3_name - Name(_S3, Package(0x04) { - One, /* PM1a_CNT.SLP_TYP */ - One, /* PM1b_CNT.SLP_TYP */ - Zero, /* reserved */ - Zero /* reserved */ - }) - ACPI_EXTRACT_NAME_STRING acpi_s4_name - ACPI_EXTRACT_PKG_START acpi_s4_pkg - Name(_S4, Package(0x04) { - 0x2, /* PM1a_CNT.SLP_TYP */ - 0x2, /* PM1b_CNT.SLP_TYP */ - Zero, /* reserved */ - Zero /* reserved */ - }) - Name(_S5, Package(0x04) { - Zero, /* PM1a_CNT.SLP_TYP */ - Zero, /* PM1b_CNT.SLP_TYP */ - Zero, /* reserved */ - Zero /* reserved */ - }) - } - - External(\_SB.PCI0, DeviceObj) - External(\_SB.PCI0.ISA, DeviceObj) - - Scope(\_SB.PCI0.ISA) { - Device(PEVT) { - Name(_HID, "QEMU0001") - /* PEST will be patched to be Zero if no such device */ - ACPI_EXTRACT_NAME_WORD_CONST ssdt_isa_pest - Name(PEST, 0xFFFF) - OperationRegion(PEOR, SystemIO, PEST, 0x01) - Field(PEOR, ByteAcc, NoLock, Preserve) { - PEPT, 8, - } - - Method(_STA, 0, NotSerialized) { - Store(PEST, Local0) - If (LEqual(Local0, Zero)) { - Return (0x00) - } Else { - Return (0x0F) - } - } - - Method(RDPT, 0, NotSerialized) { - Store(PEPT, Local0) - Return (Local0) - } - - Method(WRPT, 1, NotSerialized) { - Store(Arg0, PEPT) - } - - Name(_CRS, ResourceTemplate() { - IO(Decode16, 0x00, 0x00, 0x01, 0x01, IO) - }) - - CreateWordField(_CRS, IO._MIN, IOMN) - CreateWordField(_CRS, IO._MAX, IOMX) - - Method(_INI, 0, NotSerialized) { - Store(PEST, IOMN) - Store(PEST, IOMX) - } - } - } -} diff --git a/hw/i386/ssdt-misc.hex.generated b/hw/i386/ssdt-misc.hex.generated deleted file mode 100644 index cbcf61d..0000000 --- a/hw/i386/ssdt-misc.hex.generated +++ /dev/null @@ -1,399 +0,0 @@ -static unsigned char acpi_pci64_length[] = { -0x6f -}; -static unsigned char acpi_s4_pkg[] = { -0x99 -}; -static unsigned char ssdt_mctrl_nr_slots[] = { -0x7d -}; -static unsigned char acpi_s3_name[] = { -0x86 -}; -static unsigned char acpi_pci32_start[] = { -0x2f -}; -static unsigned char acpi_pci64_valid[] = { -0x43 -}; -static unsigned char ssdp_misc_aml[] = { -0x53, -0x53, -0x44, -0x54, -0x6c, -0x1, -0x0, -0x0, -0x1, -0x3, -0x42, -0x58, -0x50, -0x43, -0x0, -0x0, -0x42, -0x58, -0x53, -0x53, -0x44, -0x54, -0x53, -0x55, -0x1, -0x0, -0x0, -0x0, -0x49, -0x4e, -0x54, -0x4c, -0x28, -0x8, -0x14, -0x20, -0x10, -0x4c, -0x5, -0x5c, -0x0, -0x8, -0x50, -0x30, -0x53, -0x5f, -0xc, -0x78, -0x56, -0x34, -0x12, -0x8, -0x50, -0x30, -0x45, -0x5f, -0xc, -0x78, -0x56, -0x34, -0x12, -0x8, -0x50, -0x31, -0x56, -0x5f, -0xa, -0x12, -0x8, -0x50, -0x31, -0x53, -0x5f, -0x11, -0xb, -0xa, -0x8, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x8, -0x50, -0x31, -0x45, -0x5f, -0x11, -0xb, -0xa, -0x8, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x8, -0x50, -0x31, -0x4c, -0x5f, -0x11, -0xb, -0xa, -0x8, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x0, -0x8, -0x4d, -0x44, -0x4e, -0x52, -0xc, -0x78, -0x56, -0x34, -0x12, -0x10, -0x29, -0x5c, -0x0, -0x8, -0x5f, -0x53, -0x33, -0x5f, -0x12, -0x6, -0x4, -0x1, -0x1, -0x0, -0x0, -0x8, -0x5f, -0x53, -0x34, -0x5f, -0x12, -0x8, -0x4, -0xa, -0x2, -0xa, -0x2, -0x0, -0x0, -0x8, -0x5f, -0x53, -0x35, -0x5f, -0x12, -0x6, -0x4, -0x0, -0x0, -0x0, -0x0, -0x10, -0x40, -0xc, -0x5c, -0x2f, -0x3, -0x5f, -0x53, -0x42, -0x5f, -0x50, -0x43, -0x49, -0x30, -0x49, -0x53, -0x41, -0x5f, -0x5b, -0x82, -0x4d, -0xa, -0x50, -0x45, -0x56, -0x54, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xd, -0x51, -0x45, -0x4d, -0x55, -0x30, -0x30, -0x30, -0x31, -0x0, -0x8, -0x50, -0x45, -0x53, -0x54, -0xb, -0xff, -0xff, -0x5b, -0x80, -0x50, -0x45, -0x4f, -0x52, -0x1, -0x50, -0x45, -0x53, -0x54, -0x1, -0x5b, -0x81, -0xb, -0x50, -0x45, -0x4f, -0x52, -0x1, -0x50, -0x45, -0x50, -0x54, -0x8, -0x14, -0x18, -0x5f, -0x53, -0x54, -0x41, -0x0, -0x70, -0x50, -0x45, -0x53, -0x54, -0x60, -0xa0, -0x6, -0x93, -0x60, -0x0, -0xa4, -0x0, -0xa1, -0x4, -0xa4, -0xa, -0xf, -0x14, -0xe, -0x52, -0x44, -0x50, -0x54, -0x0, -0x70, -0x50, -0x45, -0x50, -0x54, -0x60, -0xa4, -0x60, -0x14, -0xc, -0x57, -0x52, -0x50, -0x54, -0x1, -0x70, -0x68, -0x50, -0x45, -0x50, -0x54, -0x8, -0x5f, -0x43, -0x52, -0x53, -0x11, -0xd, -0xa, -0xa, -0x47, -0x1, -0x0, -0x0, -0x0, -0x0, -0x1, -0x1, -0x79, -0x0, -0x8b, -0x5f, -0x43, -0x52, -0x53, -0xa, -0x2, -0x49, -0x4f, -0x4d, -0x4e, -0x8b, -0x5f, -0x43, -0x52, -0x53, -0xa, -0x4, -0x49, -0x4f, -0x4d, -0x58, -0x14, -0x18, -0x5f, -0x49, -0x4e, -0x49, -0x0, -0x70, -0x50, -0x45, -0x53, -0x54, -0x49, -0x4f, -0x4d, -0x4e, -0x70, -0x50, -0x45, -0x53, -0x54, -0x49, -0x4f, -0x4d, -0x58 -}; -static unsigned char ssdt_isa_pest[] = { -0xda -}; -static unsigned char acpi_s4_name[] = { -0x92 -}; -static unsigned char acpi_pci64_start[] = { -0x4d -}; -static unsigned char acpi_pci64_end[] = { -0x5e -}; -static unsigned char acpi_pci32_end[] = { -0x39 -}; diff --git a/hw/i386/ssdt-pcihp.dsl b/hw/i386/ssdt-pcihp.dsl deleted file mode 100644 index ac91c05..0000000 --- a/hw/i386/ssdt-pcihp.dsl +++ /dev/null @@ -1,100 +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 . - */ - -ACPI_EXTRACT_ALL_CODE ssdp_pcihp_aml - -DefinitionBlock ("ssdt-pcihp.aml", "SSDT", 0x01, "BXPC", "BXSSDTPCIHP", 0x1) -{ - -/**************************************************************** - * PCI hotplug - ****************************************************************/ - - /* Objects supplied by DSDT */ - External(\_SB.PCI0, DeviceObj) - External(\_SB.PCI0.PCEJ, MethodObj) - External(BSEL, IntObj) - - Scope(\_SB.PCI0) { - - /* Bulk generated PCI hotplug devices */ - ACPI_EXTRACT_DEVICE_START ssdt_pcihp_start - ACPI_EXTRACT_DEVICE_END ssdt_pcihp_end - ACPI_EXTRACT_DEVICE_STRING ssdt_pcihp_name - - // Extract the offsets of the device name, address dword and the slot - // name byte - we fill them in for each device. - Device(SAA) { - ACPI_EXTRACT_NAME_BYTE_CONST ssdt_pcihp_id - Name(_SUN, 0xAA) - ACPI_EXTRACT_NAME_DWORD_CONST ssdt_pcihp_adr - Name(_ADR, 0xAA0000) - Method(_EJ0, 1) { - PCEJ(BSEL, _SUN) - } - } - - ACPI_EXTRACT_DEVICE_START ssdt_pcinohp_start - ACPI_EXTRACT_DEVICE_END ssdt_pcinohp_end - ACPI_EXTRACT_DEVICE_STRING ssdt_pcinohp_name - - // Extract the offsets of the device name, address dword and the slot - // name byte - we fill them in for each device. - Device(SBB) { - ACPI_EXTRACT_NAME_DWORD_CONST ssdt_pcinohp_adr - Name(_ADR, 0xAA0000) - } - - ACPI_EXTRACT_DEVICE_START ssdt_pcivga_start - ACPI_EXTRACT_DEVICE_END ssdt_pcivga_end - ACPI_EXTRACT_DEVICE_STRING ssdt_pcivga_name - - // Extract the offsets of the device name, address dword and the slot - // name byte - we fill them in for each device. - Device(SCC) { - ACPI_EXTRACT_NAME_DWORD_CONST ssdt_pcivga_adr - Name(_ADR, 0xAA0000) - Method(_S1D, 0, NotSerialized) { - Return (0x00) - } - Method(_S2D, 0, NotSerialized) { - Return (0x00) - } - Method(_S3D, 0, NotSerialized) { - Return (0x00) - } - } - - ACPI_EXTRACT_DEVICE_START ssdt_pciqxl_start - ACPI_EXTRACT_DEVICE_END ssdt_pciqxl_end - ACPI_EXTRACT_DEVICE_STRING ssdt_pciqxl_name - - // Extract the offsets of the device name, address dword and the slot - // name byte - we fill them in for each device. - Device(SDD) { - ACPI_EXTRACT_NAME_DWORD_CONST ssdt_pciqxl_adr - Name(_ADR, 0xAA0000) - Method(_S1D, 0, NotSerialized) { - Return (0x00) - } - Method(_S2D, 0, NotSerialized) { - Return (0x00) - } - Method(_S3D, 0, NotSerialized) { - Return (0x03) // QXL - } - } - } -} diff --git a/hw/i386/ssdt-pcihp.hex.generated b/hw/i386/ssdt-pcihp.hex.generated deleted file mode 100644 index 72ffa84..0000000 --- a/hw/i386/ssdt-pcihp.hex.generated +++ /dev/null @@ -1,251 +0,0 @@ -static unsigned char ssdt_pcihp_name[] = { -0x34 -}; -static unsigned char ssdt_pcivga_end[] = { -0x99 -}; -static unsigned char ssdt_pcivga_name[] = { -0x70 -}; -static unsigned char ssdt_pcihp_adr[] = { -0x45 -}; -static unsigned char ssdt_pcinohp_end[] = { -0x6d -}; -static unsigned char ssdt_pcihp_end[] = { -0x5c -}; -static unsigned char ssdt_pciqxl_start[] = { -0x99 -}; -static unsigned char ssdt_pcinohp_name[] = { -0x5f -}; -static unsigned char ssdp_pcihp_aml[] = { -0x53, -0x53, -0x44, -0x54, -0xc6, -0x0, -0x0, -0x0, -0x1, -0x70, -0x42, -0x58, -0x50, -0x43, -0x0, -0x0, -0x42, -0x58, -0x53, -0x53, -0x44, -0x54, -0x50, -0x43, -0x1, -0x0, -0x0, -0x0, -0x49, -0x4e, -0x54, -0x4c, -0x15, -0x11, -0x13, -0x20, -0x10, -0x41, -0xa, -0x5c, -0x2e, -0x5f, -0x53, -0x42, -0x5f, -0x50, -0x43, -0x49, -0x30, -0x5b, -0x82, -0x29, -0x53, -0x41, -0x41, -0x5f, -0x8, -0x5f, -0x53, -0x55, -0x4e, -0xa, -0xaa, -0x8, -0x5f, -0x41, -0x44, -0x52, -0xc, -0x0, -0x0, -0xaa, -0x0, -0x14, -0x12, -0x5f, -0x45, -0x4a, -0x30, -0x1, -0x50, -0x43, -0x45, -0x4a, -0x42, -0x53, -0x45, -0x4c, -0x5f, -0x53, -0x55, -0x4e, -0x5b, -0x82, -0xf, -0x53, -0x42, -0x42, -0x5f, -0x8, -0x5f, -0x41, -0x44, -0x52, -0xc, -0x0, -0x0, -0xaa, -0x0, -0x5b, -0x82, -0x2a, -0x53, -0x43, -0x43, -0x5f, -0x8, -0x5f, -0x41, -0x44, -0x52, -0xc, -0x0, -0x0, -0xaa, -0x0, -0x14, -0x8, -0x5f, -0x53, -0x31, -0x44, -0x0, -0xa4, -0x0, -0x14, -0x8, -0x5f, -0x53, -0x32, -0x44, -0x0, -0xa4, -0x0, -0x14, -0x8, -0x5f, -0x53, -0x33, -0x44, -0x0, -0xa4, -0x0, -0x5b, -0x82, -0x2b, -0x53, -0x44, -0x44, -0x5f, -0x8, -0x5f, -0x41, -0x44, -0x52, -0xc, -0x0, -0x0, -0xaa, -0x0, -0x14, -0x8, -0x5f, -0x53, -0x31, -0x44, -0x0, -0xa4, -0x0, -0x14, -0x8, -0x5f, -0x53, -0x32, -0x44, -0x0, -0xa4, -0x0, -0x14, -0x9, -0x5f, -0x53, -0x33, -0x44, -0x0, -0xa4, -0xa, -0x3 -}; -static unsigned char ssdt_pciqxl_adr[] = { -0xa6 -}; -static unsigned char ssdt_pcinohp_adr[] = { -0x69 -}; -static unsigned char ssdt_pcivga_adr[] = { -0x7a -}; -static unsigned char ssdt_pciqxl_name[] = { -0x9c -}; -static unsigned char ssdt_pcivga_start[] = { -0x6d -}; -static unsigned char ssdt_pciqxl_end[] = { -0xc6 -}; -static unsigned char ssdt_pcihp_start[] = { -0x31 -}; -static unsigned char ssdt_pcihp_id[] = { -0x3e -}; -static unsigned char ssdt_pcinohp_start[] = { -0x5c -}; diff --git a/hw/i386/ssdt-proc.dsl b/hw/i386/ssdt-proc.dsl deleted file mode 100644 index 8229bfd..0000000 --- a/hw/i386/ssdt-proc.dsl +++ /dev/null @@ -1,63 +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 . - */ - -/* This file is the basis for the ssdt table generated in src/acpi.c. - * It defines the contents of the per-cpu Processor() object. At - * runtime, a dynamically generated SSDT will contain one copy of this - * AML snippet for every possible cpu in the system. The objects will - * be placed in the \_SB_ namespace. - * - * In addition to the aml code generated from this file, the - * src/acpi.c file creates a NTFY method with an entry for each cpu: - * Method(NTFY, 2) { - * If (LEqual(Arg0, 0x00)) { Notify(CP00, Arg1) } - * If (LEqual(Arg0, 0x01)) { Notify(CP01, Arg1) } - * ... - * } - * and a CPON array with the list of active and inactive cpus: - * Name(CPON, Package() { One, One, ..., Zero, Zero, ... }) - */ - -ACPI_EXTRACT_ALL_CODE ssdp_proc_aml - -DefinitionBlock ("ssdt-proc.aml", "SSDT", 0x01, "BXPC", "BXSSDT", 0x1) -{ - ACPI_EXTRACT_PROCESSOR_START ssdt_proc_start - ACPI_EXTRACT_PROCESSOR_END ssdt_proc_end - ACPI_EXTRACT_PROCESSOR_STRING ssdt_proc_name - Processor(CPAA, 0xAA, 0x00000000, 0x0) { - ACPI_EXTRACT_NAME_BYTE_CONST ssdt_proc_id - Name(ID, 0xAA) -/* - * The src/acpi.c code requires the above ACP_EXTRACT tags so that it can update - * CPAA and 0xAA with the appropriate CPU id (see - * SD_OFFSET_CPUHEX/CPUID1/CPUID2). Don't change the above without - * also updating the C code. - */ - Name(_HID, "ACPI0007") - External(CPMA, MethodObj) - External(CPST, MethodObj) - External(CPEJ, MethodObj) - Method(_MAT, 0) { - Return (CPMA(ID)) - } - Method(_STA, 0) { - Return (CPST(ID)) - } - Method(_EJ0, 1, NotSerialized) { - CPEJ(ID, Arg0) - } - } -} diff --git a/hw/i386/ssdt-proc.hex.generated b/hw/i386/ssdt-proc.hex.generated deleted file mode 100644 index 4df0734..0000000 --- a/hw/i386/ssdt-proc.hex.generated +++ /dev/null @@ -1,134 +0,0 @@ -static unsigned char ssdt_proc_name[] = { -0x28 -}; -static unsigned char ssdp_proc_aml[] = { -0x53, -0x53, -0x44, -0x54, -0x78, -0x0, -0x0, -0x0, -0x1, -0x7d, -0x42, -0x58, -0x50, -0x43, -0x0, -0x0, -0x42, -0x58, -0x53, -0x53, -0x44, -0x54, -0x0, -0x0, -0x1, -0x0, -0x0, -0x0, -0x49, -0x4e, -0x54, -0x4c, -0x15, -0x11, -0x13, -0x20, -0x5b, -0x83, -0x42, -0x5, -0x43, -0x50, -0x41, -0x41, -0xaa, -0x0, -0x0, -0x0, -0x0, -0x0, -0x8, -0x49, -0x44, -0x5f, -0x5f, -0xa, -0xaa, -0x8, -0x5f, -0x48, -0x49, -0x44, -0xd, -0x41, -0x43, -0x50, -0x49, -0x30, -0x30, -0x30, -0x37, -0x0, -0x14, -0xf, -0x5f, -0x4d, -0x41, -0x54, -0x0, -0xa4, -0x43, -0x50, -0x4d, -0x41, -0x49, -0x44, -0x5f, -0x5f, -0x14, -0xf, -0x5f, -0x53, -0x54, -0x41, -0x0, -0xa4, -0x43, -0x50, -0x53, -0x54, -0x49, -0x44, -0x5f, -0x5f, -0x14, -0xf, -0x5f, -0x45, -0x4a, -0x30, -0x1, -0x43, -0x50, -0x45, -0x4a, -0x49, -0x44, -0x5f, -0x5f, -0x68 -}; -static unsigned char ssdt_proc_id[] = { -0x38 -}; -static unsigned char ssdt_proc_end[] = { -0x78 -}; -static unsigned char ssdt_proc_start[] = { -0x24 -}; diff --git a/hw/i386/ssdt-tpm.hex.generated b/hw/i386/ssdt-tpm.hex.generated index 4a916a8..e84dc6c 100644 --- a/hw/i386/ssdt-tpm.hex.generated +++ b/hw/i386/ssdt-tpm.hex.generated @@ -8,7 +8,7 @@ static unsigned char ssdt_tpm_aml[] = { 0x0, 0x0, 0x1, -0xf, +0x1c, 0x42, 0x58, 0x50, @@ -31,9 +31,9 @@ static unsigned char ssdt_tpm_aml[] = { 0x4e, 0x54, 0x4c, -0x15, +0x7, 0x11, -0x13, +0x14, 0x20, 0x10, 0x38, diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 94f28e6..833fd45 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -34,15 +34,15 @@ #include #include -/* #define DEBUG_AHCI */ +#define DEBUG_AHCI 0 -#ifdef DEBUG_AHCI #define DPRINTF(port, fmt, ...) \ -do { fprintf(stderr, "ahci: %s: [%d] ", __FUNCTION__, port); \ - fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(port, fmt, ...) do {} while(0) -#endif +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,int slot); @@ -51,6 +51,10 @@ static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis); static void ahci_init_d2h(AHCIDevice *ad); static int ahci_dma_prepare_buf(IDEDMA *dma, int is_write); static void ahci_commit_buf(IDEDMA *dma, uint32_t tx_bytes); +static bool ahci_map_clb_address(AHCIDevice *ad); +static bool ahci_map_fis_address(AHCIDevice *ad); +static void ahci_unmap_clb_address(AHCIDevice *ad); +static void ahci_unmap_fis_address(AHCIDevice *ad); static uint32_t ahci_port_read(AHCIState *s, int port, int offset) @@ -202,25 +206,15 @@ static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val) switch (offset) { case PORT_LST_ADDR: pr->lst_addr = val; - map_page(s->as, &s->dev[port].lst, - ((uint64_t)pr->lst_addr_hi << 32) | pr->lst_addr, 1024); - s->dev[port].cur_cmd = NULL; break; case PORT_LST_ADDR_HI: pr->lst_addr_hi = val; - map_page(s->as, &s->dev[port].lst, - ((uint64_t)pr->lst_addr_hi << 32) | pr->lst_addr, 1024); - s->dev[port].cur_cmd = NULL; break; case PORT_FIS_ADDR: pr->fis_addr = val; - map_page(s->as, &s->dev[port].res_fis, - ((uint64_t)pr->fis_addr_hi << 32) | pr->fis_addr, 256); break; case PORT_FIS_ADDR_HI: pr->fis_addr_hi = val; - map_page(s->as, &s->dev[port].res_fis, - ((uint64_t)pr->fis_addr_hi << 32) | pr->fis_addr, 256); break; case PORT_IRQ_STAT: pr->irq_stat &= ~val; @@ -231,14 +225,32 @@ static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val) ahci_check_irq(s); break; case PORT_CMD: - pr->cmd = val & ~(PORT_CMD_LIST_ON | PORT_CMD_FIS_ON); + /* Block any Read-only fields from being set; + * including LIST_ON and FIS_ON. */ + pr->cmd = (pr->cmd & PORT_CMD_RO_MASK) | (val & ~PORT_CMD_RO_MASK); if (pr->cmd & PORT_CMD_START) { - pr->cmd |= PORT_CMD_LIST_ON; + if (ahci_map_clb_address(&s->dev[port])) { + pr->cmd |= PORT_CMD_LIST_ON; + } else { + error_report("AHCI: Failed to start DMA engine: " + "bad command list buffer address"); + } + } else if (pr->cmd & PORT_CMD_LIST_ON) { + ahci_unmap_clb_address(&s->dev[port]); + pr->cmd = pr->cmd & ~(PORT_CMD_LIST_ON); } if (pr->cmd & PORT_CMD_FIS_RX) { - pr->cmd |= PORT_CMD_FIS_ON; + if (ahci_map_fis_address(&s->dev[port])) { + pr->cmd |= PORT_CMD_FIS_ON; + } else { + error_report("AHCI: Failed to start FIS receive engine: " + "bad FIS receive buffer address"); + } + } else if (pr->cmd & PORT_CMD_FIS_ON) { + ahci_unmap_fis_address(&s->dev[port]); + pr->cmd = pr->cmd & ~(PORT_CMD_FIS_ON); } /* XXX usually the FIS would be pending on the bus here and @@ -551,7 +563,7 @@ static void ahci_reset_port(AHCIState *s, int port) static void debug_print_fis(uint8_t *fis, int cmd_len) { -#ifdef DEBUG_AHCI +#if DEBUG_AHCI int i; fprintf(stderr, "fis:"); @@ -565,6 +577,37 @@ static void debug_print_fis(uint8_t *fis, int cmd_len) #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); + return ad->res_fis != NULL; +} + +static void ahci_unmap_fis_address(AHCIDevice *ad) +{ + 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); + return ad->lst != NULL; +} + +static void ahci_unmap_clb_address(AHCIDevice *ad) +{ + 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, int port, uint32_t finished) { AHCIDevice *ad = &s->dev[port]; @@ -580,7 +623,7 @@ static void ahci_write_fis_sdb(AHCIState *s, int port, uint32_t finished) sdb_fis = (SDBFIS *)&ad->res_fis[RES_FIS_SDBFIS]; ide_state = &ad->port.ifs[0]; - sdb_fis->type = 0xA1; + sdb_fis->type = SATA_FIS_TYPE_SDB; /* Interrupt pending & Notification bit */ sdb_fis->flags = (ad->hba->control_regs.irqstatus ? (1 << 6) : 0); sdb_fis->status = ide_state->status & 0x77; @@ -631,7 +674,7 @@ static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len) pio_fis = &ad->res_fis[RES_FIS_PSFIS]; - pio_fis[0] = 0x5f; + 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; @@ -690,7 +733,7 @@ static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis) d2h_fis = &ad->res_fis[RES_FIS_RFIS]; - d2h_fis[0] = 0x34; + 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; @@ -799,7 +842,7 @@ static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist, qemu_sglist_init(sglist, qbus->parent, (sglist_alloc_hint - off_idx), ad->hba->as); - qemu_sglist_add(sglist, le64_to_cpu(tbl[off_idx].addr + off_pos), + qemu_sglist_add(sglist, le64_to_cpu(tbl[off_idx].addr) + off_pos, prdt_tbl_entry_size(&tbl[off_idx]) - off_pos); for (i = off_idx + 1; i < sglist_alloc_hint; i++) { @@ -1154,14 +1197,17 @@ out: static void ahci_start_dma(IDEDMA *dma, IDEState *s, BlockCompletionFunc *dma_cb) { -#ifdef DEBUG_AHCI AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); -#endif 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. */ +} + /** * Called in DMA R/W chains to read the PRDT, utilizing ahci_populate_sglist. * Not currently invoked by PIO R/W chains, @@ -1228,12 +1274,6 @@ static int ahci_dma_rw_buf(IDEDMA *dma, int is_write) return 1; } -static int ahci_dma_set_unit(IDEDMA *dma, int unit) -{ - /* only a single unit per link */ - return 0; -} - static void ahci_cmd_done(IDEDMA *dma) { AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); @@ -1254,19 +1294,14 @@ static void ahci_irq_set(void *opaque, int n, int level) { } -static void ahci_dma_restart_cb(void *opaque, int running, RunState state) -{ -} - static const IDEDMAOps ahci_dma_ops = { .start_dma = ahci_start_dma, + .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, - .set_unit = ahci_dma_set_unit, .cmd_done = ahci_cmd_done, - .restart_cb = ahci_dma_restart_cb, }; void ahci_init(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports) @@ -1296,6 +1331,7 @@ void ahci_init(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports) ad->port_no = i; ad->port.dma = &ad->dma; ad->port.dma->ops = &ahci_dma_ops; + ide_register_restart_cb(&ad->port); } } @@ -1335,6 +1371,7 @@ static const VMStateDescription vmstate_ahci_device = { .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), @@ -1366,23 +1403,27 @@ static int ahci_state_post_load(void *opaque, int version_id) for (i = 0; i < s->ports; i++) { ad = &s->dev[i]; - AHCIPortRegs *pr = &ad->port_regs; - map_page(s->as, &ad->lst, - ((uint64_t)pr->lst_addr_hi << 32) | pr->lst_addr, 1024); - map_page(s->as, &ad->res_fis, - ((uint64_t)pr->fis_addr_hi << 32) | pr->fis_addr, 256); + ahci_map_clb_address(ad); + ahci_map_fis_address(ad); /* - * All pending i/o should be flushed out on a migrate. However, - * we might not have cleared the busy_slot since this is done - * in a bh. Also, issue i/o against any slots that are pending. + * 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) && - !(ad->port.ifs[0].status & (BUSY_STAT|DRQ_STAT))) { - pr->cmd_issue &= ~(1 << ad->busy_slot); - ad->busy_slot = -1; + 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 = &((AHCICmdHdr *)ad->lst)[ad->busy_slot]; } - check_cmd(s, i); } return 0; diff --git a/hw/ide/ahci.h b/hw/ide/ahci.h index e0d2eb8..501c002 100644 --- a/hw/ide/ahci.h +++ b/hw/ide/ahci.h @@ -132,6 +132,8 @@ #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 */ @@ -156,7 +158,10 @@ #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_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 diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c index c63b7e5..79dd167 100644 --- a/hw/ide/atapi.c +++ b/hw/ide/atapi.c @@ -252,7 +252,6 @@ static void ide_atapi_cmd_reply(IDEState *s, int size, int max_size) s->packet_transfer_size = size; s->io_buffer_size = size; /* dma: send the reply data as one chunk */ s->elementary_transfer_size = 0; - s->io_buffer_index = 0; if (s->atapi_dma) { block_acct_start(blk_get_stats(s->blk), &s->acct, size, @@ -261,6 +260,7 @@ static void ide_atapi_cmd_reply(IDEState *s, int size, int max_size) 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); } } @@ -368,7 +368,6 @@ static void ide_atapi_cmd_read_dma(IDEState *s, int lba, int nb_sectors, { s->lba = lba; s->packet_transfer_size = nb_sectors * sector_size; - s->io_buffer_index = 0; s->io_buffer_size = 0; s->cd_sector_size = sector_size; @@ -394,6 +393,23 @@ static void ide_atapi_cmd_read(IDEState *s, int lba, int nb_sectors, } } + +/* Called by *_restart_bh when the transfer function points + * to ide_atapi_cmd + */ +void ide_atapi_dma_restart(IDEState *s) +{ + /* + * I'm not sure we have enough stored to restart the command + * safely, so give the guest an error it should recover from. + * I'm assuming most guests will try to recover from something + * listed as a medium error on a CD; it seems to work on Linux. + * This would be more of a problem if we did any other type of + * DMA operation. + */ + ide_atapi_cmd_error(s, MEDIUM_ERROR, ASC_NO_SEEK_COMPLETE); +} + static inline uint8_t ide_atapi_set_profile(uint8_t *buf, uint8_t *index, uint16_t profile) { @@ -621,20 +637,107 @@ static void cmd_request_sense(IDEState *s, uint8_t *buf) static void cmd_inquiry(IDEState *s, uint8_t *buf) { + uint8_t page_code = buf[2]; int max_len = buf[4]; - buf[0] = 0x05; /* CD-ROM */ - buf[1] = 0x80; /* removable */ - buf[2] = 0x00; /* ISO */ - buf[3] = 0x21; /* ATAPI-2 (XXX: put ATAPI-4 ?) */ - buf[4] = 31; /* additional length */ - 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); - ide_atapi_cmd_reply(s, 36, max_len); + 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); + return; } static void cmd_get_configuration(IDEState *s, uint8_t *buf) @@ -880,6 +983,7 @@ static void cmd_start_stop_unit(IDEState *s, uint8_t* buf) if (pwrcnd) { /* eject/load only happens for power condition == 0 */ + ide_atapi_cmd_ok(s); return; } diff --git a/hw/ide/cmd646.c b/hw/ide/cmd646.c index c8b0322..66fb9d9 100644 --- a/hw/ide/cmd646.c +++ b/hw/ide/cmd646.c @@ -326,7 +326,7 @@ static void cmd646_pci_config_write(PCIDevice *d, uint32_t addr, uint32_t val, } /* CMD646 PCI IDE controller */ -static int pci_cmd646_ide_initfn(PCIDevice *dev) +static void pci_cmd646_ide_realize(PCIDevice *dev, Error **errp) { PCIIDEState *d = PCI_IDE(dev); uint8_t *pci_conf = dev->config; @@ -368,13 +368,11 @@ static int pci_cmd646_ide_initfn(PCIDevice *dev) bmdma_init(&d->bus[i], &d->bmdma[i], d); d->bmdma[i].bus = &d->bus[i]; - qemu_add_vm_change_state_handler(d->bus[i].dma->ops->restart_cb, - &d->bmdma[i].dma); + ide_register_restart_cb(&d->bus[i]); } vmstate_register(DEVICE(dev), 0, &vmstate_ide_pci, d); qemu_register_reset(cmd646_reset, d); - return 0; } static void pci_cmd646_ide_exitfn(PCIDevice *dev) @@ -410,7 +408,7 @@ static void cmd646_ide_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = pci_cmd646_ide_initfn; + 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; diff --git a/hw/ide/core.c b/hw/ide/core.c index d4af5e2..822519b 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -561,6 +561,8 @@ static bool ide_sect_range_ok(IDEState *s, return true; } +static void ide_sector_read(IDEState *s); + static void ide_sector_read_cb(void *opaque, int ret) { IDEState *s = opaque; @@ -585,17 +587,15 @@ static void ide_sector_read_cb(void *opaque, int ret) n = s->req_nb_sectors; } - /* 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); - 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); s->io_buffer_offset += 512 * n; + ide_set_irq(s->bus); } -void ide_sector_read(IDEState *s) +static void ide_sector_read(IDEState *s) { int64_t sector_num; int n; @@ -646,6 +646,9 @@ static void dma_buf_commit(IDEState *s, uint32_t tx_bytes) 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); } @@ -666,7 +669,7 @@ static int ide_handle_rw_error(IDEState *s, int error, int op) BlockErrorAction action = blk_get_error_action(s->blk, is_read, error); if (action == BLOCK_ERROR_ACTION_STOP) { - s->bus->dma->ops->set_unit(s->bus->dma, s->unit); + assert(s->bus->retry_unit == s->unit); s->bus->error_status = op; } else if (action == BLOCK_ERROR_ACTION_REPORT) { if (op & IDE_RETRY_DMA) { @@ -679,7 +682,7 @@ static int ide_handle_rw_error(IDEState *s, int error, int op) return action != BLOCK_ERROR_ACTION_IGNORE; } -void ide_dma_cb(void *opaque, int ret) +static void ide_dma_cb(void *opaque, int ret) { IDEState *s = opaque; int n; @@ -777,7 +780,6 @@ eot: 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_index = 0; s->io_buffer_size = 0; s->dma_cmd = dma_cmd; @@ -799,11 +801,17 @@ static void ide_sector_start_dma(IDEState *s, enum ide_dma_cmd dma_cmd) 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; @@ -836,6 +844,7 @@ static void ide_sector_write_cb(void *opaque, int ret) s->nsector -= n; s->io_buffer_offset += 512 * n; + ide_set_sector(s, ide_get_sector(s) + n); if (s->nsector == 0) { /* no more sectors to write */ ide_transfer_stop(s); @@ -847,7 +856,6 @@ static void ide_sector_write_cb(void *opaque, int ret) ide_transfer_start(s, s->io_buffer, n1 * BDRV_SECTOR_SIZE, ide_sector_write); } - ide_set_sector(s, ide_get_sector(s) + n); if (win2k_install_hack && ((++s->irq_count % 16) == 0)) { /* It seems there is a bug in the Windows 2000 installer HDD @@ -863,7 +871,7 @@ static void ide_sector_write_cb(void *opaque, int ret) } } -void ide_sector_write(IDEState *s) +static void ide_sector_write(IDEState *s) { int64_t sector_num; int n; @@ -917,7 +925,7 @@ static void ide_flush_cb(void *opaque, int ret) ide_set_irq(s->bus); } -void ide_flush_cache(IDEState *s) +static void ide_flush_cache(IDEState *s) { if (s->blk == NULL) { ide_flush_cb(s, 0); @@ -2013,11 +2021,17 @@ void ide_data_writew(void *opaque, uint32_t addr, uint32_t val) } 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) + if (p >= s->data_end) { + s->status &= ~DRQ_STAT; s->end_transfer_func(s); + } } uint32_t ide_data_readw(void *opaque, uint32_t addr) @@ -2034,11 +2048,17 @@ uint32_t ide_data_readw(void *opaque, uint32_t addr) } 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) + if (p >= s->data_end) { + s->status &= ~DRQ_STAT; s->end_transfer_func(s); + } return ret; } @@ -2055,11 +2075,17 @@ void ide_data_writel(void *opaque, uint32_t addr, uint32_t val) } 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) + if (p >= s->data_end) { + s->status &= ~DRQ_STAT; s->end_transfer_func(s); + } } uint32_t ide_data_readl(void *opaque, uint32_t addr) @@ -2076,11 +2102,17 @@ uint32_t ide_data_readl(void *opaque, uint32_t addr) } 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) + if (p >= s->data_end) { + s->status &= ~DRQ_STAT; s->end_transfer_func(s); + } return ret; } @@ -2314,22 +2346,101 @@ static int ide_nop_int(IDEDMA *dma, int x) return 0; } -static int32_t ide_nop_int32(IDEDMA *dma, int x) +static void ide_nop(IDEDMA *dma) { - return 0; } -static void ide_nop_restart(void *opaque, int x, RunState y) +static int32_t ide_nop_int32(IDEDMA *dma, int x) { + return 0; } static const IDEDMAOps ide_dma_nop_ops = { .prepare_buf = ide_nop_int32, + .restart_dma = ide_nop, .rw_buf = ide_nop_int, - .set_unit = ide_nop_int, - .restart_cb = ide_nop_restart, }; +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; + + if (error_status & IDE_RETRY_DMA) { + if (error_status & IDE_RETRY_TRIM) { + ide_restart_dma(s, IDE_DMA_TRIM); + } else { + ide_restart_dma(s, is_read ? IDE_DMA_READ : IDE_DMA_WRITE); + } + } else if (error_status & IDE_RETRY_PIO) { + if (is_read) { + ide_sector_read(s); + } else { + ide_sector_write(s); + } + } else if (error_status & IDE_RETRY_FLUSH) { + ide_flush_cache(s); + } else { + /* + * We've not got any bits to tell us about ATAPI - but + * we do have the end_transfer_func that tells us what + * we're trying to do. + */ + if (s->end_transfer_func == ide_atapi_cmd) { + ide_atapi_dma_restart(s); + } + } +} + +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, @@ -2417,6 +2528,7 @@ static int ide_drive_pio_post_load(void *opaque, int version_id) 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; } @@ -2556,10 +2668,13 @@ const VMStateDescription vmstate_ide_drive = { static const VMStateDescription vmstate_ide_error_status = { .name ="ide_bus/error", - .version_id = 1, + .version_id = 2, .minimum_version_id = 1, .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() } }; diff --git a/hw/ide/ich.c b/hw/ide/ich.c index fb1d095..b1d8874 100644 --- a/hw/ide/ich.c +++ b/hw/ide/ich.c @@ -98,7 +98,7 @@ static void pci_ich9_reset(DeviceState *dev) ahci_reset(&d->ahci); } -static int pci_ich9_ahci_init(PCIDevice *dev) +static void pci_ich9_ahci_realize(PCIDevice *dev, Error **errp) { struct AHCIPCIState *d; int sata_cap_offset; @@ -123,10 +123,11 @@ static int pci_ich9_ahci_init(PCIDevice *dev) pci_register_bar(dev, ICH9_MEM_BAR, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->ahci.mem); - sata_cap_offset = pci_add_capability(dev, PCI_CAP_ID_SATA, - ICH9_SATA_CAP_OFFSET, SATA_CAP_SIZE); + 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_offset; + return; } sata_cap = dev->config + sata_cap_offset; @@ -139,8 +140,6 @@ static int pci_ich9_ahci_init(PCIDevice *dev) * 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); - - return 0; } static void pci_ich9_uninit(PCIDevice *dev) @@ -158,7 +157,7 @@ static void ich_ahci_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = pci_ich9_ahci_init; + 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; diff --git a/hw/ide/internal.h b/hw/ide/internal.h index 8a3eca4..965cc55 100644 --- a/hw/ide/internal.h +++ b/hw/ide/internal.h @@ -289,6 +289,7 @@ typedef struct IDEDMAOps IDEDMAOps; #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 @@ -296,6 +297,7 @@ typedef struct IDEDMAOps IDEDMAOps; #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 @@ -434,10 +436,9 @@ struct IDEDMAOps { DMAInt32Func *prepare_buf; DMAu32Func *commit_buf; DMAIntFunc *rw_buf; - DMAIntFunc *set_unit; + DMAVoidFunc *restart_dma; DMAStopFunc *set_inactive; DMAVoidFunc *cmd_done; - DMARestartFunc *restart_cb; DMAVoidFunc *reset; }; @@ -453,6 +454,8 @@ struct IDEBus { IDEDevice *master; IDEDevice *slave; IDEState ifs[2]; + QEMUBH *bh; + int bus_id; int max_units; IDEDMA *dma; @@ -461,6 +464,9 @@ struct IDEBus { 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" @@ -520,6 +526,9 @@ 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); @@ -529,6 +538,7 @@ void ide_dma_error(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); @@ -547,12 +557,9 @@ int ide_init_drive(IDEState *s, BlockBackend *blk, IDEDriveKind kind, 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_dma_cb(void *opaque, int ret); -void ide_sector_write(IDEState *s); -void ide_sector_read(IDEState *s); -void ide_flush_cache(IDEState *s); void ide_transfer_start(IDEState *s, uint8_t *buf, int size, EndTransferFunc *end_transfer_func); diff --git a/hw/ide/isa.c b/hw/ide/isa.c index b084162..9f80503 100644 --- a/hw/ide/isa.c +++ b/hw/ide/isa.c @@ -74,7 +74,8 @@ static void isa_ide_realizefn(DeviceState *dev, Error **errp) 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) @@ -88,9 +89,7 @@ ISADevice *isa_ide_init(ISABus *bus, int iobase, int iobase2, int isairq, qdev_prop_set_uint32(dev, "iobase", iobase); qdev_prop_set_uint32(dev, "iobase2", iobase2); qdev_prop_set_uint32(dev, "irq", isairq); - if (qdev_init(dev) < 0) { - return NULL; - } + qdev_init_nofail(dev); s = ISA_IDE(dev); if (hd0) { diff --git a/hw/ide/macio.c b/hw/ide/macio.c index f6074f2..a009674 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -558,10 +558,6 @@ static int32_t ide_nop_int32(IDEDMA *dma, int x) return 0; } -static void ide_nop_restart(void *opaque, int x, RunState y) -{ -} - static void ide_dbdma_start(IDEDMA *dma, IDEState *s, BlockCompletionFunc *cb) { @@ -576,8 +572,6 @@ static const IDEDMAOps dbdma_ops = { .start_dma = ide_dbdma_start, .prepare_buf = ide_nop_int32, .rw_buf = ide_nop_int, - .set_unit = ide_nop_int, - .restart_cb = ide_nop_restart, }; static void macio_ide_realizefn(DeviceState *dev, Error **errp) diff --git a/hw/ide/pci.c b/hw/ide/pci.c index bee5ad3..1b3d1c1 100644 --- a/hw/ide/pci.c +++ b/hw/ide/pci.c @@ -42,13 +42,10 @@ static void bmdma_start_dma(IDEDMA *dma, IDEState *s, { BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma); - bm->unit = s->unit; bm->dma_cb = dma_cb; bm->cur_prd_last = 0; bm->cur_prd_addr = 0; bm->cur_prd_len = 0; - bm->sector_num = ide_get_sector(s); - bm->nsector = s->nsector; if (bm->status & BM_STATUS_DMAING) { bm->dma_cb(bmdma_active_if(bm), 0); @@ -99,7 +96,7 @@ static int32_t bmdma_prepare_buf(IDEDMA *dma, int is_write) * This should accommodate the largest ATA transaction * for LBA48 (65,536 sectors) and 32K sector sizes. */ if (s->sg.size > INT32_MAX) { - error_report("IDE: sglist describes more than 2GiB.\n"); + error_report("IDE: sglist describes more than 2GiB."); break; } bm->cur_prd_addr += l; @@ -163,20 +160,11 @@ static int bmdma_rw_buf(IDEDMA *dma, int is_write) return 1; } -static int bmdma_set_unit(IDEDMA *dma, int unit) -{ - BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma); - bm->unit = unit; - - return 0; -} - static void bmdma_set_inactive(IDEDMA *dma, bool more) { BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma); bm->dma_cb = NULL; - bm->unit = -1; if (more) { bm->status |= BM_STATUS_DMAING; } else { @@ -184,72 +172,11 @@ static void bmdma_set_inactive(IDEDMA *dma, bool more) } } -static void bmdma_restart_dma(BMDMAState *bm, enum ide_dma_cmd dma_cmd) -{ - IDEState *s = bmdma_active_if(bm); - - ide_set_sector(s, bm->sector_num); - s->io_buffer_index = 0; - s->io_buffer_size = 0; - s->nsector = bm->nsector; - s->dma_cmd = dma_cmd; - bm->cur_addr = bm->addr; - bm->dma_cb = ide_dma_cb; - bmdma_start_dma(&bm->dma, s, bm->dma_cb); -} - -/* TODO This should be common IDE code */ -static void bmdma_restart_bh(void *opaque) -{ - BMDMAState *bm = opaque; - IDEBus *bus = bm->bus; - bool is_read; - int error_status; - - qemu_bh_delete(bm->bh); - bm->bh = NULL; - - if (bm->unit == (uint8_t) -1) { - return; - } - - 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. */ - error_status = bus->error_status; - bus->error_status = 0; - - if (error_status & IDE_RETRY_DMA) { - if (error_status & IDE_RETRY_TRIM) { - bmdma_restart_dma(bm, IDE_DMA_TRIM); - } else { - bmdma_restart_dma(bm, is_read ? IDE_DMA_READ : IDE_DMA_WRITE); - } - } else if (error_status & IDE_RETRY_PIO) { - if (is_read) { - ide_sector_read(bmdma_active_if(bm)); - } else { - ide_sector_write(bmdma_active_if(bm)); - } - } else if (error_status & IDE_RETRY_FLUSH) { - ide_flush_cache(bmdma_active_if(bm)); - } -} - -static void bmdma_restart_cb(void *opaque, int running, RunState state) +static void bmdma_restart_dma(IDEDMA *dma) { - IDEDMA *dma = opaque; BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma); - if (!running) - return; - - if (!bm->bh) { - bm->bh = qemu_bh_new(bmdma_restart_bh, &bm->dma); - qemu_bh_schedule(bm->bh); - } + bm->cur_addr = bm->addr; } static void bmdma_cancel(BMDMAState *bm) @@ -275,8 +202,6 @@ static void bmdma_reset(IDEDMA *dma) bm->cur_prd_last = 0; bm->cur_prd_addr = 0; bm->cur_prd_len = 0; - bm->sector_num = 0; - bm->nsector = 0; } static void bmdma_irq(void *opaque, int n, int level) @@ -393,6 +318,9 @@ static void ide_bmdma_pre_save(void *opaque) BMDMAState *bm = opaque; uint8_t abused_bits = BM_MIGRATION_COMPAT_STATUS_BITS; + 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); } @@ -409,6 +337,11 @@ static int ide_bmdma_post_load(void *opaque, int version_id) 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; } @@ -445,9 +378,9 @@ static const VMStateDescription vmstate_bmdma = { VMSTATE_UINT8(cmd, BMDMAState), VMSTATE_UINT8(migration_compat_status, BMDMAState), VMSTATE_UINT32(addr, BMDMAState), - VMSTATE_INT64(sector_num, BMDMAState), - VMSTATE_UINT32(nsector, BMDMAState), - VMSTATE_UINT8(unit, 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 = (VMStateSubsection []) { @@ -471,7 +404,7 @@ static int ide_pci_post_load(void *opaque, int version_id) 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].unit &= 1; + d->bmdma[i].migration_retry_unit &= 1; ide_bmdma_post_load(&d->bmdma[i], -1); } @@ -512,9 +445,8 @@ static const struct IDEDMAOps bmdma_ops = { .start_dma = bmdma_start_dma, .prepare_buf = bmdma_prepare_buf, .rw_buf = bmdma_rw_buf, - .set_unit = bmdma_set_unit, + .restart_dma = bmdma_restart_dma, .set_inactive = bmdma_set_inactive, - .restart_cb = bmdma_restart_cb, .reset = bmdma_reset, }; diff --git a/hw/ide/pci.h b/hw/ide/pci.h index 2e9314a..0f2d4b9 100644 --- a/hw/ide/pci.h +++ b/hw/ide/pci.h @@ -22,18 +22,18 @@ typedef struct BMDMAState { uint32_t cur_prd_last; uint32_t cur_prd_addr; uint32_t cur_prd_len; - uint8_t unit; BlockCompletionFunc *dma_cb; - int64_t sector_num; - uint32_t nsector; MemoryRegion addr_ioport; MemoryRegion extra_io; - QEMUBH *bh; 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; @@ -62,8 +62,8 @@ typedef struct PCIIDEState { static inline IDEState *bmdma_active_if(BMDMAState *bmdma) { - assert(bmdma->unit != (uint8_t)-1); - return bmdma->bus->ifs + bmdma->unit; + assert(bmdma->bus->retry_unit != (uint8_t)-1); + return bmdma->bus->ifs + bmdma->bus->retry_unit; } diff --git a/hw/ide/piix.c b/hw/ide/piix.c index b0172fb..5a26c86 100644 --- a/hw/ide/piix.c +++ b/hw/ide/piix.c @@ -143,12 +143,11 @@ static void pci_piix_init_ports(PCIIDEState *d) { bmdma_init(&d->bus[i], &d->bmdma[i], d); d->bmdma[i].bus = &d->bus[i]; - qemu_add_vm_change_state_handler(d->bus[i].dma->ops->restart_cb, - &d->bmdma[i].dma); + ide_register_restart_cb(&d->bus[i]); } } -static int pci_piix_ide_initfn(PCIDevice *dev) +static void pci_piix_ide_realize(PCIDevice *dev, Error **errp) { PCIIDEState *d = PCI_IDE(dev); uint8_t *pci_conf = dev->config; @@ -163,8 +162,6 @@ static int pci_piix_ide_initfn(PCIDevice *dev) vmstate_register(DEVICE(dev), 0, &vmstate_ide_pci, d); pci_piix_init_ports(d); - - return 0; } int pci_piix3_xen_ide_unplug(DeviceState *dev) @@ -172,6 +169,7 @@ int pci_piix3_xen_ide_unplug(DeviceState *dev) PCIIDEState *pci_ide; DriveInfo *di; int i; + IDEDevice *idedev; pci_ide = PCI_IDE(dev); @@ -184,6 +182,12 @@ int pci_piix3_xen_ide_unplug(DeviceState *dev) 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; blk_unref(blk); } } @@ -238,7 +242,7 @@ static void piix3_ide_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = pci_piix_ide_initfn; + 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; @@ -258,7 +262,7 @@ static void piix3_ide_xen_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = pci_piix_ide_initfn; + k->realize = pci_piix_ide_realize; k->vendor_id = PCI_VENDOR_ID_INTEL; k->device_id = PCI_DEVICE_ID_INTEL_82371SB_1; k->class_id = PCI_CLASS_STORAGE_IDE; @@ -276,7 +280,7 @@ static void piix4_ide_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = pci_piix_ide_initfn; + 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; diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c index b4f096e..788b361 100644 --- a/hw/ide/qdev.c +++ b/hw/ide/qdev.c @@ -163,12 +163,17 @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind) 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, 65536, 16, 255, &err); if (err) { - error_report("%s", error_get_pretty(err)); - error_free(err); + error_report_err(err); return -1; } } diff --git a/hw/ide/via.c b/hw/ide/via.c index 4d8089d..e2da9ef 100644 --- a/hw/ide/via.c +++ b/hw/ide/via.c @@ -166,13 +166,12 @@ static void vt82c686b_init_ports(PCIIDEState *d) { bmdma_init(&d->bus[i], &d->bmdma[i], d); d->bmdma[i].bus = &d->bus[i]; - qemu_add_vm_change_state_handler(d->bus[i].dma->ops->restart_cb, - &d->bmdma[i].dma); + ide_register_restart_cb(&d->bus[i]); } } /* via ide func */ -static int vt82c686b_ide_initfn(PCIDevice *dev) +static void vt82c686b_ide_realize(PCIDevice *dev, Error **errp) { PCIIDEState *d = PCI_IDE(dev); uint8_t *pci_conf = dev->config; @@ -187,8 +186,6 @@ static int vt82c686b_ide_initfn(PCIDevice *dev) vmstate_register(DEVICE(dev), 0, &vmstate_ide_pci, d); vt82c686b_init_ports(d); - - return 0; } static void vt82c686b_ide_exitfn(PCIDevice *dev) @@ -215,7 +212,7 @@ static void via_ide_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = vt82c686b_ide_initfn; + 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; diff --git a/hw/input/adb.c b/hw/input/adb.c index 34c8058..a18eea2 100644 --- a/hw/input/adb.c +++ b/hw/input/adb.c @@ -118,6 +118,17 @@ static const TypeInfo adb_bus_type_info = { .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); @@ -301,9 +312,10 @@ static int adb_kbd_request(ADBDevice *d, uint8_t *obuf, static const VMStateDescription vmstate_adb_kbd = { .name = "adb_kbd", - .version_id = 1, - .minimum_version_id = 1, + .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), @@ -515,9 +527,11 @@ static void adb_mouse_reset(DeviceState *dev) static const VMStateDescription vmstate_adb_mouse = { .name = "adb_mouse", - .version_id = 1, - .minimum_version_id = 1, + .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), diff --git a/hw/input/hid.c b/hw/input/hid.c index 148c003..6841cb8 100644 --- a/hw/input/hid.c +++ b/hw/input/hid.c @@ -41,7 +41,7 @@ static const uint8_t hid_usage_keys[0x100] = { 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, 0x32, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, + 0xe2, 0x2c, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x53, 0x47, 0x5f, 0x60, 0x61, 0x56, 0x5c, 0x5d, 0x5e, 0x57, 0x59, 0x5a, 0x5b, 0x62, 0x63, 0x00, 0x00, 0x00, 0x44, @@ -514,6 +514,27 @@ 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; } diff --git a/hw/input/lm832x.c b/hw/input/lm832x.c index 9eb68e8..530a6e0 100644 --- a/hw/input/lm832x.c +++ b/hw/input/lm832x.c @@ -455,7 +455,7 @@ static const VMStateDescription vmstate_lm_kbd = { VMSTATE_UINT16_ARRAY(pwm.file, LM823KbdState, 256), VMSTATE_UINT8(pwm.faddr, LM823KbdState), VMSTATE_BUFFER(pwm.addr, LM823KbdState), - VMSTATE_TIMER_ARRAY(pwm.tm, LM823KbdState, 3), + VMSTATE_TIMER_PTR_ARRAY(pwm.tm, LM823KbdState, 3), VMSTATE_END_OF_LIST() } }; diff --git a/hw/input/milkymist-softusb.c b/hw/input/milkymist-softusb.c index 5a427f0..7b0f4db 100644 --- a/hw/input/milkymist-softusb.c +++ b/hw/input/milkymist-softusb.c @@ -194,10 +194,13 @@ static void softusb_kbd_hid_datain(HIDState *hs) return; } - len = hid_keyboard_poll(hs, s->kbd_hid_buffer, sizeof(s->kbd_hid_buffer)); + 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); + if (len == 8) { + softusb_kbd_changed(s); + } } } @@ -212,11 +215,13 @@ static void softusb_mouse_hid_datain(HIDState *hs) return; } - len = hid_pointer_poll(hs, s->mouse_hid_buffer, - sizeof(s->mouse_hid_buffer)); + 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); + if (len == 4) { + softusb_mouse_changed(s); + } } } diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c index 2b0cd3d..9b9a7d7 100644 --- a/hw/input/pckbd.c +++ b/hw/input/pckbd.c @@ -101,6 +101,12 @@ #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 */ @@ -367,13 +373,13 @@ static void kbd_reset(void *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; + 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 + 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); } diff --git a/hw/input/ps2.c b/hw/input/ps2.c index a466e25..4baeea2 100644 --- a/hw/input/ps2.c +++ b/hw/input/ps2.c @@ -27,6 +27,8 @@ #include "ui/input.h" #include "sysemu/sysemu.h" +#include "trace.h" + /* debug PC keyboard */ //#define DEBUG_KBD @@ -158,6 +160,7 @@ 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) { @@ -194,6 +197,7 @@ uint32_t ps2_read_data(void *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 @@ -218,12 +222,14 @@ uint32_t ps2_read_data(void *opaque) 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); @@ -233,6 +239,7 @@ 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: @@ -319,6 +326,7 @@ void ps2_write_keyboard(void *opaque, int val) void ps2_keyboard_set_translation(void *opaque, int mode) { PS2KbdState *s = (PS2KbdState *)opaque; + trace_ps2_keyboard_set_translation(opaque, mode); s->translate = mode; } @@ -364,6 +372,7 @@ static void ps2_mouse_send_packet(PS2MouseState *s) break; } + trace_ps2_mouse_send_packet(s, dx1, dy1, dz1, b); /* update deltas */ s->mouse_dx -= dx1; s->mouse_dy -= dy1; @@ -433,6 +442,7 @@ static void ps2_mouse_sync(DeviceState *dev) void ps2_mouse_fake_event(void *opaque) { PS2MouseState *s = opaque; + trace_ps2_mouse_fake_event(opaque); s->mouse_dx++; ps2_mouse_sync(opaque); } @@ -440,6 +450,8 @@ void ps2_mouse_fake_event(void *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 @@ -606,6 +618,7 @@ 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; @@ -616,6 +629,7 @@ 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; @@ -763,6 +777,7 @@ 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; @@ -784,6 +799,7 @@ 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); diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c index 4e62f25..042e960 100644 --- a/hw/intc/apic_common.c +++ b/hw/intc/apic_common.c @@ -177,12 +177,14 @@ bool apic_next_timer(APICCommonState *s, int64_t current_time) void apic_init_reset(DeviceState *dev) { - APICCommonState *s = APIC_COMMON(dev); + APICCommonState *s; + APICCommonClass *info; int i; - if (!s) { + if (!dev) { return; } + s = APIC_COMMON(dev); s->tpr = 0; s->spurious_vec = 0xff; s->log_dest = 0; @@ -206,16 +208,25 @@ void apic_init_reset(DeviceState *dev) 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) +void apic_designate_bsp(DeviceState *dev, bool bsp) { if (dev == NULL) { return; } APICCommonState *s = APIC_COMMON(dev); - s->apicbase |= MSR_IA32_APICBASE_BSP; + if (bsp) { + s->apicbase |= MSR_IA32_APICBASE_BSP; + } else { + s->apicbase &= ~MSR_IA32_APICBASE_BSP; + } } static void apic_reset_common(DeviceState *dev) diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index 270ce05..a04c822 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -704,7 +704,8 @@ static void gic_cpu_write(GICState *s, int cpu, int offset, uint32_t value) s->bpr[cpu] = (value & 0x7); break; case 0x10: /* End Of Interrupt */ - return gic_complete_irq(s, cpu, value & 0x3ff); + gic_complete_irq(s, cpu, value & 0x3ff); + return; case 0x1c: /* Aliased Binary Point */ if (s->revision >= 2) { s->abpr[cpu] = (value & 0x7); diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c index 5038885..e1952ad 100644 --- a/hw/intc/arm_gic_kvm.c +++ b/hw/intc/arm_gic_kvm.c @@ -92,6 +92,21 @@ static bool kvm_arm_gic_can_save_restore(GICState *s) return s->dev_fd >= 0; } +static bool kvm_gic_supports_attr(GICState *s, int group, int attrnum) +{ + struct kvm_device_attr attr = { + .group = group, + .attr = attrnum, + .flags = 0, + }; + + if (s->dev_fd == -1) { + return false; + } + + return kvm_device_ioctl(s->dev_fd, KVM_HAS_DEVICE_ATTR, &attr) == 0; +} + static void kvm_gic_access(GICState *s, int group, int offset, int cpu, uint32_t *val, bool write) { @@ -355,6 +370,11 @@ static void kvm_arm_gic_put(GICState *s) * 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); @@ -363,8 +383,6 @@ static void kvm_arm_gic_put(GICState *s) kvm_dist_put(s, 0x380, 1, s->num_irq, translate_clear); kvm_dist_put(s, 0x300, 1, s->num_irq, translate_active); - /* irq_state[n].trigger -> GICD_ICFRn */ - kvm_dist_put(s, 0xc00, 2, s->num_irq, translate_trigger); /* s->priorityX[irq] -> ICD_IPRIORITYRn */ kvm_dist_put(s, 0x400, 8, s->num_irq, translate_priority); @@ -553,6 +571,18 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp) return; } + if (kvm_gic_supports_attr(s, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0)) { + uint32_t numirqs = s->num_irq; + kvm_gic_access(s, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0, 0, &numirqs, 1); + } + + /* Tell the kernel to complete VGIC initialization now */ + if (kvm_gic_supports_attr(s, KVM_DEV_ARM_VGIC_GRP_CTRL, + KVM_DEV_ARM_VGIC_CTRL_INIT)) { + kvm_gic_access(s, KVM_DEV_ARM_VGIC_GRP_CTRL, + KVM_DEV_ARM_VGIC_CTRL_INIT, 0, 0, 1); + } + /* Distributor */ memory_region_init_reservation(&s->iomem, OBJECT(s), "kvm-gic_dist", 0x1000); diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index d0543d4..6ff6c7f 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -450,7 +450,7 @@ static const VMStateDescription vmstate_nvic = { VMSTATE_UINT32(systick.control, nvic_state), VMSTATE_UINT32(systick.reload, nvic_state), VMSTATE_INT64(systick.tick, nvic_state), - VMSTATE_TIMER(systick.timer, nvic_state), + VMSTATE_TIMER_PTR(systick.timer, nvic_state), VMSTATE_END_OF_LIST() } }; diff --git a/hw/intc/etraxfs_pic.c b/hw/intc/etraxfs_pic.c index 636262b..bd58868 100644 --- a/hw/intc/etraxfs_pic.c +++ b/hw/intc/etraxfs_pic.c @@ -131,11 +131,13 @@ static void nmi_handler(void *opaque, int irq, int level) } static void irq_handler(void *opaque, int irq, int level) -{ +{ struct etrax_pic *fs = (void *)opaque; - if (irq >= 30) - return nmi_handler(opaque, irq, level); + if (irq >= 30) { + nmi_handler(opaque, irq, level); + return; + } irq -= 1; fs->regs[R_R_VECT] &= ~(1 << irq); diff --git a/hw/intc/i8259.c b/hw/intc/i8259.c index c51901b..0f5c025 100644 --- a/hw/intc/i8259.c +++ b/hw/intc/i8259.c @@ -429,7 +429,7 @@ static void pic_realize(DeviceState *dev, Error **errp) pc->parent_realize(dev, errp); } -void pic_info(Monitor *mon, const QDict *qdict) +void hmp_info_pic(Monitor *mon, const QDict *qdict) { int i; PICCommonState *s; @@ -447,7 +447,7 @@ void pic_info(Monitor *mon, const QDict *qdict) } } -void irq_info(Monitor *mon, const QDict *qdict) +void hmp_info_irq(Monitor *mon, const QDict *qdict) { #ifndef DEBUG_IRQ_COUNT monitor_printf(mon, "irq statistic code not compiled.\n"); diff --git a/hw/intc/lm32_pic.c b/hw/intc/lm32_pic.c index 72fc9ef..641ee47 100644 --- a/hw/intc/lm32_pic.c +++ b/hw/intc/lm32_pic.c @@ -43,7 +43,7 @@ struct LM32PicState { typedef struct LM32PicState LM32PicState; static LM32PicState *pic; -void lm32_do_pic_info(Monitor *mon, const QDict *qdict) +void lm32_hmp_info_pic(Monitor *mon, const QDict *qdict) { if (pic == NULL) { return; @@ -53,7 +53,7 @@ void lm32_do_pic_info(Monitor *mon, const QDict *qdict) pic->im, pic->ip, pic->irq_state); } -void lm32_irq_info(Monitor *mon, const QDict *qdict) +void lm32_hmp_info_irq(Monitor *mon, const QDict *qdict) { int i; uint32_t count; diff --git a/hw/intc/openpic.c b/hw/intc/openpic.c index 7d1f3b9..87fe2e8 100644 --- a/hw/intc/openpic.c +++ b/hw/intc/openpic.c @@ -200,11 +200,14 @@ typedef enum IRQType { 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 { - /* Round up to the nearest 64 IRQs so that the queue length - * won't change when moving between 32 and 64 bit hosts. - */ - unsigned long queue[BITS_TO_LONGS((OPENPIC_MAX_IRQ + 63) & ~63)]; + unsigned long *queue; + int32_t queue_size; /* Only used for VMSTATE_BITMAP */ int next; int priority; } IRQQueue; @@ -240,6 +243,15 @@ typedef struct IRQSource { #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; @@ -288,14 +300,9 @@ typedef struct OpenPICState { IRQDest dst[MAX_CPU]; uint32_t nb_cpus; /* Timer registers */ - struct { - uint32_t tccr; /* Global timer current count register */ - uint32_t tbcr; /* Global timer base count register */ - } timers[OPENPIC_MAX_TMR]; + OpenPICTimer timers[OPENPIC_MAX_TMR]; /* Shared MSI registers */ - struct { - uint32_t msir; /* Shared Message Signaled Interrupt Register */ - } msi[MAX_MSI]; + OpenPICMSI msi[MAX_MSI]; uint32_t max_irq; uint32_t irq_ipi0; uint32_t irq_tim0; @@ -1013,7 +1020,7 @@ static void openpic_cpu_write_internal(void *opaque, hwaddr addr, DPRINTF("%s: cpu %d addr %#" HWADDR_PRIx " <= 0x%08x\n", __func__, idx, addr, val); - if (idx < 0) { + if (idx < 0 || idx >= opp->nb_cpus) { return; } @@ -1152,7 +1159,7 @@ static uint32_t openpic_cpu_read_internal(void *opaque, hwaddr addr, DPRINTF("%s: cpu %d addr %#" HWADDR_PRIx "\n", __func__, idx, addr); retval = 0xFFFFFFFF; - if (idx < 0) { + if (idx < 0 || idx >= opp->nb_cpus) { return retval; } @@ -1287,132 +1294,6 @@ static const MemoryRegionOps openpic_summary_ops_be = { }, }; -static void openpic_save_IRQ_queue(QEMUFile* f, IRQQueue *q) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(q->queue); i++) { - /* Always put the lower half of a 64-bit long first, in case we - * restore on a 32-bit host. The least significant bits correspond - * to lower IRQ numbers in the bitmap. - */ - qemu_put_be32(f, (uint32_t)q->queue[i]); -#if LONG_MAX > 0x7FFFFFFF - qemu_put_be32(f, (uint32_t)(q->queue[i] >> 32)); -#endif - } - - qemu_put_sbe32s(f, &q->next); - qemu_put_sbe32s(f, &q->priority); -} - -static void openpic_save(QEMUFile* f, void *opaque) -{ - OpenPICState *opp = (OpenPICState *)opaque; - unsigned int i; - - qemu_put_be32s(f, &opp->gcr); - qemu_put_be32s(f, &opp->vir); - qemu_put_be32s(f, &opp->pir); - qemu_put_be32s(f, &opp->spve); - qemu_put_be32s(f, &opp->tfrr); - - qemu_put_be32s(f, &opp->nb_cpus); - - for (i = 0; i < opp->nb_cpus; i++) { - qemu_put_sbe32s(f, &opp->dst[i].ctpr); - openpic_save_IRQ_queue(f, &opp->dst[i].raised); - openpic_save_IRQ_queue(f, &opp->dst[i].servicing); - qemu_put_buffer(f, (uint8_t *)&opp->dst[i].outputs_active, - sizeof(opp->dst[i].outputs_active)); - } - - for (i = 0; i < OPENPIC_MAX_TMR; i++) { - qemu_put_be32s(f, &opp->timers[i].tccr); - qemu_put_be32s(f, &opp->timers[i].tbcr); - } - - for (i = 0; i < opp->max_irq; i++) { - qemu_put_be32s(f, &opp->src[i].ivpr); - qemu_put_be32s(f, &opp->src[i].idr); - qemu_get_be32s(f, &opp->src[i].destmask); - qemu_put_sbe32s(f, &opp->src[i].last_cpu); - qemu_put_sbe32s(f, &opp->src[i].pending); - } -} - -static void openpic_load_IRQ_queue(QEMUFile* f, IRQQueue *q) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(q->queue); i++) { - unsigned long val; - - val = qemu_get_be32(f); -#if LONG_MAX > 0x7FFFFFFF - val <<= 32; - val |= qemu_get_be32(f); -#endif - - q->queue[i] = val; - } - - qemu_get_sbe32s(f, &q->next); - qemu_get_sbe32s(f, &q->priority); -} - -static int openpic_load(QEMUFile* f, void *opaque, int version_id) -{ - OpenPICState *opp = (OpenPICState *)opaque; - unsigned int i, nb_cpus; - - if (version_id != 1) { - return -EINVAL; - } - - qemu_get_be32s(f, &opp->gcr); - qemu_get_be32s(f, &opp->vir); - qemu_get_be32s(f, &opp->pir); - qemu_get_be32s(f, &opp->spve); - qemu_get_be32s(f, &opp->tfrr); - - qemu_get_be32s(f, &nb_cpus); - if (opp->nb_cpus != nb_cpus) { - return -EINVAL; - } - assert(nb_cpus > 0 && nb_cpus <= MAX_CPU); - - for (i = 0; i < opp->nb_cpus; i++) { - qemu_get_sbe32s(f, &opp->dst[i].ctpr); - openpic_load_IRQ_queue(f, &opp->dst[i].raised); - openpic_load_IRQ_queue(f, &opp->dst[i].servicing); - qemu_get_buffer(f, (uint8_t *)&opp->dst[i].outputs_active, - sizeof(opp->dst[i].outputs_active)); - } - - for (i = 0; i < OPENPIC_MAX_TMR; i++) { - qemu_get_be32s(f, &opp->timers[i].tccr); - qemu_get_be32s(f, &opp->timers[i].tbcr); - } - - for (i = 0; i < opp->max_irq; i++) { - uint32_t val; - - val = qemu_get_be32(f); - write_IRQreg_idr(opp, i, val); - val = qemu_get_be32(f); - write_IRQreg_ivpr(opp, i, val); - - qemu_get_be32s(f, &opp->src[i].ivpr); - qemu_get_be32s(f, &opp->src[i].idr); - qemu_get_be32s(f, &opp->src[i].destmask); - qemu_get_sbe32s(f, &opp->src[i].last_cpu); - qemu_get_sbe32s(f, &opp->src[i].pending); - } - - return 0; -} - static void openpic_reset(DeviceState *d) { OpenPICState *opp = OPENPIC(d); @@ -1446,12 +1327,14 @@ static void openpic_reset(DeviceState *d) write_IRQreg_idr(opp, i, opp->idr_reset); } /* Initialise IRQ destinations */ - for (i = 0; i < MAX_CPU; i++) { + for (i = 0; i < opp->nb_cpus; i++) { opp->dst[i].ctpr = 15; - memset(&opp->dst[i].raised, 0, sizeof(IRQQueue)); opp->dst[i].raised.next = -1; - memset(&opp->dst[i].servicing, 0, sizeof(IRQQueue)); + 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++) { @@ -1525,6 +1408,110 @@ static void map_list(OpenPICState *opp, const MemReg *list, int *count) } } +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); @@ -1631,10 +1618,12 @@ static void openpic_realize(DeviceState *dev, Error **errp) for (j = 0; j < OPENPIC_OUTPUT_NB; j++) { sysbus_init_irq(d, &opp->dst[i].irqs[j]); } - } - register_savevm(dev, "openpic", 0, 2, - openpic_save, openpic_load, opp); + 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); @@ -1653,6 +1642,7 @@ static void openpic_class_init(ObjectClass *oc, void *data) dc->realize = openpic_realize; dc->props = openpic_properties; dc->reset = openpic_reset; + dc->vmsd = &vmstate_openpic; } static const TypeInfo openpic_info = { diff --git a/hw/intc/openpic_kvm.c b/hw/intc/openpic_kvm.c index 3e2cd18..f7cac58 100644 --- a/hw/intc/openpic_kvm.c +++ b/hw/intc/openpic_kvm.c @@ -248,7 +248,6 @@ static void kvm_openpic_realize(DeviceState *dev, Error **errp) kvm_irqchip_add_irq_route(kvm_state, i, 0, i); } - kvm_irqfds_allowed = true; kvm_msi_via_irqfd_allowed = true; kvm_gsi_routing_allowed = true; diff --git a/hw/intc/s390_flic.c b/hw/intc/s390_flic.c index 03c5e89..02e10b7 100644 --- a/hw/intc/s390_flic.c +++ b/hw/intc/s390_flic.c @@ -30,7 +30,6 @@ S390FLICState *s390_get_flic(void) void s390_flic_init(void) { DeviceState *dev; - int r; dev = s390_flic_kvm_create(); if (!dev) { @@ -38,10 +37,7 @@ void s390_flic_init(void) object_property_add_child(qdev_get_machine(), TYPE_QEMU_S390_FLIC, OBJECT(dev), NULL); } - r = qdev_init(dev); - if (r) { - error_report("flic: couldn't create qdev"); - } + qdev_init_nofail(dev); } static int qemu_s390_register_io_adapter(S390FLICState *fs, uint32_t id, diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c index 20b19e9..c15453f 100644 --- a/hw/intc/xics_kvm.c +++ b/hw/intc/xics_kvm.c @@ -448,7 +448,6 @@ static void xics_kvm_realize(DeviceState *dev, Error **errp) } kvm_kernel_irqchip = true; - kvm_irqfds_allowed = true; kvm_msi_via_irqfd_allowed = true; kvm_gsi_direct_mapping = true; diff --git a/hw/ipack/tpci200.c b/hw/ipack/tpci200.c index b7031a0..1df02ee 100644 --- a/hw/ipack/tpci200.c +++ b/hw/ipack/tpci200.c @@ -573,7 +573,7 @@ static const MemoryRegionOps tpci200_las3_ops = { } }; -static int tpci200_initfn(PCIDevice *pci_dev) +static void tpci200_realize(PCIDevice *pci_dev, Error **errp) { TPCI200State *s = TPCI200(pci_dev); uint8_t *c = s->dev.config; @@ -609,8 +609,6 @@ static int tpci200_initfn(PCIDevice *pci_dev) ipack_bus_new_inplace(&s->bus, sizeof(s->bus), DEVICE(pci_dev), NULL, N_MODULES, tpci200_set_irq); - - return 0; } static const VMStateDescription vmstate_tpci200 = { @@ -632,7 +630,7 @@ static void tpci200_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = tpci200_initfn; + 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; diff --git a/hw/isa/i82378.c b/hw/isa/i82378.c index a7d9aa6..9da9dfc 100644 --- a/hw/isa/i82378.c +++ b/hw/isa/i82378.c @@ -58,7 +58,7 @@ static void i82378_request_pic_irq(void *opaque, int irq, int level) qemu_set_irq(s->i8259[irq], level); } -static int i82378_initfn(PCIDevice *pci) +static void i82378_realize(PCIDevice *pci, Error **errp) { DeviceState *dev = DEVICE(pci); I82378State *s = I82378(dev); @@ -75,7 +75,8 @@ static int i82378_initfn(PCIDevice *pci) pci_config_set_interrupt_pin(pci_conf, 1); /* interrupt pin 0 */ - isabus = isa_bus_new(dev, pci_address_space_io(pci)); + isabus = isa_bus_new(dev, get_system_memory(), + pci_address_space_io(pci)); /* This device has: 2 82C59 (irq) @@ -106,8 +107,6 @@ static int i82378_initfn(PCIDevice *pci) /* timer */ isa_create_simple(isabus, "mc146818rtc"); - - return 0; } static void i82378_init(Object *obj) @@ -124,7 +123,7 @@ static void i82378_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - k->init = i82378_initfn; + k->realize = i82378_realize; k->vendor_id = PCI_VENDOR_ID_INTEL; k->device_id = PCI_DEVICE_ID_INTEL_82378; k->revision = 0x03; diff --git a/hw/isa/isa-bus.c b/hw/isa/isa-bus.c index cc85e53..825aa62 100644 --- a/hw/isa/isa-bus.c +++ b/hw/isa/isa-bus.c @@ -21,10 +21,8 @@ #include "hw/sysbus.h" #include "sysemu/sysemu.h" #include "hw/isa/isa.h" -#include "exec/address-spaces.h" static ISABus *isabus; -hwaddr isa_mem_base = 0; static void isabus_dev_print(Monitor *mon, DeviceState *dev, int indent); static char *isabus_get_fw_dev_path(DeviceState *dev); @@ -44,7 +42,8 @@ static const TypeInfo isa_bus_info = { .class_init = isa_bus_class_init, }; -ISABus *isa_bus_new(DeviceState *dev, MemoryRegion *address_space_io) +ISABus *isa_bus_new(DeviceState *dev, MemoryRegion* address_space, + MemoryRegion *address_space_io) { if (isabus) { fprintf(stderr, "Can't create a second ISA bus\n"); @@ -56,6 +55,7 @@ ISABus *isa_bus_new(DeviceState *dev, MemoryRegion *address_space_io) } isabus = ISA_BUS(qbus_create(TYPE_ISA_BUS, dev, NULL)); + isabus->address_space = address_space; isabus->address_space_io = address_space_io; return isabus; } @@ -250,7 +250,11 @@ static char *isabus_get_fw_dev_path(DeviceState *dev) MemoryRegion *isa_address_space(ISADevice *dev) { - return get_system_memory(); + if (dev) { + return isa_bus_from_device(dev)->address_space; + } + + return isabus->address_space; } MemoryRegion *isa_address_space_io(ISADevice *dev) diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index 530b074..dba7585 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -575,7 +575,7 @@ static int ich9_lpc_init(PCIDevice *d) ICH9LPCState *lpc = ICH9_LPC_DEVICE(d); ISABus *isa_bus; - isa_bus = isa_bus_new(&d->qdev, get_system_io()); + isa_bus = isa_bus_new(DEVICE(d), get_system_memory(), get_system_io()); pci_set_long(d->wmask + ICH9_LPC_PMBASE, ICH9_LPC_PMBASE_BASE_ADDRESS_MASK); @@ -610,8 +610,17 @@ static void ich9_device_plug_cb(HotplugHandler *hotplug_dev, static void ich9_device_unplug_request_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - error_setg(errp, "acpi: device unplug request for not supported device" - " type: %s", object_get_typename(OBJECT(dev))); + 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) @@ -677,6 +686,7 @@ static void ich9_lpc_class_init(ObjectClass *klass, void *data) 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; } diff --git a/hw/isa/pc87312.c b/hw/isa/pc87312.c index 40a1106..3b1fcec 100644 --- a/hw/isa/pc87312.c +++ b/hw/isa/pc87312.c @@ -278,6 +278,7 @@ static void pc87312_realize(DeviceState *dev, Error **errp) 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); @@ -296,6 +297,7 @@ static void pc87312_realize(DeviceState *dev, Error **errp) 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); @@ -319,11 +321,13 @@ static void pc87312_realize(DeviceState *dev, Error **errp) 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_nofail(d, "driveA", blk_by_legacy_dinfo(drive)); } + /* FIXME use a qdev drive property instead of drive_get() */ drive = drive_get(IF_FLOPPY, 0, 1); if (drive != NULL) { qdev_prop_set_drive_nofail(d, "driveB", diff --git a/hw/isa/piix4.c b/hw/isa/piix4.c index 1aa17d7..d9522b1 100644 --- a/hw/isa/piix4.c +++ b/hw/isa/piix4.c @@ -82,14 +82,14 @@ static const VMStateDescription vmstate_piix4 = { } }; -static int piix4_initfn(PCIDevice *dev) +static void piix4_realize(PCIDevice *dev, Error **errp) { PIIX4State *d = DO_UPCAST(PIIX4State, dev, dev); - isa_bus_new(&d->dev.qdev, pci_address_space_io(dev)); + isa_bus_new(DEVICE(d), pci_address_space(dev), + pci_address_space_io(dev)); piix4_dev = &d->dev; qemu_register_reset(piix4_reset, d); - return 0; } int piix4_init(PCIBus *bus, ISABus **isa_bus, int devfn) @@ -106,7 +106,7 @@ static void piix4_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = piix4_initfn; + 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; diff --git a/hw/isa/vt82c686.c b/hw/isa/vt82c686.c index e0c235c..b8197b1 100644 --- a/hw/isa/vt82c686.c +++ b/hw/isa/vt82c686.c @@ -36,7 +36,7 @@ typedef struct SuperIOConfig { - uint8_t config[0xff]; + uint8_t config[0x100]; uint8_t index; uint8_t data; } SuperIOConfig; @@ -50,13 +50,13 @@ typedef struct VT82C686BState { static void superio_ioport_writeb(void *opaque, hwaddr addr, uint64_t data, unsigned size) { - int can_write; 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: @@ -68,30 +68,27 @@ static void superio_ioport_writeb(void *opaque, hwaddr addr, uint64_t data, case 0xf7: case 0xf9 ... 0xfb: case 0xfd ... 0xff: - can_write = 0; + can_write = false; break; - default: - can_write = 1; - - if (can_write) { - switch (superio_conf->index) { - case 0xe7: - if ((data & 0xff) != 0xfe) { - DPRINTF("chage uart 1 base. unsupported yet\n"); - } - break; - case 0xe8: - if ((data & 0xff) != 0xbe) { - DPRINTF("chage uart 2 base. unsupported yet\n"); - } - break; - - default: - superio_conf->config[superio_conf->index] = data & 0xff; - } + 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; } - superio_conf->config[superio_conf->index] = data & 0xff; } } @@ -237,7 +234,7 @@ static const VMStateDescription vmstate_acpi = { VMSTATE_UINT16(ar.pm1.evt.en, VT686PMState), VMSTATE_UINT16(ar.pm1.cnt.cnt, VT686PMState), VMSTATE_STRUCT(apm, VT686PMState, 0, vmstate_apm, APMState), - VMSTATE_TIMER(ar.tmr.timer, VT686PMState), + VMSTATE_TIMER_PTR(ar.tmr.timer, VT686PMState), VMSTATE_INT64(ar.tmr.overflow_time, VT686PMState), VMSTATE_END_OF_LIST() } @@ -248,7 +245,7 @@ static const VMStateDescription vmstate_acpi = { * just register a PCI device now, functionalities will be implemented later. */ -static int vt82c686b_ac97_initfn(PCIDevice *dev) +static void vt82c686b_ac97_realize(PCIDevice *dev, Error **errp) { VT686AC97State *s = DO_UPCAST(VT686AC97State, dev, dev); uint8_t *pci_conf = s->dev.config; @@ -258,8 +255,6 @@ static int vt82c686b_ac97_initfn(PCIDevice *dev) pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_CAP_LIST | PCI_STATUS_DEVSEL_MEDIUM); pci_set_long(pci_conf + PCI_INTERRUPT_PIN, 0x03); - - return 0; } void vt82c686b_ac97_init(PCIBus *bus, int devfn) @@ -275,7 +270,7 @@ static void via_ac97_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = vt82c686b_ac97_initfn; + k->realize = vt82c686b_ac97_realize; k->vendor_id = PCI_VENDOR_ID_VIA; k->device_id = PCI_DEVICE_ID_VIA_AC97; k->revision = 0x50; @@ -291,7 +286,7 @@ static const TypeInfo via_ac97_info = { .class_init = via_ac97_class_init, }; -static int vt82c686b_mc97_initfn(PCIDevice *dev) +static void vt82c686b_mc97_realize(PCIDevice *dev, Error **errp) { VT686MC97State *s = DO_UPCAST(VT686MC97State, dev, dev); uint8_t *pci_conf = s->dev.config; @@ -300,8 +295,6 @@ static int vt82c686b_mc97_initfn(PCIDevice *dev) PCI_COMMAND_VGA_PALETTE); pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM); pci_set_long(pci_conf + PCI_INTERRUPT_PIN, 0x03); - - return 0; } void vt82c686b_mc97_init(PCIBus *bus, int devfn) @@ -317,7 +310,7 @@ static void via_mc97_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = vt82c686b_mc97_initfn; + 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; @@ -334,7 +327,7 @@ static const TypeInfo via_mc97_info = { }; /* vt82c686 pm init */ -static int vt82c686b_pm_initfn(PCIDevice *dev) +static void vt82c686b_pm_realize(PCIDevice *dev, Error **errp) { VT686PMState *s = DO_UPCAST(VT686PMState, dev, dev); uint8_t *pci_conf; @@ -364,8 +357,6 @@ static int vt82c686b_pm_initfn(PCIDevice *dev) 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, 2); - - return 0; } I2CBus *vt82c686b_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base, @@ -394,7 +385,7 @@ static void via_pm_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = vt82c686b_pm_initfn; + 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; @@ -424,7 +415,7 @@ static const VMStateDescription vmstate_via = { }; /* init the PCI-to-ISA bridge */ -static int vt82c686b_initfn(PCIDevice *d) +static void vt82c686b_realize(PCIDevice *d, Error **errp) { VT82C686BState *vt82c = DO_UPCAST(VT82C686BState, dev, d); uint8_t *pci_conf; @@ -432,7 +423,8 @@ static int vt82c686b_initfn(PCIDevice *d) uint8_t *wmask; int i; - isa_bus = isa_bus_new(&d->qdev, pci_address_space_io(d)); + isa_bus = isa_bus_new(DEVICE(d), get_system_memory(), + pci_address_space_io(d)); pci_conf = d->config; pci_config_set_prog_interface(pci_conf, 0x0); @@ -453,8 +445,6 @@ static int vt82c686b_initfn(PCIDevice *d) &vt82c->superio); qemu_register_reset(vt82c686b_reset, d); - - return 0; } ISABus *vt82c686b_init(PCIBus *bus, int devfn) @@ -471,7 +461,7 @@ static void via_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = vt82c686b_initfn; + 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; diff --git a/hw/lm32/lm32_boards.c b/hw/lm32/lm32_boards.c index af0abdb..14d0efc 100644 --- a/hw/lm32/lm32_boards.c +++ b/hw/lm32/lm32_boards.c @@ -111,9 +111,8 @@ static void lm32_evr_init(MachineState *machine) reset_info->flash_base = flash_base; - memory_region_init_ram(phys_ram, NULL, "lm32_evr.sdram", ram_size, - &error_abort); - vmstate_register_ram_global(phys_ram); + 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); @@ -214,9 +213,8 @@ static void lm32_uclinux_init(MachineState *machine) reset_info->flash_base = flash_base; - memory_region_init_ram(phys_ram, NULL, "lm32_uclinux.sdram", ram_size, - &error_abort); - vmstate_register_ram_global(phys_ram); + 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); diff --git a/hw/lm32/lm32_hwsetup.h b/hw/lm32/lm32_hwsetup.h index 9fd5e69..838754d 100644 --- a/hw/lm32/lm32_hwsetup.h +++ b/hw/lm32/lm32_hwsetup.h @@ -73,7 +73,8 @@ static inline void hwsetup_free(HWSetup *hw) static inline void hwsetup_create_rom(HWSetup *hw, hwaddr base) { - rom_add_blob("hwsetup", hw->data, TARGET_PAGE_SIZE, base, NULL, NULL, NULL); + 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) diff --git a/hw/lm32/milkymist-hw.h b/hw/lm32/milkymist-hw.h index 5317ce6..8d20cac 100644 --- a/hw/lm32/milkymist-hw.h +++ b/hw/lm32/milkymist-hw.h @@ -86,7 +86,7 @@ static inline DeviceState *milkymist_pfpu_create(hwaddr base, return dev; } -#ifdef CONFIG_GLX +#ifdef CONFIG_OPENGL #include #include static const int glx_fbconfig_attr[] = { @@ -100,7 +100,7 @@ static const int glx_fbconfig_attr[] = { static inline DeviceState *milkymist_tmu2_create(hwaddr base, qemu_irq irq) { -#ifdef CONFIG_GLX +#ifdef CONFIG_OPENGL DeviceState *dev; Display *d; GLXFBConfig *configs; diff --git a/hw/lm32/milkymist.c b/hw/lm32/milkymist.c index 256c102..e0cec7d 100644 --- a/hw/lm32/milkymist.c +++ b/hw/lm32/milkymist.c @@ -118,9 +118,8 @@ milkymist_init(MachineState *machine) cpu_lm32_set_phys_msb_ignore(env, 1); - memory_region_init_ram(phys_sdram, NULL, "milkymist.sdram", sdram_size, - &error_abort); - vmstate_register_ram_global(phys_sdram); + 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); @@ -155,6 +154,7 @@ milkymist_init(MachineState *machine) bios_name); exit(1); } + g_free(bios_filename); milkymist_uart_create(0x60000000, irq[0]); milkymist_sysctl_create(0x60001000, irq[1], irq[2], irq[3], diff --git a/hw/m68k/an5206.c b/hw/m68k/an5206.c index f1f1350..f63ab2b 100644 --- a/hw/m68k/an5206.c +++ b/hw/m68k/an5206.c @@ -50,8 +50,7 @@ static void an5206_init(MachineState *machine) env->rambar0 = AN5206_RAMBAR_ADDR | 1; /* DRAM at address zero */ - memory_region_init_ram(ram, NULL, "an5206.ram", ram_size, &error_abort); - vmstate_register_ram_global(ram); + memory_region_allocate_system_memory(ram, NULL, "an5206.ram", ram_size); memory_region_add_subregion(address_space_mem, 0, ram); /* Internal SRAM. */ diff --git a/hw/m68k/dummy_m68k.c b/hw/m68k/dummy_m68k.c index facd561..5b77d93 100644 --- a/hw/m68k/dummy_m68k.c +++ b/hw/m68k/dummy_m68k.c @@ -21,6 +21,7 @@ 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); @@ -30,18 +31,19 @@ static void dummy_m68k_init(MachineState *machine) if (!cpu_model) cpu_model = "cfv4e"; - env = cpu_init(cpu_model); - if (!env) { + 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_init_ram(ram, NULL, "dummy_m68k.ram", ram_size, &error_abort); - vmstate_register_ram_global(ram); + memory_region_allocate_system_memory(ram, NULL, "dummy_m68k.ram", + ram_size); memory_region_add_subregion(address_space_mem, 0, ram); /* Load kernel. */ diff --git a/hw/m68k/mcf5208.c b/hw/m68k/mcf5208.c index a01a445..326a42d 100644 --- a/hw/m68k/mcf5208.c +++ b/hw/m68k/mcf5208.c @@ -218,8 +218,7 @@ static void mcf5208evb_init(MachineState *machine) /* TODO: Configure BARs. */ /* DRAM at 0x40000000 */ - memory_region_init_ram(ram, NULL, "mcf5208.ram", ram_size, &error_abort); - vmstate_register_ram_global(ram); + memory_region_allocate_system_memory(ram, NULL, "mcf5208.ram", ram_size); memory_region_add_subregion(address_space_mem, 0x40000000, ram); /* Internal SRAM. */ diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index d431834..39f0c97 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -22,6 +22,44 @@ #include "qemu/config-file.h" #include "qapi/visitor.h" #include "qemu/range.h" +#include "sysemu/numa.h" + +typedef struct pc_dimms_capacity { + uint64_t size; + Error **errp; +} pc_dimms_capacity; + +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) { @@ -62,6 +100,32 @@ int qmp_pc_dimm_device_list(Object *obj, void *opaque) return 0; } +ram_addr_t get_current_ram_size(void) +{ + MemoryDeviceInfoList *info_list = NULL; + MemoryDeviceInfoList **prev = &info_list; + MemoryDeviceInfoList *info; + ram_addr_t size = ram_size; + + qmp_pc_dimm_device_list(qdev_get_machine(), &prev); + for (info = info_list; info; info = info->next) { + MemoryDeviceInfo *value = info->value; + + if (value) { + switch (value->kind) { + case MEMORY_DEVICE_INFO_KIND_DIMM: + size += value->dimm->size; + break; + default: + break; + } + } + } + qapi_free_MemoryDeviceInfoList(info_list); + + return size; +} + static int pc_dimm_slot2bitmap(Object *obj, void *opaque) { unsigned long *bitmap = opaque; @@ -287,6 +351,7 @@ static void pc_dimm_class_init(ObjectClass *oc, void *data) dc->realize = pc_dimm_realize; dc->props = pc_dimm_properties; + dc->desc = "DIMM memory module"; ddc->get_memory_region = pc_dimm_get_memory_region; } diff --git a/hw/microblaze/boot.c b/hw/microblaze/boot.c index a2843cd..38c59db 100644 --- a/hw/microblaze/boot.c +++ b/hw/microblaze/boot.c @@ -185,7 +185,7 @@ void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base, ram_size - initrd_offset); } if (initrd_size < 0) { - error_report("qemu: could not load initrd '%s'\n", + error_report("qemu: could not load initrd '%s'", initrd_filename); exit(EXIT_FAILURE); } diff --git a/hw/mips/gt64xxx_pci.c b/hw/mips/gt64xxx_pci.c index 1f2fe5f..10fcca3 100644 --- a/hw/mips/gt64xxx_pci.c +++ b/hw/mips/gt64xxx_pci.c @@ -239,7 +239,11 @@ typedef struct GT64120State { 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 */ @@ -290,25 +294,63 @@ static void gt64120_isd_mapping(GT64120State *s) static void gt64120_pci_mapping(GT64120State *s) { - /* Update IO 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; - isa_mem_base = s->PCI0IO_start; - if (s->PCI0IO_length) { - memory_region_init_alias(&s->PCI0IO_mem, OBJECT(s), "isa_mmio", - get_system_io(), 0, s->PCI0IO_length); - memory_region_add_subregion(get_system_memory(), s->PCI0IO_start, - &s->PCI0IO_mem); - } + /* 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); + } } } @@ -363,10 +405,12 @@ static void gt64120_writel (void *opaque, hwaddr addr, 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; @@ -380,12 +424,12 @@ static void gt64120_writel (void *opaque, hwaddr addr, 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_PCI0M0HD: - case GT_PCI0M1HD: case GT_PCI1IOHD: case GT_PCI1M0HD: case GT_PCI1M1HD: @@ -1124,10 +1168,12 @@ PCIBus *gt64120_register(qemu_irq *pic) 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, - get_system_memory(), + &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); @@ -1142,11 +1188,6 @@ static int gt64120_init(SysBusDevice *dev) s = GT64120_PCI_HOST_BRIDGE(dev); - /* FIXME: This value is computed from registers during reset, but some - devices (e.g. VGA card) need to know it when they are registered. - This also mean that changing the register to change the mapping - does not fully work. */ - isa_mem_base = 0x10000000; qemu_register_reset(gt64120_reset, s); return 0; } diff --git a/hw/mips/mips_fulong2e.c b/hw/mips/mips_fulong2e.c index 6a9ebfa..4aae64a 100644 --- a/hw/mips/mips_fulong2e.c +++ b/hw/mips/mips_fulong2e.c @@ -277,7 +277,6 @@ static void mips_fulong2e_init(MachineState *machine) PCIBus *pci_bus; ISABus *isa_bus; I2CBus *smbus; - int i; DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; MIPSCPU *cpu; CPUMIPSState *env; @@ -302,8 +301,7 @@ static void mips_fulong2e_init(MachineState *machine) bios_size = 1024 * 1024; /* allocate RAM */ - memory_region_init_ram(ram, NULL, "fulong2e.ram", ram_size, &error_abort); - vmstate_register_ram_global(ram); + memory_region_allocate_system_memory(ram, NULL, "fulong2e.ram", ram_size); memory_region_init_ram(bios, NULL, "fulong2e.bios", bios_size, &error_abort); vmstate_register_ram_global(bios); @@ -384,15 +382,8 @@ static void mips_fulong2e_init(MachineState *machine) rtc_init(isa_bus, 2000, NULL); - for(i = 0; i < MAX_SERIAL_PORTS; i++) { - if (serial_hds[i]) { - serial_isa_init(isa_bus, i, serial_hds[i]); - } - } - - if (parallel_hds[0]) { - parallel_init(isa_bus, 0, parallel_hds[0]); - } + serial_hds_isa_init(isa_bus, MAX_SERIAL_PORTS); + parallel_hds_isa_init(isa_bus, 1); /* Sound card */ audio_init(pci_bus); diff --git a/hw/mips/mips_jazz.c b/hw/mips/mips_jazz.c index 3f33093..07f3c27 100644 --- a/hw/mips/mips_jazz.c +++ b/hw/mips/mips_jazz.c @@ -60,13 +60,16 @@ static void main_cpu_reset(void *opaque) static uint64_t rtc_read(void *opaque, hwaddr addr, unsigned size) { - return cpu_inw(0x71); + uint8_t val; + address_space_read(&address_space_memory, 0x90000071, &val, 1); + return val; } static void rtc_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { - cpu_outw(0x71, val & 0xff); + uint8_t buf = val & 0xff; + address_space_write(&address_space_memory, 0x90000071, &buf, 1); } static const MemoryRegionOps rtc_ops = { @@ -120,12 +123,11 @@ static void mips_jazz_do_unassigned_access(CPUState *cpu, hwaddr addr, (*real_do_unassigned_access)(cpu, addr, is_write, is_exec, opaque, size); } -static void mips_jazz_init(MemoryRegion *address_space, - MemoryRegion *address_space_io, - ram_addr_t ram_size, - const char *cpu_model, +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; @@ -134,7 +136,8 @@ static void mips_jazz_init(MemoryRegion *address_space, qemu_irq *rc4030, *i8259; rc4030_dma *dmas; void* rc4030_opaque; - MemoryRegion *isa = g_new(MemoryRegion, 1); + 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); @@ -179,8 +182,8 @@ static void mips_jazz_init(MemoryRegion *address_space, cc->do_unassigned_access = mips_jazz_do_unassigned_access; /* allocate RAM */ - memory_region_init_ram(ram, NULL, "mips_jazz.ram", ram_size, &error_abort); - vmstate_register_ram_global(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, @@ -218,8 +221,14 @@ static void mips_jazz_init(MemoryRegion *address_space, 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); + /* ISA devices */ - isa_bus = isa_bus_new(NULL, address_space_io); i8259 = i8259_init(isa_bus, env->irq[4]); isa_bus_irqs(isa_bus, i8259); cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1); @@ -227,12 +236,6 @@ static void mips_jazz_init(MemoryRegion *address_space, pit = pit_init(isa_bus, 0x40, 0, NULL); pcspk_init(isa_bus, pit); - /* ISA IO space at 0x90000000 */ - memory_region_init_alias(isa, NULL, "isa_mmio", - get_system_io(), 0, 0x01000000); - memory_region_add_subregion(address_space, 0x90000000, isa); - isa_mem_base = 0x11000000; - /* Video card */ switch (jazz_model) { case JAZZ_MAGNUM: @@ -333,19 +336,13 @@ static void mips_jazz_init(MemoryRegion *address_space, static void mips_magnum_init(MachineState *machine) { - ram_addr_t ram_size = machine->ram_size; - const char *cpu_model = machine->cpu_model; - mips_jazz_init(get_system_memory(), get_system_io(), - ram_size, cpu_model, JAZZ_MAGNUM); + mips_jazz_init(machine, JAZZ_MAGNUM); } static void mips_pica61_init(MachineState *machine) { - ram_addr_t ram_size = machine->ram_size; - const char *cpu_model = machine->cpu_model; - mips_jazz_init(get_system_memory(), get_system_io(), - ram_size, cpu_model, JAZZ_PICA61); + mips_jazz_init(machine, JAZZ_PICA61); } static QEMUMachine mips_magnum_machine = { diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index 5845158..b0fa71a 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -993,9 +993,8 @@ void mips_malta_init(MachineState *machine) } /* register RAM at high address where it is undisturbed by IO */ - memory_region_init_ram(ram_high, NULL, "mips_malta.ram", ram_size, - &error_abort); - vmstate_register_ram_global(ram_high); + 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 */ @@ -1172,10 +1171,9 @@ void mips_malta_init(MachineState *machine) isa_create_simple(isa_bus, "i8042"); rtc_init(isa_bus, 2000, NULL); - serial_isa_init(isa_bus, 0, serial_hds[0]); - serial_isa_init(isa_bus, 1, serial_hds[1]); - if (parallel_hds[0]) - parallel_init(isa_bus, 0, parallel_hds[0]); + 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); } diff --git a/hw/mips/mips_mipssim.c b/hw/mips/mips_mipssim.c index 5d44c3f..61f74a6 100644 --- a/hw/mips/mips_mipssim.c +++ b/hw/mips/mips_mipssim.c @@ -171,9 +171,8 @@ mips_mipssim_init(MachineState *machine) qemu_register_reset(main_cpu_reset, reset_info); /* Allocate RAM. */ - memory_region_init_ram(ram, NULL, "mips_mipssim.ram", ram_size, - &error_abort); - vmstate_register_ram_global(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_abort); vmstate_register_ram_global(bios); diff --git a/hw/mips/mips_r4k.c b/hw/mips/mips_r4k.c index a7fe0ce..66e2a58 100644 --- a/hw/mips/mips_r4k.c +++ b/hw/mips/mips_r4k.c @@ -165,7 +165,8 @@ void mips_r4k_init(MachineState *machine) MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *bios; MemoryRegion *iomem = g_new(MemoryRegion, 1); - MemoryRegion *isa = 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; @@ -204,8 +205,7 @@ void mips_r4k_init(MachineState *machine) ((unsigned int)ram_size / (1 << 20))); exit(1); } - memory_region_init_ram(ram, NULL, "mips_r4k.ram", ram_size, &error_abort); - vmstate_register_ram_global(ram); + memory_region_allocate_system_memory(ram, NULL, "mips_r4k.ram", ram_size); memory_region_add_subregion(address_space_mem, 0, ram); @@ -267,27 +267,23 @@ void mips_r4k_init(MachineState *machine) 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()); + /* The PIC is attached to the MIPS CPU INT0 pin */ - isa_bus = isa_bus_new(NULL, get_system_io()); i8259 = i8259_init(isa_bus, env->irq[2]); isa_bus_irqs(isa_bus, i8259); rtc_init(isa_bus, 2000, NULL); - /* Register 64 KB of ISA IO space at 0x14000000 */ - memory_region_init_alias(isa, NULL, "isa_mmio", - get_system_io(), 0, 0x00010000); - memory_region_add_subregion(get_system_memory(), 0x14000000, isa); - - isa_mem_base = 0x10000000; - pit = pit_init(isa_bus, 0x40, 0, NULL); - for(i = 0; i < MAX_SERIAL_PORTS; i++) { - if (serial_hds[i]) { - serial_isa_init(isa_bus, i, serial_hds[i]); - } - } + serial_hds_isa_init(isa_bus, MAX_SERIAL_PORTS); isa_vga_init(isa_bus); diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index 979e532..4aa76ff 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -19,10 +19,7 @@ common-obj-$(CONFIG_PUV3) += puv3_pm.o common-obj-$(CONFIG_MACIO) += macio/ -ifeq ($(CONFIG_PCI), y) -obj-$(CONFIG_KVM) += ivshmem.o -obj-$(CONFIG_LINUX) += vfio.o -endif +obj-$(CONFIG_IVSHMEM) += ivshmem.o obj-$(CONFIG_REALVIEW) += arm_sysctl.o obj-$(CONFIG_NSERIES) += cbus.o @@ -39,5 +36,7 @@ obj-$(CONFIG_OMAP) += omap_sdrc.o obj-$(CONFIG_OMAP) += omap_tap.o obj-$(CONFIG_SLAVIO) += slavio_misc.o obj-$(CONFIG_ZYNQ) += zynq_slcr.o +obj-$(CONFIG_STM32F2XX_SYSCFG) += stm32f2xx_syscfg.o obj-$(CONFIG_PVPANIC) += pvpanic.o +obj-$(CONFIG_EDU) += edu.o diff --git a/hw/misc/applesmc.c b/hw/misc/applesmc.c index 6a56b07..6bd61e7 100644 --- a/hw/misc/applesmc.c +++ b/hw/misc/applesmc.c @@ -43,7 +43,6 @@ /* command/status port used by Apple SMC */ #define APPLESMC_CMD_PORT 0x4 #define APPLESMC_NR_PORTS 32 -#define APPLESMC_MAX_DATA_LENGTH 32 #define APPLESMC_READ_CMD 0x10 #define APPLESMC_WRITE_CMD 0x11 @@ -249,8 +248,8 @@ static void applesmc_isa_realize(DeviceState *dev, Error **errp) } static Property applesmc_isa_properties[] = { - DEFINE_PROP_UINT32("iobase", AppleSMCState, iobase, - APPLESMC_DEFAULT_IOBASE), + DEFINE_PROP_UINT32(APPLESMC_PROP_IO_BASE, AppleSMCState, iobase, + APPLESMC_DEFAULT_IOBASE), DEFINE_PROP_STRING("osk", AppleSMCState, osk), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/misc/edu.c b/hw/misc/edu.c new file mode 100644 index 0000000..f601069 --- /dev/null +++ b/hw/misc/edu.c @@ -0,0 +1,408 @@ +/* + * 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 "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 purposedly 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 int pci_edu_init(PCIDevice *pdev) +{ + 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); + + return 0; +} + +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, struct Visitor *v, void *opaque, + const char *name, Error **errp) +{ + uint64_t *val = opaque; + + visit_type_uint64(v, val, name, 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->init = pci_edu_init; + 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/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c index b4273aa..f3984e3 100644 --- a/hw/misc/macio/cuda.c +++ b/hw/misc/macio/cuda.c @@ -631,15 +631,15 @@ static const VMStateDescription vmstate_cuda_timer = { VMSTATE_UINT16(counter_value, CUDATimer), VMSTATE_INT64(load_time, CUDATimer), VMSTATE_INT64(next_irq_time, CUDATimer), - VMSTATE_TIMER_TEST(timer, CUDATimer, cuda_timer_exist), + VMSTATE_TIMER_PTR_TEST(timer, CUDATimer, cuda_timer_exist), VMSTATE_END_OF_LIST() } }; static const VMStateDescription vmstate_cuda = { .name = "cuda", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (VMStateField[]) { VMSTATE_UINT8(a, CUDAState), VMSTATE_UINT8(b, CUDAState), @@ -660,6 +660,7 @@ static const VMStateDescription vmstate_cuda = { VMSTATE_UINT32(tick_offset, CUDAState), VMSTATE_STRUCT_ARRAY(timers, CUDAState, 2, 1, vmstate_cuda_timer, CUDATimer), + VMSTATE_TIMER_PTR(adb_poll_timer, CUDAState), VMSTATE_END_OF_LIST() } }; diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c index e0f1e88..063ad80 100644 --- a/hw/misc/macio/macio.c +++ b/hw/misc/macio/macio.c @@ -273,7 +273,7 @@ static int macio_newworld_initfn(PCIDevice *d) MacIOState *s = MACIO(d); NewWorldMacIOState *ns = NEWWORLD_MACIO(d); SysBusDevice *sysbus_dev; - MemoryRegion *timer_memory = g_new(MemoryRegion, 1); + MemoryRegion *timer_memory = NULL; int i; int cur_irq = 0; int ret = macio_common_initfn(d); @@ -301,6 +301,7 @@ static int macio_newworld_initfn(PCIDevice *d) } /* 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); @@ -336,20 +337,44 @@ static void macio_instance_init(Object *obj) memory_region_add_subregion(&s->bar, 0x08000, dbdma_mem); } +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->init = macio_oldworld_initfn; 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->init = macio_newworld_initfn; pdc->device_id = PCI_DEVICE_ID_APPLE_UNI_N_KEYL; + dc->vmsd = &vmstate_macio_newworld; } static Property macio_properties[] = { diff --git a/hw/misc/milkymist-pfpu.c b/hw/misc/milkymist-pfpu.c index 609f33f..08b604f 100644 --- a/hw/misc/milkymist-pfpu.c +++ b/hw/misc/milkymist-pfpu.c @@ -362,7 +362,7 @@ static void pfpu_start(MilkymistPFPUState *s) i = 0; while (pfpu_decode_insn(s)) { /* decode at most MICROCODE_WORDS instructions */ - if (i++ >= MICROCODE_WORDS) { + if (++i >= MICROCODE_WORDS) { error_report("milkymist_pfpu: too many instructions " "executed in microcode. No VECTOUT?"); break; diff --git a/hw/misc/omap_gpmc.c b/hw/misc/omap_gpmc.c index a0de52f..74fc91c 100644 --- a/hw/misc/omap_gpmc.c +++ b/hw/misc/omap_gpmc.c @@ -624,7 +624,8 @@ static void omap_gpmc_write(void *opaque, hwaddr addr, struct omap_gpmc_cs_file_s *f; if (size != 4 && gpmc_wordaccess_only(addr)) { - return omap_badwidth_write32(opaque, addr, value); + omap_badwidth_write32(opaque, addr, value); + return; } switch (addr) { diff --git a/hw/misc/omap_l4.c b/hw/misc/omap_l4.c index f237250..245ceac 100644 --- a/hw/misc/omap_l4.c +++ b/hw/misc/omap_l4.c @@ -82,7 +82,8 @@ static void omap_l4ta_write(void *opaque, hwaddr addr, struct omap_target_agent_s *s = (struct omap_target_agent_s *) opaque; if (size != 4) { - return omap_badwidth_write32(opaque, addr, value); + omap_badwidth_write32(opaque, addr, value); + return; } switch (addr) { diff --git a/hw/misc/omap_sdrc.c b/hw/misc/omap_sdrc.c index ed62caf..3de0c0e 100644 --- a/hw/misc/omap_sdrc.c +++ b/hw/misc/omap_sdrc.c @@ -92,7 +92,8 @@ static void omap_sdrc_write(void *opaque, hwaddr addr, struct omap_sdrc_s *s = (struct omap_sdrc_s *) opaque; if (size != 4) { - return omap_badwidth_write32(opaque, addr, value); + omap_badwidth_write32(opaque, addr, value); + return; } switch (addr) { diff --git a/hw/misc/omap_tap.c b/hw/misc/omap_tap.c index 9d2b710..6f02bb9 100644 --- a/hw/misc/omap_tap.c +++ b/hw/misc/omap_tap.c @@ -95,7 +95,8 @@ static void omap_tap_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { if (size != 4) { - return omap_badwidth_write32(opaque, addr, value); + omap_badwidth_write32(opaque, addr, value); + return; } OMAP_BAD_REG(addr); diff --git a/hw/misc/pci-testdev.c b/hw/misc/pci-testdev.c index c78a63e..26b9b86 100644 --- a/hw/misc/pci-testdev.c +++ b/hw/misc/pci-testdev.c @@ -233,7 +233,7 @@ static const MemoryRegionOps pci_testdev_pio_ops = { }, }; -static int pci_testdev_init(PCIDevice *pci_dev) +static void pci_testdev_realize(PCIDevice *pci_dev, Error **errp) { PCITestDevState *d = PCI_TEST_DEV(pci_dev); uint8_t *pci_conf; @@ -275,8 +275,6 @@ static int pci_testdev_init(PCIDevice *pci_dev) assert(r >= 0); test->hasnotifier = true; } - - return 0; } static void @@ -306,7 +304,7 @@ static void pci_testdev_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = pci_testdev_init; + 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; diff --git a/hw/misc/stm32f2xx_syscfg.c b/hw/misc/stm32f2xx_syscfg.c new file mode 100644 index 0000000..4ae4042 --- /dev/null +++ b/hw/misc/stm32f2xx_syscfg.c @@ -0,0 +1,160 @@ +/* + * 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 "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/hw/net/allwinner_emac.c b/hw/net/allwinner_emac.c index d780ba0..0407dee 100644 --- a/hw/net/allwinner_emac.c +++ b/hw/net/allwinner_emac.c @@ -218,13 +218,6 @@ static ssize_t aw_emac_receive(NetClientState *nc, const uint8_t *buf, return size; } -static void aw_emac_cleanup(NetClientState *nc) -{ - AwEmacState *s = qemu_get_nic_opaque(nc); - - s->nic = NULL; -} - static void aw_emac_reset(DeviceState *dev) { AwEmacState *s = AW_EMAC(dev); @@ -433,7 +426,6 @@ static NetClientInfo net_aw_emac_info = { .size = sizeof(NICState), .can_receive = aw_emac_can_receive, .receive = aw_emac_receive, - .cleanup = aw_emac_cleanup, .link_status_changed = aw_emac_set_link, }; diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index de26609..55b6293 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -1209,14 +1209,6 @@ static const MemoryRegionOps gem_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static void gem_cleanup(NetClientState *nc) -{ - GemState *s = qemu_get_nic_opaque(nc); - - DB_PRINT("\n"); - s->nic = NULL; -} - static void gem_set_link(NetClientState *nc) { DB_PRINT("\n"); @@ -1228,7 +1220,6 @@ static NetClientInfo net_gem_info = { .size = sizeof(NICState), .can_receive = gem_can_receive, .receive = gem_receive, - .cleanup = gem_cleanup, .link_status_changed = gem_set_link, }; diff --git a/hw/net/dp8393x.c b/hw/net/dp8393x.c index 7eab7ad..7ce13d2 100644 --- a/hw/net/dp8393x.c +++ b/hw/net/dp8393x.c @@ -859,22 +859,11 @@ static void nic_reset(void *opaque) dp8393x_update_irq(s); } -static void nic_cleanup(NetClientState *nc) -{ - dp8393xState *s = qemu_get_nic_opaque(nc); - - timer_del(s->watchdog); - timer_free(s->watchdog); - - g_free(s); -} - static NetClientInfo net_dp83932_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), .can_receive = nic_can_receive, .receive = nic_receive, - .cleanup = nic_cleanup, }; void dp83932_init(NICInfo *nd, hwaddr base, int it_shift, diff --git a/hw/net/e1000.c b/hw/net/e1000.c index e33a4da..091d61a 100644 --- a/hw/net/e1000.c +++ b/hw/net/e1000.c @@ -33,6 +33,7 @@ #include "sysemu/sysemu.h" #include "sysemu/dma.h" #include "qemu/iov.h" +#include "qemu/range.h" #include "e1000_regs.h" @@ -577,7 +578,7 @@ static inline int is_vlan_packet(E1000State *s, const uint8_t *buf) { return (be16_to_cpup((uint16_t *)(buf + 12)) == - le16_to_cpup((uint16_t *)(s->mac_reg + VET))); + le16_to_cpu(s->mac_reg[VET])); } static inline int @@ -710,7 +711,7 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp) (tp->cptse || txd_lower & E1000_TXD_CMD_EOP)) { tp->vlan_needed = 1; stw_be_p(tp->vlan_header, - le16_to_cpup((uint16_t *)(s->mac_reg + VET))); + le16_to_cpu(s->mac_reg[VET])); stw_be_p(tp->vlan_header + 2, le16_to_cpu(dp->upper.fields.special)); } @@ -923,7 +924,9 @@ 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) && e1000_has_rxbufs(s, 1); + (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) @@ -1500,14 +1503,6 @@ e1000_mmio_setup(E1000State *d) } static void -e1000_cleanup(NetClientState *nc) -{ - E1000State *s = qemu_get_nic_opaque(nc); - - s->nic = NULL; -} - -static void pci_e1000_uninit(PCIDevice *dev) { E1000State *d = E1000(dev); @@ -1525,11 +1520,24 @@ static NetClientInfo net_e1000_info = { .can_receive = e1000_can_receive, .receive = e1000_receive, .receive_iov = e1000_receive_iov, - .cleanup = e1000_cleanup, .link_status_changed = e1000_set_link_status, }; -static int pci_e1000_init(PCIDevice *pci_dev) +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); @@ -1539,6 +1547,8 @@ static int pci_e1000_init(PCIDevice *pci_dev) 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 */ @@ -1571,8 +1581,6 @@ static int pci_e1000_init(PCIDevice *pci_dev) 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); - - return 0; } static void qdev_e1000_reset(DeviceState *dev) @@ -1604,7 +1612,7 @@ static void e1000_class_init(ObjectClass *klass, void *data) E1000BaseClass *e = E1000_DEVICE_CLASS(klass); const E1000Info *info = data; - k->init = pci_e1000_init; + k->realize = pci_e1000_realize; k->exit = pci_e1000_uninit; k->romfile = "efi-e1000.rom"; k->vendor_id = PCI_VENDOR_ID_INTEL; diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c index 4877bfd..c374c1a 100644 --- a/hw/net/eepro100.c +++ b/hw/net/eepro100.c @@ -1832,13 +1832,6 @@ static const VMStateDescription vmstate_eepro100 = { } }; -static void nic_cleanup(NetClientState *nc) -{ - EEPRO100State *s = qemu_get_nic_opaque(nc); - - s->nic = NULL; -} - static void pci_nic_uninit(PCIDevice *pci_dev) { EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev); @@ -1853,10 +1846,9 @@ static NetClientInfo net_eepro100_info = { .size = sizeof(NICState), .can_receive = nic_can_receive, .receive = nic_receive, - .cleanup = nic_cleanup, }; -static int e100_nic_init(PCIDevice *pci_dev) +static void e100_nic_realize(PCIDevice *pci_dev, Error **errp) { EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev); E100PCIDeviceInfo *info = eepro100_get_class(s); @@ -1900,8 +1892,6 @@ static int e100_nic_init(PCIDevice *pci_dev) 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); - - return 0; } static void eepro100_instance_init(Object *obj) @@ -2091,7 +2081,7 @@ static void eepro100_class_init(ObjectClass *klass, void *data) k->vendor_id = PCI_VENDOR_ID_INTEL; k->class_id = PCI_CLASS_NETWORK_ETHERNET; k->romfile = "pxe-eepro100.rom"; - k->init = e100_nic_init; + k->realize = e100_nic_realize; k->exit = pci_nic_uninit; k->device_id = info->device_id; k->revision = info->revision; diff --git a/hw/net/etraxfs_eth.c b/hw/net/etraxfs_eth.c index 6a3c86d..4773dea 100644 --- a/hw/net/etraxfs_eth.c +++ b/hw/net/etraxfs_eth.c @@ -581,24 +581,11 @@ static const MemoryRegionOps eth_ops = { } }; -static void eth_cleanup(NetClientState *nc) -{ - ETRAXFSEthState *eth = qemu_get_nic_opaque(nc); - - /* Disconnect the client. */ - eth->dma_out->client.push = NULL; - eth->dma_out->client.opaque = NULL; - eth->dma_in->client.opaque = NULL; - eth->dma_in->client.pull = NULL; - g_free(eth); -} - static NetClientInfo net_etraxfs_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), .can_receive = eth_can_receive, .receive = eth_receive, - .cleanup = eth_cleanup, .link_status_changed = eth_set_link, }; diff --git a/hw/net/fsl_etsec/etsec.c b/hw/net/fsl_etsec/etsec.c index d4b4429..c57365f 100644 --- a/hw/net/fsl_etsec/etsec.c +++ b/hw/net/fsl_etsec/etsec.c @@ -338,11 +338,6 @@ static void etsec_reset(DeviceState *d) MII_SR_100X_FD_CAPS | MII_SR_100T4_CAPS; } -static void etsec_cleanup(NetClientState *nc) -{ - /* qemu_log("eTSEC cleanup\n"); */ -} - static int etsec_can_receive(NetClientState *nc) { eTSEC *etsec = qemu_get_nic_opaque(nc); @@ -377,7 +372,6 @@ static NetClientInfo net_etsec_info = { .size = sizeof(NICState), .can_receive = etsec_can_receive, .receive = etsec_receive, - .cleanup = etsec_cleanup, .link_status_changed = etsec_set_link_status, }; @@ -449,10 +443,7 @@ DeviceState *etsec_create(hwaddr base, dev = qdev_create(NULL, "eTSEC"); qdev_set_nic_properties(dev, nd); - - if (qdev_init(dev)) { - return NULL; - } + qdev_init_nofail(dev); sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, tx_irq); sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, rx_irq); diff --git a/hw/net/lan9118.c b/hw/net/lan9118.c index e528290..f169c38 100644 --- a/hw/net/lan9118.c +++ b/hw/net/lan9118.c @@ -1309,19 +1309,11 @@ static const MemoryRegionOps lan9118_16bit_mem_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static void lan9118_cleanup(NetClientState *nc) -{ - lan9118_state *s = qemu_get_nic_opaque(nc); - - s->nic = NULL; -} - static NetClientInfo net_lan9118_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), .can_receive = lan9118_can_receive, .receive = lan9118_receive, - .cleanup = lan9118_cleanup, .link_status_changed = lan9118_set_link, }; diff --git a/hw/net/lance.c b/hw/net/lance.c index a1c49f1..4baa016 100644 --- a/hw/net/lance.c +++ b/hw/net/lance.c @@ -91,20 +91,12 @@ static const MemoryRegionOps lance_mem_ops = { }, }; -static void lance_cleanup(NetClientState *nc) -{ - PCNetState *d = qemu_get_nic_opaque(nc); - - pcnet_common_cleanup(d); -} - static NetClientInfo net_lance_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), .can_receive = pcnet_can_receive, .receive = pcnet_receive, .link_status_changed = pcnet_set_link_status, - .cleanup = lance_cleanup, }; static const VMStateDescription vmstate_lance = { @@ -134,7 +126,8 @@ static int lance_init(SysBusDevice *sbd) s->phys_mem_read = ledma_memory_read; s->phys_mem_write = ledma_memory_write; - return pcnet_common_init(dev, s, &net_lance_info); + pcnet_common_init(dev, s, &net_lance_info); + return 0; } static void lance_reset(DeviceState *dev) diff --git a/hw/net/mcf_fec.c b/hw/net/mcf_fec.c index 22cd7cf..0255612 100644 --- a/hw/net/mcf_fec.c +++ b/hw/net/mcf_fec.c @@ -439,19 +439,11 @@ static const MemoryRegionOps mcf_fec_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static void mcf_fec_cleanup(NetClientState *nc) -{ - mcf_fec_state *s = qemu_get_nic_opaque(nc); - - g_free(s); -} - static NetClientInfo net_mcf_fec_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), .can_receive = mcf_fec_can_receive, .receive = mcf_fec_receive, - .cleanup = mcf_fec_cleanup, }; void mcf_fec_init(MemoryRegion *sysmem, NICInfo *nd, diff --git a/hw/net/milkymist-minimac2.c b/hw/net/milkymist-minimac2.c index c632672..f06afaa 100644 --- a/hw/net/milkymist-minimac2.c +++ b/hw/net/milkymist-minimac2.c @@ -425,13 +425,6 @@ static int minimac2_can_rx(NetClientState *nc) return 0; } -static void minimac2_cleanup(NetClientState *nc) -{ - MilkymistMinimac2State *s = qemu_get_nic_opaque(nc); - - s->nic = NULL; -} - static void milkymist_minimac2_reset(DeviceState *d) { MilkymistMinimac2State *s = MILKYMIST_MINIMAC2(d); @@ -454,7 +447,6 @@ static NetClientInfo net_milkymist_minimac2_info = { .size = sizeof(NICState), .can_receive = minimac2_can_rx, .receive = minimac2_rx, - .cleanup = minimac2_cleanup, }; static int milkymist_minimac2_init(SysBusDevice *sbd) diff --git a/hw/net/mipsnet.c b/hw/net/mipsnet.c index b26c369..c813e0c 100644 --- a/hw/net/mipsnet.c +++ b/hw/net/mipsnet.c @@ -211,19 +211,11 @@ static const VMStateDescription vmstate_mipsnet = { } }; -static void mipsnet_cleanup(NetClientState *nc) -{ - MIPSnetState *s = qemu_get_nic_opaque(nc); - - s->nic = NULL; -} - static NetClientInfo net_mipsnet_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), .can_receive = mipsnet_can_receive, .receive = mipsnet_receive, - .cleanup = mipsnet_cleanup, }; static const MemoryRegionOps mipsnet_ioport_ops = { diff --git a/hw/net/ne2000-isa.c b/hw/net/ne2000-isa.c index 82e2ba1..17e7199 100644 --- a/hw/net/ne2000-isa.c +++ b/hw/net/ne2000-isa.c @@ -41,19 +41,11 @@ typedef struct ISANE2000State { NE2000State ne2000; } ISANE2000State; -static void isa_ne2000_cleanup(NetClientState *nc) -{ - NE2000State *s = qemu_get_nic_opaque(nc); - - s->nic = NULL; -} - static NetClientInfo net_ne2000_isa_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), .can_receive = ne2000_can_receive, .receive = ne2000_receive, - .cleanup = isa_ne2000_cleanup, }; static const VMStateDescription vmstate_isa_ne2000 = { diff --git a/hw/net/ne2000.c b/hw/net/ne2000.c index 3ab2d03..3492db3 100644 --- a/hw/net/ne2000.c +++ b/hw/net/ne2000.c @@ -702,22 +702,14 @@ void ne2000_setup_io(NE2000State *s, DeviceState *dev, unsigned size) memory_region_init_io(&s->io, OBJECT(dev), &ne2000_ops, s, "ne2000", size); } -static void ne2000_cleanup(NetClientState *nc) -{ - NE2000State *s = qemu_get_nic_opaque(nc); - - s->nic = NULL; -} - static NetClientInfo net_ne2000_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), .can_receive = ne2000_can_receive, .receive = ne2000_receive, - .cleanup = ne2000_cleanup, }; -static int pci_ne2000_init(PCIDevice *pci_dev) +static void pci_ne2000_realize(PCIDevice *pci_dev, Error **errp) { PCINE2000State *d = DO_UPCAST(PCINE2000State, dev, pci_dev); NE2000State *s; @@ -737,8 +729,6 @@ static int pci_ne2000_init(PCIDevice *pci_dev) 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); - - return 0; } static void pci_ne2000_exit(PCIDevice *pci_dev) @@ -771,7 +761,7 @@ static void ne2000_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = pci_ne2000_init; + k->realize = pci_ne2000_realize; k->exit = pci_ne2000_exit; k->romfile = "efi-ne2k_pci.rom", k->vendor_id = PCI_VENDOR_ID_REALTEK; diff --git a/hw/net/opencores_eth.c b/hw/net/opencores_eth.c index 4a44304..3642046 100644 --- a/hw/net/opencores_eth.c +++ b/hw/net/opencores_eth.c @@ -472,16 +472,11 @@ static ssize_t open_eth_receive(NetClientState *nc, return size; } -static void open_eth_cleanup(NetClientState *nc) -{ -} - 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, - .cleanup = open_eth_cleanup, .link_status_changed = open_eth_set_link_status, }; diff --git a/hw/net/pcnet-pci.c b/hw/net/pcnet-pci.c index fb5f5d6..8305d1b 100644 --- a/hw/net/pcnet-pci.c +++ b/hw/net/pcnet-pci.c @@ -33,6 +33,7 @@ #include "qemu/timer.h" #include "sysemu/dma.h" #include "sysemu/sysemu.h" +#include "trace.h" #include "pcnet.h" @@ -61,9 +62,8 @@ typedef struct { static void pcnet_aprom_writeb(void *opaque, uint32_t addr, uint32_t val) { PCNetState *s = opaque; -#ifdef PCNET_DEBUG - printf("pcnet_aprom_writeb addr=0x%08x val=0x%02x\n", addr, val); -#endif + + trace_pcnet_aprom_writeb(opaque, addr, val); if (BCR_APROMWE(s)) { s->prom[addr & 15] = val; } @@ -73,9 +73,8 @@ static uint32_t pcnet_aprom_readb(void *opaque, uint32_t addr) { PCNetState *s = opaque; uint32_t val = s->prom[addr & 15]; -#ifdef PCNET_DEBUG - printf("pcnet_aprom_readb addr=0x%08x val=0x%02x\n", addr, val); -#endif + + trace_pcnet_aprom_readb(opaque, addr, val); return val; } @@ -84,6 +83,7 @@ static uint64_t pcnet_ioport_read(void *opaque, hwaddr addr, { 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); @@ -111,6 +111,7 @@ static void pcnet_ioport_write(void *opaque, hwaddr addr, { 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); @@ -141,10 +142,8 @@ static const MemoryRegionOps pcnet_io_ops = { static void pcnet_mmio_writeb(void *opaque, hwaddr addr, uint32_t val) { PCNetState *d = opaque; -#ifdef PCNET_DEBUG_IO - printf("pcnet_mmio_writeb addr=0x" TARGET_FMT_plx" val=0x%02x\n", addr, - val); -#endif + + trace_pcnet_mmio_writeb(opaque, addr, val); if (!(addr & 0x10)) pcnet_aprom_writeb(d, addr & 0x0f, val); } @@ -153,22 +152,18 @@ 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); -#ifdef PCNET_DEBUG_IO - printf("pcnet_mmio_readb addr=0x" TARGET_FMT_plx " val=0x%02x\n", addr, - val & 0xff); -#endif + trace_pcnet_mmio_readb(opaque, addr, val); return val; } static void pcnet_mmio_writew(void *opaque, hwaddr addr, uint32_t val) { PCNetState *d = opaque; -#ifdef PCNET_DEBUG_IO - printf("pcnet_mmio_writew addr=0x" TARGET_FMT_plx " val=0x%04x\n", addr, - val); -#endif + + trace_pcnet_mmio_writew(opaque, addr, val); if (addr & 0x10) pcnet_ioport_writew(d, addr & 0x0f, val); else { @@ -182,6 +177,7 @@ 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 { @@ -190,20 +186,15 @@ static uint32_t pcnet_mmio_readw(void *opaque, hwaddr addr) val <<= 8; val |= pcnet_aprom_readb(d, addr); } -#ifdef PCNET_DEBUG_IO - printf("pcnet_mmio_readw addr=0x" TARGET_FMT_plx" val = 0x%04x\n", addr, - val & 0xffff); -#endif + trace_pcnet_mmio_readw(opaque, addr, val); return val; } static void pcnet_mmio_writel(void *opaque, hwaddr addr, uint32_t val) { PCNetState *d = opaque; -#ifdef PCNET_DEBUG_IO - printf("pcnet_mmio_writel addr=0x" TARGET_FMT_plx" val=0x%08x\n", addr, - val); -#endif + + trace_pcnet_mmio_writel(opaque, addr, val); if (addr & 0x10) pcnet_ioport_writel(d, addr & 0x0f, val); else { @@ -219,6 +210,7 @@ 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 { @@ -231,10 +223,7 @@ static uint32_t pcnet_mmio_readl(void *opaque, hwaddr addr) val <<= 8; val |= pcnet_aprom_readb(d, addr); } -#ifdef PCNET_DEBUG_IO - printf("pcnet_mmio_readl addr=0x" TARGET_FMT_plx " val=0x%08x\n", addr, - val); -#endif + trace_pcnet_mmio_readl(opaque, addr, val); return val; } @@ -271,13 +260,6 @@ static void pci_physical_memory_read(void *dma_opaque, hwaddr addr, pci_dma_read(dma_opaque, addr, buf, len); } -static void pci_pcnet_cleanup(NetClientState *nc) -{ - PCNetState *d = qemu_get_nic_opaque(nc); - - pcnet_common_cleanup(d); -} - static void pci_pcnet_uninit(PCIDevice *dev) { PCIPCNetState *d = PCI_PCNET(dev); @@ -294,10 +276,9 @@ static NetClientInfo net_pci_pcnet_info = { .can_receive = pcnet_can_receive, .receive = pcnet_receive, .link_status_changed = pcnet_set_link_status, - .cleanup = pci_pcnet_cleanup, }; -static int pci_pcnet_init(PCIDevice *pci_dev) +static void pci_pcnet_realize(PCIDevice *pci_dev, Error **errp) { PCIPCNetState *d = PCI_PCNET(pci_dev); PCNetState *s = &d->state; @@ -335,7 +316,7 @@ static int pci_pcnet_init(PCIDevice *pci_dev) s->phys_mem_write = pci_physical_memory_write; s->dma_opaque = pci_dev; - return pcnet_common_init(DEVICE(pci_dev), s, &net_pci_pcnet_info); + pcnet_common_init(DEVICE(pci_dev), s, &net_pci_pcnet_info); } static void pci_reset(DeviceState *dev) @@ -365,7 +346,7 @@ static void pcnet_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = pci_pcnet_init; + k->realize = pci_pcnet_realize; k->exit = pci_pcnet_uninit; k->romfile = "efi-pcnet.rom", k->vendor_id = PCI_VENDOR_ID_AMD; diff --git a/hw/net/pcnet.c b/hw/net/pcnet.c index f409b92..bdfd38f 100644 --- a/hw/net/pcnet.c +++ b/hw/net/pcnet.c @@ -40,6 +40,7 @@ #include "qemu/timer.h" #include "qemu/sockets.h" #include "sysemu/sysemu.h" +#include "trace.h" #include "pcnet.h" @@ -685,9 +686,7 @@ static void pcnet_bcr_writew(PCNetState *s, uint32_t rap, uint32_t val); static void pcnet_s_reset(PCNetState *s) { -#ifdef PCNET_DEBUG - printf("pcnet_s_reset\n"); -#endif + trace_pcnet_s_reset(s); s->rdra = 0; s->tdra = 0; @@ -760,9 +759,7 @@ static void pcnet_update_irq(PCNetState *s) s->csr[4] |= 0x0040; s->csr[0] |= 0x0080; isr = 1; -#ifdef PCNET_DEBUG - printf("pcnet user int\n"); -#endif + trace_pcnet_user_int(s); } #if 1 @@ -777,9 +774,7 @@ static void pcnet_update_irq(PCNetState *s) } if (isr != s->isr) { -#ifdef PCNET_DEBUG - printf("pcnet: INTA=%d\n", isr); -#endif + trace_pcnet_isr_change(s, isr, s->isr); } qemu_set_irq(s->irq, isr); s->isr = isr; @@ -791,9 +786,7 @@ static void pcnet_init(PCNetState *s) uint16_t padr[3], ladrf[4], mode; uint32_t rdra, tdra; -#ifdef PCNET_DEBUG - printf("pcnet_init init_addr=0x%08x\n", PHYSADDR(s,CSR_IADR(s))); -#endif + trace_pcnet_init(s, PHYSADDR(s, CSR_IADR(s))); if (BCR_SSIZE32(s)) { struct pcnet_initblk32 initblk; @@ -831,9 +824,7 @@ static void pcnet_init(PCNetState *s) tdra &= 0x00ffffff; } -#if defined(PCNET_DEBUG) - printf("rlen=%d tlen=%d\n", rlen, tlen); -#endif + trace_pcnet_rlen_tlen(s, rlen, tlen); CSR_RCVRL(s) = (rlen < 9) ? (1 << rlen) : 512; CSR_XMTRL(s) = (tlen < 9) ? (1 << tlen) : 512; @@ -852,11 +843,8 @@ static void pcnet_init(PCNetState *s) CSR_RCVRC(s) = CSR_RCVRL(s); CSR_XMTRC(s) = CSR_XMTRL(s); -#ifdef PCNET_DEBUG - printf("pcnet ss32=%d rdra=0x%08x[%d] tdra=0x%08x[%d]\n", - BCR_SSIZE32(s), - s->rdra, CSR_RCVRL(s), s->tdra, CSR_XMTRL(s)); -#endif + 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 */ @@ -1719,17 +1707,12 @@ const VMStateDescription vmstate_pcnet = { VMSTATE_BUFFER(buffer, PCNetState), VMSTATE_UNUSED_TEST(is_version_2, 4), VMSTATE_INT32(tx_busy, PCNetState), - VMSTATE_TIMER(poll_timer, PCNetState), + VMSTATE_TIMER_PTR(poll_timer, PCNetState), VMSTATE_END_OF_LIST() } }; -void pcnet_common_cleanup(PCNetState *d) -{ - d->nic = NULL; -} - -int pcnet_common_init(DeviceState *dev, PCNetState *s, NetClientInfo *info) +void pcnet_common_init(DeviceState *dev, PCNetState *s, NetClientInfo *info) { int i; uint16_t checksum; @@ -1768,6 +1751,4 @@ int pcnet_common_init(DeviceState *dev, PCNetState *s, NetClientInfo *info) *(uint16_t *)&s->prom[12] = cpu_to_le16(checksum); s->lnkst = 0x40; /* initial link state: up */ - - return 0; } diff --git a/hw/net/pcnet.h b/hw/net/pcnet.h index f8e8a6f..79c4c84 100644 --- a/hw/net/pcnet.h +++ b/hw/net/pcnet.h @@ -63,7 +63,6 @@ uint32_t pcnet_bcr_readw(PCNetState *s, uint32_t rap); int pcnet_can_receive(NetClientState *nc); ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_); void pcnet_set_link_status(NetClientState *nc); -void pcnet_common_cleanup(PCNetState *d); -int pcnet_common_init(DeviceState *dev, PCNetState *s, NetClientInfo *info); +void pcnet_common_init(DeviceState *dev, PCNetState *s, NetClientInfo *info); extern const VMStateDescription vmstate_pcnet; #endif diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c index 5f0197c..d25e8c9 100644 --- a/hw/net/rtl8139.c +++ b/hw/net/rtl8139.c @@ -508,7 +508,6 @@ typedef struct RTL8139State { /* PCI interrupt timer */ QEMUTimer *timer; - int64_t TimerExpire; MemoryRegion bar_io; MemoryRegion bar_mem; @@ -520,7 +519,7 @@ typedef struct 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, int64_t current_time); +static void rtl8139_set_next_tctr_time(RTL8139State *s); static void prom9346_decode_command(EEprom9346 *eeprom, uint8_t command) { @@ -1282,6 +1281,7 @@ static void rtl8139_reset(DeviceState *d) s->TCTR = 0; s->TimerInt = 0; s->TCTR_base = 0; + rtl8139_set_next_tctr_time(s); /* reset tally counters */ RTL8139TallyCounters_clear(&s->tally_counters); @@ -2075,20 +2075,6 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) "length to %d\n", txsize); } - if (!s->cplus_txbuffer) - { - /* out of memory */ - - DPRINTF("+++ C+ mode transmiter failed to reallocate %d bytes\n", - s->cplus_txbuffer_len); - - /* update tally counter */ - ++s->tally_counters.TxERR; - ++s->tally_counters.TxAbt; - - return 0; - } - /* append more data to the packet */ DPRINTF("+++ C+ mode transmit reading %d bytes from host memory at " @@ -2164,6 +2150,11 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) { DPRINTF("+++ C+ mode offloaded task checksum\n"); + /* Large enough for Ethernet and IP headers? */ + if (saved_size < ETH_HLEN + sizeof(ip_header)) { + goto skip_offload; + } + /* ip packet header */ ip_header *ip = NULL; int hlen = 0; @@ -2174,223 +2165,235 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) size_t eth_payload_len = 0; int proto = be16_to_cpu(*(uint16_t *)(saved_buffer + 12)); - if (proto == ETH_P_IP) + if (proto != ETH_P_IP) { - DPRINTF("+++ C+ mode has IP packet\n"); - - /* not aligned */ - eth_payload_data = saved_buffer + ETH_HLEN; - eth_payload_len = saved_size - ETH_HLEN; - - ip = (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); - ip = NULL; - } else { - hlen = IP_HEADER_LENGTH(ip); - ip_protocol = ip->ip_p; - ip_data_len = be16_to_cpu(ip->ip_len) - hlen; - } + goto skip_offload; + } + + DPRINTF("+++ C+ mode has IP packet\n"); + + /* not aligned */ + eth_payload_data = saved_buffer + ETH_HLEN; + eth_payload_len = saved_size - ETH_HLEN; + + ip = (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_HEADER_LENGTH(ip); + if (hlen < sizeof(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 (ip) + if (txdw0 & CP_TX_IPCS) { - if (txdw0 & CP_TX_IPCS) - { - DPRINTF("+++ C+ mode need IP checksum\n"); + DPRINTF("+++ C+ mode need IP checksum\n"); - if (hleneth_payload_len) {/* min header length */ - /* bad packet header len */ - /* or packet too short */ - } - else - { - 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); - } + 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; } - if ((txdw0 & CP_TX_LGSEN) && ip_protocol == IP_PROTO_TCP) - { - int large_send_mss = (txdw0 >> 16) & CP_TC_LGSEN_MSS_MASK; + 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); + 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; + int tcp_send_offset = 0; + int send_count = 0; - /* maximum IP header length is 60 bytes */ - uint8_t saved_ip_header[60]; + /* 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); + /* 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; + /* 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); + /* 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); + int tcp_hlen = TCP_HEADER_DATA_OFFSET(p_tcp_hdr); - /* 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; + /* Invalid TCP data offset? */ + if (tcp_hlen < sizeof(tcp_header) || tcp_hlen > ip_data_len) { + goto skip_offload; + } - 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); + /* 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; - /* note the cycle below overwrites IP header data, - but restores it from saved_ip_header before sending packet */ + 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); - int is_last_frame = 0; + /* note the cycle below overwrites IP header data, + but restores it from saved_ip_header before sending packet */ - 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", - be32_to_cpu(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, TCP_FLAG_PUSH|TCP_FLAG_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 */ - p_tcp_hdr->th_seq = cpu_to_be32(chunk_size + be32_to_cpu(p_tcp_hdr->th_seq)); - ++send_count; - } + int is_last_frame = 0; - /* Stop sending this frame */ - saved_size = 0; - } - else if (txdw0 & (CP_TX_TCPCS|CP_TX_UDPCS)) + for (tcp_send_offset = 0; tcp_send_offset < tcp_data_len; tcp_send_offset += tcp_chunk_size) { - DPRINTF("+++ C+ mode need TCP or UDP checksum\n"); + uint16_t chunk_size = tcp_chunk_size; - /* maximum IP header length is 60 bytes */ - uint8_t saved_ip_header[60]; - memcpy(saved_ip_header, eth_payload_data, hlen); + /* 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; + } - uint8_t *data_to_checksum = eth_payload_data + hlen - 12; - // size_t data_to_checksum_len = eth_payload_len - hlen + 12; + DPRINTF("+++ C+ mode TSO TCP seqno %08x\n", + be32_to_cpu(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); - if ((txdw0 & CP_TX_TCPCS) && ip_protocol == IP_PROTO_TCP) + DPRINTF("+++ C+ mode TSO calculating TCP checksum for " + "packet with %d bytes data\n", tcp_hlen + + chunk_size); + + if (tcp_send_offset) { - DPRINTF("+++ C+ mode calculating TCP checksum for " - "packet with %d bytes data\n", ip_data_len); + memcpy((uint8_t*)p_tcp_hdr + tcp_hlen, (uint8_t*)p_tcp_hdr + tcp_hlen + tcp_send_offset, chunk_size); + } - 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); + /* keep PUSH and FIN flags only for the last frame */ + if (!is_last_frame) + { + TCP_HEADER_CLEAR_FLAGS(p_tcp_hdr, TCP_FLAG_PUSH|TCP_FLAG_FIN); + } - tcp_header* p_tcp_hdr = (tcp_header *) (data_to_checksum+12); + /* 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; + 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); + 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; - } - 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); + p_tcp_hdr->th_sum = tcp_checksum; - 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); + /* restore IP header */ + memcpy(eth_payload_data, saved_ip_header, hlen); - udp_header *p_udp_hdr = (udp_header *) (data_to_checksum+12); + /* set IP data length and recalculate IP checksum */ + ip->ip_len = cpu_to_be16(hlen + tcp_hlen + chunk_size); - p_udp_hdr->uh_sum = 0; + /* increment IP id for subsequent frames */ + ip->ip_id = cpu_to_be16(tcp_send_offset/tcp_chunk_size + be16_to_cpu(ip->ip_id)); - int udp_checksum = ip_checksum(data_to_checksum, ip_data_len + 12); - DPRINTF("+++ C+ mode UDP checksum %04x\n", - udp_checksum); + 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); - p_udp_hdr->uh_sum = udp_checksum; - } + 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); - /* restore IP header */ - memcpy(eth_payload_data, saved_ip_header, hlen); + /* add transferred count to TCP sequence number */ + p_tcp_hdr->th_seq = cpu_to_be32(chunk_size + be32_to_cpu(p_tcp_hdr->th_seq)); + ++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; @@ -2652,7 +2655,6 @@ static void rtl8139_IntrMask_write(RTL8139State *s, uint32_t val) s->IntrMask = val; - rtl8139_set_next_tctr_time(s, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); rtl8139_update_irq(s); } @@ -2687,13 +2689,7 @@ static void rtl8139_IntrStatus_write(RTL8139State *s, uint32_t val) rtl8139_update_irq(s); s->IntrStatus = newStatus; - /* - * Computing if we miss an interrupt here is not that correct but - * considered that we should have had already an interrupt - * and probably emulated is slower is better to assume this resetting was - * done before testing on previous rtl8139_update_irq lead to IRQ losing - */ - rtl8139_set_next_tctr_time(s, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); + rtl8139_set_next_tctr_time(s); rtl8139_update_irq(s); #endif @@ -2701,8 +2697,6 @@ static void rtl8139_IntrStatus_write(RTL8139State *s, uint32_t val) static uint32_t rtl8139_IntrStatus_read(RTL8139State *s) { - rtl8139_set_next_tctr_time(s, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); - uint32_t ret = s->IntrStatus; DPRINTF("IntrStatus read(w) val=0x%04x\n", ret); @@ -2885,43 +2879,32 @@ static void rtl8139_io_writew(void *opaque, uint8_t addr, uint32_t val) } } -static void rtl8139_set_next_tctr_time(RTL8139State *s, int64_t current_time) +static void rtl8139_set_next_tctr_time(RTL8139State *s) { - int64_t pci_time, next_time; - uint32_t low_pci; + const uint64_t ns_per_period = + muldiv64(0x100000000LL, get_ticks_per_sec(), PCI_FREQUENCY); DPRINTF("entered rtl8139_set_next_tctr_time\n"); - if (s->TimerExpire && current_time >= s->TimerExpire) { - s->IntrStatus |= PCSTimeout; - rtl8139_update_irq(s); - } - - /* Set QEMU timer only if needed that is - * - TimerInt <> 0 (we have a timer) - * - mask = 1 (we want an interrupt timer) - * - irq = 0 (irq is not already active) - * If any of above change we need to compute timer again - * Also we must check if timer is passed without QEMU timer + /* 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). */ - s->TimerExpire = 0; - if (!s->TimerInt) { - return; - } - - pci_time = muldiv64(current_time - s->TCTR_base, PCI_FREQUENCY, - get_ticks_per_sec()); - low_pci = pci_time & 0xffffffff; - pci_time = pci_time - low_pci + s->TimerInt; - if (low_pci >= s->TimerInt) { - pci_time += 0x100000000LL; + while (s->TCTR_base + ns_per_period <= qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)) { + s->TCTR_base += ns_per_period; } - next_time = s->TCTR_base + muldiv64(pci_time, get_ticks_per_sec(), - PCI_FREQUENCY); - s->TimerExpire = next_time; - if ((s->IntrMask & PCSTimeout) != 0 && (s->IntrStatus & PCSTimeout) == 0) { - timer_mod(s->timer, next_time); + if (!s->TimerInt) { + timer_del(s->timer); + } else { + uint64_t delta = muldiv64(s->TimerInt, get_ticks_per_sec(), PCI_FREQUENCY); + if (s->TCTR_base + delta <= qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)) { + delta += ns_per_period; + } + timer_mod(s->timer, s->TCTR_base + delta); } } @@ -2969,14 +2952,14 @@ static void rtl8139_io_writel(void *opaque, uint8_t addr, uint32_t val) 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, s->TCTR_base); + 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, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); + rtl8139_set_next_tctr_time(s); } break; @@ -3253,7 +3236,7 @@ static uint32_t rtl8139_mmio_readl(void *opaque, hwaddr addr) static int rtl8139_post_load(void *opaque, int version_id) { RTL8139State* s = opaque; - rtl8139_set_next_tctr_time(s, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); + rtl8139_set_next_tctr_time(s); if (version_id < 4) { s->cplus_enabled = s->CpCmd != 0; } @@ -3284,8 +3267,7 @@ static void rtl8139_pre_save(void *opaque) RTL8139State* s = opaque; int64_t current_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - /* set IntrStatus correctly */ - rtl8139_set_next_tctr_time(s, current_time); + /* for migration to older versions */ s->TCTR = muldiv64(current_time - s->TCTR_base, PCI_FREQUENCY, get_ticks_per_sec()); s->rtl8139_mmio_io_addr_dummy = 0; @@ -3452,14 +3434,7 @@ static void rtl8139_timer(void *opaque) s->IntrStatus |= PCSTimeout; rtl8139_update_irq(s); - rtl8139_set_next_tctr_time(s, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); -} - -static void rtl8139_cleanup(NetClientState *nc) -{ - RTL8139State *s = qemu_get_nic_opaque(nc); - - s->nic = NULL; + rtl8139_set_next_tctr_time(s); } static void pci_rtl8139_uninit(PCIDevice *dev) @@ -3494,11 +3469,10 @@ static NetClientInfo net_rtl8139_info = { .size = sizeof(NICState), .can_receive = rtl8139_can_receive, .receive = rtl8139_receive, - .cleanup = rtl8139_cleanup, .link_status_changed = rtl8139_set_link_status, }; -static int pci_rtl8139_init(PCIDevice *dev) +static void pci_rtl8139_realize(PCIDevice *dev, Error **errp) { RTL8139State *s = RTL8139(dev); DeviceState *d = DEVICE(dev); @@ -3538,11 +3512,7 @@ static int pci_rtl8139_init(PCIDevice *dev) s->cplus_txbuffer_len = 0; s->cplus_txbuffer_offset = 0; - s->TimerExpire = 0; s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, rtl8139_timer, s); - rtl8139_set_next_tctr_time(s, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); - - return 0; } static void rtl8139_instance_init(Object *obj) @@ -3564,7 +3534,7 @@ static void rtl8139_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = pci_rtl8139_init; + k->realize = pci_rtl8139_realize; k->exit = pci_rtl8139_uninit; k->romfile = "efi-rtl8139.rom"; k->vendor_id = PCI_VENDOR_ID_REALTEK; diff --git a/hw/net/smc91c111.c b/hw/net/smc91c111.c index d1dca8f..74e06e6 100644 --- a/hw/net/smc91c111.c +++ b/hw/net/smc91c111.c @@ -736,19 +736,11 @@ static const MemoryRegionOps smc91c111_mem_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static void smc91c111_cleanup(NetClientState *nc) -{ - smc91c111_state *s = qemu_get_nic_opaque(nc); - - s->nic = NULL; -} - static NetClientInfo net_smc91c111_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), .can_receive = smc91c111_can_receive, .receive = smc91c111_receive, - .cleanup = smc91c111_cleanup, }; static int smc91c111_init1(SysBusDevice *sbd) diff --git a/hw/net/spapr_llan.c b/hw/net/spapr_llan.c index 2c8b038..2dd5ec1 100644 --- a/hw/net/spapr_llan.c +++ b/hw/net/spapr_llan.c @@ -187,19 +187,11 @@ static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf, return size; } -static void spapr_vlan_cleanup(NetClientState *nc) -{ - VIOsPAPRVLANDevice *dev = qemu_get_nic_opaque(nc); - - dev->nic = NULL; -} - 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, - .cleanup = spapr_vlan_cleanup, }; static void spapr_vlan_reset(VIOsPAPRDevice *sdev) @@ -211,7 +203,7 @@ static void spapr_vlan_reset(VIOsPAPRDevice *sdev) dev->isopen = 0; } -static int spapr_vlan_init(VIOsPAPRDevice *sdev) +static void spapr_vlan_realize(VIOsPAPRDevice *sdev, Error **errp) { VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(sdev); @@ -220,8 +212,6 @@ static int spapr_vlan_init(VIOsPAPRDevice *sdev) 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); - - return 0; } static void spapr_vlan_instance_init(Object *obj) @@ -542,7 +532,7 @@ static void spapr_vlan_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VIOsPAPRDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); - k->init = spapr_vlan_init; + k->realize = spapr_vlan_realize; k->reset = spapr_vlan_reset; k->devnode = spapr_vlan_devnode; k->dt_name = "l-lan"; diff --git a/hw/net/stellaris_enet.c b/hw/net/stellaris_enet.c index c07e513..278a654 100644 --- a/hw/net/stellaris_enet.c +++ b/hw/net/stellaris_enet.c @@ -451,19 +451,11 @@ static void stellaris_enet_reset(stellaris_enet_state *s) s->tx_fifo_len = 0; } -static void stellaris_enet_cleanup(NetClientState *nc) -{ - stellaris_enet_state *s = qemu_get_nic_opaque(nc); - - s->nic = NULL; -} - static NetClientInfo net_stellaris_enet_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), .can_receive = stellaris_enet_can_receive, .receive = stellaris_enet_receive, - .cleanup = stellaris_enet_cleanup, }; static int stellaris_enet_init(SysBusDevice *sbd) diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index 4e3a061..cf23335 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -35,7 +35,7 @@ #include -#include "hw/virtio/virtio_ring.h" +#include "standard-headers/linux/virtio_ring.h" #include "hw/virtio/vhost.h" #include "hw/virtio/virtio-bus.h" @@ -56,7 +56,7 @@ static const int kernel_feature_bits[] = { }; /* Features supported by others. */ -const int user_feature_bits[] = { +static const int user_feature_bits[] = { VIRTIO_F_NOTIFY_ON_EMPTY, VIRTIO_RING_F_INDIRECT_DESC, VIRTIO_RING_F_EVENT_IDX, diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index e574bd4..2d570e4 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -86,7 +86,7 @@ static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config) memcpy(&netcfg, config, n->config_size); - if (!(vdev->guest_features >> VIRTIO_NET_F_CTRL_MAC_ADDR & 1) && + if (!virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_MAC_ADDR) && 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); @@ -120,8 +120,8 @@ static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) return; } - if (!!n->vhost_started == - (virtio_net_started(n, status) && !nc->peer->link_down)) { + if ((virtio_net_started(n, status) && !nc->peer->link_down) == + !!n->vhost_started) { return; } if (!n->vhost_started) { @@ -305,7 +305,7 @@ static RxFilterInfo *virtio_net_query_rxfilter(NetClientState *nc) info->multicast_table = str_list; info->vlan_table = get_vlan_table(n); - if (!((1 << VIRTIO_NET_F_CTRL_VLAN) & vdev->guest_features)) { + if (!virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_VLAN)) { info->vlan = RX_STATE_ALL; } else if (!info->vlan_table) { info->vlan = RX_STATE_NONE; @@ -446,23 +446,23 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t features) VirtIONet *n = VIRTIO_NET(vdev); NetClientState *nc = qemu_get_queue(n->nic); - features |= (1 << VIRTIO_NET_F_MAC); + virtio_add_feature(&features, VIRTIO_NET_F_MAC); if (!peer_has_vnet_hdr(n)) { - features &= ~(0x1 << VIRTIO_NET_F_CSUM); - features &= ~(0x1 << VIRTIO_NET_F_HOST_TSO4); - features &= ~(0x1 << VIRTIO_NET_F_HOST_TSO6); - features &= ~(0x1 << VIRTIO_NET_F_HOST_ECN); + 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); - features &= ~(0x1 << VIRTIO_NET_F_GUEST_CSUM); - features &= ~(0x1 << VIRTIO_NET_F_GUEST_TSO4); - features &= ~(0x1 << VIRTIO_NET_F_GUEST_TSO6); - features &= ~(0x1 << VIRTIO_NET_F_GUEST_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)) { - features &= ~(0x1 << VIRTIO_NET_F_GUEST_UFO); - features &= ~(0x1 << VIRTIO_NET_F_HOST_UFO); + virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_UFO); + virtio_clear_feature(&features, VIRTIO_NET_F_HOST_UFO); } if (!get_vhost_net(nc->peer)) { @@ -477,11 +477,11 @@ static uint32_t virtio_net_bad_features(VirtIODevice *vdev) /* Linux kernel 2.6.25. It understood MAC (as everyone must), * but also these: */ - features |= (1 << VIRTIO_NET_F_MAC); - features |= (1 << VIRTIO_NET_F_CSUM); - features |= (1 << VIRTIO_NET_F_HOST_TSO4); - features |= (1 << VIRTIO_NET_F_HOST_TSO6); - features |= (1 << VIRTIO_NET_F_HOST_ECN); + 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; } @@ -519,9 +519,12 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features) VirtIONet *n = VIRTIO_NET(vdev); int i; - virtio_net_set_multiqueue(n, !!(features & (1 << VIRTIO_NET_F_MQ))); + virtio_net_set_multiqueue(n, + __virtio_has_feature(features, VIRTIO_NET_F_MQ)); - virtio_net_set_mrg_rx_bufs(n, !!(features & (1 << VIRTIO_NET_F_MRG_RXBUF))); + virtio_net_set_mrg_rx_bufs(n, + __virtio_has_feature(features, + VIRTIO_NET_F_MRG_RXBUF)); if (n->has_vnet_hdr) { n->curr_guest_offloads = @@ -538,7 +541,7 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features) vhost_net_ack_features(get_vhost_net(nc->peer), features); } - if ((1 << VIRTIO_NET_F_CTRL_VLAN) & 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); @@ -585,7 +588,7 @@ static int virtio_net_handle_offloads(VirtIONet *n, uint8_t cmd, uint64_t offloads; size_t s; - if (!((1 << VIRTIO_NET_F_CTRL_GUEST_OFFLOADS) & vdev->guest_features)) { + if (!virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) { return VIRTIO_NET_ERR; } @@ -1135,7 +1138,8 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) ssize_t ret, len; unsigned int out_num = elem.out_num; struct iovec *out_sg = &elem.out_sg[0]; - struct iovec sg[VIRTQUEUE_MAX_SIZE]; + struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE + 1]; + struct virtio_net_hdr_mrg_rxbuf mhdr; if (out_num < 1) { error_report("virtio-net header not in first element"); @@ -1143,13 +1147,25 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) } if (n->has_vnet_hdr) { - if (out_sg[0].iov_len < n->guest_hdr_len) { + 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); } - virtio_net_hdr_swap(vdev, (void *) out_sg[0].iov_base); + if (virtio_needs_swap(vdev)) { + 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 @@ -1179,7 +1195,7 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) } len += ret; - +drop: virtqueue_push(q->tx_vq, &elem, 0); virtio_notify(vdev, q->tx_vq); @@ -1306,7 +1322,7 @@ static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue) n->multiqueue = multiqueue; - for (i = 2; i <= n->max_queues * 2 + 1; i++) { + for (i = 2; i < n->max_queues * 2 + 1; i++) { virtio_del_queue(vdev, i); } @@ -1378,7 +1394,7 @@ static void virtio_net_save_device(VirtIODevice *vdev, QEMUFile *f) } } - if ((1 << VIRTIO_NET_F_CTRL_GUEST_OFFLOADS) & vdev->guest_features) { + if (virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) { qemu_put_be64(f, n->curr_guest_offloads); } } @@ -1486,7 +1502,7 @@ static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f, } } - if ((1 << VIRTIO_NET_F_CTRL_GUEST_OFFLOADS) & vdev->guest_features) { + if (virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) { n->curr_guest_offloads = qemu_get_be64(f); } else { n->curr_guest_offloads = virtio_net_supported_guest_offloads(n); @@ -1513,8 +1529,8 @@ static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f, qemu_get_subqueue(n->nic, i)->link_down = link_down; } - if (vdev->guest_features & (0x1 << VIRTIO_NET_F_GUEST_ANNOUNCE) && - vdev->guest_features & (0x1 << VIRTIO_NET_F_CTRL_VQ)) { + if (virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE) && + virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) { n->announce_counter = SELF_ANNOUNCE_ROUNDS; timer_mod(n->announce_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL)); } @@ -1522,19 +1538,11 @@ static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f, return 0; } -static void virtio_net_cleanup(NetClientState *nc) -{ - VirtIONet *n = qemu_get_nic_opaque(nc); - - n->nic = NULL; -} - static NetClientInfo net_virtio_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), .can_receive = virtio_net_can_receive, .receive = virtio_net_receive, - .cleanup = virtio_net_cleanup, .link_status_changed = virtio_net_set_link_status, .query_rx_filter = virtio_net_query_rxfilter, }; @@ -1560,7 +1568,7 @@ static void virtio_net_guest_notifier_mask(VirtIODevice *vdev, int idx, void virtio_net_set_config_size(VirtIONet *n, uint32_t host_features) { int i, config_size = 0; - host_features |= (1 << VIRTIO_NET_F_MAC); + 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); @@ -1593,6 +1601,13 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) 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_PCI_QUEUE_MAX) { + error_setg(errp, "Invalid number of queues (= %" PRIu32 "), " + "must be a postive integer less than %d.", + n->max_queues, (VIRTIO_PCI_QUEUE_MAX - 1) / 2); + virtio_cleanup(vdev); + return; + } n->vqs = g_malloc0(sizeof(VirtIONetQueue) * n->max_queues); n->vqs[0].rx_vq = virtio_add_queue(vdev, 256, virtio_net_handle_rx); n->curr_queues = 1; diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index 8eea589..dfb328d 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -1912,12 +1912,6 @@ vmxnet3_receive(NetClientState *nc, const uint8_t *buf, size_t size) return bytes_indicated; } -static void vmxnet3_cleanup(NetClientState *nc) -{ - VMXNET3State *s = qemu_get_nic_opaque(nc); - s->nic = NULL; -} - static void vmxnet3_set_link_status(NetClientState *nc) { VMXNET3State *s = qemu_get_nic_opaque(nc); @@ -1937,7 +1931,6 @@ static NetClientInfo net_vmxnet3_info = { .size = sizeof(NICState), .can_receive = vmxnet3_can_receive, .receive = vmxnet3_receive, - .cleanup = vmxnet3_cleanup, .link_status_changed = vmxnet3_set_link_status, }; @@ -2132,7 +2125,7 @@ static const MemoryRegionOps b1_ops = { }, }; -static int vmxnet3_pci_init(PCIDevice *pci_dev) +static void vmxnet3_pci_realize(PCIDevice *pci_dev, Error **errp) { DeviceState *dev = DEVICE(pci_dev); VMXNET3State *s = VMXNET3(pci_dev); @@ -2171,8 +2164,6 @@ static int vmxnet3_pci_init(PCIDevice *pci_dev) register_savevm(dev, "vmxnet3-msix", -1, 1, vmxnet3_msix_save, vmxnet3_msix_load, s); - - return 0; } static void vmxnet3_instance_init(Object *obj) @@ -2508,7 +2499,7 @@ static void vmxnet3_class_init(ObjectClass *class, void *data) DeviceClass *dc = DEVICE_CLASS(class); PCIDeviceClass *c = PCI_DEVICE_CLASS(class); - c->init = vmxnet3_pci_init; + 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; diff --git a/hw/net/xen_nic.c b/hw/net/xen_nic.c index 63918ae..19ecfc4 100644 --- a/hw/net/xen_nic.c +++ b/hw/net/xen_nic.c @@ -370,11 +370,16 @@ static int net_connect(struct XenDevice *xendev) netdev->xendev.dom, netdev->tx_ring_ref, PROT_READ | PROT_WRITE); + if (!netdev->txs) { + return -1; + } netdev->rxs = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, netdev->xendev.dom, netdev->rx_ring_ref, PROT_READ | PROT_WRITE); - if (!netdev->txs || !netdev->rxs) { + if (!netdev->rxs) { + xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->txs, 1); + netdev->txs = NULL; return -1; } BACK_RING_INIT(&netdev->tx_ring, netdev->txs, XC_PAGE_SIZE); @@ -405,10 +410,6 @@ static void net_disconnect(struct XenDevice *xendev) xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->rxs, 1); netdev->rxs = NULL; } - if (netdev->nic) { - qemu_del_nic(netdev->nic); - netdev->nic = NULL; - } } static void net_event(struct XenDevice *xendev) @@ -422,7 +423,12 @@ 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; } diff --git a/hw/net/xgmac.c b/hw/net/xgmac.c index aeffcb5..b068f3a 100644 --- a/hw/net/xgmac.c +++ b/hw/net/xgmac.c @@ -368,19 +368,11 @@ out: return ret; } -static void eth_cleanup(NetClientState *nc) -{ - XgmacState *s = qemu_get_nic_opaque(nc); - - s->nic = NULL; -} - static NetClientInfo net_xgmac_enet_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), .can_receive = eth_can_rx, .receive = eth_rx, - .cleanup = eth_cleanup, }; static int xgmac_enet_init(SysBusDevice *sbd) diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c index cd952d2..21efedf 100644 --- a/hw/net/xilinx_axienet.c +++ b/hw/net/xilinx_axienet.c @@ -857,14 +857,6 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) return size; } -static void eth_cleanup(NetClientState *nc) -{ - /* FIXME. */ - XilinxAXIEnet *s = qemu_get_nic_opaque(nc); - g_free(s->rxmem); - g_free(s); -} - static size_t xilinx_axienet_control_stream_push(StreamSlave *obj, uint8_t *buf, size_t len) { @@ -936,7 +928,6 @@ static NetClientInfo net_xilinx_enet_info = { .size = sizeof(NICState), .can_receive = eth_can_rx, .receive = eth_rx, - .cleanup = eth_cleanup, }; static void xilinx_enet_realize(DeviceState *dev, Error **errp) diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c index 1b177b3..ad6b553 100644 --- a/hw/net/xilinx_ethlite.c +++ b/hw/net/xilinx_ethlite.c @@ -146,6 +146,7 @@ eth_write(void *opaque, hwaddr addr, 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: @@ -212,19 +213,11 @@ static void xilinx_ethlite_reset(DeviceState *dev) s->rxbuf = 0; } -static void eth_cleanup(NetClientState *nc) -{ - struct xlx_ethlite *s = qemu_get_nic_opaque(nc); - - s->nic = NULL; -} - static NetClientInfo net_xilinx_ethlite_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), .can_receive = eth_can_rx, .receive = eth_rx, - .cleanup = eth_cleanup, }; static void xilinx_ethlite_realize(DeviceState *dev, Error **errp) diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index a7122ee..68eff77 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -31,11 +31,16 @@ #include "qemu/config-file.h" #define FW_CFG_SIZE 2 -#define FW_CFG_DATA_SIZE 1 -#define TYPE_FW_CFG "fw_cfg" #define FW_CFG_NAME "fw_cfg" #define FW_CFG_PATH "/machine/" FW_CFG_NAME -#define FW_CFG(obj) OBJECT_CHECK(FWCfgState, (obj), TYPE_FW_CFG) + +#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) typedef struct FWCfgEntry { uint32_t len; @@ -50,8 +55,6 @@ struct FWCfgState { SysBusDevice parent_obj; /*< public >*/ - MemoryRegion ctl_iomem, data_iomem, comb_iomem; - uint32_t ctl_iobase, data_iobase; FWCfgEntry entries[2][FW_CFG_MAX_ENTRY]; FWCfgFiles *files; uint16_t cur_entry; @@ -59,6 +62,25 @@ struct FWCfgState { Notifier machine_ready; }; +struct FWCfgIoState { + /*< private >*/ + FWCfgState parent_obj; + /*< public >*/ + + MemoryRegion comb_iomem; + uint32_t 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 @@ -264,13 +286,31 @@ static uint8_t fw_cfg_read(FWCfgState *s) static uint64_t fw_cfg_data_mem_read(void *opaque, hwaddr addr, unsigned size) { - return fw_cfg_read(opaque); + FWCfgState *s = opaque; + uint64_t value = 0; + unsigned i; + + for (i = 0; i < size; ++i) { + value = (value << 8) | fw_cfg_read(s); + } + return value; } static void fw_cfg_data_mem_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - fw_cfg_write(opaque, (uint8_t)value); + FWCfgState *s = opaque; + unsigned i = size; + + do { + fw_cfg_write(s, value >> (8 * --i)); + } while (i); +} + +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, @@ -312,17 +352,18 @@ static bool fw_cfg_comb_valid(void *opaque, hwaddr addr, static const MemoryRegionOps fw_cfg_ctl_mem_ops = { .write = fw_cfg_ctl_mem_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_BIG_ENDIAN, .valid.accepts = fw_cfg_ctl_mem_valid, }; static const MemoryRegionOps fw_cfg_data_mem_ops = { .read = fw_cfg_data_mem_read, .write = fw_cfg_data_mem_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_BIG_ENDIAN, .valid = { .min_access_size = 1, .max_access_size = 1, + .accepts = fw_cfg_data_mem_valid, }, }; @@ -431,7 +472,7 @@ void fw_cfg_add_string(FWCfgState *s, uint16_t key, const char *value) { size_t sz = strlen(value) + 1; - return fw_cfg_add_bytes(s, key, g_memdup(value, sz), sz); + fw_cfg_add_bytes(s, key, g_memdup(value, sz), sz); } void fw_cfg_add_i16(FWCfgState *s, uint16_t key, uint16_t value) @@ -560,19 +601,11 @@ static void fw_cfg_machine_ready(struct Notifier *n, void *data) qemu_register_reset(fw_cfg_machine_reset, s); } -FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port, - hwaddr ctl_addr, hwaddr data_addr) -{ - DeviceState *dev; - SysBusDevice *d; - FWCfgState *s; - dev = qdev_create(NULL, TYPE_FW_CFG); - qdev_prop_set_uint32(dev, "ctl_iobase", ctl_port); - qdev_prop_set_uint32(dev, "data_iobase", data_port); - d = SYS_BUS_DEVICE(dev); - s = FW_CFG(dev); +static void fw_cfg_init1(DeviceState *dev) +{ + FWCfgState *s = FW_CFG(dev); assert(!object_resolve_path(FW_CFG_PATH, NULL)); @@ -580,13 +613,8 @@ FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port, qdev_init_nofail(dev); - if (ctl_addr) { - sysbus_mmio_map(d, 0, ctl_addr); - } - if (data_addr) { - sysbus_mmio_map(d, 1, data_addr); - } fw_cfg_add_bytes(s, FW_CFG_SIGNATURE, (char *)"QEMU", 4); + fw_cfg_add_i32(s, FW_CFG_ID, 1); fw_cfg_add_bytes(s, FW_CFG_UUID, qemu_uuid, 16); fw_cfg_add_i16(s, FW_CFG_NOGRAPHIC, (uint16_t)(display_type == DT_NOGRAPHIC)); fw_cfg_add_i16(s, FW_CFG_NB_CPUS, (uint16_t)smp_cpus); @@ -596,49 +624,43 @@ FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port, s->machine_ready.notify = fw_cfg_machine_ready; qemu_add_machine_init_done_notifier(&s->machine_ready); - - return s; } -static void fw_cfg_initfn(Object *obj) +FWCfgState *fw_cfg_init_io(uint32_t iobase) { - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - FWCfgState *s = FW_CFG(obj); + DeviceState *dev; - memory_region_init_io(&s->ctl_iomem, OBJECT(s), &fw_cfg_ctl_mem_ops, s, - "fwcfg.ctl", FW_CFG_SIZE); - sysbus_init_mmio(sbd, &s->ctl_iomem); - memory_region_init_io(&s->data_iomem, OBJECT(s), &fw_cfg_data_mem_ops, s, - "fwcfg.data", FW_CFG_DATA_SIZE); - sysbus_init_mmio(sbd, &s->data_iomem); - /* In case ctl and data overlap: */ - memory_region_init_io(&s->comb_iomem, OBJECT(s), &fw_cfg_comb_mem_ops, s, - "fwcfg", FW_CFG_SIZE); + dev = qdev_create(NULL, TYPE_FW_CFG_IO); + qdev_prop_set_uint32(dev, "iobase", iobase); + fw_cfg_init1(dev); + + return FW_CFG(dev); } -static void fw_cfg_realize(DeviceState *dev, Error **errp) +FWCfgState *fw_cfg_init_mem_wide(hwaddr ctl_addr, hwaddr data_addr, + uint32_t data_width) { - FWCfgState *s = FW_CFG(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + DeviceState *dev; + SysBusDevice *sbd; + dev = qdev_create(NULL, TYPE_FW_CFG_MEM); + qdev_prop_set_uint32(dev, "data_width", data_width); - if (s->ctl_iobase + 1 == s->data_iobase) { - sysbus_add_io(sbd, s->ctl_iobase, &s->comb_iomem); - } else { - if (s->ctl_iobase) { - sysbus_add_io(sbd, s->ctl_iobase, &s->ctl_iomem); - } - if (s->data_iobase) { - sysbus_add_io(sbd, s->data_iobase, &s->data_iomem); - } - } + fw_cfg_init1(dev); + + sbd = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(sbd, 0, ctl_addr); + sysbus_mmio_map(sbd, 1, data_addr); + + return FW_CFG(dev); +} + +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); } -static Property fw_cfg_properties[] = { - DEFINE_PROP_UINT32("ctl_iobase", FWCfgState, ctl_iobase, -1), - DEFINE_PROP_UINT32("data_iobase", FWCfgState, data_iobase, -1), - DEFINE_PROP_END_OF_LIST(), -}; FWCfgState *fw_cfg_find(void) { @@ -649,23 +671,102 @@ static void fw_cfg_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->realize = fw_cfg_realize; dc->reset = fw_cfg_reset; dc->vmsd = &vmstate_fw_cfg; - dc->props = fw_cfg_properties; } static const TypeInfo fw_cfg_info = { .name = TYPE_FW_CFG, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(FWCfgState), - .instance_init = fw_cfg_initfn, .class_init = fw_cfg_class_init, }; + +static Property fw_cfg_io_properties[] = { + DEFINE_PROP_UINT32("iobase", FWCfgIoState, iobase, -1), + 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); + + memory_region_init_io(&s->comb_iomem, OBJECT(s), &fw_cfg_comb_mem_ops, + FW_CFG(s), "fwcfg", FW_CFG_SIZE); + sysbus_add_io(sbd, s->iobase, &s->comb_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_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_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); +} + +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/hw/nvram/spapr_nvram.c b/hw/nvram/spapr_nvram.c index 35dc6d5..11332d1 100644 --- a/hw/nvram/spapr_nvram.c +++ b/hw/nvram/spapr_nvram.c @@ -132,7 +132,7 @@ static void rtas_nvram_store(PowerPCCPU *cpu, sPAPREnvironment *spapr, rtas_st(rets, 1, (alen < 0) ? 0 : alen); } -static int spapr_nvram_init(VIOsPAPRDevice *dev) +static void spapr_nvram_realize(VIOsPAPRDevice *dev, Error **errp) { sPAPRNVRAM *nvram = VIO_SPAPR_NVRAM(dev); @@ -145,23 +145,22 @@ static int spapr_nvram_init(VIOsPAPRDevice *dev) nvram->buf = g_malloc0(nvram->size); if ((nvram->size < MIN_NVRAM_SIZE) || (nvram->size > MAX_NVRAM_SIZE)) { - fprintf(stderr, "spapr-nvram must be between %d and %d bytes in size\n", - MIN_NVRAM_SIZE, MAX_NVRAM_SIZE); - return -1; + 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) { - return -1; + 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); - - return 0; } static int spapr_nvram_devnode(VIOsPAPRDevice *dev, void *fdt, int node_off) @@ -224,7 +223,7 @@ static void spapr_nvram_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VIOsPAPRDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); - k->init = spapr_nvram_init; + k->realize = spapr_nvram_realize; k->devnode = spapr_nvram_devnode; k->dt_name = "nvram"; k->dt_type = "nvram"; diff --git a/hw/pci-bridge/Makefile.objs b/hw/pci-bridge/Makefile.objs index 968b369..96c596e 100644 --- a/hw/pci-bridge/Makefile.objs +++ b/hw/pci-bridge/Makefile.objs @@ -1,5 +1,6 @@ common-obj-y += pci_bridge_dev.o -common-obj-y += ioh3420.o xio3130_upstream.o xio3130_downstream.o -common-obj-y += i82801b11.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/hw/pci-bridge/dec.c b/hw/pci-bridge/dec.c index a6ca940..28d0ff9 100644 --- a/hw/pci-bridge/dec.c +++ b/hw/pci-bridge/dec.c @@ -107,10 +107,9 @@ static int pci_dec_21154_device_init(SysBusDevice *dev) return 0; } -static int dec_21154_pci_host_init(PCIDevice *d) +static void dec_21154_pci_host_realize(PCIDevice *d, Error **errp) { /* PCI2PCI bridge same values as PearPC - check this */ - return 0; } static void dec_21154_pci_host_class_init(ObjectClass *klass, void *data) @@ -118,7 +117,7 @@ static void dec_21154_pci_host_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - k->init = dec_21154_pci_host_init; + 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; diff --git a/hw/pci-bridge/pci_bridge_dev.c b/hw/pci-bridge/pci_bridge_dev.c index 252ea5e..36f73e1 100644 --- a/hw/pci-bridge/pci_bridge_dev.c +++ b/hw/pci-bridge/pci_bridge_dev.c @@ -97,6 +97,11 @@ static void pci_bridge_dev_exitfn(PCIDevice *dev) pci_bridge_exitfn(dev); } +static void pci_bridge_dev_instance_finalize(Object *obj) +{ + shpc_free(PCI_DEVICE(obj)); +} + static void pci_bridge_dev_write_config(PCIDevice *d, uint32_t address, uint32_t val, int len) { @@ -154,10 +159,11 @@ static void pci_bridge_dev_class_init(ObjectClass *klass, void *data) } 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, + .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 }, { } diff --git a/hw/pci-host/Makefile.objs b/hw/pci-host/Makefile.objs index bb65f9c..45f1f0e 100644 --- a/hw/pci-host/Makefile.objs +++ b/hw/pci-host/Makefile.objs @@ -15,3 +15,4 @@ 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/hw/pci-host/apb.c b/hw/pci-host/apb.c index f573875..312fa70 100644 --- a/hw/pci-host/apb.c +++ b/hw/pci-host/apb.c @@ -205,6 +205,7 @@ static AddressSpace *pbm_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn) return &is->iommu_as; } +/* Called from RCU critical section */ static IOMMUTLBEntry pbm_translate_iommu(MemoryRegion *iommu, hwaddr addr, bool is_write) { @@ -791,14 +792,13 @@ static int pci_pbm_init_device(SysBusDevice *dev) return 0; } -static int pbm_pci_host_init(PCIDevice *d) +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); - return 0; } static void pbm_pci_host_class_init(ObjectClass *klass, void *data) @@ -806,7 +806,7 @@ static void pbm_pci_host_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - k->init = pbm_pci_host_init; + 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; diff --git a/hw/pci-host/bonito.c b/hw/pci-host/bonito.c index 56292ad..8134d0b 100644 --- a/hw/pci-host/bonito.c +++ b/hw/pci-host/bonito.c @@ -233,7 +233,7 @@ static void bonito_writel(void *opaque, hwaddr addr, uint32_t saddr; int reset = 0; - saddr = (addr - BONITO_REGBASE) >> 2; + saddr = addr >> 2; DPRINTF("bonito_writel "TARGET_FMT_plx" val %x saddr %x\n", addr, val, saddr); switch (saddr) { @@ -295,7 +295,7 @@ static uint64_t bonito_readl(void *opaque, hwaddr addr, PCIBonitoState *s = opaque; uint32_t saddr; - saddr = (addr - BONITO_REGBASE) >> 2; + saddr = addr >> 2; DPRINTF("bonito_readl "TARGET_FMT_plx"\n", addr); switch (saddr) { @@ -705,7 +705,7 @@ static int bonito_pcihost_initfn(SysBusDevice *dev) return 0; } -static int bonito_initfn(PCIDevice *dev) +static void bonito_realize(PCIDevice *dev, Error **errp) { PCIBonitoState *s = DO_UPCAST(PCIBonitoState, dev, dev); SysBusDevice *sysbus = SYS_BUS_DEVICE(s->pcihost); @@ -766,8 +766,6 @@ static int bonito_initfn(PCIDevice *dev) pci_set_byte(dev->config + PCI_MAX_LAT, 0x00); qemu_register_reset(bonito_reset, s); - - return 0; } PCIBus *bonito_init(qemu_irq *pic) @@ -799,7 +797,7 @@ static void bonito_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = bonito_initfn; + k->realize = bonito_realize; k->vendor_id = 0xdf53; k->device_id = 0x00d5; k->revision = 0x01; diff --git a/hw/pci-host/gpex.c b/hw/pci-host/gpex.c new file mode 100644 index 0000000..9d8fb5a --- /dev/null +++ b/hw/pci-host/gpex.c @@ -0,0 +1,154 @@ +/* + * 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 "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/hw/pci-host/grackle.c b/hw/pci-host/grackle.c index 6c7cfdb..bfe707a 100644 --- a/hw/pci-host/grackle.c +++ b/hw/pci-host/grackle.c @@ -114,10 +114,9 @@ static int pci_grackle_init_device(SysBusDevice *dev) return 0; } -static int grackle_pci_host_init(PCIDevice *d) +static void grackle_pci_host_realize(PCIDevice *d, Error **errp) { d->config[0x09] = 0x01; - return 0; } static void grackle_pci_class_init(ObjectClass *klass, void *data) @@ -125,7 +124,7 @@ static void grackle_pci_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - k->init = grackle_pci_host_init; + k->realize = grackle_pci_host_realize; k->vendor_id = PCI_VENDOR_ID_MOTOROLA; k->device_id = PCI_DEVICE_ID_MOTOROLA_MPC106; k->revision = 0x00; diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c index 1530038..723836f 100644 --- a/hw/pci-host/piix.c +++ b/hw/pci-host/piix.c @@ -295,14 +295,13 @@ static void i440fx_pcihost_realize(DeviceState *dev, Error **errp) sysbus_init_ioports(sbd, 0xcfc, 4); } -static int i440fx_initfn(PCIDevice *dev) +static void i440fx_realize(PCIDevice *dev, Error **errp) { PCII440FXState *d = I440FX_PCI_DEVICE(dev); dev->config[I440FX_SMRAM] = 0x02; cpu_smm_register(&i440fx_set_smm, d); - return 0; } PCIBus *i440fx_init(PCII440FXState **pi440fx_state, @@ -631,11 +630,12 @@ static const MemoryRegionOps rcr_ops = { .endianness = DEVICE_LITTLE_ENDIAN }; -static int piix3_initfn(PCIDevice *dev) +static void piix3_realize(PCIDevice *dev, Error **errp) { PIIX3State *d = DO_UPCAST(PIIX3State, dev, dev); - isa_bus_new(DEVICE(d), pci_address_space_io(dev)); + isa_bus_new(DEVICE(d), get_system_memory(), + pci_address_space_io(dev)); memory_region_init_io(&d->rcr_mem, OBJECT(dev), &rcr_ops, d, "piix3-reset-control", 1); @@ -643,7 +643,6 @@ static int piix3_initfn(PCIDevice *dev) &d->rcr_mem, 1); qemu_register_reset(piix3_reset, d); - return 0; } static void piix3_class_init(ObjectClass *klass, void *data) @@ -654,7 +653,7 @@ static void piix3_class_init(ObjectClass *klass, void *data) dc->desc = "ISA bridge"; dc->vmsd = &vmstate_piix3; dc->hotpluggable = false; - k->init = piix3_initfn; + k->realize = piix3_realize; k->config_write = piix3_write_config; k->vendor_id = PCI_VENDOR_ID_INTEL; /* 82371SB PIIX3 PCI-to-ISA bridge (Step A1) */ @@ -682,7 +681,7 @@ static void piix3_xen_class_init(ObjectClass *klass, void *data) dc->desc = "ISA bridge"; dc->vmsd = &vmstate_piix3; dc->hotpluggable = false; - k->init = piix3_initfn; + k->realize = piix3_realize; k->config_write = piix3_write_config_xen; k->vendor_id = PCI_VENDOR_ID_INTEL; /* 82371SB PIIX3 PCI-to-ISA bridge (Step A1) */ @@ -707,7 +706,7 @@ static void i440fx_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = i440fx_initfn; + 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; diff --git a/hw/pci-host/ppce500.c b/hw/pci-host/ppce500.c index 1b4c0f0..613ba73 100644 --- a/hw/pci-host/ppce500.c +++ b/hw/pci-host/ppce500.c @@ -62,11 +62,19 @@ #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 { @@ -74,6 +82,7 @@ struct pci_inbound { uint32_t piwbar; uint32_t piwbear; uint32_t piwar; + MemoryRegion mem; }; #define TYPE_PPC_E500_PCI_HOST_BRIDGE "e500-pcihost" @@ -91,10 +100,13 @@ struct PPCE500PCIState { 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" @@ -181,6 +193,71 @@ static uint64_t pci_reg_read4(void *opaque, hwaddr addr, 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) { @@ -199,18 +276,22 @@ static void pci_reg_write4(void *opaque, hwaddr addr, case PPCE500_PCI_OW3: case PPCE500_PCI_OW4: idx = (addr >> 5) & 0x7; - switch (addr & 0xC) { + switch (addr & 0x1F) { case PCI_POTAR: 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; @@ -221,18 +302,22 @@ static void pci_reg_write4(void *opaque, hwaddr addr, case PPCE500_PCI_IW2: case PPCE500_PCI_IW1: idx = ((addr >> 5) & 0x3) - 1; - switch (addr & 0xC) { + switch (addr & 0x1F) { case PCI_PITAR: 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; @@ -331,7 +416,7 @@ static const VMStateDescription vmstate_ppce500_pci = { #include "exec/address-spaces.h" -static int e500_pcihost_bridge_initfn(PCIDevice *d) +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(), @@ -345,8 +430,14 @@ static int e500_pcihost_bridge_initfn(PCIDevice *d) 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); +} - return 0; +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) @@ -355,7 +446,6 @@ static int e500_pcihost_initfn(SysBusDevice *dev) PPCE500PCIState *s; PCIBus *b; int i; - MemoryRegion *address_space_mem = get_system_memory(); h = PCI_HOST_BRIDGE(dev); s = PPC_E500_PCI_HOST_BRIDGE(dev); @@ -369,12 +459,22 @@ static int e500_pcihost_initfn(SysBusDevice *dev) } 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, address_space_mem, - &s->pio, PCI_DEVFN(s->first_slot, 0), 4, TYPE_PCI_BUS); + 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); @@ -388,7 +488,6 @@ static int e500_pcihost_initfn(SysBusDevice *dev) 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); - sysbus_init_mmio(dev, &s->pio); pci_bus_set_route_irq_fn(b, e500_route_intx_pin_to_irq); return 0; @@ -399,7 +498,7 @@ static void e500_host_bridge_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = e500_pcihost_bridge_initfn; + 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; diff --git a/hw/pci-host/prep.c b/hw/pci-host/prep.c index 1de3681..6cea6ff 100644 --- a/hw/pci-host/prep.c +++ b/hw/pci-host/prep.c @@ -289,7 +289,7 @@ static void raven_pcihost_initfn(Object *obj) qdev_prop_set_bit(pci_dev, "multifunction", false); } -static int raven_init(PCIDevice *d) +static void raven_realize(PCIDevice *d, Error **errp) { RavenPCIState *s = RAVEN_PCI_DEVICE(d); char *filename; @@ -330,8 +330,6 @@ static int raven_init(PCIDevice *d) g_free(filename); } } - - return 0; } static const VMStateDescription vmstate_raven = { @@ -349,7 +347,7 @@ static void raven_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - k->init = raven_init; + k->realize = raven_realize; k->vendor_id = PCI_VENDOR_ID_MOTOROLA; k->device_id = PCI_DEVICE_ID_MOTOROLA_RAVEN; k->revision = 0x00; diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index b20bad8..c8827cc 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -390,7 +390,7 @@ static void mch_init_dmar(MCHPCIState *mch) pci_setup_iommu(pci_bus, q35_host_dma_iommu, mch->iommu); } -static int mch_init(PCIDevice *d) +static void mch_realize(PCIDevice *d, Error **errp) { int i; MCHPCIState *mch = MCH_PCI_DEVICE(d); @@ -415,10 +415,9 @@ static int mch_init(PCIDevice *d) PAM_EXPAN_BASE + i * PAM_EXPAN_SIZE, PAM_EXPAN_SIZE); } /* Intel IOMMU (VT-d) */ - if (qemu_opt_get_bool(qemu_get_machine_opts(), "iommu", false)) { + if (machine_iommu(current_machine)) { mch_init_dmar(mch); } - return 0; } uint64_t mch_mcfg_base(void) @@ -436,7 +435,7 @@ static void mch_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - k->init = mch_init; + k->realize = mch_realize; k->config_write = mch_write_config; dc->reset = mch_reset; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c index 21f805f..53f2b59 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -315,37 +315,33 @@ PCIBus *pci_pmac_u3_init(qemu_irq *pic, return h->bus; } -static int unin_main_pci_host_init(PCIDevice *d) +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 - return 0; } -static int unin_agp_pci_host_init(PCIDevice *d) +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 - return 0; } -static int u3_agp_pci_host_init(PCIDevice *d) +static void u3_agp_pci_host_realize(PCIDevice *d, Error **errp) { /* cache line size */ d->config[0x0C] = 0x08; /* latency timer */ d->config[0x0D] = 0x10; - return 0; } -static int unin_internal_pci_host_init(PCIDevice *d) +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 - return 0; } static void unin_main_pci_host_class_init(ObjectClass *klass, void *data) @@ -353,7 +349,7 @@ static void unin_main_pci_host_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - k->init = unin_main_pci_host_init; + 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; @@ -377,7 +373,7 @@ static void u3_agp_pci_host_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - k->init = u3_agp_pci_host_init; + 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; @@ -401,7 +397,7 @@ static void unin_agp_pci_host_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - k->init = unin_agp_pci_host_init; + 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; @@ -425,7 +421,7 @@ static void unin_internal_pci_host_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - k->init = unin_internal_pci_host_init; + 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; diff --git a/hw/pci-host/versatile.c b/hw/pci-host/versatile.c index 71ff0de..6d23553 100644 --- a/hw/pci-host/versatile.c +++ b/hw/pci-host/versatile.c @@ -456,12 +456,11 @@ static void pci_vpb_realize(DeviceState *dev, Error **errp) object_property_set_bool(OBJECT(&s->pci_dev), true, "realized", errp); } -static int versatile_pci_host_init(PCIDevice *d) +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); - return 0; } static void versatile_pci_host_class_init(ObjectClass *klass, void *data) @@ -469,7 +468,7 @@ static void versatile_pci_host_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - k->init = versatile_pci_host_init; + 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; diff --git a/hw/pci/Makefile.objs b/hw/pci/Makefile.objs index 80f8aa6..9f905e6 100644 --- a/hw/pci/Makefile.objs +++ b/hw/pci/Makefile.objs @@ -7,5 +7,3 @@ 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 - -common-obj-$(CONFIG_PCI_HOTPLUG_OLD) += pci-hotplug-old.o diff --git a/hw/pci/pci-hotplug-old.c b/hw/pci/pci-hotplug-old.c deleted file mode 100644 index 0c09c72..0000000 --- a/hw/pci/pci-hotplug-old.c +++ /dev/null @@ -1,343 +0,0 @@ -/* - * Deprecated PCI hotplug interface support - * This covers the old pci_add / pci_del command, whereas the more general - * device_add / device_del commands are now preferred. - * - * 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 "hw/hw.h" -#include "hw/boards.h" -#include "hw/pci/pci.h" -#include "net/net.h" -#include "hw/i386/pc.h" -#include "monitor/monitor.h" -#include "hw/scsi/scsi.h" -#include "hw/virtio/virtio-blk.h" -#include "qemu/config-file.h" -#include "sysemu/block-backend.h" -#include "qapi/error.h" - -static int pci_read_devaddr(Monitor *mon, const char *addr, - int *busp, unsigned *slotp) -{ - int dom; - - /* strip legacy tag */ - if (!strncmp(addr, "pci_addr=", 9)) { - addr += 9; - } - if (pci_parse_devaddr(addr, &dom, busp, slotp, NULL)) { - monitor_printf(mon, "Invalid pci address\n"); - return -1; - } - if (dom != 0) { - monitor_printf(mon, "Multiple PCI domains not supported, use device_add\n"); - return -1; - } - return 0; -} - -static PCIDevice *qemu_pci_hot_add_nic(Monitor *mon, - const char *devaddr, - const char *opts_str) -{ - Error *local_err = NULL; - QemuOpts *opts; - PCIBus *root = pci_find_primary_bus(); - PCIBus *bus; - int ret, devfn; - - if (!root) { - monitor_printf(mon, "no primary PCI bus (if there are multiple" - " PCI roots, you must use device_add instead)"); - return NULL; - } - - bus = pci_get_bus_devfn(&devfn, root, devaddr); - if (!bus) { - monitor_printf(mon, "Invalid PCI device address %s\n", devaddr); - return NULL; - } - if (!qbus_is_hotpluggable(BUS(bus))) { - monitor_printf(mon, "PCI bus doesn't support hotplug\n"); - return NULL; - } - - opts = qemu_opts_parse(qemu_find_opts("net"), opts_str ? opts_str : "", 0); - if (!opts) { - return NULL; - } - - qemu_opt_set(opts, "type", "nic"); - - ret = net_client_init(opts, 0, &local_err); - if (local_err) { - qerror_report_err(local_err); - error_free(local_err); - return NULL; - } - if (nd_table[ret].devaddr) { - monitor_printf(mon, "Parameter addr not supported\n"); - return NULL; - } - return pci_nic_init(&nd_table[ret], root, "rtl8139", devaddr); -} - -static int scsi_hot_add(Monitor *mon, DeviceState *adapter, - DriveInfo *dinfo, int printinfo) -{ - SCSIBus *scsibus; - SCSIDevice *scsidev; - Error *local_err = NULL; - - scsibus = (SCSIBus *) - object_dynamic_cast(OBJECT(QLIST_FIRST(&adapter->child_bus)), - TYPE_SCSI_BUS); - if (!scsibus) { - error_report("Device is not a SCSI adapter"); - return -1; - } - - /* - * drive_init() tries to find a default for dinfo->unit. Doesn't - * work at all for hotplug though as we assign the device to a - * specific bus instead of the first bus with spare scsi ids. - * - * Ditch the calculated value and reload from option string (if - * specified). - */ - dinfo->unit = qemu_opt_get_number(dinfo->opts, "unit", -1); - dinfo->bus = scsibus->busnr; - scsidev = scsi_bus_legacy_add_drive(scsibus, - blk_by_legacy_dinfo(dinfo), - dinfo->unit, false, -1, NULL, - &local_err); - if (!scsidev) { - error_report("%s", error_get_pretty(local_err)); - error_free(local_err); - return -1; - } - dinfo->unit = scsidev->id; - - if (printinfo) - monitor_printf(mon, "OK bus %d, unit %d\n", - scsibus->busnr, scsidev->id); - return 0; -} - -int pci_drive_hot_add(Monitor *mon, const QDict *qdict, DriveInfo *dinfo) -{ - int pci_bus; - unsigned slot; - PCIBus *root = pci_find_primary_bus(); - PCIDevice *dev; - const char *pci_addr = qdict_get_str(qdict, "pci_addr"); - - switch (dinfo->type) { - case IF_SCSI: - if (!root) { - monitor_printf(mon, "no primary PCI bus (if there are multiple" - " PCI roots, you must use device_add instead)"); - goto err; - } - if (pci_read_devaddr(mon, pci_addr, &pci_bus, &slot)) { - goto err; - } - dev = pci_find_device(root, pci_bus, PCI_DEVFN(slot, 0)); - if (!dev) { - monitor_printf(mon, "no pci device with address %s\n", pci_addr); - goto err; - } - if (scsi_hot_add(mon, &dev->qdev, dinfo, 1) != 0) { - goto err; - } - break; - default: - monitor_printf(mon, "Can't hot-add drive to type %d\n", dinfo->type); - goto err; - } - - return 0; -err: - return -1; -} - -static PCIDevice *qemu_pci_hot_add_storage(Monitor *mon, - const char *devaddr, - const char *opts) -{ - PCIDevice *dev; - DriveInfo *dinfo = NULL; - int type = -1; - char buf[128]; - PCIBus *root = pci_find_primary_bus(); - PCIBus *bus; - int devfn; - - if (get_param_value(buf, sizeof(buf), "if", opts)) { - if (!strcmp(buf, "scsi")) - type = IF_SCSI; - else if (!strcmp(buf, "virtio")) { - type = IF_VIRTIO; - } else { - monitor_printf(mon, "type %s not a hotpluggable PCI device.\n", buf); - return NULL; - } - } else { - monitor_printf(mon, "no if= specified\n"); - return NULL; - } - - if (get_param_value(buf, sizeof(buf), "file", opts)) { - dinfo = add_init_drive(opts); - if (!dinfo) - return NULL; - if (dinfo->devaddr) { - monitor_printf(mon, "Parameter addr not supported\n"); - return NULL; - } - } else { - dinfo = NULL; - } - - if (!root) { - monitor_printf(mon, "no primary PCI bus (if there are multiple" - " PCI roots, you must use device_add instead)"); - return NULL; - } - bus = pci_get_bus_devfn(&devfn, root, devaddr); - if (!bus) { - monitor_printf(mon, "Invalid PCI device address %s\n", devaddr); - return NULL; - } - if (!qbus_is_hotpluggable(BUS(bus))) { - monitor_printf(mon, "PCI bus doesn't support hotplug\n"); - return NULL; - } - - switch (type) { - case IF_SCSI: - dev = pci_create(bus, devfn, "lsi53c895a"); - if (qdev_init(&dev->qdev) < 0) - dev = NULL; - if (dev && dinfo) { - if (scsi_hot_add(mon, &dev->qdev, dinfo, 0) != 0) { - qdev_unplug(&dev->qdev, NULL); - dev = NULL; - } - } - break; - case IF_VIRTIO: - if (!dinfo) { - monitor_printf(mon, "virtio requires a backing file/device.\n"); - return NULL; - } - dev = pci_create(bus, devfn, "virtio-blk-pci"); - if (qdev_prop_set_drive(&dev->qdev, "drive", - blk_by_legacy_dinfo(dinfo)) < 0) { - object_unparent(OBJECT(dev)); - dev = NULL; - break; - } - if (qdev_init(&dev->qdev) < 0) - dev = NULL; - break; - default: - dev = NULL; - } - return dev; -} - -void pci_device_hot_add(Monitor *mon, const QDict *qdict) -{ - PCIDevice *dev = NULL; - const char *pci_addr = qdict_get_str(qdict, "pci_addr"); - const char *type = qdict_get_str(qdict, "type"); - const char *opts = qdict_get_try_str(qdict, "opts"); - - /* strip legacy tag */ - if (!strncmp(pci_addr, "pci_addr=", 9)) { - pci_addr += 9; - } - - if (!opts) { - opts = ""; - } - - if (!strcmp(pci_addr, "auto")) - pci_addr = NULL; - - if (strcmp(type, "nic") == 0) { - dev = qemu_pci_hot_add_nic(mon, pci_addr, opts); - } else if (strcmp(type, "storage") == 0) { - dev = qemu_pci_hot_add_storage(mon, pci_addr, opts); - } else { - monitor_printf(mon, "invalid type: %s\n", type); - } - - if (dev) { - monitor_printf(mon, "OK root bus %s, bus %d, slot %d, function %d\n", - pci_root_bus_path(dev), - pci_bus_num(dev->bus), PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn)); - } else - monitor_printf(mon, "failed to add %s\n", opts); -} - -static int pci_device_hot_remove(Monitor *mon, const char *pci_addr) -{ - PCIBus *root = pci_find_primary_bus(); - PCIDevice *d; - int bus; - unsigned slot; - Error *local_err = NULL; - - if (!root) { - monitor_printf(mon, "no primary PCI bus (if there are multiple" - " PCI roots, you must use device_del instead)"); - return -1; - } - - if (pci_read_devaddr(mon, pci_addr, &bus, &slot)) { - return -1; - } - - d = pci_find_device(root, bus, PCI_DEVFN(slot, 0)); - if (!d) { - monitor_printf(mon, "slot %d empty\n", slot); - return -1; - } - - qdev_unplug(&d->qdev, &local_err); - if (local_err) { - monitor_printf(mon, "%s\n", error_get_pretty(local_err)); - error_free(local_err); - return -1; - } - - return 0; -} - -void do_pci_device_hot_remove(Monitor *mon, const QDict *qdict) -{ - pci_device_hot_remove(mon, qdict_get_str(qdict, "pci_addr")); -} diff --git a/hw/pci/pci-stub.c b/hw/pci/pci-stub.c index 1dda89b..5e564c3 100644 --- a/hw/pci/pci-stub.c +++ b/hw/pci/pci-stub.c @@ -34,7 +34,7 @@ static void pci_error_message(Monitor *mon) monitor_printf(mon, "PCI devices not supported\n"); } -int do_pcie_aer_inject_error(Monitor *mon, +int hmp_pcie_aer_inject_error(Monitor *mon, const QDict *qdict, QObject **ret_data) { pci_error_message(mon); diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 371699c..b3d5100 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -32,6 +32,7 @@ #include "hw/loader.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" @@ -114,7 +115,7 @@ static const TypeInfo pcie_bus_info = { 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 int pci_add_option_rom(PCIDevice *pdev, bool is_default_rom); +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; @@ -512,7 +513,7 @@ void pci_device_save(PCIDevice *s, QEMUFile *f) * 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); + vmstate_save_state(f, pci_get_vmstate(s), s, NULL); /* Restore the interrupt status bit. */ pci_update_irq_status(s); } @@ -538,8 +539,8 @@ static void pci_set_default_subsystem_id(PCIDevice *pci_dev) * Parse [[:]:], return -1 on error if funcp == NULL * [[:]:]., return -1 on error */ -int pci_parse_devaddr(const char *addr, int *domp, int *busp, - unsigned int *slotp, unsigned int *funcp) +static int pci_parse_devaddr(const char *addr, int *domp, int *busp, + unsigned int *slotp, unsigned int *funcp) { const char *p; char *e; @@ -597,7 +598,8 @@ int pci_parse_devaddr(const char *addr, int *domp, int *busp, return 0; } -PCIBus *pci_get_bus_devfn(int *devfnp, PCIBus *root, const char *devaddr) +static PCIBus *pci_get_bus_devfn(int *devfnp, PCIBus *root, + const char *devaddr) { int dom, bus; unsigned slot; @@ -725,7 +727,7 @@ static void pci_init_mask_bridge(PCIDevice *d) PCI_PREF_RANGE_TYPE_MASK); } -static int pci_init_multifunction(PCIBus *bus, PCIDevice *dev) +static void pci_init_multifunction(PCIBus *bus, PCIDevice *dev, Error **errp) { uint8_t slot = PCI_SLOT(dev->devfn); uint8_t func; @@ -751,26 +753,25 @@ static int pci_init_multifunction(PCIBus *bus, PCIDevice *dev) PCIDevice *f0 = bus->devices[PCI_DEVFN(slot, 0)]; if (f0 && !(f0->cap_present & QEMU_PCI_CAP_MULTIFUNCTION)) { /* function 0 should set multifunction bit */ - error_report("PCI: single function device can't be populated " - "in function %x.%x", slot, PCI_FUNC(dev->devfn)); - return -1; + error_setg(errp, "PCI: single function device can't be populated " + "in function %x.%x", slot, PCI_FUNC(dev->devfn)); + return; } - return 0; + return; } if (dev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { - return 0; + 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_report("PCI: %x.0 indicates single function, " - "but %x.%x is already populated.", - slot, slot, func); - return -1; + error_setg(errp, "PCI: %x.0 indicates single function, " + "but %x.%x is already populated.", + slot, slot, func); + return; } } - return 0; } static void pci_config_alloc(PCIDevice *pci_dev) @@ -803,11 +804,13 @@ static void do_pci_unregister_device(PCIDevice *pci_dev) /* -1 for devfn means auto assign */ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, - const char *name, int devfn) + 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; if (devfn < 0) { @@ -816,12 +819,15 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, if (!bus->devices[devfn]) goto found; } - error_report("PCI: no slot/function available for %s, all in use", name); + error_setg(errp, "PCI: no slot/function available for %s, all in use", + name); return NULL; found: ; } else if (bus->devices[devfn]) { - error_report("PCI: slot %d function %d not available for %s, in use by %s", - PCI_SLOT(devfn), PCI_FUNC(devfn), name, bus->devices[devfn]->name); + 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; } @@ -865,7 +871,9 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, if (pc->is_bridge) { pci_init_mask_bridge(pci_dev); } - if (pci_init_multifunction(bus, 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; } @@ -896,7 +904,7 @@ static void pci_unregister_io_regions(PCIDevice *pci_dev) pci_unregister_vga(pci_dev); } -static int pci_unregister_device(DeviceState *dev) +static void pci_qdev_unrealize(DeviceState *dev, Error **errp) { PCIDevice *pci_dev = PCI_DEVICE(dev); PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(pci_dev); @@ -909,7 +917,6 @@ static int pci_unregister_device(DeviceState *dev) } do_pci_unregister_device(pci_dev); - return 0; } void pci_register_bar(PCIDevice *pci_dev, int region_num, @@ -1106,10 +1113,18 @@ static void pci_update_mappings(PCIDevice *d) /* now do the real mapping */ if (r->addr != PCI_BAR_UNMAPPED) { + trace_pci_update_mappings_del(d, pci_bus_num(d->bus), + PCI_FUNC(d->devfn), + PCI_SLOT(d->devfn), + i, r->addr, r->size); memory_region_del_subregion(r->address_space, r->memory); } r->addr = new_addr; if (r->addr != PCI_BAR_UNMAPPED) { + trace_pci_update_mappings_add(d, pci_bus_num(d->bus), + PCI_FUNC(d->devfn), + PCI_SLOT(d->devfn), + i, r->addr, r->size); memory_region_add_subregion_overlap(r->address_space, r->addr, r->memory, 1); } @@ -1596,12 +1611,13 @@ static const char * const pci_nic_names[] = { }; /* Initialize a PCI NIC. */ -/* FIXME callers should check for failure, but don't */ -PCIDevice *pci_nic_init(NICInfo *nd, PCIBus *rootbus, - const char *default_model, - const char *default_devaddr) +static PCIDevice *pci_nic_init(NICInfo *nd, PCIBus *rootbus, + const char *default_model, + const char *default_devaddr, + Error **errp) { const char *devaddr = nd->devaddr ? nd->devaddr : default_devaddr; + Error *err = NULL; PCIBus *bus; int devfn; PCIDevice *pci_dev; @@ -1622,8 +1638,13 @@ PCIDevice *pci_nic_init(NICInfo *nd, PCIBus *rootbus, pci_dev = pci_create(bus, devfn, pci_nic_names[i]); dev = &pci_dev->qdev; qdev_set_nic_properties(dev, nd); - if (qdev_init(dev) < 0) + + object_property_set_bool(OBJECT(dev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + object_unparent(OBJECT(dev)); return NULL; + } return pci_dev; } @@ -1631,14 +1652,19 @@ PCIDevice *pci_nic_init_nofail(NICInfo *nd, PCIBus *rootbus, const char *default_model, const char *default_devaddr) { + Error *err = NULL; PCIDevice *res; if (qemu_show_nic_models(nd->model, pci_nic_models)) exit(0); - res = pci_nic_init(nd, rootbus, default_model, default_devaddr); - if (!res) + res = pci_nic_init(nd, rootbus, default_model, default_devaddr, &err); + if (!res) { + if (err) { + error_report_err(err); + } exit(1); + } return res; } @@ -1742,12 +1768,12 @@ PCIDevice *pci_find_device(PCIBus *bus, int bus_num, uint8_t devfn) return bus->devices[devfn]; } -static int pci_qdev_init(DeviceState *qdev) +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; - int rc; bool is_default_rom; /* initialize cap_present for pci_is_express() and pci_config_size() */ @@ -1758,15 +1784,16 @@ static int pci_qdev_init(DeviceState *qdev) 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); + pci_dev->devfn, errp); if (pci_dev == NULL) - return -1; + return; - if (pc->init) { - rc = pc->init(pci_dev); - if (rc != 0) { + if (pc->realize) { + pc->realize(pci_dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); do_pci_unregister_device(pci_dev); - return rc; + return; } } @@ -1777,13 +1804,24 @@ static int pci_qdev_init(DeviceState *qdev) is_default_rom = true; } - rc = pci_add_option_rom(pci_dev, is_default_rom); - if (rc != 0) { - pci_unregister_device(DEVICE(pci_dev)); - return rc; + 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; } +} - return 0; +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, @@ -1923,7 +1961,8 @@ static void pci_patch_ids(PCIDevice *pdev, uint8_t *ptr, int size) } /* Add an option rom for the device */ -static int pci_add_option_rom(PCIDevice *pdev, bool is_default_rom) +static void pci_add_option_rom(PCIDevice *pdev, bool is_default_rom, + Error **errp) { int size; char *path; @@ -1932,9 +1971,9 @@ static int pci_add_option_rom(PCIDevice *pdev, bool is_default_rom) const VMStateDescription *vmsd; if (!pdev->romfile) - return 0; + return; if (strlen(pdev->romfile) == 0) - return 0; + return; if (!pdev->rom_bar) { /* @@ -1948,7 +1987,9 @@ static int pci_add_option_rom(PCIDevice *pdev, bool is_default_rom) * if the rom bar is disabled. */ if (DEVICE(pdev)->hotplugged) { - return -1; + error_setg(errp, "Hot-plugged device without ROM bar" + " can't have an option ROM"); + return; } if (class == 0x0300) { @@ -1956,7 +1997,7 @@ static int pci_add_option_rom(PCIDevice *pdev, bool is_default_rom) } else { rom_add_option(pdev->romfile, -1); } - return 0; + return; } path = qemu_find_file(QEMU_FILE_TYPE_BIOS, pdev->romfile); @@ -1966,15 +2007,13 @@ static int pci_add_option_rom(PCIDevice *pdev, bool is_default_rom) size = get_image_size(path); if (size < 0) { - error_report("%s: failed to find romfile \"%s\"", - __func__, pdev->romfile); + error_setg(errp, "failed to find romfile \"%s\"", pdev->romfile); g_free(path); - return -1; + return; } else if (size == 0) { - error_report("%s: ignoring empty romfile \"%s\"", - __func__, pdev->romfile); + error_setg(errp, "romfile \"%s\" is empty", pdev->romfile); g_free(path); - return -1; + return; } if (size & (size - 1)) { size = 1 << qemu_fls(size); @@ -2000,8 +2039,6 @@ static int pci_add_option_rom(PCIDevice *pdev, bool is_default_rom) } pci_register_bar(pdev, PCI_ROM_SLOT, 0, &pdev->rom); - - return 0; } static void pci_del_option_rom(PCIDevice *pdev) @@ -2029,8 +2066,7 @@ int pci_add_capability(PCIDevice *pdev, uint8_t cap_id, ret = pci_add_capability2(pdev, cap_id, offset, size, &local_err); if (local_err) { assert(ret < 0); - error_report("%s", error_get_pretty(local_err)); - error_free(local_err); + error_report_err(local_err); } else { /* success implies a positive offset in config space */ assert(ret > 0); @@ -2283,10 +2319,13 @@ MemoryRegion *pci_address_space_io(PCIDevice *dev) static void pci_device_class_init(ObjectClass *klass, void *data) { DeviceClass *k = DEVICE_CLASS(klass); - k->init = pci_qdev_init; - k->exit = pci_unregister_device; + 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) diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index 1abbbb1..1463e65 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -84,7 +84,7 @@ int pcie_cap_init(PCIDevice *dev, uint8_t offset, uint8_t type, uint8_t port) 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_EETLPPB); + pci_set_word(dev->wmask + pos + PCI_EXP_DEVCTL2, PCI_EXP_DEVCTL2_EETLPPB); return pos; } diff --git a/hw/pci/pcie_aer.c b/hw/pci/pcie_aer.c index 1f4be16..eaa3e6e 100644 --- a/hw/pci/pcie_aer.c +++ b/hw/pci/pcie_aer.c @@ -123,7 +123,7 @@ int pcie_aer_init(PCIDevice *dev, uint16_t offset) PCI_ERR_UNC_SUPPORTED); pci_long_test_and_set_mask(dev->w1cmask + offset + PCI_ERR_COR_STATUS, - PCI_ERR_COR_STATUS); + PCI_ERR_COR_SUPPORTED); pci_set_long(dev->config + offset + PCI_ERR_COR_MASK, PCI_ERR_COR_MASK_DEFAULT); @@ -433,7 +433,7 @@ static void pcie_aer_update_log(PCIDevice *dev, const PCIEAERErr *err) } if ((err->flags & PCIE_AER_ERR_TLP_PREFIX_PRESENT) && - (pci_get_long(dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL2) & + (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 */ @@ -618,12 +618,12 @@ static bool pcie_aer_inject_uncor_error(PCIEAERInject *inj, bool is_fatal) * 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 rerror. + * report the error. * * 6.2.4 Error Logging - * 6.2.5 Sqeunce of Device Error Signaling and Logging Operations - * table 6-2: Flowchard Showing Sequence of Device Error Signaling and Logging - * Operations + * 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) { @@ -962,7 +962,7 @@ static int pcie_aer_parse_error_string(const char *error_name, return -EINVAL; } -int do_pcie_aer_inject_error(Monitor *mon, +int hmp_pcie_aer_inject_error(Monitor *mon, const QDict *qdict, QObject **ret_data) { const char *id = qdict_get_str(qdict, "id"); diff --git a/hw/pci/pcie_host.c b/hw/pci/pcie_host.c index 3db038f..d8afba8 100644 --- a/hw/pci/pcie_host.c +++ b/hw/pci/pcie_host.c @@ -88,6 +88,8 @@ 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) @@ -98,15 +100,19 @@ void pcie_host_mmcfg_unmap(PCIExpressHost *e) } } -void pcie_host_mmcfg_map(PCIExpressHost *e, hwaddr addr, - uint32_t size) +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_init_io(&e->mmio, OBJECT(e), &pcie_mmcfg_ops, e, - "pcie-mmcfg", e->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); } @@ -116,10 +122,12 @@ void pcie_host_mmcfg_update(PCIExpressHost *e, 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 = { diff --git a/hw/pci/shpc.c b/hw/pci/shpc.c index 27c496e..759910f 100644 --- a/hw/pci/shpc.c +++ b/hw/pci/shpc.c @@ -159,7 +159,7 @@ static void shpc_interrupt_update(PCIDevice *d) 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 = 1 << SHPC_IDX_TO_LOGICAL(slot); + uint32_t mask = 1U << SHPC_IDX_TO_LOGICAL(slot); if (event & ~disable) { int_locator |= mask; } @@ -663,13 +663,22 @@ 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); - object_unparent(OBJECT(&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) diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index 19d9920..437955d 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -3,7 +3,7 @@ obj-y += ppc.o ppc_booke.o # IBM pSeries (sPAPR) obj-$(CONFIG_PSERIES) += spapr.o spapr_vio.o spapr_events.o obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o -obj-$(CONFIG_PSERIES) += spapr_pci.o +obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy) obj-y += spapr_pci_vfio.o endif diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index 2832fc0..c10e1b5 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -51,21 +51,16 @@ #define RAM_SIZES_ALIGN (64UL << 20) /* TODO: parameterize */ -#define MPC8544_CCSRBAR_BASE 0xE0000000ULL #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_BASE (MPC8544_CCSRBAR_BASE + \ - MPC8544_PCI_REGS_OFFSET) #define MPC8544_PCI_REGS_SIZE 0x1000ULL -#define MPC8544_PCI_IO 0xE1000000ULL #define MPC8544_UTIL_OFFSET 0xe0000ULL -#define MPC8544_SPIN_BASE 0xEF000000ULL #define MPC8XXX_GPIO_OFFSET 0x000FF000ULL -#define MPC8XXX_GPIO_IRQ 43 +#define MPC8XXX_GPIO_IRQ 47 struct boot_info { @@ -293,12 +288,12 @@ static int ppce500_load_device_tree(MachineState *machine, int len; uint32_t pci_ranges[14] = { - 0x2000000, 0x0, 0xc0000000, - 0x0, 0xc0000000, + 0x2000000, 0x0, params->pci_mmio_bus_base, + params->pci_mmio_base >> 32, params->pci_mmio_base, 0x0, 0x20000000, 0x1000000, 0x0, 0x0, - 0x0, 0xe1000000, + params->pci_pio_base >> 32, params->pci_pio_base, 0x0, 0x10000, }; QemuOpts *machine_opts = qemu_get_machine_opts(); @@ -313,6 +308,7 @@ static int ppce500_load_device_tree(MachineState *machine, } fdt = load_device_tree(filename, &fdt_size); + g_free(filename); if (!fdt) { goto out; } @@ -389,7 +385,7 @@ static int ppce500_load_device_tree(MachineState *machine, CPUState *cpu; PowerPCCPU *pcpu; char cpu_name[128]; - uint64_t cpu_release_addr = MPC8544_SPIN_BASE + (i * 0x20); + uint64_t cpu_release_addr = params->spin_base + (i * 0x20); cpu = qemu_get_cpu(i); if (cpu == NULL) { @@ -426,7 +422,7 @@ static int ppce500_load_device_tree(MachineState *machine, qemu_fdt_add_subnode(fdt, "/aliases"); /* XXX These should go into their respective devices' code */ - snprintf(soc, sizeof(soc), "/soc@%llx", MPC8544_CCSRBAR_BASE); + 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, @@ -434,7 +430,7 @@ static int ppce500_load_device_tree(MachineState *machine, 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, - MPC8544_CCSRBAR_BASE >> 32, MPC8544_CCSRBAR_BASE, + 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); @@ -493,7 +489,8 @@ static int ppce500_load_device_tree(MachineState *machine, 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", MPC8544_PCI_REGS_BASE); + 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"); @@ -512,8 +509,10 @@ static int ppce500_load_device_tree(MachineState *machine, } 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", MPC8544_PCI_REGS_BASE >> 32, - MPC8544_PCI_REGS_BASE, 0, 0x1000); + 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); @@ -708,17 +707,19 @@ static DeviceState *ppce500_init_mpic_qemu(PPCE500Params *params, } static DeviceState *ppce500_init_mpic_kvm(PPCE500Params *params, - qemu_irq **irqs) + qemu_irq **irqs, Error **errp) { + Error *err = NULL; DeviceState *dev; CPUState *cs; - int r; dev = qdev_create(NULL, TYPE_KVM_OPENPIC); qdev_prop_set_uint32(dev, "model", params->mpic_version); - r = qdev_init(dev); - if (r) { + object_property_set_bool(OBJECT(dev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + object_unparent(OBJECT(dev)); return NULL; } @@ -733,8 +734,8 @@ static DeviceState *ppce500_init_mpic_kvm(PPCE500Params *params, return dev; } -static qemu_irq *ppce500_init_mpic(PPCE500Params *params, MemoryRegion *ccsr, - qemu_irq **irqs) +static qemu_irq *ppce500_init_mpic(MachineState *machine, PPCE500Params *params, + MemoryRegion *ccsr, qemu_irq **irqs) { qemu_irq *mpic; DeviceState *dev = NULL; @@ -744,20 +745,15 @@ static qemu_irq *ppce500_init_mpic(PPCE500Params *params, MemoryRegion *ccsr, mpic = g_new0(qemu_irq, 256); if (kvm_enabled()) { - QemuOpts *machine_opts = qemu_get_machine_opts(); - bool irqchip_allowed = qemu_opt_get_bool(machine_opts, - "kernel_irqchip", true); - bool irqchip_required = qemu_opt_get_bool(machine_opts, - "kernel_irqchip", false); - - if (irqchip_allowed) { - dev = ppce500_init_mpic_kvm(params, irqs); - } + Error *err = NULL; - if (irqchip_required && !dev) { - fprintf(stderr, "%s: irqchip requested but unavailable\n", - __func__); - abort(); + if (machine_kernel_irqchip_allowed(machine)) { + dev = ppce500_init_mpic_kvm(params, irqs, &err); + } + if (machine_kernel_irqchip_required(machine) && !dev) { + error_report("kernel_irqchip requested but unavailable: %s", + error_get_pretty(err)); + exit(1); } } @@ -841,7 +837,7 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) 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 = MPC8544_CCSRBAR_BASE + + env->mpic_iack = params->ccsrbar_base + MPC8544_MPIC_REGS_OFFSET + 0xa0; ppc_booke_timers_init(cpu, 400000000, PPC_TIMER_E500); @@ -875,10 +871,10 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) qdev_init_nofail(dev); ccsr = CCSR(dev); ccsr_addr_space = &ccsr->ccsr_space; - memory_region_add_subregion(address_space_mem, MPC8544_CCSRBAR_BASE, + memory_region_add_subregion(address_space_mem, params->ccsrbar_base, ccsr_addr_space); - mpic = ppce500_init_mpic(params, ccsr_addr_space, irqs); + mpic = ppce500_init_mpic(machine, params, ccsr_addr_space, irqs); /* Serial */ if (serial_hds[0]) { @@ -917,8 +913,6 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) if (!pci_bus) printf("couldn't create PCI controller!\n"); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 1, MPC8544_PCI_IO); - if (pci_bus) { /* Register network interfaces. */ for (i = 0; i < nb_nics; i++) { @@ -927,7 +921,7 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) } /* Register spinning region */ - sysbus_create_simple("e500-spin", MPC8544_SPIN_BASE, NULL); + 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 */ diff --git a/hw/ppc/e500.h b/hw/ppc/e500.h index 9f61ab2..ef224ea 100644 --- a/hw/ppc/e500.h +++ b/hw/ppc/e500.h @@ -17,6 +17,11 @@ typedef struct PPCE500Params { 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); diff --git a/hw/ppc/e500plat.c b/hw/ppc/e500plat.c index d50ae00..14b14ea 100644 --- a/hw/ppc/e500plat.c +++ b/hw/ppc/e500plat.c @@ -41,6 +41,11 @@ static void e500plat_init(MachineState *machine) .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 diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 89aee71..a365bf9 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -116,10 +116,10 @@ static const MemoryRegionOps unin_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int fw_cfg_boot_set(void *opaque, const char *boot_device) +static void fw_cfg_boot_set(void *opaque, const char *boot_device, + Error **errp) { fw_cfg_add_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]); - return 0; } static uint64_t translate_kernel_address(void *opaque, uint64_t addr) @@ -371,6 +371,7 @@ static void ppc_core99_init(MachineState *machine) /* 970 gets a U3 bus */ pci_bus = pci_pmac_u3_init(pic, get_system_memory(), get_system_io()); machine_arch = ARCH_MAC99_U3; + machine->usb |= defaults_enabled() && !machine->usb_disabled; } else { pci_bus = pci_pmac_init(pic, get_system_memory(), get_system_io()); machine_arch = ARCH_MAC99; @@ -417,13 +418,16 @@ static void ppc_core99_init(MachineState *machine) dev = qdev_create(adb_bus, TYPE_ADB_MOUSE); qdev_init_nofail(dev); - if (usb_enabled(machine_arch == ARCH_MAC99_U3)) { + 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) { - usbdevice_create("keyboard"); - usbdevice_create("mouse"); + USBBus *usb_bus = usb_bus_find(-1); + + usb_create_simple(usb_bus, "usb-kbd"); + usb_create_simple(usb_bus, "usb-mouse"); } } @@ -454,9 +458,8 @@ static void ppc_core99_init(MachineState *machine) pmac_format_nvram_partition(nvr, 0x2000); /* No PCI init: the BIOS will do it */ - fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2); + 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_i32(fw_cfg, FW_CFG_ID, 1); 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); @@ -501,18 +504,27 @@ static int core99_kvm_type(const char *arg) return 2; } -static QEMUMachine core99_machine = { - .name = "mac99", - .desc = "Mac99 based PowerMAC", - .init = ppc_core99_init, - .max_cpus = MAX_CPUS, - .default_boot_order = "cd", - .kvm_type = core99_kvm_type, +static void core99_machine_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->name = "mac99"; + mc->desc = "Mac99 based PowerMAC"; + mc->init = ppc_core99_init; + mc->max_cpus = MAX_CPUS; + mc->default_boot_order = "cd"; + mc->kvm_type = core99_kvm_type; +} + +static const TypeInfo core99_machine_info = { + .name = "mac99-machine", + .parent = TYPE_MACHINE, + .class_init = core99_machine_class_init, }; -static void core99_machine_init(void) +static void mac_machine_register_types(void) { - qemu_register_machine(&core99_machine); + type_register_static(&core99_machine_info); } -machine_init(core99_machine_init); +type_init(mac_machine_register_types) diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index 32c21a4..f26133d 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -49,13 +49,12 @@ #define CLOCKFREQ 266000000UL #define BUSFREQ 66000000UL -static int fw_cfg_boot_set(void *opaque, const char *boot_device) +static void fw_cfg_boot_set(void *opaque, const char *boot_device, + Error **errp) { fw_cfg_add_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]); - return 0; } - static uint64_t translate_kernel_address(void *opaque, uint64_t addr) { return (addr & 0x0fffffff) + KERNEL_LOAD_ADDR; @@ -305,7 +304,7 @@ static void ppc_heathrow_init(MachineState *machine) dev = qdev_create(adb_bus, TYPE_ADB_MOUSE); qdev_init_nofail(dev); - if (usb_enabled(false)) { + if (usb_enabled()) { pci_create_simple(pci_bus, -1, "pci-ohci"); } @@ -314,9 +313,8 @@ static void ppc_heathrow_init(MachineState *machine) /* No PCI init: the BIOS will do it */ - fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2); + 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_i32(fw_cfg, FW_CFG_ID, 1); 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); diff --git a/hw/ppc/mpc8544ds.c b/hw/ppc/mpc8544ds.c index b99f74a..3a3b141 100644 --- a/hw/ppc/mpc8544ds.c +++ b/hw/ppc/mpc8544ds.c @@ -15,6 +15,7 @@ #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) { @@ -33,8 +34,18 @@ static void mpc8544ds_init(MachineState *machine) .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); } diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index bec82cd..99db56c 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -844,7 +844,7 @@ static void timebase_pre_save(void *opaque) return; } - tb->time_of_the_day_ns = get_clock_realtime(); + 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 @@ -873,7 +873,7 @@ static int timebase_post_load(void *opaque, int version_id) * We try to adjust timebase by downtime if host clocks are not * too much out of sync (1 second for now). */ - host_ns = get_clock_realtime(); + 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(NSEC_PER_SEC, ns_diff); migration_duration_tb = muldiv64(migration_duration_ns, freq, NSEC_PER_SEC); @@ -1318,167 +1318,6 @@ void PPC_debug_write (void *opaque, uint32_t addr, uint32_t val) } } -/*****************************************************************************/ -/* NVRAM helpers */ -static inline uint32_t nvram_read (nvram_t *nvram, uint32_t addr) -{ - return (*nvram->read_fn)(nvram->opaque, addr); -} - -static inline void nvram_write (nvram_t *nvram, uint32_t addr, uint32_t val) -{ - (*nvram->write_fn)(nvram->opaque, addr, val); -} - -static void NVRAM_set_byte(nvram_t *nvram, uint32_t addr, uint8_t value) -{ - nvram_write(nvram, addr, value); -} - -static uint8_t NVRAM_get_byte(nvram_t *nvram, uint32_t addr) -{ - return nvram_read(nvram, addr); -} - -static void NVRAM_set_word(nvram_t *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_t *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_t *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); -} - -uint32_t NVRAM_get_lword (nvram_t *nvram, uint32_t addr) -{ - uint32_t tmp; - - tmp = nvram_read(nvram, addr) << 24; - tmp |= nvram_read(nvram, addr + 1) << 16; - tmp |= nvram_read(nvram, addr + 2) << 8; - tmp |= nvram_read(nvram, addr + 3); - - return tmp; -} - -static void NVRAM_set_string(nvram_t *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'); -} - -int NVRAM_get_string (nvram_t *nvram, uint8_t *dst, uint16_t addr, int max) -{ - int i; - - memset(dst, 0, max); - for (i = 0; i < max; i++) { - dst[i] = NVRAM_get_byte(nvram, addr + i); - if (dst[i] == '\0') - break; - } - - return i; -} - -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_t *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 - -int PPC_NVRAM_set_params (nvram_t *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; -} - /* CPU device-tree ID helpers */ int ppc_get_vcpu_dt_id(PowerPCCPU *cpu) { diff --git a/hw/ppc/ppc405_boards.c b/hw/ppc/ppc405_boards.c index 1dcea77..ec6c4cb 100644 --- a/hw/ppc/ppc405_boards.c +++ b/hw/ppc/ppc405_boards.c @@ -283,7 +283,7 @@ static void ref405ep_init(MachineState *machine) #ifdef DEBUG_BOARD_INIT printf("%s: register NVRAM\n", __func__); #endif - m48t59_init(NULL, 0xF0000000, 0, 8192, 8); + m48t59_init(NULL, 0xF0000000, 0, 8192, 1968, 8); /* Load kernel */ linux_boot = (kernel_filename != NULL); if (linux_boot) { diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index dd8433d..7f52662 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -181,7 +181,7 @@ static const MemoryRegionOps PPC_XCSR_ops = { /* Fake super-io ports for PREP platform (Intel 82378ZB) */ typedef struct sysctrl_t { qemu_irq reset_irq; - M48t59State *nvram; + Nvram *nvram; uint8_t state; uint8_t syscontrol; int contiguous_map; @@ -235,13 +235,17 @@ static void PREP_io_800_writeb (void *opaque, uint32_t addr, uint32_t val) break; case 0x0810: /* Password protect 1 register */ - if (sysctrl->nvram != NULL) - m48t59_toggle_lock(sysctrl->nvram, 1); + 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) - m48t59_toggle_lock(sysctrl->nvram, 2); + if (sysctrl->nvram != NULL) { + NvramClass *k = NVRAM_GET_CLASS(sysctrl->nvram); + (k->toggle_lock)(sysctrl->nvram, 2); + } break; case 0x0814: /* L2 invalidate register */ @@ -360,6 +364,144 @@ static const MemoryRegionPortio prep_portio_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) { @@ -372,8 +514,7 @@ static void ppc_prep_init(MachineState *machine) MemoryRegion *sysmem = get_system_memory(); PowerPCCPU *cpu = NULL; CPUPPCState *env = NULL; - nvram_t nvram; - M48t59State *m48t59; + Nvram *m48t59; #if 0 MemoryRegion *xcsr = g_new(MemoryRegion, 1); #endif @@ -539,20 +680,18 @@ static void ppc_prep_init(MachineState *machine) memory_region_add_subregion(sysmem, 0xFEFF0000, xcsr); #endif - if (usb_enabled(false)) { + if (usb_enabled()) { pci_create_simple(pci_bus, -1, "pci-ohci"); } - m48t59 = m48t59_init_isa(isa_bus, 0x0074, NVRAM_SIZE, 59); + m48t59 = m48t59_init_isa(isa_bus, 0x0074, NVRAM_SIZE, 2000, 59); if (m48t59 == NULL) return; sysctrl->nvram = m48t59; /* Initialise NVRAM */ - nvram.opaque = m48t59; - nvram.read_fn = &m48t59_read; - nvram.write_fn = &m48t59_write; - PPC_NVRAM_set_params(&nvram, NVRAM_SIZE, "PREP", ram_size, ppc_boot_device, + PPC_NVRAM_set_params(m48t59, NVRAM_SIZE, "PREP", ram_size, + ppc_boot_device, kernel_base, kernel_size, kernel_cmdline, initrd_base, initrd_size, diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 30de25d..61ddc79 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -25,6 +25,7 @@ * */ #include "sysemu/sysemu.h" +#include "sysemu/numa.h" #include "hw/hw.h" #include "hw/fw-path-provider.h" #include "elf.h" @@ -109,47 +110,42 @@ struct sPAPRMachineState { sPAPREnvironment *spapr; static XICSState *try_create_xics(const char *type, int nr_servers, - int nr_irqs) + 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); - if (qdev_init(dev) < 0) { + 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(int nr_servers, int nr_irqs) +static XICSState *xics_system_init(MachineState *machine, + int nr_servers, int nr_irqs) { XICSState *icp = NULL; if (kvm_enabled()) { - QemuOpts *machine_opts = qemu_get_machine_opts(); - bool irqchip_allowed = qemu_opt_get_bool(machine_opts, - "kernel_irqchip", true); - bool irqchip_required = qemu_opt_get_bool(machine_opts, - "kernel_irqchip", false); - if (irqchip_allowed) { - icp = try_create_xics(TYPE_KVM_XICS, nr_servers, nr_irqs); - } + Error *err = NULL; - if (irqchip_required && !icp) { - perror("Failed to create in-kernel XICS\n"); - abort(); + 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_report("kernel_irqchip requested but unavailable: %s", + error_get_pretty(err)); } } if (!icp) { - icp = try_create_xics(TYPE_XICS, nr_servers, nr_irqs); - } - - if (!icp) { - perror("Failed to create XICS\n"); - abort(); + icp = try_create_xics(TYPE_XICS, nr_servers, nr_irqs, &error_abort); } return icp; @@ -318,7 +314,6 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base, hwaddr initrd_size, hwaddr kernel_size, bool little_endian, - const char *boot_device, const char *kernel_cmdline, uint32_t epow_irq) { @@ -411,9 +406,6 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base, _FDT((fdt_property(fdt, "qemu,boot-kernel-le", NULL, 0))); } } - if (boot_device) { - _FDT((fdt_property_string(fdt, "qemu,boot-device", boot_device))); - } if (boot_menu) { _FDT((fdt_property_cell(fdt, "qemu,boot-menu", boot_menu))); } @@ -725,6 +717,8 @@ static void spapr_finalize_fdt(sPAPREnvironment *spapr, hwaddr rtas_addr, hwaddr rtas_size) { + MachineState *machine = MACHINE(qdev_get_machine()); + const char *boot_device = machine->boot_order; int ret, i; size_t cb = 0; char *bootlist; @@ -784,6 +778,15 @@ static void spapr_finalize_fdt(sPAPREnvironment *spapr, 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); } @@ -819,9 +822,16 @@ static void emulate_spapr_hypercall(PowerPCCPU *cpu) } } +#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)) + static void spapr_reset_htab(sPAPREnvironment *spapr) { long shift; + int index; /* allocate hash page table. For now we always make this 16mb, * later we should probably make it scale to the size of guest @@ -833,6 +843,11 @@ static void spapr_reset_htab(sPAPREnvironment *spapr) /* Kernel handles htab, we don't need to allocate one */ spapr->htab_shift = shift; kvmppc_kern_htab = true; + + /* Tell readers to update their file descriptor */ + if (spapr->htab_fd >= 0) { + spapr->htab_fd_stale = true; + } } else { if (!spapr->htab) { /* Allocate an htab if we don't yet have one */ @@ -841,6 +856,10 @@ static void spapr_reset_htab(sPAPREnvironment *spapr) /* And clear it */ memset(spapr->htab, 0, HTAB_SIZE(spapr)); + + for (index = 0; index < HTAB_SIZE(spapr) / HASH_PTE_SIZE_64; index++) { + DIRTY_HPTE(HPTE(spapr->htab, index)); + } } /* Update the RMA size if necessary */ @@ -867,6 +886,28 @@ static int find_unknown_sysbus_device(SysBusDevice *sbdev, void *opaque) return 0; } +/* + * A guest reset will cause spapr->htab_fd to become stale if being used. + * Reopen the file descriptor to make sure the whole HTAB is properly read. + */ +static int spapr_check_htab_fd(sPAPREnvironment *spapr) +{ + int rc = 0; + + if (spapr->htab_fd_stale) { + close(spapr->htab_fd); + spapr->htab_fd = kvmppc_get_htab_fd(false); + if (spapr->htab_fd < 0) { + error_report("Unable to open fd for reading hash table from KVM: " + "%s", strerror(errno)); + rc = -1; + } + spapr->htab_fd_stale = false; + } + + return rc; +} + static void ppc_spapr_reset(void) { PowerPCCPU *first_ppc_cpu; @@ -955,6 +996,17 @@ static void spapr_create_nvram(sPAPREnvironment *spapr) spapr->nvram = (struct sPAPRNVRAM *)dev; } +static void spapr_rtc_create(sPAPREnvironment *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 int spapr_vga_init(PCIBus *pci_bus) { @@ -972,25 +1024,44 @@ static int spapr_vga_init(PCIBus *pci_bus) } } +static int spapr_post_load(void *opaque, int version_id) +{ + sPAPREnvironment *spapr = (sPAPREnvironment *)opaque; + int err = 0; + + /* In earlier versions, there was no seperate 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 = 2, + .version_id = 3, .minimum_version_id = 1, + .post_load = spapr_post_load, .fields = (VMStateField[]) { - VMSTATE_UNUSED(4), /* used to be @next_irq */ + /* used to be @next_irq */ + VMSTATE_UNUSED_BUFFER(version_before_3, 0, 4), /* RTC offset */ - VMSTATE_UINT64(rtc_offset, sPAPREnvironment), + VMSTATE_UINT64_TEST(rtc_offset, sPAPREnvironment, version_before_3), + VMSTATE_PPC_TIMEBASE_V(tb, sPAPREnvironment, 2), VMSTATE_END_OF_LIST() }, }; -#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)) - static int htab_save_setup(QEMUFile *f, void *opaque) { sPAPREnvironment *spapr = opaque; @@ -1005,6 +1076,7 @@ static int htab_save_setup(QEMUFile *f, void *opaque) assert(kvm_enabled()); spapr->htab_fd = kvmppc_get_htab_fd(false); + spapr->htab_fd_stale = false; if (spapr->htab_fd < 0) { fprintf(stderr, "Unable to open fd for reading hash table from KVM: %s\n", strerror(errno)); @@ -1037,7 +1109,7 @@ static void htab_save_first_pass(QEMUFile *f, sPAPREnvironment *spapr, /* Consume valid HPTEs */ chunkstart = index; - while ((index < htabslots) + while ((index < htabslots) && (index - chunkstart < USHRT_MAX) && HPTE_VALID(HPTE(spapr->htab, index))) { index++; CLEAN_HPTE(HPTE(spapr->htab, index)); @@ -1089,7 +1161,7 @@ static int htab_save_later_pass(QEMUFile *f, sPAPREnvironment *spapr, chunkstart = index; /* Consume valid dirty HPTEs */ - while ((index < htabslots) + 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)); @@ -1099,7 +1171,7 @@ static int htab_save_later_pass(QEMUFile *f, sPAPREnvironment *spapr, invalidstart = index; /* Consume invalid dirty HPTEs */ - while ((index < htabslots) + 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)); @@ -1157,6 +1229,11 @@ static int htab_save_iterate(QEMUFile *f, void *opaque) if (!spapr->htab) { assert(kvm_enabled()); + rc = spapr_check_htab_fd(spapr); + if (rc < 0) { + return rc; + } + rc = kvmppc_save_htab(f, spapr->htab_fd, MAX_KVM_BUF_SIZE, MAX_ITERATION_NS); if (rc < 0) { @@ -1188,6 +1265,11 @@ static int htab_save_complete(QEMUFile *f, void *opaque) assert(kvm_enabled()); + rc = spapr_check_htab_fd(spapr); + if (rc < 0) { + return rc; + } + rc = kvmppc_save_htab(f, spapr->htab_fd, MAX_KVM_BUF_SIZE, -1); if (rc < 0) { return rc; @@ -1295,6 +1377,13 @@ static SaveVMHandlers savevm_htab_handlers = { .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); +} + /* pSeries LPAR / sPAPR hardware init */ static void ppc_spapr_init(MachineState *machine) { @@ -1303,7 +1392,6 @@ static void ppc_spapr_init(MachineState *machine) const char *kernel_filename = machine->kernel_filename; const char *kernel_cmdline = machine->kernel_cmdline; const char *initrd_filename = machine->initrd_filename; - const char *boot_device = machine->boot_order; PowerPCCPU *cpu; CPUPPCState *env; PCIHostState *phb; @@ -1376,7 +1464,8 @@ static void ppc_spapr_init(MachineState *machine) } /* Set up Interrupt Controller before we create the VCPUs */ - spapr->icp = xics_system_init(smp_cpus * kvmppc_smt_threads() / smp_threads, + spapr->icp = xics_system_init(machine, + smp_cpus * kvmppc_smt_threads() / smp_threads, XICS_IRQS); /* init CPUs */ @@ -1430,6 +1519,10 @@ static void ppc_spapr_init(MachineState *machine) } filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "spapr-rtas.bin"); + if (!filename) { + hw_error("Could not find LPAR rtas '%s'\n", "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) { @@ -1438,7 +1531,7 @@ static void ppc_spapr_init(MachineState *machine) } if (spapr->rtas_size > RTAS_MAX_SIZE) { hw_error("RTAS too big ! 0x%zx bytes (max is 0x%x)\n", - spapr->rtas_size, RTAS_MAX_SIZE); + (size_t)spapr->rtas_size, RTAS_MAX_SIZE); exit(1); } g_free(filename); @@ -1446,6 +1539,9 @@ static void ppc_spapr_init(MachineState *machine) /* 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(); @@ -1484,13 +1580,17 @@ static void ppc_spapr_init(MachineState *machine) /* Graphics */ if (spapr_vga_init(phb->bus)) { spapr->has_graphics = true; + machine->usb |= defaults_enabled() && !machine->usb_disabled; } - if (usb_enabled(spapr->has_graphics)) { + if (machine->usb) { pci_create_simple(phb->bus, -1, "pci-ohci"); + if (spapr->has_graphics) { - usbdevice_create("keyboard"); - usbdevice_create("mouse"); + USBBus *usb_bus = usb_bus_find(-1); + + usb_create_simple(usb_bus, "usb-kbd"); + usb_create_simple(usb_bus, "usb-mouse"); } } @@ -1540,6 +1640,10 @@ static void ppc_spapr_init(MachineState *machine) bios_name = FW_FILE_NAME; } filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + if (!filename) { + hw_error("Could not find LPAR rtas '%s'\n", bios_name); + exit(1); + } fw_size = load_image_targphys(filename, 0, FW_MAX_SIZE); if (fw_size < 0) { hw_error("qemu: could not load LPAR rtas '%s'\n", filename); @@ -1556,9 +1660,10 @@ static void ppc_spapr_init(MachineState *machine) /* Prepare the device tree */ spapr->fdt_skel = spapr_create_fdt_skel(initrd_base, initrd_size, kernel_size, kernel_le, - boot_device, kernel_cmdline, - spapr->epow_irq); + kernel_cmdline, spapr->epow_irq); assert(spapr->fdt_skel != NULL); + + qemu_register_boot_set(spapr_boot_set, spapr); } static int spapr_kvm_type(const char *vm_type) @@ -1580,7 +1685,7 @@ static int spapr_kvm_type(const char *vm_type) } /* - * Implementation of an interface to adjust firmware patch + * Implementation of an interface to adjust firmware path * for the bootindex property handling. */ static char *spapr_get_fw_dev_path(FWPathProvider *p, BusState *bus, @@ -1655,6 +1760,9 @@ static void spapr_machine_initfn(Object *obj) { 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 ppc_cpu_do_nmi_on_cpu(void *arg) @@ -1685,7 +1793,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) mc->block_default_type = IF_SCSI; mc->max_cpus = MAX_CPUS; mc->no_parallel = 1; - mc->default_boot_order = NULL; + mc->default_boot_order = ""; mc->kvm_type = spapr_kvm_type; mc->has_dynamic_sysbus = true; @@ -1707,11 +1815,22 @@ static const TypeInfo spapr_machine_info = { }, }; +#define SPAPR_COMPAT_2_2 \ + {\ + .driver = TYPE_SPAPR_PCI_HOST_BRIDGE,\ + .property = "mem_win_size",\ + .value = "0x20000000",\ + } + +#define SPAPR_COMPAT_2_1 \ + SPAPR_COMPAT_2_2 + static void spapr_machine_2_1_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); static GlobalProperty compat_props[] = { HW_COMPAT_2_1, + SPAPR_COMPAT_2_1, { /* end of list */ } }; @@ -1728,12 +1847,15 @@ static const TypeInfo spapr_machine_2_1_info = { static void spapr_machine_2_2_class_init(ObjectClass *oc, void *data) { + static GlobalProperty compat_props[] = { + SPAPR_COMPAT_2_2, + { /* end of list */ } + }; MachineClass *mc = MACHINE_CLASS(oc); mc->name = "pseries-2.2"; mc->desc = "pSeries Logical Partition (PAPR compliant) v2.2"; - mc->alias = "pseries"; - mc->is_default = 1; + mc->compat_props = compat_props; } static const TypeInfo spapr_machine_2_2_info = { @@ -1742,11 +1864,28 @@ static const TypeInfo spapr_machine_2_2_info = { .class_init = spapr_machine_2_2_class_init, }; +static void spapr_machine_2_3_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->name = "pseries-2.3"; + mc->desc = "pSeries Logical Partition (PAPR compliant) v2.3"; + mc->alias = "pseries"; + mc->is_default = 1; +} + +static const TypeInfo spapr_machine_2_3_info = { + .name = TYPE_SPAPR_MACHINE "2.3", + .parent = TYPE_SPAPR_MACHINE, + .class_init = spapr_machine_2_3_class_init, +}; + static void spapr_machine_register_types(void) { type_register_static(&spapr_machine_info); type_register_static(&spapr_machine_2_1_info); type_register_static(&spapr_machine_2_2_info); + type_register_static(&spapr_machine_2_3_info); } type_init(spapr_machine_register_types) diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c index 1b6157d..283e96b 100644 --- a/hw/ppc/spapr_events.c +++ b/hw/ppc/spapr_events.c @@ -246,7 +246,7 @@ static void spapr_powerdown_req(Notifier *n, void *opaque) 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? */ - qemu_get_timedate(&tm, spapr->rtc_offset); + 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) diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 8651447..4f76f1c 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -731,12 +731,14 @@ static target_ulong h_set_mode_resource_le(PowerPCCPU *cpu, 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; } diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c index 6c91d8e..f3990fd 100644 --- a/hw/ppc/spapr_iommu.c +++ b/hw/ppc/spapr_iommu.c @@ -25,6 +25,7 @@ #include "trace.h" #include "hw/ppc/spapr.h" +#include "hw/ppc/spapr_vio.h" #include @@ -59,6 +60,7 @@ static sPAPRTCETable *spapr_tce_find_by_liobn(uint32_t liobn) return NULL; } +/* Called from RCU critical section */ static IOMMUTLBEntry spapr_tce_translate_iommu(MemoryRegion *iommu, hwaddr addr, bool is_write) { @@ -72,9 +74,7 @@ static IOMMUTLBEntry spapr_tce_translate_iommu(MemoryRegion *iommu, hwaddr addr, .perm = IOMMU_NONE, }; - if (tcet->bypass) { - ret.perm = IOMMU_RW; - } else if ((addr >> tcet->page_shift) < tcet->nb_table) { + if ((addr >> tcet->page_shift) < tcet->nb_table) { /* Check if we are in bound */ hwaddr page_mask = IOMMU_PAGE_MASK(tcet->page_shift); @@ -90,10 +90,22 @@ static IOMMUTLBEntry spapr_tce_translate_iommu(MemoryRegion *iommu, hwaddr addr, 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), @@ -131,7 +143,8 @@ static int spapr_tce_table_realize(DeviceState *dev) 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", ram_size); + "iommu-spapr", + (uint64_t)tcet->nb_table << tcet->page_shift); QLIST_INSERT_HEAD(&spapr_tce_tables, tcet, list); @@ -173,9 +186,9 @@ sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn, return tcet; } -static void spapr_tce_table_finalize(Object *obj) +static void spapr_tce_table_unrealize(DeviceState *dev, Error **errp) { - sPAPRTCETable *tcet = SPAPR_TCE_TABLE(obj); + sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev); QLIST_REMOVE(tcet, list); @@ -191,17 +204,11 @@ MemoryRegion *spapr_tce_get_iommu(sPAPRTCETable *tcet) return &tcet->iommu; } -void spapr_tce_set_bypass(sPAPRTCETable *tcet, bool bypass) -{ - tcet->bypass = bypass; -} - static void spapr_tce_reset(DeviceState *dev) { sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev); size_t table_size = tcet->nb_table * sizeof(uint64_t); - tcet->bypass = false; memset(tcet->table, 0, table_size); } @@ -420,6 +427,7 @@ 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); @@ -435,7 +443,6 @@ static TypeInfo spapr_tce_table_info = { .parent = TYPE_DEVICE, .instance_size = sizeof(sPAPRTCETable), .class_init = spapr_tce_table_class_init, - .instance_finalize = spapr_tce_table_finalize, }; static void register_types(void) diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 21b95b3..05f4fac 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -406,6 +406,258 @@ static void rtas_ibm_query_interrupt_source_number(PowerPCCPU *cpu, rtas_st(rets, 2, 1);/* 0 == level; 1 == edge */ } +static void rtas_ibm_set_eeh_option(PowerPCCPU *cpu, + sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, uint32_t nret, + target_ulong rets) +{ + sPAPRPHBState *sphb; + sPAPRPHBClass *spc; + uint32_t addr, option; + uint64_t buid; + int ret; + + if ((nargs != 4) || (nret != 1)) { + goto param_error_exit; + } + + buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + addr = rtas_ld(args, 0); + option = rtas_ld(args, 3); + + sphb = find_phb(spapr, buid); + if (!sphb) { + goto param_error_exit; + } + + spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb); + if (!spc->eeh_set_option) { + goto param_error_exit; + } + + ret = spc->eeh_set_option(sphb, addr, option); + 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, + sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, uint32_t nret, + target_ulong rets) +{ + sPAPRPHBState *sphb; + sPAPRPHBClass *spc; + PCIDevice *pdev; + uint32_t addr, option; + uint64_t buid; + + if ((nargs != 4) || (nret != 2)) { + goto param_error_exit; + } + + buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + sphb = find_phb(spapr, buid); + if (!sphb) { + goto param_error_exit; + } + + spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb); + if (!spc->eeh_set_option) { + 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 = 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, + sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, uint32_t nret, + target_ulong rets) +{ + sPAPRPHBState *sphb; + sPAPRPHBClass *spc; + uint64_t buid; + int state, ret; + + if ((nargs != 3) || (nret != 4 && nret != 5)) { + goto param_error_exit; + } + + buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + sphb = find_phb(spapr, buid); + if (!sphb) { + goto param_error_exit; + } + + spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb); + if (!spc->eeh_get_state) { + goto param_error_exit; + } + + ret = spc->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, + sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, uint32_t nret, + target_ulong rets) +{ + sPAPRPHBState *sphb; + sPAPRPHBClass *spc; + uint32_t option; + uint64_t buid; + int ret; + + if ((nargs != 4) || (nret != 1)) { + goto param_error_exit; + } + + buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + option = rtas_ld(args, 3); + sphb = find_phb(spapr, buid); + if (!sphb) { + goto param_error_exit; + } + + spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb); + if (!spc->eeh_reset) { + goto param_error_exit; + } + + ret = spc->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, + sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, uint32_t nret, + target_ulong rets) +{ + sPAPRPHBState *sphb; + sPAPRPHBClass *spc; + uint64_t buid; + int ret; + + if ((nargs != 3) || (nret != 1)) { + goto param_error_exit; + } + + buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + sphb = find_phb(spapr, buid); + if (!sphb) { + goto param_error_exit; + } + + spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb); + if (!spc->eeh_configure) { + goto param_error_exit; + } + + ret = spc->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, + sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, uint32_t nret, + target_ulong rets) +{ + sPAPRPHBState *sphb; + sPAPRPHBClass *spc; + int option; + uint64_t buid; + + if ((nargs != 8) || (nret != 1)) { + goto param_error_exit; + } + + buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + sphb = find_phb(spapr, buid); + if (!sphb) { + goto param_error_exit; + } + + spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb); + if (!spc->eeh_set_option) { + 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; @@ -501,6 +753,12 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) 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_BASE_LIOBN + sphb->index; @@ -669,7 +927,7 @@ static void spapr_phb_reset(DeviceState *qdev) } static Property spapr_phb_properties[] = { - DEFINE_PROP_INT32("index", sPAPRPHBState, index, -1), + 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), @@ -862,6 +1120,10 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, int bus_off, i, j; char nodename[256]; 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; @@ -876,9 +1138,15 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, { 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(memory_region_size(&phb->memwindow)), + 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)}; @@ -907,7 +1175,7 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, _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, "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)); @@ -958,6 +1226,25 @@ void spapr_pci_rtas_init(void) 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) @@ -966,3 +1253,31 @@ static void spapr_pci_register_types(void) } 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) +{ + 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/hw/ppc/spapr_pci_vfio.c b/hw/ppc/spapr_pci_vfio.c index d3bddf2..99a1be5 100644 --- a/hw/ppc/spapr_pci_vfio.c +++ b/hw/ppc/spapr_pci_vfio.c @@ -20,7 +20,7 @@ #include "hw/ppc/spapr.h" #include "hw/pci-host/spapr.h" #include "linux/vfio.h" -#include "hw/misc/vfio.h" +#include "hw/vfio/vfio.h" static Property spapr_phb_vfio_properties[] = { DEFINE_PROP_INT32("iommu", sPAPRPHBVFIOState, iommugroupid, -1), @@ -76,6 +76,117 @@ static void spapr_phb_vfio_reset(DeviceState *qdev) /* Do nothing */ } +static int spapr_phb_vfio_eeh_set_option(sPAPRPHBState *sphb, + unsigned int addr, int option) +{ + sPAPRPHBVFIOState *svphb = SPAPR_PCI_VFIO_HOST_BRIDGE(sphb); + struct vfio_eeh_pe_op op = { .argsz = sizeof(op) }; + int ret; + + switch (option) { + case RTAS_EEH_DISABLE: + op.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) { + return RTAS_OUT_PARAM_ERROR; + } + + op.op = VFIO_EEH_PE_ENABLE; + break; + } + case RTAS_EEH_THAW_IO: + op.op = VFIO_EEH_PE_UNFREEZE_IO; + break; + case RTAS_EEH_THAW_DMA: + op.op = VFIO_EEH_PE_UNFREEZE_DMA; + break; + default: + return RTAS_OUT_PARAM_ERROR; + } + + ret = vfio_container_ioctl(&svphb->phb.iommu_as, svphb->iommugroupid, + VFIO_EEH_PE_OP, &op); + if (ret < 0) { + return RTAS_OUT_HW_ERROR; + } + + return RTAS_OUT_SUCCESS; +} + +static int spapr_phb_vfio_eeh_get_state(sPAPRPHBState *sphb, int *state) +{ + sPAPRPHBVFIOState *svphb = SPAPR_PCI_VFIO_HOST_BRIDGE(sphb); + struct vfio_eeh_pe_op op = { .argsz = sizeof(op) }; + int ret; + + op.op = VFIO_EEH_PE_GET_STATE; + ret = vfio_container_ioctl(&svphb->phb.iommu_as, svphb->iommugroupid, + VFIO_EEH_PE_OP, &op); + if (ret < 0) { + return RTAS_OUT_PARAM_ERROR; + } + + *state = ret; + return RTAS_OUT_SUCCESS; +} + +static int spapr_phb_vfio_eeh_reset(sPAPRPHBState *sphb, int option) +{ + sPAPRPHBVFIOState *svphb = SPAPR_PCI_VFIO_HOST_BRIDGE(sphb); + struct vfio_eeh_pe_op op = { .argsz = sizeof(op) }; + int ret; + + switch (option) { + case RTAS_SLOT_RESET_DEACTIVATE: + op.op = VFIO_EEH_PE_RESET_DEACTIVATE; + break; + case RTAS_SLOT_RESET_HOT: + op.op = VFIO_EEH_PE_RESET_HOT; + break; + case RTAS_SLOT_RESET_FUNDAMENTAL: + op.op = VFIO_EEH_PE_RESET_FUNDAMENTAL; + break; + default: + return RTAS_OUT_PARAM_ERROR; + } + + ret = vfio_container_ioctl(&svphb->phb.iommu_as, svphb->iommugroupid, + VFIO_EEH_PE_OP, &op); + if (ret < 0) { + return RTAS_OUT_HW_ERROR; + } + + return RTAS_OUT_SUCCESS; +} + +static int spapr_phb_vfio_eeh_configure(sPAPRPHBState *sphb) +{ + sPAPRPHBVFIOState *svphb = SPAPR_PCI_VFIO_HOST_BRIDGE(sphb); + struct vfio_eeh_pe_op op = { .argsz = sizeof(op) }; + int ret; + + op.op = VFIO_EEH_PE_CONFIGURE; + ret = vfio_container_ioctl(&svphb->phb.iommu_as, svphb->iommugroupid, + VFIO_EEH_PE_OP, &op); + 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); @@ -84,6 +195,10 @@ static void spapr_phb_vfio_class_init(ObjectClass *klass, void *data) dc->props = spapr_phb_vfio_properties; dc->reset = spapr_phb_vfio_reset; spc->finish_realize = spapr_phb_vfio_finish_realize; + spc->eeh_set_option = spapr_phb_vfio_eeh_set_option; + spc->eeh_get_state = spapr_phb_vfio_eeh_get_state; + spc->eeh_reset = spapr_phb_vfio_eeh_reset; + spc->eeh_configure = spapr_phb_vfio_eeh_configure; } static const TypeInfo spapr_phb_vfio_info = { diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index 2ec2a8e..0f1ae55 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -52,51 +52,6 @@ static void rtas_display_character(PowerPCCPU *cpu, sPAPREnvironment *spapr, } } -static void rtas_get_time_of_day(PowerPCCPU *cpu, sPAPREnvironment *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, - uint32_t nret, target_ulong rets) -{ - struct tm tm; - - if (nret != 8) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; - } - - qemu_get_timedate(&tm, spapr->rtc_offset); - - 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, 0); /* we don't do nanoseconds */ -} - -static void rtas_set_time_of_day(PowerPCCPU *cpu, sPAPREnvironment *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, - uint32_t nret, target_ulong rets) -{ - struct tm tm; - - 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); - - /* Just generate a monitor event for the change */ - qapi_event_send_rtc_change(qemu_timedate_diff(&tm), &error_abort); - spapr->rtc_offset = qemu_timedate_diff(&tm); - - rtas_st(rets, 0, RTAS_OUT_SUCCESS); -} - static void rtas_power_off(PowerPCCPU *cpu, sPAPREnvironment *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) @@ -400,10 +355,6 @@ static void core_rtas_register_types(void) { spapr_rtas_register(RTAS_DISPLAY_CHARACTER, "display-character", rtas_display_character); - 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); spapr_rtas_register(RTAS_POWER_OFF, "power-off", rtas_power_off); spapr_rtas_register(RTAS_SYSTEM_REBOOT, "system-reboot", rtas_system_reboot); diff --git a/hw/ppc/spapr_rtc.c b/hw/ppc/spapr_rtc.c new file mode 100644 index 0000000..83eb7c1 --- /dev/null +++ b/hw/ppc/spapr_rtc.c @@ -0,0 +1,212 @@ +/* + * 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 "cpu.h" +#include "sysemu/sysemu.h" +#include "hw/ppc/spapr.h" +#include "qapi-event.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; +}; + +#define NSEC_PER_SEC 1000000000LL + +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 / NSEC_PER_SEC; + + 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 * NSEC_PER_SEC; + + return 0; +} + +static void rtas_get_time_of_day(PowerPCCPU *cpu, sPAPREnvironment *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, sPAPREnvironment *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 * NSEC_PER_SEC) - 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 * NSEC_PER_SEC - 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_size = sizeof(XICSStateClass), + .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/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c index dc9e46a..1360b97 100644 --- a/hw/ppc/spapr_vio.c +++ b/hw/ppc/spapr_vio.c @@ -322,6 +322,18 @@ static void spapr_vio_quiesce_one(VIOsPAPRDevice *dev) 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, sPAPREnvironment *spapr, uint32_t token, uint32_t nargs, target_ulong args, @@ -348,7 +360,7 @@ static void rtas_set_tce_bypass(PowerPCCPU *cpu, sPAPREnvironment *spapr, return; } - spapr_tce_set_bypass(dev->tcet, !!enable); + spapr_vio_set_bypass(dev, !!enable); rtas_st(rets, 0, RTAS_OUT_SUCCESS); } @@ -407,12 +419,13 @@ static void spapr_vio_busdev_reset(DeviceState *qdev) dev->signal_state = 0; + spapr_vio_set_bypass(dev, false); if (pc->reset) { pc->reset(dev); } } -static int spapr_vio_busdev_init(DeviceState *qdev) +static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp) { VIOsPAPRDevice *dev = (VIOsPAPRDevice *)qdev; VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev); @@ -428,11 +441,11 @@ static int spapr_vio_busdev_init(DeviceState *qdev) VIOsPAPRDevice *other = reg_conflict(dev); if (other) { - fprintf(stderr, "vio: %s and %s devices conflict at address %#x\n", - object_get_typename(OBJECT(qdev)), - object_get_typename(OBJECT(&other->qdev)), - dev->reg); - return -1; + 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 */ @@ -451,20 +464,32 @@ static int spapr_vio_busdev_init(DeviceState *qdev) dev->irq = xics_alloc(spapr->icp, 0, dev->irq, false); if (!dev->irq) { - return -1; + error_setg(errp, "can't allocate IRQ"); + return; } if (pc->rtce_window_size) { uint32_t liobn = SPAPR_VIO_BASE_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); - address_space_init(&dev->as, spapr_tce_get_iommu(dev->tcet), qdev->id); + dev->tcet->vdev = dev; + memory_region_add_subregion_overlap(&dev->mrroot, 0, + spapr_tce_get_iommu(dev->tcet), 2); } - return pc->init(dev); + pc->realize(dev, errp); } static target_ulong h_vio_signal(PowerPCCPU *cpu, sPAPREnvironment *spapr, @@ -570,7 +595,7 @@ const VMStateDescription vmstate_spapr_vio = { static void vio_spapr_device_class_init(ObjectClass *klass, void *data) { DeviceClass *k = DEVICE_CLASS(klass); - k->init = spapr_vio_busdev_init; + k->realize = spapr_vio_busdev_realize; k->reset = spapr_vio_busdev_reset; k->bus_type = TYPE_SPAPR_VIO_BUS; k->props = spapr_vio_props; @@ -648,7 +673,7 @@ int spapr_populate_vdevice(VIOsPAPRBus *bus, void *fdt) ret = 0; out: - free(qdevs); + g_free(qdevs); return ret; } diff --git a/hw/s390x/Makefile.objs b/hw/s390x/Makefile.objs index 1ba6c3a..27cd75a 100644 --- a/hw/s390x/Makefile.objs +++ b/hw/s390x/Makefile.objs @@ -8,3 +8,4 @@ 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 diff --git a/hw/s390x/css.c b/hw/s390x/css.c index b67c039..9a13b00 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -584,7 +584,7 @@ static void copy_schib_from_guest(SCHIB *dest, const SCHIB *src) } } -int css_do_msch(SubchDev *sch, SCHIB *orig_schib) +int css_do_msch(SubchDev *sch, const SCHIB *orig_schib) { SCSW *s = &sch->curr_status.scsw; PMCW *p = &sch->curr_status.pmcw; @@ -801,7 +801,8 @@ out: return ret; } -static void copy_irb_to_guest(IRB *dest, const IRB *src, PMCW *pmcw) +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; @@ -815,6 +816,8 @@ static void copy_irb_to_guest(IRB *dest, const IRB *src, PMCW *pmcw) 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) || @@ -832,26 +835,21 @@ static void copy_irb_to_guest(IRB *dest, const IRB *src, PMCW *pmcw) dest->emw[i] = cpu_to_be32(src->emw[i]); } } + *irb_len = sizeof(*dest); } -int css_do_tsch(SubchDev *sch, IRB *target_irb) +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; - uint16_t fctl; - uint16_t actl; IRB irb; - int ret; if (!(p->flags & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA))) { - ret = 3; - goto out; + return 3; } stctl = s->ctrl & SCSW_CTRL_MASK_STCTL; - fctl = s->ctrl & SCSW_CTRL_MASK_FCTL; - actl = s->ctrl & SCSW_CTRL_MASK_ACTL; /* Prepare the irb for the guest. */ memset(&irb, 0, sizeof(IRB)); @@ -876,7 +874,22 @@ int css_do_tsch(SubchDev *sch, IRB *target_irb) } } /* Store the irb to the guest. */ - copy_irb_to_guest(target_irb, &irb, p); + 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) { @@ -913,11 +926,6 @@ int css_do_tsch(SubchDev *sch, IRB *target_irb) memset(sch->sense_data, 0 , sizeof(sch->sense_data)); } } - - ret = ((stctl & SCSW_STCTL_STATUS_PEND) == 0); - -out: - return ret; } static void copy_crw_to_guest(CRW *dest, const CRW *src) @@ -947,6 +955,26 @@ int css_do_stcrw(CRW *crw) 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. */ @@ -1299,6 +1327,11 @@ void css_generate_chp_crws(uint8_t cssid, uint8_t chpid) /* TODO */ } +void css_generate_css_crws(uint8_t cssid) +{ + css_queue_crw(CRW_RSC_CSS, 0, 0, cssid); +} + int css_enable_mcsse(void) { trace_css_enable_facility("mcsse"); diff --git a/hw/s390x/css.h b/hw/s390x/css.h index 33104ac..7e53148 100644 --- a/hw/s390x/css.h +++ b/hw/s390x/css.h @@ -101,6 +101,7 @@ 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_adapter_interrupt(uint8_t isc); #define CSS_IO_ADAPTER_VIRTIO 1 diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index 3b77c9a..754fb19 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -18,6 +18,7 @@ #include "hw/sysbus.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 @@ -50,25 +51,76 @@ typedef struct S390IPLState { /*< private >*/ SysBusDevice parent_obj; uint64_t start_addr; + uint64_t bios_start_addr; + bool enforce_bios; + IplParameterBlock iplb; + bool iplb_valid; + bool reipl_requested; /*< public >*/ char *kernel; char *initrd; char *cmdline; char *firmware; + uint8_t cssid; + uint8_t ssid; + uint16_t devno; } S390IPLState; +static const VMStateDescription vmstate_iplb = { + .name = "ipl/iplb", + .version_id = 0, + .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 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 int s390_ipl_init(SysBusDevice *dev) { S390IPLState *ipl = S390_IPL(dev); + uint64_t pentry = KERN_IMAGE_START; int kernel_size; - if (!ipl->kernel) { - int bios_size; - char *bios_filename; + 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; - /* Load zipl bootloader */ if (bios_name == NULL) { bios_name = ipl->firmware; } @@ -78,24 +130,29 @@ static int s390_ipl_init(SysBusDevice *dev) hw_error("could not find stage1 bootloader\n"); } - bios_size = load_elf(bios_filename, NULL, NULL, &ipl->start_addr, NULL, - NULL, 1, ELF_MACHINE, 0); - if (bios_size < 0) { + bios_size = load_elf(bios_filename, bios_translate_addr, &fwbase, + &ipl->bios_start_addr, NULL, NULL, 1, + ELF_MACHINE, 0); + if (bios_size > 0) { + /* Adjust ELF start address to final location */ + ipl->bios_start_addr += fwbase; + } else { + /* Try to load non-ELF file (e.g. s390-zipl.rom) */ bios_size = load_image_targphys(bios_filename, ZIPL_IMAGE_START, 4096); - ipl->start_addr = ZIPL_IMAGE_START; - if (bios_size > 4096) { - hw_error("stage1 bootloader is > 4k\n"); - } + ipl->bios_start_addr = ZIPL_IMAGE_START; } g_free(bios_filename); if (bios_size == -1) { hw_error("could not load bootloader '%s'\n", bios_name); } - return 0; - } else { - uint64_t pentry = KERN_IMAGE_START; + + /* 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, ELF_MACHINE, 0); if (kernel_size < 0) { @@ -118,27 +175,31 @@ static int s390_ipl_init(SysBusDevice *dev) } 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) { - fprintf(stderr, "qemu: could not load initrd '%s'\n", ipl->initrd); - exit(1); - } + if (ipl->initrd) { + ram_addr_t initrd_offset; + int initrd_size; - /* 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); - } + 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) { + fprintf(stderr, "qemu: could not load initrd '%s'\n", + ipl->initrd); + exit(1); + } + /* + * 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); + } + } return 0; } @@ -147,9 +208,82 @@ static Property s390_ipl_properties[] = { 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(CPUS390XState *env, 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); +} + +int s390_ipl_update_diag308(IplParameterBlock *iplb) +{ + S390IPLState *ipl; + + ipl = S390_IPL(object_resolve_path(TYPE_S390_IPL, NULL)); + if (ipl) { + ipl->iplb = *iplb; + ipl->iplb_valid = true; + return 0; + } + return -1; +} + +IplParameterBlock *s390_ipl_get_iplb(void) +{ + S390IPLState *ipl; + + ipl = S390_IPL(object_resolve_path(TYPE_S390_IPL, NULL)); + if (!ipl || !ipl->iplb_valid) { + return NULL; + } + return &ipl->iplb; +} + +void s390_reipl_request(void) +{ + S390IPLState *ipl; + + ipl = S390_IPL(object_resolve_path(TYPE_S390_IPL, NULL)); + ipl->reipl_requested = true; + qemu_system_reset_request(); +} + static void s390_ipl_reset(DeviceState *dev) { S390IPLState *ipl = S390_IPL(dev); @@ -159,21 +293,14 @@ static void s390_ipl_reset(DeviceState *dev) env->psw.addr = ipl->start_addr; env->psw.mask = IPL_PSW_MASK; - if (!ipl->kernel) { - /* Tell firmware, if there is a preferred boot device */ - env->regs[7] = -1; - DeviceState *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 (!ipl->reipl_requested) { + ipl->iplb_valid = false; + } + ipl->reipl_requested = false; - if (ccw_dev) { - env->regs[7] = ccw_dev->sch->cssid << 24 | - ccw_dev->sch->ssid << 16 | - ccw_dev->sch->devno; - } - } + if (!ipl->kernel || ipl->iplb_valid) { + env->psw.addr = ipl->bios_start_addr; + env->regs[7] = s390_update_iplstate(env, ipl); } s390_cpu_set_state(CPU_STATE_OPERATING, cpu); @@ -187,6 +314,7 @@ static void s390_ipl_class_init(ObjectClass *klass, void *data) k->init = s390_ipl_init; dc->props = s390_ipl_properties; dc->reset = s390_ipl_reset; + dc->vmsd = &vmstate_ipl; } static const TypeInfo s390_ipl_info = { diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h new file mode 100644 index 0000000..70497bc --- /dev/null +++ b/hw/s390x/ipl.h @@ -0,0 +1,25 @@ +/* + * 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 + +typedef struct IplParameterBlock { + uint8_t reserved1[110]; + uint16_t devno; + uint8_t reserved2[88]; +} IplParameterBlock; + +int s390_ipl_update_diag308(IplParameterBlock *iplb); +IplParameterBlock *s390_ipl_get_iplb(void); +void s390_reipl_request(void); + +#endif diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c new file mode 100644 index 0000000..3c086f6 --- /dev/null +++ b/hw/s390x/s390-pci-bus.c @@ -0,0 +1,593 @@ +/* + * 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 "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); + return; +} + +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 = ldq_phys(&address_space_memory, sto_a); + sto = get_rt_sto(sto); + if (!sto) { + pte = 0; + goto out; + } + + pto_a = sto + sx * sizeof(uint64_t); + pto = ldq_phys(&address_space_memory, pto_a); + pto = get_st_pto(pto); + if (!pto) { + pte = 0; + goto out; + } + + px_a = pto + px * sizeof(uint64_t); + pte = ldq_phys(&address_space_memory, px_a); + +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, mr); + S390pciState *s = S390_PCI_HOST_BRIDGE(pci_device_root_bus(pbdev->pdev) + ->qbus.parent); + IOMMUTLBEntry ret = { + .target_as = &address_space_memory, + .iova = 0, + .translated_addr = 0, + .addr_mask = ~(hwaddr)0, + .perm = IOMMU_NONE, + }; + + DPRINTF("iommu trans addr 0x%" PRIx64 "\n", addr); + + /* 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; + } + + 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); + } + + return; +} + +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, +}; + +static void s390_pcihost_init_as(S390pciState *s) +{ + int i; + + for (i = 0; i < PCI_SLOT_MAX; i++) { + memory_region_init_iommu(&s->pbdev[i].mr, OBJECT(s), + &s390_iommu_ops, "iommu-s390", UINT64_MAX); + address_space_init(&s->pbdev[i].as, &s->pbdev[i].mr, "iommu-pci"); + } + + 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_CAP_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); + } + return; +} + +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_supported = 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/hw/s390x/s390-pci-bus.h b/hw/s390x/s390-pci-bus.h new file mode 100644 index 0000000..464a92e --- /dev/null +++ b/hw/s390x/s390-pci-bus.h @@ -0,0 +1,251 @@ +/* + * 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 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; +} 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); +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/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c new file mode 100644 index 0000000..08d8aa6 --- /dev/null +++ b/hw/s390x/s390-pci-inst.c @@ -0,0 +1,834 @@ +/* + * 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 "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, 0x80000000); + 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], 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], 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], 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 | 1 << ENABLE_BIT_OFFSET; + stl_p(&ressetpci->fh, pbdev->fh); + stw_p(&ressetpci->hdr.rsp, CLP_RC_OK); + break; + case CLP_SET_DISABLE_PCI_FN: + pbdev->fh = pbdev->fh & ~(1 << ENABLE_BIT_OFFSET); + pbdev->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], 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) { + 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; + io_mem_read(mr, offset, &data, len); + } 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) { + 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; + } + + io_mem_write(mr, offset, data, len); + } 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) { + DPRINTF("rpcit no pci dev\n"); + setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); + goto out; + } + + mr = pci_device_iommu_address_space(pbdev->pdev)->root; + 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) +{ + 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) { + 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, buffer, len)) { + return 0; + } + + for (i = 0; i < len / 8; i++) { + io_mem_write(mr, env->regs[r3] + i * 8, ldq_p(buffer + i * 8), 8); + } + + setcc(cpu, ZPCI_PCI_LS_OK); + return 0; +} + +static int reg_irqs(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib) +{ + int ret; + S390FLICState *fs = s390_get_flic(); + S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); + + ret = css_register_io_adapter(S390_PCIPT_ADAPTER, + FIB_DATA_ISC(ldl_p(&fib.data)), true, false, + &pbdev->routes.adapter.adapter_id); + assert(ret == 0); + + fsc->io_adapter_map(fs, pbdev->routes.adapter.adapter_id, + ldq_p(&fib.aisb), true); + fsc->io_adapter_map(fs, pbdev->routes.adapter.adapter_id, + ldq_p(&fib.aibv), true); + + pbdev->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) +{ + S390FLICState *fs = s390_get_flic(); + S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); + + fsc->io_adapter_map(fs, pbdev->routes.adapter.adapter_id, + pbdev->routes.adapter.ind_addr, false); + + 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; + return 0; +} + +static void dereg_ioat(S390PCIBusDevice *pbdev) +{ + pbdev->pba = 0; + pbdev->pal = 0; + pbdev->g_iota = 0; +} + +int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba) +{ + 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) { + 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, (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) +{ + 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 >> ENABLE_BIT_OFFSET) { + 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, (uint8_t *)&fib, sizeof(fib))) { + return 0; + } + + setcc(cpu, cc); + return 0; +} diff --git a/hw/s390x/s390-pci-inst.h b/hw/s390x/s390-pci-inst.h new file mode 100644 index 0000000..7e6c804 --- /dev/null +++ b/hw/s390x/s390-pci-inst.h @@ -0,0 +1,288 @@ +/* + * 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); +int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba); +int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba); + +#endif diff --git a/hw/s390x/s390-virtio-bus.c b/hw/s390x/s390-virtio-bus.c index 39dc201..047c963 100644 --- a/hw/s390x/s390-virtio-bus.c +++ b/hw/s390x/s390-virtio-bus.c @@ -111,7 +111,8 @@ VirtIOS390Bus *s390_virtio_bus_init(ram_addr_t *ram_size) return bus; } -static int s390_virtio_device_init(VirtIOS390Device *dev, VirtIODevice *vdev) +static void s390_virtio_device_init(VirtIOS390Device *dev, + VirtIODevice *vdev) { VirtIOS390Bus *bus; int dev_len; @@ -135,25 +136,26 @@ static int s390_virtio_device_init(VirtIOS390Device *dev, VirtIODevice *vdev) if (dev->qdev.hotplugged) { s390_virtio_irq(VIRTIO_PARAM_DEV_ADD, dev->dev_offs); } - - return 0; } -static int s390_virtio_net_init(VirtIOS390Device *s390_dev) +static void s390_virtio_net_realize(VirtIOS390Device *s390_dev, Error **errp) { DeviceState *qdev = DEVICE(s390_dev); VirtIONetS390 *dev = VIRTIO_NET_S390(s390_dev); DeviceState *vdev = DEVICE(&dev->vdev); + Error *err = NULL; virtio_net_set_config_size(&dev->vdev, s390_dev->host_features); virtio_net_set_netclient_name(&dev->vdev, qdev->id, object_get_typename(OBJECT(qdev))); qdev_set_parent_bus(vdev, BUS(&s390_dev->bus)); - if (qdev_init(vdev) < 0) { - return -1; + object_property_set_bool(OBJECT(vdev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; } - return s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev)); + s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev)); } static void s390_virtio_net_instance_init(Object *obj) @@ -166,15 +168,19 @@ static void s390_virtio_net_instance_init(Object *obj) "bootindex", &error_abort); } -static int s390_virtio_blk_init(VirtIOS390Device *s390_dev) +static void s390_virtio_blk_realize(VirtIOS390Device *s390_dev, Error **errp) { VirtIOBlkS390 *dev = VIRTIO_BLK_S390(s390_dev); DeviceState *vdev = DEVICE(&dev->vdev); + Error *err = NULL; + qdev_set_parent_bus(vdev, BUS(&s390_dev->bus)); - if (qdev_init(vdev) < 0) { - return -1; + object_property_set_bool(OBJECT(vdev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; } - return s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev)); + s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev)); } static void s390_virtio_blk_instance_init(Object *obj) @@ -189,13 +195,13 @@ static void s390_virtio_blk_instance_init(Object *obj) "bootindex", &error_abort); } -static int s390_virtio_serial_init(VirtIOS390Device *s390_dev) +static void s390_virtio_serial_realize(VirtIOS390Device *s390_dev, Error **errp) { VirtIOSerialS390 *dev = VIRTIO_SERIAL_S390(s390_dev); DeviceState *vdev = DEVICE(&dev->vdev); DeviceState *qdev = DEVICE(s390_dev); + Error *err = NULL; VirtIOS390Bus *bus; - int r; char *bus_name; bus = DO_UPCAST(VirtIOS390Bus, bus, qdev->parent_bus); @@ -211,16 +217,14 @@ static int s390_virtio_serial_init(VirtIOS390Device *s390_dev) } qdev_set_parent_bus(vdev, BUS(&s390_dev->bus)); - if (qdev_init(vdev) < 0) { - return -1; - } - - r = s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev)); - if (!r) { - bus->console = s390_dev; + object_property_set_bool(OBJECT(vdev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; } - return r; + s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev)); + bus->console = s390_dev; } static void s390_virtio_serial_instance_init(Object *obj) @@ -231,11 +235,12 @@ static void s390_virtio_serial_instance_init(Object *obj) TYPE_VIRTIO_SERIAL); } -static int s390_virtio_scsi_init(VirtIOS390Device *s390_dev) +static void s390_virtio_scsi_realize(VirtIOS390Device *s390_dev, Error **errp) { VirtIOSCSIS390 *dev = VIRTIO_SCSI_S390(s390_dev); DeviceState *vdev = DEVICE(&dev->vdev); DeviceState *qdev = DEVICE(s390_dev); + Error *err = NULL; char *bus_name; /* @@ -249,11 +254,13 @@ static int s390_virtio_scsi_init(VirtIOS390Device *s390_dev) } qdev_set_parent_bus(vdev, BUS(&s390_dev->bus)); - if (qdev_init(vdev) < 0) { - return -1; + object_property_set_bool(OBJECT(vdev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; } - return s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev)); + s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev)); } static void s390_virtio_scsi_instance_init(Object *obj) @@ -265,17 +272,20 @@ static void s390_virtio_scsi_instance_init(Object *obj) } #ifdef CONFIG_VHOST_SCSI -static int s390_vhost_scsi_init(VirtIOS390Device *s390_dev) +static void s390_vhost_scsi_realize(VirtIOS390Device *s390_dev, Error **errp) { VHostSCSIS390 *dev = VHOST_SCSI_S390(s390_dev); DeviceState *vdev = DEVICE(&dev->vdev); + Error *err = NULL; qdev_set_parent_bus(vdev, BUS(&s390_dev->bus)); - if (qdev_init(vdev) < 0) { - return -1; + object_property_set_bool(OBJECT(vdev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; } - return s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev)); + s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev)); } static void s390_vhost_scsi_instance_init(Object *obj) @@ -288,21 +298,24 @@ static void s390_vhost_scsi_instance_init(Object *obj) #endif -static int s390_virtio_rng_init(VirtIOS390Device *s390_dev) +static void s390_virtio_rng_realize(VirtIOS390Device *s390_dev, Error **errp) { VirtIORNGS390 *dev = VIRTIO_RNG_S390(s390_dev); DeviceState *vdev = DEVICE(&dev->vdev); + Error *err = NULL; qdev_set_parent_bus(vdev, BUS(&s390_dev->bus)); - if (qdev_init(vdev) < 0) { - return -1; + 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); - return s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev)); + s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev)); } static void s390_virtio_rng_instance_init(Object *obj) @@ -422,11 +435,6 @@ void s390_virtio_device_update_status(VirtIOS390Device *dev) virtio_set_features(vdev, features); } -VirtIOS390Device *s390_virtio_bus_console(VirtIOS390Bus *bus) -{ - return bus->console; -} - /* Find a device by vring address */ VirtIOS390Device *s390_virtio_bus_find_vring(VirtIOS390Bus *bus, ram_addr_t mem, @@ -509,7 +517,7 @@ static void s390_virtio_net_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass); - k->init = s390_virtio_net_init; + k->realize = s390_virtio_net_realize; dc->props = s390_virtio_net_properties; } @@ -525,7 +533,7 @@ static void s390_virtio_blk_class_init(ObjectClass *klass, void *data) { VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass); - k->init = s390_virtio_blk_init; + k->realize = s390_virtio_blk_realize; } static const TypeInfo s390_virtio_blk = { @@ -545,7 +553,7 @@ static void s390_virtio_serial_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass); - k->init = s390_virtio_serial_init; + k->realize = s390_virtio_serial_realize; dc->props = s390_virtio_serial_properties; } @@ -567,7 +575,7 @@ static void s390_virtio_rng_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass); - k->init = s390_virtio_rng_init; + k->realize = s390_virtio_rng_realize; dc->props = s390_virtio_rng_properties; } @@ -579,14 +587,14 @@ static const TypeInfo s390_virtio_rng = { .class_init = s390_virtio_rng_class_init, }; -static int s390_virtio_busdev_init(DeviceState *dev) +static void s390_virtio_busdev_realize(DeviceState *dev, Error **errp) { VirtIOS390Device *_dev = (VirtIOS390Device *)dev; VirtIOS390DeviceClass *_info = VIRTIO_S390_DEVICE_GET_CLASS(dev); virtio_s390_bus_new(&_dev->bus, sizeof(_dev->bus), _dev); - return _info->init(_dev); + _info->realize(_dev, errp); } static void s390_virtio_busdev_reset(DeviceState *dev) @@ -600,7 +608,7 @@ static void virtio_s390_device_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->init = s390_virtio_busdev_init; + dc->realize = s390_virtio_busdev_realize; dc->bus_type = TYPE_S390_VIRTIO_BUS; dc->reset = s390_virtio_busdev_reset; } @@ -625,7 +633,7 @@ static void s390_virtio_scsi_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass); - k->init = s390_virtio_scsi_init; + k->realize = s390_virtio_scsi_realize; dc->props = s390_virtio_scsi_properties; } @@ -648,7 +656,7 @@ static void s390_vhost_scsi_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass); - k->init = s390_vhost_scsi_init; + k->realize = s390_vhost_scsi_realize; dc->props = s390_vhost_scsi_properties; } diff --git a/hw/s390x/s390-virtio-bus.h b/hw/s390x/s390-virtio-bus.h index ffd0df7..96b1890 100644 --- a/hw/s390x/s390-virtio-bus.h +++ b/hw/s390x/s390-virtio-bus.h @@ -19,6 +19,10 @@ #ifndef HW_S390_VIRTIO_BUS_H #define HW_S390_VIRTIO_BUS_H 1 +#include + +#include "standard-headers/asm-s390/kvm_virtio.h" +#include "standard-headers/linux/virtio_ring.h" #include "hw/virtio/virtio-blk.h" #include "hw/virtio/virtio-net.h" #include "hw/virtio/virtio-rng.h" @@ -29,28 +33,26 @@ #include "hw/virtio/vhost-scsi.h" #endif -#define VIRTIO_DEV_OFFS_TYPE 0 /* 8 bits */ -#define VIRTIO_DEV_OFFS_NUM_VQ 1 /* 8 bits */ -#define VIRTIO_DEV_OFFS_FEATURE_LEN 2 /* 8 bits */ -#define VIRTIO_DEV_OFFS_CONFIG_LEN 3 /* 8 bits */ -#define VIRTIO_DEV_OFFS_STATUS 4 /* 8 bits */ -#define VIRTIO_DEV_OFFS_CONFIG 5 /* dynamic */ +typedef struct kvm_device_desc KvmDeviceDesc; + +#define VIRTIO_DEV_OFFS_TYPE offsetof(KvmDeviceDesc, type) +#define VIRTIO_DEV_OFFS_NUM_VQ offsetof(KvmDeviceDesc, num_vq) +#define VIRTIO_DEV_OFFS_FEATURE_LEN offsetof(KvmDeviceDesc, feature_len) +#define VIRTIO_DEV_OFFS_CONFIG_LEN offsetof(KvmDeviceDesc, config_len) +#define VIRTIO_DEV_OFFS_STATUS offsetof(KvmDeviceDesc, status) +#define VIRTIO_DEV_OFFS_CONFIG offsetof(KvmDeviceDesc, config) -#define VIRTIO_VQCONFIG_OFFS_TOKEN 0 /* 64 bits */ -#define VIRTIO_VQCONFIG_OFFS_ADDRESS 8 /* 64 bits */ -#define VIRTIO_VQCONFIG_OFFS_NUM 16 /* 16 bits */ -#define VIRTIO_VQCONFIG_LEN 24 +typedef struct kvm_vqconfig KvmVqConfig; +#define VIRTIO_VQCONFIG_OFFS_TOKEN offsetof(KvmVqConfig,token) /* 64 bit */ +#define VIRTIO_VQCONFIG_OFFS_ADDRESS offsetof(KvmVqConfig, address) /* 64 bit */ +#define VIRTIO_VQCONFIG_OFFS_NUM offsetof(KvmVqConfig, num) /* 16 bit */ +#define VIRTIO_VQCONFIG_LEN sizeof(KvmVqConfig) #define VIRTIO_RING_LEN (TARGET_PAGE_SIZE * 3) -#define VIRTIO_VRING_AVAIL_IDX_OFFS 2 -#define VIRTIO_VRING_USED_IDX_OFFS 2 +#define VIRTIO_VRING_AVAIL_IDX_OFFS offsetof(struct vring_avail, idx) +#define VIRTIO_VRING_USED_IDX_OFFS offsetof(struct vring_used, idx) #define S390_DEVICE_PAGES 512 -#define VIRTIO_PARAM_MASK 0xff -#define VIRTIO_PARAM_VRING_INTERRUPT 0x0 -#define VIRTIO_PARAM_CONFIG_CHANGED 0x1 -#define VIRTIO_PARAM_DEV_ADD 0x2 - #define TYPE_VIRTIO_S390_DEVICE "virtio-s390-device" #define VIRTIO_S390_DEVICE(obj) \ OBJECT_CHECK(VirtIOS390Device, (obj), TYPE_VIRTIO_S390_DEVICE) @@ -81,7 +83,7 @@ typedef struct VirtIOS390Device VirtIOS390Device; typedef struct VirtIOS390DeviceClass { DeviceClass qdev; - int (*init)(VirtIOS390Device *dev); + void (*realize)(VirtIOS390Device *dev, Error **errp); } VirtIOS390DeviceClass; struct VirtIOS390Device { @@ -106,7 +108,6 @@ typedef struct VirtIOS390Bus { void s390_virtio_device_update_status(VirtIOS390Device *dev); -VirtIOS390Device *s390_virtio_bus_console(VirtIOS390Bus *bus); VirtIOS390Bus *s390_virtio_bus_init(ram_addr_t *ram_size); VirtIOS390Device *s390_virtio_bus_find_vring(VirtIOS390Bus *bus, diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index bc4dc2a..afb539a 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -18,9 +18,22 @@ #include "css.h" #include "virtio-ccw.h" #include "qemu/config-file.h" +#include "s390-pci-bus.h" #define TYPE_S390_CCW_MACHINE "s390-ccw-machine" +#define S390_CCW_MACHINE(obj) \ + OBJECT_CHECK(S390CcwMachineState, (obj), TYPE_S390_CCW_MACHINE) + +typedef struct S390CcwMachineState { + /*< private >*/ + MachineState parent_obj; + + /*< public >*/ + bool aes_key_wrap; + bool dea_key_wrap; +} S390CcwMachineState; + void io_subsystem_reset(void) { DeviceState *css, *sclp, *flic; @@ -91,10 +104,12 @@ static void ccw_init(MachineState *machine) uint8_t *storage_keys; int ret; VirtualCssBus *css_bus; + DeviceState *dev; QemuOpts *opts = qemu_opts_find(qemu_find_opts("memory"), NULL); ram_addr_t pad_size = 0; ram_addr_t maxmem = qemu_opt_get_size(opts, "maxmem", my_ram_size); ram_addr_t standby_mem_size = maxmem - my_ram_size; + uint64_t kvm_limit; /* The storage increment size is a multiple of 1M and is a power of 2. * The number of storage increments must be MAX_STORAGE_INCREMENTS or fewer. @@ -119,14 +134,28 @@ static void ccw_init(MachineState *machine) /* let's propagate the changed ram size into the global variable. */ ram_size = my_ram_size; + machine->maxram_size = my_ram_size + standby_mem_size; + + ret = s390_set_memory_limit(machine->maxram_size, &kvm_limit); + if (ret == -E2BIG) { + hw_error("qemu: host supports a maximum of %" PRIu64 " GB", + kvm_limit >> 30); + } else if (ret) { + hw_error("qemu: setting the guest size failed"); + } /* get a BUS */ css_bus = virtual_css_bus_init(); s390_sclp_init(); s390_init_ipl_dev(machine->kernel_filename, machine->kernel_cmdline, - machine->initrd_filename, "s390-ccw.img"); + 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(); @@ -164,6 +193,10 @@ static void ccw_init(MachineState *machine) /* 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 ccw_machine_class_init(ObjectClass *oc, void *data) @@ -181,14 +214,65 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data) mc->no_serial = 1; mc->no_parallel = 1; mc->no_sdcard = 1; - mc->use_sclp = 1, + mc->use_sclp = 1; mc->max_cpus = 255; 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, + .instance_size = sizeof(S390CcwMachineState), + .instance_init = s390_machine_initfn, .class_init = ccw_machine_class_init, .interfaces = (InterfaceInfo[]) { { TYPE_NMI }, diff --git a/hw/s390x/s390-virtio.c b/hw/s390x/s390-virtio.c index c215cd8..bdb5388 100644 --- a/hw/s390x/s390-virtio.c +++ b/hw/s390x/s390-virtio.c @@ -38,6 +38,7 @@ #include "hw/s390x/sclp.h" #include "hw/s390x/s390_flic.h" #include "hw/s390x/s390-virtio.h" +#include "cpu.h" //#define DEBUG_S390 @@ -53,6 +54,9 @@ #define ZIPL_FILENAME "s390-zipl.rom" #define TYPE_S390_MACHINE "s390-machine" +#define S390_TOD_CLOCK_VALUE_MISSING 0x00 +#define S390_TOD_CLOCK_VALUE_PRESENT 0x01 + static VirtIOS390Bus *s390_bus; static S390CPU **ipi_states; @@ -128,7 +132,8 @@ static void s390_virtio_register_hcalls(void) void s390_init_ipl_dev(const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, - const char *firmware) + const char *firmware, + bool enforce_bios) { DeviceState *dev; @@ -141,6 +146,9 @@ void s390_init_ipl_dev(const char *kernel_filename, } qdev_prop_set_string(dev, "cmdline", kernel_cmdline); qdev_prop_set_string(dev, "firmware", firmware); + qdev_prop_set_bit(dev, "enforce_bios", enforce_bios); + object_property_add_child(qdev_get_machine(), "s390-ipl", + OBJECT(dev), NULL); qdev_init_nofail(dev); } @@ -192,6 +200,51 @@ void s390_create_virtio_net(BusState *bus, const char *name) } } +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; +} + /* PC hardware initialisation */ static void s390_init(MachineState *machine) { @@ -221,7 +274,7 @@ static void s390_init(MachineState *machine) s390_bus = s390_virtio_bus_init(&my_ram_size); s390_sclp_init(); s390_init_ipl_dev(machine->kernel_filename, machine->kernel_cmdline, - machine->initrd_filename, ZIPL_FILENAME); + machine->initrd_filename, ZIPL_FILENAME, false); s390_flic_init(); /* register hypercalls */ @@ -249,6 +302,9 @@ static void s390_init(MachineState *machine) /* Create VirtIO network adapters */ s390_create_virtio_net((BusState *)s390_bus, "virtio-net-s390"); + + /* Register savevm handler for guest TOD clock */ + register_savevm(NULL, "todclock", 0, 1, gtod_save, gtod_load, NULL); } void s390_nmi(NMIState *n, int cpu_index, Error **errp) diff --git a/hw/s390x/s390-virtio.h b/hw/s390x/s390-virtio.h index 33847ae..c847853 100644 --- a/hw/s390x/s390-virtio.h +++ b/hw/s390x/s390-virtio.h @@ -13,11 +13,8 @@ #define HW_S390_VIRTIO_H 1 #include "hw/nmi.h" - -#define KVM_S390_VIRTIO_NOTIFY 0 -#define KVM_S390_VIRTIO_RESET 1 -#define KVM_S390_VIRTIO_SET_STATUS 2 -#define KVM_S390_VIRTIO_CCW_NOTIFY 3 +#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); @@ -26,7 +23,8 @@ void s390_init_cpus(const char *cpu_model, uint8_t *storage_keys); void s390_init_ipl_dev(const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, - const char *firmware); + 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); #endif diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c index a759da7..a969975 100644 --- a/hw/s390x/sclp.c +++ b/hw/s390x/sclp.c @@ -20,6 +20,7 @@ #include "qemu/config-file.h" #include "hw/s390x/sclp.h" #include "hw/s390x/event-facility.h" +#include "hw/s390x/s390-pci-bus.h" static inline SCLPEventFacility *get_event_facility(void) { @@ -62,7 +63,8 @@ static void read_SCP_info(SCCB *sccb) read_info->entries[i].type = 0; } - read_info->facilities = cpu_to_be64(SCLP_HAS_CPU_INFO); + read_info->facilities = cpu_to_be64(SCLP_HAS_CPU_INFO | + SCLP_HAS_PCI_RECONFIG); /* * The storage increment size is a multiple of 1M and is a power of 2. @@ -350,6 +352,12 @@ static void sclp_execute(SCCB *sccb, uint32_t code) case SCLP_UNASSIGN_STORAGE: unassign_storage(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; diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index ea236c9..d8fde77 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -266,7 +266,7 @@ static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align, { VirtIODevice *vdev = virtio_ccw_get_vdev(sch); - if (index > VIRTIO_PCI_QUEUE_MAX) { + if (index >= VIRTIO_PCI_QUEUE_MAX) { return -EINVAL; } @@ -295,6 +295,25 @@ static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align, 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_cb(SubchDev *sch, CCW1 ccw) { int ret; @@ -351,8 +370,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) } break; case CCW_CMD_VDEV_RESET: - virtio_ccw_stop_ioeventfd(dev); - virtio_reset(vdev); + virtio_ccw_reset_virtio(dev, vdev); ret = 0; break; case CCW_CMD_READ_FEAT: @@ -400,8 +418,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) ccw.cda + sizeof(features.features)); features.features = ldl_le_phys(&address_space_memory, ccw.cda); if (features.index < ARRAY_SIZE(dev->host_features)) { - virtio_bus_set_vdev_features(&dev->bus, features.features); - vdev->guest_features = features.features; + virtio_set_features(vdev, features.features); } else { /* * If the guest supports more feature bits, assert that it @@ -481,7 +498,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) } virtio_set_status(vdev, status); if (vdev->status == 0) { - virtio_reset(vdev); + virtio_ccw_reset_virtio(dev, vdev); } if (status & VIRTIO_CONFIG_S_DRIVER_OK) { virtio_ccw_start_ioeventfd(dev); @@ -509,7 +526,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) if (!ccw.cda) { ret = -EFAULT; } else { - indicators = ldq_phys(&address_space_memory, ccw.cda); + indicators = ldq_be_phys(&address_space_memory, ccw.cda); dev->indicators = get_indicator(indicators, sizeof(uint64_t)); sch->curr_status.scsw.count = ccw.count - sizeof(indicators); ret = 0; @@ -529,7 +546,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) if (!ccw.cda) { ret = -EFAULT; } else { - indicators = ldq_phys(&address_space_memory, ccw.cda); + indicators = ldq_be_phys(&address_space_memory, ccw.cda); dev->indicators2 = get_indicator(indicators, sizeof(uint64_t)); sch->curr_status.scsw.count = ccw.count - sizeof(indicators); ret = 0; @@ -549,11 +566,15 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) if (!ccw.cda) { ret = -EFAULT; } else { - vq_config.index = lduw_phys(&address_space_memory, ccw.cda); + vq_config.index = lduw_be_phys(&address_space_memory, ccw.cda); + if (vq_config.index >= VIRTIO_PCI_QUEUE_MAX) { + ret = -EINVAL; + break; + } vq_config.num_max = virtio_queue_get_num(vdev, vq_config.index); - stw_phys(&address_space_memory, - ccw.cda + sizeof(vq_config.index), vq_config.num_max); + stw_be_phys(&address_space_memory, + ccw.cda + sizeof(vq_config.index), vq_config.num_max); sch->curr_status.scsw.count = ccw.count - sizeof(vq_config); ret = 0; } @@ -581,13 +602,17 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) if (!thinint) { ret = -EFAULT; } else { + uint64_t ind_bit = ldq_be_p(&thinint->ind_bit); + len = hw_len; dev->summary_indicator = - get_indicator(thinint->summary_indicator, sizeof(uint8_t)); - dev->indicators = get_indicator(thinint->device_indicator, - thinint->ind_bit / 8 + 1); + 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 = thinint->ind_bit; + 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, @@ -608,7 +633,8 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) return ret; } -static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev) +static void virtio_ccw_device_realize(VirtioCcwDevice *dev, + VirtIODevice *vdev, Error **errp) { unsigned int cssid = 0; unsigned int ssid = 0; @@ -617,7 +643,6 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev) bool have_devno = false; bool found = false; SubchDev *sch; - int ret; int num; DeviceState *parent = DEVICE(dev); @@ -640,21 +665,19 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev) num = sscanf(dev->bus_id, "%x.%x.%04x", &cssid, &ssid, &devno); if (num == 3) { if ((cssid > MAX_CSSID) || (ssid > MAX_SSID)) { - ret = -EINVAL; - error_report("Invalid cssid or ssid: cssid %x, ssid %x", - cssid, 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) { - ret = -EINVAL; - error_report("cssid %x not valid for virtio devices", cssid); + error_setg(errp, "cssid %x not valid for virtio devices", + cssid); goto out_err; } if (css_devno_used(cssid, ssid, devno)) { - ret = -EEXIST; - error_report("Device %x.%x.%04x already exists", cssid, ssid, - devno); + error_setg(errp, "Device %x.%x.%04x already exists", + cssid, ssid, devno); goto out_err; } sch->cssid = cssid; @@ -662,8 +685,7 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev) sch->devno = devno; have_devno = true; } else { - ret = -EINVAL; - error_report("Malformed devno parameter '%s'", dev->bus_id); + error_setg(errp, "Malformed devno parameter '%s'", dev->bus_id); goto out_err; } } @@ -679,9 +701,8 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev) } } if (!found) { - ret = -ENODEV; - error_report("No free subchannel found for %x.%x.%04x", cssid, ssid, - devno); + 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, @@ -703,8 +724,7 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev) if (devno == MAX_SCHID) { devno = 0; } else if (devno == schid - 1) { - ret = -ENODEV; - error_report("No free devno found"); + error_setg(errp, "No free devno found"); goto out_err; } else { devno++; @@ -721,8 +741,7 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev) } } if (!found) { - ret = -ENODEV; - error_report("Virtual channel subsystem is full!"); + error_setg(errp, "Virtual channel subsystem is full!"); goto out_err; } trace_virtio_ccw_new_device(cssid, ssid, schid, devno, @@ -744,17 +763,16 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev) dev->host_features[0] = virtio_bus_get_vdev_features(&dev->bus, dev->host_features[0]); - dev->host_features[0] |= 0x1 << VIRTIO_F_NOTIFY_ON_EMPTY; - dev->host_features[0] |= 0x1 << VIRTIO_F_BAD_FEATURE; + virtio_add_feature(&dev->host_features[0], VIRTIO_F_NOTIFY_ON_EMPTY); + virtio_add_feature(&dev->host_features[0], VIRTIO_F_BAD_FEATURE); css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, parent->hotplugged, 1); - return 0; + return; out_err: dev->sch = NULL; g_free(sch); - return ret; } static int virtio_ccw_exit(VirtioCcwDevice *dev) @@ -772,21 +790,24 @@ static int virtio_ccw_exit(VirtioCcwDevice *dev) return 0; } -static int virtio_ccw_net_init(VirtioCcwDevice *ccw_dev) +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_config_size(&dev->vdev, ccw_dev->host_features[0]); virtio_net_set_netclient_name(&dev->vdev, qdev->id, object_get_typename(OBJECT(qdev))); qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - if (qdev_init(vdev) < 0) { - return -1; + object_property_set_bool(OBJECT(vdev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; } - return virtio_ccw_device_init(ccw_dev, VIRTIO_DEVICE(vdev)); + virtio_ccw_device_realize(ccw_dev, VIRTIO_DEVICE(vdev), errp); } static void virtio_ccw_net_instance_init(Object *obj) @@ -799,16 +820,20 @@ static void virtio_ccw_net_instance_init(Object *obj) "bootindex", &error_abort); } -static int virtio_ccw_blk_init(VirtioCcwDevice *ccw_dev) +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)); - if (qdev_init(vdev) < 0) { - return -1; + object_property_set_bool(OBJECT(vdev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; } - return virtio_ccw_device_init(ccw_dev, VIRTIO_DEVICE(vdev)); + virtio_ccw_device_realize(ccw_dev, VIRTIO_DEVICE(vdev), errp); } static void virtio_ccw_blk_instance_init(Object *obj) @@ -823,11 +848,12 @@ static void virtio_ccw_blk_instance_init(Object *obj) "bootindex", &error_abort); } -static int virtio_ccw_serial_init(VirtioCcwDevice *ccw_dev) +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; /* @@ -841,11 +867,13 @@ static int virtio_ccw_serial_init(VirtioCcwDevice *ccw_dev) } qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - if (qdev_init(vdev) < 0) { - return -1; + object_property_set_bool(OBJECT(vdev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; } - return virtio_ccw_device_init(ccw_dev, VIRTIO_DEVICE(vdev)); + virtio_ccw_device_realize(ccw_dev, VIRTIO_DEVICE(vdev), errp); } @@ -857,17 +885,20 @@ static void virtio_ccw_serial_instance_init(Object *obj) TYPE_VIRTIO_SERIAL); } -static int virtio_ccw_balloon_init(VirtioCcwDevice *ccw_dev) +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)); - if (qdev_init(vdev) < 0) { - return -1; + object_property_set_bool(OBJECT(vdev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; } - return virtio_ccw_device_init(ccw_dev, VIRTIO_DEVICE(vdev)); + virtio_ccw_device_realize(ccw_dev, VIRTIO_DEVICE(vdev), errp); } static void balloon_ccw_stats_get_all(Object *obj, struct Visitor *v, @@ -899,9 +930,8 @@ static void balloon_ccw_stats_set_poll_interval(Object *obj, struct Visitor *v, static void virtio_ccw_balloon_instance_init(Object *obj) { VirtIOBalloonCcw *dev = VIRTIO_BALLOON_CCW(obj); - object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_BALLOON); - object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL); - object_unref(OBJECT(&dev->vdev)); + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VIRTIO_BALLOON); object_property_add(obj, "guest-stats", "guest statistics", balloon_ccw_stats_get_all, NULL, NULL, dev, NULL); @@ -911,11 +941,12 @@ static void virtio_ccw_balloon_instance_init(Object *obj) NULL, dev, NULL); } -static int virtio_ccw_scsi_init(VirtioCcwDevice *ccw_dev) +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; /* @@ -929,11 +960,13 @@ static int virtio_ccw_scsi_init(VirtioCcwDevice *ccw_dev) } qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - if (qdev_init(vdev) < 0) { - return -1; + object_property_set_bool(OBJECT(vdev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; } - return virtio_ccw_device_init(ccw_dev, VIRTIO_DEVICE(vdev)); + virtio_ccw_device_realize(ccw_dev, VIRTIO_DEVICE(vdev), errp); } static void virtio_ccw_scsi_instance_init(Object *obj) @@ -947,17 +980,20 @@ static void virtio_ccw_scsi_instance_init(Object *obj) } #ifdef CONFIG_VHOST_SCSI -static int vhost_ccw_scsi_init(VirtioCcwDevice *ccw_dev) +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)); - if (qdev_init(vdev) < 0) { - return -1; + object_property_set_bool(OBJECT(vdev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; } - return virtio_ccw_device_init(ccw_dev, VIRTIO_DEVICE(vdev)); + virtio_ccw_device_realize(ccw_dev, VIRTIO_DEVICE(vdev), errp); } static void vhost_ccw_scsi_instance_init(Object *obj) @@ -969,21 +1005,24 @@ static void vhost_ccw_scsi_instance_init(Object *obj) } #endif -static int virtio_ccw_rng_init(VirtioCcwDevice *ccw_dev) +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)); - if (qdev_init(vdev) < 0) { - return -1; + 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); - return virtio_ccw_device_init(ccw_dev, VIRTIO_DEVICE(vdev)); + virtio_ccw_device_realize(ccw_dev, VIRTIO_DEVICE(vdev), errp); } /* DeviceState to VirtioCcwDevice. Note: used on datapath, @@ -1077,21 +1116,8 @@ static void virtio_ccw_reset(DeviceState *d) VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); - virtio_ccw_stop_ioeventfd(dev); - virtio_reset(vdev); + virtio_ccw_reset_virtio(dev, vdev); css_reset_sch(dev->sch); - 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; - } } static void virtio_ccw_vmstate_change(DeviceState *d, bool running) @@ -1393,7 +1419,7 @@ static void virtio_ccw_net_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - k->init = virtio_ccw_net_init; + k->realize = virtio_ccw_net_realize; k->exit = virtio_ccw_exit; dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_net_properties; @@ -1419,7 +1445,7 @@ static void virtio_ccw_blk_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - k->init = virtio_ccw_blk_init; + k->realize = virtio_ccw_blk_realize; k->exit = virtio_ccw_exit; dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_blk_properties; @@ -1445,7 +1471,7 @@ static void virtio_ccw_serial_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - k->init = virtio_ccw_serial_init; + k->realize = virtio_ccw_serial_realize; k->exit = virtio_ccw_exit; dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_serial_properties; @@ -1471,7 +1497,7 @@ static void virtio_ccw_balloon_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - k->init = virtio_ccw_balloon_init; + k->realize = virtio_ccw_balloon_realize; k->exit = virtio_ccw_exit; dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_balloon_properties; @@ -1498,7 +1524,7 @@ static void virtio_ccw_scsi_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - k->init = virtio_ccw_scsi_init; + k->realize = virtio_ccw_scsi_realize; k->exit = virtio_ccw_exit; dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_scsi_properties; @@ -1523,7 +1549,7 @@ static void vhost_ccw_scsi_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - k->init = vhost_ccw_scsi_init; + k->realize = vhost_ccw_scsi_realize; k->exit = virtio_ccw_exit; dc->reset = virtio_ccw_reset; dc->props = vhost_ccw_scsi_properties; @@ -1560,7 +1586,7 @@ static void virtio_ccw_rng_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - k->init = virtio_ccw_rng_init; + k->realize = virtio_ccw_rng_realize; k->exit = virtio_ccw_exit; dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_rng_properties; @@ -1574,14 +1600,13 @@ static const TypeInfo virtio_ccw_rng = { .class_init = virtio_ccw_rng_class_init, }; -static int virtio_ccw_busdev_init(DeviceState *dev) +static void virtio_ccw_busdev_realize(DeviceState *dev, Error **errp) { VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev; VirtIOCCWDeviceClass *_info = VIRTIO_CCW_DEVICE_GET_CLASS(dev); virtio_ccw_bus_new(&_dev->bus, sizeof(_dev->bus), _dev); - - return _info->init(_dev); + _info->realize(_dev, errp); } static int virtio_ccw_busdev_exit(DeviceState *dev) @@ -1624,7 +1649,7 @@ static void virtio_ccw_device_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->props = virtio_ccw_properties; - dc->init = virtio_ccw_busdev_init; + dc->realize = virtio_ccw_busdev_realize; dc->exit = virtio_ccw_busdev_exit; dc->bus_type = TYPE_VIRTUAL_CSS_BUS; } diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h index 5a1f16e..4fceda7 100644 --- a/hw/s390x/virtio-ccw.h +++ b/hw/s390x/virtio-ccw.h @@ -64,7 +64,7 @@ typedef struct VirtioCcwDevice VirtioCcwDevice; typedef struct VirtIOCCWDeviceClass { DeviceClass parent_class; - int (*init)(VirtioCcwDevice *dev); + void (*realize)(VirtioCcwDevice *dev, Error **errp); int (*exit)(VirtioCcwDevice *dev); } VirtIOCCWDeviceClass; diff --git a/hw/scsi/esp-pci.c b/hw/scsi/esp-pci.c index 00b7297..8d2242d 100644 --- a/hw/scsi/esp-pci.c +++ b/hw/scsi/esp-pci.c @@ -342,13 +342,12 @@ static const struct SCSIBusInfo esp_pci_scsi_info = { .cancel = esp_request_cancelled, }; -static int esp_pci_scsi_init(PCIDevice *dev) +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; - Error *err = NULL; pci_conf = dev->config; @@ -367,13 +366,8 @@ static int esp_pci_scsi_init(PCIDevice *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, &err); - if (err != NULL) { - error_free(err); - return -1; - } + scsi_bus_legacy_handle_cmdline(&s->bus, errp); } - return 0; } static void esp_pci_scsi_uninit(PCIDevice *d) @@ -388,7 +382,7 @@ static void esp_pci_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = esp_pci_scsi_init; + 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; @@ -466,17 +460,19 @@ static void dc390_write_config(PCIDevice *dev, } } -static int dc390_scsi_init(PCIDevice *dev) +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, ret; + int i; /* init base class */ - ret = esp_pci_scsi_init(dev); - if (ret < 0) { - return ret; + esp_pci_scsi_realize(dev, &err); + if (err) { + error_propagate(errp, err); + return; } /* EEPROM */ @@ -503,8 +499,6 @@ static int dc390_scsi_init(PCIDevice *dev) chksum = 0x1234 - chksum; contents[EE_CHKSUM1] = chksum & 0xff; contents[EE_CHKSUM2] = chksum >> 8; - - return 0; } static void dc390_class_init(ObjectClass *klass, void *data) @@ -512,7 +506,7 @@ static void dc390_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = dc390_scsi_init; + k->realize = dc390_scsi_realize; k->config_read = dc390_read_config; k->config_write = dc390_write_config; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c index d9b4c7e..c5b0cc5 100644 --- a/hw/scsi/lsi53c895a.c +++ b/hw/scsi/lsi53c895a.c @@ -19,7 +19,6 @@ #include "hw/pci/pci.h" #include "hw/scsi/scsi.h" #include "sysemu/dma.h" -#include "qemu/error-report.h" //#define DEBUG_LSI //#define DEBUG_LSI_REG @@ -277,6 +276,7 @@ typedef struct { 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]; @@ -781,7 +781,7 @@ static void lsi_do_command(LSIState *s) } assert(s->current == NULL); - s->current = g_malloc0(sizeof(lsi_request)); + 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); @@ -1389,6 +1389,7 @@ again: switch ((insn >> 27) & 7) { case 0: /* Jump */ DPRINTF("Jump to 0x%08x\n", addr); + s->adder = addr; s->dsp = addr; break; case 1: /* Call */ @@ -1513,6 +1514,8 @@ static uint8_t lsi_reg_readb(LSIState *s, int offset) return 0x7f; case 0x08: /* Revision ID */ return 0x00; + case 0x09: /* SOCL */ + return s->socl; case 0xa: /* SSID */ return s->ssid; case 0xb: /* SBCL */ @@ -1577,6 +1580,8 @@ static uint8_t lsi_reg_readb(LSIState *s, int offset) 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 */ @@ -2083,12 +2088,11 @@ static const struct SCSIBusInfo lsi_scsi_info = { .cancel = lsi_request_cancelled }; -static int lsi_scsi_init(PCIDevice *dev) +static void lsi_scsi_realize(PCIDevice *dev, Error **errp) { LSIState *s = LSI53C895A(dev); DeviceState *d = DEVICE(dev); uint8_t *pci_conf; - Error *err = NULL; pci_conf = dev->config; @@ -2111,13 +2115,8 @@ static int lsi_scsi_init(PCIDevice *dev) scsi_bus_new(&s->bus, sizeof(s->bus), d, &lsi_scsi_info, NULL); if (!d->hotplugged) { - scsi_bus_legacy_handle_cmdline(&s->bus, &err); - if (err != NULL) { - error_free(err); - return -1; - } + scsi_bus_legacy_handle_cmdline(&s->bus, errp); } - return 0; } static void lsi_class_init(ObjectClass *klass, void *data) @@ -2125,7 +2124,7 @@ static void lsi_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = lsi_scsi_init; + 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; diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index 604252a..ad7317b 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -171,26 +171,29 @@ static bool megasas_is_jbod(MegasasState *s) return s->flags & MEGASAS_MASK_USE_JBOD; } -static void megasas_frame_set_cmd_status(unsigned long frame, uint8_t v) +static void megasas_frame_set_cmd_status(MegasasState *s, + unsigned long frame, uint8_t v) { - stb_phys(&address_space_memory, - frame + offsetof(struct mfi_frame_header, cmd_status), 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(unsigned long frame, uint8_t v) +static void megasas_frame_set_scsi_status(MegasasState *s, + unsigned long frame, uint8_t v) { - stb_phys(&address_space_memory, - frame + offsetof(struct mfi_frame_header, scsi_status), 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(unsigned long frame) +static uint64_t megasas_frame_get_context(MegasasState *s, + unsigned long frame) { - return ldq_le_phys(&address_space_memory, - frame + offsetof(struct mfi_frame_header, context)); + 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) @@ -523,8 +526,7 @@ static MegasasCmd *megasas_enqueue_frame(MegasasState *s, s->busy++; if (s->consumer_pa) { - s->reply_queue_tail = ldl_le_phys(&address_space_memory, - 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); @@ -547,29 +549,24 @@ static void megasas_complete_frame(MegasasState *s, uint64_t context) */ if (megasas_use_queue64(s)) { queue_offset = s->reply_queue_head * sizeof(uint64_t); - stq_le_phys(&address_space_memory, - s->reply_queue_pa + queue_offset, context); + 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_phys(&address_space_memory, - s->reply_queue_pa + queue_offset, context); + stl_le_pci_dma(pci_dev, s->reply_queue_pa + queue_offset, context); } - s->reply_queue_tail = ldl_le_phys(&address_space_memory, - s->consumer_pa); + 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_phys(&address_space_memory, - s->consumer_pa); + 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_phys(&address_space_memory, - s->producer_pa, s->reply_queue_head); + 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); @@ -651,8 +648,8 @@ static int megasas_init_firmware(MegasasState *s, MegasasCmd *cmd) 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_phys(&address_space_memory, s->producer_pa); - s->reply_queue_tail = ldl_le_phys(&address_space_memory, s->consumer_pa); + 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; @@ -1018,8 +1015,7 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun, size_t len, resid; if (!cmd->iov_buf) { - cmd->iov_buf = g_malloc(dcmd_size); - memset(cmd->iov_buf, 0, dcmd_size); + 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; @@ -1221,8 +1217,7 @@ static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun, uint64_t ld_size; if (!cmd->iov_buf) { - cmd->iov_buf = g_malloc(dcmd_size); - memset(cmd->iov_buf, 0x0, dcmd_size); + 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); @@ -1953,14 +1948,14 @@ static void megasas_handle_frame(MegasasState *s, uint64_t frame_addr, * Always read 64bit context, top bits will be * masked out if required in megasas_enqueue_frame() */ - frame_context = megasas_frame_get_context(frame_addr); + 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(frame_addr, BUSY); - megasas_frame_set_cmd_status(frame_addr, MFI_STAT_SCSI_DONE_WITH_ERROR); + 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; @@ -1995,7 +1990,7 @@ static void megasas_handle_frame(MegasasState *s, uint64_t frame_addr, if (cmd->frame) { cmd->frame->header.cmd_status = frame_status; } else { - megasas_frame_set_cmd_status(frame_addr, frame_status); + megasas_frame_set_cmd_status(s, frame_addr, frame_status); } megasas_unmap_frame(s, cmd); megasas_complete_frame(s, cmd->context); @@ -2322,14 +2317,13 @@ static const struct SCSIBusInfo megasas_scsi_info = { .cancel = megasas_command_cancel, }; -static int megasas_scsi_init(PCIDevice *dev) +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; - Error *err = NULL; pci_conf = dev->config; @@ -2409,13 +2403,8 @@ static int megasas_scsi_init(PCIDevice *dev) scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev), &megasas_scsi_info, NULL); if (!d->hotplugged) { - scsi_bus_legacy_handle_cmdline(&s->bus, &err); - if (err != NULL) { - error_free(err); - return -1; - } + scsi_bus_legacy_handle_cmdline(&s->bus, errp); } - return 0; } static void @@ -2509,7 +2498,7 @@ static void megasas_class_init(ObjectClass *oc, void *data) MegasasBaseClass *e = MEGASAS_DEVICE_CLASS(oc); const MegasasInfo *info = data; - pc->init = megasas_scsi_init; + pc->realize = megasas_scsi_realize; pc->exit = megasas_scsi_uninit; pc->vendor_id = PCI_VENDOR_ID_LSI_LOGIC; pc->device_id = info->device_id; diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index 24f7b74..0c506db 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -221,11 +221,16 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockBackend *blk, 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", @@ -237,8 +242,9 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockBackend *blk, if (serial && object_property_find(OBJECT(dev), "serial", NULL)) { qdev_prop_set_string(dev, "serial", serial); } - if (qdev_prop_set_drive(dev, "drive", blk) < 0) { - error_setg(errp, "Setting drive property failed"); + qdev_prop_set_drive(dev, "drive", blk, &err); + if (err) { + error_propagate(errp, err); object_unparent(OBJECT(dev)); return NULL; } @@ -268,7 +274,6 @@ void scsi_bus_legacy_handle_cmdline(SCSIBus *bus, Error **errp) scsi_bus_legacy_add_drive(bus, blk_by_legacy_dinfo(dinfo), unit, false, -1, NULL, &err); if (err != NULL) { - error_report("%s", error_get_pretty(err)); error_propagate(errp, err); break; } @@ -1234,10 +1239,15 @@ int scsi_cdb_length(uint8_t *buf) { int scsi_req_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf) { int rc; + int len; cmd->lba = -1; - cmd->len = scsi_cdb_length(buf); + 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); @@ -1756,6 +1766,8 @@ void scsi_req_cancel_async(SCSIRequest *req, Notifier *notifier) req->io_canceled = true; if (req->aiocb) { blk_aio_cancel_async(req->aiocb); + } else { + scsi_req_cancel_complete(req); } } @@ -1770,6 +1782,8 @@ void scsi_req_cancel(SCSIRequest *req) req->io_canceled = true; if (req->aiocb) { blk_aio_cancel(req->aiocb); + } else { + scsi_req_cancel_complete(req); } } diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 2f75d7d..54d71f4 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -49,6 +49,7 @@ do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0) #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; @@ -79,6 +80,7 @@ struct SCSIDiskState uint64_t port_wwn; uint16_t port_index; uint64_t max_unmap_size; + uint64_t max_io_size; QEMUBH *bh; char *version; char *serial; @@ -635,6 +637,8 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) 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", @@ -651,6 +655,12 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) 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; @@ -674,6 +684,17 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) 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 */ @@ -2230,6 +2251,7 @@ static void scsi_realize(SCSIDevice *dev, Error **errp) } 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) { @@ -2269,6 +2291,12 @@ static void scsi_realize(SCSIDevice *dev, Error **errp) 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) { @@ -2579,6 +2607,8 @@ static Property scsi_hd_properties[] = { 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(), }; @@ -2625,6 +2655,8 @@ static Property scsi_cd_properties[] = { DEFINE_PROP_UINT64("wwn", SCSIDiskState, wwn, 0), DEFINE_PROP_UINT64("port_wwn", SCSIDiskState, 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(), }; @@ -2690,6 +2722,8 @@ static Property scsi_disk_properties[] = { 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(), }; diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index 6b9e4e1..e53470f 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -298,8 +298,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd) #endif if (r->req.cmd.xfer == 0) { - if (r->buf != NULL) - g_free(r->buf); + g_free(r->buf); r->buflen = 0; r->buf = NULL; /* The request is used as the AIO opaque value, so add a ref. */ @@ -314,8 +313,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd) } if (r->buflen != r->req.cmd.xfer) { - if (r->buf != NULL) - g_free(r->buf); + g_free(r->buf); r->buf = g_malloc(r->req.cmd.xfer); r->buflen = r->req.cmd.xfer; } diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c index 20b20f0..891424f 100644 --- a/hw/scsi/spapr_vscsi.c +++ b/hw/scsi/spapr_vscsi.c @@ -630,7 +630,7 @@ 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); + 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); @@ -1212,24 +1212,17 @@ static void spapr_vscsi_reset(VIOsPAPRDevice *dev) } } -static int spapr_vscsi_init(VIOsPAPRDevice *dev) +static void spapr_vscsi_realize(VIOsPAPRDevice *dev, Error **errp) { VSCSIState *s = VIO_SPAPR_VSCSI_DEVICE(dev); - Error *err = NULL; 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, &err); - if (err != NULL) { - error_free(err); - return -1; - } + scsi_bus_legacy_handle_cmdline(&s->bus, errp); } - - return 0; } void spapr_vscsi_create(VIOsPAPRBus *bus) @@ -1281,7 +1274,7 @@ static void spapr_vscsi_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VIOsPAPRDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); - k->init = spapr_vscsi_init; + k->realize = spapr_vscsi_realize; k->reset = spapr_vscsi_reset; k->devnode = spapr_vscsi_devnode; k->dt_name = "v-scsi"; diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c index dcb2bc5..335f442 100644 --- a/hw/scsi/vhost-scsi.c +++ b/hw/scsi/vhost-scsi.c @@ -24,6 +24,7 @@ #include "hw/virtio/virtio-scsi.h" #include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio-access.h" +#include "hw/fw-path-provider.h" /* Features supported by host kernel. */ static const int kernel_feature_bits[] = { @@ -82,7 +83,7 @@ static int vhost_scsi_start(VHostSCSI *s) 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\n", abi_version, + " upgrade your version of QEMU", abi_version, VHOST_SCSI_ABI_VERSION); return -ENOSYS; } @@ -140,7 +141,7 @@ static void vhost_scsi_stop(VHostSCSI *s) 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\n", ret); + error_report("vhost guest notifier cleanup failed: %d", ret); } } assert(ret >= 0); @@ -185,7 +186,7 @@ static void vhost_scsi_set_status(VirtIODevice *vdev, uint8_t val) ret = vhost_scsi_start(s); if (ret < 0) { - error_report("virtio-scsi: unable to start vhost: %s\n", + error_report("virtio-scsi: unable to start vhost: %s", strerror(-ret)); /* There is no userspace virtio-scsi fallback so exit */ @@ -214,9 +215,11 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp) } if (vs->conf.vhostfd) { - vhostfd = monitor_handle_fd_param(cur_mon, vs->conf.vhostfd); + vhostfd = monitor_fd_param(cur_mon, vs->conf.vhostfd, &err); if (vhostfd == -1) { - error_setg(errp, "vhost-scsi: unable to parse vhostfd"); + error_setg(errp, "vhost-scsi: unable to parse vhostfd: %s", + error_get_pretty(err)); + error_free(err); return; } } else { @@ -250,6 +253,12 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp) 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); @@ -271,6 +280,19 @@ static void vhost_scsi_unrealize(DeviceState *dev, Error **errp) 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_VHOST_SCSI_PROPERTIES(VHostSCSI, parent_obj.conf), DEFINE_PROP_END_OF_LIST(), @@ -280,6 +302,7 @@ 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); @@ -288,6 +311,15 @@ static void vhost_scsi_class_init(ObjectClass *klass, void *data) 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 = { @@ -295,6 +327,11 @@ static const TypeInfo vhost_scsi_info = { .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) diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c index 03a1e8c..5575648 100644 --- a/hw/scsi/virtio-scsi-dataplane.c +++ b/hw/scsi/virtio-scsi-dataplane.c @@ -45,7 +45,7 @@ static VirtIOSCSIVring *virtio_scsi_vring_init(VirtIOSCSI *s, { BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - VirtIOSCSIVring *r = g_slice_new(VirtIOSCSIVring); + VirtIOSCSIVring *r; int rc; /* Set up virtqueue notify */ @@ -56,6 +56,8 @@ static VirtIOSCSIVring *virtio_scsi_vring_init(VirtIOSCSI *s, s->dataplane_fenced = true; return NULL; } + + r = g_slice_new(VirtIOSCSIVring); r->host_notifier = *virtio_queue_get_host_notifier(vq); r->guest_notifier = *virtio_queue_get_guest_notifier(vq); aio_set_event_notifier(s->ctx, &r->host_notifier, handler); @@ -94,7 +96,7 @@ void virtio_scsi_vring_push_notify(VirtIOSCSIReq *req) { VirtIODevice *vdev = VIRTIO_DEVICE(req->vring->parent); - vring_push(&req->vring->vring, &req->elem, + vring_push(vdev, &req->vring->vring, &req->elem, req->qsgl.size + req->resp_iov.size); if (vring_should_notify(vdev, &req->vring->vring)) { @@ -180,13 +182,19 @@ static void virtio_scsi_vring_teardown(VirtIOSCSI *s) if (s->ctrl_vring) { vring_teardown(&s->ctrl_vring->vring, vdev, 0); + g_slice_free(VirtIOSCSIVring, s->ctrl_vring); + s->ctrl_vring = NULL; } if (s->event_vring) { vring_teardown(&s->event_vring->vring, vdev, 1); + g_slice_free(VirtIOSCSIVring, s->event_vring); + s->event_vring = NULL; } if (s->cmd_vrings) { for (i = 0; i < vs->conf.num_queues && s->cmd_vrings[i]; i++) { vring_teardown(&s->cmd_vrings[i]->vring, vdev, 2 + i); + g_slice_free(VirtIOSCSIVring, s->cmd_vrings[i]); + s->cmd_vrings[i] = NULL; } free(s->cmd_vrings); s->cmd_vrings = NULL; @@ -211,8 +219,6 @@ void virtio_scsi_dataplane_start(VirtIOSCSI *s) s->dataplane_starting = true; - assert(!s->blocker); - error_setg(&s->blocker, "block device is in use by data plane"); /* Set up guest notifier (irq) */ rc = k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, true); if (rc != 0) { @@ -279,8 +285,6 @@ void virtio_scsi_dataplane_stop(VirtIOSCSI *s) if (!s->dataplane_started || s->dataplane_stopping) { return; } - error_free(s->blocker); - s->blocker = NULL; s->dataplane_stopping = true; assert(s->ctx == iothread_get_aio_context(vs->conf.iothread)); diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index ef48550..c9bea06 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -13,6 +13,7 @@ * */ +#include "standard-headers/linux/virtio_ids.h" #include "hw/virtio/virtio-scsi.h" #include "qemu/error-report.h" #include "qemu/iov.h" @@ -144,9 +145,13 @@ static int virtio_scsi_parse_req(VirtIOSCSIReq *req, * * TODO: always disable this workaround for virtio 1.0 devices. */ - if ((vdev->guest_features & VIRTIO_F_ANY_LAYOUT) == 0) { - req_size = req->elem.out_sg[0].iov_len; - resp_size = req->elem.in_sg[0].iov_len; + if (!virtio_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, @@ -254,10 +259,8 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) int target; int ret = 0; - if (s->dataplane_started && blk_get_aio_context(d->conf.blk) != s->ctx) { - aio_context_acquire(s->ctx); - blk_set_aio_context(d->conf.blk, s->ctx); - aio_context_release(s->ctx); + if (s->dataplane_started) { + 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; @@ -477,7 +480,7 @@ static int virtio_scsi_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, VirtIOSCSIReq *req = hba_private; if (cmd->len == 0) { - cmd->len = MIN(VIRTIO_SCSI_CDB_SIZE, SCSI_CMD_BUF_SIZE); + cmd->len = MIN(VIRTIO_SCSI_CDB_DEFAULT_SIZE, SCSI_CMD_BUF_SIZE); memcpy(cmd->buf, buf, cmd->len); } @@ -540,14 +543,12 @@ bool virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req) virtio_scsi_complete_cmd_req(req); return false; } - if (s->dataplane_started && blk_get_aio_context(d->conf.blk) != s->ctx) { - aio_context_acquire(s->ctx); - blk_set_aio_context(d->conf.blk, s->ctx); - aio_context_release(s->ctx); + 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.cdb, req); + req->req.cmd.cdb, req); if (req->sreq->cmd.mode != SCSI_XFER_NONE && (req->sreq->cmd.mode != req->mode || @@ -645,8 +646,8 @@ static void virtio_scsi_reset(VirtIODevice *vdev) qbus_reset_all(&s->bus.qbus); s->resetting--; - vs->sense_size = VIRTIO_SCSI_SENSE_SIZE; - vs->cdb_size = VIRTIO_SCSI_CDB_SIZE; + vs->sense_size = VIRTIO_SCSI_SENSE_DEFAULT_SIZE; + vs->cdb_size = VIRTIO_SCSI_CDB_DEFAULT_SIZE; s->events_dropped = false; } @@ -748,7 +749,7 @@ static void virtio_scsi_change(SCSIBus *bus, SCSIDevice *dev, SCSISense sense) VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus); VirtIODevice *vdev = VIRTIO_DEVICE(s); - if (((vdev->guest_features >> VIRTIO_SCSI_F_CHANGE) & 1) && + if (virtio_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)); @@ -767,9 +768,12 @@ static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev, 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); } - if ((vdev->guest_features >> VIRTIO_SCSI_F_HOTPLUG) & 1) { + if (virtio_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) { virtio_scsi_push_event(s, sd, VIRTIO_SCSI_T_TRANSPORT_RESET, VIRTIO_SCSI_EVT_RESET_RESCAN); @@ -783,7 +787,7 @@ static void virtio_scsi_hotunplug(HotplugHandler *hotplug_dev, DeviceState *dev, VirtIOSCSI *s = VIRTIO_SCSI(vdev); SCSIDevice *sd = SCSI_DEVICE(dev); - if ((vdev->guest_features >> VIRTIO_SCSI_F_HOTPLUG) & 1) { + if (virtio_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) { virtio_scsi_push_event(s, sd, VIRTIO_SCSI_T_TRANSPORT_RESET, VIRTIO_SCSI_EVT_RESET_REMOVED); @@ -829,9 +833,9 @@ void virtio_scsi_common_realize(DeviceState *dev, Error **errp, virtio_cleanup(vdev); return; } - s->cmd_vqs = g_malloc0(s->conf.num_queues * sizeof(VirtQueue *)); - s->sense_size = VIRTIO_SCSI_SENSE_SIZE; - s->cdb_size = VIRTIO_SCSI_CDB_SIZE; + 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); @@ -904,6 +908,8 @@ static void virtio_scsi_device_realize(DeviceState *dev, Error **errp) virtio_scsi_save, virtio_scsi_load, s); s->migration_state_notifier.notify = virtio_scsi_migration_state_changed; add_migration_state_change_notifier(&s->migration_state_notifier); + + error_setg(&s->blocker, "block device is in use by data plane"); } static void virtio_scsi_instance_init(Object *obj) @@ -929,6 +935,8 @@ 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); remove_migration_state_change_notifier(&s->migration_state_notifier); diff --git a/hw/scsi/vmw_pvscsi.c b/hw/scsi/vmw_pvscsi.c index d3a92fb..c6148d3 100644 --- a/hw/scsi/vmw_pvscsi.c +++ b/hw/scsi/vmw_pvscsi.c @@ -42,12 +42,12 @@ #define PVSCSI_MAX_CMD_DATA_WORDS \ (sizeof(PVSCSICmdDescSetupRings)/sizeof(uint32_t)) -#define RS_GET_FIELD(rs_pa, field) \ - (ldl_le_phys(&address_space_memory, \ - rs_pa + offsetof(struct PVSCSIRingsState, field))) -#define RS_SET_FIELD(rs_pa, field, val) \ - (stl_le_phys(&address_space_memory, \ - rs_pa + offsetof(struct PVSCSIRingsState, field), val)) +#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)) #define TYPE_PVSCSI "pvscsi" #define PVSCSI(obj) OBJECT_CHECK(PVSCSIState, (obj), TYPE_PVSCSI) @@ -153,13 +153,13 @@ pvscsi_ring_init_data(PVSCSIRingInfo *m, PVSCSICmdDescSetupRings *ri) m->cmp_ring_pages_pa[i] = ri->cmpRingPPNs[i] << VMW_PAGE_SHIFT; } - RS_SET_FIELD(m->rs_pa, reqProdIdx, 0); - RS_SET_FIELD(m->rs_pa, reqConsIdx, 0); - RS_SET_FIELD(m->rs_pa, reqNumEntriesLog2, txr_len_log2); + RS_SET_FIELD(m, reqProdIdx, 0); + RS_SET_FIELD(m, reqConsIdx, 0); + RS_SET_FIELD(m, reqNumEntriesLog2, txr_len_log2); - RS_SET_FIELD(m->rs_pa, cmpProdIdx, 0); - RS_SET_FIELD(m->rs_pa, cmpConsIdx, 0); - RS_SET_FIELD(m->rs_pa, cmpNumEntriesLog2, rxr_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); @@ -185,9 +185,9 @@ pvscsi_ring_init_msg(PVSCSIRingInfo *m, PVSCSICmdDescSetupMsgRing *ri) m->msg_ring_pages_pa[i] = ri->ringPPNs[i] << VMW_PAGE_SHIFT; } - RS_SET_FIELD(m->rs_pa, msgProdIdx, 0); - RS_SET_FIELD(m->rs_pa, msgConsIdx, 0); - RS_SET_FIELD(m->rs_pa, msgNumEntriesLog2, len_log2); + 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); @@ -213,7 +213,7 @@ pvscsi_ring_cleanup(PVSCSIRingInfo *mgr) static hwaddr pvscsi_ring_pop_req_descr(PVSCSIRingInfo *mgr) { - uint32_t ready_ptr = RS_GET_FIELD(mgr->rs_pa, reqProdIdx); + uint32_t ready_ptr = RS_GET_FIELD(mgr, reqProdIdx); if (ready_ptr != mgr->consumed_ptr) { uint32_t next_ready_ptr = @@ -233,7 +233,7 @@ pvscsi_ring_pop_req_descr(PVSCSIRingInfo *mgr) static void pvscsi_ring_flush_req(PVSCSIRingInfo *mgr) { - RS_SET_FIELD(mgr->rs_pa, reqConsIdx, mgr->consumed_ptr); + RS_SET_FIELD(mgr, reqConsIdx, mgr->consumed_ptr); } static hwaddr @@ -278,14 +278,14 @@ pvscsi_ring_flush_cmp(PVSCSIRingInfo *mgr) trace_pvscsi_ring_flush_cmp(mgr->filled_cmp_ptr); - RS_SET_FIELD(mgr->rs_pa, cmpProdIdx, 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->rs_pa, msgProdIdx); - uint32_t consIdx = RS_GET_FIELD(mgr->rs_pa, msgConsIdx); + uint32_t prodIdx = RS_GET_FIELD(mgr, msgProdIdx); + uint32_t consIdx = RS_GET_FIELD(mgr, msgConsIdx); return (prodIdx - consIdx) < (mgr->msg_len_mask + 1); } @@ -298,7 +298,7 @@ pvscsi_ring_flush_msg(PVSCSIRingInfo *mgr) trace_pvscsi_ring_flush_msg(mgr->filled_msg_ptr); - RS_SET_FIELD(mgr->rs_pa, msgProdIdx, mgr->filled_msg_ptr); + RS_SET_FIELD(mgr, msgProdIdx, mgr->filled_msg_ptr); } static void diff --git a/hw/sd/milkymist-memcard.c b/hw/sd/milkymist-memcard.c index 9661eaf..2209ef1 100644 --- a/hw/sd/milkymist-memcard.c +++ b/hw/sd/milkymist-memcard.c @@ -255,6 +255,7 @@ static int milkymist_memcard_init(SysBusDevice *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); @@ -296,6 +297,8 @@ static void milkymist_memcard_class_init(ObjectClass *klass, void *data) 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 = { diff --git a/hw/sd/omap_mmc.c b/hw/sd/omap_mmc.c index 86c477d..d072dec 100644 --- a/hw/sd/omap_mmc.c +++ b/hw/sd/omap_mmc.c @@ -406,7 +406,8 @@ static void omap_mmc_write(void *opaque, hwaddr offset, struct omap_mmc_s *s = (struct omap_mmc_s *) opaque; if (size != 2) { - return omap_badwidth_write16(opaque, offset, value); + omap_badwidth_write16(opaque, offset, value); + return; } switch (offset) { diff --git a/hw/sd/pl181.c b/hw/sd/pl181.c index e704b6e..11fcd47 100644 --- a/hw/sd/pl181.c +++ b/hw/sd/pl181.c @@ -490,6 +490,7 @@ static int pl181_init(SysBusDevice *sbd) 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) { @@ -507,6 +508,8 @@ static void pl181_class_init(ObjectClass *klass, void *data) 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 = { diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index b380050..e63367b 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -74,10 +74,10 @@ #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 0ul +#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 0ul +#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 || \ @@ -198,12 +198,7 @@ static void sdhci_reset(SDHCIState *s) s->stopped_state = sdhc_not_stopped; } -static void sdhci_do_data_transfer(void *opaque) -{ - SDHCIState *s = (SDHCIState *)opaque; - - SDHCI_GET_CLASS(s)->data_transfer(s); -} +static void sdhci_data_transfer(void *opaque); static void sdhci_send_command(SDHCIState *s) { @@ -261,7 +256,7 @@ static void sdhci_send_command(SDHCIState *s) if (s->blksize && (s->cmdreg & SDHC_CMD_DATA_PRESENT)) { s->data_count = 0; - sdhci_do_data_transfer(s); + sdhci_data_transfer(s); } } @@ -367,9 +362,9 @@ static uint32_t sdhci_read_dataport(SDHCIState *s, unsigned size) /* stop at gap request */ (s->stopped_state == sdhc_gap_read && !(s->prnsts & SDHC_DAT_LINE_ACTIVE))) { - SDHCI_GET_CLASS(s)->end_data_transfer(s); + sdhci_end_transfer(s); } else { /* if there are more data, read next block from card */ - SDHCI_GET_CLASS(s)->read_block_from_card(s); + sdhci_read_block_from_card(s); } break; } @@ -410,7 +405,7 @@ static void sdhci_write_block_to_card(SDHCIState *s) if ((s->trnmod & SDHC_TRNS_MULTI) == 0 || ((s->trnmod & SDHC_TRNS_MULTI) && (s->trnmod & SDHC_TRNS_BLK_CNT_EN) && (s->blkcnt == 0))) { - SDHCI_GET_CLASS(s)->end_data_transfer(s); + sdhci_end_transfer(s); } else if (s->norintstsen & SDHC_NISEN_WBUFRDY) { s->norintsts |= SDHC_NIS_WBUFRDY; } @@ -422,7 +417,7 @@ static void sdhci_write_block_to_card(SDHCIState *s) if (s->norintstsen & SDHC_EISEN_BLKGAP) { s->norintsts |= SDHC_EIS_BLKGAP; } - SDHCI_GET_CLASS(s)->end_data_transfer(s); + sdhci_end_transfer(s); } sdhci_update_irq(s); @@ -450,7 +445,7 @@ static void sdhci_write_dataport(SDHCIState *s, uint32_t value, unsigned size) s->data_count = 0; s->prnsts &= ~SDHC_SPACE_AVAILABLE; if (s->prnsts & SDHC_DOING_WRITE) { - SDHCI_GET_CLASS(s)->write_block_to_card(s); + sdhci_write_block_to_card(s); } } } @@ -537,7 +532,7 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s) } if (s->blkcnt == 0) { - SDHCI_GET_CLASS(s)->end_data_transfer(s); + sdhci_end_transfer(s); } else { if (s->norintstsen & SDHC_NISEN_DMA) { s->norintsts |= SDHC_NIS_DMA; @@ -571,7 +566,7 @@ static void sdhci_sdma_transfer_single_block(SDHCIState *s) s->blkcnt--; } - SDHCI_GET_CLASS(s)->end_data_transfer(s); + sdhci_end_transfer(s); } typedef struct ADMADescr { @@ -758,7 +753,7 @@ static void sdhci_do_adma(SDHCIState *s) sdhci_update_irq(s); } - SDHCI_GET_CLASS(s)->end_data_transfer(s); + sdhci_end_transfer(s); return; } @@ -771,9 +766,9 @@ static void sdhci_do_adma(SDHCIState *s) /* Perform data transfer according to controller configuration */ -static void sdhci_data_transfer(SDHCIState *s) +static void sdhci_data_transfer(void *opaque) { - SDHCIClass *k = SDHCI_GET_CLASS(s); + SDHCIState *s = (SDHCIState *)opaque; if (s->trnmod & SDHC_TRNS_DMA) { switch (SDHC_DMA_TYPE(s->hostctl)) { @@ -784,9 +779,9 @@ static void sdhci_data_transfer(SDHCIState *s) } if ((s->blkcnt == 1) || !(s->trnmod & SDHC_TRNS_MULTI)) { - k->do_sdma_single(s); + sdhci_sdma_transfer_single_block(s); } else { - k->do_sdma_multi(s); + sdhci_sdma_transfer_multi_blocks(s); } break; @@ -796,7 +791,7 @@ static void sdhci_data_transfer(SDHCIState *s) break; } - k->do_adma(s); + sdhci_do_adma(s); break; case SDHC_CTRL_ADMA2_32: if (!(s->capareg & SDHC_CAN_DO_ADMA2)) { @@ -804,7 +799,7 @@ static void sdhci_data_transfer(SDHCIState *s) break; } - k->do_adma(s); + sdhci_do_adma(s); break; case SDHC_CTRL_ADMA2_64: if (!(s->capareg & SDHC_CAN_DO_ADMA2) || @@ -813,7 +808,7 @@ static void sdhci_data_transfer(SDHCIState *s) break; } - k->do_adma(s); + sdhci_do_adma(s); break; default: ERRPRINT("Unsupported DMA type\n"); @@ -823,11 +818,11 @@ static void sdhci_data_transfer(SDHCIState *s) if ((s->trnmod & SDHC_TRNS_READ) && sd_data_ready(s->card)) { s->prnsts |= SDHC_DOING_READ | SDHC_DATA_INHIBIT | SDHC_DAT_LINE_ACTIVE; - SDHCI_GET_CLASS(s)->read_block_from_card(s); + sdhci_read_block_from_card(s); } else { s->prnsts |= SDHC_DOING_WRITE | SDHC_DAT_LINE_ACTIVE | SDHC_SPACE_AVAILABLE | SDHC_DATA_INHIBIT; - SDHCI_GET_CLASS(s)->write_block_to_card(s); + sdhci_write_block_to_card(s); } } } @@ -858,8 +853,9 @@ sdhci_buff_access_is_sequential(SDHCIState *s, unsigned byte_num) return true; } -static uint32_t sdhci_read(SDHCIState *s, unsigned int offset, unsigned size) +static uint64_t sdhci_read(void *opaque, hwaddr offset, unsigned size) { + SDHCIState *s = (SDHCIState *)opaque; uint32_t ret = 0; switch (offset & ~0x3) { @@ -880,8 +876,8 @@ static uint32_t sdhci_read(SDHCIState *s, unsigned int offset, unsigned size) break; case SDHC_BDATA: if (sdhci_buff_access_is_sequential(s, offset - SDHC_BDATA)) { - ret = SDHCI_GET_CLASS(s)->bdata_read(s, size); - DPRINT_L2("read %ub: addr[0x%04x] -> %u(0x%x)\n", size, offset, + ret = sdhci_read_dataport(s, size); + DPRINT_L2("read %ub: addr[0x%04x] -> %u(0x%x)\n", size, (int)offset, ret, ret); return ret; } @@ -927,13 +923,13 @@ static uint32_t sdhci_read(SDHCIState *s, unsigned int offset, unsigned size) ret = (SD_HOST_SPECv2_VERS << 16) | sdhci_slotint(s); break; default: - ERRPRINT("bad %ub read: addr[0x%04x]\n", size, offset); + 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, offset, ret, ret); + DPRINT_L2("read %ub: addr[0x%04x] -> %u(0x%x)\n", size, (int)offset, ret, ret); return ret; } @@ -948,10 +944,10 @@ static inline void sdhci_blkgap_write(SDHCIState *s, uint8_t value) (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_GET_CLASS(s)->read_block_from_card(s); + sdhci_read_block_from_card(s); } else { s->prnsts |= SDHC_DAT_LINE_ACTIVE | SDHC_DOING_WRITE; - SDHCI_GET_CLASS(s)->write_block_to_card(s); + sdhci_write_block_to_card(s); } s->stopped_state = sdhc_not_stopped; } else if (!s->stopped_state && (value & SDHC_STOP_AT_GAP_REQ)) { @@ -967,7 +963,7 @@ static inline void sdhci_reset_write(SDHCIState *s, uint8_t value) { switch (value) { case SDHC_RESET_ALL: - DEVICE_GET_CLASS(s)->reset(DEVICE(s)); + sdhci_reset(s); break; case SDHC_RESET_CMD: s->prnsts &= ~SDHC_CMD_INHIBIT; @@ -987,10 +983,12 @@ static inline void sdhci_reset_write(SDHCIState *s, uint8_t value) } static void -sdhci_write(SDHCIState *s, unsigned int offset, uint32_t value, unsigned size) +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) { @@ -1000,7 +998,7 @@ sdhci_write(SDHCIState *s, unsigned int offset, uint32_t value, unsigned size) /* 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_GET_CLASS(s)->do_sdma_multi(s); + sdhci_sdma_transfer_multi_blocks(s); } break; case SDHC_BLKSIZE: @@ -1022,15 +1020,15 @@ sdhci_write(SDHCIState *s, unsigned int offset, uint32_t value, unsigned size) MASKED_WRITE(s->cmdreg, mask >> 16, value >> 16); /* Writing to the upper byte of CMDREG triggers SD command generation */ - if ((mask & 0xFF000000) || !SDHCI_GET_CLASS(s)->can_issue_command(s)) { + if ((mask & 0xFF000000) || !sdhci_can_issue_command(s)) { break; } - SDHCI_GET_CLASS(s)->send_command(s); + sdhci_send_command(s); break; case SDHC_BDATA: if (sdhci_buff_access_is_sequential(s, offset - SDHC_BDATA)) { - SDHCI_GET_CLASS(s)->bdata_write(s, value >> shift, size); + sdhci_write_dataport(s, value >> shift, size); } break; case SDHC_HOSTCTL: @@ -1111,32 +1109,16 @@ sdhci_write(SDHCIState *s, unsigned int offset, uint32_t value, unsigned size) break; default: ERRPRINT("bad %ub write offset: addr[0x%04x] <- %u(0x%x)\n", - size, offset, value >> shift, value >> shift); + size, (int)offset, value >> shift, value >> shift); break; } DPRINT_L2("write %ub: addr[0x%04x] <- %u(0x%x)\n", - size, offset, value >> shift, value >> shift); -} - -static uint64_t -sdhci_readfn(void *opaque, hwaddr offset, unsigned size) -{ - SDHCIState *s = (SDHCIState *)opaque; - - return SDHCI_GET_CLASS(s)->mem_read(s, offset, size); -} - -static void -sdhci_writefn(void *opaque, hwaddr off, uint64_t val, unsigned sz) -{ - SDHCIState *s = (SDHCIState *)opaque; - - SDHCI_GET_CLASS(s)->mem_write(s, off, val, sz); + size, (int)offset, value >> shift, value >> shift); } static const MemoryRegionOps sdhci_mmio_ops = { - .read = sdhci_readfn, - .write = sdhci_writefn, + .read = sdhci_read, + .write = sdhci_write, .valid = { .min_access_size = 1, .max_access_size = 4, @@ -1160,11 +1142,11 @@ static inline unsigned int sdhci_get_fifolen(SDHCIState *s) } } -static void sdhci_initfn(Object *obj) +static void sdhci_initfn(SDHCIState *s) { - SDHCIState *s = SDHCI(obj); DriveInfo *di; + /* FIXME use a qdev drive property instead of drive_get_next() */ di = drive_get_next(IF_SD); s->card = sd_init(di ? blk_by_legacy_dinfo(di) : NULL, false); if (s->card == NULL) { @@ -1175,13 +1157,11 @@ static void sdhci_initfn(Object *obj) sd_set_cb(s->card, s->ro_cb, s->eject_cb); s->insert_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sdhci_raise_insertion_irq, s); - s->transfer_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sdhci_do_data_transfer, s); + s->transfer_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sdhci_data_transfer, s); } -static void sdhci_uninitfn(Object *obj) +static void sdhci_uninitfn(SDHCIState *s) { - SDHCIState *s = SDHCI(obj); - timer_del(s->insert_timer); timer_free(s->insert_timer); timer_del(s->transfer_timer); @@ -1226,8 +1206,8 @@ const VMStateDescription sdhci_vmstate = { VMSTATE_UINT64(admasysaddr, SDHCIState), VMSTATE_UINT8(stopped_state, SDHCIState), VMSTATE_VBUFFER_UINT32(fifo_buffer, SDHCIState, 1, NULL, 0, buf_maxsz), - VMSTATE_TIMER(insert_timer, SDHCIState), - VMSTATE_TIMER(transfer_timer, SDHCIState), + VMSTATE_TIMER_PTR(insert_timer, SDHCIState), + VMSTATE_TIMER_PTR(transfer_timer, SDHCIState), VMSTATE_END_OF_LIST() } }; @@ -1241,9 +1221,65 @@ static Property sdhci_properties[] = { DEFINE_PROP_END_OF_LIST(), }; -static void sdhci_realize(DeviceState *dev, Error ** errp) +static void sdhci_pci_realize(PCIDevice *dev, Error **errp) { - SDHCIState *s = SDHCI(dev); + 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_properties; + /* Reason: realize() method uses drive_get_next() */ + dc->cannot_instantiate_with_device_add_yet = true; +} + +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 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); @@ -1254,51 +1290,30 @@ static void sdhci_realize(DeviceState *dev, Error ** errp) sysbus_init_mmio(sbd, &s->iomem); } -static void sdhci_generic_reset(DeviceState *ds) -{ - SDHCIState *s = SDHCI(ds); - SDHCI_GET_CLASS(s)->reset(s); -} - -static void sdhci_class_init(ObjectClass *klass, void *data) +static void sdhci_sysbus_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SDHCIClass *k = SDHCI_CLASS(klass); dc->vmsd = &sdhci_vmstate; dc->props = sdhci_properties; - dc->reset = sdhci_generic_reset; - dc->realize = sdhci_realize; - - k->reset = sdhci_reset; - k->mem_read = sdhci_read; - k->mem_write = sdhci_write; - k->send_command = sdhci_send_command; - k->can_issue_command = sdhci_can_issue_command; - k->data_transfer = sdhci_data_transfer; - k->end_data_transfer = sdhci_end_transfer; - k->do_sdma_single = sdhci_sdma_transfer_single_block; - k->do_sdma_multi = sdhci_sdma_transfer_multi_blocks; - k->do_adma = sdhci_do_adma; - k->read_block_from_card = sdhci_read_block_from_card; - k->write_block_to_card = sdhci_write_block_to_card; - k->bdata_read = sdhci_read_dataport; - k->bdata_write = sdhci_write_dataport; + dc->realize = sdhci_sysbus_realize; + /* Reason: instance_init() method uses drive_get_next() */ + dc->cannot_instantiate_with_device_add_yet = true; } -static const TypeInfo sdhci_type_info = { - .name = TYPE_SDHCI, +static const TypeInfo sdhci_sysbus_info = { + .name = TYPE_SYSBUS_SDHCI, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SDHCIState), - .instance_init = sdhci_initfn, - .instance_finalize = sdhci_uninitfn, - .class_init = sdhci_class_init, - .class_size = sizeof(SDHCIClass) + .instance_init = sdhci_sysbus_init, + .instance_finalize = sdhci_sysbus_finalize, + .class_init = sdhci_sysbus_class_init, }; static void sdhci_register_types(void) { - type_register_static(&sdhci_type_info); + type_register_static(&sdhci_pci_info); + type_register_static(&sdhci_sysbus_info); } type_init(sdhci_register_types) diff --git a/hw/sd/sdhci.h b/hw/sd/sdhci.h index a560c3c..3352d23 100644 --- a/hw/sd/sdhci.h +++ b/hw/sd/sdhci.h @@ -26,6 +26,7 @@ #define SDHCI_H #include "qemu-common.h" +#include "hw/pci/pci.h" #include "hw/sysbus.h" #include "hw/sd.h" @@ -232,7 +233,10 @@ enum { /* SD/MMC host controller state */ typedef struct SDHCIState { - SysBusDevice busdev; + union { + PCIDevice pcidev; + SysBusDevice busdev; + }; SDState *card; MemoryRegion iomem; @@ -279,34 +283,13 @@ typedef struct SDHCIState { /* RO Host Controller Version Register always reads as 0x2401 */ } SDHCIState; -typedef struct SDHCIClass { - SysBusDeviceClass busdev_class; - - void (*reset)(SDHCIState *s); - uint32_t (*mem_read)(SDHCIState *s, unsigned int offset, unsigned size); - void (*mem_write)(SDHCIState *s, unsigned int offset, uint32_t value, - unsigned size); - void (*send_command)(SDHCIState *s); - bool (*can_issue_command)(SDHCIState *s); - void (*data_transfer)(SDHCIState *s); - void (*end_data_transfer)(SDHCIState *s); - void (*do_sdma_single)(SDHCIState *s); - void (*do_sdma_multi)(SDHCIState *s); - void (*do_adma)(SDHCIState *s); - void (*read_block_from_card)(SDHCIState *s); - void (*write_block_to_card)(SDHCIState *s); - uint32_t (*bdata_read)(SDHCIState *s, unsigned size); - void (*bdata_write)(SDHCIState *s, uint32_t value, unsigned size); -} SDHCIClass; - extern const VMStateDescription sdhci_vmstate; -#define TYPE_SDHCI "generic-sdhci" -#define SDHCI(obj) \ - OBJECT_CHECK(SDHCIState, (obj), TYPE_SDHCI) -#define SDHCI_CLASS(klass) \ - OBJECT_CLASS_CHECK(SDHCIClass, (klass), TYPE_SDHCI) -#define SDHCI_GET_CLASS(obj) \ - OBJECT_GET_CLASS(SDHCIClass, (obj), TYPE_SDHCI) +#define TYPE_PCI_SDHCI "sdhci-pci" +#define PCI_SDHCI(obj) OBJECT_CHECK(SDHCIState, (obj), TYPE_PCI_SDHCI) + +#define TYPE_SYSBUS_SDHCI "generic-sdhci" +#define SYSBUS_SDHCI(obj) \ + OBJECT_CHECK(SDHCIState, (obj), TYPE_SYSBUS_SDHCI) #endif /* SDHCI_H */ diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c index a71fbca..e4b2d4f 100644 --- a/hw/sd/ssi-sd.c +++ b/hw/sd/ssi-sd.c @@ -255,6 +255,7 @@ static int ssi_sd_init(SSISlave *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) { diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c index 12f44d2..d1d0847 100644 --- a/hw/sh4/r2d.c +++ b/hw/sh4/r2d.c @@ -301,7 +301,7 @@ static void r2d_init(MachineState *machine) "rtl8139", i==0 ? "2" : NULL); /* USB keyboard */ - usbdevice_create("keyboard"); + usb_create_simple(usb_bus_find(-1), "usb-kbd"); /* Todo: register on board registers */ memset(&boot_params, 0, sizeof(boot_params)); diff --git a/hw/sparc/leon3.c b/hw/sparc/leon3.c index 751392e..7f5dcd6 100644 --- a/hw/sparc/leon3.c +++ b/hw/sparc/leon3.c @@ -151,8 +151,7 @@ static void leon3_generic_hw_init(MachineState *machine) exit(1); } - memory_region_init_ram(ram, NULL, "leon3.ram", ram_size, &error_abort); - vmstate_register_ram_global(ram); + memory_region_allocate_system_memory(ram, NULL, "leon3.ram", ram_size); memory_region_add_subregion(address_space_mem, 0x40000000, ram); /* Allocate BIOS */ @@ -186,6 +185,7 @@ static void leon3_generic_hw_init(MachineState *machine) fprintf(stderr, "Can't read bios image %s\n", filename); exit(1); } + g_free(filename); /* Can directly load an application. */ if (kernel_filename != NULL) { diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index 8273199..a69bf2d 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -121,13 +121,13 @@ void DMA_register_channel (int nchan, { } -static int fw_cfg_boot_set(void *opaque, const char *boot_device) +static void fw_cfg_boot_set(void *opaque, const char *boot_device, + Error **errp) { fw_cfg_add_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]); - return 0; } -static void nvram_init(M48t59State *nvram, uint8_t *macaddr, +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, @@ -137,6 +137,7 @@ static void nvram_init(M48t59State *nvram, uint8_t *macaddr, 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)); @@ -170,19 +171,20 @@ static void nvram_init(M48t59State *nvram, uint8_t *macaddr, Sun_init_header((struct Sun_nvram *)&image[0x1fd8], macaddr, nvram_machine_id); - for (i = 0; i < sizeof(image); i++) - m48t59_write(nvram, i, image[i]); + for (i = 0; i < sizeof(image); i++) { + (k->write)(nvram, i, image[i]); + } } static DeviceState *slavio_intctl; -void sun4m_pic_info(Monitor *mon, const QDict *qdict) +void sun4m_hmp_info_pic(Monitor *mon, const QDict *qdict) { if (slavio_intctl) slavio_pic_info(mon, slavio_intctl); } -void sun4m_irq_info(Monitor *mon, const QDict *qdict) +void sun4m_hmp_info_irq(Monitor *mon, const QDict *qdict) { if (slavio_intctl) slavio_irq_info(mon, slavio_intctl); @@ -803,9 +805,8 @@ static int ram_init1(SysBusDevice *dev) { RamDevice *d = SUN4M_RAM(dev); - memory_region_init_ram(&d->ram, OBJECT(d), "sun4m.ram", d->size, - &error_abort); - vmstate_register_ram_global(&d->ram); + memory_region_allocate_system_memory(&d->ram, OBJECT(d), "sun4m.ram", + d->size); sysbus_init_mmio(dev, &d->ram); return 0; } @@ -1012,7 +1013,7 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, lance_init(&nd_table[0], hwdef->le_base, ledma, ledma_irq); - nvram = m48t59_init(slavio_irq[0], hwdef->nvram_base, 0, 0x2000, 8); + 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); @@ -1084,9 +1085,8 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, ecc_init(hwdef->ecc_base, slavio_irq[28], hwdef->ecc_version); - fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2); + 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_i32(fw_cfg, FW_CFG_ID, 1); 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); diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index f42112c..6f34e87 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -124,13 +124,13 @@ void DMA_register_channel (int nchan, { } -static int fw_cfg_boot_set(void *opaque, const char *boot_device) +static void fw_cfg_boot_set(void *opaque, const char *boot_device, + Error **errp) { fw_cfg_add_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]); - return 0; } -static int sun4u_NVRAM_set_params(M48t59State *nvram, uint16_t NVRAM_size, +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, @@ -144,6 +144,7 @@ static int sun4u_NVRAM_set_params(M48t59State *nvram, uint16_t NVRAM_size, 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)); @@ -176,8 +177,9 @@ static int sun4u_NVRAM_set_params(M48t59State *nvram, uint16_t NVRAM_size, Sun_init_header((struct Sun_nvram *)&image[0x1fd8], macaddr, 0x80); - for (i = 0; i < sizeof(image); i++) - m48t59_write(nvram, i, image[i]); + for (i = 0; i < sizeof(image); i++) { + (k->write)(nvram, i, image[i]); + } return 0; } @@ -596,7 +598,8 @@ pci_ebus_init1(PCIDevice *pci_dev) { EbusState *s = DO_UPCAST(EbusState, pci_dev, pci_dev); - isa_bus_new(&pci_dev->qdev, pci_address_space_io(pci_dev)); + isa_bus_new(DEVICE(pci_dev), get_system_memory(), + pci_address_space_io(pci_dev)); pci_dev->config[0x04] = 0x06; // command = bus master, pci mem pci_dev->config[0x05] = 0x00; @@ -609,7 +612,7 @@ pci_ebus_init1(PCIDevice *pci_dev) 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, 0x1000); + 0, 0x4000); pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->bar1); return 0; } @@ -817,11 +820,12 @@ static void sun4uv_init(MemoryRegion *address_space_mem, const struct hwdef *hwdef) { SPARCCPU *cpu; - M48t59State *nvram; + 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]; @@ -849,17 +853,9 @@ static void sun4uv_init(MemoryRegion *address_space_mem, NULL, 115200, serial_hds[i], DEVICE_BIG_ENDIAN); i++; } - for(; i < MAX_SERIAL_PORTS; i++) { - if (serial_hds[i]) { - serial_isa_init(isa_bus, i, serial_hds[i]); - } - } - for(i = 0; i < MAX_PARALLEL_PORTS; i++) { - if (parallel_hds[i]) { - parallel_init(isa_bus, i, parallel_hds[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); @@ -873,8 +869,13 @@ static void sun4uv_init(MemoryRegion *address_space_mem, fd[i] = drive_get(IF_FLOPPY, 0, i); } fdctrl_init_isa(isa_bus, fd); - nvram = m48t59_init_isa(isa_bus, 0x0074, NVRAM_SIZE, 59); + /* 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, @@ -892,9 +893,8 @@ static void sun4uv_init(MemoryRegion *address_space_mem, graphic_width, graphic_height, graphic_depth, (uint8_t *)&nd_table[0].macaddr); - fw_cfg = fw_cfg_init(BIOS_CFG_IOPORT, BIOS_CFG_IOPORT + 1, 0, 0); + 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_i32(fw_cfg, FW_CFG_ID, 1); 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); diff --git a/hw/ssi/omap_spi.c b/hw/ssi/omap_spi.c index 0ed3b11..119e325 100644 --- a/hw/ssi/omap_spi.c +++ b/hw/ssi/omap_spi.c @@ -226,7 +226,8 @@ static void omap_mcspi_write(void *opaque, hwaddr addr, int ch = 0; if (size != 4) { - return omap_badwidth_write32(opaque, addr, value); + omap_badwidth_write32(opaque, addr, value); + return; } switch (addr) { diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs index 2c86c3d..133bd0d 100644 --- a/hw/timer/Makefile.objs +++ b/hw/timer/Makefile.objs @@ -31,3 +31,5 @@ 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 diff --git a/hw/timer/a9gtimer.c b/hw/timer/a9gtimer.c index a0656d5..dd4aae8 100644 --- a/hw/timer/a9gtimer.c +++ b/hw/timer/a9gtimer.c @@ -121,7 +121,7 @@ static void a9_gtimer_update_no_sync(void *opaque) { A9GTimerState *s = A9_GTIMER(opaque); - return a9_gtimer_update(s, false); + a9_gtimer_update(s, false); } static uint64_t a9_gtimer_read(void *opaque, hwaddr addr, unsigned size) @@ -289,7 +289,7 @@ static void a9_gtimer_realize(DeviceState *dev, Error **errp) 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\n", + error_setg(errp, "%s: num-cpu must be between 1 and %d", __func__, A9_GTIMER_MAX_CPUS); return; } @@ -328,7 +328,7 @@ static const VMStateDescription vmstate_a9_gtimer = { .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { - VMSTATE_TIMER(timer, A9GTimerState), + VMSTATE_TIMER_PTR(timer, A9GTimerState), VMSTATE_UINT64(counter, A9GTimerState), VMSTATE_UINT64(ref_counter, A9GTimerState), VMSTATE_UINT64(cpu_ref_time, A9GTimerState), diff --git a/hw/timer/arm_mptimer.c b/hw/timer/arm_mptimer.c index 35a0a23..8b93b3c 100644 --- a/hw/timer/arm_mptimer.c +++ b/hw/timer/arm_mptimer.c @@ -246,7 +246,7 @@ static const VMStateDescription vmstate_timerblock = { VMSTATE_UINT32(control, TimerBlock), VMSTATE_UINT32(status, TimerBlock), VMSTATE_INT64(tick, TimerBlock), - VMSTATE_TIMER(timer, TimerBlock), + VMSTATE_TIMER_PTR(timer, TimerBlock), VMSTATE_END_OF_LIST() } }; diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index e160e8f..78d86be 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -1,5 +1,5 @@ /* - * High Precisition Event Timer emulation + * High Precision Event Timer emulation * * Copyright (c) 2007 Alexander Graf * Copyright (c) 2008 IBM Corporation @@ -299,7 +299,7 @@ static const VMStateDescription vmstate_hpet_timer = { VMSTATE_UINT64(fsb, HPETTimer), VMSTATE_UINT64(period, HPETTimer), VMSTATE_UINT8(wrap_flag, HPETTimer), - VMSTATE_TIMER(qemu_timer, HPETTimer), + VMSTATE_TIMER_PTR(qemu_timer, HPETTimer), VMSTATE_END_OF_LIST() } }; diff --git a/hw/timer/i8254.c b/hw/timer/i8254.c index 3450c98..9b65a33 100644 --- a/hw/timer/i8254.c +++ b/hw/timer/i8254.c @@ -196,6 +196,12 @@ static uint64_t pit_ioport_read(void *opaque, hwaddr addr, 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; diff --git a/hw/timer/m48t59.c b/hw/timer/m48t59.c index 31509d5..8ab683d 100644 --- a/hw/timer/m48t59.c +++ b/hw/timer/m48t59.c @@ -2,6 +2,7 @@ * 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 @@ -37,12 +38,35 @@ #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 @@ -50,12 +74,12 @@ * http://www.st.com/stonline/products/literature/od/7001/m48t59y.pdf */ -struct M48t59State { +typedef struct M48t59State { /* Hardware parameters */ qemu_irq IRQ; MemoryRegion iomem; - uint32_t io_base; uint32_t size; + int32_t base_year; /* RTC management */ time_t time_offset; time_t stop_time; @@ -70,28 +94,51 @@ struct M48t59State { /* NVRAM storage */ uint16_t addr; uint8_t lock; -}; - -#define TYPE_ISA_M48T59 "m48t59_isa" -#define ISA_M48T59(obj) \ - OBJECT_CHECK(M48t59ISAState, (obj), TYPE_ISA_M48T59) +} M48t59State; -typedef struct M48t59ISAState { +typedef struct M48txxISAState { ISADevice parent_obj; - M48t59State state; + uint32_t io_base; MemoryRegion io; -} M48t59ISAState; +} M48txxISAState; -#define SYSBUS_M48T59(obj) \ - OBJECT_CHECK(M48t59SysBusState, (obj), TYPE_SYSBUS_M48T59) +typedef struct M48txxISADeviceClass { + ISADeviceClass parent_class; + M48txxInfo info; +} M48txxISADeviceClass; -typedef struct M48t59SysBusState { +typedef struct M48txxSysBusState { SysBusDevice parent_obj; - M48t59State state; MemoryRegion io; -} M48t59SysBusState; +} 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 */ @@ -198,9 +245,8 @@ static void set_up_watchdog(M48t59State *NVRAM, uint8_t value) } /* Direct access to NVRAM */ -void m48t59_write (void *opaque, uint32_t addr, uint32_t val) +static void m48t59_write(M48t59State *NVRAM, uint32_t addr, uint32_t val) { - M48t59State *NVRAM = opaque; struct tm tm; int tmp; @@ -346,11 +392,7 @@ void m48t59_write (void *opaque, uint32_t addr, uint32_t val) tmp = from_bcd(val); if (tmp >= 0 && tmp <= 99) { get_time(NVRAM, &tm); - if (NVRAM->model == 8) { - tm.tm_year = from_bcd(val) + 68; // Base year is 1968 - } else { - tm.tm_year = from_bcd(val); - } + tm.tm_year = from_bcd(val) + NVRAM->base_year - 1900; set_time(NVRAM, &tm); } break; @@ -368,9 +410,8 @@ void m48t59_write (void *opaque, uint32_t addr, uint32_t val) } } -uint32_t m48t59_read (void *opaque, uint32_t addr) +static uint32_t m48t59_read(M48t59State *NVRAM, uint32_t addr) { - M48t59State *NVRAM = opaque; struct tm tm; uint32_t retval = 0xFF; @@ -453,11 +494,7 @@ uint32_t m48t59_read (void *opaque, uint32_t addr) case 0x07FF: /* year */ get_time(NVRAM, &tm); - if (NVRAM->model == 8) { - retval = to_bcd(tm.tm_year - 68); // Base year is 1968 - } else { - retval = to_bcd(tm.tm_year); - } + retval = to_bcd((tm.tm_year + 1900 - NVRAM->base_year) % 100); break; default: /* Check lock registers state */ @@ -477,10 +514,8 @@ uint32_t m48t59_read (void *opaque, uint32_t addr) return retval; } -void m48t59_toggle_lock (void *opaque, int lock) +static void m48t59_toggle_lock(M48t59State *NVRAM, int lock) { - M48t59State *NVRAM = opaque; - NVRAM->lock ^= 1 << lock; } @@ -616,7 +651,7 @@ static void m48t59_reset_common(M48t59State *NVRAM) static void m48t59_reset_isa(DeviceState *d) { - M48t59ISAState *isa = ISA_M48T59(d); + M48txxISAState *isa = M48TXX_ISA(d); M48t59State *NVRAM = &isa->state; m48t59_reset_common(NVRAM); @@ -624,7 +659,7 @@ static void m48t59_reset_isa(DeviceState *d) static void m48t59_reset_sysbus(DeviceState *d) { - M48t59SysBusState *sys = SYSBUS_M48T59(d); + M48txxSysBusState *sys = M48TXX_SYS_BUS(d); M48t59State *NVRAM = &sys->state; m48t59_reset_common(NVRAM); @@ -641,58 +676,63 @@ static const MemoryRegionOps m48t59_io_ops = { }; /* Initialisation routine */ -M48t59State *m48t59_init(qemu_irq IRQ, hwaddr mem_base, - uint32_t io_base, uint16_t size, int model) +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; - M48t59SysBusState *d; - M48t59State *state; - - dev = qdev_create(NULL, TYPE_SYSBUS_M48T59); - qdev_prop_set_uint32(dev, "model", model); - qdev_prop_set_uint32(dev, "size", size); - qdev_prop_set_uint32(dev, "io_base", io_base); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - d = SYSBUS_M48T59(dev); - state = &d->state; - sysbus_connect_irq(s, 0, IRQ); - memory_region_init_io(&d->io, OBJECT(d), &m48t59_io_ops, state, - "m48t59", 4); - if (io_base != 0) { - memory_region_add_subregion(get_system_io(), io_base, &d->io); - } - if (mem_base != 0) { - sysbus_mmio_map(s, 0, mem_base); + 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); } - return state; + assert(false); + return NULL; } -M48t59State *m48t59_init_isa(ISABus *bus, uint32_t io_base, uint16_t size, - int model) +Nvram *m48t59_init_isa(ISABus *bus, uint32_t io_base, uint16_t size, + int base_year, int model) { - M48t59ISAState *d; - ISADevice *isadev; DeviceState *dev; - M48t59State *s; - - isadev = isa_create(bus, TYPE_ISA_M48T59); - dev = DEVICE(isadev); - qdev_prop_set_uint32(dev, "model", model); - qdev_prop_set_uint32(dev, "size", size); - qdev_prop_set_uint32(dev, "io_base", io_base); - qdev_init_nofail(dev); - d = ISA_M48T59(isadev); - s = &d->state; - - memory_region_init_io(&d->io, OBJECT(d), &m48t59_io_ops, s, "m48t59", 4); - if (io_base != 0) { - isa_register_ioport(isadev, &d->io, io_base); + 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); } - return s; + assert(false); + return NULL; } static void m48t59_realize_common(M48t59State *s, Error **errp) @@ -709,25 +749,38 @@ static void m48t59_realize_common(M48t59State *s, Error **errp) static void m48t59_isa_realize(DeviceState *dev, Error **errp) { + M48txxISADeviceClass *u = M48TXX_ISA_GET_CLASS(dev); ISADevice *isadev = ISA_DEVICE(dev); - M48t59ISAState *d = ISA_M48T59(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) { - M48t59SysBusState *d = SYSBUS_M48T59(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, OBJECT(d), &nvram_ops, s, - "m48t59.nvram", s->size); + 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); @@ -737,59 +790,157 @@ static int m48t59_init1(SysBusDevice *dev) 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_UINT32("size", M48t59ISAState, state.size, -1), - DEFINE_PROP_UINT32("model", M48t59ISAState, state.model, -1), - DEFINE_PROP_UINT32("io_base", M48t59ISAState, state.io_base, 0), + 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 m48t59_isa_class_init(ObjectClass *klass, void *data) +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; - /* Reason: needs to be wired up by m48t59_init_isa() */ - dc->cannot_instantiate_with_device_add_yet = true; + nc->read = m48txx_isa_read; + nc->write = m48txx_isa_write; + nc->toggle_lock = m48txx_isa_toggle_lock; } -static const TypeInfo m48t59_isa_info = { - .name = TYPE_ISA_M48T59, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(M48t59ISAState), - .class_init = m48t59_isa_class_init, -}; +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_properties[] = { - DEFINE_PROP_UINT32("size", M48t59SysBusState, state.size, -1), - DEFINE_PROP_UINT32("model", M48t59SysBusState, state.model, -1), - DEFINE_PROP_UINT32("io_base", M48t59SysBusState, state.io_base, 0), +static Property m48t59_sysbus_properties[] = { + DEFINE_PROP_INT32("base-year", M48txxSysBusState, state.base_year, 0), DEFINE_PROP_END_OF_LIST(), }; -static void m48t59_class_init(ObjectClass *klass, void *data) +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_properties; + 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 m48t59_info = { - .name = TYPE_SYSBUS_M48T59, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(M48t59SysBusState), - .class_init = m48t59_class_init, +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) { - type_register_static(&m48t59_info); - type_register_static(&m48t59_isa_info); + 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/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c index f18d128..f2b77fa 100644 --- a/hw/timer/mc146818rtc.c +++ b/hw/timer/mc146818rtc.c @@ -734,7 +734,7 @@ static int rtc_post_load(void *opaque, int version_id) } static const VMStateDescription vmstate_rtc_irq_reinject_on_ack_count = { - .name = "irq_reinject_on_ack_count", + .name = "mc146818rtc/irq_reinject_on_ack_count", .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { @@ -758,7 +758,7 @@ static const VMStateDescription vmstate_rtc = { VMSTATE_BUFFER(cmos_data, RTCState), VMSTATE_UINT8(cmos_index, RTCState), VMSTATE_UNUSED(7*4), - VMSTATE_TIMER(periodic_timer, RTCState), + VMSTATE_TIMER_PTR(periodic_timer, RTCState), VMSTATE_INT64(next_periodic_time, RTCState), VMSTATE_UNUSED(3*8), VMSTATE_UINT32_V(irq_coalesced, RTCState, 2), @@ -766,7 +766,7 @@ static const VMStateDescription vmstate_rtc = { VMSTATE_UINT64_V(base_rtc, RTCState, 3), VMSTATE_UINT64_V(last_update, RTCState, 3), VMSTATE_INT64_V(offset, RTCState, 3), - VMSTATE_TIMER_V(update_timer, RTCState, 3), + VMSTATE_TIMER_PTR_V(update_timer, RTCState, 3), VMSTATE_UINT64_V(next_alarm_time, RTCState, 3), VMSTATE_END_OF_LIST() }, @@ -831,49 +831,12 @@ static const MemoryRegionOps cmos_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static void rtc_get_date(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void rtc_get_date(Object *obj, struct tm *current_tm, Error **errp) { - Error *err = NULL; RTCState *s = MC146818_RTC(obj); - struct tm current_tm; rtc_update_time(s); - rtc_get_time(s, ¤t_tm); - visit_start_struct(v, NULL, "struct tm", name, 0, &err); - if (err) { - goto out; - } - visit_type_int32(v, ¤t_tm.tm_year, "tm_year", &err); - if (err) { - goto out_end; - } - visit_type_int32(v, ¤t_tm.tm_mon, "tm_mon", &err); - if (err) { - goto out_end; - } - visit_type_int32(v, ¤t_tm.tm_mday, "tm_mday", &err); - if (err) { - goto out_end; - } - visit_type_int32(v, ¤t_tm.tm_hour, "tm_hour", &err); - if (err) { - goto out_end; - } - visit_type_int32(v, ¤t_tm.tm_min, "tm_min", &err); - if (err) { - goto out_end; - } - visit_type_int32(v, ¤t_tm.tm_sec, "tm_sec", &err); - if (err) { - goto out_end; - } -out_end: - error_propagate(errp, err); - err = NULL; - visit_end_struct(v, errp); -out: - error_propagate(errp, err); + rtc_get_time(s, current_tm); } static void rtc_realizefn(DeviceState *dev, Error **errp) @@ -932,8 +895,7 @@ static void rtc_realizefn(DeviceState *dev, Error **errp) qdev_set_legacy_instance_id(dev, base, 3); qemu_register_reset(rtc_reset, s); - object_property_add(OBJECT(s), "date", "struct tm", - rtc_get_date, NULL, NULL, s, NULL); + object_property_add_tm(OBJECT(s), "date", rtc_get_date, NULL); object_property_add_alias(qdev_get_machine(), "rtc-time", OBJECT(s), "date", NULL); diff --git a/hw/timer/omap_gptimer.c b/hw/timer/omap_gptimer.c index b7f3d49..b8c8c01 100644 --- a/hw/timer/omap_gptimer.c +++ b/hw/timer/omap_gptimer.c @@ -444,7 +444,7 @@ static void omap_gp_timer_writeh(void *opaque, hwaddr addr, struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; if (addr & 2) - return omap_gp_timer_write(opaque, addr, (value << 16) | s->writeh); + omap_gp_timer_write(opaque, addr, (value << 16) | s->writeh); else s->writeh = (uint16_t) value; } diff --git a/hw/timer/stm32f2xx_timer.c b/hw/timer/stm32f2xx_timer.c new file mode 100644 index 0000000..ecadf9d --- /dev/null +++ b/hw/timer/stm32f2xx_timer.c @@ -0,0 +1,328 @@ +/* + * 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 "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/hw/tpm/tpm_int.h b/hw/tpm/tpm_int.h index 2f582ca..2b35fe2 100644 --- a/hw/tpm/tpm_int.h +++ b/hw/tpm/tpm_int.h @@ -62,6 +62,7 @@ struct tpm_resp_hdr { #define TPM_FAIL 9 +#define TPM_ORD_ContinueSelfTest 0x53 #define TPM_ORD_GetTicks 0xf1 #endif /* TPM_TPM_INT_H */ diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c index 56e9e0f..2a45071 100644 --- a/hw/tpm/tpm_passthrough.c +++ b/hw/tpm/tpm_passthrough.c @@ -112,21 +112,38 @@ static void tpm_write_fatal_error_response(uint8_t *out, uint32_t out_len) } } +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) + 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)\n", + "to TPM: %s (%i)", strerror(errno), errno); } goto err_exit; @@ -139,14 +156,19 @@ static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt, 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)\n", + "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\n"); + "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: @@ -160,13 +182,15 @@ err_exit: } static int tpm_passthrough_unix_transfer(TPMPassthruState *tpm_pt, - const TPMLocality *locty_data) + 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); + locty_data->r_buffer.size, + selftest_done); } static void tpm_passthrough_worker_thread(gpointer data, @@ -175,16 +199,19 @@ static void tpm_passthrough_worker_thread(gpointer 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); + thr_parms->tpm_state->locty_data, + &selftest_done); thr_parms->recv_data_callback(thr_parms->tpm_state, - thr_parms->tpm_state->locty_number); + thr_parms->tpm_state->locty_number, + selftest_done); break; case TPM_BACKEND_CMD_INIT: case TPM_BACKEND_CMD_END: @@ -282,7 +309,7 @@ static void tpm_passthrough_cancel_cmd(TPMBackend *tb) if (tpm_pt->cancel_fd >= 0) { n = write(tpm_pt->cancel_fd, "-", 1); if (n != 1) { - error_report("Canceling TPM command failed: %s\n", + error_report("Canceling TPM command failed: %s", strerror(errno)); } else { tpm_pt->tpm_op_canceled = true; @@ -400,9 +427,7 @@ static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb) const char *value; value = qemu_opt_get(opts, "cancel-path"); - if (value) { - tb->cancel_path = g_strdup(value); - } + tb->cancel_path = g_strdup(value); value = qemu_opt_get(opts, "path"); if (!value) { @@ -415,13 +440,13 @@ static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb) 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\n", + error_report("Cannot access TPM device using '%s': %s", tpm_pt->tpm_dev, strerror(errno)); goto err_free_parameters; } if (tpm_passthrough_test_tpmdev(tpm_pt->tpm_fd)) { - error_report("'%s' is not a TPM device.\n", + error_report("'%s' is not a TPM device.", tpm_pt->tpm_dev); goto err_close_tpmdev; } diff --git a/hw/tpm/tpm_tis.c b/hw/tpm/tpm_tis.c index c0e7cd7..815c8ea 100644 --- a/hw/tpm/tpm_tis.c +++ b/hw/tpm/tpm_tis.c @@ -14,7 +14,7 @@ * * Implementation of the TIS interface according to specs found at * http://www.trustedcomputinggroup.org. This implementation currently - * supports version 1.21, revision 1.0. + * supports version 1.3, 21 March 2013 * In the developers menu choose the PC Client section then find the TIS * specification. */ @@ -51,6 +51,8 @@ #define TPM_TIS_REG_INTF_CAPABILITY 0x14 #define TPM_TIS_REG_STS 0x18 #define TPM_TIS_REG_DATA_FIFO 0x24 +#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 @@ -62,6 +64,7 @@ #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 @@ -100,8 +103,15 @@ #endif +#define TPM_TIS_CAP_INTERFACE_VERSION1_3 (2 << 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_SUPPORTED (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_TPM_DID 0x0001 @@ -145,6 +155,24 @@ static void tpm_tis_show_buffer(const TPMSizedBuffer *sb, const char *string) } /* + * Set the given flags in the STS register by clearing the register but + * preserving the SELFTEST_DONE flag 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; + l->sts |= flags; +} + +/* * Send a request to the TPM. */ static void tpm_tis_tpm_send(TPMState *s, uint8_t locty) @@ -255,7 +283,8 @@ static void tpm_tis_abort(TPMState *s, uint8_t locty) */ if (tis->aborting_locty == tis->next_locty) { tis->loc[tis->aborting_locty].state = TPM_TIS_STATE_READY; - tis->loc[tis->aborting_locty].sts = TPM_TIS_STS_COMMAND_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); } @@ -300,7 +329,8 @@ static void tpm_tis_receive_bh(void *opaque) TPMTISEmuState *tis = &s->s.tis; uint8_t locty = s->locty_number; - tis->loc[locty].sts = TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE; + 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; @@ -320,12 +350,20 @@ static void tpm_tis_receive_bh(void *opaque) /* * Callback from the TPM to indicate that the response was received. */ -static void tpm_tis_receive_cb(TPMState *s, uint8_t locty) +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); } @@ -344,7 +382,7 @@ static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty) ret = tis->loc[locty].r_buffer.buffer[tis->loc[locty].r_offset++]; if (tis->loc[locty].r_offset >= len) { /* got last byte */ - tis->loc[locty].sts = TPM_TIS_STS_VALID; + 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 @@ -427,6 +465,7 @@ static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr, 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; @@ -475,15 +514,28 @@ static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr, } break; case TPM_TIS_REG_DATA_FIFO: + case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END: if (tis->active_locty == locty) { - switch (tis->loc[locty].state) { - case TPM_TIS_STATE_COMPLETION: - val = tpm_tis_data_read(s, locty); - break; - default: - val = TPM_TIS_NO_DATA_BYTE; - break; + 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_DID_VID: @@ -518,11 +570,13 @@ static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr, { TPMState *s = opaque; TPMTISEmuState *tis = &s->s.tis; - uint16_t off = addr & 0xfff; + 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, (uint32_t)val); @@ -535,6 +589,15 @@ static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr, return; } + val &= mask; + + if (shift) { + val <<= shift; + mask <<= shift; + } + + mask ^= 0xffffffff; + switch (off) { case TPM_TIS_REG_ACCESS: @@ -646,9 +709,10 @@ static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr, break; } - tis->loc[locty].inte = (val & (TPM_TIS_INT_ENABLED | - TPM_TIS_INT_POLARITY_MASK | - TPM_TIS_INTERRUPTS_SUPPORTED)); + 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 */ @@ -686,7 +750,7 @@ static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr, break; case TPM_TIS_STATE_IDLE: - tis->loc[locty].sts = TPM_TIS_STS_COMMAND_READY; + 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; @@ -705,7 +769,8 @@ static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr, /* 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)) { - 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); @@ -727,8 +792,9 @@ static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr, switch (tis->loc[locty].state) { case TPM_TIS_STATE_COMPLETION: tis->loc[locty].r_offset = 0; - tis->loc[locty].sts = TPM_TIS_STS_VALID | - TPM_TIS_STS_DATA_AVAILABLE; + tpm_tis_sts_set(&tis->loc[locty], + TPM_TIS_STS_VALID| + TPM_TIS_STS_DATA_AVAILABLE); break; default: /* ignore */ @@ -737,6 +803,7 @@ static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr, } 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; @@ -747,18 +814,28 @@ static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr, tis->loc[locty].state == TPM_TIS_STATE_COMPLETION) { /* drop the byte */ } else { - DPRINTF("tpm_tis: Byte to send to TPM: %02x\n", (uint8_t)val); + DPRINTF("tpm_tis: Data to send to TPM: %08x (size=%d)\n", + val, size); if (tis->loc[locty].state == TPM_TIS_STATE_READY) { tis->loc[locty].state = TPM_TIS_STATE_RECEPTION; - tis->loc[locty].sts = TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID; + tpm_tis_sts_set(&tis->loc[locty], + TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID); } - if ((tis->loc[locty].sts & TPM_TIS_STS_EXPECT)) { + 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 { - tis->loc[locty].sts = TPM_TIS_STS_VALID; + tpm_tis_sts_set(&tis->loc[locty], TPM_TIS_STS_VALID); } } @@ -771,11 +848,11 @@ static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr, #endif len = tpm_tis_get_size_from_buffer(&tis->loc[locty].w_buffer); if (len > tis->loc[locty].w_offset) { - tis->loc[locty].sts = TPM_TIS_STS_EXPECT | - TPM_TIS_STS_VALID; + tpm_tis_sts_set(&tis->loc[locty], + TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID); } else { /* packet complete */ - tis->loc[locty].sts = TPM_TIS_STS_VALID; + tpm_tis_sts_set(&tis->loc[locty], TPM_TIS_STS_VALID); } #ifdef RAISE_STS_IRQ if (needIrq) { @@ -791,7 +868,7 @@ static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr, static void tpm_tis_mmio_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { - return tpm_tis_mmio_write_intern(opaque, addr, val, size, false); + tpm_tis_mmio_write_intern(opaque, addr, val, size, false); } static const MemoryRegionOps tpm_tis_memory_ops = { @@ -882,18 +959,18 @@ static void tpm_tis_realizefn(DeviceState *dev, Error **errp) 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) { - ISADevice *dev = ISA_DEVICE(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); - memory_region_add_subregion(isa_address_space(dev), TPM_TIS_ADDR_BASE, - &s->mmio); } static void tpm_tis_class_init(ObjectClass *klass, void *data) diff --git a/hw/tpm/tpm_tis.h b/hw/tpm/tpm_tis.h index 1a0db23..db78d51 100644 --- a/hw/tpm/tpm_tis.h +++ b/hw/tpm/tpm_tis.h @@ -41,7 +41,7 @@ typedef enum { typedef struct TPMLocality { TPMTISState state; uint8_t access; - uint8_t sts; + uint32_t sts; uint32_t inte; uint32_t ints; diff --git a/hw/unicore32/puv3.c b/hw/unicore32/puv3.c index c41499e..cc9a21a 100644 --- a/hw/unicore32/puv3.c +++ b/hw/unicore32/puv3.c @@ -109,6 +109,7 @@ static void puv3_init(MachineState *machine) const char *kernel_filename = machine->kernel_filename; const char *initrd_filename = machine->initrd_filename; CPUUniCore32State *env; + UniCore32CPU *cpu; if (initrd_filename) { hw_error("Please use kernel built-in initramdisk.\n"); @@ -118,10 +119,11 @@ static void puv3_init(MachineState *machine) cpu_model = "UniCore-II"; } - env = cpu_init(cpu_model); - if (!env) { + cpu = uc32_cpu_init(cpu_model); + if (!cpu) { hw_error("Unable to find CPU definition\n"); } + env = &cpu->env; puv3_soc_init(env); puv3_board_init(env, ram_size); diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs index 3fe4dff..7443e38 100644 --- a/hw/usb/Makefile.objs +++ b/hw/usb/Makefile.objs @@ -1,17 +1,18 @@ # usb subsystem core -common-obj-y += core.o combined-packet.o bus.o desc.o desc-msos.o -common-obj-y += libhw.o +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 hcd-ehci-sysbus.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 # emulated usb devices -common-obj-y += dev-hub.o -common-obj-y += dev-hid.o +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 diff --git a/hw/usb/bus.c b/hw/usb/bus.c index 986b2d8..3751675 100644 --- a/hw/usb/bus.c +++ b/hw/usb/bus.c @@ -315,23 +315,33 @@ USBDevice *usb_create(USBBus *bus, const char *name) return USB_DEVICE(dev); } -USBDevice *usb_create_simple(USBBus *bus, const char *name) +static USBDevice *usb_try_create_simple(USBBus *bus, const char *name, + Error **errp) { - USBDevice *dev = usb_create(bus, name); - int rc; + Error *err = NULL; + USBDevice *dev; + dev = USB_DEVICE(qdev_try_create(&bus->qbus, name)); if (!dev) { - error_report("Failed to create USB device '%s'", name); + error_setg(errp, "Failed to create USB device '%s'", name); return NULL; } - rc = qdev_init(&dev->qdev); - if (rc < 0) { - error_report("Failed to initialize USB device '%s'", name); + object_property_set_bool(OBJECT(dev), true, "realized", &err); + if (err) { + error_setg(errp, "Failed to initialize USB device '%s': %s", + name, error_get_pretty(err)); + error_free(err); + 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) { @@ -350,9 +360,10 @@ void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index, bus->nfree++; } -int usb_register_companion(const char *masterbus, USBPort *ports[], - uint32_t portcount, uint32_t firstport, - void *opaque, USBPortOps *ops, int speedmask) +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; @@ -363,22 +374,22 @@ int usb_register_companion(const char *masterbus, USBPort *ports[], } } - if (!bus || !bus->ops->register_companion) { - qerror_report(QERR_INVALID_PARAMETER_VALUE, "masterbus", - "an USB masterbus"); - if (bus) { - error_printf_unless_qmp( - "USB bus '%s' does not allow companion controllers\n", - masterbus); - } - return -1; + 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); } - return bus->ops->register_companion(bus, ports, portcount, firstport); + bus->ops->register_companion(bus, ports, portcount, firstport, errp); } void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr) @@ -416,17 +427,17 @@ void usb_claim_port(USBDevice *dev, Error **errp) } } if (port == NULL) { - error_setg(errp, "Error: usb port %s (bus %s) not found (in use?)", + 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_create_simple(bus, "usb-hub"); + usb_try_create_simple(bus, "usb-hub", NULL); } if (bus->nfree == 0) { - error_setg(errp, "Error: tried to attach usb device %s to a bus " + error_setg(errp, "tried to attach usb device %s to a bus " "with no free ports", dev->product_desc); return; } @@ -627,7 +638,7 @@ static char *usb_get_fw_dev_path(DeviceState *qdev) return fw_path; } -void usb_info(Monitor *mon, const QDict *qdict) +void hmp_info_usb(Monitor *mon, const QDict *qdict) { USBBus *bus; USBDevice *dev; @@ -655,10 +666,12 @@ 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) { @@ -693,14 +706,28 @@ USBDevice *usbdevice_create(const char *cmdline) return NULL; } - if (!f->usbdevice_init) { + if (f->usbdevice_init) { + dev = f->usbdevice_init(bus, params); + } else { if (*params) { error_report("usbdevice %s accepts no params", driver); return NULL; } - return usb_create_simple(bus, f->name); + dev = usb_create(bus, f->name); + } + if (!dev) { + error_report("Failed to create USB device '%s'", f->name); + return NULL; } - return f->usbdevice_init(bus, params); + object_property_set_bool(OBJECT(dev), true, "realized", &err); + if (err) { + error_report("Failed to initialize USB device '%s': %s", + f->name, error_get_pretty(err)); + error_free(err); + object_unparent(OBJECT(dev)); + return NULL; + } + return dev; } static void usb_device_class_init(ObjectClass *klass, void *data) diff --git a/hw/usb/desc-msos.c b/hw/usb/desc-msos.c index 334d1ae..32c3600 100644 --- a/hw/usb/desc-msos.c +++ b/hw/usb/desc-msos.c @@ -231,7 +231,7 @@ int usb_desc_msos(const USBDesc *desc, USBPacket *p, length = len; } memcpy(dest, buf, length); - free(buf); + g_free(buf); p->actual_length = length; return 0; diff --git a/hw/usb/dev-bluetooth.c b/hw/usb/dev-bluetooth.c index 390d475..9bf6730 100644 --- a/hw/usb/dev-bluetooth.c +++ b/hw/usb/dev-bluetooth.c @@ -530,21 +530,12 @@ static USBDevice *usb_bt_init(USBBus *bus, const char *cmdline) } else { hci = bt_new_hci(qemu_find_bt_vlan(0)); } - if (!hci) return NULL; + dev = usb_create(bus, name); - if (!dev) { - error_report("Failed to create USB device '%s'", name); - return NULL; - } s = DO_UPCAST(struct USBBtState, dev, dev); s->hci = hci; - if (qdev_init(&dev->qdev) < 0) { - error_report("Failed to initialize USB device '%s'", name); - return NULL; - } - return dev; } diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c index 5b95d5c..9be3a64 100644 --- a/hw/usb/dev-network.c +++ b/hw/usb/dev-network.c @@ -1310,6 +1310,10 @@ static int usbnet_can_receive(NetClientState *nc) { USBNetState *s = qemu_get_nic_opaque(nc); + if (!s->dev.config) { + return 0; + } + if (is_rndis(s) && s->rndis_state != RNDIS_DATA_INITIALIZED) { return 1; } @@ -1394,22 +1398,17 @@ static USBDevice *usb_net_init(USBBus *bus, const char *cmdline) if (!opts) { return NULL; } - qemu_opt_set(opts, "type", "nic"); - qemu_opt_set(opts, "model", "usb"); + 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("%s", error_get_pretty(local_err)); - error_free(local_err); + error_report_err(local_err); return NULL; } dev = usb_create(bus, "usb-net"); - if (!dev) { - return NULL; - } qdev_set_nic_properties(&dev->qdev, &nd_table[idx]); - qdev_init_nofail(&dev->qdev); return dev; } diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c index 1cee450..67c2072 100644 --- a/hw/usb/dev-serial.c +++ b/hw/usb/dev-serial.c @@ -544,16 +544,11 @@ static USBDevice *usb_serial_init(USBBus *bus, const char *filename) return NULL; dev = usb_create(bus, "usb-serial"); - if (!dev) { - return NULL; - } 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); - qdev_init_nofail(&dev->qdev); - return dev; } @@ -568,8 +563,6 @@ static USBDevice *usb_braille_init(USBBus *bus, const char *unused) dev = usb_create(bus, "usb-braille"); qdev_prop_set_chr(&dev->qdev, "chardev", cdrv); - qdev_init_nofail(&dev->qdev); - return dev; } diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index 4539733..ae8d40d 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -559,8 +559,7 @@ static void usb_msd_password_cb(void *opaque, int err) } if (local_err) { - qerror_report_err(local_err); - error_free(local_err); + error_report_err(local_err); qdev_unplug(&s->dev.qdev, NULL); } } @@ -610,7 +609,25 @@ static void usb_msd_realize_storage(USBDevice *dev, Error **errp) return; } + bdrv_add_key(blk_bs(blk), NULL, &err); + if (err) { + if (monitor_cur_is_qmp()) { + error_propagate(errp, err); + return; + } + error_free(err); + err = NULL; + if (cur_mon) { + monitor_read_bdrv_key_start(cur_mon, blk_bs(blk), + usb_msd_password_cb, s); + s->dev.auto_attach = 0; + } else { + autostart = 0; + } + } + blkconf_serial(&s->conf, &dev->serial); + blkconf_blocksizes(&s->conf); /* * Hack alert: this pretends to be a block device, but it's really @@ -637,16 +654,6 @@ static void usb_msd_realize_storage(USBDevice *dev, Error **errp) } usb_msd_handle_reset(dev); s->scsi_dev = scsi_dev; - - if (bdrv_key_required(blk_bs(blk))) { - 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; - } - } } static void usb_msd_realize_bot(USBDevice *dev, Error **errp) @@ -663,6 +670,7 @@ static void usb_msd_realize_bot(USBDevice *dev, Error **errp) static USBDevice *usb_msd_init(USBBus *bus, const char *filename) { static int nr=0; + Error *err = NULL; char id[8]; QemuOpts *opts; DriveInfo *dinfo; @@ -683,7 +691,7 @@ static USBDevice *usb_msd_init(USBBus *bus, const char *filename) if (strstart(filename, "format=", &p2)) { int len = MIN(p1 - p2, sizeof(fmt)); pstrcpy(fmt, len, p2); - qemu_opt_set(opts, "format", fmt); + qemu_opt_set(opts, "format", fmt, &error_abort); } else if (*filename != ':') { error_report("unrecognized USB mass-storage option %s", filename); return NULL; @@ -694,8 +702,8 @@ static USBDevice *usb_msd_init(USBBus *bus, const char *filename) error_report("block device specification needed"); return NULL; } - qemu_opt_set(opts, "file", filename); - qemu_opt_set(opts, "if", "none"); + qemu_opt_set(opts, "file", filename, &error_abort); + qemu_opt_set(opts, "if", "none", &error_abort); /* create host drive */ dinfo = drive_new(opts, 0); @@ -706,17 +714,13 @@ static USBDevice *usb_msd_init(USBBus *bus, const char *filename) /* create guest device */ dev = usb_create(bus, "usb-storage"); - if (!dev) { - return NULL; - } - if (qdev_prop_set_drive(&dev->qdev, "drive", - blk_by_legacy_dinfo(dinfo)) < 0) { + 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; } - if (qdev_init(&dev->qdev) < 0) - return NULL; - return dev; } diff --git a/hw/usb/hcd-ehci-pci.c b/hw/usb/hcd-ehci-pci.c index 490f2b6..7afa5f9 100644 --- a/hw/usb/hcd-ehci-pci.c +++ b/hw/usb/hcd-ehci-pci.c @@ -26,7 +26,7 @@ typedef struct EHCIPCIInfo { bool companion; } EHCIPCIInfo; -static int usb_ehci_pci_initfn(PCIDevice *dev) +static void usb_ehci_pci_realize(PCIDevice *dev, Error **errp) { EHCIPCIState *i = PCI_EHCI(dev); EHCIState *s = &i->ehci; @@ -66,8 +66,6 @@ static int usb_ehci_pci_initfn(PCIDevice *dev) usb_ehci_realize(s, DEVICE(dev), NULL); pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem); - - return 0; } static void usb_ehci_pci_init(Object *obj) @@ -103,6 +101,15 @@ static void usb_ehci_pci_exit(PCIDevice *dev) } } +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) { @@ -139,12 +146,13 @@ static void ehci_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = usb_ehci_pci_initfn; + 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 = { diff --git a/hw/usb/hcd-ehci-sysbus.c b/hw/usb/hcd-ehci-sysbus.c index 19ed2c2..cd1cc14 100644 --- a/hw/usb/hcd-ehci-sysbus.c +++ b/hw/usb/hcd-ehci-sysbus.c @@ -42,6 +42,15 @@ static void usb_ehci_sysbus_realize(DeviceState *dev, Error **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); @@ -70,6 +79,7 @@ static void ehci_sysbus_class_init(ObjectClass *klass, void *data) 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); } diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 1cc0fc1..d4d7547 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -769,30 +769,26 @@ static void ehci_wakeup(USBPort *port) qemu_bh_schedule(s->async_bh); } -static int ehci_register_companion(USBBus *bus, USBPort *ports[], - uint32_t portcount, uint32_t firstport) +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) { - qerror_report(QERR_INVALID_PARAMETER_VALUE, "firstport", - "firstport on masterbus"); - error_printf_unless_qmp( - "firstport value of %u makes companion take ports %u - %u, which " - "is outside of the valid range of 0 - %u\n", firstport, firstport, - firstport + portcount - 1, NB_PORTS - 1); - return -1; + 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]) { - qerror_report(QERR_INVALID_PARAMETER_VALUE, "masterbus", - "an USB masterbus"); - error_printf_unless_qmp( - "port %u on masterbus %s already has a companion assigned\n", - firstport + i, bus->qbus.name); - return -1; + 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; } } @@ -806,8 +802,6 @@ static int ehci_register_companion(USBBus *bus, USBPort *ports[], s->companion_count++; s->caps[0x05] = (s->companion_count << 4) | portcount; - - return 0; } static void ehci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep, @@ -845,7 +839,7 @@ static USBDevice *ehci_find_device(EHCIState *ehci, uint8_t addr) } /* 4.1 host controller initialization */ -static void ehci_reset(void *opaque) +void ehci_reset(void *opaque) { EHCIState *s = opaque; int i; @@ -2437,7 +2431,7 @@ const VMStateDescription vmstate_ehci = { VMSTATE_UINT32(portsc[4], EHCIState), VMSTATE_UINT32(portsc[5], EHCIState), /* frame timer */ - VMSTATE_TIMER(frame_timer, EHCIState), + VMSTATE_TIMER_PTR(frame_timer, EHCIState), VMSTATE_UINT64(last_run_ns, EHCIState), VMSTATE_UINT32(async_stepdown, EHCIState), /* schedule state */ @@ -2471,7 +2465,6 @@ void usb_ehci_realize(EHCIState *s, DeviceState *dev, Error **errp) s->async_bh = qemu_bh_new(ehci_frame_timer, s); s->device = dev; - qemu_register_reset(ehci_reset, s); s->vmstate = qemu_add_vm_change_state_handler(usb_ehci_vm_state_change, s); } diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h index 2bc259c..87b240f 100644 --- a/hw/usb/hcd-ehci.h +++ b/hw/usb/hcd-ehci.h @@ -325,6 +325,7 @@ 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) diff --git a/hw/usb/hcd-musb.c b/hw/usb/hcd-musb.c index 40809f6..61cc878 100644 --- a/hw/usb/hcd-musb.c +++ b/hw/usb/hcd-musb.c @@ -554,8 +554,10 @@ static void musb_schedule_cb(USBPort *port, USBPacket *packey) timeout = ep->timeout[dir]; else if (ep->interrupt[dir]) timeout = 8; - else - return musb_cb_tick(ep); + 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); @@ -772,9 +774,11 @@ static void musb_rx_packet_complete(USBPacket *packey, void *opaque) /* NAK timeouts are only generated in Bulk transfers and * Data-errors in Isochronous. */ - if (ep->interrupt[1]) - return musb_packet(s, ep, epnum, USB_TOKEN_IN, - packey->iov.size, musb_rx_packet_complete, 1); + 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) @@ -864,8 +868,7 @@ static void musb_tx_rdy(MUSBState *s, int epnum) * but it doesn't make sense for us to do that. */ } - return musb_packet(s, ep, epnum, pid, - total, musb_tx_packet_complete, 0); + musb_packet(s, ep, epnum, pid, total, musb_tx_packet_complete, 0); } static void musb_rx_req(MUSBState *s, int epnum) @@ -929,8 +932,7 @@ static void musb_rx_req(MUSBState *s, int epnum) } #endif - return musb_packet(s, ep, epnum, USB_TOKEN_IN, - total, musb_rx_packet_complete, 1); + musb_packet(s, ep, epnum, USB_TOKEN_IN, total, musb_rx_packet_complete, 1); } static uint8_t musb_read_fifo(MUSBEndPoint *ep) diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index 9a84eb6..1a22c9c 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -1827,11 +1827,12 @@ static USBPortOps ohci_port_ops = { static USBBusOps ohci_bus_ops = { }; -static int usb_ohci_init(OHCIState *ohci, DeviceState *dev, - int num_ports, dma_addr_t localmem_base, - char *masterbus, uint32_t firstport, - AddressSpace *as) +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; @@ -1857,10 +1858,13 @@ static int usb_ohci_init(OHCIState *ohci, DeviceState *dev, for(i = 0; i < num_ports; i++) { ports[i] = &ohci->rhport[i].port; } - if (usb_register_companion(masterbus, ports, num_ports, - firstport, ohci, &ohci_port_ops, - USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL) != 0) { - return -1; + 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); @@ -1879,9 +1883,6 @@ static int usb_ohci_init(OHCIState *ohci, DeviceState *dev, usb_packet_init(&ohci->usb_packet); ohci->async_td = 0; - qemu_register_reset(ohci_reset, ohci); - - return 0; } #define TYPE_PCI_OHCI "pci-ohci" @@ -1914,22 +1915,24 @@ static void ohci_die(OHCIState *ohci) PCI_STATUS_DETECTED_PARITY); } -static int usb_ohci_initfn_pci(PCIDevice *dev) +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 */ - if (usb_ohci_init(&ohci->state, DEVICE(dev), ohci->num_ports, 0, - ohci->masterbus, ohci->firstport, - pci_get_address_space(dev)) != 0) { - return -1; + 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); + ohci->state.irq = pci_allocate_irq(dev); pci_register_bar(dev, 0, 0, &ohci->state.mem); - return 0; } static void usb_ohci_exit(PCIDevice *dev) @@ -1951,6 +1954,15 @@ static void usb_ohci_exit(PCIDevice *dev) } } +static void usb_ohci_reset_pci(DeviceState *d) +{ + PCIDevice *dev = PCI_DEVICE(d); + OHCIPCIState *ohci = PCI_OHCI(dev); + OHCIState *s = &ohci->state; + + ohci_reset(s); +} + #define TYPE_SYSBUS_OHCI "sysbus-ohci" #define SYSBUS_OHCI(obj) OBJECT_CHECK(OHCISysBusState, (obj), TYPE_SYSBUS_OHCI) @@ -1971,11 +1983,19 @@ static void ohci_realize_pxa(DeviceState *dev, Error **errp) /* 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); + &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_reset(ohci); +} + static Property ohci_pci_properties[] = { DEFINE_PROP_STRING("masterbus", OHCIPCIState, masterbus), DEFINE_PROP_UINT32("num-ports", OHCIPCIState, num_ports, 3), @@ -2015,7 +2035,7 @@ static const VMStateDescription vmstate_ohci_eof_timer = { .minimum_version_id = 1, .pre_load = ohci_eof_timer_pre_load, .fields = (VMStateField[]) { - VMSTATE_TIMER(eof_timer, OHCIState), + VMSTATE_TIMER_PTR(eof_timer, OHCIState), VMSTATE_END_OF_LIST() }, }; @@ -2087,7 +2107,7 @@ static void ohci_pci_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = usb_ohci_initfn_pci; + 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; @@ -2097,6 +2117,7 @@ static void ohci_pci_class_init(ObjectClass *klass, void *data) dc->props = ohci_pci_properties; dc->hotpluggable = false; dc->vmsd = &vmstate_ohci; + dc->reset = usb_ohci_reset_pci; } static const TypeInfo ohci_pci_info = { @@ -2120,6 +2141,7 @@ static void ohci_sysbus_class_init(ObjectClass *klass, void *data) 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 = { diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 4a4215d..327f26d 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -66,7 +66,7 @@ struct UHCIInfo { uint16_t device_id; uint8_t revision; uint8_t irq_pin; - int (*initfn)(PCIDevice *dev); + void (*realize)(PCIDevice *dev, Error **errp); bool unplug; }; @@ -348,9 +348,10 @@ static void uhci_update_irq(UHCIState *s) pci_set_irq(&s->dev, level); } -static void uhci_reset(void *opaque) +static void uhci_reset(DeviceState *dev) { - UHCIState *s = opaque; + PCIDevice *d = PCI_DEVICE(dev); + UHCIState *s = DO_UPCAST(UHCIState, dev, d); uint8_t *pci_conf; int i; UHCIPort *port; @@ -419,7 +420,7 @@ static const VMStateDescription vmstate_uhci = { VMSTATE_UINT32(fl_base_addr, UHCIState), VMSTATE_UINT8(sof_timing, UHCIState), VMSTATE_UINT8(status2, UHCIState), - VMSTATE_TIMER(frame_timer, 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() @@ -454,11 +455,11 @@ static void uhci_port_write(void *opaque, hwaddr addr, port = &s->ports[i]; usb_device_reset(port->port.dev); } - uhci_reset(s); + uhci_reset(DEVICE(s)); return; } if (val & UHCI_CMD_HCRESET) { - uhci_reset(s); + uhci_reset(DEVICE(s)); return; } s->cmd = val; @@ -1190,8 +1191,9 @@ static USBPortOps uhci_port_ops = { static USBBusOps uhci_bus_ops = { }; -static int usb_uhci_common_initfn(PCIDevice *dev) +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 = DO_UPCAST(UHCIState, dev, dev); @@ -1209,10 +1211,13 @@ static int usb_uhci_common_initfn(PCIDevice *dev) for(i = 0; i < NB_PORTS; i++) { ports[i] = &s->ports[i].port; } - if (usb_register_companion(s->masterbus, ports, NB_PORTS, - s->firstport, s, &uhci_port_ops, - USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL) != 0) { - return -1; + 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)); @@ -1226,19 +1231,15 @@ static int usb_uhci_common_initfn(PCIDevice *dev) s->num_ports_vmstate = NB_PORTS; QTAILQ_INIT(&s->queues); - qemu_register_reset(uhci_reset, s); - 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); - - return 0; } -static int usb_uhci_vt82c686b_initfn(PCIDevice *dev) +static void usb_uhci_vt82c686b_realize(PCIDevice *dev, Error **errp) { UHCIState *s = DO_UPCAST(UHCIState, dev, dev); uint8_t *pci_conf = s->dev.config; @@ -1250,7 +1251,7 @@ static int usb_uhci_vt82c686b_initfn(PCIDevice *dev) /* USB legacy support */ pci_set_long(pci_conf + 0xc0,0x00002000); - return usb_uhci_common_initfn(dev); + usb_uhci_common_realize(dev, errp); } static void usb_uhci_exit(PCIDevice *dev) @@ -1296,13 +1297,14 @@ static void uhci_class_init(ObjectClass *klass, void *data) UHCIPCIDeviceClass *u = container_of(k, UHCIPCIDeviceClass, parent_class); UHCIInfo *info = data; - k->init = info->initfn ? info->initfn : usb_uhci_common_initfn; + 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; k->class_id = PCI_CLASS_SERIAL_USB; dc->vmsd = &vmstate_uhci; + dc->reset = uhci_reset; if (!info->unplug) { /* uhci controllers in companion setups can't be hotplugged */ dc->hotpluggable = false; @@ -1335,7 +1337,7 @@ static UHCIInfo uhci_info[] = { .device_id = PCI_DEVICE_ID_VIA_UHCI, .revision = 0x01, .irq_pin = 3, - .initfn = usb_uhci_vt82c686b_initfn, + .realize = usb_uhci_vt82c686b_realize, .unplug = true, },{ .name = "ich9-usb-uhci1", /* 00:1d.0 */ diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 9a942cf..ba15ae0 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -1767,9 +1767,18 @@ static void xhci_xfer_report(XHCITransfer *xfer) break; } - if (!reported && ((trb->control & TRB_TR_IOC) || - (shortpkt && (trb->control & TRB_TR_ISP)) || - (xfer->status != CC_SUCCESS && left == 0))) { + /* + * XHCI 1.1, 4.11.3.1 Transfer Event TRB -- "each Transfer TRB + * encountered with its IOC flag set to '1' shall generate a Transfer + * Event." + * + * Otherwise, longer transfers can have multiple data TRBs (for scatter + * gather). Short transfers and errors should be reported once per + * transfer only. + */ + if ((trb->control & TRB_TR_IOC) || + (!reported && ((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; @@ -3567,7 +3576,7 @@ static void usb_xhci_init(XHCIState *xhci) } } -static int usb_xhci_initfn(struct PCIDevice *dev) +static void usb_xhci_realize(struct PCIDevice *dev, Error **errp) { int i, ret; @@ -3646,8 +3655,6 @@ static int usb_xhci_initfn(struct PCIDevice *dev) &xhci->mem, 0, OFF_MSIX_PBA, 0x90); } - - return 0; } static void usb_xhci_exit(PCIDevice *dev) @@ -3855,7 +3862,7 @@ static const VMStateDescription vmstate_xhci = { /* Runtime Registers & state */ VMSTATE_INT64(mfindex_start, XHCIState), - VMSTATE_TIMER(mfwrap_timer, XHCIState), + VMSTATE_TIMER_PTR(mfwrap_timer, XHCIState), VMSTATE_STRUCT(cmd_ring, XHCIState, 1, vmstate_xhci_ring, XHCIRing), VMSTATE_END_OF_LIST() @@ -3887,7 +3894,7 @@ static void xhci_class_init(ObjectClass *klass, void *data) dc->props = xhci_properties; dc->reset = xhci_reset; set_bit(DEVICE_CATEGORY_USB, dc->categories); - k->init = usb_xhci_initfn; + 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; diff --git a/hw/usb/host-legacy.c b/hw/usb/host-legacy.c index 3cc9c42..422ed9a 100644 --- a/hw/usb/host-legacy.c +++ b/hw/usb/host-legacy.c @@ -128,7 +128,6 @@ USBDevice *usb_host_device_open(USBBus *bus, const char *devname) 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); - qdev_init_nofail(&dev->qdev); return dev; fail: diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index a5f9dab..10f4735 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -878,8 +878,7 @@ static int usb_host_open(USBHostDevice *s, libusb_device *dev) usb_device_attach(udev, &local_err); if (local_err) { - error_report("%s", error_get_pretty(local_err)); - error_free(local_err); + error_report_err(local_err); goto fail; } @@ -1237,7 +1236,7 @@ static void usb_host_handle_control(USBDevice *udev, USBPacket *p, /* Fix up USB-3 ep0 maxpacket size to allow superspeed connected devices * to work redirected to a not superspeed capable hcd */ if (udev->speed == USB_SPEED_SUPER && - !((udev->port->speedmask & USB_SPEED_MASK_SUPER)) && + !(udev->port->speedmask & USB_SPEED_MASK_SUPER) && request == 0x8006 && value == 0x100 && index == 0) { r->usb3ep0quirk = true; } @@ -1637,7 +1636,7 @@ static void usb_host_auto_check(void *unused) timer_mod(usb_auto_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 2000); } -void usb_host_info(Monitor *mon, const QDict *qdict) +void hmp_info_usbhost(Monitor *mon, const QDict *qdict) { libusb_device **devs = NULL; struct libusb_device_descriptor ddesc; diff --git a/hw/usb/host-stub.c b/hw/usb/host-stub.c index 28d8032..2eaaa83 100644 --- a/hw/usb/host-stub.c +++ b/hw/usb/host-stub.c @@ -35,7 +35,7 @@ #include "hw/usb.h" #include "monitor/monitor.h" -void usb_host_info(Monitor *mon, const QDict *qdict) +void hmp_info_usbhost(Monitor *mon, const QDict *qdict) { monitor_printf(mon, "USB host devices not supported\n"); } diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 9fbd59e..2416de8 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -1273,8 +1273,7 @@ static void usbredir_do_attach(void *opaque) usb_device_attach(&dev->dev, &local_err); if (local_err) { - error_report("%s", error_get_pretty(local_err)); - error_free(local_err); + error_report_err(local_err); WARNING("rejecting device due to speed mismatch\n"); usbredir_reject_device(dev); } @@ -2438,7 +2437,7 @@ static const VMStateDescription usbredir_vmstate = { .post_load = usbredir_post_load, .fields = (VMStateField[]) { VMSTATE_USB_DEVICE(dev, USBRedirDevice), - VMSTATE_TIMER(attach_timer, USBRedirDevice), + VMSTATE_TIMER_PTR(attach_timer, USBRedirDevice), { .name = "parser", .version_id = 0, diff --git a/hw/vfio/Makefile.objs b/hw/vfio/Makefile.objs new file mode 100644 index 0000000..e31f30e --- /dev/null +++ b/hw/vfio/Makefile.objs @@ -0,0 +1,4 @@ +ifeq ($(CONFIG_LINUX), y) +obj-$(CONFIG_SOFTMMU) += common.o +obj-$(CONFIG_PCI) += pci.o +endif diff --git a/hw/vfio/common.c b/hw/vfio/common.c new file mode 100644 index 0000000..b012620 --- /dev/null +++ b/hw/vfio/common.c @@ -0,0 +1,961 @@ +/* + * 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 +#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 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. + */ + 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); + return; + } + /* + * 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"); + return; + } + + 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); + } + } +} + +static void vfio_listener_region_add(MemoryListener *listener, + MemoryRegionSection *section) +{ + VFIOContainer *container = container_of(listener, VFIOContainer, + iommu_data.type1.listener); + hwaddr iova, end; + Int128 llend; + 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; + } + + memory_region_ref(section->mr); + + if (memory_region_is_iommu(section->mr)) { + VFIOGuestIOMMU *giommu; + + trace_vfio_listener_region_add_iommu(iova, + int128_get64(int128_sub(llend, int128_one()))); + /* + * 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). + */ + /* + * This assumes that the guest IOMMU is empty of + * mappings at this point. + * + * One way of doing this is: + * 1. Avoid sharing IOMMUs between emulated devices or different + * IOMMU groups. + * 2. Implement VFIO_IOMMU_ENABLE in the host kernel to fail if + * there are some mappings in IOMMU. + * + * VFIO on SPAPR does that. Other IOMMU models may do that different, + * they must make sure there are no existing mappings or + * loop through existing mappings to map them into VFIO. + */ + giommu = g_malloc0(sizeof(*giommu)); + giommu->iommu = section->mr; + giommu->container = container; + giommu->n.notify = vfio_iommu_map_notify; + QLIST_INSERT_HEAD(&container->giommu_list, giommu, giommu_next); + memory_region_register_iommu_notifier(giommu->iommu, &giommu->n); + + return; + } + + /* Here we assume that memory_region_is_ram(section->mr)==true */ + + end = int128_get64(llend); + vaddr = memory_region_get_ram_ptr(section->mr) + + section->offset_within_region + + (iova - section->offset_within_address_space); + + trace_vfio_listener_region_add_ram(iova, end - 1, vaddr); + + ret = vfio_dma_map(container, iova, end - iova, vaddr, section->readonly); + if (ret) { + error_report("vfio_dma_map(%p, 0x%"HWADDR_PRIx", " + "0x%"HWADDR_PRIx", %p) = %d (%m)", + container, iova, end - iova, vaddr, ret); + + /* + * On the initfn path, store the first error in the container so we + * can gracefully fail. Runtime, there's not much we can do other + * than throw a hardware error. + */ + if (!container->iommu_data.type1.initialized) { + if (!container->iommu_data.type1.error) { + container->iommu_data.type1.error = ret; + } + } else { + hw_error("vfio: DMA mapping failed, unable to continue"); + } + } +} + +static void vfio_listener_region_del(MemoryListener *listener, + MemoryRegionSection *section) +{ + VFIOContainer *container = container_of(listener, VFIOContainer, + iommu_data.type1.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->iommu_data.type1.listener); +} + +int vfio_mmap_region(Object *obj, VFIORegion *region, + MemoryRegion *mem, MemoryRegion *submem, + void **map, size_t size, off_t offset, + const char *name) +{ + int ret = 0; + VFIODevice *vbasedev = region->vbasedev; + + if (vbasedev->allow_mmap && size && region->flags & + VFIO_REGION_INFO_FLAG_MMAP) { + int prot = 0; + + if (region->flags & VFIO_REGION_INFO_FLAG_READ) { + prot |= PROT_READ; + } + + if (region->flags & VFIO_REGION_INFO_FLAG_WRITE) { + prot |= PROT_WRITE; + } + + *map = mmap(NULL, size, prot, MAP_SHARED, + vbasedev->fd, + region->fd_offset + offset); + if (*map == MAP_FAILED) { + *map = NULL; + ret = -errno; + goto empty_region; + } + + memory_region_init_ram_ptr(submem, obj, name, size, *map); + memory_region_set_skip_dump(submem); + } else { +empty_region: + /* Create a zero sized sub-region to make cleanup easy. */ + memory_region_init(submem, obj, name, 0); + } + + memory_region_add_subregion(mem, offset, submem); + + return ret; +} + +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); + + 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; + } + + container->iommu_data.type1.listener = vfio_memory_listener; + container->iommu_data.release = vfio_listener_release; + + memory_listener_register(&container->iommu_data.type1.listener, + container->space->as); + + if (container->iommu_data.type1.error) { + ret = container->iommu_data.type1.error; + error_report("vfio: memory listener initialization failed for container"); + goto listener_release_exit; + } + + container->iommu_data.type1.initialized = true; + + } else if (ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_SPAPR_TCE_IOMMU)) { + 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; + } + + container->iommu_data.type1.listener = vfio_memory_listener; + container->iommu_data.release = vfio_listener_release; + + memory_listener_register(&container->iommu_data.type1.listener, + container->space->as); + + } else { + error_report("vfio: No available IOMMU models"); + ret = -EINVAL; + goto free_container_exit; + } + + 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; + + if (container->iommu_data.release) { + container->iommu_data.release(container); + } + QLIST_REMOVE(container, next); + 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); +} + +static int vfio_container_do_ioctl(AddressSpace *as, int32_t groupid, + int req, void *param) +{ + VFIOGroup *group; + VFIOContainer *container; + int ret = -1; + + group = vfio_get_group(groupid, as); + if (!group) { + error_report("vfio: group %d not registered", groupid); + return ret; + } + + container = group->container; + if (group->container) { + ret = ioctl(container->fd, req, param); + if (ret < 0) { + error_report("vfio: failed to ioctl %d to container: ret=%d, %s", + _IOC_NR(req) - VFIO_BASE, ret, strerror(errno)); + } + } + + vfio_put_group(group); + + return ret; +} + +int vfio_container_ioctl(AddressSpace *as, int32_t groupid, + int req, void *param) +{ + /* We allow only certain ioctls to the container */ + switch (req) { + case VFIO_CHECK_EXTENSION: + case VFIO_IOMMU_SPAPR_TCE_GET_INFO: + case VFIO_EEH_PE_OP: + break; + default: + /* Return an error on unknown requests */ + error_report("vfio: unsupported ioctl %X", req); + return -1; + } + + return vfio_container_do_ioctl(as, groupid, req, param); +} diff --git a/hw/misc/vfio.c b/hw/vfio/pci.c similarity index 64% rename from hw/misc/vfio.c rename to hw/vfio/pci.c index fd318a1..beaa306 100644 --- a/hw/misc/vfio.c +++ b/hw/vfio/pci.c @@ -39,28 +39,15 @@ #include "qemu/range.h" #include "sysemu/kvm.h" #include "sysemu/sysemu.h" -#include "hw/misc/vfio.h" +#include "trace.h" +#include "hw/vfio/vfio.h" +#include "hw/vfio/vfio-common.h" -/* #define DEBUG_VFIO */ -#ifdef DEBUG_VFIO -#define DPRINTF(fmt, ...) \ - do { fprintf(stderr, "vfio: " fmt, ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) \ - do { } while (0) -#endif - -/* Extra debugging, trap acceleration paths for more logging */ -#define VFIO_ALLOW_MMAP 1 -#define VFIO_ALLOW_KVM_INTX 1 -#define VFIO_ALLOW_KVM_MSI 1 -#define VFIO_ALLOW_KVM_MSIX 1 - -struct VFIODevice; +struct VFIOPCIDevice; typedef struct VFIOQuirk { MemoryRegion mem; - struct VFIODevice *vdev; + struct VFIOPCIDevice *vdev; QLIST_ENTRY(VFIOQuirk) next; struct { uint32_t base_offset:TARGET_PAGE_BITS; @@ -82,14 +69,7 @@ typedef struct VFIOQuirk { } VFIOQuirk; typedef struct VFIOBAR { - off_t fd_offset; /* offset of BAR within device fd */ - int fd; /* device fd, allows us to pass VFIOBAR as opaque data */ - MemoryRegion mem; /* slow, read/write access */ - MemoryRegion mmap_mem; /* direct mapped access */ - void *mmap; - size_t size; - uint32_t flags; /* VFIO region flags (rd/wr/mmap) */ - uint8_t nr; /* cache the BAR number for debug */ + VFIORegion region; bool ioport; bool mem64; QLIST_HEAD(, VFIOQuirk) quirks; @@ -131,7 +111,7 @@ typedef struct VFIOMSIVector { */ EventNotifier interrupt; EventNotifier kvm_interrupt; - struct VFIODevice *vdev; /* back pointer to device */ + struct VFIOPCIDevice *vdev; /* back pointer to device */ int virq; bool use; } VFIOMSIVector; @@ -143,45 +123,6 @@ enum { VFIO_INT_MSIX = 3, }; -typedef struct VFIOAddressSpace { - AddressSpace *as; - QLIST_HEAD(, VFIOContainer) containers; - QLIST_ENTRY(VFIOAddressSpace) list; -} VFIOAddressSpace; - -static QLIST_HEAD(, VFIOAddressSpace) vfio_address_spaces = - QLIST_HEAD_INITIALIZER(vfio_address_spaces); - -struct VFIOGroup; - -typedef struct VFIOType1 { - MemoryListener listener; - int error; - bool initialized; -} VFIOType1; - -typedef struct VFIOContainer { - VFIOAddressSpace *space; - int fd; /* /dev/vfio/vfio, empowered by the attached groups */ - struct { - /* enable abstraction to support various iommu backends */ - union { - VFIOType1 type1; - }; - void (*release)(struct VFIOContainer *); - } iommu_data; - QLIST_HEAD(, VFIOGuestIOMMU) giommu_list; - QLIST_HEAD(, VFIOGroup) group_list; - QLIST_ENTRY(VFIOContainer) next; -} VFIOContainer; - -typedef struct VFIOGuestIOMMU { - VFIOContainer *container; - MemoryRegion *iommu; - Notifier n; - QLIST_ENTRY(VFIOGuestIOMMU) giommu_next; -} VFIOGuestIOMMU; - /* Cache of MSI-X setup plus extra mmap and memory region for split BAR map */ typedef struct VFIOMSIXInfo { uint8_t table_bar; @@ -193,9 +134,9 @@ typedef struct VFIOMSIXInfo { void *mmap; } VFIOMSIXInfo; -typedef struct VFIODevice { +typedef struct VFIOPCIDevice { PCIDevice pdev; - int fd; + VFIODevice vbasedev; VFIOINTx intx; unsigned int config_size; uint8_t *emulated_config_bits; /* QEMU emulated bits, little-endian */ @@ -211,31 +152,22 @@ typedef struct VFIODevice { VFIOBAR bars[PCI_NUM_REGIONS - 1]; /* No ROM */ VFIOVGA vga; /* 0xa0000, 0x3b0, 0x3c0 */ PCIHostDeviceAddress host; - QLIST_ENTRY(VFIODevice) next; - struct VFIOGroup *group; EventNotifier err_notifier; + EventNotifier req_notifier; 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 reset_works; bool has_vga; bool pci_aer; + bool req_enabled; bool has_flr; bool has_pm_reset; - bool needs_reset; bool rom_read_failed; -} VFIODevice; - -typedef struct VFIOGroup { - int fd; - int groupid; - VFIOContainer *container; - QLIST_HEAD(, VFIODevice) device_list; - QLIST_ENTRY(VFIOGroup) next; - QLIST_ENTRY(VFIOGroup) container_next; -} VFIOGroup; +} VFIOPCIDevice; typedef struct VFIORomBlacklistEntry { uint16_t vendor_id; @@ -262,72 +194,11 @@ static const VFIORomBlacklistEntry romblacklist[] = { #define MSIX_CAP_LENGTH 12 -static QLIST_HEAD(, VFIOGroup) - group_list = QLIST_HEAD_INITIALIZER(group_list); - -#ifdef CONFIG_KVM -/* - * We have a single VFIO pseudo device per KVM VM. Once created it lives - * for the life of the VM. Closing the file descriptor only drops our - * reference to it and the device's reference to kvm. Therefore once - * initialized, this file descriptor is only released on QEMU exit and - * we'll re-use it should another vfio device be attached before then. - */ -static int vfio_kvm_device_fd = -1; -#endif - -static void vfio_disable_interrupts(VFIODevice *vdev); +static void vfio_disable_interrupts(VFIOPCIDevice *vdev); static uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len); static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr, uint32_t val, int len); -static void vfio_mmap_set_enabled(VFIODevice *vdev, bool enabled); - -/* - * Common VFIO interrupt disable - */ -static void vfio_disable_irqindex(VFIODevice *vdev, 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(vdev->fd, VFIO_DEVICE_SET_IRQS, &irq_set); -} - -/* - * INTx - */ -static void vfio_unmask_intx(VFIODevice *vdev) -{ - struct vfio_irq_set irq_set = { - .argsz = sizeof(irq_set), - .flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_UNMASK, - .index = VFIO_PCI_INTX_IRQ_INDEX, - .start = 0, - .count = 1, - }; - - ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &irq_set); -} - -#ifdef CONFIG_KVM /* Unused outside of CONFIG_KVM code */ -static void vfio_mask_intx(VFIODevice *vdev) -{ - struct vfio_irq_set irq_set = { - .argsz = sizeof(irq_set), - .flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_MASK, - .index = VFIO_PCI_INTX_IRQ_INDEX, - .start = 0, - .count = 1, - }; - - ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &irq_set); -} -#endif +static void vfio_mmap_set_enabled(VFIOPCIDevice *vdev, bool enabled); /* * Disabling BAR mmaping can be slow, but toggling it around INTx can @@ -346,7 +217,7 @@ static void vfio_mask_intx(VFIODevice *vdev) */ static void vfio_intx_mmap_enable(void *opaque) { - VFIODevice *vdev = opaque; + VFIOPCIDevice *vdev = opaque; if (vdev->intx.pending) { timer_mod(vdev->intx.mmap_timer, @@ -359,15 +230,13 @@ static void vfio_intx_mmap_enable(void *opaque) static void vfio_intx_interrupt(void *opaque) { - VFIODevice *vdev = opaque; + VFIOPCIDevice *vdev = opaque; if (!event_notifier_test_and_clear(&vdev->intx.interrupt)) { return; } - DPRINTF("%s(%04x:%02x:%02x.%x) Pin %c\n", __func__, vdev->host.domain, - vdev->host.bus, vdev->host.slot, vdev->host.function, - 'A' + vdev->intx.pin); + trace_vfio_intx_interrupt(vdev->vbasedev.name, 'A' + vdev->intx.pin); vdev->intx.pending = true; pci_irq_assert(&vdev->pdev); @@ -378,21 +247,22 @@ static void vfio_intx_interrupt(void *opaque) } } -static void vfio_eoi(VFIODevice *vdev) +static void vfio_eoi(VFIODevice *vbasedev) { + VFIOPCIDevice *vdev = container_of(vbasedev, VFIOPCIDevice, vbasedev); + if (!vdev->intx.pending) { return; } - DPRINTF("%s(%04x:%02x:%02x.%x) EOI\n", __func__, vdev->host.domain, - vdev->host.bus, vdev->host.slot, vdev->host.function); + trace_vfio_eoi(vbasedev->name); vdev->intx.pending = false; pci_irq_deassert(&vdev->pdev); - vfio_unmask_intx(vdev); + vfio_unmask_single_irqindex(vbasedev, VFIO_PCI_INTX_IRQ_INDEX); } -static void vfio_enable_intx_kvm(VFIODevice *vdev) +static void vfio_enable_intx_kvm(VFIOPCIDevice *vdev) { #ifdef CONFIG_KVM struct kvm_irqfd irqfd = { @@ -406,13 +276,13 @@ static void vfio_enable_intx_kvm(VFIODevice *vdev) if (!VFIO_ALLOW_KVM_INTX || !kvm_irqfds_enabled() || vdev->intx.route.mode != PCI_INTX_ENABLED || - !kvm_check_extension(kvm_state, KVM_CAP_IRQFD_RESAMPLE)) { + !kvm_resamplefds_enabled()) { return; } /* Get to a known interrupt state */ qemu_set_fd_handler(irqfd.fd, NULL, NULL, vdev); - vfio_mask_intx(vdev); + vfio_mask_single_irqindex(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); vdev->intx.pending = false; pci_irq_deassert(&vdev->pdev); @@ -442,7 +312,7 @@ static void vfio_enable_intx_kvm(VFIODevice *vdev) *pfd = irqfd.resamplefd; - ret = ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, irq_set); + 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"); @@ -450,13 +320,11 @@ static void vfio_enable_intx_kvm(VFIODevice *vdev) } /* Let'em rip */ - vfio_unmask_intx(vdev); + vfio_unmask_single_irqindex(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); vdev->intx.kvm_accel = true; - DPRINTF("%s(%04x:%02x:%02x.%x) KVM INTx accel enabled\n", - __func__, vdev->host.domain, vdev->host.bus, - vdev->host.slot, vdev->host.function); + trace_vfio_enable_intx_kvm(vdev->vbasedev.name); return; @@ -467,11 +335,11 @@ fail_irqfd: event_notifier_cleanup(&vdev->intx.unmask); fail: qemu_set_fd_handler(irqfd.fd, vfio_intx_interrupt, NULL, vdev); - vfio_unmask_intx(vdev); + vfio_unmask_single_irqindex(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); #endif } -static void vfio_disable_intx_kvm(VFIODevice *vdev) +static void vfio_disable_intx_kvm(VFIOPCIDevice *vdev) { #ifdef CONFIG_KVM struct kvm_irqfd irqfd = { @@ -488,7 +356,7 @@ static void vfio_disable_intx_kvm(VFIODevice *vdev) * Get to a known state, hardware masked, QEMU ready to accept new * interrupts, QEMU IRQ de-asserted. */ - vfio_mask_intx(vdev); + vfio_mask_single_irqindex(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); vdev->intx.pending = false; pci_irq_deassert(&vdev->pdev); @@ -506,17 +374,15 @@ static void vfio_disable_intx_kvm(VFIODevice *vdev) vdev->intx.kvm_accel = false; /* If we've missed an event, let it re-fire through QEMU */ - vfio_unmask_intx(vdev); + vfio_unmask_single_irqindex(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); - DPRINTF("%s(%04x:%02x:%02x.%x) KVM INTx accel disabled\n", - __func__, vdev->host.domain, vdev->host.bus, - vdev->host.slot, vdev->host.function); + trace_vfio_disable_intx_kvm(vdev->vbasedev.name); #endif } static void vfio_update_irq(PCIDevice *pdev) { - VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev); + VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev); PCIINTxRoute route; if (vdev->interrupt != VFIO_INT_INTx) { @@ -529,9 +395,8 @@ static void vfio_update_irq(PCIDevice *pdev) return; /* Nothing changed */ } - DPRINTF("%s(%04x:%02x:%02x.%x) IRQ moved %d -> %d\n", __func__, - vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function, vdev->intx.route.irq, route.irq); + trace_vfio_update_irq(vdev->vbasedev.name, + vdev->intx.route.irq, route.irq); vfio_disable_intx_kvm(vdev); @@ -544,10 +409,10 @@ static void vfio_update_irq(PCIDevice *pdev) vfio_enable_intx_kvm(vdev); /* Re-enable the interrupt in cased we missed an EOI */ - vfio_eoi(vdev); + vfio_eoi(&vdev->vbasedev); } -static int vfio_enable_intx(VFIODevice *vdev) +static int vfio_enable_intx(VFIOPCIDevice *vdev) { uint8_t pin = vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1); int ret, argsz; @@ -568,8 +433,7 @@ static int vfio_enable_intx(VFIODevice *vdev) * Only conditional to avoid generating error messages on platforms * where we won't actually use the result anyway. */ - if (kvm_irqfds_enabled() && - kvm_check_extension(kvm_state, KVM_CAP_IRQFD_RESAMPLE)) { + if (kvm_irqfds_enabled() && kvm_resamplefds_enabled()) { vdev->intx.route = pci_device_route_intx_to_irq(&vdev->pdev, vdev->intx.pin); } @@ -594,7 +458,7 @@ static int vfio_enable_intx(VFIODevice *vdev) *pfd = event_notifier_get_fd(&vdev->intx.interrupt); qemu_set_fd_handler(*pfd, vfio_intx_interrupt, NULL, vdev); - ret = ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, irq_set); + 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"); @@ -607,19 +471,18 @@ static int vfio_enable_intx(VFIODevice *vdev) vdev->interrupt = VFIO_INT_INTx; - DPRINTF("%s(%04x:%02x:%02x.%x)\n", __func__, vdev->host.domain, - vdev->host.bus, vdev->host.slot, vdev->host.function); + trace_vfio_enable_intx(vdev->vbasedev.name); return 0; } -static void vfio_disable_intx(VFIODevice *vdev) +static void vfio_disable_intx(VFIOPCIDevice *vdev) { int fd; timer_del(vdev->intx.mmap_timer); vfio_disable_intx_kvm(vdev); - vfio_disable_irqindex(vdev, VFIO_PCI_INTX_IRQ_INDEX); + 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); @@ -630,8 +493,7 @@ static void vfio_disable_intx(VFIODevice *vdev) vdev->interrupt = VFIO_INT_NONE; - DPRINTF("%s(%04x:%02x:%02x.%x)\n", __func__, vdev->host.domain, - vdev->host.bus, vdev->host.slot, vdev->host.function); + trace_vfio_disable_intx(vdev->vbasedev.name); } /* @@ -640,7 +502,7 @@ static void vfio_disable_intx(VFIODevice *vdev) static void vfio_msi_interrupt(void *opaque) { VFIOMSIVector *vector = opaque; - VFIODevice *vdev = vector->vdev; + VFIOPCIDevice *vdev = vector->vdev; int nr = vector - vdev->msi_vectors; if (!event_notifier_test_and_clear(&vector->interrupt)) { @@ -658,9 +520,7 @@ static void vfio_msi_interrupt(void *opaque) abort(); } - DPRINTF("%s(%04x:%02x:%02x.%x) vector %d 0x%"PRIx64"/0x%x\n", __func__, - vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function, nr, msg.address, msg.data); + trace_vfio_msi_interrupt(vdev->vbasedev.name, nr, msg.address, msg.data); #endif if (vdev->interrupt == VFIO_INT_MSIX) { @@ -672,7 +532,7 @@ static void vfio_msi_interrupt(void *opaque) } } -static int vfio_enable_vectors(VFIODevice *vdev, bool msix) +static int vfio_enable_vectors(VFIOPCIDevice *vdev, bool msix) { struct vfio_irq_set *irq_set; int ret = 0, i, argsz; @@ -709,7 +569,7 @@ static int vfio_enable_vectors(VFIODevice *vdev, bool msix) fds[i] = fd; } - ret = ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, irq_set); + ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_SET_IRQS, irq_set); g_free(irq_set); @@ -763,13 +623,11 @@ static void vfio_update_kvm_msi_virq(VFIOMSIVector *vector, MSIMessage msg) static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr, MSIMessage *msg, IOHandler *handler) { - VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev); + VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev); VFIOMSIVector *vector; int ret; - DPRINTF("%s(%04x:%02x:%02x.%x) vector %d used\n", __func__, - vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function, nr); + trace_vfio_msix_vector_do_use(vdev->vbasedev.name, nr); vector = &vdev->msi_vectors[nr]; @@ -806,7 +664,7 @@ static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr, * increase them as needed. */ if (vdev->nr_vectors < nr + 1) { - vfio_disable_irqindex(vdev, VFIO_PCI_MSIX_IRQ_INDEX); + vfio_disable_irqindex(&vdev->vbasedev, VFIO_PCI_MSIX_IRQ_INDEX); vdev->nr_vectors = nr + 1; ret = vfio_enable_vectors(vdev, true); if (ret) { @@ -834,7 +692,7 @@ static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr, *pfd = event_notifier_get_fd(&vector->interrupt); } - ret = ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, irq_set); + 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); @@ -852,12 +710,10 @@ static int vfio_msix_vector_use(PCIDevice *pdev, static void vfio_msix_vector_release(PCIDevice *pdev, unsigned int nr) { - VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev); + VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev); VFIOMSIVector *vector = &vdev->msi_vectors[nr]; - DPRINTF("%s(%04x:%02x:%02x.%x) vector %d released\n", __func__, - vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function, nr); + trace_vfio_msix_vector_release(vdev->vbasedev.name, nr); /* * There are still old guests that mask and unmask vectors on every @@ -885,13 +741,13 @@ static void vfio_msix_vector_release(PCIDevice *pdev, unsigned int nr) *pfd = event_notifier_get_fd(&vector->interrupt); - ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, irq_set); + ioctl(vdev->vbasedev.fd, VFIO_DEVICE_SET_IRQS, irq_set); g_free(irq_set); } } -static void vfio_enable_msix(VFIODevice *vdev) +static void vfio_enable_msix(VFIOPCIDevice *vdev) { vfio_disable_interrupts(vdev); @@ -920,11 +776,10 @@ static void vfio_enable_msix(VFIODevice *vdev) error_report("vfio: msix_set_vector_notifiers failed"); } - DPRINTF("%s(%04x:%02x:%02x.%x)\n", __func__, vdev->host.domain, - vdev->host.bus, vdev->host.slot, vdev->host.function); + trace_vfio_enable_msix(vdev->vbasedev.name); } -static void vfio_enable_msi(VFIODevice *vdev) +static void vfio_enable_msi(VFIOPCIDevice *vdev) { int ret, i; @@ -997,12 +852,10 @@ retry: return; } - DPRINTF("%s(%04x:%02x:%02x.%x) Enabled %d MSI vectors\n", __func__, - vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function, vdev->nr_vectors); + trace_vfio_enable_msi(vdev->vbasedev.name, vdev->nr_vectors); } -static void vfio_disable_msi_common(VFIODevice *vdev) +static void vfio_disable_msi_common(VFIOPCIDevice *vdev) { int i; @@ -1026,7 +879,7 @@ static void vfio_disable_msi_common(VFIODevice *vdev) vfio_enable_intx(vdev); } -static void vfio_disable_msix(VFIODevice *vdev) +static void vfio_disable_msix(VFIOPCIDevice *vdev) { int i; @@ -1044,25 +897,23 @@ static void vfio_disable_msix(VFIODevice *vdev) } if (vdev->nr_vectors) { - vfio_disable_irqindex(vdev, VFIO_PCI_MSIX_IRQ_INDEX); + vfio_disable_irqindex(&vdev->vbasedev, VFIO_PCI_MSIX_IRQ_INDEX); } vfio_disable_msi_common(vdev); - DPRINTF("%s(%04x:%02x:%02x.%x)\n", __func__, vdev->host.domain, - vdev->host.bus, vdev->host.slot, vdev->host.function); + trace_vfio_disable_msix(vdev->vbasedev.name); } -static void vfio_disable_msi(VFIODevice *vdev) +static void vfio_disable_msi(VFIOPCIDevice *vdev) { - vfio_disable_irqindex(vdev, VFIO_PCI_MSI_IRQ_INDEX); + vfio_disable_irqindex(&vdev->vbasedev, VFIO_PCI_MSI_IRQ_INDEX); vfio_disable_msi_common(vdev); - DPRINTF("%s(%04x:%02x:%02x.%x)\n", __func__, vdev->host.domain, - vdev->host.bus, vdev->host.slot, vdev->host.function); + trace_vfio_disable_msi(vdev->vbasedev.name); } -static void vfio_update_msi(VFIODevice *vdev) +static void vfio_update_msi(VFIOPCIDevice *vdev) { int i; @@ -1079,119 +930,7 @@ static void vfio_update_msi(VFIODevice *vdev) } } -/* - * IO Port/MMIO - Beware of the endians, VFIO is always little endian - */ -static void vfio_bar_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - VFIOBAR *bar = opaque; - 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(bar->fd, &buf, size, bar->fd_offset + addr) != size) { - error_report("%s(,0x%"HWADDR_PRIx", 0x%"PRIx64", %d) failed: %m", - __func__, addr, data, size); - } - -#ifdef DEBUG_VFIO - { - VFIODevice *vdev = container_of(bar, VFIODevice, bars[bar->nr]); - - DPRINTF("%s(%04x:%02x:%02x.%x:BAR%d+0x%"HWADDR_PRIx", 0x%"PRIx64 - ", %d)\n", __func__, vdev->host.domain, vdev->host.bus, - vdev->host.slot, vdev->host.function, bar->nr, addr, - data, size); - } -#endif - - /* - * 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. - */ - vfio_eoi(container_of(bar, VFIODevice, bars[bar->nr])); -} - -static uint64_t vfio_bar_read(void *opaque, - hwaddr addr, unsigned size) -{ - VFIOBAR *bar = opaque; - union { - uint8_t byte; - uint16_t word; - uint32_t dword; - uint64_t qword; - } buf; - uint64_t data = 0; - - if (pread(bar->fd, &buf, size, bar->fd_offset + addr) != size) { - error_report("%s(,0x%"HWADDR_PRIx", %d) failed: %m", - __func__, 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; - } - -#ifdef DEBUG_VFIO - { - VFIODevice *vdev = container_of(bar, VFIODevice, bars[bar->nr]); - - DPRINTF("%s(%04x:%02x:%02x.%x:BAR%d+0x%"HWADDR_PRIx - ", %d) = 0x%"PRIx64"\n", __func__, vdev->host.domain, - vdev->host.bus, vdev->host.slot, vdev->host.function, - bar->nr, addr, size, data); - } -#endif - - /* Same as write above */ - vfio_eoi(container_of(bar, VFIODevice, bars[bar->nr])); - - return data; -} - -static const MemoryRegionOps vfio_bar_ops = { - .read = vfio_bar_read, - .write = vfio_bar_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void vfio_pci_load_rom(VFIODevice *vdev) +static void vfio_pci_load_rom(VFIOPCIDevice *vdev) { struct vfio_region_info reg_info = { .argsz = sizeof(reg_info), @@ -1201,16 +940,14 @@ static void vfio_pci_load_rom(VFIODevice *vdev) off_t off = 0; size_t bytes; - if (ioctl(vdev->fd, VFIO_DEVICE_GET_REGION_INFO, ®_info)) { + if (ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_REGION_INFO, ®_info)) { error_report("vfio: Error getting ROM info: %m"); return; } - DPRINTF("Device %04x:%02x:%02x.%x ROM:\n", vdev->host.domain, - vdev->host.bus, vdev->host.slot, vdev->host.function); - DPRINTF(" size: 0x%lx, offset: 0x%lx, flags: 0x%lx\n", - (unsigned long)reg_info.size, (unsigned long)reg_info.offset, - (unsigned long)reg_info.flags); + trace_vfio_pci_load_rom(vdev->vbasedev.name, (unsigned long)reg_info.size, + (unsigned long)reg_info.offset, + (unsigned long)reg_info.flags); vdev->rom_size = size = reg_info.size; vdev->rom_offset = reg_info.offset; @@ -1218,9 +955,7 @@ static void vfio_pci_load_rom(VFIODevice *vdev) if (!vdev->rom_size) { vdev->rom_read_failed = true; error_report("vfio-pci: Cannot read device rom at " - "%04x:%02x:%02x.%x", - vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function); + "%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"); @@ -1231,7 +966,8 @@ static void vfio_pci_load_rom(VFIODevice *vdev) memset(vdev->rom, 0xff, size); while (size) { - bytes = pread(vdev->fd, vdev->rom + off, size, vdev->rom_offset + off); + bytes = pread(vdev->vbasedev.fd, vdev->rom + off, + size, vdev->rom_offset + off); if (bytes == 0) { break; } else if (bytes > 0) { @@ -1249,7 +985,7 @@ static void vfio_pci_load_rom(VFIODevice *vdev) static uint64_t vfio_rom_read(void *opaque, hwaddr addr, unsigned size) { - VFIODevice *vdev = opaque; + VFIOPCIDevice *vdev = opaque; union { uint8_t byte; uint16_t word; @@ -1281,9 +1017,7 @@ static uint64_t vfio_rom_read(void *opaque, hwaddr addr, unsigned size) break; } - DPRINTF("%s(%04x:%02x:%02x.%x, 0x%"HWADDR_PRIx", 0x%x) = 0x%"PRIx64"\n", - __func__, vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function, addr, size, data); + trace_vfio_rom_read(vdev->vbasedev.name, addr, size, data); return data; } @@ -1299,7 +1033,7 @@ static const MemoryRegionOps vfio_rom_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static bool vfio_blacklist_opt_rom(VFIODevice *vdev) +static bool vfio_blacklist_opt_rom(VFIOPCIDevice *vdev) { PCIDevice *pdev = &vdev->pdev; uint16_t vendor_id, device_id; @@ -1319,12 +1053,13 @@ static bool vfio_blacklist_opt_rom(VFIODevice *vdev) return false; } -static void vfio_pci_size_rom(VFIODevice *vdev) +static void vfio_pci_size_rom(VFIOPCIDevice *vdev) { uint32_t orig, size = cpu_to_le32((uint32_t)PCI_ROM_ADDRESS_MASK); off_t offset = vdev->config_offset + PCI_ROM_ADDRESS; DeviceState *dev = DEVICE(vdev); char name[32]; + int fd = vdev->vbasedev.fd; if (vdev->pdev.romfile || !vdev->pdev.rom_bar) { /* Since pci handles romfile, just print a message and return */ @@ -1343,10 +1078,10 @@ static void vfio_pci_size_rom(VFIODevice *vdev) * 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(vdev->fd, &orig, 4, offset) != 4 || - pwrite(vdev->fd, &size, 4, offset) != 4 || - pread(vdev->fd, &size, 4, offset) != 4 || - pwrite(vdev->fd, &orig, 4, offset) != 4) { + 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(%04x:%02x:%02x.%x) failed: %m", __func__, vdev->host.domain, vdev->host.bus, vdev->host.slot, vdev->host.function); @@ -1379,8 +1114,7 @@ static void vfio_pci_size_rom(VFIODevice *vdev) } } - DPRINTF("%04x:%02x:%02x.%x ROM size 0x%x\n", vdev->host.domain, - vdev->host.bus, vdev->host.slot, vdev->host.function, size); + trace_vfio_pci_size_rom(vdev->vbasedev.name, size); snprintf(name, sizeof(name), "vfio[%04x:%02x:%02x.%x].rom", vdev->host.domain, vdev->host.bus, vdev->host.slot, @@ -1429,8 +1163,7 @@ static void vfio_vga_write(void *opaque, hwaddr addr, __func__, region->offset + addr, data, size); } - DPRINTF("%s(0x%"HWADDR_PRIx", 0x%"PRIx64", %d)\n", - __func__, region->offset + addr, data, size); + trace_vfio_vga_write(region->offset + addr, data, size); } static uint64_t vfio_vga_read(void *opaque, hwaddr addr, unsigned size) @@ -1467,8 +1200,7 @@ static uint64_t vfio_vga_read(void *opaque, hwaddr addr, unsigned size) break; } - DPRINTF("%s(0x%"HWADDR_PRIx", %d) = 0x%"PRIx64"\n", - __func__, region->offset + addr, size, data); + trace_vfio_vga_read(region->offset + addr, size, data); return data; } @@ -1498,7 +1230,7 @@ static uint64_t vfio_generic_window_quirk_read(void *opaque, hwaddr addr, unsigned size) { VFIOQuirk *quirk = opaque; - VFIODevice *vdev = quirk->vdev; + VFIOPCIDevice *vdev = quirk->vdev; uint64_t data; if (vfio_flags_enabled(quirk->data.flags, quirk->data.read_flags) && @@ -1515,13 +1247,13 @@ static uint64_t vfio_generic_window_quirk_read(void *opaque, data = vfio_pci_read_config(&vdev->pdev, quirk->data.address_val + offset, size); - DPRINTF("%s read(%04x:%02x:%02x.%x:BAR%d+0x%"HWADDR_PRIx", %d) = 0x%" - PRIx64"\n", memory_region_name(&quirk->mem), vdev->host.domain, - vdev->host.bus, vdev->host.slot, vdev->host.function, - quirk->data.bar, addr, size, data); + trace_vfio_generic_window_quirk_read(memory_region_name(&quirk->mem), + vdev->vbasedev.name, + quirk->data.bar, + addr, size, data); } else { - data = vfio_bar_read(&vdev->bars[quirk->data.bar], - addr + quirk->data.base_offset, size); + data = vfio_region_read(&vdev->bars[quirk->data.bar].region, + addr + quirk->data.base_offset, size); } return data; @@ -1531,7 +1263,7 @@ static void vfio_generic_window_quirk_write(void *opaque, hwaddr addr, uint64_t data, unsigned size) { VFIOQuirk *quirk = opaque; - VFIODevice *vdev = quirk->vdev; + VFIOPCIDevice *vdev = quirk->vdev; if (ranges_overlap(addr, size, quirk->data.address_offset, quirk->data.address_size)) { @@ -1564,14 +1296,14 @@ static void vfio_generic_window_quirk_write(void *opaque, hwaddr addr, vfio_pci_write_config(&vdev->pdev, quirk->data.address_val + offset, data, size); - DPRINTF("%s write(%04x:%02x:%02x.%x:BAR%d+0x%"HWADDR_PRIx", 0x%" - PRIx64", %d)\n", memory_region_name(&quirk->mem), - vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function, quirk->data.bar, addr, data, size); + trace_vfio_generic_window_quirk_write(memory_region_name(&quirk->mem), + vdev->vbasedev.name, + quirk->data.bar, + addr, data, size); return; } - vfio_bar_write(&vdev->bars[quirk->data.bar], + vfio_region_write(&vdev->bars[quirk->data.bar].region, addr + quirk->data.base_offset, data, size); } @@ -1585,7 +1317,7 @@ static uint64_t vfio_generic_quirk_read(void *opaque, hwaddr addr, unsigned size) { VFIOQuirk *quirk = opaque; - VFIODevice *vdev = quirk->vdev; + VFIOPCIDevice *vdev = quirk->vdev; hwaddr base = quirk->data.address_match & TARGET_PAGE_MASK; hwaddr offset = quirk->data.address_match & ~TARGET_PAGE_MASK; uint64_t data; @@ -1600,12 +1332,12 @@ static uint64_t vfio_generic_quirk_read(void *opaque, data = vfio_pci_read_config(&vdev->pdev, addr - offset, size); - DPRINTF("%s read(%04x:%02x:%02x.%x:BAR%d+0x%"HWADDR_PRIx", %d) = 0x%" - PRIx64"\n", memory_region_name(&quirk->mem), vdev->host.domain, - vdev->host.bus, vdev->host.slot, vdev->host.function, - quirk->data.bar, addr + base, size, data); + trace_vfio_generic_quirk_read(memory_region_name(&quirk->mem), + vdev->vbasedev.name, quirk->data.bar, + addr + base, size, data); } else { - data = vfio_bar_read(&vdev->bars[quirk->data.bar], addr + base, size); + data = vfio_region_read(&vdev->bars[quirk->data.bar].region, + addr + base, size); } return data; @@ -1615,7 +1347,7 @@ static void vfio_generic_quirk_write(void *opaque, hwaddr addr, uint64_t data, unsigned size) { VFIOQuirk *quirk = opaque; - VFIODevice *vdev = quirk->vdev; + VFIOPCIDevice *vdev = quirk->vdev; hwaddr base = quirk->data.address_match & TARGET_PAGE_MASK; hwaddr offset = quirk->data.address_match & ~TARGET_PAGE_MASK; @@ -1629,12 +1361,12 @@ static void vfio_generic_quirk_write(void *opaque, hwaddr addr, vfio_pci_write_config(&vdev->pdev, addr - offset, data, size); - DPRINTF("%s write(%04x:%02x:%02x.%x:BAR%d+0x%"HWADDR_PRIx", 0x%" - PRIx64", %d)\n", memory_region_name(&quirk->mem), - vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function, quirk->data.bar, addr + base, data, size); + trace_vfio_generic_quirk_write(memory_region_name(&quirk->mem), + vdev->vbasedev.name, quirk->data.bar, + addr + base, data, size); } else { - vfio_bar_write(&vdev->bars[quirk->data.bar], addr + base, data, size); + vfio_region_write(&vdev->bars[quirk->data.bar].region, + addr + base, data, size); } } @@ -1660,11 +1392,11 @@ static uint64_t vfio_ati_3c3_quirk_read(void *opaque, hwaddr addr, unsigned size) { VFIOQuirk *quirk = opaque; - VFIODevice *vdev = quirk->vdev; + VFIOPCIDevice *vdev = quirk->vdev; uint64_t data = vfio_pci_read_config(&vdev->pdev, PCI_BASE_ADDRESS_0 + (4 * 4) + 1, size); - DPRINTF("%s(0x3c3, 1) = 0x%"PRIx64"\n", __func__, data); + trace_vfio_ati_3c3_quirk_read(data); return data; } @@ -1674,7 +1406,7 @@ static const MemoryRegionOps vfio_ati_3c3_quirk = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static void vfio_vga_probe_ati_3c3_quirk(VFIODevice *vdev) +static void vfio_vga_probe_ati_3c3_quirk(VFIOPCIDevice *vdev) { PCIDevice *pdev = &vdev->pdev; VFIOQuirk *quirk; @@ -1687,7 +1419,7 @@ static void vfio_vga_probe_ati_3c3_quirk(VFIODevice *vdev) * As long as the BAR is >= 256 bytes it will be aligned such that the * lower byte is always zero. Filter out anything else, if it exists. */ - if (!vdev->bars[4].ioport || vdev->bars[4].size < 256) { + if (!vdev->bars[4].ioport || vdev->bars[4].region.size < 256) { return; } @@ -1702,9 +1434,7 @@ static void vfio_vga_probe_ati_3c3_quirk(VFIODevice *vdev) QLIST_INSERT_HEAD(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].quirks, quirk, next); - DPRINTF("Enabled ATI/AMD quirk 0x3c3 BAR4for device %04x:%02x:%02x.%x\n", - vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function); + trace_vfio_vga_probe_ati_3c3_quirk(vdev->vbasedev.name); } /* @@ -1717,7 +1447,7 @@ static void vfio_vga_probe_ati_3c3_quirk(VFIODevice *vdev) * that only read-only access is provided, but we drop writes when the window * is enabled to config space nonetheless. */ -static void vfio_probe_ati_bar4_window_quirk(VFIODevice *vdev, int nr) +static void vfio_probe_ati_bar4_window_quirk(VFIOPCIDevice *vdev, int nr) { PCIDevice *pdev = &vdev->pdev; VFIOQuirk *quirk; @@ -1740,14 +1470,12 @@ static void vfio_probe_ati_bar4_window_quirk(VFIODevice *vdev, int nr) memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_generic_window_quirk, quirk, "vfio-ati-bar4-window-quirk", 8); - memory_region_add_subregion_overlap(&vdev->bars[nr].mem, + memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem, quirk->data.base_offset, &quirk->mem, 1); QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); - DPRINTF("Enabled ATI/AMD BAR4 window quirk for device %04x:%02x:%02x.%x\n", - vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function); + trace_vfio_probe_ati_bar4_window_quirk(vdev->vbasedev.name); } #define PCI_VENDOR_ID_REALTEK 0x10ec @@ -1779,25 +1507,25 @@ static uint64_t vfio_rtl8168_window_quirk_read(void *opaque, hwaddr addr, unsigned size) { VFIOQuirk *quirk = opaque; - VFIODevice *vdev = quirk->vdev; + VFIOPCIDevice *vdev = quirk->vdev; switch (addr) { case 4: /* address */ if (quirk->data.flags) { - DPRINTF("%s fake read(%04x:%02x:%02x.%d)\n", - memory_region_name(&quirk->mem), vdev->host.domain, - vdev->host.bus, vdev->host.slot, vdev->host.function); + trace_vfio_rtl8168_window_quirk_read_fake( + memory_region_name(&quirk->mem), + vdev->vbasedev.name); - return quirk->data.address_match ^ 0x10000000U; + return quirk->data.address_match ^ 0x80000000U; } break; case 0: /* data */ if (quirk->data.flags) { uint64_t val; - DPRINTF("%s MSI-X table read(%04x:%02x:%02x.%d)\n", - memory_region_name(&quirk->mem), vdev->host.domain, - vdev->host.bus, vdev->host.slot, vdev->host.function); + trace_vfio_rtl8168_window_quirk_read_table( + memory_region_name(&quirk->mem), + vdev->vbasedev.name); if (!(vdev->pdev.cap_present & QEMU_PCI_CAP_MSIX)) { return 0; @@ -1810,32 +1538,33 @@ static uint64_t vfio_rtl8168_window_quirk_read(void *opaque, } } - DPRINTF("%s direct read(%04x:%02x:%02x.%d)\n", - memory_region_name(&quirk->mem), vdev->host.domain, - vdev->host.bus, vdev->host.slot, vdev->host.function); + trace_vfio_rtl8168_window_quirk_read_direct(memory_region_name(&quirk->mem), + vdev->vbasedev.name); - return vfio_bar_read(&vdev->bars[quirk->data.bar], addr + 0x70, size); + return vfio_region_read(&vdev->bars[quirk->data.bar].region, + addr + 0x70, size); } static void vfio_rtl8168_window_quirk_write(void *opaque, hwaddr addr, uint64_t data, unsigned size) { VFIOQuirk *quirk = opaque; - VFIODevice *vdev = quirk->vdev; + VFIOPCIDevice *vdev = quirk->vdev; switch (addr) { case 4: /* address */ if ((data & 0x7fff0000) == 0x10000) { - if (data & 0x10000000U && + if (data & 0x80000000U && vdev->pdev.cap_present & QEMU_PCI_CAP_MSIX) { - DPRINTF("%s MSI-X table write(%04x:%02x:%02x.%d)\n", - memory_region_name(&quirk->mem), vdev->host.domain, - vdev->host.bus, vdev->host.slot, vdev->host.function); + trace_vfio_rtl8168_window_quirk_write_table( + memory_region_name(&quirk->mem), + vdev->vbasedev.name); io_mem_write(&vdev->pdev.msix_table_mmio, - (hwaddr)(quirk->data.address_match & 0xfff), - data, size); + (hwaddr)(data & 0xfff), + (uint64_t)quirk->data.address_mask, + size); } quirk->data.flags = 1; @@ -1850,11 +1579,12 @@ static void vfio_rtl8168_window_quirk_write(void *opaque, hwaddr addr, break; } - DPRINTF("%s direct write(%04x:%02x:%02x.%d)\n", - memory_region_name(&quirk->mem), vdev->host.domain, - vdev->host.bus, vdev->host.slot, vdev->host.function); + trace_vfio_rtl8168_window_quirk_write_direct( + memory_region_name(&quirk->mem), + vdev->vbasedev.name); - vfio_bar_write(&vdev->bars[quirk->data.bar], addr + 0x70, data, size); + vfio_region_write(&vdev->bars[quirk->data.bar].region, + addr + 0x70, data, size); } static const MemoryRegionOps vfio_rtl8168_window_quirk = { @@ -1868,7 +1598,7 @@ static const MemoryRegionOps vfio_rtl8168_window_quirk = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static void vfio_probe_rtl8168_bar2_window_quirk(VFIODevice *vdev, int nr) +static void vfio_probe_rtl8168_bar2_window_quirk(VFIOPCIDevice *vdev, int nr) { PCIDevice *pdev = &vdev->pdev; VFIOQuirk *quirk; @@ -1884,19 +1614,17 @@ static void vfio_probe_rtl8168_bar2_window_quirk(VFIODevice *vdev, int nr) memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_rtl8168_window_quirk, quirk, "vfio-rtl8168-window-quirk", 8); - memory_region_add_subregion_overlap(&vdev->bars[nr].mem, + memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem, 0x70, &quirk->mem, 1); QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); - DPRINTF("Enabled RTL8168 BAR2 window quirk for device %04x:%02x:%02x.%x\n", - vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function); + trace_vfio_probe_rtl8168_bar2_window_quirk(vdev->vbasedev.name); } /* * Trap the BAR2 MMIO window to config space as well. */ -static void vfio_probe_ati_bar2_4000_quirk(VFIODevice *vdev, int nr) +static void vfio_probe_ati_bar2_4000_quirk(VFIOPCIDevice *vdev, int nr) { PCIDevice *pdev = &vdev->pdev; VFIOQuirk *quirk; @@ -1917,15 +1645,13 @@ static void vfio_probe_ati_bar2_4000_quirk(VFIODevice *vdev, int nr) memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_generic_quirk, quirk, "vfio-ati-bar2-4000-quirk", TARGET_PAGE_ALIGN(quirk->data.address_mask + 1)); - memory_region_add_subregion_overlap(&vdev->bars[nr].mem, + memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem, quirk->data.address_match & TARGET_PAGE_MASK, &quirk->mem, 1); QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); - DPRINTF("Enabled ATI/AMD BAR2 0x4000 quirk for device %04x:%02x:%02x.%x\n", - vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function); + trace_vfio_probe_ati_bar2_4000_quirk(vdev->vbasedev.name); } /* @@ -1964,14 +1690,14 @@ static uint64_t vfio_nvidia_3d0_quirk_read(void *opaque, hwaddr addr, unsigned size) { VFIOQuirk *quirk = opaque; - VFIODevice *vdev = quirk->vdev; + VFIOPCIDevice *vdev = quirk->vdev; PCIDevice *pdev = &vdev->pdev; uint64_t data = vfio_vga_read(&vdev->vga.region[QEMU_PCI_VGA_IO_HI], addr + quirk->data.base_offset, size); if (quirk->data.flags == NV_3D0_READ && addr == quirk->data.data_offset) { data = vfio_pci_read_config(pdev, quirk->data.address_val, size); - DPRINTF("%s(0x3d0, %d) = 0x%"PRIx64"\n", __func__, size, data); + trace_vfio_nvidia_3d0_quirk_read(size, data); } quirk->data.flags = NV_3D0_NONE; @@ -1983,7 +1709,7 @@ static void vfio_nvidia_3d0_quirk_write(void *opaque, hwaddr addr, uint64_t data, unsigned size) { VFIOQuirk *quirk = opaque; - VFIODevice *vdev = quirk->vdev; + VFIOPCIDevice *vdev = quirk->vdev; PCIDevice *pdev = &vdev->pdev; switch (quirk->data.flags) { @@ -2014,7 +1740,7 @@ static void vfio_nvidia_3d0_quirk_write(void *opaque, hwaddr addr, quirk->data.flags = NV_3D0_NONE; if (addr == quirk->data.data_offset) { vfio_pci_write_config(pdev, quirk->data.address_val, data, size); - DPRINTF("%s(0x3d0, 0x%"PRIx64", %d)\n", __func__, data, size); + trace_vfio_nvidia_3d0_quirk_write(data, size); return; } break; @@ -2030,13 +1756,13 @@ static const MemoryRegionOps vfio_nvidia_3d0_quirk = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static void vfio_vga_probe_nvidia_3d0_quirk(VFIODevice *vdev) +static void vfio_vga_probe_nvidia_3d0_quirk(VFIOPCIDevice *vdev) { PCIDevice *pdev = &vdev->pdev; VFIOQuirk *quirk; if (pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_NVIDIA || - !vdev->bars[1].size) { + !vdev->bars[1].region.size) { return; } @@ -2058,9 +1784,7 @@ static void vfio_vga_probe_nvidia_3d0_quirk(VFIODevice *vdev) QLIST_INSERT_HEAD(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].quirks, quirk, next); - DPRINTF("Enabled NVIDIA VGA 0x3d0 quirk for device %04x:%02x:%02x.%x\n", - vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function); + trace_vfio_vga_probe_nvidia_3d0_quirk(vdev->vbasedev.name); } /* @@ -2122,7 +1846,7 @@ static const MemoryRegionOps vfio_nvidia_bar5_window_quirk = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static void vfio_probe_nvidia_bar5_window_quirk(VFIODevice *vdev, int nr) +static void vfio_probe_nvidia_bar5_window_quirk(VFIOPCIDevice *vdev, int nr) { PCIDevice *pdev = &vdev->pdev; VFIOQuirk *quirk; @@ -2144,20 +1868,19 @@ static void vfio_probe_nvidia_bar5_window_quirk(VFIODevice *vdev, int nr) memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_nvidia_bar5_window_quirk, quirk, "vfio-nvidia-bar5-window-quirk", 16); - memory_region_add_subregion_overlap(&vdev->bars[nr].mem, 0, &quirk->mem, 1); + memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem, + 0, &quirk->mem, 1); QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); - DPRINTF("Enabled NVIDIA BAR5 window quirk for device %04x:%02x:%02x.%x\n", - vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function); + trace_vfio_probe_nvidia_bar5_window_quirk(vdev->vbasedev.name); } static void vfio_nvidia_88000_quirk_write(void *opaque, hwaddr addr, uint64_t data, unsigned size) { VFIOQuirk *quirk = opaque; - VFIODevice *vdev = quirk->vdev; + VFIOPCIDevice *vdev = quirk->vdev; PCIDevice *pdev = &vdev->pdev; hwaddr base = quirk->data.address_match & TARGET_PAGE_MASK; @@ -2171,7 +1894,8 @@ static void vfio_nvidia_88000_quirk_write(void *opaque, hwaddr addr, */ if ((pdev->cap_present & QEMU_PCI_CAP_MSI) && vfio_range_contained(addr, size, pdev->msi_cap, PCI_MSI_FLAGS)) { - vfio_bar_write(&vdev->bars[quirk->data.bar], addr + base, data, size); + vfio_region_write(&vdev->bars[quirk->data.bar].region, + addr + base, data, size); } } @@ -2190,7 +1914,7 @@ static const MemoryRegionOps vfio_nvidia_88000_quirk = { * * Here's offset 0x88000... */ -static void vfio_probe_nvidia_bar0_88000_quirk(VFIODevice *vdev, int nr) +static void vfio_probe_nvidia_bar0_88000_quirk(VFIOPCIDevice *vdev, int nr) { PCIDevice *pdev = &vdev->pdev; VFIOQuirk *quirk; @@ -2214,21 +1938,19 @@ static void vfio_probe_nvidia_bar0_88000_quirk(VFIODevice *vdev, int nr) memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_nvidia_88000_quirk, quirk, "vfio-nvidia-bar0-88000-quirk", TARGET_PAGE_ALIGN(quirk->data.address_mask + 1)); - memory_region_add_subregion_overlap(&vdev->bars[nr].mem, + memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem, quirk->data.address_match & TARGET_PAGE_MASK, &quirk->mem, 1); QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); - DPRINTF("Enabled NVIDIA BAR0 0x88000 quirk for device %04x:%02x:%02x.%x\n", - vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function); + trace_vfio_probe_nvidia_bar0_88000_quirk(vdev->vbasedev.name); } /* * And here's the same for BAR0 offset 0x1800... */ -static void vfio_probe_nvidia_bar0_1800_quirk(VFIODevice *vdev, int nr) +static void vfio_probe_nvidia_bar0_1800_quirk(VFIOPCIDevice *vdev, int nr) { PCIDevice *pdev = &vdev->pdev; VFIOQuirk *quirk; @@ -2239,8 +1961,9 @@ static void vfio_probe_nvidia_bar0_1800_quirk(VFIODevice *vdev, int nr) } /* Log the chipset ID */ - DPRINTF("Nvidia NV%02x\n", - (unsigned int)(vfio_bar_read(&vdev->bars[0], 0, 4) >> 20) & 0xff); + trace_vfio_probe_nvidia_bar0_1800_quirk_id( + (unsigned int)(vfio_region_read(&vdev->bars[0].region, 0, 4) >> 20) + & 0xff); quirk = g_malloc0(sizeof(*quirk)); quirk->vdev = vdev; @@ -2252,15 +1975,13 @@ static void vfio_probe_nvidia_bar0_1800_quirk(VFIODevice *vdev, int nr) memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_generic_quirk, quirk, "vfio-nvidia-bar0-1800-quirk", TARGET_PAGE_ALIGN(quirk->data.address_mask + 1)); - memory_region_add_subregion_overlap(&vdev->bars[nr].mem, + memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem, quirk->data.address_match & TARGET_PAGE_MASK, &quirk->mem, 1); QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); - DPRINTF("Enabled NVIDIA BAR0 0x1800 quirk for device %04x:%02x:%02x.%x\n", - vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function); + trace_vfio_probe_nvidia_bar0_1800_quirk(vdev->vbasedev.name); } /* @@ -2272,20 +1993,31 @@ static void vfio_probe_nvidia_bar0_1800_quirk(VFIODevice *vdev, int nr) /* * Common quirk probe entry points. */ -static void vfio_vga_quirk_setup(VFIODevice *vdev) +static void vfio_vga_quirk_setup(VFIOPCIDevice *vdev) { vfio_vga_probe_ati_3c3_quirk(vdev); vfio_vga_probe_nvidia_3d0_quirk(vdev); } -static void vfio_vga_quirk_teardown(VFIODevice *vdev) +static void vfio_vga_quirk_teardown(VFIOPCIDevice *vdev) +{ + VFIOQuirk *quirk; + int i; + + for (i = 0; i < ARRAY_SIZE(vdev->vga.region); i++) { + QLIST_FOREACH(quirk, &vdev->vga.region[i].quirks, next) { + memory_region_del_subregion(&vdev->vga.region[i].mem, &quirk->mem); + } + } +} + +static void vfio_vga_quirk_free(VFIOPCIDevice *vdev) { int i; for (i = 0; i < ARRAY_SIZE(vdev->vga.region); i++) { while (!QLIST_EMPTY(&vdev->vga.region[i].quirks)) { VFIOQuirk *quirk = QLIST_FIRST(&vdev->vga.region[i].quirks); - memory_region_del_subregion(&vdev->vga.region[i].mem, &quirk->mem); object_unparent(OBJECT(&quirk->mem)); QLIST_REMOVE(quirk, next); g_free(quirk); @@ -2293,7 +2025,7 @@ static void vfio_vga_quirk_teardown(VFIODevice *vdev) } } -static void vfio_bar_quirk_setup(VFIODevice *vdev, int nr) +static void vfio_bar_quirk_setup(VFIOPCIDevice *vdev, int nr) { vfio_probe_ati_bar4_window_quirk(vdev, nr); vfio_probe_ati_bar2_4000_quirk(vdev, nr); @@ -2303,13 +2035,22 @@ static void vfio_bar_quirk_setup(VFIODevice *vdev, int nr) vfio_probe_rtl8168_bar2_window_quirk(vdev, nr); } -static void vfio_bar_quirk_teardown(VFIODevice *vdev, int nr) +static void vfio_bar_quirk_teardown(VFIOPCIDevice *vdev, int nr) +{ + VFIOBAR *bar = &vdev->bars[nr]; + VFIOQuirk *quirk; + + QLIST_FOREACH(quirk, &bar->quirks, next) { + memory_region_del_subregion(&bar->region.mem, &quirk->mem); + } +} + +static void vfio_bar_quirk_free(VFIOPCIDevice *vdev, int nr) { VFIOBAR *bar = &vdev->bars[nr]; while (!QLIST_EMPTY(&bar->quirks)) { VFIOQuirk *quirk = QLIST_FIRST(&bar->quirks); - memory_region_del_subregion(&bar->mem, &quirk->mem); object_unparent(OBJECT(&quirk->mem)); QLIST_REMOVE(quirk, next); g_free(quirk); @@ -2321,7 +2062,7 @@ static void vfio_bar_quirk_teardown(VFIODevice *vdev, int nr) */ static uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len) { - VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev); + 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); @@ -2334,7 +2075,8 @@ static uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len) if (~emu_bits & (0xffffffffU >> (32 - len * 8))) { ssize_t ret; - ret = pread(vdev->fd, &phys_val, len, vdev->config_offset + addr); + ret = pread(vdev->vbasedev.fd, &phys_val, len, + vdev->config_offset + addr); if (ret != len) { error_report("%s(%04x:%02x:%02x.%x, 0x%x, 0x%x) failed: %m", __func__, vdev->host.domain, vdev->host.bus, @@ -2346,9 +2088,7 @@ static uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len) val = (emu_val & emu_bits) | (phys_val & ~emu_bits); - DPRINTF("%s(%04x:%02x:%02x.%x, @0x%x, len=0x%x) %x\n", __func__, - vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function, addr, len, val); + trace_vfio_pci_read_config(vdev->vbasedev.name, addr, len, val); return val; } @@ -2356,15 +2096,14 @@ static uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len) static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr, uint32_t val, int len) { - VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev); + VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev); uint32_t val_le = cpu_to_le32(val); - DPRINTF("%s(%04x:%02x:%02x.%x, @0x%x, 0x%x, len=0x%x)\n", __func__, - vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function, addr, val, len); + 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->fd, &val_le, len, vdev->config_offset + addr) != len) { + if (pwrite(vdev->vbasedev.fd, &val_le, len, vdev->config_offset + addr) + != len) { error_report("%s(%04x:%02x:%02x.%x, 0x%x, 0x%x, 0x%x) failed: %m", __func__, vdev->host.domain, vdev->host.bus, vdev->host.slot, vdev->host.function, addr, val, len); @@ -2410,418 +2149,119 @@ static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr, } /* - * DMA - Mapping and unmapping for the "type1" IOMMU interface used on x86 + * Interrupt setup */ -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)) { - DPRINTF("VFIO_UNMAP_DMA: %d\n", -errno); - return -errno; - } - - return 0; -} - -static int vfio_dma_map(VFIOContainer *container, hwaddr iova, - ram_addr_t size, void *vaddr, bool readonly) +static void vfio_disable_interrupts(VFIOPCIDevice *vdev) { - 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 the VGA ROM space. + * 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 (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; + if (vdev->interrupt == VFIO_INT_MSIX) { + vfio_disable_msix(vdev); + } else if (vdev->interrupt == VFIO_INT_MSI) { + vfio_disable_msi(vdev); } - DPRINTF("VFIO_MAP_DMA: %d\n", -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); + if (vdev->interrupt == VFIO_INT_INTx) { + vfio_disable_intx(vdev); + } } -static void vfio_iommu_map_notify(Notifier *n, void *data) +static int vfio_setup_msi(VFIOPCIDevice *vdev, int pos) { - 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; - - DPRINTF("iommu map @ %"HWADDR_PRIx" - %"HWADDR_PRIx"\n", - iotlb->iova, iotlb->iova + iotlb->addr_mask); + uint16_t ctrl; + bool msi_64bit, msi_maskbit; + int ret, entries; - /* - * 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. - */ - mr = address_space_translate(&address_space_memory, - iotlb->translated_addr, - &xlat, &len, iotlb->perm & IOMMU_WO); - if (!memory_region_is_ram(mr)) { - DPRINTF("iommu map to non memory area %"HWADDR_PRIx"\n", - xlat); - return; - } - /* - * Translation truncates length to the IOMMU page size, - * check that it did not truncate too much. - */ - if (len & iotlb->addr_mask) { - DPRINTF("iommu has granularity incompatible with target AS\n"); - return; + if (pread(vdev->vbasedev.fd, &ctrl, sizeof(ctrl), + vdev->config_offset + pos + PCI_CAP_FLAGS) != sizeof(ctrl)) { + return -errno; } + ctrl = le16_to_cpu(ctrl); - if ((iotlb->perm & IOMMU_RW) != IOMMU_NONE) { - vaddr = memory_region_get_ram_ptr(mr) + xlat; + msi_64bit = !!(ctrl & PCI_MSI_FLAGS_64BIT); + msi_maskbit = !!(ctrl & PCI_MSI_FLAGS_MASKBIT); + entries = 1 << ((ctrl & PCI_MSI_FLAGS_QMASK) >> 1); - 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); + trace_vfio_setup_msi(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_listener_region_add(MemoryListener *listener, - MemoryRegionSection *section) +/* + * 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_early_setup_msix(VFIOPCIDevice *vdev) { - VFIOContainer *container = container_of(listener, VFIOContainer, - iommu_data.type1.listener); - hwaddr iova, end; - Int128 llend; - void *vaddr; - int ret; + uint8_t pos; + uint16_t ctrl; + uint32_t table, pba; + int fd = vdev->vbasedev.fd; - if (vfio_listener_skipped_section(section)) { - DPRINTF("SKIPPING region_add %"HWADDR_PRIx" - %"PRIx64"\n", - section->offset_within_address_space, - section->offset_within_address_space + - int128_get64(int128_sub(section->size, int128_one()))); - return; + pos = pci_find_capability(&vdev->pdev, PCI_CAP_ID_MSIX); + if (!pos) { + return 0; } - 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 (pread(fd, &ctrl, sizeof(ctrl), + vdev->config_offset + pos + PCI_CAP_FLAGS) != sizeof(ctrl)) { + return -errno; } - 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 (pread(fd, &table, sizeof(table), + vdev->config_offset + pos + PCI_MSIX_TABLE) != sizeof(table)) { + return -errno; + } - if (int128_ge(int128_make64(iova), llend)) { - return; + if (pread(fd, &pba, sizeof(pba), + vdev->config_offset + pos + PCI_MSIX_PBA) != sizeof(pba)) { + return -errno; } - memory_region_ref(section->mr); + ctrl = le16_to_cpu(ctrl); + table = le32_to_cpu(table); + pba = le32_to_cpu(pba); - if (memory_region_is_iommu(section->mr)) { - VFIOGuestIOMMU *giommu; + vdev->msix = g_malloc0(sizeof(*(vdev->msix))); + vdev->msix->table_bar = table & PCI_MSIX_FLAGS_BIRMASK; + vdev->msix->table_offset = table & ~PCI_MSIX_FLAGS_BIRMASK; + vdev->msix->pba_bar = pba & PCI_MSIX_FLAGS_BIRMASK; + vdev->msix->pba_offset = pba & ~PCI_MSIX_FLAGS_BIRMASK; + vdev->msix->entries = (ctrl & PCI_MSIX_FLAGS_QSIZE) + 1; - DPRINTF("region_add [iommu] %"HWADDR_PRIx" - %"HWADDR_PRIx"\n", - iova, int128_get64(int128_sub(llend, int128_one()))); - /* - * 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). - */ - /* - * This assumes that the guest IOMMU is empty of - * mappings at this point. - * - * One way of doing this is: - * 1. Avoid sharing IOMMUs between emulated devices or different - * IOMMU groups. - * 2. Implement VFIO_IOMMU_ENABLE in the host kernel to fail if - * there are some mappings in IOMMU. - * - * VFIO on SPAPR does that. Other IOMMU models may do that different, - * they must make sure there are no existing mappings or - * loop through existing mappings to map them into VFIO. - */ - giommu = g_malloc0(sizeof(*giommu)); - giommu->iommu = section->mr; - giommu->container = container; - giommu->n.notify = vfio_iommu_map_notify; - QLIST_INSERT_HEAD(&container->giommu_list, giommu, giommu_next); - memory_region_register_iommu_notifier(giommu->iommu, &giommu->n); - - return; - } - - /* Here we assume that memory_region_is_ram(section->mr)==true */ - - end = int128_get64(llend); - vaddr = memory_region_get_ram_ptr(section->mr) + - section->offset_within_region + - (iova - section->offset_within_address_space); - - DPRINTF("region_add [ram] %"HWADDR_PRIx" - %"HWADDR_PRIx" [%p]\n", - iova, end - 1, vaddr); - - ret = vfio_dma_map(container, iova, end - iova, vaddr, section->readonly); - if (ret) { - error_report("vfio_dma_map(%p, 0x%"HWADDR_PRIx", " - "0x%"HWADDR_PRIx", %p) = %d (%m)", - container, iova, end - iova, vaddr, ret); - - /* - * On the initfn path, store the first error in the container so we - * can gracefully fail. Runtime, there's not much we can do other - * than throw a hardware error. - */ - if (!container->iommu_data.type1.initialized) { - if (!container->iommu_data.type1.error) { - container->iommu_data.type1.error = ret; - } - } else { - hw_error("vfio: DMA mapping failed, unable to continue"); - } - } -} - -static void vfio_listener_region_del(MemoryListener *listener, - MemoryRegionSection *section) -{ - VFIOContainer *container = container_of(listener, VFIOContainer, - iommu_data.type1.listener); - hwaddr iova, end; - int ret; - - if (vfio_listener_skipped_section(section)) { - DPRINTF("SKIPPING region_del %"HWADDR_PRIx" - %"PRIx64"\n", - 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; - } - - DPRINTF("region_del %"HWADDR_PRIx" - %"HWADDR_PRIx"\n", - 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 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->iommu_data.type1.listener); -} - -/* - * Interrupt setup - */ -static void vfio_disable_interrupts(VFIODevice *vdev) -{ - switch (vdev->interrupt) { - case VFIO_INT_INTx: - vfio_disable_intx(vdev); - break; - case VFIO_INT_MSI: - vfio_disable_msi(vdev); - break; - case VFIO_INT_MSIX: - vfio_disable_msix(vdev); - break; - } -} - -static int vfio_setup_msi(VFIODevice *vdev, int pos) -{ - uint16_t ctrl; - bool msi_64bit, msi_maskbit; - int ret, entries; - - if (pread(vdev->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); - - DPRINTF("%04x:%02x:%02x.%x PCI MSI CAP @0x%x\n", vdev->host.domain, - vdev->host.bus, vdev->host.slot, vdev->host.function, 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); + trace_vfio_early_setup_msix(vdev->vbasedev.name, pos, + vdev->msix->table_bar, + vdev->msix->table_offset, + vdev->msix->entries); return 0; } -/* - * 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_early_setup_msix(VFIODevice *vdev) -{ - uint8_t pos; - uint16_t ctrl; - uint32_t table, pba; - - pos = pci_find_capability(&vdev->pdev, PCI_CAP_ID_MSIX); - if (!pos) { - return 0; - } - - if (pread(vdev->fd, &ctrl, sizeof(ctrl), - vdev->config_offset + pos + PCI_CAP_FLAGS) != sizeof(ctrl)) { - return -errno; - } - - if (pread(vdev->fd, &table, sizeof(table), - vdev->config_offset + pos + PCI_MSIX_TABLE) != sizeof(table)) { - return -errno; - } - - if (pread(vdev->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); - - vdev->msix = g_malloc0(sizeof(*(vdev->msix))); - vdev->msix->table_bar = table & PCI_MSIX_FLAGS_BIRMASK; - vdev->msix->table_offset = table & ~PCI_MSIX_FLAGS_BIRMASK; - vdev->msix->pba_bar = pba & PCI_MSIX_FLAGS_BIRMASK; - vdev->msix->pba_offset = pba & ~PCI_MSIX_FLAGS_BIRMASK; - vdev->msix->entries = (ctrl & PCI_MSIX_FLAGS_QSIZE) + 1; - - DPRINTF("%04x:%02x:%02x.%x " - "PCI MSI-X CAP @0x%x, BAR %d, offset 0x%x, entries %d\n", - vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function, pos, vdev->msix->table_bar, - vdev->msix->table_offset, vdev->msix->entries); - - return 0; -} - -static int vfio_setup_msix(VFIODevice *vdev, int pos) +static int vfio_setup_msix(VFIOPCIDevice *vdev, int pos) { int ret; ret = msix_init(&vdev->pdev, vdev->msix->entries, - &vdev->bars[vdev->msix->table_bar].mem, + &vdev->bars[vdev->msix->table_bar].region.mem, vdev->msix->table_bar, vdev->msix->table_offset, - &vdev->bars[vdev->msix->pba_bar].mem, + &vdev->bars[vdev->msix->pba_bar].region.mem, vdev->msix->pba_bar, vdev->msix->pba_offset, pos); if (ret < 0) { if (ret == -ENOTSUP) { @@ -2834,99 +2274,76 @@ static int vfio_setup_msix(VFIODevice *vdev, int pos) return 0; } -static void vfio_teardown_msi(VFIODevice *vdev) +static void vfio_teardown_msi(VFIOPCIDevice *vdev) { msi_uninit(&vdev->pdev); if (vdev->msix) { - msix_uninit(&vdev->pdev, &vdev->bars[vdev->msix->table_bar].mem, - &vdev->bars[vdev->msix->pba_bar].mem); + msix_uninit(&vdev->pdev, + &vdev->bars[vdev->msix->table_bar].region.mem, + &vdev->bars[vdev->msix->pba_bar].region.mem); } } /* * Resource setup */ -static void vfio_mmap_set_enabled(VFIODevice *vdev, bool enabled) +static void vfio_mmap_set_enabled(VFIOPCIDevice *vdev, bool enabled) { int i; for (i = 0; i < PCI_ROM_SLOT; i++) { VFIOBAR *bar = &vdev->bars[i]; - if (!bar->size) { + if (!bar->region.size) { continue; } - memory_region_set_enabled(&bar->mmap_mem, enabled); + memory_region_set_enabled(&bar->region.mmap_mem, enabled); if (vdev->msix && vdev->msix->table_bar == i) { memory_region_set_enabled(&vdev->msix->mmap_mem, enabled); } } } -static void vfio_unmap_bar(VFIODevice *vdev, int nr) +static void vfio_unregister_bar(VFIOPCIDevice *vdev, int nr) { VFIOBAR *bar = &vdev->bars[nr]; - if (!bar->size) { + if (!bar->region.size) { return; } vfio_bar_quirk_teardown(vdev, nr); - memory_region_del_subregion(&bar->mem, &bar->mmap_mem); - munmap(bar->mmap, memory_region_size(&bar->mmap_mem)); + memory_region_del_subregion(&bar->region.mem, &bar->region.mmap_mem); if (vdev->msix && vdev->msix->table_bar == nr) { - memory_region_del_subregion(&bar->mem, &vdev->msix->mmap_mem); - munmap(vdev->msix->mmap, memory_region_size(&vdev->msix->mmap_mem)); + memory_region_del_subregion(&bar->region.mem, &vdev->msix->mmap_mem); } } -static int vfio_mmap_bar(VFIODevice *vdev, VFIOBAR *bar, - MemoryRegion *mem, MemoryRegion *submem, - void **map, size_t size, off_t offset, - const char *name) +static void vfio_unmap_bar(VFIOPCIDevice *vdev, int nr) { - int ret = 0; - - if (VFIO_ALLOW_MMAP && size && bar->flags & VFIO_REGION_INFO_FLAG_MMAP) { - int prot = 0; + VFIOBAR *bar = &vdev->bars[nr]; - if (bar->flags & VFIO_REGION_INFO_FLAG_READ) { - prot |= PROT_READ; - } + if (!bar->region.size) { + return; + } - if (bar->flags & VFIO_REGION_INFO_FLAG_WRITE) { - prot |= PROT_WRITE; - } + vfio_bar_quirk_free(vdev, nr); - *map = mmap(NULL, size, prot, MAP_SHARED, - bar->fd, bar->fd_offset + offset); - if (*map == MAP_FAILED) { - *map = NULL; - ret = -errno; - goto empty_region; - } + munmap(bar->region.mmap, memory_region_size(&bar->region.mmap_mem)); - memory_region_init_ram_ptr(submem, OBJECT(vdev), name, size, *map); - memory_region_set_skip_dump(submem); - } else { -empty_region: - /* Create a zero sized sub-region to make cleanup easy. */ - memory_region_init(submem, OBJECT(vdev), name, 0); + if (vdev->msix && vdev->msix->table_bar == nr) { + munmap(vdev->msix->mmap, memory_region_size(&vdev->msix->mmap_mem)); } - - memory_region_add_subregion(mem, offset, submem); - - return ret; } -static void vfio_map_bar(VFIODevice *vdev, int nr) +static void vfio_map_bar(VFIOPCIDevice *vdev, int nr) { VFIOBAR *bar = &vdev->bars[nr]; - unsigned size = bar->size; + uint64_t size = bar->region.size; char name[64]; uint32_t pci_bar; uint8_t type; @@ -2942,7 +2359,7 @@ static void vfio_map_bar(VFIODevice *vdev, int nr) vdev->host.function, nr); /* Determine what type of BAR this is for registration */ - ret = pread(vdev->fd, &pci_bar, sizeof(pci_bar), + 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); @@ -2956,9 +2373,9 @@ static void vfio_map_bar(VFIODevice *vdev, int nr) ~PCI_BASE_ADDRESS_MEM_MASK); /* A "slow" read/write mapping underlies all BARs */ - memory_region_init_io(&bar->mem, OBJECT(vdev), &vfio_bar_ops, + memory_region_init_io(&bar->region.mem, OBJECT(vdev), &vfio_region_ops, bar, name, size); - pci_register_bar(&vdev->pdev, nr, type, &bar->mem); + pci_register_bar(&vdev->pdev, nr, type, &bar->region.mem); /* * We can't mmap areas overlapping the MSIX vector table, so we @@ -2969,21 +2386,23 @@ static void vfio_map_bar(VFIODevice *vdev, int nr) } strncat(name, " mmap", sizeof(name) - strlen(name) - 1); - if (vfio_mmap_bar(vdev, bar, &bar->mem, - &bar->mmap_mem, &bar->mmap, size, 0, name)) { + if (vfio_mmap_region(OBJECT(vdev), &bar->region, &bar->region.mem, + &bar->region.mmap_mem, &bar->region.mmap, + size, 0, name)) { error_report("%s unsupported. Performance may be slow", name); } if (vdev->msix && vdev->msix->table_bar == nr) { - unsigned start; + uint64_t start; start = HOST_PAGE_ALIGN(vdev->msix->table_offset + (vdev->msix->entries * PCI_MSIX_ENTRY_SIZE)); - size = start < bar->size ? bar->size - start : 0; + size = start < bar->region.size ? bar->region.size - start : 0; strncat(name, " msix-hi", sizeof(name) - strlen(name) - 1); /* VFIOMSIXInfo contains another MemoryRegion for this mapping */ - if (vfio_mmap_bar(vdev, bar, &bar->mem, &vdev->msix->mmap_mem, + if (vfio_mmap_region(OBJECT(vdev), &bar->region, &bar->region.mem, + &vdev->msix->mmap_mem, &vdev->msix->mmap, size, start, name)) { error_report("%s unsupported. Performance may be slow", name); } @@ -2992,7 +2411,7 @@ static void vfio_map_bar(VFIODevice *vdev, int nr) vfio_bar_quirk_setup(vdev, nr); } -static void vfio_map_bars(VFIODevice *vdev) +static void vfio_map_bars(VFIOPCIDevice *vdev) { int i; @@ -3024,12 +2443,12 @@ static void vfio_map_bars(VFIODevice *vdev) } } -static void vfio_unmap_bars(VFIODevice *vdev) +static void vfio_unregister_bars(VFIOPCIDevice *vdev) { int i; for (i = 0; i < PCI_ROM_SLOT; i++) { - vfio_unmap_bar(vdev, i); + vfio_unregister_bar(vdev, i); } if (vdev->has_vga) { @@ -3038,6 +2457,19 @@ static void vfio_unmap_bars(VFIODevice *vdev) } } +static void vfio_unmap_bars(VFIOPCIDevice *vdev) +{ + int i; + + for (i = 0; i < PCI_ROM_SLOT; i++) { + vfio_unmap_bar(vdev, i); + } + + if (vdev->has_vga) { + vfio_vga_quirk_free(vdev); + } +} + /* * General setup */ @@ -3060,7 +2492,7 @@ 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(VFIODevice *vdev, int pos, +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); @@ -3073,7 +2505,7 @@ 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(VFIODevice *vdev, int pos, +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); @@ -3081,7 +2513,7 @@ static void vfio_add_emulated_long(VFIODevice *vdev, int pos, vfio_set_long_bits(vdev->emulated_config_bits + pos, mask, mask); } -static int vfio_setup_pcie_cap(VFIODevice *vdev, int pos, uint8_t size) +static int vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size) { uint16_t flags; uint8_t type; @@ -3173,43 +2605,37 @@ static int vfio_setup_pcie_cap(VFIODevice *vdev, int pos, uint8_t size) return pos; } -static void vfio_check_pcie_flr(VFIODevice *vdev, uint8_t 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) { - DPRINTF("%04x:%02x:%02x.%x Supports FLR via PCIe cap\n", - vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function); + trace_vfio_check_pcie_flr(vdev->vbasedev.name); vdev->has_flr = true; } } -static void vfio_check_pm_reset(VFIODevice *vdev, uint8_t pos) +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)) { - DPRINTF("%04x:%02x:%02x.%x Supports PM reset\n", - vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function); + trace_vfio_check_pm_reset(vdev->vbasedev.name); vdev->has_pm_reset = true; } } -static void vfio_check_af_flr(VFIODevice *vdev, uint8_t pos) +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)) { - DPRINTF("%04x:%02x:%02x.%x Supports FLR via AF cap\n", - vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function); + trace_vfio_check_af_flr(vdev->vbasedev.name); vdev->has_flr = true; } } -static int vfio_add_std_cap(VFIODevice *vdev, uint8_t pos) +static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos) { PCIDevice *pdev = &vdev->pdev; uint8_t cap_id, next, size; @@ -3284,7 +2710,7 @@ static int vfio_add_std_cap(VFIODevice *vdev, uint8_t pos) return 0; } -static int vfio_add_capabilities(VFIODevice *vdev) +static int vfio_add_capabilities(VFIOPCIDevice *vdev) { PCIDevice *pdev = &vdev->pdev; @@ -3296,7 +2722,7 @@ static int vfio_add_capabilities(VFIODevice *vdev) return vfio_add_std_cap(vdev, pdev->config[PCI_CAPABILITY_LIST]); } -static void vfio_pci_pre_reset(VFIODevice *vdev) +static void vfio_pci_pre_reset(VFIOPCIDevice *vdev) { PCIDevice *pdev = &vdev->pdev; uint16_t cmd; @@ -3333,7 +2759,7 @@ static void vfio_pci_pre_reset(VFIODevice *vdev) vfio_pci_write_config(pdev, PCI_COMMAND, cmd, 2); } -static void vfio_pci_post_reset(VFIODevice *vdev) +static void vfio_pci_post_reset(VFIOPCIDevice *vdev) { vfio_enable_intx(vdev); } @@ -3345,7 +2771,7 @@ static bool vfio_pci_host_match(PCIHostDeviceAddress *host1, host1->slot == host2->slot && host1->function == host2->function); } -static int vfio_pci_hot_reset(VFIODevice *vdev, bool single) +static int vfio_pci_hot_reset(VFIOPCIDevice *vdev, bool single) { VFIOGroup *group; struct vfio_pci_hot_reset_info *info; @@ -3355,17 +2781,15 @@ static int vfio_pci_hot_reset(VFIODevice *vdev, bool single) int ret, i, count; bool multi = false; - DPRINTF("%s(%04x:%02x:%02x.%x) %s\n", __func__, vdev->host.domain, - vdev->host.bus, vdev->host.slot, vdev->host.function, - single ? "one" : "multi"); + trace_vfio_pci_hot_reset(vdev->vbasedev.name, single ? "one" : "multi"); vfio_pci_pre_reset(vdev); - vdev->needs_reset = false; + vdev->vbasedev.needs_reset = false; info = g_malloc0(sizeof(*info)); info->argsz = sizeof(*info); - ret = ioctl(vdev->fd, VFIO_DEVICE_GET_PCI_HOT_RESET_INFO, 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) { @@ -3381,35 +2805,34 @@ static int vfio_pci_hot_reset(VFIODevice *vdev, bool single) info->argsz = sizeof(*info) + (count * sizeof(*devices)); devices = &info->devices[0]; - ret = ioctl(vdev->fd, VFIO_DEVICE_GET_PCI_HOT_RESET_INFO, info); + 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; } - DPRINTF("%04x:%02x:%02x.%x: hot reset dependent devices:\n", - vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function); + 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; - VFIODevice *tmp; + 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); - DPRINTF("\t%04x:%02x:%02x.%x group %d\n", host.domain, + trace_vfio_pci_hot_reset_dep_devices(host.domain, host.bus, host.slot, host.function, devices[i].group_id); if (vfio_pci_host_match(&host, &vdev->host)) { continue; } - QLIST_FOREACH(group, &group_list, next) { + QLIST_FOREACH(group, &vfio_group_list, next) { if (group->groupid == devices[i].group_id) { break; } @@ -3417,27 +2840,27 @@ static int vfio_pci_hot_reset(VFIODevice *vdev, bool single) if (!group) { if (!vdev->has_pm_reset) { - error_report("vfio: Cannot reset device %04x:%02x:%02x.%x, " + error_report("vfio: Cannot reset device %s, " "depends on group %d which is not owned.", - vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function, devices[i].group_id); + vdev->vbasedev.name, devices[i].group_id); } ret = -EPERM; goto out; } /* Prep dependent devices for reset and clear our marker. */ - QLIST_FOREACH(tmp, &group->device_list, next) { + 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->host)) { if (single) { - DPRINTF("vfio: found another in-use device " - "%04x:%02x:%02x.%x\n", host.domain, host.bus, - host.slot, host.function); ret = -EINVAL; goto out_single; } vfio_pci_pre_reset(tmp); - tmp->needs_reset = false; + tmp->vbasedev.needs_reset = false; multi = true; break; } @@ -3445,14 +2868,13 @@ static int vfio_pci_hot_reset(VFIODevice *vdev, bool single) } if (!single && !multi) { - DPRINTF("vfio: No other in-use devices for multi hot reset\n"); ret = -EINVAL; goto out_single; } /* Determine how many group fds need to be passed */ count = 0; - QLIST_FOREACH(group, &group_list, next) { + QLIST_FOREACH(group, &vfio_group_list, next) { for (i = 0; i < info->count; i++) { if (group->groupid == devices[i].group_id) { count++; @@ -3466,7 +2888,7 @@ static int vfio_pci_hot_reset(VFIODevice *vdev, bool single) fds = &reset->group_fds[0]; /* Fill in group fds */ - QLIST_FOREACH(group, &group_list, next) { + 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; @@ -3476,18 +2898,18 @@ static int vfio_pci_hot_reset(VFIODevice *vdev, bool single) } /* Bus reset! */ - ret = ioctl(vdev->fd, VFIO_DEVICE_PCI_HOT_RESET, reset); + ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_PCI_HOT_RESET, reset); g_free(reset); - DPRINTF("%04x:%02x:%02x.%x hot reset: %s\n", vdev->host.domain, - vdev->host.bus, vdev->host.slot, vdev->host.function, - ret ? "%m" : "Success"); + 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; - VFIODevice *tmp; + VFIOPCIDevice *tmp; + VFIODevice *vbasedev_iter; host.domain = devices[i].segment; host.bus = devices[i].bus; @@ -3498,7 +2920,7 @@ out: continue; } - QLIST_FOREACH(group, &group_list, next) { + QLIST_FOREACH(group, &vfio_group_list, next) { if (group->groupid == devices[i].group_id) { break; } @@ -3508,7 +2930,11 @@ out: break; } - QLIST_FOREACH(tmp, &group->device_list, next) { + 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->host)) { vfio_pci_post_reset(tmp); break; @@ -3537,439 +2963,89 @@ out_single: * _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(VFIODevice *vdev) +static int vfio_pci_hot_reset_one(VFIOPCIDevice *vdev) { return vfio_pci_hot_reset(vdev, true); } -static int vfio_pci_hot_reset_multi(VFIODevice *vdev) +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_reset_handler(void *opaque) -{ - VFIOGroup *group; - VFIODevice *vdev; - - QLIST_FOREACH(group, &group_list, next) { - QLIST_FOREACH(vdev, &group->device_list, next) { - if (!vdev->reset_works || (!vdev->has_flr && vdev->has_pm_reset)) { - vdev->needs_reset = true; - } - } - } - - QLIST_FOREACH(group, &group_list, next) { - QLIST_FOREACH(vdev, &group->device_list, next) { - if (vdev->needs_reset) { - vfio_pci_hot_reset_multi(vdev); - } - } - } -} - -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)) { - DPRINTF("KVM_CREATE_DEVICE: %m\n"); - 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)) { - 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_TYPE1_IOMMU); - if (ret) { - error_report("vfio: failed to set iommu for container: %m"); - ret = -errno; - goto free_container_exit; - } - - container->iommu_data.type1.listener = vfio_memory_listener; - container->iommu_data.release = vfio_listener_release; - - memory_listener_register(&container->iommu_data.type1.listener, - &address_space_memory); - - if (container->iommu_data.type1.error) { - ret = container->iommu_data.type1.error; - error_report("vfio: memory listener initialization failed for container"); - goto listener_release_exit; - } - - container->iommu_data.type1.initialized = true; - - } else if (ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_SPAPR_TCE_IOMMU)) { - 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; - } - - container->iommu_data.type1.listener = vfio_memory_listener; - container->iommu_data.release = vfio_listener_release; - - memory_listener_register(&container->iommu_data.type1.listener, - container->space->as); - - } else { - error_report("vfio: No available IOMMU models"); - ret = -EINVAL; - goto free_container_exit; - } - - 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) +static void vfio_pci_compute_needs_reset(VFIODevice *vbasedev) { - 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); + VFIOPCIDevice *vdev = container_of(vbasedev, VFIOPCIDevice, vbasedev); + if (!vbasedev->reset_works || (!vdev->has_flr && vdev->has_pm_reset)) { + vbasedev->needs_reset = true; } - - QLIST_REMOVE(group, container_next); - group->container = NULL; - - if (QLIST_EMPTY(&container->group_list)) { - VFIOAddressSpace *space = container->space; - - if (container->iommu_data.release) { - container->iommu_data.release(container); - } - QLIST_REMOVE(container, next); - DPRINTF("vfio_disconnect_container: close container->fd\n"); - close(container->fd); - g_free(container); - - vfio_put_address_space(space); - } -} - -static VFIOGroup *vfio_get_group(int groupid, AddressSpace *as) -{ - VFIOGroup *group; - char path[32]; - struct vfio_group_status status = { .argsz = sizeof(status) }; - - QLIST_FOREACH(group, &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(&group_list)) { - qemu_register_reset(vfio_pci_reset_handler, NULL); - } - - QLIST_INSERT_HEAD(&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; } -static void vfio_put_group(VFIOGroup *group) -{ - if (!QLIST_EMPTY(&group->device_list)) { - return; - } - - vfio_kvm_device_del_group(group); - vfio_disconnect_container(group); - QLIST_REMOVE(group, next); - DPRINTF("vfio_put_group: close group->fd\n"); - close(group->fd); - g_free(group); - - if (QLIST_EMPTY(&group_list)) { - qemu_unregister_reset(vfio_pci_reset_handler, NULL); - } -} +static VFIODeviceOps vfio_pci_ops = { + .vfio_compute_needs_reset = vfio_pci_compute_needs_reset, + .vfio_hot_reset_multi = vfio_pci_hot_reset_multi, + .vfio_eoi = vfio_eoi, +}; -static int vfio_get_device(VFIOGroup *group, const char *name, VFIODevice *vdev) +static int vfio_populate_device(VFIOPCIDevice *vdev) { - struct vfio_device_info dev_info = { .argsz = sizeof(dev_info) }; + VFIODevice *vbasedev = &vdev->vbasedev; struct vfio_region_info reg_info = { .argsz = sizeof(reg_info) }; struct vfio_irq_info irq_info = { .argsz = sizeof(irq_info) }; - int ret, i; - - ret = ioctl(group->fd, VFIO_GROUP_GET_DEVICE_FD, name); - if (ret < 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-pci " - "or pci-stub and not already in use\n", group->groupid); - return ret; - } - - vdev->fd = ret; - vdev->group = group; - QLIST_INSERT_HEAD(&group->device_list, vdev, next); + int i, ret = -1; /* Sanity check device */ - ret = ioctl(vdev->fd, VFIO_DEVICE_GET_INFO, &dev_info); - if (ret) { - error_report("vfio: error getting device info: %m"); - goto error; - } - - DPRINTF("Device %s flags: %u, regions: %u, irgs: %u\n", name, - dev_info.flags, dev_info.num_regions, dev_info.num_irqs); - - if (!(dev_info.flags & VFIO_DEVICE_FLAGS_PCI)) { + if (!(vbasedev->flags & VFIO_DEVICE_FLAGS_PCI)) { error_report("vfio: Um, this isn't a PCI device"); goto error; } - vdev->reset_works = !!(dev_info.flags & VFIO_DEVICE_FLAGS_RESET); - - if (dev_info.num_regions < VFIO_PCI_CONFIG_REGION_INDEX + 1) { + if (vbasedev->num_regions < VFIO_PCI_CONFIG_REGION_INDEX + 1) { error_report("vfio: unexpected number of io regions %u", - dev_info.num_regions); + vbasedev->num_regions); goto error; } - if (dev_info.num_irqs < VFIO_PCI_MSIX_IRQ_INDEX + 1) { - error_report("vfio: unexpected number of irqs %u", dev_info.num_irqs); + 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++) { reg_info.index = i; - ret = ioctl(vdev->fd, VFIO_DEVICE_GET_REGION_INFO, ®_info); + ret = ioctl(vbasedev->fd, VFIO_DEVICE_GET_REGION_INFO, ®_info); if (ret) { error_report("vfio: Error getting region %d info: %m", i); goto error; } - DPRINTF("Device %s region %d:\n", name, i); - DPRINTF(" size: 0x%lx, offset: 0x%lx, flags: 0x%lx\n", - (unsigned long)reg_info.size, (unsigned long)reg_info.offset, - (unsigned long)reg_info.flags); + trace_vfio_populate_device_region(vbasedev->name, i, + (unsigned long)reg_info.size, + (unsigned long)reg_info.offset, + (unsigned long)reg_info.flags); - vdev->bars[i].flags = reg_info.flags; - vdev->bars[i].size = reg_info.size; - vdev->bars[i].fd_offset = reg_info.offset; - vdev->bars[i].fd = vdev->fd; - vdev->bars[i].nr = i; + vdev->bars[i].region.vbasedev = vbasedev; + vdev->bars[i].region.flags = reg_info.flags; + vdev->bars[i].region.size = reg_info.size; + vdev->bars[i].region.fd_offset = reg_info.offset; + vdev->bars[i].region.nr = i; QLIST_INIT(&vdev->bars[i].quirks); } reg_info.index = VFIO_PCI_CONFIG_REGION_INDEX; - ret = ioctl(vdev->fd, VFIO_DEVICE_GET_REGION_INFO, ®_info); + ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_REGION_INFO, ®_info); if (ret) { error_report("vfio: Error getting config info: %m"); goto error; } - DPRINTF("Device %s config:\n", name); - DPRINTF(" size: 0x%lx, offset: 0x%lx, flags: 0x%lx\n", - (unsigned long)reg_info.size, (unsigned long)reg_info.offset, - (unsigned long)reg_info.flags); + 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) { @@ -3978,13 +3054,13 @@ static int vfio_get_device(VFIOGroup *group, const char *name, VFIODevice *vdev) vdev->config_offset = reg_info.offset; if ((vdev->features & VFIO_FEATURE_ENABLE_VGA) && - dev_info.num_regions > VFIO_PCI_VGA_REGION_INDEX) { + vbasedev->num_regions > VFIO_PCI_VGA_REGION_INDEX) { struct vfio_region_info vga_info = { .argsz = sizeof(vga_info), .index = VFIO_PCI_VGA_REGION_INDEX, }; - ret = ioctl(vdev->fd, VFIO_DEVICE_GET_REGION_INFO, &vga_info); + ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_REGION_INFO, &vga_info); if (ret) { error_report( "vfio: Device does not support requested feature x-vga"); @@ -4001,7 +3077,7 @@ static int vfio_get_device(VFIOGroup *group, const char *name, VFIODevice *vdev) } vdev->vga.fd_offset = vga_info.offset; - vdev->vga.fd = vdev->fd; + vdev->vga.fd = vdev->vbasedev.fd; vdev->vga.region[QEMU_PCI_VGA_MEM].offset = QEMU_PCI_VGA_MEM_BASE; vdev->vga.region[QEMU_PCI_VGA_MEM].nr = QEMU_PCI_VGA_MEM; @@ -4017,46 +3093,40 @@ static int vfio_get_device(VFIOGroup *group, const char *name, VFIODevice *vdev) vdev->has_vga = true; } + irq_info.index = VFIO_PCI_ERR_IRQ_INDEX; - ret = ioctl(vdev->fd, VFIO_DEVICE_GET_IRQ_INFO, &irq_info); + 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 */ - DPRINTF("VFIO_DEVICE_GET_IRQ_INFO failure: %m\n"); + trace_vfio_populate_device_get_irq_info_failure(); ret = 0; } else if (irq_info.count == 1) { vdev->pci_aer = true; } else { - error_report("vfio: %04x:%02x:%02x.%x " + error_report("vfio: %s " "Could not enable error recovery for the device", - vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function); + vbasedev->name); } error: - if (ret) { - QLIST_REMOVE(vdev, next); - vdev->group = NULL; - close(vdev->fd); - } return ret; } -static void vfio_put_device(VFIODevice *vdev) +static void vfio_put_device(VFIOPCIDevice *vdev) { - QLIST_REMOVE(vdev, next); - vdev->group = NULL; - DPRINTF("vfio_put_device: close vdev->fd\n"); - close(vdev->fd); + g_free(vdev->vbasedev.name); if (vdev->msix) { + object_unparent(OBJECT(&vdev->msix->mmap_mem)); g_free(vdev->msix); vdev->msix = NULL; } + vfio_put_base_device(&vdev->vbasedev); } static void vfio_err_notifier_handler(void *opaque) { - VFIODevice *vdev = opaque; + VFIOPCIDevice *vdev = opaque; if (!event_notifier_test_and_clear(&vdev->err_notifier)) { return; @@ -4085,7 +3155,7 @@ static void vfio_err_notifier_handler(void *opaque) * and continue after disabling error recovery support for the * device. */ -static void vfio_register_err_notifier(VFIODevice *vdev) +static void vfio_register_err_notifier(VFIOPCIDevice *vdev) { int ret; int argsz; @@ -4116,7 +3186,7 @@ static void vfio_register_err_notifier(VFIODevice *vdev) *pfd = event_notifier_get_fd(&vdev->err_notifier); qemu_set_fd_handler(*pfd, vfio_err_notifier_handler, NULL, vdev); - ret = ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, irq_set); + 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); @@ -4126,7 +3196,7 @@ static void vfio_register_err_notifier(VFIODevice *vdev) g_free(irq_set); } -static void vfio_unregister_err_notifier(VFIODevice *vdev) +static void vfio_unregister_err_notifier(VFIOPCIDevice *vdev) { int argsz; struct vfio_irq_set *irq_set; @@ -4149,7 +3219,7 @@ static void vfio_unregister_err_notifier(VFIODevice *vdev) pfd = (int32_t *)&irq_set->data; *pfd = -1; - ret = ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, irq_set); + ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_SET_IRQS, irq_set); if (ret) { error_report("vfio: Failed to de-assign error fd: %m"); } @@ -4159,9 +3229,101 @@ static void vfio_unregister_err_notifier(VFIODevice *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) { - VFIODevice *pvdev, *vdev = DO_UPCAST(VFIODevice, pdev, pdev); + VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev); + VFIODevice *vbasedev_iter; VFIOGroup *group; char path[PATH_MAX], iommu_group_path[PATH_MAX], *group_name; ssize_t len; @@ -4179,6 +3341,13 @@ static int vfio_initfn(PCIDevice *pdev) return -errno; } + vdev->vbasedev.ops = &vfio_pci_ops; + + vdev->vbasedev.type = VFIO_DEVICE_TYPE_PCI; + vdev->vbasedev.name = g_strdup_printf("%04x:%02x:%02x.%01x", + vdev->host.domain, vdev->host.bus, + vdev->host.slot, vdev->host.function); + strncat(path, "iommu_group", sizeof(path) - strlen(path) - 1); len = readlink(path, iommu_group_path, sizeof(path)); @@ -4195,8 +3364,7 @@ static int vfio_initfn(PCIDevice *pdev) return -errno; } - DPRINTF("%s(%04x:%02x:%02x.%x) group %d\n", __func__, vdev->host.domain, - vdev->host.bus, vdev->host.slot, vdev->host.function, groupid); + trace_vfio_initfn(vdev->vbasedev.name, groupid); group = vfio_get_group(groupid, pci_device_iommu_address_space(pdev)); if (!group) { @@ -4208,33 +3376,34 @@ static int vfio_initfn(PCIDevice *pdev) vdev->host.domain, vdev->host.bus, vdev->host.slot, vdev->host.function); - QLIST_FOREACH(pvdev, &group->device_list, next) { - if (pvdev->host.domain == vdev->host.domain && - pvdev->host.bus == vdev->host.bus && - pvdev->host.slot == vdev->host.slot && - pvdev->host.function == vdev->host.function) { - + QLIST_FOREACH(vbasedev_iter, &group->device_list, next) { + if (strcmp(vbasedev_iter->name, vdev->vbasedev.name) == 0) { error_report("vfio: error: device %s is already attached", path); vfio_put_group(group); return -EBUSY; } } - ret = vfio_get_device(group, path, vdev); + ret = vfio_get_device(group, path, &vdev->vbasedev); if (ret) { error_report("vfio: failed to get device %s", path); vfio_put_group(group); return ret; } + ret = vfio_populate_device(vdev); + if (ret) { + return ret; + } + /* Get a copy of config space */ - ret = pread(vdev->fd, vdev->pdev.config, + 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"); - goto out_put; + return ret; } /* vfio emulates a lot for us, but some bits need extra love */ @@ -4266,7 +3435,7 @@ static int vfio_initfn(PCIDevice *pdev) ret = vfio_early_setup_msix(vdev); if (ret) { - goto out_put; + return ret; } vfio_map_bars(vdev); @@ -4298,25 +3467,35 @@ static int vfio_initfn(PCIDevice *pdev) } vfio_register_err_notifier(vdev); + vfio_register_req_notifier(vdev); return 0; out_teardown: pci_device_set_intx_routing_notifier(&vdev->pdev, NULL); vfio_teardown_msi(vdev); + vfio_unregister_bars(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_unmap_bars(vdev); -out_put: g_free(vdev->emulated_config_bits); + g_free(vdev->rom); vfio_put_device(vdev); vfio_put_group(group); - return ret; } static void vfio_exitfn(PCIDevice *pdev) { - VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev); - VFIOGroup *group = vdev->group; + 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); @@ -4324,27 +3503,22 @@ static void vfio_exitfn(PCIDevice *pdev) timer_free(vdev->intx.mmap_timer); } vfio_teardown_msi(vdev); - vfio_unmap_bars(vdev); - g_free(vdev->emulated_config_bits); - g_free(vdev->rom); - vfio_put_device(vdev); - vfio_put_group(group); + vfio_unregister_bars(vdev); } static void vfio_pci_reset(DeviceState *dev) { PCIDevice *pdev = DO_UPCAST(PCIDevice, qdev, dev); - VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev); + VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev); - DPRINTF("%s(%04x:%02x:%02x.%x)\n", __func__, vdev->host.domain, - vdev->host.bus, vdev->host.slot, vdev->host.function); + trace_vfio_pci_reset(vdev->vbasedev.name); vfio_pci_pre_reset(vdev); - if (vdev->reset_works && (vdev->has_flr || !vdev->has_pm_reset) && - !ioctl(vdev->fd, VFIO_DEVICE_RESET)) { - DPRINTF("%04x:%02x:%02x.%x FLR/VFIO_DEVICE_RESET\n", vdev->host.domain, - vdev->host.bus, vdev->host.slot, vdev->host.function); + 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; } @@ -4354,10 +3528,9 @@ static void vfio_pci_reset(DeviceState *dev) } /* If nothing else works and the device supports PM reset, use it */ - if (vdev->reset_works && vdev->has_pm_reset && - !ioctl(vdev->fd, VFIO_DEVICE_RESET)) { - DPRINTF("%04x:%02x:%02x.%x PCI PM Reset\n", vdev->host.domain, - vdev->host.bus, vdev->host.slot, vdev->host.function); + 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; } @@ -4368,7 +3541,7 @@ post_reset: static void vfio_instance_init(Object *obj) { PCIDevice *pci_dev = PCI_DEVICE(obj); - VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, PCI_DEVICE(obj)); + VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, PCI_DEVICE(obj)); device_add_bootindex_property(obj, &vdev->bootindex, "bootindex", NULL, @@ -4376,15 +3549,18 @@ static void vfio_instance_init(Object *obj) } static Property vfio_pci_dev_properties[] = { - DEFINE_PROP_PCI_HOST_DEVADDR("host", VFIODevice, host), - DEFINE_PROP_UINT32("x-intx-mmap-timeout-ms", VFIODevice, + DEFINE_PROP_PCI_HOST_DEVADDR("host", VFIOPCIDevice, host), + DEFINE_PROP_UINT32("x-intx-mmap-timeout-ms", VFIOPCIDevice, intx.mmap_timeout, 1100), - DEFINE_PROP_BIT("x-vga", VFIODevice, features, + DEFINE_PROP_BIT("x-vga", VFIOPCIDevice, features, VFIO_FEATURE_ENABLE_VGA_BIT, false), + DEFINE_PROP_BIT("x-req", VFIOPCIDevice, features, + VFIO_FEATURE_ENABLE_REQ_BIT, true), + DEFINE_PROP_BOOL("x-mmap", VFIOPCIDevice, vbasedev.allow_mmap, true), /* * TODO - support passed fds... is this necessary? - * DEFINE_PROP_STRING("vfiofd", VFIODevice, vfiofd_name), - * DEFINE_PROP_STRING("vfiogroupfd, VFIODevice, vfiogroupfd_name), + * DEFINE_PROP_STRING("vfiofd", VFIOPCIDevice, vfiofd_name), + * DEFINE_PROP_STRING("vfiogroupfd, VFIOPCIDevice, vfiogroupfd_name), */ DEFINE_PROP_END_OF_LIST(), }; @@ -4414,9 +3590,10 @@ static void vfio_pci_dev_class_init(ObjectClass *klass, void *data) static const TypeInfo vfio_pci_dev_info = { .name = "vfio-pci", .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(VFIODevice), + .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) @@ -4425,47 +3602,3 @@ static void register_vfio_pci_dev_type(void) } type_init(register_vfio_pci_dev_type) - -static int vfio_container_do_ioctl(AddressSpace *as, int32_t groupid, - int req, void *param) -{ - VFIOGroup *group; - VFIOContainer *container; - int ret = -1; - - group = vfio_get_group(groupid, as); - if (!group) { - error_report("vfio: group %d not registered", groupid); - return ret; - } - - container = group->container; - if (group->container) { - ret = ioctl(container->fd, req, param); - if (ret < 0) { - error_report("vfio: failed to ioctl container: ret=%d, %s", - ret, strerror(errno)); - } - } - - vfio_put_group(group); - - return ret; -} - -int vfio_container_ioctl(AddressSpace *as, int32_t groupid, - int req, void *param) -{ - /* We allow only certain ioctls to the container */ - switch (req) { - case VFIO_CHECK_EXTENSION: - case VFIO_IOMMU_SPAPR_TCE_GET_INFO: - break; - default: - /* Return an error on unknown requests */ - error_report("vfio: unsupported ioctl %X", req); - return -1; - } - - return vfio_container_do_ioctl(as, groupid, req, param); -} diff --git a/hw/virtio/Makefile.objs b/hw/virtio/Makefile.objs index d21c397..19b224a 100644 --- a/hw/virtio/Makefile.objs +++ b/hw/virtio/Makefile.objs @@ -2,7 +2,7 @@ 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 -common-obj-$(CONFIG_VIRTIO) += dataplane/ +obj-$(CONFIG_VIRTIO) += dataplane/ obj-y += virtio.o virtio-balloon.o obj-$(CONFIG_LINUX) += vhost.o vhost-backend.o vhost-user.o diff --git a/hw/virtio/dataplane/Makefile.objs b/hw/virtio/dataplane/Makefile.objs index 9a8cfc0..753a9ca 100644 --- a/hw/virtio/dataplane/Makefile.objs +++ b/hw/virtio/dataplane/Makefile.objs @@ -1 +1 @@ -common-obj-y += vring.o +obj-y += vring.o diff --git a/hw/virtio/dataplane/vring.c b/hw/virtio/dataplane/vring.c index 61f6d83..5c7b8c2 100644 --- a/hw/virtio/dataplane/vring.c +++ b/hw/virtio/dataplane/vring.c @@ -18,7 +18,9 @@ #include "hw/hw.h" #include "exec/memory.h" #include "exec/address-spaces.h" +#include "hw/virtio/virtio-access.h" #include "hw/virtio/dataplane/vring.h" +#include "hw/virtio/dataplane/vring-accessors.h" #include "qemu/error-report.h" /* vring_map can be coupled with vring_unmap or (if you still have the @@ -83,7 +85,7 @@ bool vring_setup(Vring *vring, VirtIODevice *vdev, int n) vring_init(&vring->vr, virtio_queue_get_num(vdev, n), vring_ptr, 4096); vring->last_avail_idx = virtio_queue_get_last_avail_idx(vdev, n); - vring->last_used_idx = vring->vr.used->idx; + vring->last_used_idx = vring_get_used_idx(vdev, vring); vring->signalled_used = 0; vring->signalled_used_valid = false; @@ -103,8 +105,8 @@ void vring_teardown(Vring *vring, VirtIODevice *vdev, int n) /* Disable guest->host notifies */ void vring_disable_notification(VirtIODevice *vdev, Vring *vring) { - if (!(vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX))) { - vring->vr.used->flags |= VRING_USED_F_NO_NOTIFY; + if (!virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { + vring_set_used_flags(vdev, vring, VRING_USED_F_NO_NOTIFY); } } @@ -114,13 +116,13 @@ void vring_disable_notification(VirtIODevice *vdev, Vring *vring) */ bool vring_enable_notification(VirtIODevice *vdev, Vring *vring) { - if (vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX)) { + if (virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { vring_avail_event(&vring->vr) = vring->vr.avail->idx; } else { - vring->vr.used->flags &= ~VRING_USED_F_NO_NOTIFY; + vring_clear_used_flags(vdev, vring, VRING_USED_F_NO_NOTIFY); } smp_mb(); /* ensure update is seen before reading avail_idx */ - return !vring_more_avail(vring); + return !vring_more_avail(vdev, vring); } /* This is stolen from linux/drivers/vhost/vhost.c:vhost_notify() */ @@ -133,13 +135,14 @@ bool vring_should_notify(VirtIODevice *vdev, Vring *vring) * interrupts. */ smp_mb(); - if ((vdev->guest_features & VIRTIO_F_NOTIFY_ON_EMPTY) && - unlikely(vring->vr.avail->idx == vring->last_avail_idx)) { + if (virtio_has_feature(vdev, VIRTIO_F_NOTIFY_ON_EMPTY) && + unlikely(!vring_more_avail(vdev, vring))) { return true; } - if (!(vdev->guest_features & VIRTIO_RING_F_EVENT_IDX)) { - return !(vring->vr.avail->flags & VRING_AVAIL_F_NO_INTERRUPT); + if (!virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { + return !(vring_get_avail_flags(vdev, vring) & + VRING_AVAIL_F_NO_INTERRUPT); } old = vring->signalled_used; v = vring->signalled_used_valid; @@ -202,9 +205,19 @@ static int get_desc(Vring *vring, VirtQueueElement *elem, return 0; } +static void copy_in_vring_desc(VirtIODevice *vdev, + const struct vring_desc *guest, + struct vring_desc *host) +{ + host->addr = virtio_ldq_p(vdev, &guest->addr); + host->len = virtio_ldl_p(vdev, &guest->len); + host->flags = virtio_lduw_p(vdev, &guest->flags); + host->next = virtio_lduw_p(vdev, &guest->next); +} + /* This is stolen from linux/drivers/vhost/vhost.c. */ -static int get_indirect(Vring *vring, VirtQueueElement *elem, - struct vring_desc *indirect) +static int get_indirect(VirtIODevice *vdev, Vring *vring, + VirtQueueElement *elem, struct vring_desc *indirect) { struct vring_desc desc; unsigned int i = 0, count, found = 0; @@ -244,7 +257,7 @@ static int get_indirect(Vring *vring, VirtQueueElement *elem, vring->broken = true; return -EFAULT; } - desc = *desc_ptr; + copy_in_vring_desc(vdev, desc_ptr, &desc); memory_region_unref(mr); /* Ensure descriptor has been loaded before accessing fields */ @@ -320,7 +333,7 @@ int vring_pop(VirtIODevice *vdev, Vring *vring, /* Check it isn't doing very strange things with descriptor numbers. */ last_avail_idx = vring->last_avail_idx; - avail_idx = vring->vr.avail->idx; + avail_idx = vring_get_avail_idx(vdev, vring); barrier(); /* load indices now and not again later */ if (unlikely((uint16_t)(avail_idx - last_avail_idx) > num)) { @@ -341,7 +354,7 @@ int vring_pop(VirtIODevice *vdev, Vring *vring, /* Grab the next descriptor number they're advertising, and increment * the index we've seen. */ - head = vring->vr.avail->ring[last_avail_idx % num]; + head = vring_get_avail_ring(vdev, vring, last_avail_idx % num); elem->index = head; @@ -365,13 +378,13 @@ int vring_pop(VirtIODevice *vdev, Vring *vring, ret = -EFAULT; goto out; } - desc = vring->vr.desc[i]; + copy_in_vring_desc(vdev, &vring->vr.desc[i], &desc); /* Ensure descriptor is loaded before accessing fields */ barrier(); if (desc.flags & VRING_DESC_F_INDIRECT) { - ret = get_indirect(vring, elem, &desc); + ret = get_indirect(vdev, vring, elem, &desc); if (ret < 0) { goto out; } @@ -388,7 +401,7 @@ int vring_pop(VirtIODevice *vdev, Vring *vring, /* On success, increment avail index. */ vring->last_avail_idx++; - if (vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX)) { + if (virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { vring_avail_event(&vring->vr) = vring->last_avail_idx; } @@ -407,9 +420,9 @@ out: * * Stolen from linux/drivers/vhost/vhost.c. */ -void vring_push(Vring *vring, VirtQueueElement *elem, int len) +void vring_push(VirtIODevice *vdev, Vring *vring, VirtQueueElement *elem, + int len) { - struct vring_used_elem *used; unsigned int head = elem->index; uint16_t new; @@ -422,14 +435,16 @@ void vring_push(Vring *vring, VirtQueueElement *elem, int len) /* The virtqueue contains a ring of used buffers. Get a pointer to the * next entry in that used ring. */ - used = &vring->vr.used->ring[vring->last_used_idx % vring->vr.num]; - used->id = head; - used->len = len; + vring_set_used_ring_id(vdev, vring, vring->last_used_idx % vring->vr.num, + head); + vring_set_used_ring_len(vdev, vring, vring->last_used_idx % vring->vr.num, + len); /* Make sure buffer is written before we update index. */ smp_wmb(); - new = vring->vr.used->idx = ++vring->last_used_idx; + new = ++vring->last_used_idx; + vring_set_used_idx(vdev, vring, new); if (unlikely((int16_t)(new - vring->signalled_used) < (uint16_t)1)) { vring->signalled_used_valid = false; } diff --git a/hw/virtio/vhost-backend.c b/hw/virtio/vhost-backend.c index ff4f200..4d68a27 100644 --- a/hw/virtio/vhost-backend.c +++ b/hw/virtio/vhost-backend.c @@ -61,7 +61,7 @@ int vhost_set_backend_type(struct vhost_dev *dev, VhostBackendType backend_type) dev->vhost_ops = &user_ops; break; default: - error_report("Unknown vhost backend type\n"); + error_report("Unknown vhost backend type"); r = -1; } diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 5a12861..a7858d3 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -288,7 +288,7 @@ static inline void vhost_dev_log_resize(struct vhost_dev* dev, uint64_t size) int r; log = g_malloc0(size * sizeof *log); - log_base = (uint64_t)(unsigned long)log; + log_base = (uintptr_t)log; r = dev->vhost_ops->vhost_call(dev, VHOST_SET_LOG_BASE, &log_base); assert(r >= 0); /* Sync only the range covered by the old log */ @@ -921,7 +921,7 @@ 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; + int i, r, e; if (!k->set_host_notifier) { fprintf(stderr, "binding does not support host notifiers\n"); r = -ENOSYS; @@ -939,12 +939,12 @@ int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) return 0; fail_vq: while (--i >= 0) { - r = k->set_host_notifier(qbus->parent, hdev->vq_index + i, false); - if (r < 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 (r >= 0); + assert (e >= 0); } fail: return r; @@ -1057,10 +1057,13 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) } if (hdev->log_enabled) { + uint64_t log_base; + hdev->log_size = vhost_get_log_size(hdev); hdev->log = hdev->log_size ? g_malloc0(hdev->log_size * sizeof *hdev->log) : NULL; - r = hdev->vhost_ops->vhost_call(hdev, VHOST_SET_LOG_BASE, hdev->log); + log_base = (uintptr_t)hdev->log; + r = hdev->vhost_ops->vhost_call(hdev, VHOST_SET_LOG_BASE, &log_base); if (r < 0) { r = -errno; goto fail_log; diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index 7bfbb75..95b0643 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -25,6 +25,7 @@ #include "exec/address-spaces.h" #include "qapi/visitor.h" #include "qapi-event.h" +#include "trace.h" #if defined(__linux__) #include @@ -69,7 +70,7 @@ static inline void reset_stats(VirtIOBalloon *dev) static bool balloon_stats_supported(const VirtIOBalloon *s) { VirtIODevice *vdev = VIRTIO_DEVICE(s); - return vdev->guest_features & (1 << VIRTIO_BALLOON_F_STATS_VQ); + return virtio_has_feature(vdev, VIRTIO_BALLOON_F_STATS_VQ); } static bool balloon_stats_enabled(const VirtIOBalloon *s) @@ -222,6 +223,8 @@ static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq) 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; @@ -285,6 +288,7 @@ static void virtio_balloon_get_config(VirtIODevice *vdev, uint8_t *config_data) 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)); } @@ -294,13 +298,16 @@ static void virtio_balloon_set_config(VirtIODevice *vdev, 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(ram_size - + 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 uint32_t virtio_balloon_get_features(VirtIODevice *vdev, uint32_t f) @@ -312,22 +319,24 @@ static uint32_t virtio_balloon_get_features(VirtIODevice *vdev, uint32_t f) static void virtio_balloon_stat(void *opaque, BalloonInfo *info) { VirtIOBalloon *dev = opaque; - info->actual = ram_size - ((uint64_t) dev->actual << - VIRTIO_BALLOON_PFN_SHIFT); + 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 > ram_size) { - target = ram_size; + if (target > vm_ram_size) { + target = vm_ram_size; } if (target) { - dev->num_pages = (ram_size - target) >> VIRTIO_BALLOON_PFN_SHIFT; + 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) diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c index eb77019..be886e7 100644 --- a/hw/virtio/virtio-bus.c +++ b/hw/virtio/virtio-bus.c @@ -58,7 +58,7 @@ void virtio_bus_reset(VirtioBusState *bus) { VirtIODevice *vdev = virtio_bus_get_device(bus); - DPRINTF("%s: reset device.\n", qbus->name); + DPRINTF("%s: reset device.\n", BUS(bus)->name); if (vdev != NULL) { virtio_reset(vdev); } @@ -109,20 +109,6 @@ uint32_t virtio_bus_get_vdev_features(VirtioBusState *bus, return k->get_features(vdev, requested_features); } -/* Set the features of the plugged device. */ -void virtio_bus_set_vdev_features(VirtioBusState *bus, - uint32_t requested_features) -{ - VirtIODevice *vdev = virtio_bus_get_device(bus); - VirtioDeviceClass *k; - - assert(vdev != NULL); - k = VIRTIO_DEVICE_GET_CLASS(vdev); - if (k->set_features != NULL) { - k->set_features(vdev, requested_features); - } -} - /* Get bad features of the plugged device. */ uint32_t virtio_bus_get_vdev_bad_features(VirtioBusState *bus) { diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c index 2450c13..10123f3 100644 --- a/hw/virtio/virtio-mmio.c +++ b/hw/virtio/virtio-mmio.c @@ -349,7 +349,7 @@ static void virtio_mmio_device_plugged(DeviceState *opaque) { VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque); - proxy->host_features |= (0x1 << VIRTIO_F_NOTIFY_ON_EMPTY); + virtio_add_feature(&proxy->host_features, VIRTIO_F_NOTIFY_ON_EMPTY); proxy->host_features = virtio_bus_get_vdev_features(&proxy->bus, proxy->host_features); } diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index dde1d73..c7c3f72 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -17,6 +17,7 @@ #include +#include "standard-headers/linux/virtio_pci.h" #include "hw/virtio/virtio.h" #include "hw/virtio/virtio-blk.h" #include "hw/virtio/virtio-net.h" @@ -35,56 +36,11 @@ #include "hw/virtio/virtio-bus.h" #include "qapi/visitor.h" -/* from Linux's linux/virtio_pci.h */ - -/* A 32-bit r/o bitmask of the features supported by the host */ -#define VIRTIO_PCI_HOST_FEATURES 0 - -/* A 32-bit r/w bitmask of features activated by the guest */ -#define VIRTIO_PCI_GUEST_FEATURES 4 - -/* A 32-bit r/w PFN for the currently selected queue */ -#define VIRTIO_PCI_QUEUE_PFN 8 - -/* A 16-bit r/o queue size for the currently selected queue */ -#define VIRTIO_PCI_QUEUE_NUM 12 - -/* A 16-bit r/w queue selector */ -#define VIRTIO_PCI_QUEUE_SEL 14 - -/* A 16-bit r/w queue notifier */ -#define VIRTIO_PCI_QUEUE_NOTIFY 16 - -/* An 8-bit device status register. */ -#define VIRTIO_PCI_STATUS 18 - -/* An 8-bit r/o interrupt status register. Reading the value will return the - * current contents of the ISR and will also clear it. This is effectively - * a read-and-acknowledge. */ -#define VIRTIO_PCI_ISR 19 - -/* MSI-X registers: only enabled if MSI-X is enabled. */ -/* A 16-bit vector for configuration changes. */ -#define VIRTIO_MSI_CONFIG_VECTOR 20 -/* A 16-bit vector for selected queue notifications. */ -#define VIRTIO_MSI_QUEUE_VECTOR 22 - -/* Config space size */ -#define VIRTIO_PCI_CONFIG_NOMSI 20 -#define VIRTIO_PCI_CONFIG_MSI 24 -#define VIRTIO_PCI_REGION_SIZE(dev) (msix_present(dev) ? \ - VIRTIO_PCI_CONFIG_MSI : \ - VIRTIO_PCI_CONFIG_NOMSI) +#define VIRTIO_PCI_REGION_SIZE(dev) VIRTIO_PCI_CONFIG_OFF(msix_present(dev)) /* The remaining space is defined by each driver as the per-driver * configuration space */ -#define VIRTIO_PCI_CONFIG(dev) (msix_enabled(dev) ? \ - VIRTIO_PCI_CONFIG_MSI : \ - VIRTIO_PCI_CONFIG_NOMSI) - -/* How many bits to shift physical queue address written to QUEUE_PFN. - * 12 is historical, and due to x86 page size. */ -#define VIRTIO_PCI_QUEUE_ADDR_SHIFT 12 +#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); @@ -392,7 +348,7 @@ static uint64_t virtio_pci_config_read(void *opaque, hwaddr addr, { VirtIOPCIProxy *proxy = opaque; VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev); + uint32_t config = VIRTIO_PCI_CONFIG_SIZE(&proxy->pci_dev); uint64_t val = 0; if (addr < config) { return virtio_ioport_read(proxy, addr); @@ -423,7 +379,7 @@ static void virtio_pci_config_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { VirtIOPCIProxy *proxy = opaque; - uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev); + 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); @@ -900,16 +856,13 @@ static void virtio_pci_vmstate_change(DeviceState *d, bool running) } #ifdef CONFIG_VIRTFS -static int virtio_9p_init_pci(VirtIOPCIProxy *vpci_dev) +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)); - if (qdev_init(vdev) < 0) { - return -1; - } - return 0; + object_property_set_bool(OBJECT(vdev), true, "realized", errp); } static Property virtio_9p_pci_properties[] = { @@ -925,7 +878,7 @@ static void virtio_9p_pci_class_init(ObjectClass *klass, void *data) PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); - k->init = virtio_9p_init_pci; + 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; @@ -996,8 +949,8 @@ static void virtio_pci_device_plugged(DeviceState *d) proxy->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD; } - proxy->host_features |= 0x1 << VIRTIO_F_NOTIFY_ON_EMPTY; - proxy->host_features |= 0x1 << VIRTIO_F_BAD_FEATURE; + virtio_add_feature(&proxy->host_features, VIRTIO_F_NOTIFY_ON_EMPTY); + virtio_add_feature(&proxy->host_features, VIRTIO_F_BAD_FEATURE); proxy->host_features = virtio_bus_get_vdev_features(bus, proxy->host_features); } @@ -1009,15 +962,15 @@ static void virtio_pci_device_unplugged(DeviceState *d) virtio_pci_stop_ioeventfd(proxy); } -static int virtio_pci_init(PCIDevice *pci_dev) +static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) { VirtIOPCIProxy *dev = VIRTIO_PCI(pci_dev); VirtioPCIClass *k = VIRTIO_PCI_GET_CLASS(pci_dev); + virtio_pci_bus_new(&dev->bus, sizeof(dev->bus), dev); - if (k->init != NULL) { - return k->init(dev); + if (k->realize) { + k->realize(dev, errp); } - return 0; } static void virtio_pci_exit(PCIDevice *pci_dev) @@ -1047,7 +1000,7 @@ static void virtio_pci_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); dc->props = virtio_pci_properties; - k->init = virtio_pci_init; + k->realize = virtio_pci_realize; k->exit = virtio_pci_exit; k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; k->revision = VIRTIO_PCI_ABI_VERSION; @@ -1074,15 +1027,13 @@ static Property virtio_blk_pci_properties[] = { DEFINE_PROP_END_OF_LIST(), }; -static int virtio_blk_pci_init(VirtIOPCIProxy *vpci_dev) +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)); - if (qdev_init(vdev) < 0) { - return -1; - } - return 0; + object_property_set_bool(OBJECT(vdev), true, "realized", errp); } static void virtio_blk_pci_class_init(ObjectClass *klass, void *data) @@ -1093,7 +1044,7 @@ static void virtio_blk_pci_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); dc->props = virtio_blk_pci_properties; - k->init = virtio_blk_pci_init; + 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; @@ -1131,7 +1082,7 @@ static Property virtio_scsi_pci_properties[] = { DEFINE_PROP_END_OF_LIST(), }; -static int virtio_scsi_pci_init_pci(VirtIOPCIProxy *vpci_dev) +static void virtio_scsi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) { VirtIOSCSIPCI *dev = VIRTIO_SCSI_PCI(vpci_dev); DeviceState *vdev = DEVICE(&dev->vdev); @@ -1154,10 +1105,7 @@ static int virtio_scsi_pci_init_pci(VirtIOPCIProxy *vpci_dev) } qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); - if (qdev_init(vdev) < 0) { - return -1; - } - return 0; + object_property_set_bool(OBJECT(vdev), true, "realized", errp); } static void virtio_scsi_pci_class_init(ObjectClass *klass, void *data) @@ -1165,7 +1113,8 @@ 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->init = virtio_scsi_pci_init_pci; + + 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; @@ -1201,7 +1150,7 @@ static Property vhost_scsi_pci_properties[] = { DEFINE_PROP_END_OF_LIST(), }; -static int vhost_scsi_pci_init_pci(VirtIOPCIProxy *vpci_dev) +static void vhost_scsi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) { VHostSCSIPCI *dev = VHOST_SCSI_PCI(vpci_dev); DeviceState *vdev = DEVICE(&dev->vdev); @@ -1212,10 +1161,7 @@ static int vhost_scsi_pci_init_pci(VirtIOPCIProxy *vpci_dev) } qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); - if (qdev_init(vdev) < 0) { - return -1; - } - return 0; + object_property_set_bool(OBJECT(vdev), true, "realized", errp); } static void vhost_scsi_pci_class_init(ObjectClass *klass, void *data) @@ -1223,7 +1169,7 @@ 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->init = vhost_scsi_pci_init_pci; + 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; @@ -1238,6 +1184,8 @@ static void vhost_scsi_pci_instance_init(Object *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 = { @@ -1282,7 +1230,7 @@ static Property virtio_balloon_pci_properties[] = { DEFINE_PROP_END_OF_LIST(), }; -static int virtio_balloon_pci_init(VirtIOPCIProxy *vpci_dev) +static void virtio_balloon_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) { VirtIOBalloonPCI *dev = VIRTIO_BALLOON_PCI(vpci_dev); DeviceState *vdev = DEVICE(&dev->vdev); @@ -1293,10 +1241,7 @@ static int virtio_balloon_pci_init(VirtIOPCIProxy *vpci_dev) } qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); - if (qdev_init(vdev) < 0) { - return -1; - } - return 0; + object_property_set_bool(OBJECT(vdev), true, "realized", errp); } static void virtio_balloon_pci_class_init(ObjectClass *klass, void *data) @@ -1304,7 +1249,7 @@ 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->init = virtio_balloon_pci_init; + 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; @@ -1316,9 +1261,8 @@ static void virtio_balloon_pci_class_init(ObjectClass *klass, void *data) static void virtio_balloon_pci_instance_init(Object *obj) { VirtIOBalloonPCI *dev = VIRTIO_BALLOON_PCI(obj); - object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_BALLOON); - object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL); - object_unref(OBJECT(&dev->vdev)); + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VIRTIO_BALLOON); object_property_add(obj, "guest-stats", "guest statistics", balloon_pci_stats_get_all, NULL, NULL, dev, NULL); @@ -1339,7 +1283,7 @@ static const TypeInfo virtio_balloon_pci_info = { /* virtio-serial-pci */ -static int virtio_serial_pci_init(VirtIOPCIProxy *vpci_dev) +static void virtio_serial_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) { VirtIOSerialPCI *dev = VIRTIO_SERIAL_PCI(vpci_dev); DeviceState *vdev = DEVICE(&dev->vdev); @@ -1369,10 +1313,7 @@ static int virtio_serial_pci_init(VirtIOPCIProxy *vpci_dev) } qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); - if (qdev_init(vdev) < 0) { - return -1; - } - return 0; + object_property_set_bool(OBJECT(vdev), true, "realized", errp); } static Property virtio_serial_pci_properties[] = { @@ -1388,7 +1329,7 @@ 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->init = virtio_serial_pci_init; + 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; @@ -1423,7 +1364,7 @@ static Property virtio_net_properties[] = { DEFINE_PROP_END_OF_LIST(), }; -static int virtio_net_pci_init(VirtIOPCIProxy *vpci_dev) +static void virtio_net_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) { DeviceState *qdev = DEVICE(vpci_dev); VirtIONetPCI *dev = VIRTIO_NET_PCI(vpci_dev); @@ -1433,10 +1374,7 @@ static int virtio_net_pci_init(VirtIOPCIProxy *vpci_dev) virtio_net_set_netclient_name(&dev->vdev, qdev->id, object_get_typename(OBJECT(qdev))); qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); - if (qdev_init(vdev) < 0) { - return -1; - } - return 0; + object_property_set_bool(OBJECT(vdev), true, "realized", errp); } static void virtio_net_pci_class_init(ObjectClass *klass, void *data) @@ -1452,7 +1390,7 @@ static void virtio_net_pci_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_NETWORK_ETHERNET; set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); dc->props = virtio_net_properties; - vpciklass->init = virtio_net_pci_init; + vpciklass->realize = virtio_net_pci_realize; } static void virtio_net_pci_instance_init(Object *obj) @@ -1479,21 +1417,22 @@ static Property virtio_rng_pci_properties[] = { DEFINE_PROP_END_OF_LIST(), }; -static int virtio_rng_pci_init(VirtIOPCIProxy *vpci_dev) +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)); - if (qdev_init(vdev) < 0) { - return -1; + 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); - - return 0; } static void virtio_rng_pci_class_init(ObjectClass *klass, void *data) @@ -1502,7 +1441,7 @@ static void virtio_rng_pci_class_init(ObjectClass *klass, void *data) VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); - k->init = virtio_rng_pci_init; + k->realize = virtio_rng_pci_realize; set_bit(DEVICE_CATEGORY_MISC, dc->categories); dc->props = virtio_rng_pci_properties; diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h index 8873b6d..3bac016 100644 --- a/hw/virtio/virtio-pci.h +++ b/hw/virtio/virtio-pci.h @@ -82,7 +82,7 @@ typedef struct { typedef struct VirtioPCIClass { PCIDeviceClass parent_class; - int (*init)(VirtIOPCIProxy *vpci_dev); + void (*realize)(VirtIOPCIProxy *vpci_dev, Error **errp); } VirtioPCIClass; struct VirtIOPCIProxy { diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c index 473c044..06e7178 100644 --- a/hw/virtio/virtio-rng.c +++ b/hw/virtio/virtio-rng.c @@ -149,7 +149,7 @@ static void virtio_rng_device_realize(DeviceState *dev, Error **errp) VirtIORNG *vrng = VIRTIO_RNG(dev); Error *local_err = NULL; - if (!vrng->conf.period_ms > 0) { + if (vrng->conf.period_ms <= 0) { error_setg(errp, "'period' parameter expects a positive integer"); return; } diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 013979a..17c1260 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -155,7 +155,7 @@ static inline uint16_t vring_avail_ring(VirtQueue *vq, int i) return virtio_lduw_phys(vq->vdev, pa); } -static inline uint16_t vring_used_event(VirtQueue *vq) +static inline uint16_t vring_get_used_event(VirtQueue *vq) { return vring_avail_ring(vq, vq->vring.num); } @@ -204,7 +204,7 @@ static inline void vring_used_flags_unset_bit(VirtQueue *vq, int mask) virtio_stw_phys(vdev, pa, virtio_lduw_phys(vdev, pa) & ~mask); } -static inline void vring_avail_event(VirtQueue *vq, uint16_t val) +static inline void vring_set_avail_event(VirtQueue *vq, uint16_t val) { hwaddr pa; if (!vq->notification) { @@ -217,8 +217,8 @@ static inline void vring_avail_event(VirtQueue *vq, uint16_t val) void virtio_queue_set_notification(VirtQueue *vq, int enable) { vq->notification = enable; - if (vq->vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX)) { - vring_avail_event(vq, vring_avail_idx(vq)); + if (virtio_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 { @@ -468,8 +468,8 @@ int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem) max = vq->vring.num; i = head = virtqueue_get_head(vq, vq->last_avail_idx++); - if (vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX)) { - vring_avail_event(vq, vq->last_avail_idx); + if (virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { + vring_set_avail_event(vq, vq->last_avail_idx); } if (vring_desc_flags(vdev, desc_pa, i) & VRING_DESC_F_INDIRECT) { @@ -759,8 +759,9 @@ void virtio_queue_set_align(VirtIODevice *vdev, int n, int align) void virtio_queue_notify_vq(VirtQueue *vq) { - if (vq->vring.desc) { + 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); } @@ -819,19 +820,6 @@ void virtio_irq(VirtQueue *vq) virtio_notify_vector(vq->vdev, vq->vector); } -/* Assuming a given event_idx value from the other size, if - * we have just incremented index from old to new_idx, - * should we trigger an event? */ -static inline int vring_need_event(uint16_t event, uint16_t new, uint16_t old) -{ - /* Note: Xen has similar logic for notification hold-off - * in include/xen/interface/io/ring.h with req_event and req_prod - * corresponding to event_idx + 1 and new respectively. - * Note also that req_event and req_prod in Xen start at 1, - * event indexes in virtio start at 0. */ - return (uint16_t)(new - event - 1) < (uint16_t)(new - old); -} - static bool vring_notify(VirtIODevice *vdev, VirtQueue *vq) { uint16_t old, new; @@ -839,12 +827,12 @@ static bool vring_notify(VirtIODevice *vdev, VirtQueue *vq) /* We need to expose used array entries before checking used event. */ smp_mb(); /* Always notify when queue is empty (when feature acknowledge) */ - if (((vdev->guest_features & (1 << VIRTIO_F_NOTIFY_ON_EMPTY)) && - !vq->inuse && vring_avail_idx(vq) == vq->last_avail_idx)) { + if (virtio_has_feature(vdev, VIRTIO_F_NOTIFY_ON_EMPTY) && + !vq->inuse && vring_avail_idx(vq) == vq->last_avail_idx) { return true; } - if (!(vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX))) { + if (!virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { return !(vring_avail_flags(vq) & VRING_AVAIL_F_NO_INTERRUPT); } @@ -852,7 +840,7 @@ static bool vring_notify(VirtIODevice *vdev, VirtQueue *vq) vq->signalled_used_valid = true; old = vq->signalled_used; new = vq->signalled_used = vring_used_idx(vq); - return !v || vring_need_event(vring_used_event(vq), new, old); + return !v || vring_need_event(vring_get_used_event(vq), new, old); } void virtio_notify(VirtIODevice *vdev, VirtQueue *vq) @@ -955,7 +943,7 @@ void virtio_save(VirtIODevice *vdev, QEMUFile *f) } /* Subsections */ - vmstate_save_state(f, &vmstate_virtio, vdev); + vmstate_save_state(f, &vmstate_virtio, vdev, NULL); } int virtio_set_features(VirtIODevice *vdev, uint32_t val) diff --git a/hw/watchdog/watchdog.c b/hw/watchdog/watchdog.c index c307f9b..54440c9 100644 --- a/hw/watchdog/watchdog.c +++ b/hw/watchdog/watchdog.c @@ -68,7 +68,7 @@ int select_watchdog(const char *p) /* add the device */ opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, &error_abort); - qemu_opt_set(opts, "driver", p); + qemu_opt_set(opts, "driver", p, &error_abort); return 0; } } diff --git a/hw/watchdog/wdt_i6300esb.c b/hw/watchdog/wdt_i6300esb.c index 687c8b1..4ebdbb8 100644 --- a/hw/watchdog/wdt_i6300esb.c +++ b/hw/watchdog/wdt_i6300esb.c @@ -125,8 +125,14 @@ static void i6300esb_restart_timer(I6300State *d, int stage) else timeout <<= 5; - /* Get the timeout in units of ticks_per_sec. */ - timeout = get_ticks_per_sec() * timeout / 33000000; + /* Get the timeout in units of ticks_per_sec. + * + * ticks_per_sec is typically 10^9 == 0x3B9ACA00 (30 bits), with + * 20 bits of user supplied preload, and 15 bits of scale, the + * multiply here can exceed 64-bits, before we divide by 33MHz, so + * we use a higher-precision intermediate result. + */ + timeout = muldiv64(get_ticks_per_sec(), timeout, 33000000); i6300esb_debug("stage %d, timeout %" PRIi64 "\n", d->stage, timeout); @@ -369,7 +375,7 @@ static const MemoryRegionOps i6300esb_ops = { i6300esb_mem_writel, }, }, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static const VMStateDescription vmstate_i6300esb = { @@ -398,7 +404,7 @@ static const VMStateDescription vmstate_i6300esb = { VMSTATE_INT32(free_run, I6300State), VMSTATE_INT32(locked, I6300State), VMSTATE_INT32(enabled, I6300State), - VMSTATE_TIMER(timer, I6300State), + VMSTATE_TIMER_PTR(timer, I6300State), VMSTATE_UINT32(timer1_preload, I6300State), VMSTATE_UINT32(timer2_preload, I6300State), VMSTATE_INT32(stage, I6300State), @@ -408,7 +414,7 @@ static const VMStateDescription vmstate_i6300esb = { } }; -static int i6300esb_init(PCIDevice *dev) +static void i6300esb_realize(PCIDevice *dev, Error **errp) { I6300State *d = DO_UPCAST(I6300State, dev, dev); @@ -421,8 +427,6 @@ static int i6300esb_init(PCIDevice *dev) "i6300esb", 0x10); pci_register_bar(&d->dev, 0, 0, &d->io_mem); /* qemu_register_coalesced_mmio (addr, 0x10); ? */ - - return 0; } static WatchdogTimerModel model = { @@ -437,7 +441,7 @@ static void i6300esb_class_init(ObjectClass *klass, void *data) k->config_read = i6300esb_config_read; k->config_write = i6300esb_config_write; - k->init = i6300esb_init; + 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; diff --git a/hw/watchdog/wdt_ib700.c b/hw/watchdog/wdt_ib700.c index 8cb9827..0917a71 100644 --- a/hw/watchdog/wdt_ib700.c +++ b/hw/watchdog/wdt_ib700.c @@ -93,7 +93,7 @@ static const VMStateDescription vmstate_ib700 = { .version_id = 0, .minimum_version_id = 0, .fields = (VMStateField[]) { - VMSTATE_TIMER(timer, IB700State), + VMSTATE_TIMER_PTR(timer, IB700State), VMSTATE_END_OF_LIST() } }; diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c index c1bf357..d095c08 100644 --- a/hw/xen/xen_pt.c +++ b/hw/xen/xen_pt.c @@ -388,7 +388,7 @@ static const MemoryRegionOps ops = { .write = xen_pt_bar_write, }; -static int xen_pt_register_regions(XenPCIPassthroughState *s) +static int xen_pt_register_regions(XenPCIPassthroughState *s, uint16_t *cmd) { int i = 0; XenHostPCIDevice *d = &s->real_device; @@ -406,6 +406,7 @@ static int xen_pt_register_regions(XenPCIPassthroughState *s) 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) { @@ -414,6 +415,7 @@ static int xen_pt_register_regions(XenPCIPassthroughState *s) 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, @@ -638,6 +640,7 @@ static int xen_pt_initfn(PCIDevice *d) XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, d); int rc = 0; uint8_t machine_irq = 0; + uint16_t cmd = 0; int pirq = XEN_PT_UNASSIGNED_PIRQ; /* register real device */ @@ -672,7 +675,7 @@ static int xen_pt_initfn(PCIDevice *d) s->io_listener = xen_pt_io_listener; /* Handle real device's MMIO/PIO BARs */ - xen_pt_register_regions(s); + xen_pt_register_regions(s, &cmd); /* reinitialize each config register to be emulated */ if (xen_pt_config_init(s)) { @@ -736,7 +739,12 @@ static int xen_pt_initfn(PCIDevice *d) } out: - memory_listener_register(&s->memory_listener, &address_space_memory); + if (cmd) { + xen_host_pci_set_word(&s->real_device, PCI_COMMAND, + pci_get_word(d->config + PCI_COMMAND) | cmd); + } + + memory_listener_register(&s->memory_listener, &s->dev.bus_master_as); memory_listener_register(&s->io_listener, &address_space_io); XEN_PT_LOG(d, "Real physical device %02x:%02x.%d registered successfully!\n", diff --git a/hw/xen/xen_pt_config_init.c b/hw/xen/xen_pt_config_init.c index de9a20f..95a51db 100644 --- a/hw/xen/xen_pt_config_init.c +++ b/hw/xen/xen_pt_config_init.c @@ -286,23 +286,6 @@ static int xen_pt_irqpin_reg_init(XenPCIPassthroughState *s, } /* Command register */ -static int xen_pt_cmd_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 emu_mask = reg->emu_mask; - - if (s->is_virtfn) { - emu_mask |= PCI_COMMAND_MEMORY; - } - - /* emulate word register */ - valid_emu_mask = emu_mask & valid_mask; - *value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); - - return 0; -} static int xen_pt_cmd_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, uint16_t *val, uint16_t dev_value, uint16_t valid_mask) @@ -310,18 +293,13 @@ static int xen_pt_cmd_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, XenPTRegInfo *reg = cfg_entry->reg; uint16_t writable_mask = 0; uint16_t throughable_mask = 0; - uint16_t emu_mask = reg->emu_mask; - - if (s->is_virtfn) { - emu_mask |= PCI_COMMAND_MEMORY; - } /* modify emulate register */ writable_mask = ~reg->ro_mask & valid_mask; cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); /* create value for writing to I/O device register */ - throughable_mask = ~emu_mask & valid_mask; + throughable_mask = ~reg->emu_mask & valid_mask; if (*val & PCI_COMMAND_INTX_DISABLE) { throughable_mask |= PCI_COMMAND_INTX_DISABLE; @@ -360,15 +338,13 @@ static uint64_t xen_pt_get_bar_size(PCIIORegion *r) } static XenPTBarFlag xen_pt_bar_reg_parse(XenPCIPassthroughState *s, - XenPTRegInfo *reg) + int index) { PCIDevice *d = &s->dev; XenPTRegion *region = NULL; PCIIORegion *r; - int index = 0; /* check 64bit BAR */ - index = xen_pt_bar_offset_to_index(reg->offset); if ((0 < index) && (index < PCI_ROM_SLOT)) { int type = s->real_device.io_regions[index - 1].type; @@ -422,7 +398,7 @@ static int xen_pt_bar_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg, } /* set BAR flag */ - s->bases[index].bar_flag = xen_pt_bar_reg_parse(s, reg); + 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; } @@ -440,7 +416,7 @@ static int xen_pt_bar_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, /* get BAR index */ index = xen_pt_bar_offset_to_index(reg->offset); - if (index < 0 || index >= PCI_NUM_REGIONS) { + if (index < 0 || index >= PCI_NUM_REGIONS - 1) { XEN_PT_ERR(&s->dev, "Internal error: Invalid BAR index [%d].\n", index); return -1; } @@ -605,9 +581,9 @@ static XenPTRegInfo xen_pt_emu_reg_header0[] = { .size = 2, .init_val = 0x0000, .ro_mask = 0xF880, - .emu_mask = 0x0740, + .emu_mask = 0x0743, .init = xen_pt_common_reg_init, - .u.w.read = xen_pt_cmd_reg_read, + .u.w.read = xen_pt_word_reg_read, .u.w.write = xen_pt_cmd_reg_write, }, /* Capabilities Pointer reg */ diff --git a/hw/xtensa/sim.c b/hw/xtensa/sim.c index 37ea9ae..328d209 100644 --- a/hw/xtensa/sim.c +++ b/hw/xtensa/sim.c @@ -64,7 +64,7 @@ static void xtensa_sim_init(MachineState *machine) for (n = 0; n < smp_cpus; n++) { cpu = cpu_xtensa_init(cpu_model); if (cpu == NULL) { - error_report("unable to find CPU definition '%s'\n", + error_report("unable to find CPU definition '%s'", cpu_model); exit(EXIT_FAILURE); } diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c index e5a6bba..ab4d0e4 100644 --- a/hw/xtensa/xtfpga.c +++ b/hw/xtensa/xtfpga.c @@ -162,6 +162,23 @@ static void lx60_reset(void *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 @@ -190,7 +207,7 @@ static void lx_init(const LxBoardDesc *board, MachineState *machine) for (n = 0; n < smp_cpus; n++) { cpu = cpu_xtensa_init(cpu_model); if (cpu == NULL) { - error_report("unable to find CPU definition '%s'\n", + error_report("unable to find CPU definition '%s'", cpu_model); exit(EXIT_FAILURE); } @@ -211,7 +228,8 @@ static void lx_init(const LxBoardDesc *board, MachineState *machine) memory_region_add_subregion(system_memory, 0, ram); system_io = g_malloc(sizeof(*system_io)); - memory_region_init(system_io, NULL, "lx60.io", 224 * 1024 * 1024); + 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) { @@ -235,7 +253,7 @@ static void lx_init(const LxBoardDesc *board, MachineState *machine) board->flash_size / board->flash_sector_size, 4, 0x0000, 0x0000, 0x0000, 0x0000, be); if (flash == NULL) { - error_report("unable to mount pflash\n"); + error_report("unable to mount pflash"); exit(EXIT_FAILURE); } } @@ -287,7 +305,7 @@ static void lx_init(const LxBoardDesc *board, MachineState *machine) uint32_t dtb_addr = tswap32(cur_lowmem); if (!fdt) { - error_report("could not load DTB '%s'\n", dtb_filename); + error_report("could not load DTB '%s'", dtb_filename); exit(EXIT_FAILURE); } @@ -307,7 +325,7 @@ static void lx_init(const LxBoardDesc *board, MachineState *machine) lowmem_end - cur_lowmem); } if (initrd_size < 0) { - error_report("could not load initrd '%s'\n", initrd_filename); + error_report("could not load initrd '%s'", initrd_filename); exit(EXIT_FAILURE); } initrd_location.start = tswap32(cur_lowmem); @@ -333,7 +351,7 @@ static void lx_init(const LxBoardDesc *board, MachineState *machine) if (success > 0 && is_linux) { entry_point = ep; } else { - error_report("could not load kernel '%s'\n", + error_report("could not load kernel '%s'", kernel_filename); exit(EXIT_FAILURE); } @@ -390,7 +408,7 @@ static void xtensa_ml605_init(MachineState *machine) { static const LxBoardDesc ml605_board = { .flash_base = 0xf8000000, - .flash_size = 0x02000000, + .flash_size = 0x01000000, .flash_sector_size = 0x20000, .sram_size = 0x2000000, }; diff --git a/include/block/accounting.h b/include/block/accounting.h index 50b42b3..4c406cf 100644 --- a/include/block/accounting.h +++ b/include/block/accounting.h @@ -39,6 +39,7 @@ typedef struct BlockAcctStats { uint64_t nr_bytes[BLOCK_MAX_IOTYPE]; uint64_t nr_ops[BLOCK_MAX_IOTYPE]; uint64_t total_time_ns[BLOCK_MAX_IOTYPE]; + uint64_t merged[BLOCK_MAX_IOTYPE]; uint64_t wr_highest_sector; } BlockAcctStats; @@ -53,5 +54,7 @@ void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie, void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie); void block_acct_highest_sector(BlockAcctStats *stats, int64_t sector_num, unsigned int nb_sectors); +void block_acct_merge_done(BlockAcctStats *stats, enum BlockAcctType type, + int num_requests); #endif diff --git a/include/block/aio.h b/include/block/aio.h index 6bf0e04..7d1e26b 100644 --- a/include/block/aio.h +++ b/include/block/aio.h @@ -314,7 +314,7 @@ static inline void aio_timer_init(AioContext *ctx, int scale, QEMUTimerCB *cb, void *opaque) { - timer_init(ts, ctx->tlg.tl[type], scale, cb, opaque); + timer_init_tl(ts, ctx->tlg.tl[type], scale, cb, opaque); } /** diff --git a/include/block/block.h b/include/block/block.h index 5450610..98c6703 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -60,6 +60,17 @@ typedef enum { BDRV_REQ_MAY_UNMAP = 0x4, } BdrvRequestFlags; +typedef struct BlockSizes { + uint32_t phys; + uint32_t log; +} BlockSizes; + +typedef struct HDGeometry { + uint32_t heads; + uint32_t sectors; + uint32_t cylinders; +} HDGeometry; + #define BDRV_O_RDWR 0x0002 #define BDRV_O_SNAPSHOT 0x0008 /* open the file read only and save writes in a snapshot */ #define BDRV_O_TEMPORARY 0x0010 /* delete the file after use */ @@ -83,6 +94,9 @@ typedef enum { #define BDRV_SECTOR_SIZE (1ULL << BDRV_SECTOR_BITS) #define BDRV_SECTOR_MASK ~(BDRV_SECTOR_SIZE - 1) +#define BDRV_REQUEST_MAX_SECTORS MIN(SIZE_MAX >> BDRV_SECTOR_BITS, \ + INT_MAX >> BDRV_SECTOR_BITS) + /* * Allocation status flags * BDRV_BLOCK_DATA: data is read from bs->file or another file @@ -133,7 +147,8 @@ typedef enum BlockOpType { BLOCK_OP_TYPE_BACKUP_SOURCE, BLOCK_OP_TYPE_BACKUP_TARGET, BLOCK_OP_TYPE_CHANGE, - BLOCK_OP_TYPE_COMMIT, + BLOCK_OP_TYPE_COMMIT_SOURCE, + BLOCK_OP_TYPE_COMMIT_TARGET, BLOCK_OP_TYPE_DATAPLANE, BLOCK_OP_TYPE_DRIVE_DEL, BLOCK_OP_TYPE_EJECT, @@ -164,7 +179,8 @@ void bdrv_io_limits_disable(BlockDriverState *bs); void bdrv_init(void); void bdrv_init_with_whitelist(void); BlockDriver *bdrv_find_protocol(const char *filename, - bool allow_protocol_prefix); + bool allow_protocol_prefix, + Error **errp); BlockDriver *bdrv_find_format(const char *format_name); BlockDriver *bdrv_find_whitelisted_format(const char *format_name, bool readonly); @@ -329,8 +345,6 @@ BlockAIOCB *bdrv_aio_ioctl(BlockDriverState *bs, void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp); void bdrv_invalidate_cache_all(Error **errp); -void bdrv_clear_incoming_migration_all(void); - /* Ensure contents are flushed to disk. */ int bdrv_flush(BlockDriverState *bs); int coroutine_fn bdrv_co_flush(BlockDriverState *bs); @@ -347,6 +361,10 @@ bool bdrv_unallocated_blocks_are_zero(BlockDriverState *bs); bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs); int64_t bdrv_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum); +int64_t bdrv_get_block_status_above(BlockDriverState *bs, + BlockDriverState *base, + int64_t sector_num, + int nb_sectors, int *pnum); int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum); int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base, @@ -367,20 +385,22 @@ int bdrv_media_changed(BlockDriverState *bs); void bdrv_lock_medium(BlockDriverState *bs, bool locked); void bdrv_eject(BlockDriverState *bs, bool eject_flag); const char *bdrv_get_format_name(BlockDriverState *bs); -BlockDriverState *bdrv_find(const char *name); BlockDriverState *bdrv_find_node(const char *node_name); BlockDeviceInfoList *bdrv_named_nodes_list(void); BlockDriverState *bdrv_lookup_bs(const char *device, const char *node_name, Error **errp); bool bdrv_chain_contains(BlockDriverState *top, BlockDriverState *base); +BlockDriverState *bdrv_next_node(BlockDriverState *bs); BlockDriverState *bdrv_next(BlockDriverState *bs); int bdrv_is_encrypted(BlockDriverState *bs); int bdrv_key_required(BlockDriverState *bs); int bdrv_set_key(BlockDriverState *bs, const char *key); +void bdrv_add_key(BlockDriverState *bs, const char *key, Error **errp); int bdrv_query_missing_keys(void); void bdrv_iterate_format(void (*it)(void *opaque, const char *name), void *opaque); +const char *bdrv_get_node_name(const BlockDriverState *bs); const char *bdrv_get_device_name(const BlockDriverState *bs); int bdrv_get_flags(BlockDriverState *bs); int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num, @@ -396,9 +416,14 @@ const char *bdrv_get_encrypted_filename(BlockDriverState *bs); void bdrv_get_backing_filename(BlockDriverState *bs, char *filename, int filename_size); void bdrv_get_full_backing_filename(BlockDriverState *bs, - char *dest, size_t sz); + char *dest, size_t sz, Error **errp); +void bdrv_get_full_backing_filename_from_filename(const char *backed, + const char *backing, + char *dest, size_t sz, + Error **errp); int bdrv_is_snapshot(BlockDriverState *bs); +int path_has_protocol(const char *path); int path_is_absolute(const char *path); void path_combine(char *dest, int dest_size, const char *base_path, @@ -433,8 +458,10 @@ BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, int granularity, void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap); BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs); int bdrv_get_dirty(BlockDriverState *bs, BdrvDirtyBitmap *bitmap, int64_t sector); -void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector, int nr_sectors); -void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector, int nr_sectors); +void bdrv_set_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap, + int64_t cur_sector, int nr_sectors); +void bdrv_reset_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap, + int64_t cur_sector, int nr_sectors); void bdrv_dirty_iter_init(BlockDriverState *bs, BdrvDirtyBitmap *bitmap, struct HBitmapIter *hbi); int64_t bdrv_get_dirty_count(BlockDriverState *bs, BdrvDirtyBitmap *bitmap); @@ -534,10 +561,11 @@ AioContext *bdrv_get_aio_context(BlockDriverState *bs); * Changes the #AioContext used for fd handlers, timers, and BHs by this * BlockDriverState and all its children. * - * This function must be called from the old #AioContext or with a lock held so - * the old #AioContext is not executing. + * This function must be called with iothread lock held. */ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context); +int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz); +int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo); void bdrv_io_plug(BlockDriverState *bs); void bdrv_io_unplug(BlockDriverState *bs); diff --git a/include/block/block_int.h b/include/block/block_int.h index a1c17b9..3f70228 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -56,6 +56,10 @@ #define BLOCK_OPT_ADAPTER_TYPE "adapter_type" #define BLOCK_OPT_REDUNDANCY "redundancy" #define BLOCK_OPT_NOCOW "nocow" +#define BLOCK_OPT_OBJECT_SIZE "object_size" +#define BLOCK_OPT_REFCOUNT_BITS "refcount_bits" + +#define BLOCK_PROBE_BUF_SIZE 512 typedef struct BdrvTrackedRequest { BlockDriverState *bs; @@ -271,6 +275,21 @@ struct BlockDriver { void (*bdrv_io_unplug)(BlockDriverState *bs); void (*bdrv_flush_io_queue)(BlockDriverState *bs); + /** + * Try to get @bs's logical and physical block size. + * On success, store them in @bsz and return zero. + * On failure, return negative errno. + */ + int (*bdrv_probe_blocksizes)(BlockDriverState *bs, BlockSizes *bsz); + /** + * Try to get @bs's geometry (cyls, heads, sectors) + * On success, store them in @geo and return 0. + * On failure return -errno. + * Only drivers that want to override guest geometry implement this + * callback; see hd_geometry_guess(). + */ + int (*bdrv_probe_geometry)(BlockDriverState *bs, HDGeometry *geo); + QLIST_ENTRY(BlockDriver) list; }; @@ -324,6 +343,7 @@ struct BlockDriverState { int sg; /* if true, the device is a /dev/sg* */ int copy_on_read; /* if true, copy read backing sectors into image note this is a reference count */ + bool probed; BlockDriver *drv; /* NULL means no media */ void *opaque; @@ -336,13 +356,13 @@ struct BlockDriverState { * regarding this BDS's context */ QLIST_HEAD(, BdrvAioNotifier) aio_notifiers; - char filename[1024]; - char backing_file[1024]; /* if non zero, the image is a diff of - this file image */ + char filename[PATH_MAX]; + char backing_file[PATH_MAX]; /* if non zero, the image is a diff of + this file image */ char backing_format[16]; /* if non-zero and backing_file exists */ QDict *full_open_options; - char exact_filename[1024]; + char exact_filename[PATH_MAX]; BlockDriverState *backing_hd; BlockDriverState *file; @@ -366,9 +386,6 @@ struct BlockDriverState { /* I/O Limits */ BlockLimits bl; - /* Whether the disk can expand beyond total_sectors */ - int growable; - /* Whether produces zeros when read beyond eof */ bool zero_beyond_eof; @@ -409,9 +426,23 @@ struct BlockDriverState { /* The error object in use for blocking operations on backing_hd */ Error *backing_blocker; + + /* threshold limit for writes, in bytes. "High water mark". */ + uint64_t write_threshold_offset; + NotifierWithReturn write_threshold_notifier; }; + +/* Essential block drivers which must always be statically linked into qemu, and + * which therefore can be accessed without using bdrv_find_format() */ +extern BlockDriver bdrv_file; +extern BlockDriver bdrv_raw; +extern BlockDriver bdrv_qcow2; + + int get_tmp_filename(char *filename, int size); +BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size, + const char *filename); void bdrv_set_io_limits(BlockDriverState *bs, ThrottleConfig *cfg); @@ -548,6 +579,7 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *base, * @mode: Whether to collapse all images in the chain to the target. * @on_source_error: The action to take upon error reading from the source. * @on_target_error: The action to take upon error writing to the target. + * @unmap: Whether to unmap target where source sectors only contain zeroes. * @cb: Completion function for the job. * @opaque: Opaque pointer value passed to @cb. * @errp: Error object. @@ -562,6 +594,7 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target, int64_t speed, int64_t granularity, int64_t buf_size, MirrorSyncMode mode, BlockdevOnError on_source_error, BlockdevOnError on_target_error, + bool unmap, BlockCompletionFunc *cb, void *opaque, Error **errp); diff --git a/include/block/coroutine.h b/include/block/coroutine.h index 793df0e..20c027a 100644 --- a/include/block/coroutine.h +++ b/include/block/coroutine.h @@ -216,14 +216,4 @@ void coroutine_fn co_aio_sleep_ns(AioContext *ctx, QEMUClockType type, */ void coroutine_fn yield_until_fd_readable(int fd); -/** - * Add or subtract from the coroutine pool size - * - * The coroutine implementation keeps a pool of coroutines to be reused by - * qemu_coroutine_create(). This makes coroutine creation cheap. Heavy - * coroutine users should call this to reserve pool space. Call it again with - * a negative number to release pool space. - */ -void qemu_coroutine_adjust_pool_size(int n); - #endif /* QEMU_COROUTINE_H */ diff --git a/include/block/coroutine_int.h b/include/block/coroutine_int.h index f133d65..9aa1aae 100644 --- a/include/block/coroutine_int.h +++ b/include/block/coroutine_int.h @@ -31,6 +31,7 @@ typedef enum { COROUTINE_YIELD = 1, COROUTINE_TERMINATE = 2, + COROUTINE_ENTER = 3, } CoroutineAction; struct Coroutine { diff --git a/include/block/nbd.h b/include/block/nbd.h index 9e835d2..65f409d 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -54,8 +54,8 @@ struct nbd_reply { /* Reply types. */ #define NBD_REP_ACK (1) /* Data sending finished. */ #define NBD_REP_SERVER (2) /* Export description. */ -#define NBD_REP_ERR_UNSUP ((1 << 31) | 1) /* Unknown option. */ -#define NBD_REP_ERR_INVALID ((1 << 31) | 3) /* Invalid length. */ +#define NBD_REP_ERR_UNSUP ((UINT32_C(1) << 31) | 1) /* Unknown option. */ +#define NBD_REP_ERR_INVALID ((UINT32_C(1) << 31) | 3) /* Invalid length. */ #define NBD_CMD_MASK_COMMAND 0x0000ffff #define NBD_CMD_FLAG_FUA (1 << 16) @@ -75,8 +75,8 @@ enum { ssize_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read); int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags, - off_t *size, size_t *blocksize); -int nbd_init(int fd, int csock, uint32_t flags, off_t size, size_t blocksize); + off_t *size, Error **errp); +int nbd_init(int fd, int csock, uint32_t flags, off_t size); ssize_t nbd_send_request(int csock, struct nbd_request *request); ssize_t nbd_receive_reply(int csock, struct nbd_reply *reply); int nbd_client(int fd); @@ -85,14 +85,14 @@ int nbd_disconnect(int fd); typedef struct NBDExport NBDExport; typedef struct NBDClient NBDClient; -NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, - off_t size, uint32_t nbdflags, - void (*close)(NBDExport *)); +NBDExport *nbd_export_new(BlockBackend *blk, off_t dev_offset, off_t size, + uint32_t nbdflags, void (*close)(NBDExport *), + Error **errp); void nbd_export_close(NBDExport *exp); void nbd_export_get(NBDExport *exp); void nbd_export_put(NBDExport *exp); -BlockDriverState *nbd_export_get_blockdev(NBDExport *exp); +BlockBackend *nbd_export_get_blockdev(NBDExport *exp); NBDExport *nbd_export_find(const char *name); void nbd_export_set_name(NBDExport *exp, const char *name); @@ -100,7 +100,6 @@ void nbd_export_close_all(void); NBDClient *nbd_client_new(NBDExport *exp, int csock, void (*close)(NBDClient *)); -void nbd_client_close(NBDClient *client); void nbd_client_get(NBDClient *client); void nbd_client_put(NBDClient *client); diff --git a/include/block/write-threshold.h b/include/block/write-threshold.h new file mode 100644 index 0000000..f1b899c --- /dev/null +++ b/include/block/write-threshold.h @@ -0,0 +1,64 @@ +/* + * QEMU System Emulator block write threshold notification + * + * Copyright Red Hat, Inc. 2014 + * + * Authors: + * Francesco Romani + * + * 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. + */ +#ifndef BLOCK_WRITE_THRESHOLD_H +#define BLOCK_WRITE_THRESHOLD_H + +#include + +#include "qemu/typedefs.h" +#include "qemu-common.h" + +/* + * bdrv_write_threshold_set: + * + * Set the write threshold for block devices, in bytes. + * Notify when a write exceeds the threshold, meaning the device + * is becoming full, so it can be transparently resized. + * To be used with thin-provisioned block devices. + * + * Use threshold_bytes == 0 to disable. + */ +void bdrv_write_threshold_set(BlockDriverState *bs, uint64_t threshold_bytes); + +/* + * bdrv_write_threshold_get + * + * Get the configured write threshold, in bytes. + * Zero means no threshold configured. + */ +uint64_t bdrv_write_threshold_get(const BlockDriverState *bs); + +/* + * bdrv_write_threshold_is_set + * + * Tell if a write threshold is set for a given BDS. + */ +bool bdrv_write_threshold_is_set(const BlockDriverState *bs); + +/* + * bdrv_write_threshold_exceeded + * + * Return the extent of a write request that exceeded the threshold, + * or zero if the request is below the threshold. + * Return zero also if the threshold was not set. + * + * NOTE: here we assume the following holds for each request this code + * deals with: + * + * assert((req->offset + req->bytes) <= UINT64_MAX) + * + * Please not there is *not* an actual C assert(). + */ +uint64_t bdrv_write_threshold_exceeded(const BlockDriverState *bs, + const BdrvTrackedRequest *req); + +#endif diff --git a/include/elf.h b/include/elf.h index a516584..3e75f05 100644 --- a/include/elf.h +++ b/include/elf.h @@ -1508,6 +1508,7 @@ struct elf32_fdpic_loadmap { #define elf_shdr elf32_shdr #define elf_sym elf32_sym #define elf_addr_t Elf32_Off +#define elf_rela elf32_rela #ifdef ELF_USES_RELOCA # define ELF_RELOC Elf32_Rela @@ -1523,6 +1524,7 @@ struct elf32_fdpic_loadmap { #define elf_shdr elf64_shdr #define elf_sym elf64_sym #define elf_addr_t Elf64_Off +#define elf_rela elf64_rela #ifdef ELF_USES_RELOCA # define ELF_RELOC Elf64_Rela diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index c085804..ac06c67 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -24,6 +24,7 @@ #include "exec/memory.h" #include "qemu/thread.h" #include "qom/cpu.h" +#include "qemu/rcu.h" /* some important defines: * @@ -115,43 +116,9 @@ static inline void tswap64s(uint64_t *s) #define bswaptls(s) bswap64s(s) #endif -/* CPU memory access without any memory or io remapping */ - -/* - * the generic syntax for the memory accesses is: - * - * load: ld{type}{sign}{size}{endian}_{access_type}(ptr) - * - * store: st{type}{size}{endian}_{access_type}(ptr, val) - * - * type is: - * (empty): integer access - * f : float access - * - * sign is: - * (empty): for floats or 32 bit size - * u : unsigned - * s : signed - * - * size is: - * b: 8 bits - * w: 16 bits - * l: 32 bits - * q: 64 bits - * - * endian is: - * (empty): target cpu endianness or 8 bit access - * r : reversed target cpu endianness (not implemented yet) - * be : big endian (not implemented yet) - * le : little endian (not implemented yet) - * - * access_type is: - * raw : host memory access - * user : user mode access using soft MMU - * kernel : kernel mode access using soft MMU +/* Target-endianness CPU memory access functions. These fit into the + * {ld,st}{type}{sign}{size}{endian}_p naming scheme described in bswap.h. */ - -/* target-endianness CPU memory access functions */ #if defined(TARGET_WORDS_BIGENDIAN) #define lduw_p(p) lduw_be_p(p) #define ldsw_p(p) ldsw_be_p(p) @@ -299,27 +266,38 @@ CPUArchState *cpu_copy(CPUArchState *env); /* memory API */ -typedef struct RAMBlock { +typedef struct RAMBlock RAMBlock; + +struct RAMBlock { + struct rcu_head rcu; struct MemoryRegion *mr; uint8_t *host; ram_addr_t offset; - ram_addr_t length; + ram_addr_t used_length; + ram_addr_t max_length; + void (*resized)(const char*, uint64_t length, void *host); uint32_t flags; + /* Protected by iothread lock. */ char idstr[256]; - /* Reads can take either the iothread or the ramlist lock. - * Writes must take both locks. - */ - QTAILQ_ENTRY(RAMBlock) next; + /* RCU-enabled, writes protected by the ramlist lock */ + QLIST_ENTRY(RAMBlock) next; int fd; -} RAMBlock; +}; + +static inline void *ramblock_ptr(RAMBlock *block, ram_addr_t offset) +{ + assert(offset < block->used_length); + assert(block->host); + return (char *)block->host + offset; +} typedef struct RAMList { QemuMutex mutex; /* Protected by the iothread lock. */ unsigned long *dirty_memory[DIRTY_MEMORY_NUM]; RAMBlock *mru_block; - /* Protected by the ramlist lock. */ - QTAILQ_HEAD(, RAMBlock) blocks; + /* RCU-enabled, writes protected by the ramlist lock. */ + QLIST_HEAD(, RAMBlock) blocks; uint32_t version; } RAMList; extern RAMList ram_list; @@ -335,6 +313,7 @@ extern RAMList ram_list; #define TLB_MMIO (1 << 5) void dump_exec_info(FILE *f, fprintf_function cpu_fprintf); +void dump_opcount_info(FILE *f, fprintf_function cpu_fprintf); ram_addr_t last_ram_offset(void); void qemu_mutex_lock_ramlist(void); void qemu_mutex_unlock_ramlist(void); diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 427b851..fcc3162 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -52,6 +52,7 @@ typedef uintptr_t ram_addr_t; #endif extern ram_addr_t ram_size; +ram_addr_t get_current_ram_size(void); /* memory API */ diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h index e5550e7..1673287 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -23,7 +23,26 @@ * * Used by target op helpers. * - * MMU mode suffixes are defined in target cpu.h. + * The syntax for the accessors is: + * + * load: cpu_ld{sign}{size}_{mmusuffix}(env, ptr) + * + * store: cpu_st{sign}{size}_{mmusuffix}(env, ptr, val) + * + * sign is: + * (empty): for 32 and 64 bit sizes + * u : unsigned + * s : signed + * + * size is: + * b: 8 bits + * w: 16 bits + * l: 32 bits + * q: 64 bits + * + * mmusuffix is one of the generic suffixes "data" or "code", or + * (for softmmu configs) a target-specific MMU mode suffix as defined + * in target cpu.h. */ #ifndef CPU_LDST_H #define CPU_LDST_H @@ -53,112 +72,43 @@ h2g_nocheck(x); \ }) -#define saddr(x) g2h(x) -#define laddr(x) g2h(x) - -#else /* !CONFIG_USER_ONLY */ -/* NOTE: we use double casts if pointers and target_ulong have - different sizes */ -#define saddr(x) (uint8_t *)(intptr_t)(x) -#define laddr(x) (uint8_t *)(intptr_t)(x) #endif -#define ldub_raw(p) ldub_p(laddr((p))) -#define ldsb_raw(p) ldsb_p(laddr((p))) -#define lduw_raw(p) lduw_p(laddr((p))) -#define ldsw_raw(p) ldsw_p(laddr((p))) -#define ldl_raw(p) ldl_p(laddr((p))) -#define ldq_raw(p) ldq_p(laddr((p))) -#define ldfl_raw(p) ldfl_p(laddr((p))) -#define ldfq_raw(p) ldfq_p(laddr((p))) -#define stb_raw(p, v) stb_p(saddr((p)), v) -#define stw_raw(p, v) stw_p(saddr((p)), v) -#define stl_raw(p, v) stl_p(saddr((p)), v) -#define stq_raw(p, v) stq_p(saddr((p)), v) -#define stfl_raw(p, v) stfl_p(saddr((p)), v) -#define stfq_raw(p, v) stfq_p(saddr((p)), v) +#if defined(CONFIG_USER_ONLY) +/* In user-only mode we provide only the _code and _data accessors. */ -#if defined(CONFIG_USER_ONLY) +#define MEMSUFFIX _data +#define DATA_SIZE 1 +#include "exec/cpu_ldst_useronly_template.h" -/* if user mode, no other memory access functions */ -#define ldub(p) ldub_raw(p) -#define ldsb(p) ldsb_raw(p) -#define lduw(p) lduw_raw(p) -#define ldsw(p) ldsw_raw(p) -#define ldl(p) ldl_raw(p) -#define ldq(p) ldq_raw(p) -#define ldfl(p) ldfl_raw(p) -#define ldfq(p) ldfq_raw(p) -#define stb(p, v) stb_raw(p, v) -#define stw(p, v) stw_raw(p, v) -#define stl(p, v) stl_raw(p, v) -#define stq(p, v) stq_raw(p, v) -#define stfl(p, v) stfl_raw(p, v) -#define stfq(p, v) stfq_raw(p, v) - -#define cpu_ldub_code(env1, p) ldub_raw(p) -#define cpu_ldsb_code(env1, p) ldsb_raw(p) -#define cpu_lduw_code(env1, p) lduw_raw(p) -#define cpu_ldsw_code(env1, p) ldsw_raw(p) -#define cpu_ldl_code(env1, p) ldl_raw(p) -#define cpu_ldq_code(env1, p) ldq_raw(p) - -#define cpu_ldub_data(env, addr) ldub_raw(addr) -#define cpu_lduw_data(env, addr) lduw_raw(addr) -#define cpu_ldsw_data(env, addr) ldsw_raw(addr) -#define cpu_ldl_data(env, addr) ldl_raw(addr) -#define cpu_ldq_data(env, addr) ldq_raw(addr) - -#define cpu_stb_data(env, addr, data) stb_raw(addr, data) -#define cpu_stw_data(env, addr, data) stw_raw(addr, data) -#define cpu_stl_data(env, addr, data) stl_raw(addr, data) -#define cpu_stq_data(env, addr, data) stq_raw(addr, data) - -#define cpu_ldub_kernel(env, addr) ldub_raw(addr) -#define cpu_lduw_kernel(env, addr) lduw_raw(addr) -#define cpu_ldsw_kernel(env, addr) ldsw_raw(addr) -#define cpu_ldl_kernel(env, addr) ldl_raw(addr) -#define cpu_ldq_kernel(env, addr) ldq_raw(addr) - -#define cpu_stb_kernel(env, addr, data) stb_raw(addr, data) -#define cpu_stw_kernel(env, addr, data) stw_raw(addr, data) -#define cpu_stl_kernel(env, addr, data) stl_raw(addr, data) -#define cpu_stq_kernel(env, addr, data) stq_raw(addr, data) - -#define ldub_kernel(p) ldub_raw(p) -#define ldsb_kernel(p) ldsb_raw(p) -#define lduw_kernel(p) lduw_raw(p) -#define ldsw_kernel(p) ldsw_raw(p) -#define ldl_kernel(p) ldl_raw(p) -#define ldq_kernel(p) ldq_raw(p) -#define ldfl_kernel(p) ldfl_raw(p) -#define ldfq_kernel(p) ldfq_raw(p) -#define stb_kernel(p, v) stb_raw(p, v) -#define stw_kernel(p, v) stw_raw(p, v) -#define stl_kernel(p, v) stl_raw(p, v) -#define stq_kernel(p, v) stq_raw(p, v) -#define stfl_kernel(p, v) stfl_raw(p, v) -#define stfq_kernel(p, vt) stfq_raw(p, v) - -#define cpu_ldub_data(env, addr) ldub_raw(addr) -#define cpu_lduw_data(env, addr) lduw_raw(addr) -#define cpu_ldl_data(env, addr) ldl_raw(addr) - -#define cpu_stb_data(env, addr, data) stb_raw(addr, data) -#define cpu_stw_data(env, addr, data) stw_raw(addr, data) -#define cpu_stl_data(env, addr, data) stl_raw(addr, data) +#define DATA_SIZE 2 +#include "exec/cpu_ldst_useronly_template.h" -#else +#define DATA_SIZE 4 +#include "exec/cpu_ldst_useronly_template.h" -/* XXX: find something cleaner. - * Furthermore, this is false for 64 bits targets - */ -#define ldul_user ldl_user -#define ldul_kernel ldl_kernel -#define ldul_hypv ldl_hypv -#define ldul_executive ldl_executive -#define ldul_supervisor ldl_supervisor +#define DATA_SIZE 8 +#include "exec/cpu_ldst_useronly_template.h" +#undef MEMSUFFIX + +#define MEMSUFFIX _code +#define CODE_ACCESS +#define DATA_SIZE 1 +#include "exec/cpu_ldst_useronly_template.h" + +#define DATA_SIZE 2 +#include "exec/cpu_ldst_useronly_template.h" + +#define DATA_SIZE 4 +#include "exec/cpu_ldst_useronly_template.h" + +#define DATA_SIZE 8 +#include "exec/cpu_ldst_useronly_template.h" +#undef MEMSUFFIX +#undef CODE_ACCESS + +#else /* The memory helpers for tcg-generated code need tcg_target_long etc. */ #include "tcg.h" @@ -182,6 +132,7 @@ uint16_t helper_ldw_cmmu(CPUArchState *env, target_ulong addr, int mmu_idx); uint32_t helper_ldl_cmmu(CPUArchState *env, target_ulong addr, int mmu_idx); uint64_t helper_ldq_cmmu(CPUArchState *env, target_ulong addr, int mmu_idx); +#ifdef MMU_MODE0_SUFFIX #define CPU_MMU_INDEX 0 #define MEMSUFFIX MMU_MODE0_SUFFIX #define DATA_SIZE 1 @@ -197,7 +148,9 @@ uint64_t helper_ldq_cmmu(CPUArchState *env, target_ulong addr, int mmu_idx); #include "exec/cpu_ldst_template.h" #undef CPU_MMU_INDEX #undef MEMSUFFIX +#endif +#if (NB_MMU_MODES >= 2) && defined(MMU_MODE1_SUFFIX) #define CPU_MMU_INDEX 1 #define MEMSUFFIX MMU_MODE1_SUFFIX #define DATA_SIZE 1 @@ -213,8 +166,9 @@ uint64_t helper_ldq_cmmu(CPUArchState *env, target_ulong addr, int mmu_idx); #include "exec/cpu_ldst_template.h" #undef CPU_MMU_INDEX #undef MEMSUFFIX +#endif -#if (NB_MMU_MODES >= 3) +#if (NB_MMU_MODES >= 3) && defined(MMU_MODE2_SUFFIX) #define CPU_MMU_INDEX 2 #define MEMSUFFIX MMU_MODE2_SUFFIX @@ -233,7 +187,7 @@ uint64_t helper_ldq_cmmu(CPUArchState *env, target_ulong addr, int mmu_idx); #undef MEMSUFFIX #endif /* (NB_MMU_MODES >= 3) */ -#if (NB_MMU_MODES >= 4) +#if (NB_MMU_MODES >= 4) && defined(MMU_MODE3_SUFFIX) #define CPU_MMU_INDEX 3 #define MEMSUFFIX MMU_MODE3_SUFFIX @@ -252,7 +206,7 @@ uint64_t helper_ldq_cmmu(CPUArchState *env, target_ulong addr, int mmu_idx); #undef MEMSUFFIX #endif /* (NB_MMU_MODES >= 4) */ -#if (NB_MMU_MODES >= 5) +#if (NB_MMU_MODES >= 5) && defined(MMU_MODE4_SUFFIX) #define CPU_MMU_INDEX 4 #define MEMSUFFIX MMU_MODE4_SUFFIX @@ -271,7 +225,7 @@ uint64_t helper_ldq_cmmu(CPUArchState *env, target_ulong addr, int mmu_idx); #undef MEMSUFFIX #endif /* (NB_MMU_MODES >= 5) */ -#if (NB_MMU_MODES >= 6) +#if (NB_MMU_MODES >= 6) && defined(MMU_MODE5_SUFFIX) #define CPU_MMU_INDEX 5 #define MEMSUFFIX MMU_MODE5_SUFFIX @@ -290,9 +244,31 @@ uint64_t helper_ldq_cmmu(CPUArchState *env, target_ulong addr, int mmu_idx); #undef MEMSUFFIX #endif /* (NB_MMU_MODES >= 6) */ -#if (NB_MMU_MODES > 6) -#error "NB_MMU_MODES > 6 is not supported for now" -#endif /* (NB_MMU_MODES > 6) */ +#if (NB_MMU_MODES >= 7) && defined(MMU_MODE6_SUFFIX) + +#define CPU_MMU_INDEX 6 +#define MEMSUFFIX MMU_MODE6_SUFFIX +#define DATA_SIZE 1 +#include "exec/cpu_ldst_template.h" + +#define DATA_SIZE 2 +#include "exec/cpu_ldst_template.h" + +#define DATA_SIZE 4 +#include "exec/cpu_ldst_template.h" + +#define DATA_SIZE 8 +#include "exec/cpu_ldst_template.h" +#undef CPU_MMU_INDEX +#undef MEMSUFFIX +#endif /* (NB_MMU_MODES >= 7) */ + +#if (NB_MMU_MODES > 7) +/* Note that supporting NB_MMU_MODES == 9 would require + * changes to at least the ARM TCG backend. + */ +#error "NB_MMU_MODES > 7 is not supported for now" +#endif /* (NB_MMU_MODES > 7) */ /* these access are slower, they must be as rare as possible */ #define CPU_MMU_INDEX (cpu_mmu_index(env)) @@ -311,18 +287,6 @@ uint64_t helper_ldq_cmmu(CPUArchState *env, target_ulong addr, int mmu_idx); #undef CPU_MMU_INDEX #undef MEMSUFFIX -#define ldub(p) ldub_data(p) -#define ldsb(p) ldsb_data(p) -#define lduw(p) lduw_data(p) -#define ldsw(p) ldsw_data(p) -#define ldl(p) ldl_data(p) -#define ldq(p) ldq_data(p) - -#define stb(p, v) stb_data(p, v) -#define stw(p, v) stw_data(p, v) -#define stl(p, v) stl_data(p, v) -#define stq(p, v) stq_data(p, v) - #define CPU_MMU_INDEX (cpu_mmu_index(env)) #define MEMSUFFIX _code #define SOFTMMU_CODE_ACCESS diff --git a/include/exec/cpu_ldst_template.h b/include/exec/cpu_ldst_template.h index 006093a..95ab750 100644 --- a/include/exec/cpu_ldst_template.h +++ b/include/exec/cpu_ldst_template.h @@ -4,9 +4,7 @@ * Generate inline load/store functions for one MMU mode and data * size. * - * Generate a store function as well as signed and unsigned loads. For - * 32 and 64 bit cases, also generate floating point functions with - * the same size. + * Generate a store function as well as signed and unsigned loads. * * Not used directly but included from cpu_ldst.h. * @@ -79,7 +77,7 @@ glue(glue(cpu_ld, USUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr) res = glue(glue(helper_ld, SUFFIX), MMUSUFFIX)(env, addr, mmu_idx); } else { uintptr_t hostaddr = addr + env->tlb_table[mmu_idx][page_index].addend; - res = glue(glue(ld, USUFFIX), _raw)(hostaddr); + res = glue(glue(ld, USUFFIX), _p)((uint8_t *)hostaddr); } return res; } @@ -101,7 +99,7 @@ glue(glue(cpu_lds, SUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr) MMUSUFFIX)(env, addr, mmu_idx); } else { uintptr_t hostaddr = addr + env->tlb_table[mmu_idx][page_index].addend; - res = glue(glue(lds, SUFFIX), _raw)(hostaddr); + res = glue(glue(lds, SUFFIX), _p)((uint8_t *)hostaddr); } return res; } @@ -127,60 +125,10 @@ glue(glue(cpu_st, SUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr, glue(glue(helper_st, SUFFIX), MMUSUFFIX)(env, addr, v, mmu_idx); } else { uintptr_t hostaddr = addr + env->tlb_table[mmu_idx][page_index].addend; - glue(glue(st, SUFFIX), _raw)(hostaddr, v); + glue(glue(st, SUFFIX), _p)((uint8_t *)hostaddr, v); } } - - -#if DATA_SIZE == 8 -static inline float64 glue(cpu_ldfq, MEMSUFFIX)(CPUArchState *env, - target_ulong ptr) -{ - union { - float64 d; - uint64_t i; - } u; - u.i = glue(cpu_ldq, MEMSUFFIX)(env, ptr); - return u.d; -} - -static inline void glue(cpu_stfq, MEMSUFFIX)(CPUArchState *env, - target_ulong ptr, float64 v) -{ - union { - float64 d; - uint64_t i; - } u; - u.d = v; - glue(cpu_stq, MEMSUFFIX)(env, ptr, u.i); -} -#endif /* DATA_SIZE == 8 */ - -#if DATA_SIZE == 4 -static inline float32 glue(cpu_ldfl, MEMSUFFIX)(CPUArchState *env, - target_ulong ptr) -{ - union { - float32 f; - uint32_t i; - } u; - u.i = glue(cpu_ldl, MEMSUFFIX)(env, ptr); - return u.f; -} - -static inline void glue(cpu_stfl, MEMSUFFIX)(CPUArchState *env, - target_ulong ptr, float32 v) -{ - union { - float32 f; - uint32_t i; - } u; - u.f = v; - glue(cpu_stl, MEMSUFFIX)(env, ptr, u.i); -} -#endif /* DATA_SIZE == 4 */ - #endif /* !SOFTMMU_CODE_ACCESS */ #undef RES_TYPE diff --git a/include/exec/cpu_ldst_useronly_template.h b/include/exec/cpu_ldst_useronly_template.h new file mode 100644 index 0000000..b3b865f --- /dev/null +++ b/include/exec/cpu_ldst_useronly_template.h @@ -0,0 +1,81 @@ +/* + * User-only accessor function support + * + * Generate inline load/store functions for one data size. + * + * Generate a store function as well as signed and unsigned loads. + * + * Not used directly but included from cpu_ldst.h. + * + * Copyright (c) 2015 Linaro Limited + * + * 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 DATA_SIZE == 8 +#define SUFFIX q +#define USUFFIX q +#define DATA_TYPE uint64_t +#elif DATA_SIZE == 4 +#define SUFFIX l +#define USUFFIX l +#define DATA_TYPE uint32_t +#elif DATA_SIZE == 2 +#define SUFFIX w +#define USUFFIX uw +#define DATA_TYPE uint16_t +#define DATA_STYPE int16_t +#elif DATA_SIZE == 1 +#define SUFFIX b +#define USUFFIX ub +#define DATA_TYPE uint8_t +#define DATA_STYPE int8_t +#else +#error unsupported data size +#endif + +#if DATA_SIZE == 8 +#define RES_TYPE uint64_t +#else +#define RES_TYPE uint32_t +#endif + +static inline RES_TYPE +glue(glue(cpu_ld, USUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr) +{ + return glue(glue(ld, USUFFIX), _p)(g2h(ptr)); +} + +#if DATA_SIZE <= 2 +static inline int +glue(glue(cpu_lds, SUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr) +{ + return glue(glue(lds, SUFFIX), _p)(g2h(ptr)); +} +#endif + +#ifndef CODE_ACCESS +static inline void +glue(glue(cpu_st, SUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr, + RES_TYPE v) +{ + glue(glue(st, SUFFIX), _p)(g2h(ptr), v); +} +#endif + +#undef RES_TYPE +#undef DATA_TYPE +#undef DATA_STYPE +#undef SUFFIX +#undef USUFFIX +#undef DATA_SIZE diff --git a/include/exec/cputlb.h b/include/exec/cputlb.h index b8ecd6f..e0da9d7 100644 --- a/include/exec/cputlb.h +++ b/include/exec/cputlb.h @@ -34,7 +34,7 @@ extern int tlb_flush_count; void tb_flush_jmp_cache(CPUState *cpu, target_ulong addr); MemoryRegionSection * -address_space_translate_for_iotlb(AddressSpace *as, hwaddr addr, hwaddr *xlat, +address_space_translate_for_iotlb(CPUState *cpu, hwaddr addr, hwaddr *xlat, hwaddr *plen); hwaddr memory_region_section_get_iotlb(CPUState *cpu, MemoryRegionSection *section, diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 0844885..8eb0db3 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -96,6 +96,8 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end, int is_cpu_write_access); #if !defined(CONFIG_USER_ONLY) +bool qemu_in_vcpu_thread(void); +void cpu_reload_memory_map(CPUState *cpu); void tcg_cpu_address_space_init(CPUState *cpu, AddressSpace *as); /* cputlb.c */ void tlb_flush_page(CPUState *cpu, target_ulong addr); @@ -142,9 +144,12 @@ struct TranslationBlock { uint64_t flags; /* flags defining in which context the code was generated */ uint16_t size; /* size of target code for this block (1 <= size <= TARGET_PAGE_SIZE) */ - uint16_t cflags; /* compile flags */ + uint16_t icount; + uint32_t cflags; /* compile flags */ #define CF_COUNT_MASK 0x7fff #define CF_LAST_IO 0x8000 /* Last insn may be an IO access. */ +#define CF_NOCACHE 0x10000 /* To be freed after execution */ +#define CF_USE_ICOUNT 0x20000 void *tc_ptr; /* pointer to the translated code */ /* next matching tb for physical address. */ @@ -168,7 +173,6 @@ struct TranslationBlock { jmp_first */ struct TranslationBlock *jmp_next[2]; struct TranslationBlock *jmp_first; - uint32_t icount; }; #include "exec/spinlock.h" @@ -335,7 +339,8 @@ extern uintptr_t tci_tb_ptr; void phys_mem_set_alloc(void *(*alloc)(size_t, uint64_t *align)); -struct MemoryRegion *iotlb_to_region(AddressSpace *as, hwaddr index); +struct MemoryRegion *iotlb_to_region(CPUState *cpu, + hwaddr index); bool io_mem_read(struct MemoryRegion *mr, hwaddr addr, uint64_t *pvalue, unsigned size); bool io_mem_write(struct MemoryRegion *mr, hwaddr addr, diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index a608a26..c633248 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -95,4 +95,10 @@ extern bool gdb_has_xml; /* in gdbstub-xml.c, generated by scripts/feature_to_c.sh */ extern const char *const xml_builtin[][2]; +/* Command line option defining whether semihosting should go via gdb or not */ +extern int semihosting_target; +#define SEMIHOSTING_TARGET_AUTO 0 +#define SEMIHOSTING_TARGET_NATIVE 1 +#define SEMIHOSTING_TARGET_GDB 2 + #endif diff --git a/include/exec/gen-icount.h b/include/exec/gen-icount.h index da53395..05d89d3 100644 --- a/include/exec/gen-icount.h +++ b/include/exec/gen-icount.h @@ -6,13 +6,13 @@ /* Helpers for instruction counting code generation. */ static TCGArg *icount_arg; -static int icount_label; -static int exitreq_label; +static TCGLabel *icount_label; +static TCGLabel *exitreq_label; -static inline void gen_tb_start(void) +static inline void gen_tb_start(TranslationBlock *tb) { - TCGv_i32 count; - TCGv_i32 flag; + TCGv_i32 count, flag, imm; + int i; exitreq_label = gen_new_label(); flag = tcg_temp_new_i32(); @@ -21,16 +21,25 @@ static inline void gen_tb_start(void) tcg_gen_brcondi_i32(TCG_COND_NE, flag, 0, exitreq_label); tcg_temp_free_i32(flag); - if (!use_icount) + if (!(tb->cflags & CF_USE_ICOUNT)) { return; + } icount_label = gen_new_label(); count = tcg_temp_local_new_i32(); tcg_gen_ld_i32(count, cpu_env, -ENV_OFFSET + offsetof(CPUState, icount_decr.u32)); + + imm = tcg_temp_new_i32(); + tcg_gen_movi_i32(imm, 0xdeadbeef); + /* This is a horrid hack to allow fixing up the value later. */ - icount_arg = tcg_ctx.gen_opparam_ptr + 1; - tcg_gen_subi_i32(count, count, 0xdeadbeef); + i = tcg_ctx.gen_last_op_idx; + i = tcg_ctx.gen_op_buf[i].args; + icount_arg = &tcg_ctx.gen_opparam_buf[i + 1]; + + tcg_gen_sub_i32(count, count, imm); + tcg_temp_free_i32(imm); tcg_gen_brcondi_i32(TCG_COND_LT, count, 0, icount_label); tcg_gen_st16_i32(count, cpu_env, @@ -43,11 +52,14 @@ static void gen_tb_end(TranslationBlock *tb, int num_insns) gen_set_label(exitreq_label); tcg_gen_exit_tb((uintptr_t)tb + TB_EXIT_REQUESTED); - if (use_icount) { + if (tb->cflags & CF_USE_ICOUNT) { *icount_arg = num_insns; gen_set_label(icount_label); tcg_gen_exit_tb((uintptr_t)tb + TB_EXIT_ICOUNT_EXPIRED); } + + /* Terminate the linked list. */ + tcg_ctx.gen_op_buf[tcg_ctx.gen_last_op_idx].next = -1; } static inline void gen_io_start(void) diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index 25c43c0..fb467ac 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -23,6 +23,7 @@ typedef struct AddressSpaceDispatch AddressSpaceDispatch; void address_space_init_dispatch(AddressSpace *as); +void address_space_unregister(AddressSpace *as); void address_space_destroy_dispatch(AddressSpace *as); extern const MemoryRegionOps unassigned_mem_ops; diff --git a/include/exec/memory.h b/include/exec/memory.h index f64ab5e..06ffa1d 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -33,6 +33,7 @@ #include "qemu/notify.h" #include "qapi/error.h" #include "qom/object.h" +#include "qemu/rcu.h" #define MAX_PHYS_ADDR_SPACE_BITS 62 #define MAX_PHYS_ADDR (((hwaddr)1 << MAX_PHYS_ADDR_SPACE_BITS) - 1) @@ -207,9 +208,13 @@ struct MemoryListener { */ struct AddressSpace { /* All fields are private. */ + struct rcu_head rcu; char *name; MemoryRegion *root; + + /* Accessed via RCU. */ struct FlatView *current_map; + int ioeventfd_nb; struct MemoryRegionIoeventfd *ioeventfds; struct AddressSpaceDispatch *dispatch; @@ -321,6 +326,30 @@ void memory_region_init_ram(MemoryRegion *mr, uint64_t size, Error **errp); +/** + * memory_region_init_resizeable_ram: Initialize memory region with resizeable + * RAM. Accesses into the region will + * modify memory directly. Only an initial + * portion of this RAM is actually used. + * The used size can change across reboots. + * + * @mr: the #MemoryRegion to be initialized. + * @owner: the object that tracks the region's reference count + * @name: the name of the region. + * @size: used size of the region. + * @max_size: max size of the region. + * @resized: callback to notify owner about used size change. + * @errp: pointer to Error*, to store an error if it happens. + */ +void memory_region_init_resizeable_ram(MemoryRegion *mr, + struct Object *owner, + const char *name, + uint64_t size, + uint64_t max_size, + void (*resized)(const char*, + uint64_t length, + void *host), + Error **errp); #ifdef __linux__ /** * memory_region_init_ram_from_file: Initialize RAM memory region with a @@ -878,6 +907,16 @@ void memory_region_set_enabled(MemoryRegion *mr, bool enabled); void memory_region_set_address(MemoryRegion *mr, hwaddr addr); /* + * memory_region_set_size: dynamically update the size of a region. + * + * Dynamically updates the size of a region. + * + * @mr: the region to be updated + * @size: used size of the region. + */ +void memory_region_set_size(MemoryRegion *mr, uint64_t size); + +/* * memory_region_set_alias_offset: dynamically update a memory alias's offset * * Dynamically updates the offset into the target region that an alias points diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index 8fc75cd..ff558a4 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -28,12 +28,19 @@ ram_addr_t qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr, ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, MemoryRegion *mr, Error **errp); ram_addr_t qemu_ram_alloc(ram_addr_t size, MemoryRegion *mr, Error **errp); +ram_addr_t qemu_ram_alloc_resizeable(ram_addr_t size, ram_addr_t max_size, + void (*resized)(const char*, + uint64_t length, + void *host), + MemoryRegion *mr, Error **errp); int qemu_get_ram_fd(ram_addr_t addr); void *qemu_get_ram_block_host_ptr(ram_addr_t addr); void *qemu_get_ram_ptr(ram_addr_t addr); void qemu_ram_free(ram_addr_t addr); void qemu_ram_free_from_ptr(ram_addr_t addr); +int qemu_ram_resize(ram_addr_t base, ram_addr_t newsize, Error **errp); + static inline bool cpu_physical_memory_get_dirty(ram_addr_t start, ram_addr_t length, unsigned client) @@ -172,9 +179,9 @@ static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, } #endif /* not _WIN32 */ -static inline void cpu_physical_memory_clear_dirty_range(ram_addr_t start, - ram_addr_t length, - unsigned client) +static inline void cpu_physical_memory_clear_dirty_range_type(ram_addr_t start, + ram_addr_t length, + unsigned client) { unsigned long end, page; @@ -184,6 +191,15 @@ static inline void cpu_physical_memory_clear_dirty_range(ram_addr_t start, bitmap_clear(ram_list.dirty_memory[client], page, end - page); } +static inline void cpu_physical_memory_clear_dirty_range(ram_addr_t start, + ram_addr_t length) +{ + cpu_physical_memory_clear_dirty_range_type(start, length, DIRTY_MEMORY_MIGRATION); + cpu_physical_memory_clear_dirty_range_type(start, length, DIRTY_MEMORY_VGA); + cpu_physical_memory_clear_dirty_range_type(start, length, DIRTY_MEMORY_CODE); +} + + void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t length, unsigned client); diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h index e32e25d..ded34eb 100644 --- a/include/fpu/softfloat.h +++ b/include/fpu/softfloat.h @@ -1,13 +1,24 @@ /* * QEMU float support * - * Derived from SoftFloat. + * The code in this source file is derived from release 2a of the SoftFloat + * IEC/IEEE Floating-point Arithmetic Package. Those parts of the code (and + * some later contributions) are provided under that license, as detailed below. + * It has subsequently been modified by contributors to the QEMU Project, + * so some portions are provided under: + * the SoftFloat-2a license + * the BSD license + * GPL-v2-or-later + * + * Any future contributions to this file after December 1st 2014 will be + * taken to be licensed under the Softfloat-2a license unless specifically + * indicated otherwise. */ -/*============================================================================ - -This C header file is part of the SoftFloat IEC/IEEE Floating-point Arithmetic -Package, Release 2b. +/* +=============================================================================== +This C header file is part of the SoftFloat IEC/IEEE Floating-point +Arithmetic Package, Release 2a. Written by John R. Hauser. This work was made possible in part by the International Computer Science Institute, located at Suite 600, 1947 Center @@ -16,24 +27,57 @@ National Science Foundation under grant MIP-9311980. The original version of this code was written as part of a project to build a fixed-point vector processor in collaboration with the University of California at Berkeley, overseen by Profs. Nelson Morgan and John Wawrzynek. More information -is available through the Web page `http://www.cs.berkeley.edu/~jhauser/ +is available through the Web page `http://HTTP.CS.Berkeley.EDU/~jhauser/ arithmetic/SoftFloat.html'. -THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort has -been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES -RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS -AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES, -COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE -EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE -INSTITUTE (possibly via similar legal warning) AGAINST ALL LOSSES, COSTS, OR -OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE. +THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort +has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT +TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO +PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY +AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE. Derivative works are acceptable, even for commercial purposes, so long as -(1) the source code for the derivative work includes prominent notice that -the work is derivative, and (2) the source code includes prominent notice with -these four paragraphs for those parts of this code that are retained. +(1) they include prominent notice that the work is derivative, and (2) they +include prominent notice akin to these four paragraphs for those parts of +this code that are retained. -=============================================================================*/ +=============================================================================== +*/ + +/* BSD licensing: + * Copyright (c) 2006, Fabrice Bellard + * 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. + * + * 3. Neither the name of the copyright holder 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 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. + */ + +/* Portions of this work are licensed under the terms of the GNU GPL, + * version 2 or later. See the COPYING file in the top-level directory. + */ #ifndef SOFTFLOAT_H #define SOFTFLOAT_H @@ -64,10 +108,6 @@ typedef int64_t int64; #define LIT64( a ) a##LL -#define STATUS_PARAM , float_status *status -#define STATUS(field) status->field -#define STATUS_VAR , status - /*---------------------------------------------------------------------------- | Software IEC/IEEE floating-point ordering relations *----------------------------------------------------------------------------*/ @@ -180,75 +220,76 @@ typedef struct float_status { flag default_nan_mode; } float_status; -static inline void set_float_detect_tininess(int val STATUS_PARAM) +static inline void set_float_detect_tininess(int val, float_status *status) { - STATUS(float_detect_tininess) = val; + status->float_detect_tininess = val; } -static inline void set_float_rounding_mode(int val STATUS_PARAM) +static inline void set_float_rounding_mode(int val, float_status *status) { - STATUS(float_rounding_mode) = val; + status->float_rounding_mode = val; } -static inline void set_float_exception_flags(int val STATUS_PARAM) +static inline void set_float_exception_flags(int val, float_status *status) { - STATUS(float_exception_flags) = val; + status->float_exception_flags = val; } -static inline void set_floatx80_rounding_precision(int val STATUS_PARAM) +static inline void set_floatx80_rounding_precision(int val, + float_status *status) { - STATUS(floatx80_rounding_precision) = val; + status->floatx80_rounding_precision = val; } -static inline void set_flush_to_zero(flag val STATUS_PARAM) +static inline void set_flush_to_zero(flag val, float_status *status) { - STATUS(flush_to_zero) = val; + status->flush_to_zero = val; } -static inline void set_flush_inputs_to_zero(flag val STATUS_PARAM) +static inline void set_flush_inputs_to_zero(flag val, float_status *status) { - STATUS(flush_inputs_to_zero) = val; + status->flush_inputs_to_zero = val; } -static inline void set_default_nan_mode(flag val STATUS_PARAM) +static inline void set_default_nan_mode(flag val, float_status *status) { - STATUS(default_nan_mode) = val; + status->default_nan_mode = val; } static inline int get_float_detect_tininess(float_status *status) { - return STATUS(float_detect_tininess); + return status->float_detect_tininess; } static inline int get_float_rounding_mode(float_status *status) { - return STATUS(float_rounding_mode); + return status->float_rounding_mode; } static inline int get_float_exception_flags(float_status *status) { - return STATUS(float_exception_flags); + return status->float_exception_flags; } static inline int get_floatx80_rounding_precision(float_status *status) { - return STATUS(floatx80_rounding_precision); + return status->floatx80_rounding_precision; } static inline flag get_flush_to_zero(float_status *status) { - return STATUS(flush_to_zero); + return status->flush_to_zero; } static inline flag get_flush_inputs_to_zero(float_status *status) { - return STATUS(flush_inputs_to_zero); + return status->flush_inputs_to_zero; } static inline flag get_default_nan_mode(float_status *status) { - return STATUS(default_nan_mode); + return status->default_nan_mode; } /*---------------------------------------------------------------------------- | Routine to raise any or all of the software IEC/IEEE floating-point | exception flags. *----------------------------------------------------------------------------*/ -void float_raise( int8 flags STATUS_PARAM); +void float_raise(int8 flags, float_status *status); /*---------------------------------------------------------------------------- | If `a' is denormal and we are in flush-to-zero mode then set the | input-denormal exception and return zero. Otherwise just return the value. *----------------------------------------------------------------------------*/ -float32 float32_squash_input_denormal(float32 a STATUS_PARAM); -float64 float64_squash_input_denormal(float64 a STATUS_PARAM); +float32 float32_squash_input_denormal(float32 a, float_status *status); +float64 float64_squash_input_denormal(float64 a, float_status *status); /*---------------------------------------------------------------------------- | Options to indicate which negations to perform in float*_muladd() @@ -268,48 +309,48 @@ enum { /*---------------------------------------------------------------------------- | Software IEC/IEEE integer-to-floating-point conversion routines. *----------------------------------------------------------------------------*/ -float32 int32_to_float32(int32_t STATUS_PARAM); -float64 int32_to_float64(int32_t STATUS_PARAM); -float32 uint32_to_float32(uint32_t STATUS_PARAM); -float64 uint32_to_float64(uint32_t STATUS_PARAM); -floatx80 int32_to_floatx80(int32_t STATUS_PARAM); -float128 int32_to_float128(int32_t STATUS_PARAM); -float32 int64_to_float32(int64_t STATUS_PARAM); -float32 uint64_to_float32(uint64_t STATUS_PARAM); -float64 int64_to_float64(int64_t STATUS_PARAM); -float64 uint64_to_float64(uint64_t STATUS_PARAM); -floatx80 int64_to_floatx80(int64_t STATUS_PARAM); -float128 int64_to_float128(int64_t STATUS_PARAM); -float128 uint64_to_float128(uint64_t STATUS_PARAM); +float32 int32_to_float32(int32_t, float_status *status); +float64 int32_to_float64(int32_t, float_status *status); +float32 uint32_to_float32(uint32_t, float_status *status); +float64 uint32_to_float64(uint32_t, float_status *status); +floatx80 int32_to_floatx80(int32_t, float_status *status); +float128 int32_to_float128(int32_t, float_status *status); +float32 int64_to_float32(int64_t, float_status *status); +float64 int64_to_float64(int64_t, float_status *status); +floatx80 int64_to_floatx80(int64_t, float_status *status); +float128 int64_to_float128(int64_t, float_status *status); +float32 uint64_to_float32(uint64_t, float_status *status); +float64 uint64_to_float64(uint64_t, float_status *status); +float128 uint64_to_float128(uint64_t, float_status *status); /* We provide the int16 versions for symmetry of API with float-to-int */ -static inline float32 int16_to_float32(int16_t v STATUS_PARAM) +static inline float32 int16_to_float32(int16_t v, float_status *status) { - return int32_to_float32(v STATUS_VAR); + return int32_to_float32(v, status); } -static inline float32 uint16_to_float32(uint16_t v STATUS_PARAM) +static inline float32 uint16_to_float32(uint16_t v, float_status *status) { - return uint32_to_float32(v STATUS_VAR); + return uint32_to_float32(v, status); } -static inline float64 int16_to_float64(int16_t v STATUS_PARAM) +static inline float64 int16_to_float64(int16_t v, float_status *status) { - return int32_to_float64(v STATUS_VAR); + return int32_to_float64(v, status); } -static inline float64 uint16_to_float64(uint16_t v STATUS_PARAM) +static inline float64 uint16_to_float64(uint16_t v, float_status *status) { - return uint32_to_float64(v STATUS_VAR); + return uint32_to_float64(v, status); } /*---------------------------------------------------------------------------- | Software half-precision conversion routines. *----------------------------------------------------------------------------*/ -float16 float32_to_float16( float32, flag STATUS_PARAM ); -float32 float16_to_float32( float16, flag STATUS_PARAM ); -float16 float64_to_float16(float64 a, flag ieee STATUS_PARAM); -float64 float16_to_float64(float16 a, flag ieee STATUS_PARAM); +float16 float32_to_float16(float32, flag, float_status *status); +float32 float16_to_float32(float16, flag, float_status *status); +float16 float64_to_float16(float64 a, flag ieee, float_status *status); +float64 float16_to_float64(float16 a, flag ieee, float_status *status); /*---------------------------------------------------------------------------- | Software half-precision operations. @@ -331,55 +372,55 @@ extern const float16 float16_default_nan; /*---------------------------------------------------------------------------- | Software IEC/IEEE single-precision conversion routines. *----------------------------------------------------------------------------*/ -int_fast16_t float32_to_int16(float32 STATUS_PARAM); -uint_fast16_t float32_to_uint16(float32 STATUS_PARAM); -int_fast16_t float32_to_int16_round_to_zero(float32 STATUS_PARAM); -uint_fast16_t float32_to_uint16_round_to_zero(float32 STATUS_PARAM); -int32 float32_to_int32( float32 STATUS_PARAM ); -int32 float32_to_int32_round_to_zero( float32 STATUS_PARAM ); -uint32 float32_to_uint32( float32 STATUS_PARAM ); -uint32 float32_to_uint32_round_to_zero( float32 STATUS_PARAM ); -int64 float32_to_int64( float32 STATUS_PARAM ); -uint64 float32_to_uint64(float32 STATUS_PARAM); -uint64 float32_to_uint64_round_to_zero(float32 STATUS_PARAM); -int64 float32_to_int64_round_to_zero( float32 STATUS_PARAM ); -float64 float32_to_float64( float32 STATUS_PARAM ); -floatx80 float32_to_floatx80( float32 STATUS_PARAM ); -float128 float32_to_float128( float32 STATUS_PARAM ); +int_fast16_t float32_to_int16(float32, float_status *status); +uint_fast16_t float32_to_uint16(float32, float_status *status); +int_fast16_t float32_to_int16_round_to_zero(float32, float_status *status); +uint_fast16_t float32_to_uint16_round_to_zero(float32, float_status *status); +int32 float32_to_int32(float32, float_status *status); +int32 float32_to_int32_round_to_zero(float32, float_status *status); +uint32 float32_to_uint32(float32, float_status *status); +uint32 float32_to_uint32_round_to_zero(float32, float_status *status); +int64 float32_to_int64(float32, float_status *status); +uint64 float32_to_uint64(float32, float_status *status); +uint64 float32_to_uint64_round_to_zero(float32, float_status *status); +int64 float32_to_int64_round_to_zero(float32, float_status *status); +float64 float32_to_float64(float32, float_status *status); +floatx80 float32_to_floatx80(float32, float_status *status); +float128 float32_to_float128(float32, float_status *status); /*---------------------------------------------------------------------------- | Software IEC/IEEE single-precision operations. *----------------------------------------------------------------------------*/ -float32 float32_round_to_int( float32 STATUS_PARAM ); -float32 float32_add( float32, float32 STATUS_PARAM ); -float32 float32_sub( float32, float32 STATUS_PARAM ); -float32 float32_mul( float32, float32 STATUS_PARAM ); -float32 float32_div( float32, float32 STATUS_PARAM ); -float32 float32_rem( float32, float32 STATUS_PARAM ); -float32 float32_muladd(float32, float32, float32, int STATUS_PARAM); -float32 float32_sqrt( float32 STATUS_PARAM ); -float32 float32_exp2( float32 STATUS_PARAM ); -float32 float32_log2( float32 STATUS_PARAM ); -int float32_eq( float32, float32 STATUS_PARAM ); -int float32_le( float32, float32 STATUS_PARAM ); -int float32_lt( float32, float32 STATUS_PARAM ); -int float32_unordered( float32, float32 STATUS_PARAM ); -int float32_eq_quiet( float32, float32 STATUS_PARAM ); -int float32_le_quiet( float32, float32 STATUS_PARAM ); -int float32_lt_quiet( float32, float32 STATUS_PARAM ); -int float32_unordered_quiet( float32, float32 STATUS_PARAM ); -int float32_compare( float32, float32 STATUS_PARAM ); -int float32_compare_quiet( float32, float32 STATUS_PARAM ); -float32 float32_min(float32, float32 STATUS_PARAM); -float32 float32_max(float32, float32 STATUS_PARAM); -float32 float32_minnum(float32, float32 STATUS_PARAM); -float32 float32_maxnum(float32, float32 STATUS_PARAM); -float32 float32_minnummag(float32, float32 STATUS_PARAM); -float32 float32_maxnummag(float32, float32 STATUS_PARAM); +float32 float32_round_to_int(float32, float_status *status); +float32 float32_add(float32, float32, float_status *status); +float32 float32_sub(float32, float32, float_status *status); +float32 float32_mul(float32, float32, float_status *status); +float32 float32_div(float32, float32, float_status *status); +float32 float32_rem(float32, float32, float_status *status); +float32 float32_muladd(float32, float32, float32, int, float_status *status); +float32 float32_sqrt(float32, float_status *status); +float32 float32_exp2(float32, float_status *status); +float32 float32_log2(float32, float_status *status); +int float32_eq(float32, float32, float_status *status); +int float32_le(float32, float32, float_status *status); +int float32_lt(float32, float32, float_status *status); +int float32_unordered(float32, float32, float_status *status); +int float32_eq_quiet(float32, float32, float_status *status); +int float32_le_quiet(float32, float32, float_status *status); +int float32_lt_quiet(float32, float32, float_status *status); +int float32_unordered_quiet(float32, float32, float_status *status); +int float32_compare(float32, float32, float_status *status); +int float32_compare_quiet(float32, float32, float_status *status); +float32 float32_min(float32, float32, float_status *status); +float32 float32_max(float32, float32, float_status *status); +float32 float32_minnum(float32, float32, float_status *status); +float32 float32_maxnum(float32, float32, float_status *status); +float32 float32_minnummag(float32, float32, float_status *status); +float32 float32_maxnummag(float32, float32, float_status *status); int float32_is_quiet_nan( float32 ); int float32_is_signaling_nan( float32 ); float32 float32_maybe_silence_nan( float32 ); -float32 float32_scalbn( float32, int STATUS_PARAM ); +float32 float32_scalbn(float32, int, float_status *status); static inline float32 float32_abs(float32 a) { @@ -443,55 +484,55 @@ extern const float32 float32_default_nan; /*---------------------------------------------------------------------------- | Software IEC/IEEE double-precision conversion routines. *----------------------------------------------------------------------------*/ -int_fast16_t float64_to_int16(float64 STATUS_PARAM); -uint_fast16_t float64_to_uint16(float64 STATUS_PARAM); -int_fast16_t float64_to_int16_round_to_zero(float64 STATUS_PARAM); -uint_fast16_t float64_to_uint16_round_to_zero(float64 STATUS_PARAM); -int32 float64_to_int32( float64 STATUS_PARAM ); -int32 float64_to_int32_round_to_zero( float64 STATUS_PARAM ); -uint32 float64_to_uint32( float64 STATUS_PARAM ); -uint32 float64_to_uint32_round_to_zero( float64 STATUS_PARAM ); -int64 float64_to_int64( float64 STATUS_PARAM ); -int64 float64_to_int64_round_to_zero( float64 STATUS_PARAM ); -uint64 float64_to_uint64 (float64 a STATUS_PARAM); -uint64 float64_to_uint64_round_to_zero (float64 a STATUS_PARAM); -float32 float64_to_float32( float64 STATUS_PARAM ); -floatx80 float64_to_floatx80( float64 STATUS_PARAM ); -float128 float64_to_float128( float64 STATUS_PARAM ); +int_fast16_t float64_to_int16(float64, float_status *status); +uint_fast16_t float64_to_uint16(float64, float_status *status); +int_fast16_t float64_to_int16_round_to_zero(float64, float_status *status); +uint_fast16_t float64_to_uint16_round_to_zero(float64, float_status *status); +int32 float64_to_int32(float64, float_status *status); +int32 float64_to_int32_round_to_zero(float64, float_status *status); +uint32 float64_to_uint32(float64, float_status *status); +uint32 float64_to_uint32_round_to_zero(float64, float_status *status); +int64 float64_to_int64(float64, float_status *status); +int64 float64_to_int64_round_to_zero(float64, float_status *status); +uint64 float64_to_uint64(float64 a, float_status *status); +uint64 float64_to_uint64_round_to_zero(float64 a, float_status *status); +float32 float64_to_float32(float64, float_status *status); +floatx80 float64_to_floatx80(float64, float_status *status); +float128 float64_to_float128(float64, float_status *status); /*---------------------------------------------------------------------------- | Software IEC/IEEE double-precision operations. *----------------------------------------------------------------------------*/ -float64 float64_round_to_int( float64 STATUS_PARAM ); -float64 float64_trunc_to_int( float64 STATUS_PARAM ); -float64 float64_add( float64, float64 STATUS_PARAM ); -float64 float64_sub( float64, float64 STATUS_PARAM ); -float64 float64_mul( float64, float64 STATUS_PARAM ); -float64 float64_div( float64, float64 STATUS_PARAM ); -float64 float64_rem( float64, float64 STATUS_PARAM ); -float64 float64_muladd(float64, float64, float64, int STATUS_PARAM); -float64 float64_sqrt( float64 STATUS_PARAM ); -float64 float64_log2( float64 STATUS_PARAM ); -int float64_eq( float64, float64 STATUS_PARAM ); -int float64_le( float64, float64 STATUS_PARAM ); -int float64_lt( float64, float64 STATUS_PARAM ); -int float64_unordered( float64, float64 STATUS_PARAM ); -int float64_eq_quiet( float64, float64 STATUS_PARAM ); -int float64_le_quiet( float64, float64 STATUS_PARAM ); -int float64_lt_quiet( float64, float64 STATUS_PARAM ); -int float64_unordered_quiet( float64, float64 STATUS_PARAM ); -int float64_compare( float64, float64 STATUS_PARAM ); -int float64_compare_quiet( float64, float64 STATUS_PARAM ); -float64 float64_min(float64, float64 STATUS_PARAM); -float64 float64_max(float64, float64 STATUS_PARAM); -float64 float64_minnum(float64, float64 STATUS_PARAM); -float64 float64_maxnum(float64, float64 STATUS_PARAM); -float64 float64_minnummag(float64, float64 STATUS_PARAM); -float64 float64_maxnummag(float64, float64 STATUS_PARAM); +float64 float64_round_to_int(float64, float_status *status); +float64 float64_trunc_to_int(float64, float_status *status); +float64 float64_add(float64, float64, float_status *status); +float64 float64_sub(float64, float64, float_status *status); +float64 float64_mul(float64, float64, float_status *status); +float64 float64_div(float64, float64, float_status *status); +float64 float64_rem(float64, float64, float_status *status); +float64 float64_muladd(float64, float64, float64, int, float_status *status); +float64 float64_sqrt(float64, float_status *status); +float64 float64_log2(float64, float_status *status); +int float64_eq(float64, float64, float_status *status); +int float64_le(float64, float64, float_status *status); +int float64_lt(float64, float64, float_status *status); +int float64_unordered(float64, float64, float_status *status); +int float64_eq_quiet(float64, float64, float_status *status); +int float64_le_quiet(float64, float64, float_status *status); +int float64_lt_quiet(float64, float64, float_status *status); +int float64_unordered_quiet(float64, float64, float_status *status); +int float64_compare(float64, float64, float_status *status); +int float64_compare_quiet(float64, float64, float_status *status); +float64 float64_min(float64, float64, float_status *status); +float64 float64_max(float64, float64, float_status *status); +float64 float64_minnum(float64, float64, float_status *status); +float64 float64_maxnum(float64, float64, float_status *status); +float64 float64_minnummag(float64, float64, float_status *status); +float64 float64_maxnummag(float64, float64, float_status *status); int float64_is_quiet_nan( float64 a ); int float64_is_signaling_nan( float64 ); float64 float64_maybe_silence_nan( float64 ); -float64 float64_scalbn( float64, int STATUS_PARAM ); +float64 float64_scalbn(float64, int, float_status *status); static inline float64 float64_abs(float64 a) { @@ -555,38 +596,38 @@ extern const float64 float64_default_nan; /*---------------------------------------------------------------------------- | Software IEC/IEEE extended double-precision conversion routines. *----------------------------------------------------------------------------*/ -int32 floatx80_to_int32( floatx80 STATUS_PARAM ); -int32 floatx80_to_int32_round_to_zero( floatx80 STATUS_PARAM ); -int64 floatx80_to_int64( floatx80 STATUS_PARAM ); -int64 floatx80_to_int64_round_to_zero( floatx80 STATUS_PARAM ); -float32 floatx80_to_float32( floatx80 STATUS_PARAM ); -float64 floatx80_to_float64( floatx80 STATUS_PARAM ); -float128 floatx80_to_float128( floatx80 STATUS_PARAM ); +int32 floatx80_to_int32(floatx80, float_status *status); +int32 floatx80_to_int32_round_to_zero(floatx80, float_status *status); +int64 floatx80_to_int64(floatx80, float_status *status); +int64 floatx80_to_int64_round_to_zero(floatx80, float_status *status); +float32 floatx80_to_float32(floatx80, float_status *status); +float64 floatx80_to_float64(floatx80, float_status *status); +float128 floatx80_to_float128(floatx80, float_status *status); /*---------------------------------------------------------------------------- | Software IEC/IEEE extended double-precision operations. *----------------------------------------------------------------------------*/ -floatx80 floatx80_round_to_int( floatx80 STATUS_PARAM ); -floatx80 floatx80_add( floatx80, floatx80 STATUS_PARAM ); -floatx80 floatx80_sub( floatx80, floatx80 STATUS_PARAM ); -floatx80 floatx80_mul( floatx80, floatx80 STATUS_PARAM ); -floatx80 floatx80_div( floatx80, floatx80 STATUS_PARAM ); -floatx80 floatx80_rem( floatx80, floatx80 STATUS_PARAM ); -floatx80 floatx80_sqrt( floatx80 STATUS_PARAM ); -int floatx80_eq( floatx80, floatx80 STATUS_PARAM ); -int floatx80_le( floatx80, floatx80 STATUS_PARAM ); -int floatx80_lt( floatx80, floatx80 STATUS_PARAM ); -int floatx80_unordered( floatx80, floatx80 STATUS_PARAM ); -int floatx80_eq_quiet( floatx80, floatx80 STATUS_PARAM ); -int floatx80_le_quiet( floatx80, floatx80 STATUS_PARAM ); -int floatx80_lt_quiet( floatx80, floatx80 STATUS_PARAM ); -int floatx80_unordered_quiet( floatx80, floatx80 STATUS_PARAM ); -int floatx80_compare( floatx80, floatx80 STATUS_PARAM ); -int floatx80_compare_quiet( floatx80, floatx80 STATUS_PARAM ); +floatx80 floatx80_round_to_int(floatx80, float_status *status); +floatx80 floatx80_add(floatx80, floatx80, float_status *status); +floatx80 floatx80_sub(floatx80, floatx80, float_status *status); +floatx80 floatx80_mul(floatx80, floatx80, float_status *status); +floatx80 floatx80_div(floatx80, floatx80, float_status *status); +floatx80 floatx80_rem(floatx80, floatx80, float_status *status); +floatx80 floatx80_sqrt(floatx80, float_status *status); +int floatx80_eq(floatx80, floatx80, float_status *status); +int floatx80_le(floatx80, floatx80, float_status *status); +int floatx80_lt(floatx80, floatx80, float_status *status); +int floatx80_unordered(floatx80, floatx80, float_status *status); +int floatx80_eq_quiet(floatx80, floatx80, float_status *status); +int floatx80_le_quiet(floatx80, floatx80, float_status *status); +int floatx80_lt_quiet(floatx80, floatx80, float_status *status); +int floatx80_unordered_quiet(floatx80, floatx80, float_status *status); +int floatx80_compare(floatx80, floatx80, float_status *status); +int floatx80_compare_quiet(floatx80, floatx80, float_status *status); int floatx80_is_quiet_nan( floatx80 ); int floatx80_is_signaling_nan( floatx80 ); floatx80 floatx80_maybe_silence_nan( floatx80 ); -floatx80 floatx80_scalbn( floatx80, int STATUS_PARAM ); +floatx80 floatx80_scalbn(floatx80, int, float_status *status); static inline floatx80 floatx80_abs(floatx80 a) { @@ -640,38 +681,38 @@ extern const floatx80 floatx80_default_nan; /*---------------------------------------------------------------------------- | Software IEC/IEEE quadruple-precision conversion routines. *----------------------------------------------------------------------------*/ -int32 float128_to_int32( float128 STATUS_PARAM ); -int32 float128_to_int32_round_to_zero( float128 STATUS_PARAM ); -int64 float128_to_int64( float128 STATUS_PARAM ); -int64 float128_to_int64_round_to_zero( float128 STATUS_PARAM ); -float32 float128_to_float32( float128 STATUS_PARAM ); -float64 float128_to_float64( float128 STATUS_PARAM ); -floatx80 float128_to_floatx80( float128 STATUS_PARAM ); +int32 float128_to_int32(float128, float_status *status); +int32 float128_to_int32_round_to_zero(float128, float_status *status); +int64 float128_to_int64(float128, float_status *status); +int64 float128_to_int64_round_to_zero(float128, float_status *status); +float32 float128_to_float32(float128, float_status *status); +float64 float128_to_float64(float128, float_status *status); +floatx80 float128_to_floatx80(float128, float_status *status); /*---------------------------------------------------------------------------- | Software IEC/IEEE quadruple-precision operations. *----------------------------------------------------------------------------*/ -float128 float128_round_to_int( float128 STATUS_PARAM ); -float128 float128_add( float128, float128 STATUS_PARAM ); -float128 float128_sub( float128, float128 STATUS_PARAM ); -float128 float128_mul( float128, float128 STATUS_PARAM ); -float128 float128_div( float128, float128 STATUS_PARAM ); -float128 float128_rem( float128, float128 STATUS_PARAM ); -float128 float128_sqrt( float128 STATUS_PARAM ); -int float128_eq( float128, float128 STATUS_PARAM ); -int float128_le( float128, float128 STATUS_PARAM ); -int float128_lt( float128, float128 STATUS_PARAM ); -int float128_unordered( float128, float128 STATUS_PARAM ); -int float128_eq_quiet( float128, float128 STATUS_PARAM ); -int float128_le_quiet( float128, float128 STATUS_PARAM ); -int float128_lt_quiet( float128, float128 STATUS_PARAM ); -int float128_unordered_quiet( float128, float128 STATUS_PARAM ); -int float128_compare( float128, float128 STATUS_PARAM ); -int float128_compare_quiet( float128, float128 STATUS_PARAM ); +float128 float128_round_to_int(float128, float_status *status); +float128 float128_add(float128, float128, float_status *status); +float128 float128_sub(float128, float128, float_status *status); +float128 float128_mul(float128, float128, float_status *status); +float128 float128_div(float128, float128, float_status *status); +float128 float128_rem(float128, float128, float_status *status); +float128 float128_sqrt(float128, float_status *status); +int float128_eq(float128, float128, float_status *status); +int float128_le(float128, float128, float_status *status); +int float128_lt(float128, float128, float_status *status); +int float128_unordered(float128, float128, float_status *status); +int float128_eq_quiet(float128, float128, float_status *status); +int float128_le_quiet(float128, float128, float_status *status); +int float128_lt_quiet(float128, float128, float_status *status); +int float128_unordered_quiet(float128, float128, float_status *status); +int float128_compare(float128, float128, float_status *status); +int float128_compare_quiet(float128, float128, float_status *status); int float128_is_quiet_nan( float128 ); int float128_is_signaling_nan( float128 ); float128 float128_maybe_silence_nan( float128 ); -float128 float128_scalbn( float128, int STATUS_PARAM ); +float128 float128_scalbn(float128, int, float_status *status); static inline float128 float128_abs(float128 a) { diff --git a/include/glib-compat.h b/include/glib-compat.h index f0615c9..011352b 100644 --- a/include/glib-compat.h +++ b/include/glib-compat.h @@ -32,7 +32,7 @@ static inline guint g_timeout_add_seconds(guint interval, GSourceFunc function, #endif #if !GLIB_CHECK_VERSION(2, 28, 0) -static inline gint64 g_get_monotonic_time(void) +static inline gint64 qemu_g_get_monotonic_time(void) { /* g_get_monotonic_time() is best-effort so we can use the wall clock as a * fallback. @@ -43,6 +43,8 @@ static inline gint64 g_get_monotonic_time(void) return time.tv_sec * G_TIME_SPAN_SECOND + time.tv_usec; } +/* work around distro backports of this interface */ +#define g_get_monotonic_time() qemu_g_get_monotonic_time() #endif #if !GLIB_CHECK_VERSION(2, 16, 0) diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h new file mode 100644 index 0000000..17d3beb --- /dev/null +++ b/include/hw/acpi/aml-build.h @@ -0,0 +1,191 @@ +#ifndef HW_ACPI_GEN_UTILS_H +#define HW_ACPI_GEN_UTILS_H + +#include +#include +#include "qemu/compiler.h" + +typedef enum { + AML_NO_OPCODE = 0,/* has only data */ + AML_OPCODE, /* has opcode optionally followed by data */ + AML_PACKAGE, /* has opcode and uses PkgLength for its length */ + AML_EXT_PACKAGE, /* Same as AML_PACKAGE but also has 'ExOpPrefix' */ + AML_BUFFER, /* data encoded as 'DefBuffer' */ + AML_RES_TEMPLATE, /* encoded as ResourceTemplate macro */ +} AmlBlockFlags; + +struct Aml { + GArray *buf; + + /*< private >*/ + uint8_t op; + AmlBlockFlags block_flags; +}; +typedef struct Aml Aml; + +typedef enum { + aml_decode10 = 0, + aml_decode16 = 1, +} AmlIODecode; + +typedef enum { + aml_any_acc = 0, + aml_byte_acc = 1, + aml_word_acc = 2, + aml_dword_acc = 3, + aml_qword_acc = 4, + aml_buffer_acc = 5, +} AmlFieldFlags; + +typedef enum { + aml_system_memory = 0x00, + aml_system_io = 0x01, +} AmlRegionSpace; + +typedef enum { + aml_memory_range = 0, + aml_io_range = 1, + aml_bus_number_range = 2, +} AmlResourceType; + +typedef enum { + aml_sub_decode = 1 << 1, + aml_pos_decode = 0 +} AmlDecode; + +typedef enum { + aml_max_fixed = 1 << 3, + aml_max_not_fixed = 0, +} AmlMaxFixed; + +typedef enum { + aml_min_fixed = 1 << 2, + aml_min_not_fixed = 0 +} AmlMinFixed; + +/* + * ACPI 1.0b: Table 6-26 I/O Resource Flag (Resource Type = 1) Definitions + * _RNG field definition + */ +typedef enum { + aml_isa_only = 1, + aml_non_isa_only = 2, + aml_entire_range = 3, +} AmlISARanges; + +/* + * ACPI 1.0b: Table 6-25 Memory Resource Flag (Resource Type = 0) Definitions + * _MEM field definition + */ +typedef enum { + aml_non_cacheable = 0, + aml_cacheable = 1, + aml_write_combining = 2, + aml_prefetchable = 3, +} AmlCacheble; + +/* + * ACPI 1.0b: Table 6-25 Memory Resource Flag (Resource Type = 0) Definitions + * _RW field definition + */ +typedef enum { + aml_ReadOnly = 0, + aml_ReadWrite = 1, +} AmlReadAndWrite; + +/** + * init_aml_allocator: + * + * Called for initializing API allocator which allow to use + * AML API. + * Returns: toplevel container which accumulates all other + * AML elements for a table. + */ +Aml *init_aml_allocator(void); + +/** + * free_aml_allocator: + * + * Releases all elements used by AML API, frees associated memory + * and invalidates AML allocator. After this call @init_aml_allocator + * should be called again if AML API is to be used again. + */ +void free_aml_allocator(void); + +/** + * aml_append: + * @parent_ctx: context to which @child element is added + * @child: element that is copied into @parent_ctx context + * + * Joins Aml elements together and helps to construct AML tables + * Examle of usage: + * Aml *table = aml_def_block("SSDT", ...); + * Aml *sb = aml_scope("\_SB"); + * Aml *dev = aml_device("PCI0"); + * + * aml_append(dev, aml_name_decl("HID", aml_eisaid("PNP0A03"))); + * aml_append(sb, dev); + * aml_append(table, sb); + */ +void aml_append(Aml *parent_ctx, Aml *child); + +/* non block AML object primitives */ +Aml *aml_name(const char *name_format, ...) GCC_FMT_ATTR(1, 2); +Aml *aml_name_decl(const char *name, Aml *val); +Aml *aml_return(Aml *val); +Aml *aml_int(const uint64_t val); +Aml *aml_arg(int pos); +Aml *aml_store(Aml *val, Aml *target); +Aml *aml_and(Aml *arg1, Aml *arg2); +Aml *aml_notify(Aml *arg1, Aml *arg2); +Aml *aml_call1(const char *method, Aml *arg1); +Aml *aml_call2(const char *method, Aml *arg1, Aml *arg2); +Aml *aml_call3(const char *method, Aml *arg1, Aml *arg2, Aml *arg3); +Aml *aml_call4(const char *method, Aml *arg1, Aml *arg2, Aml *arg3, Aml *arg4); +Aml *aml_io(AmlIODecode dec, uint16_t min_base, uint16_t max_base, + uint8_t aln, uint8_t len); +Aml *aml_operation_region(const char *name, AmlRegionSpace rs, + uint32_t offset, uint32_t len); +Aml *aml_irq_no_flags(uint8_t irq); +Aml *aml_named_field(const char *name, unsigned length); +Aml *aml_reserved_field(unsigned length); +Aml *aml_local(int num); +Aml *aml_string(const char *name_format, ...) GCC_FMT_ATTR(1, 2); +Aml *aml_equal(Aml *arg1, Aml *arg2); +Aml *aml_processor(uint8_t proc_id, uint32_t pblk_addr, uint8_t pblk_len, + const char *name_format, ...) GCC_FMT_ATTR(4, 5); +Aml *aml_eisaid(const char *str); +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); +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); +Aml *aml_dword_memory(AmlDecode dec, AmlMinFixed min_fixed, + AmlMaxFixed max_fixed, AmlCacheble cacheable, + AmlReadAndWrite read_and_write, + uint32_t addr_gran, uint32_t addr_min, + uint32_t addr_max, uint32_t addr_trans, + uint32_t len); +Aml *aml_qword_memory(AmlDecode dec, AmlMinFixed min_fixed, + AmlMaxFixed max_fixed, AmlCacheble cacheable, + AmlReadAndWrite read_and_write, + uint64_t addr_gran, uint64_t addr_min, + uint64_t addr_max, uint64_t addr_trans, + uint64_t len); + +/* Block AML object primitives */ +Aml *aml_scope(const char *name_format, ...) GCC_FMT_ATTR(1, 2); +Aml *aml_device(const char *name_format, ...) GCC_FMT_ATTR(1, 2); +Aml *aml_method(const char *name, int arg_count); +Aml *aml_if(Aml *predicate); +Aml *aml_package(uint8_t num_elements); +Aml *aml_buffer(void); +Aml *aml_resource_template(void); +Aml *aml_field(const char *name, AmlFieldFlags flags); +Aml *aml_varpackage(uint32_t num_elements); + +#endif diff --git a/hw/i386/bios-linker-loader.h b/include/hw/acpi/bios-linker-loader.h similarity index 100% rename from hw/i386/bios-linker-loader.h rename to include/hw/acpi/bios-linker-loader.h diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h index fe975e6..c2d3dba 100644 --- a/include/hw/acpi/ich9.h +++ b/include/hw/acpi/ich9.h @@ -49,6 +49,10 @@ typedef struct ICH9LPCPMRegs { AcpiCpuHotplug gpe_cpu; MemHotplugState acpi_memory_hotplug; + + uint8_t disable_s3; + uint8_t disable_s4; + uint8_t s4_val; } ICH9LPCPMRegs; void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, @@ -59,6 +63,10 @@ extern const VMStateDescription vmstate_ich9_pm; void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp); void ich9_pm_device_plug_cb(ICH9LPCPMRegs *pm, DeviceState *dev, Error **errp); +void ich9_pm_device_unplug_request_cb(ICH9LPCPMRegs *pm, DeviceState *dev, + Error **errp); +void ich9_pm_device_unplug_cb(ICH9LPCPMRegs *pm, DeviceState *dev, + Error **errp); void ich9_pm_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list); #endif /* HW_ACPI_ICH9_H */ diff --git a/include/hw/acpi/pc-hotplug.h b/include/hw/acpi/pc-hotplug.h index b9db295..efa6ed7 100644 --- a/include/hw/acpi/pc-hotplug.h +++ b/include/hw/acpi/pc-hotplug.h @@ -28,6 +28,7 @@ #define ICH9_CPU_HOTPLUG_IO_BASE 0x0CD8 #define PIIX4_CPU_HOTPLUG_IO_BASE 0xaf00 +#define CPU_HOTPLUG_RESOURCE_DEVICE PRES #define ACPI_MEMORY_HOTPLUG_IO_LEN 24 #define ACPI_MEMORY_HOTPLUG_BASE 0x0a00 diff --git a/include/hw/acpi/pcihp.h b/include/hw/acpi/pcihp.h index 9323838..f3526d4 100644 --- a/include/hw/acpi/pcihp.h +++ b/include/hw/acpi/pcihp.h @@ -32,6 +32,9 @@ #include "hw/acpi/acpi.h" #include "migration/vmstate.h" +#define ACPI_PCIHP_IO_BASE_PROP "acpi-pcihp-io-base" +#define ACPI_PCIHP_IO_LEN_PROP "acpi-pcihp-io-len" + typedef struct AcpiPciHpPciStatus { uint32_t up; uint32_t down; @@ -48,9 +51,11 @@ typedef struct AcpiPciHpState { PCIBus *root; MemoryRegion io; bool legacy_piix; + uint16_t io_base; + uint16_t io_len; } AcpiPciHpState; -void acpi_pcihp_init(AcpiPciHpState *, PCIBus *root, +void acpi_pcihp_init(Object *owner, AcpiPciHpState *, PCIBus *root, MemoryRegion *address_space_io, bool bridges_enabled); void acpi_pcihp_device_plug_cb(ACPIREGS *ar, qemu_irq irq, AcpiPciHpState *s, diff --git a/include/hw/arm/arm.h b/include/hw/arm/arm.h index cefc9e6..5c940eb 100644 --- a/include/hw/arm/arm.h +++ b/include/hw/arm/arm.h @@ -15,8 +15,7 @@ #include "hw/irq.h" /* armv7m.c */ -qemu_irq *armv7m_init(MemoryRegion *system_memory, - int flash_size, int sram_size, +qemu_irq *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq, const char *kernel_filename, const char *cpu_model); /* arm_boot.c */ @@ -37,6 +36,10 @@ struct arm_boot_info { hwaddr gic_cpu_if_addr; int nb_cpus; int board_id; + /* ARM machines that support the ARM Security Extensions use this field to + * control whether Linux is booted as secure(true) or non-secure(false). + */ + bool secure_boot; int (*atag_board)(const struct arm_boot_info *info, void *p); /* multicore boards that use the default secondary core boot functions * can ignore these two function calls. If the default functions won't @@ -66,6 +69,11 @@ struct arm_boot_info { hwaddr initrd_start; hwaddr initrd_size; hwaddr entry; + + /* Boot firmware has been loaded, typically at address 0, with -bios or + * -pflash. It also implies that fw_cfg_find() will succeed. + */ + bool firmware_loaded; }; void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info); diff --git a/include/hw/arm/stm32f205_soc.h b/include/hw/arm/stm32f205_soc.h new file mode 100644 index 0000000..0390eff --- /dev/null +++ b/include/hw/arm/stm32f205_soc.h @@ -0,0 +1,57 @@ +/* + * 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. + */ + +#ifndef HW_ARM_STM32F205SOC_H +#define HW_ARM_STM32F205SOC_H + +#include "hw/misc/stm32f2xx_syscfg.h" +#include "hw/timer/stm32f2xx_timer.h" +#include "hw/char/stm32f2xx_usart.h" + +#define TYPE_STM32F205_SOC "stm32f205-soc" +#define STM32F205_SOC(obj) \ + OBJECT_CHECK(STM32F205State, (obj), TYPE_STM32F205_SOC) + +#define STM_NUM_USARTS 6 +#define STM_NUM_TIMERS 4 + +#define FLASH_BASE_ADDRESS 0x08000000 +#define FLASH_SIZE (1024 * 1024) +#define SRAM_BASE_ADDRESS 0x20000000 +#define SRAM_SIZE (128 * 1024) + +typedef struct STM32F205State { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + char *kernel_filename; + char *cpu_model; + + STM32F2XXSyscfgState syscfg; + STM32F2XXUsartState usart[STM_NUM_USARTS]; + STM32F2XXTimerState timer[STM_NUM_TIMERS]; +} STM32F205State; + +#endif diff --git a/include/hw/block/block.h b/include/hw/block/block.h index 0d0ce9a..8d7c4b4 100644 --- a/include/hw/block/block.h +++ b/include/hw/block/block.h @@ -44,9 +44,9 @@ static inline unsigned int get_physical_block_exp(BlockConf *conf) #define DEFINE_BLOCK_PROPERTIES(_state, _conf) \ DEFINE_PROP_DRIVE("drive", _state, _conf.blk), \ DEFINE_PROP_BLOCKSIZE("logical_block_size", _state, \ - _conf.logical_block_size, 512), \ + _conf.logical_block_size), \ DEFINE_PROP_BLOCKSIZE("physical_block_size", _state, \ - _conf.physical_block_size, 512), \ + _conf.physical_block_size), \ DEFINE_PROP_UINT16("min_io_size", _state, _conf.min_io_size, 0), \ DEFINE_PROP_UINT32("opt_io_size", _state, _conf.opt_io_size, 0), \ DEFINE_PROP_UINT32("discard_granularity", _state, \ @@ -63,6 +63,7 @@ void blkconf_serial(BlockConf *conf, char **serial); void blkconf_geometry(BlockConf *conf, int *trans, unsigned cyls_max, unsigned heads_max, unsigned secs_max, Error **errp); +void blkconf_blocksizes(BlockConf *conf); /* Hard disk geometry */ diff --git a/include/hw/boards.h b/include/hw/boards.h index e0a6790..1f11881 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -65,6 +65,15 @@ int qemu_register_machine(QEMUMachine *m); MachineClass *find_default_machine(void); extern MachineState *current_machine; +bool machine_usb(MachineState *machine); +bool machine_iommu(MachineState *machine); +bool machine_kernel_irqchip_allowed(MachineState *machine); +bool machine_kernel_irqchip_required(MachineState *machine); +int machine_kvm_shadow_mem(MachineState *machine); +int machine_phandle_start(MachineState *machine); +bool machine_dump_guest_core(MachineState *machine); +bool machine_mem_merge(MachineState *machine); + /** * MachineClass: * @qemu_machine: #QEMUMachine @@ -73,6 +82,10 @@ extern MachineState *current_machine; * of HotplugHandler object, which handles hotplug operation * for a given @dev. It may return NULL if @dev doesn't require * any actions to be performed by hotplug handler. + * @cpu_index_to_socket_id: + * used to provide @cpu_index to socket number mapping, allowing + * a machine to group CPU threads belonging to the same socket/package + * Returns: socket number given cpu_index belongs to. */ struct MachineClass { /*< private >*/ @@ -109,6 +122,7 @@ struct MachineClass { HotplugHandler *(*get_hotplug_handler)(MachineState *machine, DeviceState *dev); + unsigned (*cpu_index_to_socket_id)(unsigned cpu_index); }; /** @@ -122,7 +136,8 @@ struct MachineState { /*< public >*/ char *accel; - bool kernel_irqchip; + bool kernel_irqchip_allowed; + bool kernel_irqchip_required; int kvm_shadow_mem; char *dtb; char *dumpdtb; @@ -131,8 +146,10 @@ struct MachineState { bool dump_guest_core; bool mem_merge; bool usb; + bool usb_disabled; char *firmware; bool iommu; + bool suppress_vmdesc; ram_addr_t ram_size; ram_addr_t maxram_size; diff --git a/include/hw/char/serial.h b/include/hw/char/serial.h index f431764..15beb6b 100644 --- a/include/hw/char/serial.h +++ b/include/hw/char/serial.h @@ -92,6 +92,6 @@ SerialState *serial_mm_init(MemoryRegion *address_space, /* serial-isa.c */ #define TYPE_ISA_SERIAL "isa-serial" -bool serial_isa_init(ISABus *bus, int index, CharDriverState *chr); +void serial_hds_isa_init(ISABus *bus, int n); #endif diff --git a/include/hw/char/stm32f2xx_usart.h b/include/hw/char/stm32f2xx_usart.h new file mode 100644 index 0000000..b97f192 --- /dev/null +++ b/include/hw/char/stm32f2xx_usart.h @@ -0,0 +1,73 @@ +/* + * 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. + */ + +#ifndef HW_STM32F2XX_USART_H +#define HW_STM32F2XX_USART_H + +#include "hw/sysbus.h" +#include "sysemu/char.h" +#include "hw/hw.h" + +#define USART_SR 0x00 +#define USART_DR 0x04 +#define USART_BRR 0x08 +#define USART_CR1 0x0C +#define USART_CR2 0x10 +#define USART_CR3 0x14 +#define USART_GTPR 0x18 + +#define USART_SR_RESET 0x00C00000 + +#define USART_SR_TXE (1 << 7) +#define USART_SR_TC (1 << 6) +#define USART_SR_RXNE (1 << 5) + +#define USART_CR1_UE (1 << 13) +#define USART_CR1_RXNEIE (1 << 5) +#define USART_CR1_TE (1 << 3) +#define USART_CR1_RE (1 << 2) + +#define TYPE_STM32F2XX_USART "stm32f2xx-usart" +#define STM32F2XX_USART(obj) \ + OBJECT_CHECK(STM32F2XXUsartState, (obj), TYPE_STM32F2XX_USART) + +typedef struct { + /* */ + SysBusDevice parent_obj; + + /* */ + MemoryRegion mmio; + + uint32_t usart_sr; + uint32_t usart_dr; + uint32_t usart_brr; + uint32_t usart_cr1; + uint32_t usart_cr2; + uint32_t usart_cr3; + uint32_t usart_gtpr; + + CharDriverState *chr; + qemu_irq irq; +} STM32F2XXUsartState; +#endif /* HW_STM32F2XX_USART_H */ diff --git a/include/hw/elf_ops.h b/include/hw/elf_ops.h index a517753..bd71968 100644 --- a/include/hw/elf_ops.h +++ b/include/hw/elf_ops.h @@ -49,6 +49,13 @@ static void glue(bswap_sym, SZ)(struct elf_sym *sym) bswap16s(&sym->st_shndx); } +static void glue(bswap_rela, SZ)(struct elf_rela *rela) +{ + bswapSZs(&rela->r_offset); + bswapSZs(&rela->r_info); + bswapSZs((elf_word *)&rela->r_addend); +} + static struct elf_shdr *glue(find_section, SZ)(struct elf_shdr *shdr_table, int n, int type) { @@ -182,6 +189,75 @@ static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab, return -1; } +static int glue(elf_reloc, SZ)(struct elfhdr *ehdr, int fd, int must_swab, + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, uint8_t *data, + struct elf_phdr *ph, int elf_machine) +{ + struct elf_shdr *reltab, *shdr_table = NULL; + struct elf_rela *rels = NULL; + int nrels, i, ret = -1; + elf_word wordval; + void *addr; + + shdr_table = load_at(fd, ehdr->e_shoff, + sizeof(struct elf_shdr) * ehdr->e_shnum); + if (!shdr_table) { + return -1; + } + if (must_swab) { + for (i = 0; i < ehdr->e_shnum; i++) { + glue(bswap_shdr, SZ)(&shdr_table[i]); + } + } + + reltab = glue(find_section, SZ)(shdr_table, ehdr->e_shnum, SHT_RELA); + if (!reltab) { + goto fail; + } + rels = load_at(fd, reltab->sh_offset, reltab->sh_size); + if (!rels) { + goto fail; + } + nrels = reltab->sh_size / sizeof(struct elf_rela); + + for (i = 0; i < nrels; i++) { + if (must_swab) { + glue(bswap_rela, SZ)(&rels[i]); + } + if (rels[i].r_offset < ph->p_vaddr || + rels[i].r_offset >= ph->p_vaddr + ph->p_filesz) { + continue; + } + addr = &data[rels[i].r_offset - ph->p_vaddr]; + switch (elf_machine) { + case EM_S390: + switch (rels[i].r_info) { + case R_390_RELATIVE: + wordval = *(elf_word *)addr; + if (must_swab) { + bswapSZs(&wordval); + } + wordval = translate_fn(translate_opaque, wordval); + if (must_swab) { + bswapSZs(&wordval); + } + *(elf_word *)addr = wordval; + break; + default: + fprintf(stderr, "Unsupported relocation type %i!\n", + (int)rels[i].r_info); + } + } + } + + ret = 0; +fail: + g_free(rels); + g_free(shdr_table); + return ret; +} + static int glue(load_elf, SZ)(const char *name, int fd, uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, @@ -239,7 +315,9 @@ static int glue(load_elf, SZ)(const char *name, int fd, glue(load_symbols, SZ)(&ehdr, fd, must_swab, clear_lsb); size = ehdr.e_phnum * sizeof(phdr[0]); - lseek(fd, ehdr.e_phoff, SEEK_SET); + if (lseek(fd, ehdr.e_phoff, SEEK_SET) != ehdr.e_phoff) { + goto fail; + } phdr = g_malloc0(size); if (!phdr) goto fail; @@ -271,6 +349,8 @@ static int glue(load_elf, SZ)(const char *name, int fd, linked at the wrong physical address. */ if (translate_fn) { addr = translate_fn(translate_opaque, ph->p_paddr); + glue(elf_reloc, SZ)(&ehdr, fd, must_swab, translate_fn, + translate_opaque, data, ph, elf_machine); } else { addr = ph->p_paddr; } diff --git a/include/hw/hotplug.h b/include/hw/hotplug.h index 050d2f0..2db025d 100644 --- a/include/hw/hotplug.h +++ b/include/hw/hotplug.h @@ -52,7 +52,7 @@ typedef void (*hotplug_fn)(HotplugHandler *plug_handler, * require asynchronous unplug handling. * @unplug: unplug callback. * Used for device removal with devices that implement - * asynchronous and synchronous (suprise) removal. + * asynchronous and synchronous (surprise) removal. */ typedef struct HotplugHandlerClass { /* */ diff --git a/include/hw/hw.h b/include/hw/hw.h index 33bdb92..c78adae 100644 --- a/include/hw/hw.h +++ b/include/hw/hw.h @@ -41,12 +41,6 @@ typedef void QEMUResetHandler(void *opaque); void qemu_register_reset(QEMUResetHandler *func, void *opaque); void qemu_unregister_reset(QEMUResetHandler *func, void *opaque); -/* handler to set the boot_device order for a specific type of QEMUMachine */ -/* return 0 if success */ -typedef int QEMUBootSetHandler(void *opaque, const char *boot_order); -void qemu_register_boot_set(QEMUBootSetHandler *func, void *opaque); -int qemu_boot_set(const char *boot_order); - #ifdef NEED_CPU_H #if TARGET_LONG_BITS == 64 #define VMSTATE_UINTTL_V(_f, _s, _v) \ diff --git a/include/hw/i386/apic.h b/include/hw/i386/apic.h index 1d48e02..51eb6d3 100644 --- a/include/hw/i386/apic.h +++ b/include/hw/i386/apic.h @@ -21,7 +21,7 @@ void apic_sipi(DeviceState *s); void apic_handle_tpr_access_report(DeviceState *d, target_ulong ip, TPRAccess access); void apic_poll_irq(DeviceState *d); -void apic_designate_bsp(DeviceState *d); +void apic_designate_bsp(DeviceState *d, bool bsp); /* pc.c */ DeviceState *cpu_get_current_apic(void); diff --git a/include/hw/i386/apic_internal.h b/include/hw/i386/apic_internal.h index 83e2a42..dc7a89d 100644 --- a/include/hw/i386/apic_internal.h +++ b/include/hw/i386/apic_internal.h @@ -89,6 +89,7 @@ typedef struct APICCommonClass void (*external_nmi)(APICCommonState *s); void (*pre_save)(APICCommonState *s); void (*post_load)(APICCommonState *s); + void (*reset)(APICCommonState *s); } APICCommonClass; struct APICCommonState { diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 69d9cf8..1b35168 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -104,26 +104,12 @@ struct PcGuestInfo { int legacy_acpi_table_size; bool has_acpi_build; bool has_reserved_memory; + bool rsdp_in_ram; }; /* parallel.c */ -static inline bool parallel_init(ISABus *bus, int index, CharDriverState *chr) -{ - DeviceState *dev; - ISADevice *isadev; - isadev = isa_try_create(bus, "isa-parallel"); - if (!isadev) { - return false; - } - dev = DEVICE(isadev); - qdev_prop_set_uint32(dev, "index", index); - qdev_prop_set_chr(dev, "chardev", chr); - if (qdev_init(dev) < 0) { - return false; - } - return true; -} +void parallel_hds_isa_init(ISABus *bus, int n); bool parallel_mm_init(MemoryRegion *address_space, hwaddr base, int it_shift, qemu_irq irq, @@ -136,8 +122,8 @@ qemu_irq *i8259_init(ISABus *bus, qemu_irq parent_irq); qemu_irq *kvm_i8259_init(ISABus *bus); int pic_read_irq(DeviceState *d); int pic_get_output(DeviceState *d); -void pic_info(Monitor *mon, const QDict *qdict); -void irq_info(Monitor *mon, const QDict *qdict); +void hmp_info_pic(Monitor *mon, const QDict *qdict); +void hmp_info_irq(Monitor *mon, const QDict *qdict); /* Global System Interrupts */ diff --git a/target-i386/topology.h b/include/hw/i386/topology.h similarity index 97% rename from target-i386/topology.h rename to include/hw/i386/topology.h index 07a6c5f..9c6f3a9 100644 --- a/target-i386/topology.h +++ b/include/hw/i386/topology.h @@ -21,8 +21,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#ifndef TARGET_I386_TOPOLOGY_H -#define TARGET_I386_TOPOLOGY_H +#ifndef HW_I386_TOPOLOGY_H +#define HW_I386_TOPOLOGY_H /* This file implements the APIC-ID-based CPU topology enumeration logic, * documented at the following document: @@ -131,4 +131,4 @@ static inline apic_id_t x86_apicid_from_cpu_idx(unsigned nr_cores, return apicid_from_topo_ids(nr_cores, nr_threads, pkg_id, core_id, smt_id); } -#endif /* TARGET_I386_TOPOLOGY_H */ +#endif /* HW_I386_TOPOLOGY_H */ diff --git a/include/hw/isa/isa.h b/include/hw/isa/isa.h index e0c749f..f21ceaa 100644 --- a/include/hw/isa/isa.h +++ b/include/hw/isa/isa.h @@ -21,10 +21,17 @@ #define ISA_BUS(obj) OBJECT_CHECK(ISABus, (obj), TYPE_ISA_BUS) #define TYPE_APPLE_SMC "isa-applesmc" +#define APPLESMC_MAX_DATA_LENGTH 32 +#define APPLESMC_PROP_IO_BASE "iobase" -static inline bool applesmc_find(void) +static inline uint16_t applesmc_port(void) { - return object_resolve_path_type("", TYPE_APPLE_SMC, NULL); + Object *obj = object_resolve_path_type("", TYPE_APPLE_SMC, NULL); + + if (obj) { + return object_property_get_int(obj, APPLESMC_PROP_IO_BASE, NULL); + } + return 0; } typedef struct ISADeviceClass { @@ -36,6 +43,7 @@ struct ISABus { BusState parent_obj; /*< public >*/ + MemoryRegion *address_space; MemoryRegion *address_space_io; qemu_irq *irqs; }; @@ -50,7 +58,8 @@ struct ISADevice { int ioport_id; }; -ISABus *isa_bus_new(DeviceState *dev, MemoryRegion *address_space_io); +ISABus *isa_bus_new(DeviceState *dev, MemoryRegion *address_space, + MemoryRegion *address_space_io); void isa_bus_irqs(ISABus *bus, qemu_irq *irqs); qemu_irq isa_get_irq(ISADevice *dev, int isairq); void isa_init_irq(ISADevice *dev, qemu_irq *p, int isairq); @@ -97,8 +106,6 @@ static inline ISABus *isa_bus_from_device(ISADevice *d) return ISA_BUS(qdev_get_parent_bus(DEVICE(d))); } -extern hwaddr isa_mem_base; - /* dma.c */ int DMA_get_channel_mode (int nchan); int DMA_read_memory (int nchan, void *buf, int pos, int size); diff --git a/include/hw/lm32/lm32_pic.h b/include/hw/lm32/lm32_pic.h index 5556803..189fa38 100644 --- a/include/hw/lm32/lm32_pic.h +++ b/include/hw/lm32/lm32_pic.h @@ -8,7 +8,7 @@ uint32_t lm32_pic_get_im(DeviceState *d); void lm32_pic_set_ip(DeviceState *d, uint32_t ip); void lm32_pic_set_im(DeviceState *d, uint32_t im); -void lm32_do_pic_info(Monitor *mon, const QDict *qdict); -void lm32_irq_info(Monitor *mon, const QDict *qdict); +void lm32_hmp_info_pic(Monitor *mon, const QDict *qdict); +void lm32_hmp_info_irq(Monitor *mon, const QDict *qdict); #endif /* QEMU_HW_LM32_PIC_H */ diff --git a/include/hw/loader.h b/include/hw/loader.h index 6481639..4f0681b 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -16,6 +16,15 @@ int load_image(const char *filename, uint8_t *addr); /* deprecated */ ssize_t load_image_size(const char *filename, void *addr, size_t size); int load_image_targphys(const char *filename, hwaddr, uint64_t max_sz); + +/* This is the limit on the maximum uncompressed image size that + * load_image_gzipped_buffer() and load_image_gzipped() will read. It prevents + * g_malloc() in those functions from allocating a huge amount of memory. + */ +#define LOAD_IMAGE_MAX_GUNZIP_BYTES (256 << 20) + +int load_image_gzipped_buffer(const char *filename, uint64_t max_sz, + uint8_t **buffer); int load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz); #define ELF_LOAD_FAILED -1 @@ -60,7 +69,7 @@ int rom_add_file(const char *file, const char *fw_dir, hwaddr addr, int32_t bootindex, bool option_rom); ram_addr_t rom_add_blob(const char *name, const void *blob, size_t len, - hwaddr addr, const char *fw_file_name, + size_t max_len, hwaddr addr, const char *fw_file_name, FWCfgReadCallback fw_callback, void *callback_opaque); int rom_add_elf_program(const char *name, void *data, size_t datasize, size_t romsize, hwaddr addr); @@ -69,12 +78,12 @@ void rom_load_done(void); void rom_set_fw(FWCfgState *f); int rom_copy(uint8_t *dest, hwaddr addr, size_t size); void *rom_ptr(hwaddr addr); -void do_info_roms(Monitor *mon, const QDict *qdict); +void hmp_info_roms(Monitor *mon, const QDict *qdict); #define rom_add_file_fixed(_f, _a, _i) \ rom_add_file(_f, NULL, _a, _i, false) #define rom_add_blob_fixed(_f, _b, _l, _a) \ - rom_add_blob(_f, _b, _l, _a, NULL, NULL, NULL) + rom_add_blob(_f, _b, _l, _l, _a, NULL, NULL, NULL) #define PC_ROM_MIN_VGA 0xc0000 #define PC_ROM_MIN_OPTION 0xc8000 diff --git a/include/hw/mem/pc-dimm.h b/include/hw/mem/pc-dimm.h index e1dcbbc..f7b80b4 100644 --- a/include/hw/mem/pc-dimm.h +++ b/include/hw/mem/pc-dimm.h @@ -78,4 +78,5 @@ uint64_t pc_dimm_get_free_addr(uint64_t address_space_start, int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp); int qmp_pc_dimm_device_list(Object *obj, void *opaque); +uint64_t pc_existing_dimms_capacity(Error **errp); #endif diff --git a/include/hw/misc/stm32f2xx_syscfg.h b/include/hw/misc/stm32f2xx_syscfg.h new file mode 100644 index 0000000..69e6a30 --- /dev/null +++ b/include/hw/misc/stm32f2xx_syscfg.h @@ -0,0 +1,61 @@ +/* + * 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. + */ + +#ifndef HW_STM32F2XX_SYSCFG_H +#define HW_STM32F2XX_SYSCFG_H + +#include "hw/sysbus.h" +#include "hw/hw.h" + +#define SYSCFG_MEMRMP 0x00 +#define SYSCFG_PMC 0x04 +#define SYSCFG_EXTICR1 0x08 +#define SYSCFG_EXTICR2 0x0C +#define SYSCFG_EXTICR3 0x10 +#define SYSCFG_EXTICR4 0x14 +#define SYSCFG_CMPCR 0x20 + +#define TYPE_STM32F2XX_SYSCFG "stm32f2xx-syscfg" +#define STM32F2XX_SYSCFG(obj) \ + OBJECT_CHECK(STM32F2XXSyscfgState, (obj), TYPE_STM32F2XX_SYSCFG) + +typedef struct { + /* */ + SysBusDevice parent_obj; + + /* */ + MemoryRegion mmio; + + uint32_t syscfg_memrmp; + uint32_t syscfg_pmc; + uint32_t syscfg_exticr1; + uint32_t syscfg_exticr2; + uint32_t syscfg_exticr3; + uint32_t syscfg_exticr4; + uint32_t syscfg_cmpcr; + + qemu_irq irq; +} STM32F2XXSyscfgState; + +#endif /* HW_STM32F2XX_SYSCFG_H */ diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h index 56e1ed7..6d8a8ac 100644 --- a/include/hw/nvram/fw_cfg.h +++ b/include/hw/nvram/fw_cfg.h @@ -78,8 +78,10 @@ void fw_cfg_add_file_callback(FWCfgState *s, const char *filename, void *data, size_t len); void *fw_cfg_modify_file(FWCfgState *s, const char *filename, void *data, size_t len); -FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port, - hwaddr crl_addr, hwaddr data_addr); +FWCfgState *fw_cfg_init_io(uint32_t iobase); +FWCfgState *fw_cfg_init_mem(hwaddr ctl_addr, hwaddr data_addr); +FWCfgState *fw_cfg_init_mem_wide(hwaddr ctl_addr, hwaddr data_addr, + uint32_t data_width); FWCfgState *fw_cfg_find(void); diff --git a/include/hw/pci-host/gpex.h b/include/hw/pci-host/gpex.h new file mode 100644 index 0000000..68c9348 --- /dev/null +++ b/include/hw/pci-host/gpex.h @@ -0,0 +1,56 @@ +/* + * QEMU Generic PCI Express Bridge Emulation + * + * Copyright (C) 2015 Alexander Graf + * + * 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 Lesser General Public + * License along with this library; if not, see + */ + +#ifndef HW_GPEX_H +#define HW_GPEX_H + +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "hw/pci/pci.h" +#include "hw/pci/pcie_host.h" + +#define TYPE_GPEX_HOST "gpex-pcihost" +#define GPEX_HOST(obj) \ + OBJECT_CHECK(GPEXHost, (obj), TYPE_GPEX_HOST) + +#define TYPE_GPEX_ROOT_DEVICE "gpex-root" +#define MCH_PCI_DEVICE(obj) \ + OBJECT_CHECK(GPEXRootState, (obj), TYPE_GPEX_ROOT_DEVICE) + +#define GPEX_NUM_IRQS 4 + +typedef struct GPEXRootState { + /*< private >*/ + PCIDevice parent_obj; + /*< public >*/ +} GPEXRootState; + +typedef struct GPEXHost { + /*< private >*/ + PCIExpressHost parent_obj; + /*< public >*/ + + GPEXRootState gpex_root; + + MemoryRegion io_ioport; + MemoryRegion io_mmio; + qemu_irq irq[GPEX_NUM_IRQS]; +} GPEXHost; + +#endif /* HW_GPEX_H */ diff --git a/include/hw/pci-host/spapr.h b/include/hw/pci-host/spapr.h index 4ea2a0d..895d273 100644 --- a/include/hw/pci-host/spapr.h +++ b/include/hw/pci-host/spapr.h @@ -49,6 +49,10 @@ struct sPAPRPHBClass { PCIHostBridgeClass parent_class; void (*finish_realize)(sPAPRPHBState *sphb, Error **errp); + int (*eeh_set_option)(sPAPRPHBState *sphb, unsigned int addr, int option); + int (*eeh_get_state)(sPAPRPHBState *sphb, int *state); + int (*eeh_reset)(sPAPRPHBState *sphb, int option); + int (*eeh_configure)(sPAPRPHBState *sphb); }; typedef struct spapr_pci_msi { @@ -64,7 +68,7 @@ typedef struct spapr_pci_msi_mig { struct sPAPRPHBState { PCIHostState parent_obj; - int32_t index; + uint32_t index; uint64_t buid; char *dtbusname; @@ -94,19 +98,22 @@ struct sPAPRPHBVFIOState { int32_t iommugroupid; }; +#define SPAPR_PCI_MAX_INDEX 255 + #define SPAPR_PCI_BASE_BUID 0x800000020000000ULL +#define SPAPR_PCI_MEM_WIN_BUS_OFFSET 0x80000000ULL + #define SPAPR_PCI_WINDOW_BASE 0x10000000000ULL #define SPAPR_PCI_WINDOW_SPACING 0x1000000000ULL #define SPAPR_PCI_MMIO_WIN_OFF 0xA0000000 -#define SPAPR_PCI_MMIO_WIN_SIZE 0x20000000 +#define SPAPR_PCI_MMIO_WIN_SIZE (SPAPR_PCI_WINDOW_SPACING - \ + SPAPR_PCI_MEM_WIN_BUS_OFFSET) #define SPAPR_PCI_IO_WIN_OFF 0x80000000 #define SPAPR_PCI_IO_WIN_SIZE 0x10000 #define SPAPR_PCI_MSI_WINDOW 0x40000000000ULL -#define SPAPR_PCI_MEM_WIN_BUS_OFFSET 0x80000000ULL - static inline qemu_irq spapr_phb_lsi_qirq(struct sPAPRPHBState *phb, int pin) { return xics_get_qirq(spapr->icp, phb->lsi_table[pin].irq); diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index c352c7b..b97c295 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -88,6 +88,8 @@ #define PCI_DEVICE_ID_REDHAT_SERIAL2 0x0003 #define PCI_DEVICE_ID_REDHAT_SERIAL4 0x0004 #define PCI_DEVICE_ID_REDHAT_TEST 0x0005 +#define PCI_DEVICE_ID_REDHAT_SDHCI 0x0007 +#define PCI_DEVICE_ID_REDHAT_PCIE_HOST 0x0008 #define PCI_DEVICE_ID_REDHAT_QXL 0x0100 #define FMT_PCIBUS PRIx64 @@ -135,7 +137,7 @@ enum { #define PCI_CONFIG_HEADER_SIZE 0x40 /* Size of the standard PCI config space */ #define PCI_CONFIG_SPACE_SIZE 0x100 -/* Size of the standart PCIe config space: 4KB */ +/* Size of the standard PCIe config space: 4KB */ #define PCIE_CONFIG_SPACE_SIZE 0x1000 #define PCI_NUM_PINS 4 /* A-D */ @@ -183,7 +185,8 @@ typedef struct PCIINTxRoute { typedef struct PCIDeviceClass { DeviceClass parent_class; - int (*init)(PCIDevice *dev); + void (*realize)(PCIDevice *dev, Error **errp); + int (*init)(PCIDevice *dev);/* TODO convert to realize() and remove */ PCIUnregisterFunc *exit; PCIConfigReadFunc *config_read; PCIConfigWriteFunc *config_write; @@ -368,9 +371,6 @@ void pci_device_set_intx_routing_notifier(PCIDevice *dev, PCIINTxRoutingNotifier notifier); void pci_device_reset(PCIDevice *dev); -PCIDevice *pci_nic_init(NICInfo *nd, PCIBus *rootbus, - const char *default_model, - const char *default_devaddr); PCIDevice *pci_nic_init_nofail(NICInfo *nd, PCIBus *rootbus, const char *default_model, const char *default_devaddr); @@ -400,12 +400,8 @@ PCIBus *pci_device_root_bus(const PCIDevice *d); const char *pci_root_bus_path(PCIDevice *dev); PCIDevice *pci_find_device(PCIBus *bus, int bus_num, uint8_t devfn); int pci_qdev_find_device(const char *id, PCIDevice **pdev); -PCIBus *pci_get_bus_devfn(int *devfnp, PCIBus *root, const char *devaddr); void pci_bus_get_w64_range(PCIBus *bus, Range *range); -int pci_parse_devaddr(const char *addr, int *domp, int *busp, - unsigned int *slotp, unsigned int *funcp); - void pci_device_deassert_intx(PCIDevice *dev); typedef AddressSpace *(*PCIIOMMUFunc)(PCIBus *, void *, int); diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h index 321d622..d7be386 100644 --- a/include/hw/pci/pci_ids.h +++ b/include/hw/pci/pci_ids.h @@ -31,6 +31,7 @@ #define PCI_CLASS_MEMORY_RAM 0x0500 +#define PCI_CLASS_SYSTEM_SDHCI 0x0805 #define PCI_CLASS_SYSTEM_OTHER 0x0880 #define PCI_CLASS_SERIAL_USB 0x0c03 diff --git a/include/hw/pci/pcie_aer.h b/include/hw/pci/pcie_aer.h index bcac80a..2fb8388 100644 --- a/include/hw/pci/pcie_aer.h +++ b/include/hw/pci/pcie_aer.h @@ -51,7 +51,7 @@ struct PCIEAERLog { PCIEAERErr *log; }; -/* aer error message: error signaling message has only error sevirity and +/* aer error message: error signaling message has only error severity and source id. See 2.2.8.3 error signaling messages */ struct PCIEAERMsg { /* diff --git a/include/hw/pci/pcie_host.h b/include/hw/pci/pcie_host.h index ff44ef6..4d23c80 100644 --- a/include/hw/pci/pcie_host.h +++ b/include/hw/pci/pcie_host.h @@ -50,6 +50,7 @@ struct PCIExpressHost { }; void pcie_host_mmcfg_unmap(PCIExpressHost *e); +void pcie_host_mmcfg_init(PCIExpressHost *e, uint32_t size); void pcie_host_mmcfg_map(PCIExpressHost *e, hwaddr addr, uint32_t size); void pcie_host_mmcfg_update(PCIExpressHost *e, int enable, diff --git a/include/hw/pci/pcie_regs.h b/include/hw/pci/pcie_regs.h index 652d9fc..848ab1c 100644 --- a/include/hw/pci/pcie_regs.h +++ b/include/hw/pci/pcie_regs.h @@ -72,7 +72,7 @@ #define PCI_EXP_DEVCAP2_EFF 0x100000 #define PCI_EXP_DEVCAP2_EETLPP 0x200000 -#define PCI_EXP_DEVCTL2_EETLPPB 0x80 +#define PCI_EXP_DEVCTL2_EETLPPB 0x8000 /* ARI */ #define PCI_ARI_VER 1 diff --git a/include/hw/pci/shpc.h b/include/hw/pci/shpc.h index 025bc5b..9bbea39 100644 --- a/include/hw/pci/shpc.h +++ b/include/hw/pci/shpc.h @@ -41,6 +41,7 @@ void shpc_reset(PCIDevice *d); int shpc_bar_size(PCIDevice *dev); int shpc_init(PCIDevice *dev, PCIBus *sec_bus, MemoryRegion *bar, unsigned off); void shpc_cleanup(PCIDevice *dev, MemoryRegion *bar); +void shpc_free(PCIDevice *dev); void shpc_cap_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int len); diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 749daf4..af71e8b 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -15,6 +15,7 @@ typedef struct sPAPREnvironment { QLIST_HEAD(, sPAPRPHBState) phbs; struct sPAPRNVRAM *nvram; XICSState *icp; + DeviceState *rtc; hwaddr ram_limit; void *htab; @@ -26,7 +27,7 @@ typedef struct sPAPREnvironment { void *rtas_blob; void *fdt_skel; target_ulong entry_point; - uint64_t rtc_offset; + uint64_t rtc_offset; /* Now used only during incoming migration */ struct PPCTimebase tb; bool has_graphics; @@ -37,6 +38,7 @@ typedef struct sPAPREnvironment { int htab_save_index; bool htab_first_pass; int htab_fd; + bool htab_fd_stale; } sPAPREnvironment; #define H_SUCCESS 0 @@ -337,6 +339,39 @@ target_ulong spapr_hypercall(PowerPCCPU *cpu, target_ulong opcode, int spapr_allocate_irq(int hint, bool lsi); int spapr_allocate_irq_block(int num, bool lsi, bool msi); +/* ibm,set-eeh-option */ +#define RTAS_EEH_DISABLE 0 +#define RTAS_EEH_ENABLE 1 +#define RTAS_EEH_THAW_IO 2 +#define RTAS_EEH_THAW_DMA 3 + +/* ibm,get-config-addr-info2 */ +#define RTAS_GET_PE_ADDR 0 +#define RTAS_GET_PE_MODE 1 +#define RTAS_PE_MODE_NONE 0 +#define RTAS_PE_MODE_NOT_SHARED 1 +#define RTAS_PE_MODE_SHARED 2 + +/* ibm,read-slot-reset-state2 */ +#define RTAS_EEH_PE_STATE_NORMAL 0 +#define RTAS_EEH_PE_STATE_RESET 1 +#define RTAS_EEH_PE_STATE_STOPPED_IO_DMA 2 +#define RTAS_EEH_PE_STATE_STOPPED_DMA 4 +#define RTAS_EEH_PE_STATE_UNAVAIL 5 +#define RTAS_EEH_NOT_SUPPORT 0 +#define RTAS_EEH_SUPPORT 1 +#define RTAS_EEH_PE_UNAVAIL_INFO 1000 +#define RTAS_EEH_PE_RECOVER_INFO 0 + +/* ibm,set-slot-reset */ +#define RTAS_SLOT_RESET_DEACTIVATE 0 +#define RTAS_SLOT_RESET_HOT 1 +#define RTAS_SLOT_RESET_FUNDAMENTAL 3 + +/* ibm,slot-error-detail */ +#define RTAS_SLOT_TEMP_ERR_LOG 1 +#define RTAS_SLOT_PERM_ERR_LOG 2 + /* RTAS return codes */ #define RTAS_OUT_SUCCESS 0 #define RTAS_OUT_NO_ERRORS_FOUND 1 @@ -381,8 +416,14 @@ int spapr_allocate_irq_block(int num, bool lsi, bool msi); #define RTAS_GET_SENSOR_STATE (RTAS_TOKEN_BASE + 0x1D) #define RTAS_IBM_CONFIGURE_CONNECTOR (RTAS_TOKEN_BASE + 0x1E) #define RTAS_IBM_OS_TERM (RTAS_TOKEN_BASE + 0x1F) +#define RTAS_IBM_SET_EEH_OPTION (RTAS_TOKEN_BASE + 0x20) +#define RTAS_IBM_GET_CONFIG_ADDR_INFO2 (RTAS_TOKEN_BASE + 0x21) +#define RTAS_IBM_READ_SLOT_RESET_STATE2 (RTAS_TOKEN_BASE + 0x22) +#define RTAS_IBM_SET_SLOT_RESET (RTAS_TOKEN_BASE + 0x23) +#define RTAS_IBM_CONFIGURE_PE (RTAS_TOKEN_BASE + 0x24) +#define RTAS_IBM_SLOT_ERROR_DETAIL (RTAS_TOKEN_BASE + 0x25) -#define RTAS_TOKEN_MAX (RTAS_TOKEN_BASE + 0x20) +#define RTAS_TOKEN_MAX (RTAS_TOKEN_BASE + 0x26) /* RTAS ibm,get-system-parameter token values */ #define RTAS_SYSPARM_SPLPAR_CHARACTERISTICS 20 @@ -462,6 +503,7 @@ struct sPAPRTCETable { bool vfio_accel; int fd; MemoryRegion iommu; + struct VIOsPAPRDevice *vdev; /* for @bypass migration compatibility only */ QLIST_ENTRY(sPAPRTCETable) list; }; @@ -474,10 +516,15 @@ sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn, uint32_t nb_table, bool vfio_accel); MemoryRegion *spapr_tce_get_iommu(sPAPRTCETable *tcet); -void spapr_tce_set_bypass(sPAPRTCETable *tcet, bool bypass); int spapr_dma_dt(void *fdt, int node_off, const char *propname, uint32_t liobn, uint64_t window, uint32_t size); int spapr_tcet_dma_dt(void *fdt, int node_off, const char *propname, sPAPRTCETable *tcet); +void spapr_pci_switch_vga(bool big_endian); + +#define TYPE_SPAPR_RTC "spapr-rtc" + +void spapr_rtc_read(DeviceState *dev, struct tm *tm, uint32_t *ns); +int spapr_rtc_import_offset(DeviceState *dev, int64_t legacy_offset); #endif /* !defined (__HW_SPAPR_H__) */ diff --git a/include/hw/ppc/spapr_vio.h b/include/hw/ppc/spapr_vio.h index 46edc2a..f95016a 100644 --- a/include/hw/ppc/spapr_vio.h +++ b/include/hw/ppc/spapr_vio.h @@ -52,7 +52,7 @@ typedef struct VIOsPAPRDeviceClass { const char *dt_name, *dt_type, *dt_compatible; target_ulong signal_mask; uint32_t rtce_window_size; - int (*init)(VIOsPAPRDevice *dev); + void (*realize)(VIOsPAPRDevice *dev, Error **errp); void (*reset)(VIOsPAPRDevice *dev); int (*devnode)(VIOsPAPRDevice *dev, void *fdt, int node_off); } VIOsPAPRDeviceClass; @@ -64,6 +64,8 @@ struct VIOsPAPRDevice { target_ulong signal_state; VIOsPAPR_CRQ crq; AddressSpace as; + MemoryRegion mrroot; + MemoryRegion mrbypass; sPAPRTCETable *tcet; }; @@ -139,4 +141,6 @@ extern const VMStateDescription vmstate_spapr_vio; #define VMSTATE_SPAPR_VIO(_f, _s) \ VMSTATE_STRUCT(_f, _s, 0, vmstate_spapr_vio, VIOsPAPRDevice) +void spapr_vio_set_bypass(VIOsPAPRDevice *dev, bool bypass); + #endif /* _HW_SPAPR_VIO_H */ diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 589bbe7..4e673f9 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -165,6 +165,12 @@ struct DeviceState { int alias_required_for_version; }; +struct DeviceListener { + void (*realize)(DeviceListener *listener, DeviceState *dev); + void (*unrealize)(DeviceListener *listener, DeviceState *dev); + QTAILQ_ENTRY(DeviceListener) link; +}; + #define TYPE_BUS "bus" #define BUS(obj) OBJECT_CHECK(BusState, (obj), TYPE_BUS) #define BUS_CLASS(klass) OBJECT_CLASS_CHECK(BusClass, (klass), TYPE_BUS) @@ -336,6 +342,7 @@ void qbus_reset_all_fn(void *opaque); BusState *sysbus_get_default(void); char *qdev_get_fw_dev_path(DeviceState *dev); +char *qdev_get_own_fw_dev_path_from_handler(BusState *bus, DeviceState *dev); /** * @qdev_machine_init @@ -376,4 +383,8 @@ static inline bool qbus_is_hotpluggable(BusState *bus) { return bus->hotplug_handler; } + +void device_listener_register(DeviceListener *listener); +void device_listener_unregister(DeviceListener *listener); + #endif diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index 070006c..d67dad5 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -149,8 +149,8 @@ extern PropertyInfo qdev_prop_arraylen; LostTickPolicy) #define DEFINE_PROP_BIOS_CHS_TRANS(_n, _s, _f, _d) \ DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_bios_chs_trans, int) -#define DEFINE_PROP_BLOCKSIZE(_n, _s, _f, _d) \ - DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_blocksize, uint16_t) +#define DEFINE_PROP_BLOCKSIZE(_n, _s, _f) \ + DEFINE_PROP_DEFAULT(_n, _s, _f, 0, qdev_prop_blocksize, uint16_t) #define DEFINE_PROP_PCI_HOST_DEVADDR(_n, _s, _f) \ DEFINE_PROP(_n, _s, _f, qdev_prop_pci_host_devaddr, PCIHostDeviceAddress) @@ -168,8 +168,8 @@ void qdev_prop_set_uint64(DeviceState *dev, const char *name, uint64_t value); void qdev_prop_set_string(DeviceState *dev, const char *name, const char *value); void qdev_prop_set_chr(DeviceState *dev, const char *name, CharDriverState *value); void qdev_prop_set_netdev(DeviceState *dev, const char *name, NetClientState *value); -int qdev_prop_set_drive(DeviceState *dev, const char *name, - BlockBackend *value) QEMU_WARN_UNUSED_RESULT; +void qdev_prop_set_drive(DeviceState *dev, const char *name, + BlockBackend *value, Error **errp); void qdev_prop_set_drive_nofail(DeviceState *dev, const char *name, BlockBackend *value); void qdev_prop_set_macaddr(DeviceState *dev, const char *name, uint8_t *value); @@ -180,9 +180,7 @@ void qdev_prop_set_ptr(DeviceState *dev, const char *name, void *value); void qdev_prop_register_global(GlobalProperty *prop); void qdev_prop_register_global_list(GlobalProperty *props); int qdev_prop_check_globals(void); -void qdev_prop_set_globals(DeviceState *dev, Error **errp); -void qdev_prop_set_globals_for_type(DeviceState *dev, const char *typename, - Error **errp); +void qdev_prop_set_globals(DeviceState *dev); void error_set_from_qdev_prop_error(Error **errp, int ret, DeviceState *dev, Property *prop, const char *value); diff --git a/include/hw/s390x/sclp.h b/include/hw/s390x/sclp.h index ec07a11..e8a64e2 100644 --- a/include/hw/s390x/sclp.h +++ b/include/hw/s390x/sclp.h @@ -43,14 +43,22 @@ #define SCLP_CMDW_CONFIGURE_CPU 0x00110001 #define SCLP_CMDW_DECONFIGURE_CPU 0x00100001 +/* SCLP PCI codes */ +#define SCLP_HAS_PCI_RECONFIG 0x0000000040000000ULL +#define SCLP_CMDW_CONFIGURE_PCI 0x001a0001 +#define SCLP_CMDW_DECONFIGURE_PCI 0x001b0001 +#define SCLP_RECONFIG_PCI_ATPYE 2 + /* SCLP response codes */ #define SCLP_RC_NORMAL_READ_COMPLETION 0x0010 #define SCLP_RC_NORMAL_COMPLETION 0x0020 #define SCLP_RC_SCCB_BOUNDARY_VIOLATION 0x0100 +#define SCLP_RC_NO_ACTION_REQUIRED 0x0120 #define SCLP_RC_INVALID_SCLP_COMMAND 0x01f0 #define SCLP_RC_CONTAINED_EQUIPMENT_CHECK 0x0340 #define SCLP_RC_INSUFFICIENT_SCCB_LENGTH 0x0300 #define SCLP_RC_STANDBY_READ_COMPLETION 0x0410 +#define SCLP_RC_ADAPTER_ID_NOT_RECOGNIZED 0x09f0 #define SCLP_RC_INVALID_FUNCTION 0x40f0 #define SCLP_RC_NO_EVENT_BUFFERS_STORED 0x60f0 #define SCLP_RC_INVALID_SELECTION_MASK 0x70f0 diff --git a/include/hw/sparc/grlib.h b/include/hw/sparc/grlib.h index 470ce72..9a0db7b 100644 --- a/include/hw/sparc/grlib.h +++ b/include/hw/sparc/grlib.h @@ -55,9 +55,7 @@ DeviceState *grlib_irqmp_create(hwaddr base, qdev_prop_set_ptr(dev, "set_pil_in", set_pil_in); qdev_prop_set_ptr(dev, "set_pil_in_opaque", env); - if (qdev_init(dev)) { - return NULL; - } + qdev_init_nofail(dev); env->irq_manager = dev; @@ -87,9 +85,7 @@ DeviceState *grlib_gptimer_create(hwaddr base, qdev_prop_set_uint32(dev, "frequency", freq); qdev_prop_set_uint32(dev, "irq-line", base_irq); - if (qdev_init(dev)) { - return NULL; - } + qdev_init_nofail(dev); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); @@ -112,9 +108,7 @@ DeviceState *grlib_apbuart_create(hwaddr base, dev = qdev_create(NULL, "grlib,apbuart"); qdev_prop_set_chr(dev, "chrdev", serial); - if (qdev_init(dev)) { - return NULL; - } + qdev_init_nofail(dev); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); diff --git a/include/hw/sparc/sun4m.h b/include/hw/sparc/sun4m.h index a587700..9c17425 100644 --- a/include/hw/sparc/sun4m.h +++ b/include/hw/sparc/sun4m.h @@ -29,8 +29,8 @@ void slavio_pic_info(Monitor *mon, DeviceState *dev); void slavio_irq_info(Monitor *mon, DeviceState *dev); /* sun4m.c */ -void sun4m_pic_info(Monitor *mon, const QDict *qdict); -void sun4m_irq_info(Monitor *mon, const QDict *qdict); +void sun4m_hmp_info_pic(Monitor *mon, const QDict *qdict); +void sun4m_hmp_info_irq(Monitor *mon, const QDict *qdict); /* sparc32_dma.c */ #include "hw/sparc/sparc32_dma.h" diff --git a/include/hw/timer/m48t59.h b/include/hw/timer/m48t59.h index 8217522..3367923 100644 --- a/include/hw/timer/m48t59.h +++ b/include/hw/timer/m48t59.h @@ -1,37 +1,34 @@ #ifndef NVRAM_H #define NVRAM_H -/* NVRAM helpers */ -typedef uint32_t (*nvram_read_t)(void *private, uint32_t addr); -typedef void (*nvram_write_t)(void *private, uint32_t addr, uint32_t val); -typedef struct nvram_t { - void *opaque; - nvram_read_t read_fn; - nvram_write_t write_fn; -} nvram_t; - -uint32_t NVRAM_get_lword (nvram_t *nvram, uint32_t addr); -int NVRAM_get_string (nvram_t *nvram, uint8_t *dst, uint16_t addr, int max); - -int PPC_NVRAM_set_params (nvram_t *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); - -#define TYPE_SYSBUS_M48T59 "m48t59" - -typedef struct M48t59State M48t59State; - -void m48t59_write (void *private, uint32_t addr, uint32_t val); -uint32_t m48t59_read (void *private, uint32_t addr); -void m48t59_toggle_lock (void *private, int lock); -M48t59State *m48t59_init_isa(ISABus *bus, uint32_t io_base, uint16_t size, - int type); -M48t59State *m48t59_init(qemu_irq IRQ, hwaddr mem_base, - uint32_t io_base, uint16_t size, int type); +#include "qemu-common.h" +#include "qom/object.h" + +#define TYPE_NVRAM "nvram" + +#define NVRAM_CLASS(klass) \ + OBJECT_CLASS_CHECK(NvramClass, (klass), TYPE_NVRAM) +#define NVRAM_GET_CLASS(obj) \ + OBJECT_GET_CLASS(NvramClass, (obj), TYPE_NVRAM) +#define NVRAM(obj) \ + INTERFACE_CHECK(Nvram, (obj), TYPE_NVRAM) + +typedef struct Nvram { + Object parent; +} Nvram; + +typedef struct NvramClass { + InterfaceClass parent; + + uint32_t (*read)(Nvram *obj, uint32_t addr); + void (*write)(Nvram *obj, uint32_t addr, uint32_t val); + void (*toggle_lock)(Nvram *obj, int lock); +} NvramClass; + +Nvram *m48t59_init_isa(ISABus *bus, uint32_t io_base, uint16_t size, + int base_year, int type); +Nvram *m48t59_init(qemu_irq IRQ, hwaddr mem_base, + uint32_t io_base, uint16_t size, int base_year, + int type); #endif /* !NVRAM_H */ diff --git a/include/hw/timer/stm32f2xx_timer.h b/include/hw/timer/stm32f2xx_timer.h new file mode 100644 index 0000000..e6a8323 --- /dev/null +++ b/include/hw/timer/stm32f2xx_timer.h @@ -0,0 +1,101 @@ +/* + * 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. + */ + +#ifndef HW_STM32F2XX_TIMER_H +#define HW_STM32F2XX_TIMER_H + +#include "hw/sysbus.h" +#include "qemu/timer.h" +#include "sysemu/sysemu.h" + +#define TIM_CR1 0x00 +#define TIM_CR2 0x04 +#define TIM_SMCR 0x08 +#define TIM_DIER 0x0C +#define TIM_SR 0x10 +#define TIM_EGR 0x14 +#define TIM_CCMR1 0x18 +#define TIM_CCMR2 0x1C +#define TIM_CCER 0x20 +#define TIM_CNT 0x24 +#define TIM_PSC 0x28 +#define TIM_ARR 0x2C +#define TIM_CCR1 0x34 +#define TIM_CCR2 0x38 +#define TIM_CCR3 0x3C +#define TIM_CCR4 0x40 +#define TIM_DCR 0x48 +#define TIM_DMAR 0x4C +#define TIM_OR 0x50 + +#define TIM_CR1_CEN 1 + +#define TIM_EGR_UG 1 + +#define TIM_CCER_CC2E (1 << 4) +#define TIM_CCMR1_OC2M2 (1 << 14) +#define TIM_CCMR1_OC2M1 (1 << 13) +#define TIM_CCMR1_OC2M0 (1 << 12) +#define TIM_CCMR1_OC2PE (1 << 11) + +#define TIM_DIER_UIE 1 + +#define TYPE_STM32F2XX_TIMER "stm32f2xx-timer" +#define STM32F2XXTIMER(obj) OBJECT_CHECK(STM32F2XXTimerState, \ + (obj), TYPE_STM32F2XX_TIMER) + +typedef struct STM32F2XXTimerState { + /* */ + SysBusDevice parent_obj; + + /* */ + MemoryRegion iomem; + QEMUTimer *timer; + qemu_irq irq; + + int64_t tick_offset; + uint64_t hit_time; + uint64_t freq_hz; + + uint32_t tim_cr1; + uint32_t tim_cr2; + uint32_t tim_smcr; + uint32_t tim_dier; + uint32_t tim_sr; + uint32_t tim_egr; + uint32_t tim_ccmr1; + uint32_t tim_ccmr2; + uint32_t tim_ccer; + uint32_t tim_psc; + uint32_t tim_arr; + uint32_t tim_ccr1; + uint32_t tim_ccr2; + uint32_t tim_ccr3; + uint32_t tim_ccr4; + uint32_t tim_dcr; + uint32_t tim_dmar; + uint32_t tim_or; +} STM32F2XXTimerState; + +#endif /* HW_STM32F2XX_TIMER_H */ diff --git a/include/hw/usb.h b/include/hw/usb.h index b20b959..5be2937 100644 --- a/include/hw/usb.h +++ b/include/hw/usb.h @@ -473,7 +473,7 @@ int set_usb_string(uint8_t *buf, const char *str); /* usb-linux.c */ USBDevice *usb_host_device_open(USBBus *bus, const char *devname); -void usb_host_info(Monitor *mon, const QDict *qdict); +void hmp_info_usbhost(Monitor *mon, const QDict *qdict); /* usb ports of the VM */ @@ -526,8 +526,9 @@ struct USBBus { }; struct USBBusOps { - int (*register_companion)(USBBus *bus, USBPort *ports[], - uint32_t portcount, uint32_t firstport); + void (*register_companion)(USBBus *bus, USBPort *ports[], + uint32_t portcount, uint32_t firstport, + Error **errp); void (*wakeup_endpoint)(USBBus *bus, USBEndpoint *ep, unsigned int stream); }; @@ -543,9 +544,10 @@ USBDevice *usb_create_simple(USBBus *bus, const char *name); USBDevice *usbdevice_create(const char *cmdline); void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index, USBPortOps *ops, int speedmask); -int usb_register_companion(const char *masterbus, USBPort *ports[], - uint32_t portcount, uint32_t firstport, - void *opaque, USBPortOps *ops, int speedmask); +void usb_register_companion(const char *masterbus, USBPort *ports[], + uint32_t portcount, uint32_t firstport, + void *opaque, USBPortOps *ops, int speedmask, + Error **errp); void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr); void usb_unregister_port(USBBus *bus, USBPort *port); void usb_claim_port(USBDevice *dev, Error **errp); diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h new file mode 100644 index 0000000..0d1fb80 --- /dev/null +++ b/include/hw/vfio/vfio-common.h @@ -0,0 +1,148 @@ +/* + * common header for 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) + */ +#ifndef HW_VFIO_VFIO_COMMON_H +#define HW_VFIO_VFIO_COMMON_H + +#include "qemu-common.h" +#include "exec/address-spaces.h" +#include "exec/memory.h" +#include "qemu/queue.h" +#include "qemu/notify.h" + +/*#define DEBUG_VFIO*/ +#ifdef DEBUG_VFIO +#define DPRINTF(fmt, ...) \ + do { fprintf(stderr, "vfio: " fmt, ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) \ + do { } while (0) +#endif + +/* Extra debugging, trap acceleration paths for more logging */ +#define VFIO_ALLOW_KVM_INTX 1 +#define VFIO_ALLOW_KVM_MSI 1 +#define VFIO_ALLOW_KVM_MSIX 1 + +enum { + VFIO_DEVICE_TYPE_PCI = 0, +}; + +typedef struct VFIORegion { + struct VFIODevice *vbasedev; + off_t fd_offset; /* offset of region within device fd */ + MemoryRegion mem; /* slow, read/write access */ + MemoryRegion mmap_mem; /* direct mapped access */ + void *mmap; + size_t size; + uint32_t flags; /* VFIO region flags (rd/wr/mmap) */ + uint8_t nr; /* cache the region number for debug */ +} VFIORegion; + +typedef struct VFIOAddressSpace { + AddressSpace *as; + QLIST_HEAD(, VFIOContainer) containers; + QLIST_ENTRY(VFIOAddressSpace) list; +} VFIOAddressSpace; + +struct VFIOGroup; + +typedef struct VFIOType1 { + MemoryListener listener; + int error; + bool initialized; +} VFIOType1; + +typedef struct VFIOContainer { + VFIOAddressSpace *space; + int fd; /* /dev/vfio/vfio, empowered by the attached groups */ + struct { + /* enable abstraction to support various iommu backends */ + union { + VFIOType1 type1; + }; + void (*release)(struct VFIOContainer *); + } iommu_data; + QLIST_HEAD(, VFIOGuestIOMMU) giommu_list; + QLIST_HEAD(, VFIOGroup) group_list; + QLIST_ENTRY(VFIOContainer) next; +} VFIOContainer; + +typedef struct VFIOGuestIOMMU { + VFIOContainer *container; + MemoryRegion *iommu; + Notifier n; + QLIST_ENTRY(VFIOGuestIOMMU) giommu_next; +} VFIOGuestIOMMU; + +typedef struct VFIODeviceOps VFIODeviceOps; + +typedef struct VFIODevice { + QLIST_ENTRY(VFIODevice) next; + struct VFIOGroup *group; + char *name; + int fd; + int type; + bool reset_works; + bool needs_reset; + bool allow_mmap; + VFIODeviceOps *ops; + unsigned int num_irqs; + unsigned int num_regions; + unsigned int flags; +} VFIODevice; + +struct VFIODeviceOps { + void (*vfio_compute_needs_reset)(VFIODevice *vdev); + int (*vfio_hot_reset_multi)(VFIODevice *vdev); + void (*vfio_eoi)(VFIODevice *vdev); +}; + +typedef struct VFIOGroup { + int fd; + int groupid; + VFIOContainer *container; + QLIST_HEAD(, VFIODevice) device_list; + QLIST_ENTRY(VFIOGroup) next; + QLIST_ENTRY(VFIOGroup) container_next; +} VFIOGroup; + +void vfio_put_base_device(VFIODevice *vbasedev); +void vfio_disable_irqindex(VFIODevice *vbasedev, int index); +void vfio_unmask_single_irqindex(VFIODevice *vbasedev, int index); +void vfio_mask_single_irqindex(VFIODevice *vbasedev, int index); +void vfio_region_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size); +uint64_t vfio_region_read(void *opaque, + hwaddr addr, unsigned size); +int vfio_mmap_region(Object *vdev, VFIORegion *region, + MemoryRegion *mem, MemoryRegion *submem, + void **map, size_t size, off_t offset, + const char *name); +void vfio_reset_handler(void *opaque); +VFIOGroup *vfio_get_group(int groupid, AddressSpace *as); +void vfio_put_group(VFIOGroup *group); +int vfio_get_device(VFIOGroup *group, const char *name, + VFIODevice *vbasedev); + +extern const MemoryRegionOps vfio_region_ops; +extern QLIST_HEAD(vfio_group_head, VFIOGroup) vfio_group_list; +extern QLIST_HEAD(vfio_as_head, VFIOAddressSpace) vfio_address_spaces; + +#endif /* !HW_VFIO_VFIO_COMMON_H */ diff --git a/include/hw/misc/vfio.h b/include/hw/vfio/vfio.h similarity index 100% rename from include/hw/misc/vfio.h rename to include/hw/vfio/vfio.h diff --git a/include/hw/virtio/dataplane/vring-accessors.h b/include/hw/virtio/dataplane/vring-accessors.h new file mode 100644 index 0000000..815c19b --- /dev/null +++ b/include/hw/virtio/dataplane/vring-accessors.h @@ -0,0 +1,75 @@ +#ifndef VRING_ACCESSORS_H +#define VRING_ACCESSORS_H + +#include "standard-headers/linux/virtio_ring.h" +#include "hw/virtio/virtio.h" +#include "hw/virtio/virtio-access.h" + +static inline uint16_t vring_get_used_idx(VirtIODevice *vdev, Vring *vring) +{ + return virtio_tswap16(vdev, vring->vr.used->idx); +} + +static inline void vring_set_used_idx(VirtIODevice *vdev, Vring *vring, + uint16_t idx) +{ + vring->vr.used->idx = virtio_tswap16(vdev, idx); +} + +static inline uint16_t vring_get_avail_idx(VirtIODevice *vdev, Vring *vring) +{ + return virtio_tswap16(vdev, vring->vr.avail->idx); +} + +static inline uint16_t vring_get_avail_ring(VirtIODevice *vdev, Vring *vring, + int i) +{ + return virtio_tswap16(vdev, vring->vr.avail->ring[i]); +} + +static inline void vring_set_used_ring_id(VirtIODevice *vdev, Vring *vring, + int i, uint32_t id) +{ + vring->vr.used->ring[i].id = virtio_tswap32(vdev, id); +} + +static inline void vring_set_used_ring_len(VirtIODevice *vdev, Vring *vring, + int i, uint32_t len) +{ + vring->vr.used->ring[i].len = virtio_tswap32(vdev, len); +} + +static inline uint16_t vring_get_used_flags(VirtIODevice *vdev, Vring *vring) +{ + return virtio_tswap16(vdev, vring->vr.used->flags); +} + +static inline uint16_t vring_get_avail_flags(VirtIODevice *vdev, Vring *vring) +{ + return virtio_tswap16(vdev, vring->vr.avail->flags); +} + +static inline void vring_set_used_flags(VirtIODevice *vdev, Vring *vring, + uint16_t flags) +{ + vring->vr.used->flags |= virtio_tswap16(vdev, flags); +} + +static inline void vring_clear_used_flags(VirtIODevice *vdev, Vring *vring, + uint16_t flags) +{ + vring->vr.used->flags &= virtio_tswap16(vdev, ~flags); +} + +static inline unsigned int vring_get_num(Vring *vring) +{ + return vring->vr.num; +} + +/* Are there more descriptors available? */ +static inline bool vring_more_avail(VirtIODevice *vdev, Vring *vring) +{ + return vring_get_avail_idx(vdev, vring) != vring->last_avail_idx; +} + +#endif diff --git a/include/hw/virtio/dataplane/vring.h b/include/hw/virtio/dataplane/vring.h index d3e086a..8d97db9 100644 --- a/include/hw/virtio/dataplane/vring.h +++ b/include/hw/virtio/dataplane/vring.h @@ -18,7 +18,7 @@ #define VRING_H #include "qemu-common.h" -#include "hw/virtio/virtio_ring.h" +#include "standard-headers/linux/virtio_ring.h" #include "hw/virtio/virtio.h" typedef struct { @@ -31,17 +31,6 @@ typedef struct { bool broken; /* was there a fatal error? */ } Vring; -static inline unsigned int vring_get_num(Vring *vring) -{ - return vring->vr.num; -} - -/* Are there more descriptors available? */ -static inline bool vring_more_avail(Vring *vring) -{ - return vring->vr.avail->idx != vring->last_avail_idx; -} - /* Fail future vring_pop() and vring_push() calls until reset */ static inline void vring_set_broken(Vring *vring) { @@ -54,6 +43,7 @@ void vring_disable_notification(VirtIODevice *vdev, Vring *vring); bool vring_enable_notification(VirtIODevice *vdev, Vring *vring); bool vring_should_notify(VirtIODevice *vdev, Vring *vring); int vring_pop(VirtIODevice *vdev, Vring *vring, VirtQueueElement *elem); -void vring_push(Vring *vring, VirtQueueElement *elem, int len); +void vring_push(VirtIODevice *vdev, Vring *vring, VirtQueueElement *elem, + int len); #endif /* VRING_H */ diff --git a/include/hw/virtio/vhost-scsi.h b/include/hw/virtio/vhost-scsi.h index 85cc031..dea0075 100644 --- a/include/hw/virtio/vhost-scsi.h +++ b/include/hw/virtio/vhost-scsi.h @@ -60,11 +60,16 @@ typedef struct VHostSCSI { Error *migration_blocker; struct vhost_dev dev; + int32_t bootindex; + int channel; + int target; + int lun; } VHostSCSI; #define DEFINE_VHOST_SCSI_PROPERTIES(_state, _conf_field) \ DEFINE_PROP_STRING("vhostfd", _state, _conf_field.vhostfd), \ DEFINE_PROP_STRING("wwpn", _state, _conf_field.wwpn), \ + DEFINE_PROP_UINT32("boot_tpgt", _state, _conf_field.boot_tpgt, 0), \ DEFINE_PROP_UINT32("num_queues", _state, _conf_field.num_queues, 1), \ DEFINE_PROP_UINT32("max_sectors", _state, _conf_field.max_sectors, 0xFFFF), \ DEFINE_PROP_UINT32("cmd_per_lun", _state, _conf_field.cmd_per_lun, 128) diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index d5593d1..8f04888 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -36,7 +36,7 @@ struct vhost_dev { MemoryRegionSection *mem_sections; struct vhost_virtqueue *vqs; int nvqs; - /* the first virtuque which would be used by this vhost dev */ + /* the first virtqueue which would be used by this vhost dev */ int vq_index; unsigned long long features; unsigned long long acked_features; diff --git a/include/hw/virtio/virtio-access.h b/include/hw/virtio/virtio-access.h index 46456fd..f88f731 100644 --- a/include/hw/virtio/virtio-access.h +++ b/include/hw/virtio/virtio-access.h @@ -126,6 +126,15 @@ static inline uint64_t virtio_ldq_p(VirtIODevice *vdev, const void *ptr) } } +static inline bool virtio_needs_swap(VirtIODevice *vdev) +{ +#ifdef HOST_WORDS_BIGENDIAN + return virtio_access_is_big_endian(vdev) ? false : true; +#else + return virtio_access_is_big_endian(vdev) ? true : false; +#endif +} + static inline uint16_t virtio_tswap16(VirtIODevice *vdev, uint16_t s) { #ifdef HOST_WORDS_BIGENDIAN diff --git a/include/hw/virtio/virtio-balloon.h b/include/hw/virtio/virtio-balloon.h index f863bfe..4ab8f54 100644 --- a/include/hw/virtio/virtio-balloon.h +++ b/include/hw/virtio/virtio-balloon.h @@ -15,6 +15,7 @@ #ifndef _QEMU_VIRTIO_BALLOON_H #define _QEMU_VIRTIO_BALLOON_H +#include "standard-headers/linux/virtio_balloon.h" #include "hw/virtio/virtio.h" #include "hw/pci/pci.h" @@ -22,39 +23,7 @@ #define VIRTIO_BALLOON(obj) \ OBJECT_CHECK(VirtIOBalloon, (obj), TYPE_VIRTIO_BALLOON) -/* from Linux's linux/virtio_balloon.h */ - -/* The ID for virtio_balloon */ -#define VIRTIO_ID_BALLOON 5 - -/* The feature bitmap for virtio balloon */ -#define VIRTIO_BALLOON_F_MUST_TELL_HOST 0 /* Tell before reclaiming pages */ -#define VIRTIO_BALLOON_F_STATS_VQ 1 /* Memory stats virtqueue */ - -/* Size of a PFN in the balloon interface. */ -#define VIRTIO_BALLOON_PFN_SHIFT 12 - -struct virtio_balloon_config -{ - /* Number of pages host wants Guest to give up. */ - uint32_t num_pages; - /* Number of pages we've actually got in balloon. */ - uint32_t actual; -}; - -/* Memory Statistics */ -#define VIRTIO_BALLOON_S_SWAP_IN 0 /* Amount of memory swapped in */ -#define VIRTIO_BALLOON_S_SWAP_OUT 1 /* Amount of memory swapped out */ -#define VIRTIO_BALLOON_S_MAJFLT 2 /* Number of major faults */ -#define VIRTIO_BALLOON_S_MINFLT 3 /* Number of minor faults */ -#define VIRTIO_BALLOON_S_MEMFREE 4 /* Total amount of free memory */ -#define VIRTIO_BALLOON_S_MEMTOT 5 /* Total amount of memory */ -#define VIRTIO_BALLOON_S_NR 6 - -typedef struct VirtIOBalloonStat { - uint16_t tag; - uint64_t val; -} QEMU_PACKED VirtIOBalloonStat; +typedef struct virtio_balloon_stat VirtIOBalloonStat; typedef struct VirtIOBalloon { VirtIODevice parent_obj; diff --git a/include/hw/virtio/virtio-blk.h b/include/hw/virtio/virtio-blk.h index 3979dc4..6bf5905 100644 --- a/include/hw/virtio/virtio-blk.h +++ b/include/hw/virtio/virtio-blk.h @@ -14,6 +14,7 @@ #ifndef _QEMU_VIRTIO_BLK_H #define _QEMU_VIRTIO_BLK_H +#include "standard-headers/linux/virtio_blk.h" #include "hw/virtio/virtio.h" #include "hw/block/block.h" #include "sysemu/iothread.h" @@ -23,88 +24,12 @@ #define VIRTIO_BLK(obj) \ OBJECT_CHECK(VirtIOBlock, (obj), TYPE_VIRTIO_BLK) -/* from Linux's linux/virtio_blk.h */ - -/* The ID for virtio_block */ -#define VIRTIO_ID_BLOCK 2 - -/* Feature bits */ -#define VIRTIO_BLK_F_BARRIER 0 /* Does host support barriers? */ -#define VIRTIO_BLK_F_SIZE_MAX 1 /* Indicates maximum segment size */ -#define VIRTIO_BLK_F_SEG_MAX 2 /* Indicates maximum # of segments */ -#define VIRTIO_BLK_F_GEOMETRY 4 /* Indicates support of legacy geometry */ -#define VIRTIO_BLK_F_RO 5 /* Disk is read-only */ -#define VIRTIO_BLK_F_BLK_SIZE 6 /* Block size of disk is available*/ -#define VIRTIO_BLK_F_SCSI 7 /* Supports scsi command passthru */ -/* #define VIRTIO_BLK_F_IDENTIFY 8 ATA IDENTIFY supported, DEPRECATED */ -#define VIRTIO_BLK_F_WCE 9 /* write cache enabled */ -#define VIRTIO_BLK_F_TOPOLOGY 10 /* Topology information is available */ -#define VIRTIO_BLK_F_CONFIG_WCE 11 /* write cache configurable */ - -#define VIRTIO_BLK_ID_BYTES 20 /* ID string length */ - -struct virtio_blk_config -{ - uint64_t capacity; - uint32_t size_max; - uint32_t seg_max; - uint16_t cylinders; - uint8_t heads; - uint8_t sectors; - uint32_t blk_size; - uint8_t physical_block_exp; - uint8_t alignment_offset; - uint16_t min_io_size; - uint32_t opt_io_size; - uint8_t wce; -} QEMU_PACKED; - -/* These two define direction. */ -#define VIRTIO_BLK_T_IN 0 -#define VIRTIO_BLK_T_OUT 1 - -/* This bit says it's a scsi command, not an actual read or write. */ -#define VIRTIO_BLK_T_SCSI_CMD 2 - -/* Flush the volatile write cache */ -#define VIRTIO_BLK_T_FLUSH 4 - -/* return the device ID string */ -#define VIRTIO_BLK_T_GET_ID 8 - -/* Barrier before this op. */ -#define VIRTIO_BLK_T_BARRIER 0x80000000 - -/* This is the first element of the read scatter-gather list. */ -struct virtio_blk_outhdr -{ - /* VIRTIO_BLK_T* */ - uint32_t type; - /* io priority. */ - uint32_t ioprio; - /* Sector (ie. 512 byte offset) */ - uint64_t sector; -}; - -#define VIRTIO_BLK_S_OK 0 -#define VIRTIO_BLK_S_IOERR 1 -#define VIRTIO_BLK_S_UNSUPP 2 - /* This is the last element of the write scatter-gather list */ struct virtio_blk_inhdr { unsigned char status; }; -/* SCSI pass-through header */ -struct virtio_scsi_inhdr -{ - uint32_t errors; - uint32_t data_len; - uint32_t sense_len; - uint32_t residual; -}; - struct VirtIOBlkConf { BlockConf conf; @@ -113,6 +38,7 @@ struct VirtIOBlkConf uint32_t scsi; uint32_t config_wce; uint32_t data_plane; + uint32_t request_merging; }; struct VirtIOBlockDataPlane; @@ -134,30 +60,33 @@ typedef struct VirtIOBlock { struct VirtIOBlockDataPlane *dataplane; } VirtIOBlock; -typedef struct MultiReqBuffer { - BlockRequest blkreq[32]; - unsigned int num_writes; -} MultiReqBuffer; - typedef struct VirtIOBlockReq { + int64_t sector_num; VirtIOBlock *dev; VirtQueueElement elem; struct virtio_blk_inhdr *in; struct virtio_blk_outhdr out; QEMUIOVector qiov; + size_t in_len; struct VirtIOBlockReq *next; + struct VirtIOBlockReq *mr_next; BlockAcctCookie acct; } VirtIOBlockReq; +#define VIRTIO_BLK_MAX_MERGE_REQS 32 + +typedef struct MultiReqBuffer { + VirtIOBlockReq *reqs[VIRTIO_BLK_MAX_MERGE_REQS]; + unsigned int num_reqs; + bool is_write; +} MultiReqBuffer; + VirtIOBlockReq *virtio_blk_alloc_request(VirtIOBlock *s); void virtio_blk_free_request(VirtIOBlockReq *req); -int virtio_blk_handle_scsi_req(VirtIOBlock *blk, - VirtQueueElement *elem); - void virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb); -void virtio_submit_multiwrite(BlockBackend *blk, MultiReqBuffer *mrb); +void virtio_blk_submit_multireq(BlockBackend *blk, MultiReqBuffer *mrb); #endif diff --git a/include/hw/virtio/virtio-bus.h b/include/hw/virtio/virtio-bus.h index 0756545..0d2e7b4 100644 --- a/include/hw/virtio/virtio-bus.h +++ b/include/hw/virtio/virtio-bus.h @@ -84,9 +84,6 @@ size_t virtio_bus_get_vdev_config_len(VirtioBusState *bus); /* Get the features of the plugged device. */ uint32_t virtio_bus_get_vdev_features(VirtioBusState *bus, uint32_t requested_features); -/* Set the features of the plugged device. */ -void virtio_bus_set_vdev_features(VirtioBusState *bus, - uint32_t requested_features); /* Get bad features of the plugged device. */ uint32_t virtio_bus_get_vdev_bad_features(VirtioBusState *bus); /* Get config of the plugged device. */ diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h index 6ceb5aa9..4c2fe83 100644 --- a/include/hw/virtio/virtio-net.h +++ b/include/hw/virtio/virtio-net.h @@ -14,49 +14,15 @@ #ifndef _QEMU_VIRTIO_NET_H #define _QEMU_VIRTIO_NET_H +#include "standard-headers/linux/virtio_net.h" #include "hw/virtio/virtio.h" -#include "hw/pci/pci.h" #define TYPE_VIRTIO_NET "virtio-net-device" #define VIRTIO_NET(obj) \ OBJECT_CHECK(VirtIONet, (obj), TYPE_VIRTIO_NET) -#define ETH_ALEN 6 - -/* from Linux's virtio_net.h */ - -/* The ID for virtio_net */ -#define VIRTIO_ID_NET 1 - -/* The feature bitmap for virtio net */ -#define VIRTIO_NET_F_CSUM 0 /* Host handles pkts w/ partial csum */ -#define VIRTIO_NET_F_GUEST_CSUM 1 /* Guest handles pkts w/ partial csum */ #define VIRTIO_NET_F_CTRL_GUEST_OFFLOADS 2 /* Control channel offload * configuration support */ -#define VIRTIO_NET_F_MAC 5 /* Host has given MAC address. */ -#define VIRTIO_NET_F_GSO 6 /* Host handles pkts w/ any GSO type */ -#define VIRTIO_NET_F_GUEST_TSO4 7 /* Guest can handle TSOv4 in. */ -#define VIRTIO_NET_F_GUEST_TSO6 8 /* Guest can handle TSOv6 in. */ -#define VIRTIO_NET_F_GUEST_ECN 9 /* Guest can handle TSO[6] w/ ECN in. */ -#define VIRTIO_NET_F_GUEST_UFO 10 /* Guest can handle UFO in. */ -#define VIRTIO_NET_F_HOST_TSO4 11 /* Host can handle TSOv4 in. */ -#define VIRTIO_NET_F_HOST_TSO6 12 /* Host can handle TSOv6 in. */ -#define VIRTIO_NET_F_HOST_ECN 13 /* Host can handle TSO[6] w/ ECN in. */ -#define VIRTIO_NET_F_HOST_UFO 14 /* Host can handle UFO in. */ -#define VIRTIO_NET_F_MRG_RXBUF 15 /* Host can merge receive buffers. */ -#define VIRTIO_NET_F_STATUS 16 /* virtio_net_config.status available */ -#define VIRTIO_NET_F_CTRL_VQ 17 /* Control channel available */ -#define VIRTIO_NET_F_CTRL_RX 18 /* Control channel RX mode support */ -#define VIRTIO_NET_F_CTRL_VLAN 19 /* Control channel VLAN filtering */ -#define VIRTIO_NET_F_CTRL_RX_EXTRA 20 /* Extra RX mode control support */ -#define VIRTIO_NET_F_GUEST_ANNOUNCE 21 /* Guest can announce itself */ -#define VIRTIO_NET_F_MQ 22 /* Device supports Receive Flow - * Steering */ - -#define VIRTIO_NET_F_CTRL_MAC_ADDR 23 /* Set MAC address */ - -#define VIRTIO_NET_S_LINK_UP 1 /* Link is up */ -#define VIRTIO_NET_S_ANNOUNCE 2 /* Announcement is needed */ #define TX_TIMER_INTERVAL 150000 /* 150 us */ @@ -77,72 +43,6 @@ typedef struct virtio_net_conf /* Maximum packet size we can receive from tap device: header + 64k */ #define VIRTIO_NET_MAX_BUFSIZE (sizeof(struct virtio_net_hdr) + (64 << 10)) -struct virtio_net_config -{ - /* The config defining mac address ($ETH_ALEN bytes) */ - uint8_t mac[ETH_ALEN]; - /* See VIRTIO_NET_F_STATUS and VIRTIO_NET_S_* above */ - uint16_t status; - /* Max virtqueue pairs supported by the device */ - uint16_t max_virtqueue_pairs; -} QEMU_PACKED; - -/* - * Control virtqueue data structures - * - * The control virtqueue expects a header in the first sg entry - * and an ack/status response in the last entry. Data for the - * command goes in between. - */ -struct virtio_net_ctrl_hdr { - uint8_t class; - uint8_t cmd; -}; - -typedef uint8_t virtio_net_ctrl_ack; - -#define VIRTIO_NET_OK 0 -#define VIRTIO_NET_ERR 1 - -/* - * Control the RX mode, ie. promisucous, allmulti, etc... - * All commands require an "out" sg entry containing a 1 byte - * state value, zero = disable, non-zero = enable. Commands - * 0 and 1 are supported with the VIRTIO_NET_F_CTRL_RX feature. - * Commands 2-5 are added with VIRTIO_NET_F_CTRL_RX_EXTRA. - */ -#define VIRTIO_NET_CTRL_RX 0 - #define VIRTIO_NET_CTRL_RX_PROMISC 0 - #define VIRTIO_NET_CTRL_RX_ALLMULTI 1 - #define VIRTIO_NET_CTRL_RX_ALLUNI 2 - #define VIRTIO_NET_CTRL_RX_NOMULTI 3 - #define VIRTIO_NET_CTRL_RX_NOUNI 4 - #define VIRTIO_NET_CTRL_RX_NOBCAST 5 - -/* - * Control the MAC - * - * The MAC filter table is managed by the hypervisor, the guest should - * assume the size is infinite. Filtering should be considered - * non-perfect, ie. based on hypervisor resources, the guest may - * received packets from sources not specified in the filter list. - * - * In addition to the class/cmd header, the TABLE_SET command requires - * two out scatterlists. Each contains a 4 byte count of entries followed - * by a concatenated byte stream of the ETH_ALEN MAC addresses. The - * first sg list contains unicast addresses, the second is for multicast. - * This functionality is present if the VIRTIO_NET_F_CTRL_RX feature - * is available. - * - * The ADDR_SET command requests one out scatterlist, it contains a - * 6 bytes MAC address. This functionality is present if the - * VIRTIO_NET_F_CTRL_MAC_ADDR feature is available. - */ -struct virtio_net_ctrl_mac { - uint32_t entries; - uint8_t macs[][ETH_ALEN]; -}; - typedef struct VirtIONetQueue { VirtQueue *rx_vq; VirtQueue *tx_vq; @@ -199,55 +99,6 @@ typedef struct VirtIONet { int announce_counter; } VirtIONet; -#define VIRTIO_NET_CTRL_MAC 1 - #define VIRTIO_NET_CTRL_MAC_TABLE_SET 0 - #define VIRTIO_NET_CTRL_MAC_ADDR_SET 1 - -/* - * Control VLAN filtering - * - * The VLAN filter table is controlled via a simple ADD/DEL interface. - * VLAN IDs not added may be filterd by the hypervisor. Del is the - * opposite of add. Both commands expect an out entry containing a 2 - * byte VLAN ID. VLAN filterting is available with the - * VIRTIO_NET_F_CTRL_VLAN feature bit. - */ -#define VIRTIO_NET_CTRL_VLAN 2 - #define VIRTIO_NET_CTRL_VLAN_ADD 0 - #define VIRTIO_NET_CTRL_VLAN_DEL 1 - -/* - * Control link announce acknowledgement - * - * VIRTIO_NET_S_ANNOUNCE bit in the status field requests link announcement from - * guest driver. The driver is notified by config space change interrupt. The - * command VIRTIO_NET_CTRL_ANNOUNCE_ACK is used to indicate that the driver has - * received the notification. It makes the device clear the bit - * VIRTIO_NET_S_ANNOUNCE in the status field. - */ -#define VIRTIO_NET_CTRL_ANNOUNCE 3 - #define VIRTIO_NET_CTRL_ANNOUNCE_ACK 0 - -/* - * Control Multiqueue - * - * The command VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET - * enables multiqueue, specifying the number of the transmit and - * receive queues that will be used. After the command is consumed and acked by - * the device, the device will not steer new packets on receive virtqueues - * other than specified nor read from transmit virtqueues other than specified. - * Accordingly, driver should not transmit new packets on virtqueues other than - * specified. - */ -struct virtio_net_ctrl_mq { - uint16_t virtqueue_pairs; -}; - -#define VIRTIO_NET_CTRL_MQ 4 - #define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET 0 - #define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN 1 - #define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX 0x8000 - /* * Control network offloads * diff --git a/include/hw/virtio/virtio-rng.h b/include/hw/virtio/virtio-rng.h index 14e85a5..7702ff4 100644 --- a/include/hw/virtio/virtio-rng.h +++ b/include/hw/virtio/virtio-rng.h @@ -14,6 +14,7 @@ #include "sysemu/rng.h" #include "sysemu/rng-random.h" +#include "standard-headers/linux/virtio_rng.h" #define TYPE_VIRTIO_RNG "virtio-rng-device" #define VIRTIO_RNG(obj) \ @@ -21,9 +22,6 @@ #define VIRTIO_RNG_GET_PARENT_CLASS(obj) \ OBJECT_GET_PARENT_CLASS(obj, TYPE_VIRTIO_RNG) -/* The Virtio ID for the virtio rng device */ -#define VIRTIO_ID_RNG 4 - struct VirtIORNGConf { RngBackend *rng; uint64_t max_bytes; diff --git a/include/hw/virtio/virtio-scsi.h b/include/hw/virtio/virtio-scsi.h index bf17cc9..f93b57d 100644 --- a/include/hw/virtio/virtio-scsi.h +++ b/include/hw/virtio/virtio-scsi.h @@ -14,6 +14,10 @@ #ifndef _QEMU_VIRTIO_SCSI_H #define _QEMU_VIRTIO_SCSI_H +/* Override CDB/sense data size: they are dynamic (guest controlled) in QEMU */ +#define VIRTIO_SCSI_CDB_SIZE 0 +#define VIRTIO_SCSI_SENSE_SIZE 0 +#include "standard-headers/linux/virtio_scsi.h" #include "hw/virtio/virtio.h" #include "hw/pci/pci.h" #include "hw/scsi/scsi.h" @@ -28,124 +32,19 @@ #define VIRTIO_SCSI(obj) \ OBJECT_CHECK(VirtIOSCSI, (obj), TYPE_VIRTIO_SCSI) - -/* The ID for virtio_scsi */ -#define VIRTIO_ID_SCSI 8 - -/* Feature Bits */ -#define VIRTIO_SCSI_F_INOUT 0 -#define VIRTIO_SCSI_F_HOTPLUG 1 -#define VIRTIO_SCSI_F_CHANGE 2 - #define VIRTIO_SCSI_VQ_SIZE 128 -#define VIRTIO_SCSI_CDB_SIZE 32 -#define VIRTIO_SCSI_SENSE_SIZE 96 #define VIRTIO_SCSI_MAX_CHANNEL 0 #define VIRTIO_SCSI_MAX_TARGET 255 #define VIRTIO_SCSI_MAX_LUN 16383 -/* Response codes */ -#define VIRTIO_SCSI_S_OK 0 -#define VIRTIO_SCSI_S_OVERRUN 1 -#define VIRTIO_SCSI_S_ABORTED 2 -#define VIRTIO_SCSI_S_BAD_TARGET 3 -#define VIRTIO_SCSI_S_RESET 4 -#define VIRTIO_SCSI_S_BUSY 5 -#define VIRTIO_SCSI_S_TRANSPORT_FAILURE 6 -#define VIRTIO_SCSI_S_TARGET_FAILURE 7 -#define VIRTIO_SCSI_S_NEXUS_FAILURE 8 -#define VIRTIO_SCSI_S_FAILURE 9 -#define VIRTIO_SCSI_S_FUNCTION_SUCCEEDED 10 -#define VIRTIO_SCSI_S_FUNCTION_REJECTED 11 -#define VIRTIO_SCSI_S_INCORRECT_LUN 12 - -/* Controlq type codes. */ -#define VIRTIO_SCSI_T_TMF 0 -#define VIRTIO_SCSI_T_AN_QUERY 1 -#define VIRTIO_SCSI_T_AN_SUBSCRIBE 2 - -/* Valid TMF subtypes. */ -#define VIRTIO_SCSI_T_TMF_ABORT_TASK 0 -#define VIRTIO_SCSI_T_TMF_ABORT_TASK_SET 1 -#define VIRTIO_SCSI_T_TMF_CLEAR_ACA 2 -#define VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET 3 -#define VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET 4 -#define VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET 5 -#define VIRTIO_SCSI_T_TMF_QUERY_TASK 6 -#define VIRTIO_SCSI_T_TMF_QUERY_TASK_SET 7 - -/* Events. */ -#define VIRTIO_SCSI_T_EVENTS_MISSED 0x80000000 -#define VIRTIO_SCSI_T_NO_EVENT 0 -#define VIRTIO_SCSI_T_TRANSPORT_RESET 1 -#define VIRTIO_SCSI_T_ASYNC_NOTIFY 2 -#define VIRTIO_SCSI_T_PARAM_CHANGE 3 - -/* Reasons for transport reset event */ -#define VIRTIO_SCSI_EVT_RESET_HARD 0 -#define VIRTIO_SCSI_EVT_RESET_RESCAN 1 -#define VIRTIO_SCSI_EVT_RESET_REMOVED 2 - -/* SCSI command request, followed by CDB and data-out */ -typedef struct { - uint8_t lun[8]; /* Logical Unit Number */ - uint64_t tag; /* Command identifier */ - uint8_t task_attr; /* Task attribute */ - uint8_t prio; - uint8_t crn; -} QEMU_PACKED VirtIOSCSICmdReq; - -/* Response, followed by sense data and data-in */ -typedef struct { - uint32_t sense_len; /* Sense data length */ - uint32_t resid; /* Residual bytes in data buffer */ - uint16_t status_qualifier; /* Status qualifier */ - uint8_t status; /* Command completion status */ - uint8_t response; /* Response values */ -} QEMU_PACKED VirtIOSCSICmdResp; - -/* Task Management Request */ -typedef struct { - uint32_t type; - uint32_t subtype; - uint8_t lun[8]; - uint64_t tag; -} QEMU_PACKED VirtIOSCSICtrlTMFReq; - -typedef struct { - uint8_t response; -} QEMU_PACKED VirtIOSCSICtrlTMFResp; - -/* Asynchronous notification query/subscription */ -typedef struct { - uint32_t type; - uint8_t lun[8]; - uint32_t event_requested; -} QEMU_PACKED VirtIOSCSICtrlANReq; - -typedef struct { - uint32_t event_actual; - uint8_t response; -} QEMU_PACKED VirtIOSCSICtrlANResp; - -typedef struct { - uint32_t event; - uint8_t lun[8]; - uint32_t reason; -} QEMU_PACKED VirtIOSCSIEvent; - -typedef struct { - uint32_t num_queues; - uint32_t seg_max; - uint32_t max_sectors; - uint32_t cmd_per_lun; - uint32_t event_info_size; - uint32_t sense_size; - uint32_t cdb_size; - uint16_t max_channel; - uint16_t max_target; - uint32_t max_lun; -} QEMU_PACKED VirtIOSCSIConfig; +typedef struct virtio_scsi_cmd_req VirtIOSCSICmdReq; +typedef struct virtio_scsi_cmd_resp VirtIOSCSICmdResp; +typedef struct virtio_scsi_ctrl_tmf_req VirtIOSCSICtrlTMFReq; +typedef struct virtio_scsi_ctrl_tmf_resp VirtIOSCSICtrlTMFResp; +typedef struct virtio_scsi_ctrl_an_req VirtIOSCSICtrlANReq; +typedef struct virtio_scsi_ctrl_an_resp VirtIOSCSICtrlANResp; +typedef struct virtio_scsi_event VirtIOSCSIEvent; +typedef struct virtio_scsi_config VirtIOSCSIConfig; struct VirtIOSCSIConf { uint32_t num_queues; @@ -153,6 +52,7 @@ struct VirtIOSCSIConf { uint32_t cmd_per_lun; char *vhostfd; char *wwpn; + uint32_t boot_tpgt; IOThread *iothread; }; @@ -209,8 +109,7 @@ typedef struct VirtIOSCSIReq { /* Note: * - fields before elem are initialized by virtio_scsi_init_req; * - elem is uninitialized at the time of allocation. - * - fields after elem (except the ending cdb[]) are zeroed by - * virtio_scsi_init_req. + * - fields after elem are zeroed by virtio_scsi_init_req. * */ VirtQueueElement elem; @@ -235,18 +134,12 @@ typedef struct VirtIOSCSIReq { VirtIOSCSIEvent event; } resp; union { - struct { - VirtIOSCSICmdReq cmd; - uint8_t cdb[]; - } QEMU_PACKED; + VirtIOSCSICmdReq cmd; VirtIOSCSICtrlTMFReq tmf; VirtIOSCSICtrlANReq an; } req; } VirtIOSCSIReq; -QEMU_BUILD_BUG_ON(offsetof(VirtIOSCSIReq, req.cdb) != - offsetof(VirtIOSCSIReq, req.cmd) + sizeof(VirtIOSCSICmdReq)); - #define DEFINE_VIRTIO_SCSI_PROPERTIES(_state, _conf_field) \ DEFINE_PROP_UINT32("num_queues", _state, _conf_field.num_queues, 1), \ DEFINE_PROP_UINT32("max_sectors", _state, _conf_field.max_sectors, 0xFFFF),\ diff --git a/include/hw/virtio/virtio-serial.h b/include/hw/virtio/virtio-serial.h index a679e54..18d1bcc 100644 --- a/include/hw/virtio/virtio-serial.h +++ b/include/hw/virtio/virtio-serial.h @@ -15,53 +15,15 @@ #ifndef _QEMU_VIRTIO_SERIAL_H #define _QEMU_VIRTIO_SERIAL_H +#include "standard-headers/linux/virtio_console.h" #include "hw/qdev.h" #include "hw/virtio/virtio.h" -/* == Interface shared between the guest kernel and qemu == */ - -/* The Virtio ID for virtio console / serial ports */ -#define VIRTIO_ID_CONSOLE 3 - -/* Features supported */ -#define VIRTIO_CONSOLE_F_MULTIPORT 1 - -#define VIRTIO_CONSOLE_BAD_ID (~(uint32_t)0) - -struct virtio_console_config { - /* - * These two fields are used by VIRTIO_CONSOLE_F_SIZE which - * isn't implemented here yet - */ - uint16_t cols; - uint16_t rows; - - uint32_t max_nr_ports; -} QEMU_PACKED; - -struct virtio_console_control { - uint32_t id; /* Port number */ - uint16_t event; /* The kind of control event (see below) */ - uint16_t value; /* Extra information for the key */ -}; - struct virtio_serial_conf { /* Max. number of ports we can have for a virtio-serial device */ uint32_t max_virtserial_ports; }; -/* Some events for the internal messages (control packets) */ -#define VIRTIO_CONSOLE_DEVICE_READY 0 -#define VIRTIO_CONSOLE_PORT_ADD 1 -#define VIRTIO_CONSOLE_PORT_REMOVE 2 -#define VIRTIO_CONSOLE_PORT_READY 3 -#define VIRTIO_CONSOLE_CONSOLE_PORT 4 -#define VIRTIO_CONSOLE_RESIZE 5 -#define VIRTIO_CONSOLE_PORT_OPEN 6 -#define VIRTIO_CONSOLE_PORT_NAME 7 - -/* == In-qemu interface == */ - #define TYPE_VIRTIO_SERIAL_PORT "virtio-serial-port" #define VIRTIO_SERIAL_PORT(obj) \ OBJECT_CHECK(VirtIOSerialPort, (obj), TYPE_VIRTIO_SERIAL_PORT) @@ -98,6 +60,17 @@ typedef struct VirtIOSerialPortClass { /* Guest is now ready to accept data (virtqueues set up). */ void (*guest_ready)(VirtIOSerialPort *port); + /* + * Guest has enqueued a buffer for the host to write into. + * Called each time a buffer is enqueued by the guest; + * irrespective of whether there already were free buffers the + * host could have consumed. + * + * This is dependent on both the guest and host end being + * connected. + */ + void (*guest_writable)(VirtIOSerialPort *port); + /* * Guest wrote some data to the port. This data is handed over to * the app via this callback. The app can return a size less than @@ -207,8 +180,6 @@ struct VirtIOSerial { /* bitmap for identifying active ports */ uint32_t *ports_map; - struct virtio_console_config config; - struct VirtIOSerialPostLoad *post_load; virtio_serial_conf serial; diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index 0726d76..d95f8b6 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -19,56 +19,12 @@ #include "hw/qdev.h" #include "sysemu/sysemu.h" #include "qemu/event_notifier.h" -#ifdef CONFIG_VIRTFS -#include "hw/virtio/virtio-9p.h" -#endif +#include "standard-headers/linux/virtio_config.h" +#include "standard-headers/linux/virtio_ring.h" -/* from Linux's linux/virtio_config.h */ - -/* Status byte for guest to report progress, and synchronize features. */ -/* We have seen device and processed generic fields (VIRTIO_CONFIG_F_VIRTIO) */ -#define VIRTIO_CONFIG_S_ACKNOWLEDGE 1 -/* We have found a driver for the device. */ -#define VIRTIO_CONFIG_S_DRIVER 2 -/* Driver has used its parts of the config, and is happy */ -#define VIRTIO_CONFIG_S_DRIVER_OK 4 -/* We've given up on this device. */ -#define VIRTIO_CONFIG_S_FAILED 0x80 - -/* Some virtio feature bits (currently bits 28 through 31) are reserved for the - * transport being used (eg. virtio_ring), the rest are per-device feature bits. */ -#define VIRTIO_TRANSPORT_F_START 28 -#define VIRTIO_TRANSPORT_F_END 32 - -/* We notify when the ring is completely used, even if the guest is suppressing - * callbacks */ -#define VIRTIO_F_NOTIFY_ON_EMPTY 24 -/* Can the device handle any descriptor layout? */ -#define VIRTIO_F_ANY_LAYOUT 27 -/* We support indirect buffer descriptors */ -#define VIRTIO_RING_F_INDIRECT_DESC 28 -/* The Guest publishes the used index for which it expects an interrupt - * at the end of the avail ring. Host should ignore the avail->flags field. */ -/* The Host publishes the avail index for which it expects a kick - * at the end of the used ring. Guest should ignore the used->flags field. */ -#define VIRTIO_RING_F_EVENT_IDX 29 /* A guest should never accept this. It implies negotiation is broken. */ #define VIRTIO_F_BAD_FEATURE 30 -/* from Linux's linux/virtio_ring.h */ - -/* This marks a buffer as continuing via the next field. */ -#define VRING_DESC_F_NEXT 1 -/* This marks a buffer as write-only (otherwise read-only). */ -#define VRING_DESC_F_WRITE 2 -/* This means the buffer contains a list of buffer descriptors. */ -#define VRING_DESC_F_INDIRECT 4 - -/* This means don't notify other side when buffer added. */ -#define VRING_USED_F_NO_NOTIFY 1 -/* This means don't interrupt guest when buffer consumed. */ -#define VRING_AVAIL_F_NO_INTERRUPT 1 - struct VirtQueue; static inline hwaddr vring_align(hwaddr addr, @@ -230,9 +186,6 @@ int virtio_set_features(VirtIODevice *vdev, uint32_t val); /* Base devices. */ typedef struct VirtIOBlkConf VirtIOBlkConf; struct virtio_net_conf; -VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, - struct virtio_net_conf *net, - uint32_t host_features); typedef struct virtio_serial_conf virtio_serial_conf; typedef struct VirtIOSCSIConf VirtIOSCSIConf; typedef struct VirtIORNGConf VirtIORNGConf; @@ -266,6 +219,29 @@ void virtio_queue_set_host_notifier_fd_handler(VirtQueue *vq, bool assign, void virtio_queue_notify_vq(VirtQueue *vq); void virtio_irq(VirtQueue *vq); +static inline void virtio_add_feature(uint32_t *features, unsigned int fbit) +{ + assert(fbit < 32); + *features |= (1 << fbit); +} + +static inline void virtio_clear_feature(uint32_t *features, unsigned int fbit) +{ + assert(fbit < 32); + *features &= ~(1 << fbit); +} + +static inline bool __virtio_has_feature(uint32_t features, unsigned int fbit) +{ + assert(fbit < 32); + return !!(features & (1 << fbit)); +} + +static inline bool virtio_has_feature(VirtIODevice *vdev, unsigned int fbit) +{ + return __virtio_has_feature(vdev->guest_features, fbit); +} + static inline bool virtio_is_big_endian(VirtIODevice *vdev) { assert(vdev->device_endian != VIRTIO_DEVICE_ENDIAN_UNKNOWN); diff --git a/include/hw/xen/xen.h b/include/hw/xen/xen.h index b0ed04c..4356af4 100644 --- a/include/hw/xen/xen.h +++ b/include/hw/xen/xen.h @@ -32,7 +32,6 @@ int xen_pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num); void xen_piix3_set_irq(void *opaque, int irq_num, int level); void xen_piix_pci_write_config_client(uint32_t address, uint32_t val, int len); void xen_hvm_inject_msi(uint64_t addr, uint32_t data); -void xen_cmos_set_s3_resume(void *opaque, int irq, int level); qemu_irq *xen_interrupt_controller_init(void); diff --git a/include/hw/xen/xen_common.h b/include/hw/xen/xen_common.h index 95612a4..38f29fb 100644 --- a/include/hw/xen/xen_common.h +++ b/include/hw/xen/xen_common.h @@ -16,7 +16,9 @@ #include "hw/hw.h" #include "hw/xen/xen.h" +#include "hw/pci/pci.h" #include "qemu/queue.h" +#include "trace.h" /* * We don't support Xen prior to 3.3.0. @@ -166,17 +168,243 @@ void xen_shutdown_fatal_error(const char *fmt, ...) GCC_FMT_ATTR(1, 2); #ifdef HVM_PARAM_VMPORT_REGS_PFN static inline int xen_get_vmport_regs_pfn(XenXC xc, domid_t dom, - unsigned long *vmport_regs_pfn) + xen_pfn_t *vmport_regs_pfn) { - return xc_get_hvm_param(xc, dom, HVM_PARAM_VMPORT_REGS_PFN, - vmport_regs_pfn); + int rc; + uint64_t value; + rc = xc_hvm_param_get(xc, dom, HVM_PARAM_VMPORT_REGS_PFN, &value); + if (rc >= 0) { + *vmport_regs_pfn = (xen_pfn_t) value; + } + return rc; } #else static inline int xen_get_vmport_regs_pfn(XenXC xc, domid_t dom, - unsigned long *vmport_regs_pfn) + xen_pfn_t *vmport_regs_pfn) { return -ENOSYS; } #endif +/* Xen before 4.5 */ +#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 450 + +#ifndef HVM_PARAM_BUFIOREQ_EVTCHN +#define HVM_PARAM_BUFIOREQ_EVTCHN 26 +#endif + +#define IOREQ_TYPE_PCI_CONFIG 2 + +typedef uint32_t ioservid_t; + +static inline void xen_map_memory_section(XenXC xc, domid_t dom, + ioservid_t ioservid, + MemoryRegionSection *section) +{ +} + +static inline void xen_unmap_memory_section(XenXC xc, domid_t dom, + ioservid_t ioservid, + MemoryRegionSection *section) +{ +} + +static inline void xen_map_io_section(XenXC xc, domid_t dom, + ioservid_t ioservid, + MemoryRegionSection *section) +{ +} + +static inline void xen_unmap_io_section(XenXC xc, domid_t dom, + ioservid_t ioservid, + MemoryRegionSection *section) +{ +} + +static inline void xen_map_pcidev(XenXC xc, domid_t dom, + ioservid_t ioservid, + PCIDevice *pci_dev) +{ +} + +static inline void xen_unmap_pcidev(XenXC xc, domid_t dom, + ioservid_t ioservid, + PCIDevice *pci_dev) +{ +} + +static inline int xen_create_ioreq_server(XenXC xc, domid_t dom, + ioservid_t *ioservid) +{ + return 0; +} + +static inline void xen_destroy_ioreq_server(XenXC xc, domid_t dom, + ioservid_t ioservid) +{ +} + +static inline int xen_get_ioreq_server_info(XenXC xc, domid_t dom, + ioservid_t ioservid, + xen_pfn_t *ioreq_pfn, + xen_pfn_t *bufioreq_pfn, + evtchn_port_t *bufioreq_evtchn) +{ + unsigned long param; + int rc; + + rc = xc_get_hvm_param(xc, dom, HVM_PARAM_IOREQ_PFN, ¶m); + if (rc < 0) { + fprintf(stderr, "failed to get HVM_PARAM_IOREQ_PFN\n"); + return -1; + } + + *ioreq_pfn = param; + + rc = xc_get_hvm_param(xc, dom, HVM_PARAM_BUFIOREQ_PFN, ¶m); + if (rc < 0) { + fprintf(stderr, "failed to get HVM_PARAM_BUFIOREQ_PFN\n"); + return -1; + } + + *bufioreq_pfn = param; + + rc = xc_get_hvm_param(xc, dom, HVM_PARAM_BUFIOREQ_EVTCHN, + ¶m); + if (rc < 0) { + fprintf(stderr, "failed to get HVM_PARAM_BUFIOREQ_EVTCHN\n"); + return -1; + } + + *bufioreq_evtchn = param; + + return 0; +} + +static inline int xen_set_ioreq_server_state(XenXC xc, domid_t dom, + ioservid_t ioservid, + bool enable) +{ + return 0; +} + +/* Xen 4.5 */ +#else + +static inline void xen_map_memory_section(XenXC xc, domid_t dom, + ioservid_t ioservid, + MemoryRegionSection *section) +{ + hwaddr start_addr = section->offset_within_address_space; + ram_addr_t size = int128_get64(section->size); + hwaddr end_addr = start_addr + size - 1; + + trace_xen_map_mmio_range(ioservid, start_addr, end_addr); + xc_hvm_map_io_range_to_ioreq_server(xc, dom, ioservid, 1, + start_addr, end_addr); +} + +static inline void xen_unmap_memory_section(XenXC xc, domid_t dom, + ioservid_t ioservid, + MemoryRegionSection *section) +{ + hwaddr start_addr = section->offset_within_address_space; + ram_addr_t size = int128_get64(section->size); + hwaddr end_addr = start_addr + size - 1; + + trace_xen_unmap_mmio_range(ioservid, start_addr, end_addr); + xc_hvm_unmap_io_range_from_ioreq_server(xc, dom, ioservid, 1, + start_addr, end_addr); +} + +static inline void xen_map_io_section(XenXC xc, domid_t dom, + ioservid_t ioservid, + MemoryRegionSection *section) +{ + hwaddr start_addr = section->offset_within_address_space; + ram_addr_t size = int128_get64(section->size); + hwaddr end_addr = start_addr + size - 1; + + trace_xen_map_portio_range(ioservid, start_addr, end_addr); + xc_hvm_map_io_range_to_ioreq_server(xc, dom, ioservid, 0, + start_addr, end_addr); +} + +static inline void xen_unmap_io_section(XenXC xc, domid_t dom, + ioservid_t ioservid, + MemoryRegionSection *section) +{ + hwaddr start_addr = section->offset_within_address_space; + ram_addr_t size = int128_get64(section->size); + hwaddr end_addr = start_addr + size - 1; + + trace_xen_unmap_portio_range(ioservid, start_addr, end_addr); + xc_hvm_unmap_io_range_from_ioreq_server(xc, dom, ioservid, 0, + start_addr, end_addr); +} + +static inline void xen_map_pcidev(XenXC xc, domid_t dom, + ioservid_t ioservid, + PCIDevice *pci_dev) +{ + trace_xen_map_pcidev(ioservid, pci_bus_num(pci_dev->bus), + PCI_SLOT(pci_dev->devfn), PCI_FUNC(pci_dev->devfn)); + xc_hvm_map_pcidev_to_ioreq_server(xc, dom, ioservid, + 0, pci_bus_num(pci_dev->bus), + PCI_SLOT(pci_dev->devfn), + PCI_FUNC(pci_dev->devfn)); +} + +static inline void xen_unmap_pcidev(XenXC xc, domid_t dom, + ioservid_t ioservid, + PCIDevice *pci_dev) +{ + trace_xen_unmap_pcidev(ioservid, pci_bus_num(pci_dev->bus), + PCI_SLOT(pci_dev->devfn), PCI_FUNC(pci_dev->devfn)); + xc_hvm_unmap_pcidev_from_ioreq_server(xc, dom, ioservid, + 0, pci_bus_num(pci_dev->bus), + PCI_SLOT(pci_dev->devfn), + PCI_FUNC(pci_dev->devfn)); +} + +static inline int xen_create_ioreq_server(XenXC xc, domid_t dom, + ioservid_t *ioservid) +{ + int rc = xc_hvm_create_ioreq_server(xc, dom, 1, ioservid); + + if (rc == 0) { + trace_xen_ioreq_server_create(*ioservid); + } + + return rc; +} + +static inline void xen_destroy_ioreq_server(XenXC xc, domid_t dom, + ioservid_t ioservid) +{ + trace_xen_ioreq_server_destroy(ioservid); + xc_hvm_destroy_ioreq_server(xc, dom, ioservid); +} + +static inline int xen_get_ioreq_server_info(XenXC xc, domid_t dom, + ioservid_t ioservid, + xen_pfn_t *ioreq_pfn, + xen_pfn_t *bufioreq_pfn, + evtchn_port_t *bufioreq_evtchn) +{ + return xc_hvm_get_ioreq_server_info(xc, dom, ioservid, + ioreq_pfn, bufioreq_pfn, + bufioreq_evtchn); +} + +static inline int xen_set_ioreq_server_state(XenXC xc, domid_t dom, + ioservid_t ioservid, + bool enable) +{ + trace_xen_ioreq_server_state(ioservid, enable); + return xc_hvm_set_ioreq_server_state(xc, dom, ioservid, enable); +} + +#endif + #endif /* QEMU_HW_XEN_COMMON_H */ diff --git a/include/migration/migration.h b/include/migration/migration.h index 3cb5ba8..bf09968 100644 --- a/include/migration/migration.h +++ b/include/migration/migration.h @@ -33,6 +33,7 @@ #define QEMU_VM_SECTION_END 0x03 #define QEMU_VM_SECTION_FULL 0x04 #define QEMU_VM_SUBSECTION 0x05 +#define QEMU_VM_VMDESCRIPTION 0x06 struct MigrationParams { bool blk; @@ -70,10 +71,6 @@ void qemu_start_incoming_migration(const char *uri, Error **errp); uint64_t migrate_max_downtime(void); -void do_info_migrate_print(Monitor *mon, const QObject *data); - -void do_info_migrate(Monitor *mon, QObject **ret_data); - void exec_start_incoming_migration(const char *host_port, Error **errp); void exec_start_outgoing_migration(MigrationState *s, const char *host_port, Error **errp); @@ -142,7 +139,6 @@ void migrate_add_blocker(Error *reason); */ void migrate_del_blocker(Error *reason); -bool migrate_rdma_pin_all(void); bool migrate_zero_blocks(void); bool migrate_auto_converge(void); @@ -172,6 +168,6 @@ void ram_control_load_hook(QEMUFile *f, uint64_t flags); size_t ram_control_save_page(QEMUFile *f, ram_addr_t block_offset, ram_addr_t offset, size_t size, - int *bytes_sent); + uint64_t *bytes_sent); #endif diff --git a/include/migration/page_cache.h b/include/migration/page_cache.h index 2d5ce2d..10ed532 100644 --- a/include/migration/page_cache.h +++ b/include/migration/page_cache.h @@ -43,8 +43,10 @@ void cache_fini(PageCache *cache); * * @cache pointer to the PageCache struct * @addr: page addr + * @current_age: current bitmap generation */ -bool cache_is_cached(const PageCache *cache, uint64_t addr); +bool cache_is_cached(const PageCache *cache, uint64_t addr, + uint64_t current_age); /** * get_cached_data: Get the data cached for an addr @@ -60,13 +62,15 @@ uint8_t *get_cached_data(const PageCache *cache, uint64_t addr); * cache_insert: insert the page into the cache. the page cache * will dup the data on insert. the previous value will be overwritten * - * Returns -1 on error + * Returns -1 when the page isn't inserted into cache * * @cache pointer to the PageCache struct * @addr: page address * @pdata: pointer to the page + * @current_age: current bitmap generation */ -int cache_insert(PageCache *cache, uint64_t addr, const uint8_t *pdata); +int cache_insert(PageCache *cache, uint64_t addr, const uint8_t *pdata, + uint64_t current_age); /** * cache_resize: resize the page cache. In case of size reduction the extra diff --git a/include/migration/qemu-file.h b/include/migration/qemu-file.h index 401676b..745a850 100644 --- a/include/migration/qemu-file.h +++ b/include/migration/qemu-file.h @@ -82,7 +82,15 @@ typedef size_t (QEMURamSaveFunc)(QEMUFile *f, void *opaque, ram_addr_t block_offset, ram_addr_t offset, size_t size, - int *bytes_sent); + uint64_t *bytes_sent); + +/* + * Stop any read or write (depending on flags) on the underlying + * transport on the QEMUFile. + * Existing blocking reads/writes must be woken + * Returns 0 on success, -err on error + */ +typedef int (QEMUFileShutdownFunc)(void *opaque, bool rd, bool wr); typedef struct QEMUFileOps { QEMUFilePutBufferFunc *put_buffer; @@ -94,6 +102,7 @@ typedef struct QEMUFileOps { QEMURamHookFunc *after_ram_iterate; QEMURamHookFunc *hook_ram_load; QEMURamSaveFunc *save_page; + QEMUFileShutdownFunc *shut_down; } QEMUFileOps; struct QEMUSizedBuffer { @@ -112,6 +121,7 @@ QEMUFile *qemu_bufopen(const char *mode, QEMUSizedBuffer *input); int qemu_get_fd(QEMUFile *f); int qemu_fclose(QEMUFile *f); int64_t qemu_ftell(QEMUFile *f); +int64_t qemu_ftell_fast(QEMUFile *f); void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size); void qemu_put_byte(QEMUFile *f, int v); /* @@ -123,7 +133,6 @@ bool qemu_file_mode_is_not_valid(const char *mode); bool qemu_file_is_writable(QEMUFile *f); QEMUSizedBuffer *qsb_create(const uint8_t *buffer, size_t len); -QEMUSizedBuffer *qsb_clone(const QEMUSizedBuffer *); void qsb_free(QEMUSizedBuffer *); size_t qsb_set_length(QEMUSizedBuffer *qsb, size_t length); size_t qsb_get_length(const QEMUSizedBuffer *qsb); @@ -177,6 +186,7 @@ void qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate); int64_t qemu_file_get_rate_limit(QEMUFile *f); int qemu_file_get_error(QEMUFile *f); void qemu_file_set_error(QEMUFile *f, int ret); +int qemu_file_shutdown(QEMUFile *f); void qemu_fflush(QEMUFile *f); static inline void qemu_put_be64s(QEMUFile *f, const uint64_t *pv) diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index e45fc49..bc7616a 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -29,6 +29,7 @@ #ifndef CONFIG_USER_ONLY #include #endif +#include typedef void SaveStateHandler(QEMUFile *f, void *opaque); typedef int LoadStateHandler(QEMUFile *f, void *opaque, int version_id); @@ -138,9 +139,7 @@ struct VMStateDescription { const VMStateSubsection *subsections; }; -#ifdef CONFIG_USER_ONLY extern const VMStateDescription vmstate_dummy; -#endif extern const VMStateInfo vmstate_info_bool; @@ -189,7 +188,7 @@ extern const VMStateInfo vmstate_info_bitmap; type_check_2darray(_type, typeof_field(_state, _field), _n1, _n2)) #define vmstate_offset_sub_array(_state, _field, _type, _start) \ - (offsetof(_state, _field[_start])) + vmstate_offset_value(_state, _field[_start], _type) #define vmstate_offset_buffer(_state, _field) \ vmstate_offset_array(_state, _field, uint8_t, \ @@ -359,6 +358,16 @@ extern const VMStateInfo vmstate_info_bitmap; .offset = vmstate_offset_array(_s, _f, _type*, _n), \ } +#define VMSTATE_STRUCT_SUB_ARRAY(_field, _state, _start, _num, _version, _vmsd, _type) { \ + .name = (stringify(_field)), \ + .version_id = (_version), \ + .num = (_num), \ + .vmsd = &(_vmsd), \ + .size = sizeof(_type), \ + .flags = VMS_STRUCT|VMS_ARRAY, \ + .offset = vmstate_offset_sub_array(_state, _field, _type, _start), \ +} + #define VMSTATE_STRUCT_ARRAY_TEST(_field, _state, _num, _test, _version, _vmsd, _type) { \ .name = (stringify(_field)), \ .num = (_num), \ @@ -626,6 +635,18 @@ extern const VMStateInfo vmstate_info_bitmap; #define VMSTATE_INT32_POSITIVE_LE(_f, _s) \ VMSTATE_SINGLE(_f, _s, 0, vmstate_info_int32_le, int32_t) +#define VMSTATE_INT8_TEST(_f, _s, _t) \ + VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_int8, int8_t) + +#define VMSTATE_INT16_TEST(_f, _s, _t) \ + VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_int16, int16_t) + +#define VMSTATE_INT32_TEST(_f, _s, _t) \ + VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_int32, int32_t) + +#define VMSTATE_INT64_TEST(_f, _s, _t) \ + VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_int64, int64_t) + #define VMSTATE_UINT8_TEST(_f, _s, _t) \ VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_uint8, uint8_t) @@ -635,6 +656,9 @@ extern const VMStateInfo vmstate_info_bitmap; #define VMSTATE_UINT32_TEST(_f, _s, _t) \ VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_uint32, uint32_t) +#define VMSTATE_UINT64_TEST(_f, _s, _t) \ + VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_uint64, uint64_t) + #define VMSTATE_FLOAT64_V(_f, _s, _v) \ VMSTATE_SINGLE(_f, _s, _v, vmstate_info_float64, float64) @@ -642,17 +666,29 @@ extern const VMStateInfo vmstate_info_bitmap; #define VMSTATE_FLOAT64(_f, _s) \ VMSTATE_FLOAT64_V(_f, _s, 0) -#define VMSTATE_TIMER_TEST(_f, _s, _test) \ +#define VMSTATE_TIMER_PTR_TEST(_f, _s, _test) \ VMSTATE_POINTER_TEST(_f, _s, _test, vmstate_info_timer, QEMUTimer *) -#define VMSTATE_TIMER_V(_f, _s, _v) \ +#define VMSTATE_TIMER_PTR_V(_f, _s, _v) \ VMSTATE_POINTER(_f, _s, _v, vmstate_info_timer, QEMUTimer *) +#define VMSTATE_TIMER_PTR(_f, _s) \ + VMSTATE_TIMER_PTR_V(_f, _s, 0) + +#define VMSTATE_TIMER_PTR_ARRAY(_f, _s, _n) \ + VMSTATE_ARRAY_OF_POINTER(_f, _s, _n, 0, vmstate_info_timer, QEMUTimer *) + +#define VMSTATE_TIMER_TEST(_f, _s, _test) \ + VMSTATE_SINGLE_TEST(_f, _s, _test, 0, vmstate_info_timer, QEMUTimer) + +#define VMSTATE_TIMER_V(_f, _s, _v) \ + VMSTATE_SINGLE(_f, _s, _v, vmstate_info_timer, QEMUTimer) + #define VMSTATE_TIMER(_f, _s) \ VMSTATE_TIMER_V(_f, _s, 0) #define VMSTATE_TIMER_ARRAY(_f, _s, _n) \ - VMSTATE_ARRAY_OF_POINTER(_f, _s, _n, 0, vmstate_info_timer, QEMUTimer *) + VMSTATE_ARRAY(_f, _s, _n, 0, vmstate_info_timer, QEMUTimer) #define VMSTATE_BOOL_ARRAY_V(_f, _s, _n, _v) \ VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_bool, bool) @@ -779,7 +815,7 @@ extern const VMStateInfo vmstate_info_bitmap; int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, int version_id); void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, - void *opaque); + void *opaque, QJSON *vmdesc); int vmstate_register_with_alias_id(DeviceState *dev, int instance_id, const VMStateDescription *vmsd, diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index 47606d0..1c06bed 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -34,8 +34,7 @@ int monitor_read_block_device_key(Monitor *mon, const char *device, void *opaque); int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp); -int monitor_handle_fd_param(Monitor *mon, const char *fdname); -int monitor_handle_fd_param2(Monitor *mon, const char *fdname, Error **errp); +int monitor_fd_param(Monitor *mon, const char *fdname, Error **errp); void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0); diff --git a/include/monitor/qdev.h b/include/monitor/qdev.h index 8d16e11..7190752 100644 --- a/include/monitor/qdev.h +++ b/include/monitor/qdev.h @@ -6,8 +6,9 @@ /*** monitor commands ***/ -void do_info_qtree(Monitor *mon, const QDict *qdict); -void do_info_qdm(Monitor *mon, const QDict *qdict); +void hmp_info_qtree(Monitor *mon, const QDict *qdict); +void hmp_info_qdm(Monitor *mon, const QDict *qdict); +void hmp_info_qom_tree(Monitor *mon, const QDict *dict); int do_device_add(Monitor *mon, const QDict *qdict, QObject **ret_data); int qdev_device_help(QemuOpts *opts); DeviceState *qdev_device_add(QemuOpts *opts); diff --git a/include/net/net.h b/include/net/net.h index 008d610..50ffcb9 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -156,7 +156,7 @@ ssize_t qemu_deliver_packet_iov(NetClientState *sender, void *opaque); void print_net_client(Monitor *mon, NetClientState *nc); -void do_info_network(Monitor *mon, const QDict *qdict); +void hmp_info_network(Monitor *mon, const QDict *qdict); /* NIC info */ @@ -187,8 +187,8 @@ int net_client_parse(QemuOptsList *opts_list, const char *str); int net_init_clients(void); void net_check_clients(void); void net_cleanup(void); -void net_host_device_add(Monitor *mon, const QDict *qdict); -void net_host_device_remove(Monitor *mon, const QDict *qdict); +void hmp_host_net_add(Monitor *mon, const QDict *qdict); +void hmp_host_net_remove(Monitor *mon, const QDict *qdict); void netdev_add(QemuOpts *opts, Error **errp); int qmp_netdev_add(Monitor *mon, const QDict *qdict, QObject **ret); diff --git a/include/net/slirp.h b/include/net/slirp.h index 0502389..64b795c 100644 --- a/include/net/slirp.h +++ b/include/net/slirp.h @@ -31,8 +31,8 @@ #ifdef CONFIG_SLIRP -void net_slirp_hostfwd_add(Monitor *mon, const QDict *qdict); -void net_slirp_hostfwd_remove(Monitor *mon, const QDict *qdict); +void hmp_hostfwd_add(Monitor *mon, const QDict *qdict); +void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict); int net_slirp_redir(const char *redir_str); @@ -40,7 +40,7 @@ int net_slirp_parse_legacy(QemuOptsList *opts_list, const char *optarg, int *ret int net_slirp_smb(const char *exported_dir); -void do_info_usernet(Monitor *mon, const QDict *qdict); +void hmp_info_usernet(Monitor *mon, const QDict *qdict); #endif diff --git a/include/net/tap.h b/include/net/tap.h index 6daeb42..5da4edc 100644 --- a/include/net/tap.h +++ b/include/net/tap.h @@ -28,6 +28,7 @@ #include "qemu-common.h" #include "qapi-types.h" +#include "standard-headers/linux/virtio_net.h" int tap_enable(NetClientState *nc); int tap_disable(NetClientState *nc); @@ -37,27 +38,4 @@ int tap_get_fd(NetClientState *nc); struct vhost_net; struct vhost_net *tap_get_vhost_net(NetClientState *nc); -struct virtio_net_hdr -{ -#define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 // Use csum_start, csum_offset -#define VIRTIO_NET_HDR_F_DATA_VALID 2 // Csum is valid - uint8_t flags; -#define VIRTIO_NET_HDR_GSO_NONE 0 // Not a GSO frame -#define VIRTIO_NET_HDR_GSO_TCPV4 1 // GSO frame, IPv4 TCP (TSO) -#define VIRTIO_NET_HDR_GSO_UDP 3 // GSO frame, IPv4 UDP (UFO) -#define VIRTIO_NET_HDR_GSO_TCPV6 4 // GSO frame, IPv6 TCP -#define VIRTIO_NET_HDR_GSO_ECN 0x80 // TCP has ECN set - uint8_t gso_type; - uint16_t hdr_len; - uint16_t gso_size; - uint16_t csum_start; - uint16_t csum_offset; -}; - -struct virtio_net_hdr_mrg_rxbuf -{ - struct virtio_net_hdr hdr; - uint16_t num_buffers; /* Number of merged rx buffers */ -}; - #endif /* QEMU_NET_TAP_H */ diff --git a/include/qapi/error.h b/include/qapi/error.h index d712089..f44c451 100644 --- a/include/qapi/error.h +++ b/include/qapi/error.h @@ -83,6 +83,11 @@ Error *error_copy(const Error *err); const char *error_get_pretty(Error *err); /** + * Convenience function to error_report() and free an error object. + */ +void error_report_err(Error *); + +/** * Propagate an error to an indirect pointer to an error. This function will * always transfer ownership of the error reference and handles the case where * dst_err is NULL correctly. Errors after the first are discarded. diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h index 0ca6cbd..57a62d4 100644 --- a/include/qapi/qmp/qerror.h +++ b/include/qapi/qmp/qerror.h @@ -37,27 +37,18 @@ void qerror_report_err(Error *err); #define QERR_BASE_NOT_FOUND \ ERROR_CLASS_GENERIC_ERROR, "Base '%s' not found" -#define QERR_BLOCK_JOB_NOT_ACTIVE \ - ERROR_CLASS_DEVICE_NOT_ACTIVE, "No active block job on device '%s'" +#define QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED \ + ERROR_CLASS_GENERIC_ERROR, "Block format '%s' used by device '%s' does not support feature '%s'" #define QERR_BLOCK_JOB_NOT_READY \ ERROR_CLASS_GENERIC_ERROR, "The active block job for device '%s' cannot be completed" -#define QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED \ - ERROR_CLASS_GENERIC_ERROR, "Block format '%s' used by device '%s' does not support feature '%s'" - #define QERR_BUS_NO_HOTPLUG \ ERROR_CLASS_GENERIC_ERROR, "Bus '%s' does not support hotplugging" #define QERR_BUS_NOT_FOUND \ ERROR_CLASS_GENERIC_ERROR, "Bus '%s' not found" -#define QERR_COMMAND_NOT_FOUND \ - ERROR_CLASS_COMMAND_NOT_FOUND, "The command %s has not been found" - -#define QERR_DEVICE_ENCRYPTED \ - ERROR_CLASS_DEVICE_ENCRYPTED, "'%s' (%s) is encrypted" - #define QERR_DEVICE_HAS_NO_MEDIUM \ ERROR_CLASS_GENERIC_ERROR, "Device '%s' has no medium" @@ -73,12 +64,6 @@ void qerror_report_err(Error *err); #define QERR_DEVICE_NO_HOTPLUG \ ERROR_CLASS_GENERIC_ERROR, "Device '%s' does not support hotplugging" -#define QERR_DEVICE_NOT_ACTIVE \ - ERROR_CLASS_DEVICE_NOT_ACTIVE, "No %s device has been activated" - -#define QERR_DEVICE_NOT_ENCRYPTED \ - ERROR_CLASS_GENERIC_ERROR, "Device '%s' is not encrypted" - #define QERR_DEVICE_NOT_FOUND \ ERROR_CLASS_DEVICE_NOT_FOUND, "Device '%s' not found" @@ -112,9 +97,6 @@ void qerror_report_err(Error *err); #define QERR_JSON_PARSING \ ERROR_CLASS_GENERIC_ERROR, "Invalid JSON syntax" -#define QERR_KVM_MISSING_CAP \ - ERROR_CLASS_KVM_MISSING_CAP, "Using KVM without %s, %s unavailable" - #define QERR_MIGRATION_ACTIVE \ ERROR_CLASS_GENERIC_ERROR, "There's a migration process in progress" diff --git a/include/qemu-common.h b/include/qemu-common.h index f862214..1b5cffb 100644 --- a/include/qemu-common.h +++ b/include/qemu-common.h @@ -370,6 +370,12 @@ static inline uint8_t from_bcd(uint8_t val) } /* compute with 96 bit intermediate result: (a*b)/c */ +#ifdef CONFIG_INT128 +static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) +{ + return (__int128_t)a * b / c; +} +#else static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) { union { @@ -392,6 +398,7 @@ static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c; return res.ll; } +#endif /* Round number down to multiple */ #define QEMU_ALIGN_DOWN(n, m) ((n) / (m) * (m)) @@ -411,6 +418,9 @@ static inline bool is_power_of_2(uint64_t value) /* round down to the nearest power of 2*/ int64_t pow2floor(int64_t value); +/* round up to the nearest power of 2 (0 if overflow) */ +uint64_t pow2ceil(uint64_t value); + #include "qemu/module.h" /* diff --git a/include/qemu-io.h b/include/qemu-io.h index 5d6006f..4d402b9 100644 --- a/include/qemu-io.h +++ b/include/qemu-io.h @@ -22,7 +22,7 @@ #define CMD_FLAG_GLOBAL ((int)0x80000000) /* don't iterate "args" */ -typedef int (*cfunc_t)(BlockDriverState *bs, int argc, char **argv); +typedef int (*cfunc_t)(BlockBackend *blk, int argc, char **argv); typedef void (*helpfunc_t)(void); typedef struct cmdinfo { @@ -40,7 +40,7 @@ typedef struct cmdinfo { extern bool qemuio_misalign; -bool qemuio_command(BlockDriverState *bs, const char *cmd); +bool qemuio_command(BlockBackend *blk, const char *cmd); void qemuio_add_command(const cmdinfo_t *ci); int qemuio_command_usage(const cmdinfo_t *ci); diff --git a/include/qemu/atomic.h b/include/qemu/atomic.h index 492bce1..98e05ca 100644 --- a/include/qemu/atomic.h +++ b/include/qemu/atomic.h @@ -122,11 +122,72 @@ #endif #ifndef atomic_read -#define atomic_read(ptr) (*(__typeof__(*ptr) *volatile) (ptr)) +#define atomic_read(ptr) (*(__typeof__(*ptr) volatile*) (ptr)) #endif #ifndef atomic_set -#define atomic_set(ptr, i) ((*(__typeof__(*ptr) *volatile) (ptr)) = (i)) +#define atomic_set(ptr, i) ((*(__typeof__(*ptr) volatile*) (ptr)) = (i)) +#endif + +/** + * atomic_rcu_read - reads a RCU-protected pointer to a local variable + * into a RCU read-side critical section. The pointer can later be safely + * dereferenced within the critical section. + * + * This ensures that the pointer copy is invariant thorough the whole critical + * section. + * + * Inserts memory barriers on architectures that require them (currently only + * Alpha) and documents which pointers are protected by RCU. + * + * Unless the __ATOMIC_CONSUME memory order is available, atomic_rcu_read also + * includes a compiler barrier to ensure that value-speculative optimizations + * (e.g. VSS: Value Speculation Scheduling) does not perform the data read + * before the pointer read by speculating the value of the pointer. On new + * enough compilers, atomic_load takes care of such concern about + * dependency-breaking optimizations. + * + * Should match atomic_rcu_set(), atomic_xchg(), atomic_cmpxchg(). + */ +#ifndef atomic_rcu_read +#ifdef __ATOMIC_CONSUME +#define atomic_rcu_read(ptr) ({ \ + typeof(*ptr) _val; \ + __atomic_load(ptr, &_val, __ATOMIC_CONSUME); \ + _val; \ +}) +#else +#define atomic_rcu_read(ptr) ({ \ + typeof(*ptr) _val = atomic_read(ptr); \ + smp_read_barrier_depends(); \ + _val; \ +}) +#endif +#endif + +/** + * atomic_rcu_set - assigns (publicizes) a pointer to a new data structure + * meant to be read by RCU read-side critical sections. + * + * Documents which pointers will be dereferenced by RCU read-side critical + * sections and adds the required memory barriers on architectures requiring + * them. It also makes sure the compiler does not reorder code initializing the + * data structure before its publication. + * + * Should match atomic_rcu_read(). + */ +#ifndef atomic_rcu_set +#ifdef __ATOMIC_RELEASE +#define atomic_rcu_set(ptr, i) do { \ + typeof(*ptr) _val = (i); \ + __atomic_store(ptr, &_val, __ATOMIC_RELEASE); \ +} while(0) +#else +#define atomic_rcu_set(ptr, i) do { \ + smp_wmb(); \ + atomic_set(ptr, i); \ +} while (0) +#endif #endif /* These have the same semantics as Java volatile variables. diff --git a/include/qemu/bitops.h b/include/qemu/bitops.h index 181bd46..90ca8df 100644 --- a/include/qemu/bitops.h +++ b/include/qemu/bitops.h @@ -354,7 +354,7 @@ static inline int32_t sextract32(uint32_t value, int start, int length) * Returns: the sign extended value of the bit field extracted from the * input value. */ -static inline uint64_t sextract64(uint64_t value, int start, int length) +static inline int64_t sextract64(uint64_t value, int start, int length) { assert(start >= 0 && length > 0 && length <= 64 - start); /* Note that this implementation relies on right shift of signed diff --git a/include/qemu/bswap.h b/include/qemu/bswap.h index 78c1ced..07d88de 100644 --- a/include/qemu/bswap.h +++ b/include/qemu/bswap.h @@ -204,7 +204,7 @@ typedef union { * f : float access * * sign is: - * (empty): for floats or 32 bit size + * (empty): for 32 or 64 bit sizes (including floats and doubles) * u : unsigned * s : signed * @@ -218,7 +218,16 @@ typedef union { * he : host endian * be : big endian * le : little endian + * te : target endian * (except for byte accesses, which have no endian infix). + * + * The target endian accessors are obviously only available to source + * files which are built per-target; they are defined in cpu-all.h. + * + * In all cases these functions take a host pointer. + * For accessors that take a guest address rather than a + * host address, see the cpu_{ld,st}_* accessors defined in + * cpu_ldst.h. */ static inline int ldub_p(const void *ptr) diff --git a/include/qemu/log.h b/include/qemu/log.h index d515424..195f665 100644 --- a/include/qemu/log.h +++ b/include/qemu/log.h @@ -40,6 +40,7 @@ static inline bool qemu_log_enabled(void) #define CPU_LOG_RESET (1 << 9) #define LOG_UNIMP (1 << 10) #define LOG_GUEST_ERROR (1 << 11) +#define CPU_LOG_MMU (1 << 12) /* Returns true if a bit is set in the current loglevel mask */ diff --git a/include/qemu/option.h b/include/qemu/option.h index 59bea75..f88b545 100644 --- a/include/qemu/option.h +++ b/include/qemu/option.h @@ -94,11 +94,12 @@ uint64_t qemu_opt_get_number_del(QemuOpts *opts, const char *name, uint64_t qemu_opt_get_size_del(QemuOpts *opts, const char *name, uint64_t defval); int qemu_opt_unset(QemuOpts *opts, const char *name); -int qemu_opt_set(QemuOpts *opts, const char *name, const char *value); -void qemu_opt_set_err(QemuOpts *opts, const char *name, const char *value, - Error **errp); -int qemu_opt_set_bool(QemuOpts *opts, const char *name, bool val); -int qemu_opt_set_number(QemuOpts *opts, const char *name, int64_t val); +void qemu_opt_set(QemuOpts *opts, const char *name, const char *value, + Error **errp); +void qemu_opt_set_bool(QemuOpts *opts, const char *name, bool val, + Error **errp); +void qemu_opt_set_number(QemuOpts *opts, const char *name, int64_t val, + Error **errp); typedef int (*qemu_opt_loopfunc)(const char *name, const char *value, void *opaque); int qemu_opt_foreach(QemuOpts *opts, qemu_opt_loopfunc func, void *opaque, int abort_on_failure); @@ -108,13 +109,14 @@ QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id, int fail_if_exists, Error **errp); void qemu_opts_reset(QemuOptsList *list); void qemu_opts_loc_restore(QemuOpts *opts); -int qemu_opts_set(QemuOptsList *list, const char *id, - const char *name, const char *value); +void qemu_opts_set(QemuOptsList *list, const char *id, + const char *name, const char *value, Error **errp); const char *qemu_opts_id(QemuOpts *opts); void qemu_opts_set_id(QemuOpts *opts, char *id); void qemu_opts_del(QemuOpts *opts); void qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc, Error **errp); -int qemu_opts_do_parse(QemuOpts *opts, const char *params, const char *firstname); +void qemu_opts_do_parse(QemuOpts *opts, const char *params, + const char *firstname, Error **errp); QemuOpts *qemu_opts_parse(QemuOptsList *list, const char *params, int permit_abbrev); void qemu_opts_set_defaults(QemuOptsList *list, const char *params, int permit_abbrev); @@ -124,7 +126,7 @@ QDict *qemu_opts_to_qdict(QemuOpts *opts, QDict *qdict); void qemu_opts_absorb_qdict(QemuOpts *opts, QDict *qdict, Error **errp); typedef int (*qemu_opts_loopfunc)(QemuOpts *opts, void *opaque); -void qemu_opts_print(QemuOpts *opts); +void qemu_opts_print(QemuOpts *opts, const char *sep); int qemu_opts_foreach(QemuOptsList *list, qemu_opts_loopfunc func, void *opaque, int abort_on_failure); void qemu_opts_print_help(QemuOptsList *list); diff --git a/include/qemu/queue.h b/include/qemu/queue.h index d433b90..f781aa2 100644 --- a/include/qemu/queue.h +++ b/include/qemu/queue.h @@ -104,6 +104,19 @@ struct { \ (head)->lh_first = NULL; \ } while (/*CONSTCOND*/0) +#define QLIST_SWAP(dstlist, srclist, field) do { \ + void *tmplist; \ + tmplist = (srclist)->lh_first; \ + (srclist)->lh_first = (dstlist)->lh_first; \ + if ((srclist)->lh_first != NULL) { \ + (srclist)->lh_first->field.le_prev = &(srclist)->lh_first; \ + } \ + (dstlist)->lh_first = tmplist; \ + if ((dstlist)->lh_first != NULL) { \ + (dstlist)->lh_first->field.le_prev = &(dstlist)->lh_first; \ + } \ +} while (/*CONSTCOND*/0) + #define QLIST_INSERT_AFTER(listelm, elm, field) do { \ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ (listelm)->field.le_next->field.le_prev = \ @@ -126,17 +139,6 @@ struct { \ (elm)->field.le_prev = &(head)->lh_first; \ } while (/*CONSTCOND*/0) -#define QLIST_INSERT_HEAD_RCU(head, elm, field) do { \ - (elm)->field.le_prev = &(head)->lh_first; \ - (elm)->field.le_next = (head)->lh_first; \ - smp_wmb(); /* fill elm before linking it */ \ - if ((head)->lh_first != NULL) { \ - (head)->lh_first->field.le_prev = &(elm)->field.le_next; \ - } \ - (head)->lh_first = (elm); \ - smp_wmb(); \ -} while (/* CONSTCOND*/0) - #define QLIST_REMOVE(elm, field) do { \ if ((elm)->field.le_next != NULL) \ (elm)->field.le_next->field.le_prev = \ @@ -191,8 +193,20 @@ struct { \ } while (/*CONSTCOND*/0) #define QSLIST_INSERT_HEAD(head, elm, field) do { \ - (elm)->field.sle_next = (head)->slh_first; \ - (head)->slh_first = (elm); \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define QSLIST_INSERT_HEAD_ATOMIC(head, elm, field) do { \ + typeof(elm) save_sle_next; \ + do { \ + save_sle_next = (elm)->field.sle_next = (head)->slh_first; \ + } while (atomic_cmpxchg(&(head)->slh_first, save_sle_next, (elm)) != \ + save_sle_next); \ +} while (/*CONSTCOND*/0) + +#define QSLIST_MOVE_ATOMIC(dest, src) do { \ + (dest)->slh_first = atomic_xchg(&(src)->slh_first, NULL); \ } while (/*CONSTCOND*/0) #define QSLIST_REMOVE_HEAD(head, field) do { \ @@ -268,6 +282,17 @@ struct { \ (head)->sqh_last = &(head)->sqh_first; \ } while (/*CONSTCOND*/0) +#define QSIMPLEQ_SPLIT_AFTER(head, elm, field, removed) do { \ + QSIMPLEQ_INIT(removed); \ + if (((removed)->sqh_first = (head)->sqh_first) != NULL) { \ + if (((head)->sqh_first = (elm)->field.sqe_next) == NULL) { \ + (head)->sqh_last = &(head)->sqh_first; \ + } \ + (removed)->sqh_last = &(elm)->field.sqe_next; \ + (elm)->field.sqe_next = NULL; \ + } \ +} while (/*CONSTCOND*/0) + #define QSIMPLEQ_REMOVE(head, elm, type, field) do { \ if ((head)->sqh_first == (elm)) { \ QSIMPLEQ_REMOVE_HEAD((head), field); \ diff --git a/include/qemu/rcu.h b/include/qemu/rcu.h new file mode 100644 index 0000000..7df1e86 --- /dev/null +++ b/include/qemu/rcu.h @@ -0,0 +1,156 @@ +#ifndef QEMU_RCU_H +#define QEMU_RCU_H + +/* + * urcu-mb.h + * + * Userspace RCU header with explicit memory barrier. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * IBM's contributions to this file may be relicensed under LGPLv2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "qemu/compiler.h" +#include "qemu/thread.h" +#include "qemu/queue.h" +#include "qemu/atomic.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Important ! + * + * Each thread containing read-side critical sections must be registered + * with rcu_register_thread() before calling rcu_read_lock(). + * rcu_unregister_thread() should be called before the thread exits. + */ + +#ifdef DEBUG_RCU +#define rcu_assert(args...) assert(args) +#else +#define rcu_assert(args...) +#endif + +/* + * Global quiescent period counter with low-order bits unused. + * Using a int rather than a char to eliminate false register dependencies + * causing stalls on some architectures. + */ +extern unsigned long rcu_gp_ctr; + +extern QemuEvent rcu_gp_event; + +struct rcu_reader_data { + /* Data used by both reader and synchronize_rcu() */ + unsigned long ctr; + bool waiting; + + /* Data used by reader only */ + unsigned depth; + + /* Data used for registry, protected by rcu_gp_lock */ + QLIST_ENTRY(rcu_reader_data) node; +}; + +extern __thread struct rcu_reader_data rcu_reader; + +static inline void rcu_read_lock(void) +{ + struct rcu_reader_data *p_rcu_reader = &rcu_reader; + unsigned ctr; + + if (p_rcu_reader->depth++ > 0) { + return; + } + + ctr = atomic_read(&rcu_gp_ctr); + atomic_xchg(&p_rcu_reader->ctr, ctr); + if (atomic_read(&p_rcu_reader->waiting)) { + atomic_set(&p_rcu_reader->waiting, false); + qemu_event_set(&rcu_gp_event); + } +} + +static inline void rcu_read_unlock(void) +{ + struct rcu_reader_data *p_rcu_reader = &rcu_reader; + + assert(p_rcu_reader->depth != 0); + if (--p_rcu_reader->depth > 0) { + return; + } + + atomic_xchg(&p_rcu_reader->ctr, 0); + if (atomic_read(&p_rcu_reader->waiting)) { + atomic_set(&p_rcu_reader->waiting, false); + qemu_event_set(&rcu_gp_event); + } +} + +extern void synchronize_rcu(void); + +/* + * Reader thread registration. + */ +extern void rcu_register_thread(void); +extern void rcu_unregister_thread(void); +extern void rcu_after_fork(void); + +struct rcu_head; +typedef void RCUCBFunc(struct rcu_head *head); + +struct rcu_head { + struct rcu_head *next; + RCUCBFunc *func; +}; + +extern void call_rcu1(struct rcu_head *head, RCUCBFunc *func); + +/* The operands of the minus operator must have the same type, + * which must be the one that we specify in the cast. + */ +#define call_rcu(head, func, field) \ + call_rcu1(({ \ + char __attribute__((unused)) \ + offset_must_be_zero[-offsetof(typeof(*(head)), field)], \ + func_type_invalid = (func) - (void (*)(typeof(head)))(func); \ + &(head)->field; \ + }), \ + (RCUCBFunc *)(func)) + +#define g_free_rcu(obj, field) \ + call_rcu1(({ \ + char __attribute__((unused)) \ + offset_must_be_zero[-offsetof(typeof(*(obj)), field)]; \ + &(obj)->field; \ + }), \ + (RCUCBFunc *)g_free); + +#ifdef __cplusplus +} +#endif + +#endif /* QEMU_RCU_H */ diff --git a/include/qemu/rcu_queue.h b/include/qemu/rcu_queue.h new file mode 100644 index 0000000..3aca7a5 --- /dev/null +++ b/include/qemu/rcu_queue.h @@ -0,0 +1,134 @@ +#ifndef QEMU_RCU_QUEUE_H +#define QEMU_RCU_QUEUE_H + +/* + * rcu_queue.h + * + * RCU-friendly versions of the queue.h primitives. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Copyright (c) 2013 Mike D. Day, IBM Corporation. + * + * IBM's contributions to this file may be relicensed under LGPLv2 or later. + */ + +#include "qemu/queue.h" +#include "qemu/atomic.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + * List access methods. + */ +#define QLIST_EMPTY_RCU(head) (atomic_rcu_read(&(head)->lh_first) == NULL) +#define QLIST_FIRST_RCU(head) (atomic_rcu_read(&(head)->lh_first)) +#define QLIST_NEXT_RCU(elm, field) (atomic_rcu_read(&(elm)->field.le_next)) + +/* + * List functions. + */ + + +/* + * The difference between atomic_read/set and atomic_rcu_read/set + * is in the including of a read/write memory barrier to the volatile + * access. atomic_rcu_* macros include the memory barrier, the + * plain atomic macros do not. Therefore, it should be correct to + * issue a series of reads or writes to the same element using only + * the atomic_* macro, until the last read or write, which should be + * atomic_rcu_* to introduce a read or write memory barrier as + * appropriate. + */ + +/* Upon publication of the listelm->next value, list readers + * will see the new node when following next pointers from + * antecedent nodes, but may not see the new node when following + * prev pointers from subsequent nodes until after the RCU grace + * period expires. + * see linux/include/rculist.h __list_add_rcu(new, prev, next) + */ +#define QLIST_INSERT_AFTER_RCU(listelm, elm, field) do { \ + (elm)->field.le_next = (listelm)->field.le_next; \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ + atomic_rcu_set(&(listelm)->field.le_next, (elm)); \ + if ((elm)->field.le_next != NULL) { \ + (elm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + } \ +} while (/*CONSTCOND*/0) + +/* Upon publication of the listelm->prev->next value, list + * readers will see the new element when following prev pointers + * from subsequent elements, but may not see the new element + * when following next pointers from antecedent elements + * until after the RCU grace period expires. + */ +#define QLIST_INSERT_BEFORE_RCU(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + atomic_rcu_set((listelm)->field.le_prev, (elm)); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +/* Upon publication of the head->first value, list readers + * will see the new element when following the head, but may + * not see the new element when following prev pointers from + * subsequent elements until after the RCU grace period has + * expired. + */ +#define QLIST_INSERT_HEAD_RCU(head, elm, field) do { \ + (elm)->field.le_prev = &(head)->lh_first; \ + (elm)->field.le_next = (head)->lh_first; \ + atomic_rcu_set((&(head)->lh_first), (elm)); \ + if ((elm)->field.le_next != NULL) { \ + (elm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + } \ +} while (/*CONSTCOND*/0) + + +/* prior to publication of the elm->prev->next value, some list + * readers may still see the removed element when following + * the antecedent's next pointer. + */ +#define QLIST_REMOVE_RCU(elm, field) do { \ + if ((elm)->field.le_next != NULL) { \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + } \ + *(elm)->field.le_prev = (elm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +/* List traversal must occur within an RCU critical section. */ +#define QLIST_FOREACH_RCU(var, head, field) \ + for ((var) = atomic_rcu_read(&(head)->lh_first); \ + (var); \ + (var) = atomic_rcu_read(&(var)->field.le_next)) + +/* List traversal must occur within an RCU critical section. */ +#define QLIST_FOREACH_SAFE_RCU(var, head, field, next_var) \ + for ((var) = (atomic_rcu_read(&(head)->lh_first)); \ + (var) && \ + ((next_var) = atomic_rcu_read(&(var)->field.le_next), 1); \ + (var) = (next_var)) + +#ifdef __cplusplus +} +#endif +#endif /* QEMU_RCU_QUEUE.H */ diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h index f47dae6..7992ece 100644 --- a/include/qemu/sockets.h +++ b/include/qemu/sockets.h @@ -44,6 +44,13 @@ int socket_set_fast_reuse(int fd); int send_all(int fd, const void *buf, int len1); int recv_all(int fd, void *buf, int len1, bool single_read); +#ifdef WIN32 +/* Windows has different names for the same constants with the same values */ +#define SHUT_RD 0 +#define SHUT_WR 1 +#define SHUT_RDWR 2 +#endif + /* callback function for nonblocking connect * valid fd on success, negative error code on failure */ diff --git a/include/qemu/thread.h b/include/qemu/thread.h index f7e3b9b..5114ec8 100644 --- a/include/qemu/thread.h +++ b/include/qemu/thread.h @@ -25,9 +25,6 @@ void qemu_mutex_lock(QemuMutex *mutex); int qemu_mutex_trylock(QemuMutex *mutex); void qemu_mutex_unlock(QemuMutex *mutex); -#define rcu_read_lock() do { } while (0) -#define rcu_read_unlock() do { } while (0) - void qemu_cond_init(QemuCond *cond); void qemu_cond_destroy(QemuCond *cond); @@ -61,4 +58,8 @@ bool qemu_thread_is_self(QemuThread *thread); void qemu_thread_exit(void *retval); void qemu_thread_naming(bool enable); +struct Notifier; +void qemu_thread_atexit_add(struct Notifier *notifier); +void qemu_thread_atexit_remove(struct Notifier *notifier); + #endif diff --git a/include/qemu/timer.h b/include/qemu/timer.h index 5f5210d..e5bd494 100644 --- a/include/qemu/timer.h +++ b/include/qemu/timer.h @@ -36,12 +36,20 @@ * is suspended, and it will reflect system time changes the host may * undergo (e.g. due to NTP). The host clock has the same precision as * the virtual clock. + * + * @QEMU_CLOCK_VIRTUAL_RT: realtime clock used for icount warp + * + * Outside icount mode, this clock is the same as @QEMU_CLOCK_VIRTUAL. + * In icount mode, this clock counts nanoseconds while the virtual + * machine is running. It is used to increase @QEMU_CLOCK_VIRTUAL + * while the CPUs are sleeping and thus not executing instructions. */ typedef enum { QEMU_CLOCK_REALTIME = 0, QEMU_CLOCK_VIRTUAL = 1, QEMU_CLOCK_HOST = 2, + QEMU_CLOCK_VIRTUAL_RT = 3, QEMU_CLOCK_MAX } QEMUClockType; @@ -402,7 +410,7 @@ int64_t timerlistgroup_deadline_ns(QEMUTimerListGroup *tlg); */ /** - * timer_init: + * timer_init_tl: * @ts: the timer to be initialised * @timer_list: the timer list to attach the timer to * @scale: the scale value for the timer @@ -415,9 +423,82 @@ int64_t timerlistgroup_deadline_ns(QEMUTimerListGroup *tlg); * You need not call an explicit deinit call. Simply make * sure it is not on a list with timer_del. */ -void timer_init(QEMUTimer *ts, - QEMUTimerList *timer_list, int scale, - QEMUTimerCB *cb, void *opaque); +void timer_init_tl(QEMUTimer *ts, + QEMUTimerList *timer_list, int scale, + QEMUTimerCB *cb, void *opaque); + +/** + * timer_init: + * @type: the clock to associate with the timer + * @scale: the scale value for the timer + * @cb: the callback to call when the timer expires + * @opaque: the opaque pointer to pass to the callback + * + * Initialize a timer with the given scale on the default timer list + * associated with the clock. + * + * You need not call an explicit deinit call. Simply make + * sure it is not on a list with timer_del. + */ +static inline void timer_init(QEMUTimer *ts, QEMUClockType type, int scale, + QEMUTimerCB *cb, void *opaque) +{ + timer_init_tl(ts, main_loop_tlg.tl[type], scale, cb, opaque); +} + +/** + * timer_init_ns: + * @type: the clock to associate with the timer + * @cb: the callback to call when the timer expires + * @opaque: the opaque pointer to pass to the callback + * + * Initialize a timer with nanosecond scale on the default timer list + * associated with the clock. + * + * You need not call an explicit deinit call. Simply make + * sure it is not on a list with timer_del. + */ +static inline void timer_init_ns(QEMUTimer *ts, QEMUClockType type, + QEMUTimerCB *cb, void *opaque) +{ + timer_init(ts, type, SCALE_NS, cb, opaque); +} + +/** + * timer_init_us: + * @type: the clock to associate with the timer + * @cb: the callback to call when the timer expires + * @opaque: the opaque pointer to pass to the callback + * + * Initialize a timer with microsecond scale on the default timer list + * associated with the clock. + * + * You need not call an explicit deinit call. Simply make + * sure it is not on a list with timer_del. + */ +static inline void timer_init_us(QEMUTimer *ts, QEMUClockType type, + QEMUTimerCB *cb, void *opaque) +{ + timer_init(ts, type, SCALE_US, cb, opaque); +} + +/** + * timer_init_ms: + * @type: the clock to associate with the timer + * @cb: the callback to call when the timer expires + * @opaque: the opaque pointer to pass to the callback + * + * Initialize a timer with millisecond scale on the default timer list + * associated with the clock. + * + * You need not call an explicit deinit call. Simply make + * sure it is not on a list with timer_del. + */ +static inline void timer_init_ms(QEMUTimer *ts, QEMUClockType type, + QEMUTimerCB *cb, void *opaque) +{ + timer_init(ts, type, SCALE_MS, cb, opaque); +} /** * timer_new_tl: @@ -440,7 +521,7 @@ static inline QEMUTimer *timer_new_tl(QEMUTimerList *timer_list, void *opaque) { QEMUTimer *ts = g_malloc0(sizeof(QEMUTimer)); - timer_init(ts, timer_list, scale, cb, opaque); + timer_init_tl(ts, timer_list, scale, cb, opaque); return ts; } @@ -514,6 +595,17 @@ static inline QEMUTimer *timer_new_ms(QEMUClockType type, QEMUTimerCB *cb, } /** + * timer_deinit: + * @ts: the timer to be de-initialised + * + * Deassociate the timer from any timerlist. You should + * call timer_del before. After this call, any further + * timer_del call cannot cause dangling pointer accesses + * even if the previously used timerlist is freed. + */ +void timer_deinit(QEMUTimer *ts); + +/** * timer_free: * @ts: the timer * @@ -743,9 +835,9 @@ static inline int64_t get_clock(void) #endif /* icount */ +int64_t cpu_get_icount_raw(void); int64_t cpu_get_icount(void); int64_t cpu_get_clock(void); -int64_t cpu_get_clock_offset(void); int64_t cpu_icount_to_ns(int64_t icount); /*******************************************/ @@ -907,11 +999,10 @@ static inline int64_t cpu_get_real_ticks (void) #ifdef CONFIG_PROFILER static inline int64_t profile_getclock(void) { - return cpu_get_real_ticks(); + return get_clock(); } -extern int64_t qemu_time, qemu_time_start; -extern int64_t tlb_flush_time; +extern int64_t tcg_time; extern int64_t dev_time; #endif diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 3475177..cde3314 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -3,80 +3,78 @@ /* A load of opaque types so that device init declarations don't have to pull in all the real definitions. */ -typedef struct QEMUTimer QEMUTimer; -typedef struct QEMUTimerListGroup QEMUTimerListGroup; -typedef struct QEMUFile QEMUFile; -typedef struct QEMUBH QEMUBH; - -typedef struct AioContext AioContext; - -typedef struct Visitor Visitor; - struct Monitor; -typedef struct Monitor Monitor; -typedef struct MigrationParams MigrationParams; - -typedef struct Property Property; -typedef struct PropertyInfo PropertyInfo; -typedef struct CompatProperty CompatProperty; -typedef struct DeviceState DeviceState; -typedef struct BusState BusState; -typedef struct BusClass BusClass; +/* Please keep this list in alphabetical order */ +typedef struct AdapterInfo AdapterInfo; typedef struct AddressSpace AddressSpace; -typedef struct MemoryRegion MemoryRegion; -typedef struct MemoryRegionSection MemoryRegionSection; -typedef struct MemoryListener MemoryListener; - -typedef struct MemoryMappingList MemoryMappingList; - -typedef struct QEMUMachine QEMUMachine; -typedef struct MachineClass MachineClass; -typedef struct MachineState MachineState; -typedef struct NICInfo NICInfo; -typedef struct HCIInfo HCIInfo; +typedef struct AioContext AioContext; typedef struct AudioState AudioState; typedef struct BlockBackend BlockBackend; typedef struct BlockDriverState BlockDriverState; -typedef struct DriveInfo DriveInfo; -typedef struct DisplayState DisplayState; +typedef struct BusClass BusClass; +typedef struct BusState BusState; +typedef struct CharDriverState CharDriverState; +typedef struct CompatProperty CompatProperty; +typedef struct DeviceState DeviceState; +typedef struct DeviceListener DeviceListener; typedef struct DisplayChangeListener DisplayChangeListener; +typedef struct DisplayState DisplayState; typedef struct DisplaySurface DisplaySurface; -typedef struct PixelFormat PixelFormat; -typedef struct QemuConsole QemuConsole; -typedef struct CharDriverState CharDriverState; -typedef struct MACAddr MACAddr; -typedef struct NetClientState NetClientState; +typedef struct DriveInfo DriveInfo; +typedef struct EventNotifier EventNotifier; +typedef struct FWCfgIoState FWCfgIoState; +typedef struct FWCfgMemState FWCfgMemState; +typedef struct FWCfgState FWCfgState; +typedef struct HCIInfo HCIInfo; typedef struct I2CBus I2CBus; +typedef struct I2SCodec I2SCodec; typedef struct ISABus ISABus; typedef struct ISADevice ISADevice; -typedef struct SMBusDevice SMBusDevice; -typedef struct PCIHostState PCIHostState; -typedef struct PCIExpressHost PCIExpressHost; +typedef struct MACAddr MACAddr; +typedef struct MachineClass MachineClass; +typedef struct MachineState MachineState; +typedef struct MemoryListener MemoryListener; +typedef struct MemoryMappingList MemoryMappingList; +typedef struct MemoryRegion MemoryRegion; +typedef struct MemoryRegionSection MemoryRegionSection; +typedef struct MigrationParams MigrationParams; +typedef struct Monitor Monitor; +typedef struct MouseTransformInfo MouseTransformInfo; +typedef struct MSIMessage MSIMessage; +typedef struct NetClientState NetClientState; +typedef struct NICInfo NICInfo; +typedef struct PcGuestInfo PcGuestInfo; +typedef struct PCIBridge PCIBridge; typedef struct PCIBus PCIBus; typedef struct PCIDevice PCIDevice; -typedef struct PCIExpressDevice PCIExpressDevice; -typedef struct PCIBridge PCIBridge; -typedef struct PCIEAERMsg PCIEAERMsg; -typedef struct PCIEAERLog PCIEAERLog; typedef struct PCIEAERErr PCIEAERErr; +typedef struct PCIEAERLog PCIEAERLog; +typedef struct PCIEAERMsg PCIEAERMsg; typedef struct PCIEPort PCIEPort; typedef struct PCIESlot PCIESlot; -typedef struct MSIMessage MSIMessage; -typedef struct SerialState SerialState; +typedef struct PCIExpressDevice PCIExpressDevice; +typedef struct PCIExpressHost PCIExpressHost; +typedef struct PCIHostState PCIHostState; typedef struct PCMCIACardState PCMCIACardState; -typedef struct MouseTransformInfo MouseTransformInfo; -typedef struct uWireSlave uWireSlave; -typedef struct I2SCodec I2SCodec; -typedef struct SSIBus SSIBus; -typedef struct EventNotifier EventNotifier; -typedef struct VirtIODevice VirtIODevice; +typedef struct PixelFormat PixelFormat; +typedef struct PropertyInfo PropertyInfo; +typedef struct Property Property; +typedef struct QEMUBH QEMUBH; +typedef struct QemuConsole QemuConsole; +typedef struct QEMUFile QEMUFile; +typedef struct QEMUMachine QEMUMachine; typedef struct QEMUSGList QEMUSGList; typedef struct QEMUSizedBuffer QEMUSizedBuffer; -typedef struct SHPCDevice SHPCDevice; -typedef struct FWCfgState FWCfgState; -typedef struct PcGuestInfo PcGuestInfo; +typedef struct QEMUTimerListGroup QEMUTimerListGroup; +typedef struct QEMUTimer QEMUTimer; typedef struct Range Range; -typedef struct AdapterInfo AdapterInfo; +typedef struct SerialState SerialState; +typedef struct SHPCDevice SHPCDevice; +typedef struct SMBusDevice SMBusDevice; +typedef struct SSIBus SSIBus; +typedef struct uWireSlave uWireSlave; +typedef struct VirtIODevice VirtIODevice; +typedef struct Visitor Visitor; #endif /* QEMU_TYPEDEFS_H */ diff --git a/include/qjson.h b/include/qjson.h new file mode 100644 index 0000000..7c54fdf --- /dev/null +++ b/include/qjson.h @@ -0,0 +1,29 @@ +/* + * QEMU JSON writer + * + * Copyright Alexander Graf + * + * Authors: + * Alexander Graf + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ +#ifndef QEMU_QJSON_H +#define QEMU_QJSON_H + +#define TYPE_QJSON "QJSON" +typedef struct QJSON QJSON; + +QJSON *qjson_new(void); +void json_prop_str(QJSON *json, const char *name, const char *str); +void json_prop_int(QJSON *json, const char *name, int64_t val); +void json_end_array(QJSON *json); +void json_start_array(QJSON *json, const char *name); +void json_end_object(QJSON *json); +void json_start_object(QJSON *json, const char *name); +const char *qjson_get_str(QJSON *json); +void qjson_finish(QJSON *json); + +#endif /* QEMU_QJSON_H */ diff --git a/include/qom/cpu.h b/include/qom/cpu.h index 2098f1c..9dafb48 100644 --- a/include/qom/cpu.h +++ b/include/qom/cpu.h @@ -82,6 +82,10 @@ struct TranslationBlock; * @do_unassigned_access: Callback for unassigned access handling. * @do_unaligned_access: Callback for unaligned access handling, if * the target defines #ALIGNED_ONLY. + * @virtio_is_big_endian: Callback to return %true if a CPU which supports + * runtime configurable endianness is currently big-endian. Non-configurable + * CPUs can use the default implementation of this method. This method should + * not be used by any callers other than the pre-1.0 virtio devices. * @memory_rw_debug: Callback for GDB memory access. * @dump_state: Callback for dumping state. * @dump_statistics: Callback for dumping statistics. @@ -96,6 +100,14 @@ struct TranslationBlock; * @gdb_read_register: Callback for letting GDB read a register. * @gdb_write_register: Callback for letting GDB write a register. * @debug_excp_handler: Callback for handling debug exceptions. + * @write_elf64_note: Callback for writing a CPU-specific ELF note to a + * 64-bit VM coredump. + * @write_elf32_qemunote: Callback for writing a CPU- and QEMU-specific ELF + * note to a 32-bit VM coredump. + * @write_elf32_note: Callback for writing a CPU-specific ELF note to a + * 32-bit VM coredump. + * @write_elf32_qemunote: Callback for writing a CPU- and QEMU-specific ELF + * note to a 32-bit VM coredump. * @vmsd: State description for migration. * @gdb_num_core_regs: Number of core registers accessible to GDB. * @gdb_core_xml_file: File name for core registers GDB XML description. @@ -256,6 +268,7 @@ struct CPUState { sigjmp_buf jmp_env; AddressSpace *as; + struct AddressSpaceDispatch *memory_dispatch; MemoryListener *tcg_as_listener; void *env_ptr; /* CPUArchState */ @@ -580,7 +593,7 @@ static inline void cpu_unaligned_access(CPUState *cpu, vaddr addr, { CPUClass *cc = CPU_GET_CLASS(cpu); - return cc->do_unaligned_access(cpu, addr, is_write, is_user, retaddr); + cc->do_unaligned_access(cpu, addr, is_write, is_user, retaddr); } #endif diff --git a/include/qom/object.h b/include/qom/object.h index 89c3092..d2d7748 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -273,7 +273,7 @@ typedef struct InterfaceInfo InterfaceInfo; * .name = TYPE_DERIVED, * .parent = TYPE_MY, * .class_size = sizeof(DerivedClass), - * .class_init = my_class_init, + * .class_init = derived_class_init, * }; * * @@ -1204,6 +1204,20 @@ void object_property_add_bool(Object *obj, const char *name, Error **errp); /** + * object_property_add_tm: + * @obj: the object to add a property to + * @name: the name of the property + * @get: the getter or NULL if the property is write-only. + * @errp: if an error occurs, a pointer to an area to store the error + * + * Add a read-only struct tm valued property using a getter function. + * This function will add a property of type 'struct tm'. + */ +void object_property_add_tm(Object *obj, const char *name, + void (*get)(Object *, struct tm *, Error **), + Error **errp); + +/** * object_property_add_uint8_ptr: * @obj: the object to add a property to * @name: the name of the property diff --git a/include/qom/object_interfaces.h b/include/qom/object_interfaces.h index b792283..283ae0d 100644 --- a/include/qom/object_interfaces.h +++ b/include/qom/object_interfaces.h @@ -25,6 +25,8 @@ typedef struct UserCreatable { * UserCreatableClass: * @parent_class: the base class * @complete: callback to be called after @obj's properties are set. + * @can_be_deleted: callback to be called before an object is removed + * to check if @obj can be removed safely. * * Interface is designed to work with -object/object-add/object_add * commands. @@ -47,6 +49,7 @@ typedef struct UserCreatableClass { /* */ void (*complete)(UserCreatable *uc, Error **errp); + bool (*can_be_deleted)(UserCreatable *uc, Error **errp); } UserCreatableClass; /** @@ -59,4 +62,14 @@ typedef struct UserCreatableClass { * nothing. */ void user_creatable_complete(Object *obj, Error **errp); + +/** + * user_creatable_can_be_deleted: + * @uc: the object whose can_be_deleted() method is called if implemented + * @errp: if an error occurs, a pointer to an area to store the error + * + * Wrapper to call can_be_deleted() method if one of types it's inherited + * from implements USER_CREATABLE interface. + */ +bool user_creatable_can_be_deleted(UserCreatable *uc, Error **errp); #endif diff --git a/include/standard-headers/asm-s390/kvm_virtio.h b/include/standard-headers/asm-s390/kvm_virtio.h new file mode 100644 index 0000000..daad324 --- /dev/null +++ b/include/standard-headers/asm-s390/kvm_virtio.h @@ -0,0 +1,64 @@ +/* + * definition for virtio for kvm on s390 + * + * Copyright IBM Corp. 2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License (version 2 only) + * as published by the Free Software Foundation. + * + * Author(s): Christian Borntraeger + */ + +#ifndef __KVM_S390_VIRTIO_H +#define __KVM_S390_VIRTIO_H + +#include "standard-headers/linux/types.h" + +struct kvm_device_desc { + /* The device type: console, network, disk etc. Type 0 terminates. */ + uint8_t type; + /* The number of virtqueues (first in config array) */ + uint8_t num_vq; + /* + * The number of bytes of feature bits. Multiply by 2: one for host + * features and one for guest acknowledgements. + */ + uint8_t feature_len; + /* The number of bytes of the config array after virtqueues. */ + uint8_t config_len; + /* A status byte, written by the Guest. */ + uint8_t status; + uint8_t config[0]; +}; + +/* + * This is how we expect the device configuration field for a virtqueue + * to be laid out in config space. + */ +struct kvm_vqconfig { + /* The token returned with an interrupt. Set by the guest */ + uint64_t token; + /* The address of the virtio ring */ + uint64_t address; + /* The number of entries in the virtio_ring */ + uint16_t num; + +}; + +#define KVM_S390_VIRTIO_NOTIFY 0 +#define KVM_S390_VIRTIO_RESET 1 +#define KVM_S390_VIRTIO_SET_STATUS 2 + +/* The alignment to use between consumer and producer parts of vring. + * This is pagesize for historical reasons. */ +#define KVM_S390_VIRTIO_RING_ALIGN 4096 + + +/* These values are supposed to be in ext_params on an interrupt */ +#define VIRTIO_PARAM_MASK 0xff +#define VIRTIO_PARAM_VRING_INTERRUPT 0x0 +#define VIRTIO_PARAM_CONFIG_CHANGED 0x1 +#define VIRTIO_PARAM_DEV_ADD 0x2 + +#endif diff --git a/include/standard-headers/asm-s390/virtio-ccw.h b/include/standard-headers/asm-s390/virtio-ccw.h new file mode 100644 index 0000000..a9a4ebf --- /dev/null +++ b/include/standard-headers/asm-s390/virtio-ccw.h @@ -0,0 +1,21 @@ +/* + * Definitions for virtio-ccw devices. + * + * Copyright IBM Corp. 2013 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License (version 2 only) + * as published by the Free Software Foundation. + * + * Author(s): Cornelia Huck + */ +#ifndef __KVM_VIRTIO_CCW_H +#define __KVM_VIRTIO_CCW_H + +/* Alignment of vring buffers. */ +#define KVM_VIRTIO_CCW_RING_ALIGN 4096 + +/* Subcode for diagnose 500 (virtio hypercall). */ +#define KVM_S390_VIRTIO_CCW_NOTIFY 3 + +#endif diff --git a/include/standard-headers/linux/if_ether.h b/include/standard-headers/linux/if_ether.h new file mode 100644 index 0000000..91cf735 --- /dev/null +++ b/include/standard-headers/linux/if_ether.h @@ -0,0 +1 @@ +#define ETH_ALEN 6 diff --git a/include/standard-headers/linux/types.h b/include/standard-headers/linux/types.h new file mode 100644 index 0000000..0526c2b --- /dev/null +++ b/include/standard-headers/linux/types.h @@ -0,0 +1,2 @@ +#include +#include "qemu/compiler.h" diff --git a/include/standard-headers/linux/virtio_9p.h b/include/standard-headers/linux/virtio_9p.h new file mode 100644 index 0000000..e68f71d --- /dev/null +++ b/include/standard-headers/linux/virtio_9p.h @@ -0,0 +1,44 @@ +#ifndef _LINUX_VIRTIO_9P_H +#define _LINUX_VIRTIO_9P_H +/* This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. + * + * 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. + * 3. Neither the name of IBM 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 IBM 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 "standard-headers/linux/types.h" +#include "standard-headers/linux/virtio_ids.h" +#include "standard-headers/linux/virtio_config.h" + +/* The feature bitmap for virtio 9P */ + +/* The mount point is specified in a config variable */ +#define VIRTIO_9P_MOUNT_TAG 0 + +struct virtio_9p_config { + /* length of the tag name */ + uint16_t tag_len; + /* non-NULL terminated tag name */ + uint8_t tag[0]; +} QEMU_PACKED; + +#endif /* _LINUX_VIRTIO_9P_H */ diff --git a/include/standard-headers/linux/virtio_balloon.h b/include/standard-headers/linux/virtio_balloon.h new file mode 100644 index 0000000..799376d --- /dev/null +++ b/include/standard-headers/linux/virtio_balloon.h @@ -0,0 +1,59 @@ +#ifndef _LINUX_VIRTIO_BALLOON_H +#define _LINUX_VIRTIO_BALLOON_H +/* This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. + * + * 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. + * 3. Neither the name of IBM 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 IBM 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 "standard-headers/linux/virtio_ids.h" +#include "standard-headers/linux/virtio_config.h" + +/* The feature bitmap for virtio balloon */ +#define VIRTIO_BALLOON_F_MUST_TELL_HOST 0 /* Tell before reclaiming pages */ +#define VIRTIO_BALLOON_F_STATS_VQ 1 /* Memory Stats virtqueue */ +#define VIRTIO_BALLOON_F_DEFLATE_ON_OOM 2 /* Deflate balloon on OOM */ + +/* Size of a PFN in the balloon interface. */ +#define VIRTIO_BALLOON_PFN_SHIFT 12 + +struct virtio_balloon_config { + /* Number of pages host wants Guest to give up. */ + uint32_t num_pages; + /* Number of pages we've actually got in balloon. */ + uint32_t actual; +}; + +#define VIRTIO_BALLOON_S_SWAP_IN 0 /* Amount of memory swapped in */ +#define VIRTIO_BALLOON_S_SWAP_OUT 1 /* Amount of memory swapped out */ +#define VIRTIO_BALLOON_S_MAJFLT 2 /* Number of major faults */ +#define VIRTIO_BALLOON_S_MINFLT 3 /* Number of minor faults */ +#define VIRTIO_BALLOON_S_MEMFREE 4 /* Total amount of free memory */ +#define VIRTIO_BALLOON_S_MEMTOT 5 /* Total amount of memory */ +#define VIRTIO_BALLOON_S_NR 6 + +struct virtio_balloon_stat { + uint16_t tag; + uint64_t val; +} QEMU_PACKED; + +#endif /* _LINUX_VIRTIO_BALLOON_H */ diff --git a/include/standard-headers/linux/virtio_blk.h b/include/standard-headers/linux/virtio_blk.h new file mode 100644 index 0000000..12016b4 --- /dev/null +++ b/include/standard-headers/linux/virtio_blk.h @@ -0,0 +1,143 @@ +#ifndef _LINUX_VIRTIO_BLK_H +#define _LINUX_VIRTIO_BLK_H +/* This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. + * + * 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. + * 3. Neither the name of IBM 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 IBM 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 "standard-headers/linux/types.h" +#include "standard-headers/linux/virtio_ids.h" +#include "standard-headers/linux/virtio_config.h" +#include "standard-headers/linux/virtio_types.h" + +/* Feature bits */ +#define VIRTIO_BLK_F_SIZE_MAX 1 /* Indicates maximum segment size */ +#define VIRTIO_BLK_F_SEG_MAX 2 /* Indicates maximum # of segments */ +#define VIRTIO_BLK_F_GEOMETRY 4 /* Legacy geometry available */ +#define VIRTIO_BLK_F_RO 5 /* Disk is read-only */ +#define VIRTIO_BLK_F_BLK_SIZE 6 /* Block size of disk is available*/ +#define VIRTIO_BLK_F_TOPOLOGY 10 /* Topology information is available */ +#define VIRTIO_BLK_F_MQ 12 /* support more than one vq */ + +/* Legacy feature bits */ +#ifndef VIRTIO_BLK_NO_LEGACY +#define VIRTIO_BLK_F_BARRIER 0 /* Does host support barriers? */ +#define VIRTIO_BLK_F_SCSI 7 /* Supports scsi command passthru */ +#define VIRTIO_BLK_F_WCE 9 /* Writeback mode enabled after reset */ +#define VIRTIO_BLK_F_CONFIG_WCE 11 /* Writeback mode available in config */ +/* Old (deprecated) name for VIRTIO_BLK_F_WCE. */ +#define VIRTIO_BLK_F_FLUSH VIRTIO_BLK_F_WCE +#endif /* !VIRTIO_BLK_NO_LEGACY */ + +#define VIRTIO_BLK_ID_BYTES 20 /* ID string length */ + +struct virtio_blk_config { + /* The capacity (in 512-byte sectors). */ + uint64_t capacity; + /* The maximum segment size (if VIRTIO_BLK_F_SIZE_MAX) */ + uint32_t size_max; + /* The maximum number of segments (if VIRTIO_BLK_F_SEG_MAX) */ + uint32_t seg_max; + /* geometry the device (if VIRTIO_BLK_F_GEOMETRY) */ + struct virtio_blk_geometry { + uint16_t cylinders; + uint8_t heads; + uint8_t sectors; + } geometry; + + /* block size of device (if VIRTIO_BLK_F_BLK_SIZE) */ + uint32_t blk_size; + + /* the next 4 entries are guarded by VIRTIO_BLK_F_TOPOLOGY */ + /* exponent for physical block per logical block. */ + uint8_t physical_block_exp; + /* alignment offset in logical blocks. */ + uint8_t alignment_offset; + /* minimum I/O size without performance penalty in logical blocks. */ + uint16_t min_io_size; + /* optimal sustained I/O size in logical blocks. */ + uint32_t opt_io_size; + + /* writeback mode (if VIRTIO_BLK_F_CONFIG_WCE) */ + uint8_t wce; + uint8_t unused; + + /* number of vqs, only available when VIRTIO_BLK_F_MQ is set */ + uint16_t num_queues; +} QEMU_PACKED; + +/* + * Command types + * + * Usage is a bit tricky as some bits are used as flags and some are not. + * + * Rules: + * VIRTIO_BLK_T_OUT may be combined with VIRTIO_BLK_T_SCSI_CMD or + * VIRTIO_BLK_T_BARRIER. VIRTIO_BLK_T_FLUSH is a command of its own + * and may not be combined with any of the other flags. + */ + +/* These two define direction. */ +#define VIRTIO_BLK_T_IN 0 +#define VIRTIO_BLK_T_OUT 1 + +#ifndef VIRTIO_BLK_NO_LEGACY +/* This bit says it's a scsi command, not an actual read or write. */ +#define VIRTIO_BLK_T_SCSI_CMD 2 +#endif /* VIRTIO_BLK_NO_LEGACY */ + +/* Cache flush command */ +#define VIRTIO_BLK_T_FLUSH 4 + +/* Get device ID command */ +#define VIRTIO_BLK_T_GET_ID 8 + +#ifndef VIRTIO_BLK_NO_LEGACY +/* Barrier before this op. */ +#define VIRTIO_BLK_T_BARRIER 0x80000000 +#endif /* !VIRTIO_BLK_NO_LEGACY */ + +/* This is the first element of the read scatter-gather list. */ +struct virtio_blk_outhdr { + /* VIRTIO_BLK_T* */ + __virtio32 type; + /* io priority. */ + __virtio32 ioprio; + /* Sector (ie. 512 byte offset) */ + __virtio64 sector; +}; + +#ifndef VIRTIO_BLK_NO_LEGACY +struct virtio_scsi_inhdr { + __virtio32 errors; + __virtio32 data_len; + __virtio32 sense_len; + __virtio32 residual; +}; +#endif /* !VIRTIO_BLK_NO_LEGACY */ + +/* And this is the final byte of the write scatter-gather list. */ +#define VIRTIO_BLK_S_OK 0 +#define VIRTIO_BLK_S_IOERR 1 +#define VIRTIO_BLK_S_UNSUPP 2 +#endif /* _LINUX_VIRTIO_BLK_H */ diff --git a/include/standard-headers/linux/virtio_config.h b/include/standard-headers/linux/virtio_config.h new file mode 100644 index 0000000..bcc445b --- /dev/null +++ b/include/standard-headers/linux/virtio_config.h @@ -0,0 +1,64 @@ +#ifndef _LINUX_VIRTIO_CONFIG_H +#define _LINUX_VIRTIO_CONFIG_H +/* This header, excluding the #ifdef __KERNEL__ part, is BSD licensed so + * anyone can use the definitions to implement compatible drivers/servers. + * + * 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. + * 3. Neither the name of IBM 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 IBM 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. */ + +/* Virtio devices use a standardized configuration space to define their + * features and pass configuration information, but each implementation can + * store and access that space differently. */ +#include "standard-headers/linux/types.h" + +/* Status byte for guest to report progress, and synchronize features. */ +/* We have seen device and processed generic fields (VIRTIO_CONFIG_F_VIRTIO) */ +#define VIRTIO_CONFIG_S_ACKNOWLEDGE 1 +/* We have found a driver for the device. */ +#define VIRTIO_CONFIG_S_DRIVER 2 +/* Driver has used its parts of the config, and is happy */ +#define VIRTIO_CONFIG_S_DRIVER_OK 4 +/* Driver has finished configuring features */ +#define VIRTIO_CONFIG_S_FEATURES_OK 8 +/* We've given up on this device. */ +#define VIRTIO_CONFIG_S_FAILED 0x80 + +/* Some virtio feature bits (currently bits 28 through 32) are reserved for the + * transport being used (eg. virtio_ring), the rest are per-device feature + * bits. */ +#define VIRTIO_TRANSPORT_F_START 28 +#define VIRTIO_TRANSPORT_F_END 33 + +#ifndef VIRTIO_CONFIG_NO_LEGACY +/* Do we get callbacks when the ring is completely used, even if we've + * suppressed them? */ +#define VIRTIO_F_NOTIFY_ON_EMPTY 24 + +/* Can the device handle any descriptor layout? */ +#define VIRTIO_F_ANY_LAYOUT 27 +#endif /* VIRTIO_CONFIG_NO_LEGACY */ + +/* v1.0 compliant. */ +#define VIRTIO_F_VERSION_1 32 + +#endif /* _LINUX_VIRTIO_CONFIG_H */ diff --git a/include/standard-headers/linux/virtio_console.h b/include/standard-headers/linux/virtio_console.h new file mode 100644 index 0000000..0dedc9e --- /dev/null +++ b/include/standard-headers/linux/virtio_console.h @@ -0,0 +1,78 @@ +/* + * This header, excluding the #ifdef __KERNEL__ part, is BSD licensed so + * anyone can use the definitions to implement compatible drivers/servers: + * + * + * 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. + * 3. Neither the name of IBM 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 IBM 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. + * + * Copyright (C) Red Hat, Inc., 2009, 2010, 2011 + * Copyright (C) Amit Shah , 2009, 2010, 2011 + */ +#ifndef _LINUX_VIRTIO_CONSOLE_H +#define _LINUX_VIRTIO_CONSOLE_H +#include "standard-headers/linux/types.h" +#include "standard-headers/linux/virtio_types.h" +#include "standard-headers/linux/virtio_ids.h" +#include "standard-headers/linux/virtio_config.h" + +/* Feature bits */ +#define VIRTIO_CONSOLE_F_SIZE 0 /* Does host provide console size? */ +#define VIRTIO_CONSOLE_F_MULTIPORT 1 /* Does host provide multiple ports? */ +#define VIRTIO_CONSOLE_F_EMERG_WRITE 2 /* Does host support emergency write? */ + +#define VIRTIO_CONSOLE_BAD_ID (~(uint32_t)0) + +struct virtio_console_config { + /* colums of the screens */ + uint16_t cols; + /* rows of the screens */ + uint16_t rows; + /* max. number of ports this device can hold */ + uint32_t max_nr_ports; + /* emergency write register */ + uint32_t emerg_wr; +} QEMU_PACKED; + +/* + * A message that's passed between the Host and the Guest for a + * particular port. + */ +struct virtio_console_control { + __virtio32 id; /* Port number */ + __virtio16 event; /* The kind of control event (see below) */ + __virtio16 value; /* Extra information for the key */ +}; + +/* Some events for control messages */ +#define VIRTIO_CONSOLE_DEVICE_READY 0 +#define VIRTIO_CONSOLE_PORT_ADD 1 +#define VIRTIO_CONSOLE_PORT_REMOVE 2 +#define VIRTIO_CONSOLE_PORT_READY 3 +#define VIRTIO_CONSOLE_CONSOLE_PORT 4 +#define VIRTIO_CONSOLE_RESIZE 5 +#define VIRTIO_CONSOLE_PORT_OPEN 6 +#define VIRTIO_CONSOLE_PORT_NAME 7 + + +#endif /* _LINUX_VIRTIO_CONSOLE_H */ diff --git a/include/standard-headers/linux/virtio_ids.h b/include/standard-headers/linux/virtio_ids.h new file mode 100644 index 0000000..284fc3a --- /dev/null +++ b/include/standard-headers/linux/virtio_ids.h @@ -0,0 +1,43 @@ +#ifndef _LINUX_VIRTIO_IDS_H +#define _LINUX_VIRTIO_IDS_H +/* + * Virtio IDs + * + * This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. + * + * 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. + * 3. Neither the name of IBM 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 IBM 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. */ + +#define VIRTIO_ID_NET 1 /* virtio net */ +#define VIRTIO_ID_BLOCK 2 /* virtio block */ +#define VIRTIO_ID_CONSOLE 3 /* virtio console */ +#define VIRTIO_ID_RNG 4 /* virtio rng */ +#define VIRTIO_ID_BALLOON 5 /* virtio balloon */ +#define VIRTIO_ID_RPMSG 7 /* virtio remote processor messaging */ +#define VIRTIO_ID_SCSI 8 /* virtio scsi */ +#define VIRTIO_ID_9P 9 /* 9p virtio console */ +#define VIRTIO_ID_RPROC_SERIAL 11 /* virtio remoteproc serial link */ +#define VIRTIO_ID_CAIF 12 /* Virtio caif */ + +#endif /* _LINUX_VIRTIO_IDS_H */ diff --git a/include/standard-headers/linux/virtio_net.h b/include/standard-headers/linux/virtio_net.h new file mode 100644 index 0000000..3209c90 --- /dev/null +++ b/include/standard-headers/linux/virtio_net.h @@ -0,0 +1,229 @@ +#ifndef _LINUX_VIRTIO_NET_H +#define _LINUX_VIRTIO_NET_H +/* This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. + * + * 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. + * 3. Neither the name of IBM 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 IBM 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 "standard-headers/linux/types.h" +#include "standard-headers/linux/virtio_ids.h" +#include "standard-headers/linux/virtio_config.h" +#include "standard-headers/linux/virtio_types.h" +#include "standard-headers/linux/if_ether.h" + +/* The feature bitmap for virtio net */ +#define VIRTIO_NET_F_CSUM 0 /* Host handles pkts w/ partial csum */ +#define VIRTIO_NET_F_GUEST_CSUM 1 /* Guest handles pkts w/ partial csum */ +#define VIRTIO_NET_F_MAC 5 /* Host has given MAC address. */ +#define VIRTIO_NET_F_GUEST_TSO4 7 /* Guest can handle TSOv4 in. */ +#define VIRTIO_NET_F_GUEST_TSO6 8 /* Guest can handle TSOv6 in. */ +#define VIRTIO_NET_F_GUEST_ECN 9 /* Guest can handle TSO[6] w/ ECN in. */ +#define VIRTIO_NET_F_GUEST_UFO 10 /* Guest can handle UFO in. */ +#define VIRTIO_NET_F_HOST_TSO4 11 /* Host can handle TSOv4 in. */ +#define VIRTIO_NET_F_HOST_TSO6 12 /* Host can handle TSOv6 in. */ +#define VIRTIO_NET_F_HOST_ECN 13 /* Host can handle TSO[6] w/ ECN in. */ +#define VIRTIO_NET_F_HOST_UFO 14 /* Host can handle UFO in. */ +#define VIRTIO_NET_F_MRG_RXBUF 15 /* Host can merge receive buffers. */ +#define VIRTIO_NET_F_STATUS 16 /* virtio_net_config.status available */ +#define VIRTIO_NET_F_CTRL_VQ 17 /* Control channel available */ +#define VIRTIO_NET_F_CTRL_RX 18 /* Control channel RX mode support */ +#define VIRTIO_NET_F_CTRL_VLAN 19 /* Control channel VLAN filtering */ +#define VIRTIO_NET_F_CTRL_RX_EXTRA 20 /* Extra RX mode control support */ +#define VIRTIO_NET_F_GUEST_ANNOUNCE 21 /* Guest can announce device on the + * network */ +#define VIRTIO_NET_F_MQ 22 /* Device supports Receive Flow + * Steering */ +#define VIRTIO_NET_F_CTRL_MAC_ADDR 23 /* Set MAC address */ + +#ifndef VIRTIO_NET_NO_LEGACY +#define VIRTIO_NET_F_GSO 6 /* Host handles pkts w/ any GSO type */ +#endif /* VIRTIO_NET_NO_LEGACY */ + +#define VIRTIO_NET_S_LINK_UP 1 /* Link is up */ +#define VIRTIO_NET_S_ANNOUNCE 2 /* Announcement is needed */ + +struct virtio_net_config { + /* The config defining mac address (if VIRTIO_NET_F_MAC) */ + uint8_t mac[ETH_ALEN]; + /* See VIRTIO_NET_F_STATUS and VIRTIO_NET_S_* above */ + uint16_t status; + /* Maximum number of each of transmit and receive queues; + * see VIRTIO_NET_F_MQ and VIRTIO_NET_CTRL_MQ. + * Legal values are between 1 and 0x8000 + */ + uint16_t max_virtqueue_pairs; +} QEMU_PACKED; + +/* + * This header comes first in the scatter-gather list. If you don't + * specify GSO or CSUM features, you can simply ignore the header. + * + * This is bitwise-equivalent to the legacy struct virtio_net_hdr_mrg_rxbuf, + * only flattened. + */ +struct virtio_net_hdr_v1 { +#define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 /* Use csum_start, csum_offset */ +#define VIRTIO_NET_HDR_F_DATA_VALID 2 /* Csum is valid */ + uint8_t flags; +#define VIRTIO_NET_HDR_GSO_NONE 0 /* Not a GSO frame */ +#define VIRTIO_NET_HDR_GSO_TCPV4 1 /* GSO frame, IPv4 TCP (TSO) */ +#define VIRTIO_NET_HDR_GSO_UDP 3 /* GSO frame, IPv4 UDP (UFO) */ +#define VIRTIO_NET_HDR_GSO_TCPV6 4 /* GSO frame, IPv6 TCP */ +#define VIRTIO_NET_HDR_GSO_ECN 0x80 /* TCP has ECN set */ + uint8_t gso_type; + __virtio16 hdr_len; /* Ethernet + IP + tcp/udp hdrs */ + __virtio16 gso_size; /* Bytes to append to hdr_len per frame */ + __virtio16 csum_start; /* Position to start checksumming from */ + __virtio16 csum_offset; /* Offset after that to place checksum */ + __virtio16 num_buffers; /* Number of merged rx buffers */ +}; + +#ifndef VIRTIO_NET_NO_LEGACY +/* This header comes first in the scatter-gather list. + * For legacy virtio, if VIRTIO_F_ANY_LAYOUT is not negotiated, it must + * be the first element of the scatter-gather list. If you don't + * specify GSO or CSUM features, you can simply ignore the header. */ +struct virtio_net_hdr { + /* See VIRTIO_NET_HDR_F_* */ + uint8_t flags; + /* See VIRTIO_NET_HDR_GSO_* */ + uint8_t gso_type; + __virtio16 hdr_len; /* Ethernet + IP + tcp/udp hdrs */ + __virtio16 gso_size; /* Bytes to append to hdr_len per frame */ + __virtio16 csum_start; /* Position to start checksumming from */ + __virtio16 csum_offset; /* Offset after that to place checksum */ +}; + +/* This is the version of the header to use when the MRG_RXBUF + * feature has been negotiated. */ +struct virtio_net_hdr_mrg_rxbuf { + struct virtio_net_hdr hdr; + __virtio16 num_buffers; /* Number of merged rx buffers */ +}; +#endif /* ...VIRTIO_NET_NO_LEGACY */ + +/* + * Control virtqueue data structures + * + * The control virtqueue expects a header in the first sg entry + * and an ack/status response in the last entry. Data for the + * command goes in between. + */ +struct virtio_net_ctrl_hdr { + uint8_t class; + uint8_t cmd; +} QEMU_PACKED; + +typedef uint8_t virtio_net_ctrl_ack; + +#define VIRTIO_NET_OK 0 +#define VIRTIO_NET_ERR 1 + +/* + * Control the RX mode, ie. promisucous, allmulti, etc... + * All commands require an "out" sg entry containing a 1 byte + * state value, zero = disable, non-zero = enable. Commands + * 0 and 1 are supported with the VIRTIO_NET_F_CTRL_RX feature. + * Commands 2-5 are added with VIRTIO_NET_F_CTRL_RX_EXTRA. + */ +#define VIRTIO_NET_CTRL_RX 0 + #define VIRTIO_NET_CTRL_RX_PROMISC 0 + #define VIRTIO_NET_CTRL_RX_ALLMULTI 1 + #define VIRTIO_NET_CTRL_RX_ALLUNI 2 + #define VIRTIO_NET_CTRL_RX_NOMULTI 3 + #define VIRTIO_NET_CTRL_RX_NOUNI 4 + #define VIRTIO_NET_CTRL_RX_NOBCAST 5 + +/* + * Control the MAC + * + * The MAC filter table is managed by the hypervisor, the guest should + * assume the size is infinite. Filtering should be considered + * non-perfect, ie. based on hypervisor resources, the guest may + * received packets from sources not specified in the filter list. + * + * In addition to the class/cmd header, the TABLE_SET command requires + * two out scatterlists. Each contains a 4 byte count of entries followed + * by a concatenated byte stream of the ETH_ALEN MAC addresses. The + * first sg list contains unicast addresses, the second is for multicast. + * This functionality is present if the VIRTIO_NET_F_CTRL_RX feature + * is available. + * + * The ADDR_SET command requests one out scatterlist, it contains a + * 6 bytes MAC address. This functionality is present if the + * VIRTIO_NET_F_CTRL_MAC_ADDR feature is available. + */ +struct virtio_net_ctrl_mac { + __virtio32 entries; + uint8_t macs[][ETH_ALEN]; +} QEMU_PACKED; + +#define VIRTIO_NET_CTRL_MAC 1 + #define VIRTIO_NET_CTRL_MAC_TABLE_SET 0 + #define VIRTIO_NET_CTRL_MAC_ADDR_SET 1 + +/* + * Control VLAN filtering + * + * The VLAN filter table is controlled via a simple ADD/DEL interface. + * VLAN IDs not added may be filterd by the hypervisor. Del is the + * opposite of add. Both commands expect an out entry containing a 2 + * byte VLAN ID. VLAN filterting is available with the + * VIRTIO_NET_F_CTRL_VLAN feature bit. + */ +#define VIRTIO_NET_CTRL_VLAN 2 + #define VIRTIO_NET_CTRL_VLAN_ADD 0 + #define VIRTIO_NET_CTRL_VLAN_DEL 1 + +/* + * Control link announce acknowledgement + * + * The command VIRTIO_NET_CTRL_ANNOUNCE_ACK is used to indicate that + * driver has recevied the notification; device would clear the + * VIRTIO_NET_S_ANNOUNCE bit in the status field after it receives + * this command. + */ +#define VIRTIO_NET_CTRL_ANNOUNCE 3 + #define VIRTIO_NET_CTRL_ANNOUNCE_ACK 0 + +/* + * Control Receive Flow Steering + * + * The command VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET + * enables Receive Flow Steering, specifying the number of the transmit and + * receive queues that will be used. After the command is consumed and acked by + * the device, the device will not steer new packets on receive virtqueues + * other than specified nor read from transmit virtqueues other than specified. + * Accordingly, driver should not transmit new packets on virtqueues other than + * specified. + */ +struct virtio_net_ctrl_mq { + __virtio16 virtqueue_pairs; +}; + +#define VIRTIO_NET_CTRL_MQ 4 + #define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET 0 + #define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN 1 + #define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX 0x8000 + +#endif /* _LINUX_VIRTIO_NET_H */ diff --git a/include/standard-headers/linux/virtio_pci.h b/include/standard-headers/linux/virtio_pci.h new file mode 100644 index 0000000..ecdc133 --- /dev/null +++ b/include/standard-headers/linux/virtio_pci.h @@ -0,0 +1,193 @@ +/* + * Virtio PCI driver + * + * This module allows virtio devices to be used over a virtual PCI device. + * This can be used with QEMU based VMMs like KVM or Xen. + * + * Copyright IBM Corp. 2007 + * + * Authors: + * Anthony Liguori + * + * This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. + * + * 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. + * 3. Neither the name of IBM 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 IBM 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 _LINUX_VIRTIO_PCI_H +#define _LINUX_VIRTIO_PCI_H + +#include "standard-headers/linux/types.h" + +#ifndef VIRTIO_PCI_NO_LEGACY + +/* A 32-bit r/o bitmask of the features supported by the host */ +#define VIRTIO_PCI_HOST_FEATURES 0 + +/* A 32-bit r/w bitmask of features activated by the guest */ +#define VIRTIO_PCI_GUEST_FEATURES 4 + +/* A 32-bit r/w PFN for the currently selected queue */ +#define VIRTIO_PCI_QUEUE_PFN 8 + +/* A 16-bit r/o queue size for the currently selected queue */ +#define VIRTIO_PCI_QUEUE_NUM 12 + +/* A 16-bit r/w queue selector */ +#define VIRTIO_PCI_QUEUE_SEL 14 + +/* A 16-bit r/w queue notifier */ +#define VIRTIO_PCI_QUEUE_NOTIFY 16 + +/* An 8-bit device status register. */ +#define VIRTIO_PCI_STATUS 18 + +/* An 8-bit r/o interrupt status register. Reading the value will return the + * current contents of the ISR and will also clear it. This is effectively + * a read-and-acknowledge. */ +#define VIRTIO_PCI_ISR 19 + +/* MSI-X registers: only enabled if MSI-X is enabled. */ +/* A 16-bit vector for configuration changes. */ +#define VIRTIO_MSI_CONFIG_VECTOR 20 +/* A 16-bit vector for selected queue notifications. */ +#define VIRTIO_MSI_QUEUE_VECTOR 22 + +/* The remaining space is defined by each driver as the per-driver + * configuration space */ +#define VIRTIO_PCI_CONFIG_OFF(msix_enabled) ((msix_enabled) ? 24 : 20) +/* Deprecated: please use VIRTIO_PCI_CONFIG_OFF instead */ +#define VIRTIO_PCI_CONFIG(dev) VIRTIO_PCI_CONFIG_OFF((dev)->msix_enabled) + +/* Virtio ABI version, this must match exactly */ +#define VIRTIO_PCI_ABI_VERSION 0 + +/* How many bits to shift physical queue address written to QUEUE_PFN. + * 12 is historical, and due to x86 page size. */ +#define VIRTIO_PCI_QUEUE_ADDR_SHIFT 12 + +/* The alignment to use between consumer and producer parts of vring. + * x86 pagesize again. */ +#define VIRTIO_PCI_VRING_ALIGN 4096 + +#endif /* VIRTIO_PCI_NO_LEGACY */ + +/* The bit of the ISR which indicates a device configuration change. */ +#define VIRTIO_PCI_ISR_CONFIG 0x2 +/* Vector value used to disable MSI for queue */ +#define VIRTIO_MSI_NO_VECTOR 0xffff + +#ifndef VIRTIO_PCI_NO_MODERN + +/* IDs for different capabilities. Must all exist. */ + +/* Common configuration */ +#define VIRTIO_PCI_CAP_COMMON_CFG 1 +/* Notifications */ +#define VIRTIO_PCI_CAP_NOTIFY_CFG 2 +/* ISR access */ +#define VIRTIO_PCI_CAP_ISR_CFG 3 +/* Device specific configuration */ +#define VIRTIO_PCI_CAP_DEVICE_CFG 4 +/* PCI configuration access */ +#define VIRTIO_PCI_CAP_PCI_CFG 5 + +/* This is the PCI capability header: */ +struct virtio_pci_cap { + uint8_t cap_vndr; /* Generic PCI field: PCI_CAP_ID_VNDR */ + uint8_t cap_next; /* Generic PCI field: next ptr. */ + uint8_t cap_len; /* Generic PCI field: capability length */ + uint8_t cfg_type; /* Identifies the structure. */ + uint8_t bar; /* Where to find it. */ + uint8_t padding[3]; /* Pad to full dword. */ + uint32_t offset; /* Offset within bar. */ + uint32_t length; /* Length of the structure, in bytes. */ +}; + +struct virtio_pci_notify_cap { + struct virtio_pci_cap cap; + uint32_t notify_off_multiplier; /* Multiplier for queue_notify_off. */ +}; + +/* Fields in VIRTIO_PCI_CAP_COMMON_CFG: */ +struct virtio_pci_common_cfg { + /* About the whole device. */ + uint32_t device_feature_select; /* read-write */ + uint32_t device_feature; /* read-only */ + uint32_t guest_feature_select; /* read-write */ + uint32_t guest_feature; /* read-write */ + uint16_t msix_config; /* read-write */ + uint16_t num_queues; /* read-only */ + uint8_t device_status; /* read-write */ + uint8_t config_generation; /* read-only */ + + /* About a specific virtqueue. */ + uint16_t queue_select; /* read-write */ + uint16_t queue_size; /* read-write, power of 2. */ + uint16_t queue_msix_vector; /* read-write */ + uint16_t queue_enable; /* read-write */ + uint16_t queue_notify_off; /* read-only */ + uint32_t queue_desc_lo; /* read-write */ + uint32_t queue_desc_hi; /* read-write */ + uint32_t queue_avail_lo; /* read-write */ + uint32_t queue_avail_hi; /* read-write */ + uint32_t queue_used_lo; /* read-write */ + uint32_t queue_used_hi; /* read-write */ +}; + +/* Macro versions of offsets for the Old Timers! */ +#define VIRTIO_PCI_CAP_VNDR 0 +#define VIRTIO_PCI_CAP_NEXT 1 +#define VIRTIO_PCI_CAP_LEN 2 +#define VIRTIO_PCI_CAP_CFG_TYPE 3 +#define VIRTIO_PCI_CAP_BAR 4 +#define VIRTIO_PCI_CAP_OFFSET 8 +#define VIRTIO_PCI_CAP_LENGTH 12 + +#define VIRTIO_PCI_NOTIFY_CAP_MULT 16 + +#define VIRTIO_PCI_COMMON_DFSELECT 0 +#define VIRTIO_PCI_COMMON_DF 4 +#define VIRTIO_PCI_COMMON_GFSELECT 8 +#define VIRTIO_PCI_COMMON_GF 12 +#define VIRTIO_PCI_COMMON_MSIX 16 +#define VIRTIO_PCI_COMMON_NUMQ 18 +#define VIRTIO_PCI_COMMON_STATUS 20 +#define VIRTIO_PCI_COMMON_CFGGENERATION 21 +#define VIRTIO_PCI_COMMON_Q_SELECT 22 +#define VIRTIO_PCI_COMMON_Q_SIZE 24 +#define VIRTIO_PCI_COMMON_Q_MSIX 26 +#define VIRTIO_PCI_COMMON_Q_ENABLE 28 +#define VIRTIO_PCI_COMMON_Q_NOFF 30 +#define VIRTIO_PCI_COMMON_Q_DESCLO 32 +#define VIRTIO_PCI_COMMON_Q_DESCHI 36 +#define VIRTIO_PCI_COMMON_Q_AVAILLO 40 +#define VIRTIO_PCI_COMMON_Q_AVAILHI 44 +#define VIRTIO_PCI_COMMON_Q_USEDLO 48 +#define VIRTIO_PCI_COMMON_Q_USEDHI 52 + +#endif /* VIRTIO_PCI_NO_MODERN */ + +#endif diff --git a/include/hw/virtio/virtio_ring.h b/include/standard-headers/linux/virtio_ring.h similarity index 61% rename from include/hw/virtio/virtio_ring.h rename to include/standard-headers/linux/virtio_ring.h index 0b42e6e..cc647d6 100644 --- a/include/hw/virtio/virtio_ring.h +++ b/include/standard-headers/linux/virtio_ring.h @@ -1,10 +1,5 @@ #ifndef _LINUX_VIRTIO_RING_H #define _LINUX_VIRTIO_RING_H -/* - * This file is copied from /usr/include/linux while converting __uNN types - * to uXX_t, __inline__ to inline, and tab to spaces. - * */ - /* An interface for efficient virtio implementation, currently for use by KVM * and lguest, but hopefully others soon. Do NOT change this since it will * break existing servers and clients. @@ -36,118 +31,127 @@ * SUCH DAMAGE. * * Copyright Rusty Russell IBM Corporation 2007. */ +#include "standard-headers/linux/types.h" +#include "standard-headers/linux/virtio_types.h" /* This marks a buffer as continuing via the next field. */ -#define VRING_DESC_F_NEXT 1 +#define VRING_DESC_F_NEXT 1 /* This marks a buffer as write-only (otherwise read-only). */ -#define VRING_DESC_F_WRITE 2 +#define VRING_DESC_F_WRITE 2 /* This means the buffer contains a list of buffer descriptors. */ -#define VRING_DESC_F_INDIRECT 4 +#define VRING_DESC_F_INDIRECT 4 /* The Host uses this in used->flags to advise the Guest: don't kick me when * you add a buffer. It's unreliable, so it's simply an optimization. Guest * will still kick if it's out of buffers. */ -#define VRING_USED_F_NO_NOTIFY 1 +#define VRING_USED_F_NO_NOTIFY 1 /* The Guest uses this in avail->flags to advise the Host: don't interrupt me * when you consume a buffer. It's unreliable, so it's simply an * optimization. */ -#define VRING_AVAIL_F_NO_INTERRUPT 1 +#define VRING_AVAIL_F_NO_INTERRUPT 1 /* We support indirect buffer descriptors */ -#define VIRTIO_RING_F_INDIRECT_DESC 28 +#define VIRTIO_RING_F_INDIRECT_DESC 28 /* The Guest publishes the used index for which it expects an interrupt * at the end of the avail ring. Host should ignore the avail->flags field. */ /* The Host publishes the avail index for which it expects a kick * at the end of the used ring. Guest should ignore the used->flags field. */ -#define VIRTIO_RING_F_EVENT_IDX 29 +#define VIRTIO_RING_F_EVENT_IDX 29 /* Virtio ring descriptors: 16 bytes. These can chain together via "next". */ struct vring_desc { - /* Address (guest-physical). */ - uint64_t addr; - /* Length. */ - uint32_t len; - /* The flags as indicated above. */ - uint16_t flags; - /* We chain unused descriptors via this, too */ - uint16_t next; + /* Address (guest-physical). */ + __virtio64 addr; + /* Length. */ + __virtio32 len; + /* The flags as indicated above. */ + __virtio16 flags; + /* We chain unused descriptors via this, too */ + __virtio16 next; }; struct vring_avail { - uint16_t flags; - uint16_t idx; - uint16_t ring[]; + __virtio16 flags; + __virtio16 idx; + __virtio16 ring[]; }; /* u32 is used here for ids for padding reasons. */ struct vring_used_elem { - /* Index of start of used descriptor chain. */ - uint32_t id; - /* Total length of the descriptor chain which was used (written to) */ - uint32_t len; + /* Index of start of used descriptor chain. */ + __virtio32 id; + /* Total length of the descriptor chain which was used (written to) */ + __virtio32 len; }; struct vring_used { - uint16_t flags; - uint16_t idx; - struct vring_used_elem ring[]; + __virtio16 flags; + __virtio16 idx; + struct vring_used_elem ring[]; }; struct vring { - unsigned int num; + unsigned int num; - struct vring_desc *desc; + struct vring_desc *desc; - struct vring_avail *avail; + struct vring_avail *avail; - struct vring_used *used; + struct vring_used *used; }; +/* Alignment requirements for vring elements. + * When using pre-virtio 1.0 layout, these fall out naturally. + */ +#define VRING_AVAIL_ALIGN_SIZE 2 +#define VRING_USED_ALIGN_SIZE 4 +#define VRING_DESC_ALIGN_SIZE 16 + /* The standard layout for the ring is a continuous chunk of memory which looks * like this. We assume num is a power of 2. * * struct vring * { - * // The actual descriptors (16 bytes each) - * struct vring_desc desc[num]; + * // The actual descriptors (16 bytes each) + * struct vring_desc desc[num]; * - * // A ring of available descriptor heads with free-running index. - * uint16_t avail_flags; - * uint16_t avail_idx; - * uint16_t available[num]; - * uint16_t used_event_idx; + * // A ring of available descriptor heads with free-running index. + * __virtio16 avail_flags; + * __virtio16 avail_idx; + * __virtio16 available[num]; + * __virtio16 used_event_idx; * - * // Padding to the next align boundary. - * char pad[]; + * // Padding to the next align boundary. + * char pad[]; * - * // A ring of used descriptor heads with free-running index. - * uint16_t used_flags; - * uint16_t used_idx; - * struct vring_used_elem used[num]; - * uint16_t avail_event_idx; + * // A ring of used descriptor heads with free-running index. + * __virtio16 used_flags; + * __virtio16 used_idx; + * struct vring_used_elem used[num]; + * __virtio16 avail_event_idx; * }; */ /* We publish the used event index at the end of the available ring, and vice * versa. They are at the end for backwards compatibility. */ #define vring_used_event(vr) ((vr)->avail->ring[(vr)->num]) -#define vring_avail_event(vr) (*(uint16_t *)&(vr)->used->ring[(vr)->num]) +#define vring_avail_event(vr) (*(__virtio16 *)&(vr)->used->ring[(vr)->num]) static inline void vring_init(struct vring *vr, unsigned int num, void *p, - unsigned long align) + unsigned long align) { - vr->num = num; - vr->desc = p; - vr->avail = p + num*sizeof(struct vring_desc); - vr->used = (void *)(((uintptr_t)&vr->avail->ring[num] + sizeof(uint16_t) - + align - 1) & ~(align - 1)); + vr->num = num; + vr->desc = p; + vr->avail = p + num*sizeof(struct vring_desc); + vr->used = (void *)(((unsigned long)&vr->avail->ring[num] + sizeof(__virtio16) + + align-1) & ~(align - 1)); } static inline unsigned vring_size(unsigned int num, unsigned long align) { - return ((sizeof(struct vring_desc) * num + sizeof(uint16_t) * (3 + num) - + align - 1) & ~(align - 1)) - + sizeof(uint16_t) * 3 + sizeof(struct vring_used_elem) * num; + return ((sizeof(struct vring_desc) * num + sizeof(__virtio16) * (3 + num) + + align - 1) & ~(align - 1)) + + sizeof(__virtio16) * 3 + sizeof(struct vring_used_elem) * num; } /* The following is used with USED_EVENT_IDX and AVAIL_EVENT_IDX */ @@ -156,12 +160,12 @@ static inline unsigned vring_size(unsigned int num, unsigned long align) * should we trigger an event? */ static inline int vring_need_event(uint16_t event_idx, uint16_t new_idx, uint16_t old) { - /* Note: Xen has similar logic for notification hold-off - * in include/xen/interface/io/ring.h with req_event and req_prod - * corresponding to event_idx + 1 and new_idx respectively. - * Note also that req_event and req_prod in Xen start at 1, - * event indexes in virtio start at 0. */ - return (uint16_t)(new_idx - event_idx - 1) < (uint16_t)(new_idx - old); + /* Note: Xen has similar logic for notification hold-off + * in include/xen/interface/io/ring.h with req_event and req_prod + * corresponding to event_idx + 1 and new_idx respectively. + * Note also that req_event and req_prod in Xen start at 1, + * event indexes in virtio start at 0. */ + return (uint16_t)(new_idx - event_idx - 1) < (uint16_t)(new_idx - old); } #endif /* _LINUX_VIRTIO_RING_H */ diff --git a/include/standard-headers/linux/virtio_rng.h b/include/standard-headers/linux/virtio_rng.h new file mode 100644 index 0000000..60fc798 --- /dev/null +++ b/include/standard-headers/linux/virtio_rng.h @@ -0,0 +1,8 @@ +#ifndef _LINUX_VIRTIO_RNG_H +#define _LINUX_VIRTIO_RNG_H +/* This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. */ +#include "standard-headers/linux/virtio_ids.h" +#include "standard-headers/linux/virtio_config.h" + +#endif /* _LINUX_VIRTIO_RNG_H */ diff --git a/include/standard-headers/linux/virtio_scsi.h b/include/standard-headers/linux/virtio_scsi.h new file mode 100644 index 0000000..ab66166 --- /dev/null +++ b/include/standard-headers/linux/virtio_scsi.h @@ -0,0 +1,172 @@ +/* + * This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. + * + * 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 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 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 _LINUX_VIRTIO_SCSI_H +#define _LINUX_VIRTIO_SCSI_H + +#include "standard-headers/linux/virtio_types.h" + +/* Default values of the CDB and sense data size configuration fields */ +#define VIRTIO_SCSI_CDB_DEFAULT_SIZE 32 +#define VIRTIO_SCSI_SENSE_DEFAULT_SIZE 96 + +#ifndef VIRTIO_SCSI_CDB_SIZE +#define VIRTIO_SCSI_CDB_SIZE VIRTIO_SCSI_CDB_DEFAULT_SIZE +#endif +#ifndef VIRTIO_SCSI_SENSE_SIZE +#define VIRTIO_SCSI_SENSE_SIZE VIRTIO_SCSI_SENSE_DEFAULT_SIZE +#endif + +/* SCSI command request, followed by data-out */ +struct virtio_scsi_cmd_req { + uint8_t lun[8]; /* Logical Unit Number */ + __virtio64 tag; /* Command identifier */ + uint8_t task_attr; /* Task attribute */ + uint8_t prio; /* SAM command priority field */ + uint8_t crn; + uint8_t cdb[VIRTIO_SCSI_CDB_SIZE]; +} QEMU_PACKED; + +/* SCSI command request, followed by protection information */ +struct virtio_scsi_cmd_req_pi { + uint8_t lun[8]; /* Logical Unit Number */ + __virtio64 tag; /* Command identifier */ + uint8_t task_attr; /* Task attribute */ + uint8_t prio; /* SAM command priority field */ + uint8_t crn; + __virtio32 pi_bytesout; /* DataOUT PI Number of bytes */ + __virtio32 pi_bytesin; /* DataIN PI Number of bytes */ + uint8_t cdb[VIRTIO_SCSI_CDB_SIZE]; +} QEMU_PACKED; + +/* Response, followed by sense data and data-in */ +struct virtio_scsi_cmd_resp { + __virtio32 sense_len; /* Sense data length */ + __virtio32 resid; /* Residual bytes in data buffer */ + __virtio16 status_qualifier; /* Status qualifier */ + uint8_t status; /* Command completion status */ + uint8_t response; /* Response values */ + uint8_t sense[VIRTIO_SCSI_SENSE_SIZE]; +} QEMU_PACKED; + +/* Task Management Request */ +struct virtio_scsi_ctrl_tmf_req { + __virtio32 type; + __virtio32 subtype; + uint8_t lun[8]; + __virtio64 tag; +} QEMU_PACKED; + +struct virtio_scsi_ctrl_tmf_resp { + uint8_t response; +} QEMU_PACKED; + +/* Asynchronous notification query/subscription */ +struct virtio_scsi_ctrl_an_req { + __virtio32 type; + uint8_t lun[8]; + __virtio32 event_requested; +} QEMU_PACKED; + +struct virtio_scsi_ctrl_an_resp { + __virtio32 event_actual; + uint8_t response; +} QEMU_PACKED; + +struct virtio_scsi_event { + __virtio32 event; + uint8_t lun[8]; + __virtio32 reason; +} QEMU_PACKED; + +struct virtio_scsi_config { + uint32_t num_queues; + uint32_t seg_max; + uint32_t max_sectors; + uint32_t cmd_per_lun; + uint32_t event_info_size; + uint32_t sense_size; + uint32_t cdb_size; + uint16_t max_channel; + uint16_t max_target; + uint32_t max_lun; +} QEMU_PACKED; + +/* Feature Bits */ +#define VIRTIO_SCSI_F_INOUT 0 +#define VIRTIO_SCSI_F_HOTPLUG 1 +#define VIRTIO_SCSI_F_CHANGE 2 +#define VIRTIO_SCSI_F_T10_PI 3 + +/* Response codes */ +#define VIRTIO_SCSI_S_OK 0 +#define VIRTIO_SCSI_S_OVERRUN 1 +#define VIRTIO_SCSI_S_ABORTED 2 +#define VIRTIO_SCSI_S_BAD_TARGET 3 +#define VIRTIO_SCSI_S_RESET 4 +#define VIRTIO_SCSI_S_BUSY 5 +#define VIRTIO_SCSI_S_TRANSPORT_FAILURE 6 +#define VIRTIO_SCSI_S_TARGET_FAILURE 7 +#define VIRTIO_SCSI_S_NEXUS_FAILURE 8 +#define VIRTIO_SCSI_S_FAILURE 9 +#define VIRTIO_SCSI_S_FUNCTION_SUCCEEDED 10 +#define VIRTIO_SCSI_S_FUNCTION_REJECTED 11 +#define VIRTIO_SCSI_S_INCORRECT_LUN 12 + +/* Controlq type codes. */ +#define VIRTIO_SCSI_T_TMF 0 +#define VIRTIO_SCSI_T_AN_QUERY 1 +#define VIRTIO_SCSI_T_AN_SUBSCRIBE 2 + +/* Valid TMF subtypes. */ +#define VIRTIO_SCSI_T_TMF_ABORT_TASK 0 +#define VIRTIO_SCSI_T_TMF_ABORT_TASK_SET 1 +#define VIRTIO_SCSI_T_TMF_CLEAR_ACA 2 +#define VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET 3 +#define VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET 4 +#define VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET 5 +#define VIRTIO_SCSI_T_TMF_QUERY_TASK 6 +#define VIRTIO_SCSI_T_TMF_QUERY_TASK_SET 7 + +/* Events. */ +#define VIRTIO_SCSI_T_EVENTS_MISSED 0x80000000 +#define VIRTIO_SCSI_T_NO_EVENT 0 +#define VIRTIO_SCSI_T_TRANSPORT_RESET 1 +#define VIRTIO_SCSI_T_ASYNC_NOTIFY 2 +#define VIRTIO_SCSI_T_PARAM_CHANGE 3 + +/* Reasons of transport reset event */ +#define VIRTIO_SCSI_EVT_RESET_HARD 0 +#define VIRTIO_SCSI_EVT_RESET_RESCAN 1 +#define VIRTIO_SCSI_EVT_RESET_REMOVED 2 + +#define VIRTIO_SCSI_S_SIMPLE 0 +#define VIRTIO_SCSI_S_ORDERED 1 +#define VIRTIO_SCSI_S_HEAD 2 +#define VIRTIO_SCSI_S_ACA 3 + + +#endif /* _LINUX_VIRTIO_SCSI_H */ diff --git a/include/standard-headers/linux/virtio_types.h b/include/standard-headers/linux/virtio_types.h new file mode 100644 index 0000000..fd0d351 --- /dev/null +++ b/include/standard-headers/linux/virtio_types.h @@ -0,0 +1,46 @@ +#ifndef _LINUX_VIRTIO_TYPES_H +#define _LINUX_VIRTIO_TYPES_H +/* Type definitions for virtio implementations. + * + * This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. + * + * 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. + * 3. Neither the name of IBM 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 IBM 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. + * + * Copyright (C) 2014 Red Hat, Inc. + * Author: Michael S. Tsirkin + */ +#include "standard-headers/linux/types.h" + +/* + * __virtio{16,32,64} have the following meaning: + * - __u{16,32,64} for virtio devices in legacy mode, accessed in native endian + * - __le{16,32,64} for standard-compliant virtio devices + */ + +typedef uint16_t __virtio16; +typedef uint32_t __virtio32; +typedef uint64_t __virtio64; + +#endif /* _LINUX_VIRTIO_TYPES_H */ diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index 52d13c1..77e9b9c 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -62,6 +62,9 @@ typedef struct BlockDevOps { BlockBackend *blk_new(const char *name, Error **errp); BlockBackend *blk_new_with_bs(const char *name, Error **errp); +BlockBackend *blk_new_open(const char *name, const char *filename, + const char *reference, QDict *options, int flags, + Error **errp); void blk_ref(BlockBackend *blk); void blk_unref(BlockBackend *blk); const char *blk_name(BlockBackend *blk); @@ -70,7 +73,7 @@ BlockBackend *blk_next(BlockBackend *blk); BlockDriverState *blk_bs(BlockBackend *blk); -void blk_hide_on_behalf_of_do_drive_del(BlockBackend *blk); +void blk_hide_on_behalf_of_hmp_drive_del(BlockBackend *blk); void blk_iostatus_enable(BlockBackend *blk); int blk_attach_dev(BlockBackend *blk, void *dev); @@ -91,6 +94,7 @@ int blk_pread(BlockBackend *blk, int64_t offset, void *buf, int count); int blk_pwrite(BlockBackend *blk, int64_t offset, const void *buf, int count); int64_t blk_getlength(BlockBackend *blk); void blk_get_geometry(BlockBackend *blk, uint64_t *nb_sectors_ptr); +int64_t blk_nb_sectors(BlockBackend *blk); BlockAIOCB *blk_aio_readv(BlockBackend *blk, int64_t sector_num, QEMUIOVector *iov, int nb_sectors, BlockCompletionFunc *cb, void *opaque); @@ -108,6 +112,8 @@ int blk_aio_multiwrite(BlockBackend *blk, BlockRequest *reqs, int num_reqs); int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf); BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf, BlockCompletionFunc *cb, void *opaque); +int blk_co_discard(BlockBackend *blk, int64_t sector_num, int nb_sectors); +int blk_co_flush(BlockBackend *blk); int blk_flush(BlockBackend *blk); int blk_flush_all(void); void blk_drain_all(void); @@ -120,10 +126,12 @@ int blk_is_read_only(BlockBackend *blk); int blk_is_sg(BlockBackend *blk); int blk_enable_write_cache(BlockBackend *blk); void blk_set_enable_write_cache(BlockBackend *blk, bool wce); +void blk_invalidate_cache(BlockBackend *blk, Error **errp); int blk_is_inserted(BlockBackend *blk); void blk_lock_medium(BlockBackend *blk, bool locked); void blk_eject(BlockBackend *blk, bool eject_flag); int blk_get_flags(BlockBackend *blk); +int blk_get_max_transfer_length(BlockBackend *blk); void blk_set_guest_block_size(BlockBackend *blk, int align); void *blk_blockalign(BlockBackend *blk, size_t size); bool blk_op_is_blocked(BlockBackend *blk, BlockOpType op, Error **errp); @@ -132,11 +140,31 @@ void blk_op_block_all(BlockBackend *blk, Error *reason); void blk_op_unblock_all(BlockBackend *blk, Error *reason); AioContext *blk_get_aio_context(BlockBackend *blk); void blk_set_aio_context(BlockBackend *blk, AioContext *new_context); +void blk_add_aio_context_notifier(BlockBackend *blk, + void (*attached_aio_context)(AioContext *new_context, void *opaque), + void (*detach_aio_context)(void *opaque), void *opaque); +void blk_remove_aio_context_notifier(BlockBackend *blk, + void (*attached_aio_context)(AioContext *, + void *), + void (*detach_aio_context)(void *), + void *opaque); +void blk_add_close_notifier(BlockBackend *blk, Notifier *notify); void blk_io_plug(BlockBackend *blk); void blk_io_unplug(BlockBackend *blk); BlockAcctStats *blk_get_stats(BlockBackend *blk); void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk, BlockCompletionFunc *cb, void *opaque); +int coroutine_fn blk_co_write_zeroes(BlockBackend *blk, int64_t sector_num, + int nb_sectors, BdrvRequestFlags flags); +int blk_write_compressed(BlockBackend *blk, int64_t sector_num, + const uint8_t *buf, int nb_sectors); +int blk_truncate(BlockBackend *blk, int64_t offset); +int blk_discard(BlockBackend *blk, int64_t sector_num, int nb_sectors); +int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf, + int64_t pos, int size); +int blk_load_vmstate(BlockBackend *blk, uint8_t *buf, int64_t pos, int size); +int blk_probe_blocksizes(BlockBackend *blk, BlockSizes *bsz); +int blk_probe_geometry(BlockBackend *blk, HDGeometry *geo); #endif diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h index 09d1e30..7ca59b5 100644 --- a/include/sysemu/blockdev.h +++ b/include/sysemu/blockdev.h @@ -63,10 +63,8 @@ DriveInfo *drive_new(QemuOpts *arg, BlockInterfaceType block_default_type); /* device-hotplug */ -DriveInfo *add_init_drive(const char *opts); - void qmp_change_blockdev(const char *device, const char *filename, const char *format, Error **errp); -void do_commit(Monitor *mon, const QDict *qdict); -int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data); +void hmp_commit(Monitor *mon, const QDict *qdict); +int hmp_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data); #endif diff --git a/include/sysemu/device_tree.h b/include/sysemu/device_tree.h index 899f05c..359e143 100644 --- a/include/sysemu/device_tree.h +++ b/include/sysemu/device_tree.h @@ -110,4 +110,13 @@ int qemu_fdt_setprop_sized_cells_from_array(void *fdt, qdt_tmp); \ }) +#define FDT_PCI_RANGE_RELOCATABLE 0x80000000 +#define FDT_PCI_RANGE_PREFETCHABLE 0x40000000 +#define FDT_PCI_RANGE_ALIASED 0x20000000 +#define FDT_PCI_RANGE_TYPE_MASK 0x03000000 +#define FDT_PCI_RANGE_MMIO_64BIT 0x03000000 +#define FDT_PCI_RANGE_MMIO 0x02000000 +#define FDT_PCI_RANGE_IOPORT 0x01000000 +#define FDT_PCI_RANGE_CONFIG 0x00000000 + #endif /* __DEVICE_TREE_H__ */ diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 22e42ef..197e6c0 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -45,6 +45,7 @@ extern bool kvm_async_interrupts_allowed; extern bool kvm_halt_in_kernel_allowed; extern bool kvm_eventfds_allowed; extern bool kvm_irqfds_allowed; +extern bool kvm_resamplefds_allowed; extern bool kvm_msi_via_irqfd_allowed; extern bool kvm_gsi_routing_allowed; extern bool kvm_gsi_direct_mapping; @@ -102,6 +103,15 @@ extern bool kvm_readonly_mem_allowed; #define kvm_irqfds_enabled() (kvm_irqfds_allowed) /** + * kvm_resamplefds_enabled: + * + * Returns: true if we can use resamplefds to inject interrupts into + * a KVM CPU (ie the kernel supports resamplefds and we are running + * with a configuration where it is meaningful to use them). + */ +#define kvm_resamplefds_enabled() (kvm_resamplefds_allowed) + +/** * kvm_msi_via_irqfd_enabled: * * Returns: true if we can route a PCI MSI (Message Signaled Interrupt) @@ -148,6 +158,7 @@ extern bool kvm_readonly_mem_allowed; struct kvm_run; struct kvm_lapic_state; +struct kvm_irq_routing_entry; typedef struct KVMCapabilityInfo { const char *name; @@ -214,6 +225,18 @@ int kvm_vcpu_ioctl(CPUState *cpu, int type, ...); int kvm_device_ioctl(int fd, int type, ...); /** + * kvm_vm_check_attr - check for existence of a specific vm attribute + * @s: The KVMState pointer + * @group: the group + * @attr: the attribute of that group to query for + * + * Returns: 1 if the attribute exists + * 0 if the attribute either does not exist or if the vm device + * interface is unavailable + */ +int kvm_vm_check_attr(KVMState *s, uint32_t group, uint64_t attr); + +/** * kvm_create_device - create a KVM device for the device control API * @KVMState: The KVMState pointer * @type: The KVM device type (see Documentation/virtual/kvm/devices in the @@ -248,7 +271,7 @@ int kvm_arch_get_registers(CPUState *cpu); int kvm_arch_put_registers(CPUState *cpu, int level); -int kvm_arch_init(KVMState *s); +int kvm_arch_init(MachineState *ms, KVMState *s); int kvm_arch_init_vcpu(CPUState *cpu); @@ -260,6 +283,9 @@ int kvm_arch_on_sigbus(int code, void *addr); void kvm_arch_init_irq_routing(KVMState *s); +int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, + uint64_t address, uint32_t data); + int kvm_set_irq(KVMState *s, int irq, int level); int kvm_irqchip_send_msi(KVMState *s, MSIMessage msg); diff --git a/include/sysemu/numa.h b/include/sysemu/numa.h new file mode 100644 index 0000000..6523b4d --- /dev/null +++ b/include/sysemu/numa.h @@ -0,0 +1,25 @@ +#ifndef SYSEMU_NUMA_H +#define SYSEMU_NUMA_H + +#include +#include "qemu/bitmap.h" +#include "qemu/option.h" +#include "sysemu/sysemu.h" +#include "sysemu/hostmem.h" +#include "hw/boards.h" + +extern int nb_numa_nodes; /* Number of NUMA nodes */ + +typedef struct node_info { + uint64_t node_mem; + DECLARE_BITMAP(node_cpu, MAX_CPUMASK_BITS); + struct HostMemoryBackend *node_memdev; + bool present; +} NodeInfo; +extern NodeInfo numa_info[MAX_NODES]; +void parse_numa_opts(MachineClass *mc); +void numa_post_machine_init(void); +void query_numa_node_mem(uint64_t node_mem[]); +extern QemuOptsList qemu_numa_opts; + +#endif diff --git a/include/sysemu/os-win32.h b/include/sysemu/os-win32.h index af3fbc4..9cc9e08 100644 --- a/include/sysemu/os-win32.h +++ b/include/sysemu/os-win32.h @@ -81,7 +81,6 @@ struct tm *gmtime_r(const time_t *timep, struct tm *result); #undef localtime_r struct tm *localtime_r(const time_t *timep, struct tm *result); -char *strtok_r(char *str, const char *delim, char **saveptr); static inline void os_setup_signal_handling(void) {} static inline void os_daemonize(void) {} diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 9fea3bc..8a52934 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -74,10 +74,10 @@ void qemu_remove_exit_notifier(Notifier *notify); void qemu_add_machine_init_done_notifier(Notifier *notify); -void do_savevm(Monitor *mon, const QDict *qdict); +void hmp_savevm(Monitor *mon, const QDict *qdict); int load_vmstate(const char *name); -void do_delvm(Monitor *mon, const QDict *qdict); -void do_info_snapshots(Monitor *mon, const QDict *qdict); +void hmp_delvm(Monitor *mon, const QDict *qdict); +void hmp_info_snapshots(Monitor *mon, const QDict *qdict); void qemu_announce_self(void); @@ -90,9 +90,6 @@ void qemu_savevm_state_cancel(void); uint64_t qemu_savevm_state_pending(QEMUFile *f, uint64_t max_size); int qemu_loadvm_state(QEMUFile *f); -/* SLIRP */ -void do_info_slirp(Monitor *mon); - typedef enum DisplayType { DT_DEFAULT, @@ -117,6 +114,7 @@ extern int graphic_width; extern int graphic_height; extern int graphic_depth; extern DisplayType display_type; +extern int display_opengl; extern const char *keyboard_layout; extern int win2k_install_hack; extern int alt_grab; @@ -147,24 +145,6 @@ extern int mem_prealloc; */ #define MAX_CPUMASK_BITS 255 -extern int nb_numa_nodes; /* Number of NUMA nodes */ -extern int max_numa_nodeid; /* Highest specified NUMA node ID, plus one. - * For all nodes, nodeid < max_numa_nodeid - */ - -typedef struct node_info { - uint64_t node_mem; - DECLARE_BITMAP(node_cpu, MAX_CPUMASK_BITS); - struct HostMemoryBackend *node_memdev; - bool present; -} NodeInfo; -extern NodeInfo numa_info[MAX_NODES]; -void set_numa_nodes(void); -void set_numa_modes(void); -void query_numa_node_mem(uint64_t node_mem[]); -extern QemuOptsList qemu_numa_opts; -int numa_init_func(QemuOpts *opts, void *opaque); - #define MAX_OPTION_ROMS 16 typedef struct QEMUOptionRom { const char *name; @@ -177,17 +157,12 @@ extern int nb_option_roms; extern const char *prom_envs[MAX_PROM_ENVS]; extern unsigned int nb_prom_envs; -/* pci-hotplug */ -void pci_device_hot_add(Monitor *mon, const QDict *qdict); -int pci_drive_hot_add(Monitor *mon, const QDict *qdict, DriveInfo *dinfo); -void do_pci_device_hot_remove(Monitor *mon, const QDict *qdict); - /* generic hotplug */ -void drive_hot_add(Monitor *mon, const QDict *qdict); +void hmp_drive_add(Monitor *mon, const QDict *qdict); /* pcie aer error injection */ void pcie_aer_inject_error_print(Monitor *mon, const QObject *data); -int do_pcie_aer_inject_error(Monitor *mon, +int hmp_pcie_aer_inject_error(Monitor *mon, const QDict *qdict, QObject **ret_data); /* serial ports */ @@ -202,9 +177,9 @@ extern CharDriverState *serial_hds[MAX_SERIAL_PORTS]; extern CharDriverState *parallel_hds[MAX_PARALLEL_PORTS]; -void do_usb_add(Monitor *mon, const QDict *qdict); -void do_usb_del(Monitor *mon, const QDict *qdict); -void usb_info(Monitor *mon, const QDict *qdict); +void hmp_usb_add(Monitor *mon, const QDict *qdict); +void hmp_usb_del(Monitor *mon, const QDict *qdict); +void hmp_info_usb(Monitor *mon, const QDict *qdict); void add_boot_device_path(int32_t bootindex, DeviceState *dev, const char *suffix); @@ -216,10 +191,19 @@ void del_boot_device_path(DeviceState *dev, const char *suffix); void device_add_bootindex_property(Object *obj, int32_t *bootindex, const char *name, const char *suffix, DeviceState *dev, Error **errp); +void restore_boot_order(void *opaque); +void validate_bootdevices(const char *devices, Error **errp); + +/* handler to set the boot_device order for a specific type of QEMUMachine */ +typedef void QEMUBootSetHandler(void *opaque, const char *boot_order, + Error **errp); +void qemu_register_boot_set(QEMUBootSetHandler *func, void *opaque); +void qemu_boot_set(const char *boot_order, Error **errp); QemuOpts *qemu_get_machine_opts(void); -bool usb_enabled(bool default_usb); +bool defaults_enabled(void); +bool usb_enabled(void); extern QemuOptsList qemu_legacy_drive_opts; extern QemuOptsList qemu_common_drive_opts; diff --git a/include/sysemu/tpm_backend.h b/include/sysemu/tpm_backend.h index 825f33b..540ee25 100644 --- a/include/sysemu/tpm_backend.h +++ b/include/sysemu/tpm_backend.h @@ -56,7 +56,7 @@ struct TPMBackend { QLIST_ENTRY(TPMBackend) list; }; -typedef void (TPMRecvDataCB)(TPMState *, uint8_t locty); +typedef void (TPMRecvDataCB)(TPMState *, uint8_t locty, bool selftest_done); typedef struct TPMSizedBuffer { uint32_t size; diff --git a/include/ui/console.h b/include/ui/console.h index 22ef8ca..2f5b9f0 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -36,7 +36,6 @@ typedef struct QEMUPutLEDEntry QEMUPutLEDEntry; QEMUPutKbdEntry *qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque); -void qemu_remove_kbd_event_handler(QEMUPutKbdEntry *entry); QEMUPutMouseEntry *qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, void *opaque, int absolute, const char *name); @@ -56,7 +55,7 @@ struct MouseTransformInfo { int a[7]; }; -void do_mouse_set(Monitor *mon, const QDict *qdict); +void hmp_mouse_set(Monitor *mon, const QDict *qdict); /* keysym is a unicode code except for special keys (see QEMU_KEY_xxx constants) */ @@ -161,6 +160,8 @@ typedef struct DisplayChangeListenerOps { void (*dpy_gfx_copy)(DisplayChangeListener *dcl, int src_x, int src_y, int dst_x, int dst_y, int w, int h); + bool (*dpy_gfx_check_format)(DisplayChangeListener *dcl, + pixman_format_code_t format); void (*dpy_text_cursor)(DisplayChangeListener *dcl, int x, int y); @@ -192,7 +193,6 @@ DisplaySurface *qemu_create_displaysurface_guestmem(int width, int height, pixman_format_code_t format, int linesize, uint64_t addr); -PixelFormat qemu_different_endianness_pixelformat(int bpp); PixelFormat qemu_default_pixelformat(int bpp); DisplaySurface *qemu_create_displaysurface(int width, int height); @@ -235,6 +235,8 @@ void dpy_gfx_update_dirty(QemuConsole *con, MemoryRegion *address_space, uint64_t base, bool invalidate); +bool dpy_gfx_check_format(QemuConsole *con, + pixman_format_code_t format); static inline int surface_stride(DisplaySurface *s) { @@ -318,7 +320,6 @@ void qemu_console_resize(QemuConsole *con, int width, int height); void qemu_console_copy(QemuConsole *con, int src_x, int src_y, int dst_x, int dst_y, int w, int h); DisplaySurface *qemu_console_surface(QemuConsole *con); -DisplayState *qemu_console_displaystate(QemuConsole *console); /* sdl.c */ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame); @@ -327,19 +328,21 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame); void cocoa_display_init(DisplayState *ds, int full_screen); /* vnc.c */ -void vnc_display_init(DisplayState *ds); -void vnc_display_open(DisplayState *ds, const char *display, Error **errp); -void vnc_display_add_client(DisplayState *ds, int csock, bool skipauth); -char *vnc_display_local_addr(DisplayState *ds); +void vnc_display_init(const char *id); +void vnc_display_open(const char *id, Error **errp); +void vnc_display_add_client(const char *id, int csock, bool skipauth); +char *vnc_display_local_addr(const char *id); #ifdef CONFIG_VNC -int vnc_display_password(DisplayState *ds, const char *password); -int vnc_display_pw_expire(DisplayState *ds, time_t expires); +int vnc_display_password(const char *id, const char *password); +int vnc_display_pw_expire(const char *id, time_t expires); +QemuOpts *vnc_parse_func(const char *str); +int vnc_init_func(QemuOpts *opts, void *opaque); #else -static inline int vnc_display_password(DisplayState *ds, const char *password) +static inline int vnc_display_password(const char *id, const char *password) { return -ENODEV; } -static inline int vnc_display_pw_expire(DisplayState *ds, time_t expires) +static inline int vnc_display_pw_expire(const char *id, time_t expires) { return -ENODEV; }; diff --git a/include/ui/qemu-pixman.h b/include/ui/qemu-pixman.h index 381969d..5d7a9ac 100644 --- a/include/ui/qemu-pixman.h +++ b/include/ui/qemu-pixman.h @@ -27,8 +27,24 @@ #ifdef HOST_WORDS_BIGENDIAN # define PIXMAN_BE_r8g8b8 PIXMAN_r8g8b8 +# define PIXMAN_BE_x8r8g8b8 PIXMAN_x8r8g8b8 +# define PIXMAN_BE_a8r8g8b8 PIXMAN_a8r8g8b8 +# define PIXMAN_BE_b8g8r8x8 PIXMAN_b8g8r8x8 +# define PIXMAN_BE_b8g8r8a8 PIXMAN_b8g8r8a8 +# define PIXMAN_BE_r8g8b8x8 PIXMAN_r8g8b8x8 +# define PIXMAN_BE_r8g8b8a8 PIXMAN_r8g8b8a8 +# define PIXMAN_BE_x8b8g8r8 PIXMAN_x8b8g8r8 +# define PIXMAN_BE_a8b8g8r8 PIXMAN_a8b8g8r8 #else # define PIXMAN_BE_r8g8b8 PIXMAN_b8g8r8 +# define PIXMAN_BE_x8r8g8b8 PIXMAN_b8g8r8x8 +# define PIXMAN_BE_a8r8g8b8 PIXMAN_b8g8r8a8 +# define PIXMAN_BE_b8g8r8x8 PIXMAN_x8r8g8b8 +# define PIXMAN_BE_b8g8r8a8 PIXMAN_a8r8g8b8 +# define PIXMAN_BE_r8g8b8x8 PIXMAN_x8b8g8r8 +# define PIXMAN_BE_r8g8b8a8 PIXMAN_a8b8g8r8 +# define PIXMAN_BE_x8b8g8r8 PIXMAN_r8g8b8x8 +# define PIXMAN_BE_a8b8g8r8 PIXMAN_r8g8b8a8 #endif /* -------------------------------------------------------------------- */ @@ -37,6 +53,8 @@ PixelFormat qemu_pixelformat_from_pixman(pixman_format_code_t format); pixman_format_code_t qemu_default_pixman_format(int bpp, bool native_endian); int qemu_pixman_get_type(int rshift, int gshift, int bshift); pixman_format_code_t qemu_pixman_get_format(PixelFormat *pf); +bool qemu_pixman_check_format(DisplayChangeListener *dcl, + pixman_format_code_t format); pixman_image_t *qemu_pixman_linebuf_create(pixman_format_code_t format, int width); diff --git a/include/ui/qemu-spice.h b/include/ui/qemu-spice.h index a93b4b2..25b94c7 100644 --- a/include/ui/qemu-spice.h +++ b/include/ui/qemu-spice.h @@ -45,9 +45,6 @@ int qemu_spice_migrate_info(const char *hostname, int port, int tls_port, const char *subject, MonitorCompletion cb, void *opaque); -void do_info_spice_print(Monitor *mon, const QObject *data); -void do_info_spice(Monitor *mon, QObject **ret_data); - CharDriverState *qemu_chr_open_spice_vmc(const char *type); #if SPICE_SERVER_VERSION >= 0x000c02 CharDriverState *qemu_chr_open_spice_port(const char *name); @@ -88,4 +85,14 @@ static inline int qemu_spice_display_add_client(int csock, int skipauth, #endif /* CONFIG_SPICE */ +static inline bool qemu_using_spice(Error **errp) +{ + if (!using_spice) { + error_set(errp, ERROR_CLASS_DEVICE_NOT_ACTIVE, + "SPICE is not in use"); + return false; + } + return true; +} + #endif /* QEMU_SPICE_H */ diff --git a/include/ui/sdl2.h b/include/ui/sdl2.h new file mode 100644 index 0000000..51fff2e --- /dev/null +++ b/include/ui/sdl2.h @@ -0,0 +1,34 @@ +#ifndef SDL2_H +#define SDL2_H + +struct sdl2_console { + DisplayChangeListener dcl; + DisplaySurface *surface; + SDL_Texture *texture; + SDL_Window *real_window; + SDL_Renderer *real_renderer; + int idx; + int last_vm_running; /* per console for caption reasons */ + int x, y; + int hidden; +}; + +void sdl2_window_create(struct sdl2_console *scon); +void sdl2_window_destroy(struct sdl2_console *scon); +void sdl2_window_resize(struct sdl2_console *scon); +void sdl2_poll_events(struct sdl2_console *scon); + +void sdl2_reset_keys(struct sdl2_console *scon); +void sdl2_process_key(struct sdl2_console *scon, + SDL_KeyboardEvent *ev); + +void sdl2_2d_update(DisplayChangeListener *dcl, + int x, int y, int w, int h); +void sdl2_2d_switch(DisplayChangeListener *dcl, + DisplaySurface *new_surface); +void sdl2_2d_refresh(DisplayChangeListener *dcl); +void sdl2_2d_redraw(struct sdl2_console *scon); +bool sdl2_2d_check_format(DisplayChangeListener *dcl, + pixman_format_code_t format); + +#endif /* SDL2_H */ diff --git a/include/ui/spice-display.h b/include/ui/spice-display.h index 4252ab8..53883a1 100644 --- a/include/ui/spice-display.h +++ b/include/ui/spice-display.h @@ -102,6 +102,7 @@ struct SimpleSpiceDisplay { /* cursor (with qxl): qxl local renderer -> displaychangelistener */ QEMUCursor *cursor; int mouse_x, mouse_y; + QEMUBH *cursor_bh; }; struct SimpleSpiceUpdate { @@ -134,7 +135,7 @@ void qemu_spice_display_update(SimpleSpiceDisplay *ssd, void qemu_spice_display_switch(SimpleSpiceDisplay *ssd, DisplaySurface *surface); void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd); -void qemu_spice_cursor_refresh_unlocked(SimpleSpiceDisplay *ssd); +void qemu_spice_cursor_refresh_bh(void *opaque); void qemu_spice_add_memslot(SimpleSpiceDisplay *ssd, QXLDevMemSlot *memslot, qxl_async_io async); diff --git a/kvm-all.c b/kvm-all.c index 937bc9d..481c560 100644 --- a/kvm-all.c +++ b/kvm-all.c @@ -120,11 +120,13 @@ bool kvm_async_interrupts_allowed; bool kvm_halt_in_kernel_allowed; bool kvm_eventfds_allowed; bool kvm_irqfds_allowed; +bool kvm_resamplefds_allowed; bool kvm_msi_via_irqfd_allowed; bool kvm_gsi_routing_allowed; bool kvm_gsi_direct_mapping; bool kvm_allowed; bool kvm_readonly_mem_allowed; +bool kvm_vm_attributes_allowed; static const KVMCapabilityInfo kvm_required_capabilites[] = { KVM_CAP_INFO(USER_MEMORY), @@ -365,7 +367,7 @@ static void kvm_log_stop(MemoryListener *listener, } } -static int kvm_set_migration_log(int enable) +static int kvm_set_migration_log(bool enable) { KVMState *s = kvm_state; KVMSlot *mem; @@ -416,7 +418,7 @@ static int kvm_physical_sync_dirty_bitmap(MemoryRegionSection *section) { KVMState *s = kvm_state; unsigned long size, allocated_size = 0; - KVMDirtyLog d; + KVMDirtyLog d = {}; KVMSlot *mem; int ret = 0; hwaddr start_addr = section->offset_within_address_space; @@ -526,13 +528,33 @@ int kvm_vm_check_extension(KVMState *s, unsigned int extension) return ret; } +static uint32_t adjust_ioeventfd_endianness(uint32_t val, uint32_t size) +{ +#if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN) + /* The kernel expects ioeventfd values in HOST_WORDS_BIGENDIAN + * endianness, but the memory core hands them in target endianness. + * For example, PPC is always treated as big-endian even if running + * on KVM and on PPC64LE. Correct here. + */ + switch (size) { + case 2: + val = bswap16(val); + break; + case 4: + val = bswap32(val); + break; + } +#endif + return val; +} + static int kvm_set_ioeventfd_mmio(int fd, hwaddr addr, uint32_t val, bool assign, uint32_t size, bool datamatch) { int ret; struct kvm_ioeventfd iofd; - iofd.datamatch = datamatch ? val : 0; + iofd.datamatch = datamatch ? adjust_ioeventfd_endianness(val, size) : 0; iofd.addr = addr; iofd.len = size; iofd.flags = 0; @@ -562,7 +584,7 @@ static int kvm_set_ioeventfd_pio(int fd, uint16_t addr, uint16_t val, bool assign, uint32_t size, bool datamatch) { struct kvm_ioeventfd kick = { - .datamatch = datamatch ? val : 0, + .datamatch = datamatch ? adjust_ioeventfd_endianness(val, size) : 0, .addr = addr, .flags = KVM_IOEVENTFD_FLAG_PIO, .len = size, @@ -693,7 +715,7 @@ static void kvm_set_phys_mem(MemoryRegionSection *section, bool add) old = *mem; - if (mem->flags & KVM_MEM_LOG_DIRTY_PAGES) { + if ((mem->flags & KVM_MEM_LOG_DIRTY_PAGES) || s->migration_log) { kvm_physical_sync_dirty_bitmap(section); } @@ -1120,9 +1142,17 @@ static int kvm_irqchip_get_virq(KVMState *s) uint32_t *word = s->used_gsi_bitmap; int max_words = ALIGN(s->gsi_count, 32) / 32; int i, bit; - bool retry = true; -again: + /* + * PIC and IOAPIC share the first 16 GSI numbers, thus the available + * GSI numbers are more than the number of IRQ route. Allocating a GSI + * number can succeed even though a new route entry cannot be added. + * When this happens, flush dynamic MSI entries to free IRQ route entries. + */ + if (!s->direct_msi && s->irq_routes->nr == s->gsi_count) { + kvm_flush_dynamic_msi_routes(s); + } + /* Return the lowest unused GSI in the bitmap */ for (i = 0; i < max_words; i++) { bit = ffs(~word[i]); @@ -1132,11 +1162,6 @@ again: return bit - 1 + i * 32; } - if (!s->direct_msi && retry) { - retry = false; - kvm_flush_dynamic_msi_routes(s); - goto again; - } return -ENOSPC; } @@ -1224,6 +1249,10 @@ int kvm_irqchip_add_msi_route(KVMState *s, MSIMessage msg) kroute.u.msi.address_lo = (uint32_t)msg.address; kroute.u.msi.address_hi = msg.address >> 32; kroute.u.msi.data = le32_to_cpu(msg.data); + if (kvm_arch_fixup_msi_route(&kroute, msg.address, msg.data)) { + kvm_irqchip_release_virq(s, virq); + return -EINVAL; + } kvm_add_routing_entry(s, &kroute); kvm_irqchip_commit_routes(s); @@ -1249,6 +1278,9 @@ int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg) kroute.u.msi.address_lo = (uint32_t)msg.address; kroute.u.msi.address_hi = msg.address >> 32; kroute.u.msi.data = le32_to_cpu(msg.data); + if (kvm_arch_fixup_msi_route(&kroute, msg.address, msg.data)) { + return -EINVAL; + } return kvm_update_routing_entry(s, &kroute); } @@ -1276,7 +1308,7 @@ static int kvm_irqchip_assign_irqfd(KVMState *s, int fd, int rfd, int virq, int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter) { - struct kvm_irq_routing_entry kroute; + struct kvm_irq_routing_entry kroute = {}; int virq; if (!kvm_gsi_routing_enabled()) { @@ -1352,11 +1384,11 @@ int kvm_irqchip_remove_irqfd_notifier(KVMState *s, EventNotifier *n, int virq) false); } -static int kvm_irqchip_create(KVMState *s) +static int kvm_irqchip_create(MachineState *machine, KVMState *s) { int ret; - if (!qemu_opt_get_bool(qemu_get_machine_opts(), "kernel_irqchip", true) || + if (!machine_kernel_irqchip_allowed(machine) || (!kvm_check_extension(s, KVM_CAP_IRQCHIP) && (kvm_vm_enable_cap(s, KVM_CAP_S390_IRQCHIP, 0) < 0))) { return 0; @@ -1584,12 +1616,21 @@ static int kvm_init(MachineState *ms) kvm_eventfds_allowed = (kvm_check_extension(s, KVM_CAP_IOEVENTFD) > 0); - ret = kvm_arch_init(s); + kvm_irqfds_allowed = + (kvm_check_extension(s, KVM_CAP_IRQFD) > 0); + + kvm_resamplefds_allowed = + (kvm_check_extension(s, KVM_CAP_IRQFD_RESAMPLE) > 0); + + kvm_vm_attributes_allowed = + (kvm_check_extension(s, KVM_CAP_VM_ATTRIBUTES) > 0); + + ret = kvm_arch_init(ms, s); if (ret < 0) { goto err; } - ret = kvm_irqchip_create(s); + ret = kvm_irqchip_create(ms, s); if (ret < 0) { goto err; } @@ -1922,6 +1963,23 @@ int kvm_device_ioctl(int fd, int type, ...) return ret; } +int kvm_vm_check_attr(KVMState *s, uint32_t group, uint64_t attr) +{ + int ret; + struct kvm_device_attr attribute = { + .group = group, + .attr = attr, + }; + + if (!kvm_vm_attributes_allowed) { + return 0; + } + + ret = kvm_vm_ioctl(s, KVM_HAS_DEVICE_ATTR, &attribute); + /* kvm returns 0 on success for HAS_DEVICE_ATTR */ + return ret ? 0 : 1; +} + int kvm_has_sync_mmu(void) { return kvm_check_extension(kvm_state, KVM_CAP_SYNC_MMU); @@ -2056,10 +2114,6 @@ int kvm_insert_breakpoint(CPUState *cpu, target_ulong addr, } bp = g_malloc(sizeof(struct kvm_sw_breakpoint)); - if (!bp) { - return -ENOMEM; - } - bp->pc = addr; bp->use_count = 1; err = kvm_arch_insert_sw_breakpoint(cpu, bp); diff --git a/libcacard/Makefile b/libcacard/Makefile index 0e7903f..b5eddff 100644 --- a/libcacard/Makefile +++ b/libcacard/Makefile @@ -19,6 +19,8 @@ vscclient$(EXESUF): libcacard/vscclient.o libcacard.la libcacard.la: LDFLAGS += -rpath $(libdir) -no-undefined \ -export-symbols $(SRC_PATH)/libcacard/libcacard.syms +# Prevent libcacard.so linking against the entire world of 3rd party libs +libcacard.la: LIBS = libcacard.la: $(libcacard-lobj-y) $(call LINK,$^) diff --git a/linux-headers/asm-arm/kvm.h b/linux-headers/asm-arm/kvm.h index 09ee408..0db25bc 100644 --- a/linux-headers/asm-arm/kvm.h +++ b/linux-headers/asm-arm/kvm.h @@ -175,6 +175,8 @@ struct kvm_arch_memory_slot { #define KVM_DEV_ARM_VGIC_OFFSET_SHIFT 0 #define KVM_DEV_ARM_VGIC_OFFSET_MASK (0xffffffffULL << KVM_DEV_ARM_VGIC_OFFSET_SHIFT) #define KVM_DEV_ARM_VGIC_GRP_NR_IRQS 3 +#define KVM_DEV_ARM_VGIC_GRP_CTRL 4 +#define KVM_DEV_ARM_VGIC_CTRL_INIT 0 /* KVM_IRQ_LINE irq field index values */ #define KVM_ARM_IRQ_TYPE_SHIFT 24 diff --git a/linux-headers/asm-arm64/kvm.h b/linux-headers/asm-arm64/kvm.h index 8e38878..3ef77a4 100644 --- a/linux-headers/asm-arm64/kvm.h +++ b/linux-headers/asm-arm64/kvm.h @@ -78,6 +78,13 @@ struct kvm_regs { #define KVM_VGIC_V2_DIST_SIZE 0x1000 #define KVM_VGIC_V2_CPU_SIZE 0x2000 +/* Supported VGICv3 address types */ +#define KVM_VGIC_V3_ADDR_TYPE_DIST 2 +#define KVM_VGIC_V3_ADDR_TYPE_REDIST 3 + +#define KVM_VGIC_V3_DIST_SIZE SZ_64K +#define KVM_VGIC_V3_REDIST_SIZE (2 * SZ_64K) + #define KVM_ARM_VCPU_POWER_OFF 0 /* CPU is started in OFF state */ #define KVM_ARM_VCPU_EL1_32BIT 1 /* CPU running a 32bit VM */ #define KVM_ARM_VCPU_PSCI_0_2 2 /* CPU uses PSCI v0.2 */ @@ -161,6 +168,8 @@ struct kvm_arch_memory_slot { #define KVM_DEV_ARM_VGIC_OFFSET_SHIFT 0 #define KVM_DEV_ARM_VGIC_OFFSET_MASK (0xffffffffULL << KVM_DEV_ARM_VGIC_OFFSET_SHIFT) #define KVM_DEV_ARM_VGIC_GRP_NR_IRQS 3 +#define KVM_DEV_ARM_VGIC_GRP_CTRL 4 +#define KVM_DEV_ARM_VGIC_CTRL_INIT 0 /* KVM_IRQ_LINE irq field index values */ #define KVM_ARM_IRQ_TYPE_SHIFT 24 diff --git a/linux-headers/asm-s390/kvm.h b/linux-headers/asm-s390/kvm.h index d36b2fa..c5a93eb 100644 --- a/linux-headers/asm-s390/kvm.h +++ b/linux-headers/asm-s390/kvm.h @@ -57,10 +57,44 @@ struct kvm_s390_io_adapter_req { /* kvm attr_group on vm fd */ #define KVM_S390_VM_MEM_CTRL 0 +#define KVM_S390_VM_TOD 1 +#define KVM_S390_VM_CRYPTO 2 +#define KVM_S390_VM_CPU_MODEL 3 /* kvm attributes for mem_ctrl */ #define KVM_S390_VM_MEM_ENABLE_CMMA 0 #define KVM_S390_VM_MEM_CLR_CMMA 1 +#define KVM_S390_VM_MEM_LIMIT_SIZE 2 + +/* kvm attributes for KVM_S390_VM_TOD */ +#define KVM_S390_VM_TOD_LOW 0 +#define KVM_S390_VM_TOD_HIGH 1 + +/* kvm attributes for KVM_S390_VM_CPU_MODEL */ +/* processor related attributes are r/w */ +#define KVM_S390_VM_CPU_PROCESSOR 0 +struct kvm_s390_vm_cpu_processor { + __u64 cpuid; + __u16 ibc; + __u8 pad[6]; + __u64 fac_list[256]; +}; + +/* machine related attributes are r/o */ +#define KVM_S390_VM_CPU_MACHINE 1 +struct kvm_s390_vm_cpu_machine { + __u64 cpuid; + __u32 ibc; + __u8 pad[4]; + __u64 fac_mask[256]; + __u64 fac_list[256]; +}; + +/* kvm attributes for crypto */ +#define KVM_S390_VM_CRYPTO_ENABLE_AES_KW 0 +#define KVM_S390_VM_CRYPTO_ENABLE_DEA_KW 1 +#define KVM_S390_VM_CRYPTO_DISABLE_AES_KW 2 +#define KVM_S390_VM_CRYPTO_DISABLE_DEA_KW 3 /* for KVM_GET_REGS and KVM_SET_REGS */ struct kvm_regs { @@ -107,6 +141,9 @@ struct kvm_guest_debug_arch { struct kvm_hw_breakpoint *hw_bp; }; +/* for KVM_SYNC_PFAULT and KVM_REG_S390_PFTOKEN */ +#define KVM_S390_PFAULT_TOKEN_INVALID 0xffffffffffffffffULL + #define KVM_SYNC_PREFIX (1UL << 0) #define KVM_SYNC_GPRS (1UL << 1) #define KVM_SYNC_ACRS (1UL << 2) diff --git a/linux-headers/asm-x86/hyperv.h b/linux-headers/asm-x86/hyperv.h index 462efe7..90c458e 100644 --- a/linux-headers/asm-x86/hyperv.h +++ b/linux-headers/asm-x86/hyperv.h @@ -187,6 +187,17 @@ #define HV_X64_MSR_SINT14 0x4000009E #define HV_X64_MSR_SINT15 0x4000009F +/* + * Synthetic Timer MSRs. Four timers per vcpu. + */ +#define HV_X64_MSR_STIMER0_CONFIG 0x400000B0 +#define HV_X64_MSR_STIMER0_COUNT 0x400000B1 +#define HV_X64_MSR_STIMER1_CONFIG 0x400000B2 +#define HV_X64_MSR_STIMER1_COUNT 0x400000B3 +#define HV_X64_MSR_STIMER2_CONFIG 0x400000B4 +#define HV_X64_MSR_STIMER2_COUNT 0x400000B5 +#define HV_X64_MSR_STIMER3_CONFIG 0x400000B6 +#define HV_X64_MSR_STIMER3_COUNT 0x400000B7 #define HV_X64_MSR_HYPERCALL_ENABLE 0x00000001 #define HV_X64_MSR_HYPERCALL_PAGE_ADDRESS_SHIFT 12 diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index 12045a1..60a54c8 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -491,6 +491,11 @@ struct kvm_s390_emerg_info { __u16 code; }; +#define KVM_S390_STOP_FLAG_STORE_STATUS 0x01 +struct kvm_s390_stop_info { + __u32 flags; +}; + struct kvm_s390_mchk_info { __u64 cr14; __u64 mcic; @@ -509,6 +514,7 @@ struct kvm_s390_irq { struct kvm_s390_emerg_info emerg; struct kvm_s390_extcall_info extcall; struct kvm_s390_prefix_info prefix; + struct kvm_s390_stop_info stop; struct kvm_s390_mchk_info mchk; char reserved[64]; } u; @@ -647,11 +653,7 @@ struct kvm_ppc_smmu_info { #define KVM_CAP_MP_STATE 14 #define KVM_CAP_COALESCED_MMIO 15 #define KVM_CAP_SYNC_MMU 16 /* Changes to host mmap are reflected in guest */ -#define KVM_CAP_DEVICE_ASSIGNMENT 17 #define KVM_CAP_IOMMU 18 -#ifdef __KVM_HAVE_MSI -#define KVM_CAP_DEVICE_MSI 20 -#endif /* Bug in KVM_SET_USER_MEMORY_REGION fixed: */ #define KVM_CAP_DESTROY_MEMORY_REGION_WORKS 21 #define KVM_CAP_USER_NMI 22 @@ -663,10 +665,6 @@ struct kvm_ppc_smmu_info { #endif #define KVM_CAP_IRQ_ROUTING 25 #define KVM_CAP_IRQ_INJECT_STATUS 26 -#define KVM_CAP_DEVICE_DEASSIGNMENT 27 -#ifdef __KVM_HAVE_MSIX -#define KVM_CAP_DEVICE_MSIX 28 -#endif #define KVM_CAP_ASSIGN_DEV_IRQ 29 /* Another bug in KVM_SET_USER_MEMORY_REGION fixed: */ #define KVM_CAP_JOIN_MEMORY_REGIONS_WORKS 30 @@ -761,6 +759,7 @@ struct kvm_ppc_smmu_info { #define KVM_CAP_PPC_FIXUP_HCALL 103 #define KVM_CAP_PPC_ENABLE_HCALL 104 #define KVM_CAP_CHECK_EXTENSION_VM 105 +#define KVM_CAP_S390_USER_SIGP 106 #ifdef KVM_CAP_IRQ_ROUTING @@ -960,6 +959,8 @@ enum kvm_device_type { #define KVM_DEV_TYPE_ARM_VGIC_V2 KVM_DEV_TYPE_ARM_VGIC_V2 KVM_DEV_TYPE_FLIC, #define KVM_DEV_TYPE_FLIC KVM_DEV_TYPE_FLIC + KVM_DEV_TYPE_ARM_VGIC_V3, +#define KVM_DEV_TYPE_ARM_VGIC_V3 KVM_DEV_TYPE_ARM_VGIC_V3 KVM_DEV_TYPE_MAX, }; @@ -1107,9 +1108,6 @@ struct kvm_s390_ucas_mapping { #define KVM_X86_SETUP_MCE _IOW(KVMIO, 0x9c, __u64) #define KVM_X86_GET_MCE_CAP_SUPPORTED _IOR(KVMIO, 0x9d, __u64) #define KVM_X86_SET_MCE _IOW(KVMIO, 0x9e, struct kvm_x86_mce) -/* IA64 stack access */ -#define KVM_IA64_VCPU_GET_STACK _IOR(KVMIO, 0x9a, void *) -#define KVM_IA64_VCPU_SET_STACK _IOW(KVMIO, 0x9b, void *) /* Available with KVM_CAP_VCPU_EVENTS */ #define KVM_GET_VCPU_EVENTS _IOR(KVMIO, 0x9f, struct kvm_vcpu_events) #define KVM_SET_VCPU_EVENTS _IOW(KVMIO, 0xa0, struct kvm_vcpu_events) diff --git a/linux-headers/linux/vfio.h b/linux-headers/linux/vfio.h index 0f21aa6..95ba870 100644 --- a/linux-headers/linux/vfio.h +++ b/linux-headers/linux/vfio.h @@ -333,6 +333,7 @@ enum { VFIO_PCI_MSI_IRQ_INDEX, VFIO_PCI_MSIX_IRQ_INDEX, VFIO_PCI_ERR_IRQ_INDEX, + VFIO_PCI_REQ_IRQ_INDEX, VFIO_PCI_NUM_IRQS }; diff --git a/linux-headers/linux/virtio_config.h b/linux-headers/linux/virtio_config.h index 75dc20b..5590f7d 100644 --- a/linux-headers/linux/virtio_config.h +++ b/linux-headers/linux/virtio_config.h @@ -1,57 +1 @@ -#ifndef _LINUX_VIRTIO_CONFIG_H -#define _LINUX_VIRTIO_CONFIG_H -/* This header, excluding the #ifdef __KERNEL__ part, is BSD licensed so - * anyone can use the definitions to implement compatible drivers/servers. - * - * 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. - * 3. Neither the name of IBM 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 IBM 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. */ - -/* Virtio devices use a standardized configuration space to define their - * features and pass configuration information, but each implementation can - * store and access that space differently. */ -#include - -/* Status byte for guest to report progress, and synchronize features. */ -/* We have seen device and processed generic fields (VIRTIO_CONFIG_F_VIRTIO) */ -#define VIRTIO_CONFIG_S_ACKNOWLEDGE 1 -/* We have found a driver for the device. */ -#define VIRTIO_CONFIG_S_DRIVER 2 -/* Driver has used its parts of the config, and is happy */ -#define VIRTIO_CONFIG_S_DRIVER_OK 4 -/* We've given up on this device. */ -#define VIRTIO_CONFIG_S_FAILED 0x80 - -/* Some virtio feature bits (currently bits 28 through 31) are reserved for the - * transport being used (eg. virtio_ring), the rest are per-device feature - * bits. */ -#define VIRTIO_TRANSPORT_F_START 28 -#define VIRTIO_TRANSPORT_F_END 32 - -/* Do we get callbacks when the ring is completely used, even if we've - * suppressed them? */ -#define VIRTIO_F_NOTIFY_ON_EMPTY 24 - -/* Can the device handle any descriptor layout? */ -#define VIRTIO_F_ANY_LAYOUT 27 - -#endif /* _LINUX_VIRTIO_CONFIG_H */ +#include "standard-headers/linux/virtio_config.h" diff --git a/linux-headers/linux/virtio_ring.h b/linux-headers/linux/virtio_ring.h index 1b333e2..c6f0fb6 100644 --- a/linux-headers/linux/virtio_ring.h +++ b/linux-headers/linux/virtio_ring.h @@ -1,163 +1 @@ -#ifndef _LINUX_VIRTIO_RING_H -#define _LINUX_VIRTIO_RING_H -/* An interface for efficient virtio implementation, currently for use by KVM - * and lguest, but hopefully others soon. Do NOT change this since it will - * break existing servers and clients. - * - * This header is BSD licensed so anyone can use the definitions to implement - * compatible drivers/servers. - * - * 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. - * 3. Neither the name of IBM 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 IBM 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. - * - * Copyright Rusty Russell IBM Corporation 2007. */ -#include - -/* This marks a buffer as continuing via the next field. */ -#define VRING_DESC_F_NEXT 1 -/* This marks a buffer as write-only (otherwise read-only). */ -#define VRING_DESC_F_WRITE 2 -/* This means the buffer contains a list of buffer descriptors. */ -#define VRING_DESC_F_INDIRECT 4 - -/* The Host uses this in used->flags to advise the Guest: don't kick me when - * you add a buffer. It's unreliable, so it's simply an optimization. Guest - * will still kick if it's out of buffers. */ -#define VRING_USED_F_NO_NOTIFY 1 -/* The Guest uses this in avail->flags to advise the Host: don't interrupt me - * when you consume a buffer. It's unreliable, so it's simply an - * optimization. */ -#define VRING_AVAIL_F_NO_INTERRUPT 1 - -/* We support indirect buffer descriptors */ -#define VIRTIO_RING_F_INDIRECT_DESC 28 - -/* The Guest publishes the used index for which it expects an interrupt - * at the end of the avail ring. Host should ignore the avail->flags field. */ -/* The Host publishes the avail index for which it expects a kick - * at the end of the used ring. Guest should ignore the used->flags field. */ -#define VIRTIO_RING_F_EVENT_IDX 29 - -/* Virtio ring descriptors: 16 bytes. These can chain together via "next". */ -struct vring_desc { - /* Address (guest-physical). */ - __u64 addr; - /* Length. */ - __u32 len; - /* The flags as indicated above. */ - __u16 flags; - /* We chain unused descriptors via this, too */ - __u16 next; -}; - -struct vring_avail { - __u16 flags; - __u16 idx; - __u16 ring[]; -}; - -/* u32 is used here for ids for padding reasons. */ -struct vring_used_elem { - /* Index of start of used descriptor chain. */ - __u32 id; - /* Total length of the descriptor chain which was used (written to) */ - __u32 len; -}; - -struct vring_used { - __u16 flags; - __u16 idx; - struct vring_used_elem ring[]; -}; - -struct vring { - unsigned int num; - - struct vring_desc *desc; - - struct vring_avail *avail; - - struct vring_used *used; -}; - -/* The standard layout for the ring is a continuous chunk of memory which looks - * like this. We assume num is a power of 2. - * - * struct vring - * { - * // The actual descriptors (16 bytes each) - * struct vring_desc desc[num]; - * - * // A ring of available descriptor heads with free-running index. - * __u16 avail_flags; - * __u16 avail_idx; - * __u16 available[num]; - * __u16 used_event_idx; - * - * // Padding to the next align boundary. - * char pad[]; - * - * // A ring of used descriptor heads with free-running index. - * __u16 used_flags; - * __u16 used_idx; - * struct vring_used_elem used[num]; - * __u16 avail_event_idx; - * }; - */ -/* We publish the used event index at the end of the available ring, and vice - * versa. They are at the end for backwards compatibility. */ -#define vring_used_event(vr) ((vr)->avail->ring[(vr)->num]) -#define vring_avail_event(vr) (*(__u16 *)&(vr)->used->ring[(vr)->num]) - -static __inline__ void vring_init(struct vring *vr, unsigned int num, void *p, - unsigned long align) -{ - vr->num = num; - vr->desc = p; - vr->avail = p + num*sizeof(struct vring_desc); - vr->used = (void *)(((unsigned long)&vr->avail->ring[num] + sizeof(__u16) - + align-1) & ~(align - 1)); -} - -static __inline__ unsigned vring_size(unsigned int num, unsigned long align) -{ - return ((sizeof(struct vring_desc) * num + sizeof(__u16) * (3 + num) - + align - 1) & ~(align - 1)) - + sizeof(__u16) * 3 + sizeof(struct vring_used_elem) * num; -} - -/* The following is used with USED_EVENT_IDX and AVAIL_EVENT_IDX */ -/* Assuming a given event_idx value from the other size, if - * we have just incremented index from old to new_idx, - * should we trigger an event? */ -static __inline__ int vring_need_event(__u16 event_idx, __u16 new_idx, __u16 old) -{ - /* Note: Xen has similar logic for notification hold-off - * in include/xen/interface/io/ring.h with req_event and req_prod - * corresponding to event_idx + 1 and new_idx respectively. - * Note also that req_event and req_prod in Xen start at 1, - * event indexes in virtio start at 0. */ - return (__u16)(new_idx - event_idx - 1) < (__u16)(new_idx - old); -} - -#endif /* _LINUX_VIRTIO_RING_H */ +#include "standard-headers/linux/virtio_ring.h" diff --git a/linux-user/aarch64/target_cpu.h b/linux-user/aarch64/target_cpu.h index 21560ef..b5593dc 100644 --- a/linux-user/aarch64/target_cpu.h +++ b/linux-user/aarch64/target_cpu.h @@ -32,7 +32,7 @@ static inline void cpu_set_tls(CPUARMState *env, target_ulong newtls) /* Note that AArch64 Linux keeps the TLS pointer in TPIDR; this is * different from AArch32 Linux, which uses TPIDRRO. */ - env->cp15.tpidr_el0 = newtls; + env->cp15.tpidr_el[0] = newtls; } #endif diff --git a/linux-user/alpha/syscall_nr.h b/linux-user/alpha/syscall_nr.h index 625f301..dde8d5c 100644 --- a/linux-user/alpha/syscall_nr.h +++ b/linux-user/alpha/syscall_nr.h @@ -185,6 +185,10 @@ #define TARGET_NR_osf_utsname 207 #define TARGET_NR_lchown 208 #define TARGET_NR_osf_shmat 209 +/* this has the usual shmat semantics so give it the name syscall.c expects + * so that our support for it is enabled. + */ +#define TARGET_NR_shmat TARGET_NR_osf_shmat #define TARGET_NR_shmctl 210 #define TARGET_NR_shmdt 211 #define TARGET_NR_shmget 212 diff --git a/linux-user/arm/nwfpe/fpopcode.c b/linux-user/arm/nwfpe/fpopcode.c index 0dc5c9c..0ada30c 100644 --- a/linux-user/arm/nwfpe/fpopcode.c +++ b/linux-user/arm/nwfpe/fpopcode.c @@ -88,25 +88,3 @@ unsigned int getDestinationSize(const unsigned int opcode) return(nRc); } - -/* condition code lookup table - index into the table is test code: EQ, NE, ... LT, GT, AL, NV - bit position in short is condition code: NZCV */ -static const unsigned short aCC[16] = { - 0xF0F0, // EQ == Z set - 0x0F0F, // NE - 0xCCCC, // CS == C set - 0x3333, // CC - 0xFF00, // MI == N set - 0x00FF, // PL - 0xAAAA, // VS == V set - 0x5555, // VC - 0x0C0C, // HI == C set && Z clear - 0xF3F3, // LS == C clear || Z set - 0xAA55, // GE == (N==V) - 0x55AA, // LT == (N!=V) - 0x0A05, // GT == (!Z && (N==V)) - 0xF5FA, // LE == (Z || (N!=V)) - 0xFFFF, // AL always - 0 // NV -}; diff --git a/linux-user/arm/target_cpu.h b/linux-user/arm/target_cpu.h index 39d65b6..6832262 100644 --- a/linux-user/arm/target_cpu.h +++ b/linux-user/arm/target_cpu.h @@ -29,7 +29,20 @@ static inline void cpu_clone_regs(CPUARMState *env, target_ulong newsp) static inline void cpu_set_tls(CPUARMState *env, target_ulong newtls) { - env->cp15.tpidrro_el0 = newtls; + if (access_secure_reg(env)) { + env->cp15.tpidruro_s = newtls; + } else { + env->cp15.tpidrro_el[0] = newtls; + } +} + +static inline target_ulong cpu_get_tls(CPUARMState *env) +{ + if (access_secure_reg(env)) { + return env->cp15.tpidruro_s; + } else { + return env->cp15.tpidrro_el[0]; + } } #endif diff --git a/linux-user/elfload.c b/linux-user/elfload.c index e2596a4..399c021 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -829,8 +829,11 @@ static inline void init_thread(struct target_pt_regs *_regs, struct image_info * _regs->gpr[1] = infop->start_stack; #if defined(TARGET_PPC64) && !defined(TARGET_ABI32) if (get_ppc64_abi(infop) < 2) { - _regs->gpr[2] = ldq_raw(infop->entry + 8) + infop->load_bias; - infop->entry = ldq_raw(infop->entry) + infop->load_bias; + uint64_t val; + get_user_u64(val, infop->entry + 8); + _regs->gpr[2] = val + infop->load_bias; + get_user_u64(val, infop->entry); + infop->entry = val + infop->load_bias; } else { _regs->gpr[12] = infop->entry; /* r12 set to global entry address */ } diff --git a/linux-user/main.c b/linux-user/main.c index 5c14c1e..a8adb04 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -169,7 +169,7 @@ static inline void start_exclusive(void) } /* Finish an exclusive operation. */ -static inline void end_exclusive(void) +static inline void __attribute__((unused)) end_exclusive(void) { pending_cpus = 0; pthread_cond_broadcast(&exclusive_resume); @@ -283,7 +283,9 @@ void cpu_loop(CPUX86State *env) target_siginfo_t info; for(;;) { + cpu_exec_start(cs); trapnr = cpu_x86_exec(env); + cpu_exec_end(cs); switch(trapnr) { case 0x80: /* linux syscall from int $0x80 */ @@ -313,7 +315,7 @@ void cpu_loop(CPUX86State *env) #endif case EXCP0B_NOSEG: case EXCP0C_STACK: - info.si_signo = SIGBUS; + info.si_signo = TARGET_SIGBUS; info.si_errno = 0; info.si_code = TARGET_SI_KERNEL; info._sifields._sigfault._addr = 0; @@ -327,7 +329,7 @@ void cpu_loop(CPUX86State *env) } else #endif { - info.si_signo = SIGSEGV; + info.si_signo = TARGET_SIGSEGV; info.si_errno = 0; info.si_code = TARGET_SI_KERNEL; info._sifields._sigfault._addr = 0; @@ -335,7 +337,7 @@ void cpu_loop(CPUX86State *env) } break; case EXCP0E_PAGE: - info.si_signo = SIGSEGV; + info.si_signo = TARGET_SIGSEGV; info.si_errno = 0; if (!(env->error_code & 1)) info.si_code = TARGET_SEGV_MAPERR; @@ -352,7 +354,7 @@ void cpu_loop(CPUX86State *env) #endif { /* division by zero */ - info.si_signo = SIGFPE; + info.si_signo = TARGET_SIGFPE; info.si_errno = 0; info.si_code = TARGET_FPE_INTDIV; info._sifields._sigfault._addr = env->eip; @@ -367,7 +369,7 @@ void cpu_loop(CPUX86State *env) } else #endif { - info.si_signo = SIGTRAP; + info.si_signo = TARGET_SIGTRAP; info.si_errno = 0; if (trapnr == EXCP01_DB) { info.si_code = TARGET_TRAP_BRKPT; @@ -387,7 +389,7 @@ void cpu_loop(CPUX86State *env) } else #endif { - info.si_signo = SIGSEGV; + info.si_signo = TARGET_SIGSEGV; info.si_errno = 0; info.si_code = TARGET_SI_KERNEL; info._sifields._sigfault._addr = 0; @@ -395,7 +397,7 @@ void cpu_loop(CPUX86State *env) } break; case EXCP06_ILLOP: - info.si_signo = SIGILL; + info.si_signo = TARGET_SIGILL; info.si_errno = 0; info.si_code = TARGET_ILL_ILLOPN; info._sifields._sigfault._addr = env->eip; @@ -517,14 +519,12 @@ segv: end_exclusive(); /* We get the PC of the entry address - which is as good as anything, on a real kernel what you get depends on which mode it uses. */ - info.si_signo = SIGSEGV; + info.si_signo = TARGET_SIGSEGV; info.si_errno = 0; /* XXX: check env->error_code */ info.si_code = TARGET_SEGV_MAPERR; info._sifields._sigfault._addr = env->exception.vaddress; queue_signal(env, info.si_signo, &info); - - end_exclusive(); } /* Handle a jump to the kernel code page. */ @@ -564,7 +564,7 @@ do_kernel_trap(CPUARMState *env) end_exclusive(); break; case 0xffff0fe0: /* __kernel_get_tls */ - env->regs[0] = env->cp15.tpidrro_el0; + env->regs[0] = cpu_get_tls(env); break; case 0xffff0f60: /* __kernel_cmpxchg64 */ arm_kernel_cmpxchg64_helper(env); @@ -694,7 +694,7 @@ void cpu_loop(CPUARMState *env) rc = EmulateAll(opcode, &ts->fpa, env); if (rc == 0) { /* illegal instruction */ - info.si_signo = SIGILL; + info.si_signo = TARGET_SIGILL; info.si_errno = 0; info.si_code = TARGET_ILL_ILLOPN; info._sifields._sigfault._addr = env->regs[15]; @@ -718,7 +718,7 @@ void cpu_loop(CPUARMState *env) //printf("fpsr 0x%x, arm_fpe 0x%x\n",fpsr,arm_fpe); if (fpsr & (arm_fpe << 16)) { /* exception enabled? */ - info.si_signo = SIGFPE; + info.si_signo = TARGET_SIGFPE; info.si_errno = 0; /* ordered by priority, least first */ @@ -842,7 +842,7 @@ void cpu_loop(CPUARMState *env) case EXCP_DATA_ABORT: addr = env->exception.vaddress; { - info.si_signo = SIGSEGV; + info.si_signo = TARGET_SIGSEGV; info.si_errno = 0; /* XXX: check env->error_code */ info.si_code = TARGET_SEGV_MAPERR; @@ -1028,7 +1028,7 @@ void cpu_loop(CPUARMState *env) /* just indicate that signals should be handled asap */ break; case EXCP_UDEF: - info.si_signo = SIGILL; + info.si_signo = TARGET_SIGILL; info.si_errno = 0; info.si_code = TARGET_ILL_ILLOPN; info._sifields._sigfault._addr = env->pc; @@ -1041,7 +1041,7 @@ void cpu_loop(CPUARMState *env) /* fall through for segv */ case EXCP_PREFETCH_ABORT: case EXCP_DATA_ABORT: - info.si_signo = SIGSEGV; + info.si_signo = TARGET_SIGSEGV; info.si_errno = 0; /* XXX: check env->error_code */ info.si_code = TARGET_SEGV_MAPERR; @@ -1121,7 +1121,7 @@ void cpu_loop(CPUUniCore32State *env) break; case UC32_EXCP_DTRAP: case UC32_EXCP_ITRAP: - info.si_signo = SIGSEGV; + info.si_signo = TARGET_SIGSEGV; info.si_errno = 0; /* XXX: check env->error_code */ info.si_code = TARGET_SEGV_MAPERR; @@ -1288,7 +1288,9 @@ void cpu_loop (CPUSPARCState *env) target_siginfo_t info; while (1) { + cpu_exec_start(cs); trapnr = cpu_sparc_exec (env); + cpu_exec_end(cs); /* Compute PSR before exposing state. */ if (env->cc_op != CC_OP_FLAGS) { @@ -2656,7 +2658,9 @@ void cpu_loop(CPUOpenRISCState *env) int trapnr, gdbsig; for (;;) { + cpu_exec_start(cs); trapnr = cpu_exec(env); + cpu_exec_end(cs); gdbsig = 0; switch (trapnr) { @@ -2666,7 +2670,7 @@ void cpu_loop(CPUOpenRISCState *env) break; case EXCP_BUSERR: qemu_log("\nBus error, exit, pc is %#x\n", env->pc); - gdbsig = SIGBUS; + gdbsig = TARGET_SIGBUS; break; case EXCP_DPF: case EXCP_IPF: @@ -2678,11 +2682,11 @@ void cpu_loop(CPUOpenRISCState *env) break; case EXCP_ALIGN: qemu_log("\nAlignment pc is %#x\n", env->pc); - gdbsig = SIGBUS; + gdbsig = TARGET_SIGBUS; break; case EXCP_ILLEGAL: qemu_log("\nIllegal instructionpc is %#x\n", env->pc); - gdbsig = SIGILL; + gdbsig = TARGET_SIGILL; break; case EXCP_INT: qemu_log("\nExternal interruptpc is %#x\n", env->pc); @@ -2693,7 +2697,7 @@ void cpu_loop(CPUOpenRISCState *env) break; case EXCP_RANGE: qemu_log("\nRange\n"); - gdbsig = SIGSEGV; + gdbsig = TARGET_SIGSEGV; break; case EXCP_SYSCALL: env->pc += 4; /* 0xc00; */ @@ -2711,7 +2715,7 @@ void cpu_loop(CPUOpenRISCState *env) break; case EXCP_TRAP: qemu_log("\nTrap\n"); - gdbsig = SIGTRAP; + gdbsig = TARGET_SIGTRAP; break; case EXCP_NR: qemu_log("\nNR\n"); @@ -2744,7 +2748,9 @@ void cpu_loop(CPUSH4State *env) target_siginfo_t info; while (1) { + cpu_exec_start(cs); trapnr = cpu_sh4_exec (env); + cpu_exec_end(cs); switch (trapnr) { case 0x160: @@ -2779,7 +2785,7 @@ void cpu_loop(CPUSH4State *env) break; case 0xa0: case 0xc0: - info.si_signo = SIGSEGV; + info.si_signo = TARGET_SIGSEGV; info.si_errno = 0; info.si_code = TARGET_SEGV_MAPERR; info._sifields._sigfault._addr = env->tea; @@ -2804,11 +2810,13 @@ void cpu_loop(CPUCRISState *env) target_siginfo_t info; while (1) { + cpu_exec_start(cs); trapnr = cpu_cris_exec (env); + cpu_exec_end(cs); switch (trapnr) { case 0xaa: { - info.si_signo = SIGSEGV; + info.si_signo = TARGET_SIGSEGV; info.si_errno = 0; /* XXX: check env->error_code */ info.si_code = TARGET_SEGV_MAPERR; @@ -2863,11 +2871,13 @@ void cpu_loop(CPUMBState *env) target_siginfo_t info; while (1) { + cpu_exec_start(cs); trapnr = cpu_mb_exec (env); + cpu_exec_end(cs); switch (trapnr) { case 0xaa: { - info.si_signo = SIGSEGV; + info.si_signo = TARGET_SIGSEGV; info.si_errno = 0; /* XXX: check env->error_code */ info.si_code = TARGET_SEGV_MAPERR; @@ -2905,14 +2915,14 @@ void cpu_loop(CPUMBState *env) switch (env->sregs[SR_ESR] & 31) { case ESR_EC_DIVZERO: - info.si_signo = SIGFPE; + info.si_signo = TARGET_SIGFPE; info.si_errno = 0; info.si_code = TARGET_FPE_FLTDIV; info._sifields._sigfault._addr = 0; queue_signal(env, info.si_signo, &info); break; case ESR_EC_FPU: - info.si_signo = SIGFPE; + info.si_signo = TARGET_SIGFPE; info.si_errno = 0; if (env->sregs[SR_FSR] & FSR_IO) { info.si_code = TARGET_FPE_FLTINV; @@ -2966,13 +2976,15 @@ void cpu_loop(CPUM68KState *env) TaskState *ts = cs->opaque; for(;;) { + cpu_exec_start(cs); trapnr = cpu_m68k_exec(env); + cpu_exec_end(cs); switch(trapnr) { case EXCP_ILLEGAL: { if (ts->sim_syscalls) { uint16_t nr; - nr = lduw(env->pc + 2); + get_user_u16(nr, env->pc + 2); env->pc += 4; do_m68k_simcall(env, nr); } else { @@ -2989,7 +3001,7 @@ void cpu_loop(CPUM68KState *env) case EXCP_LINEF: case EXCP_UNSUPPORTED: do_sigill: - info.si_signo = SIGILL; + info.si_signo = TARGET_SIGILL; info.si_errno = 0; info.si_code = TARGET_ILL_ILLOPN; info._sifields._sigfault._addr = env->pc; @@ -3016,7 +3028,7 @@ void cpu_loop(CPUM68KState *env) break; case EXCP_ACCESS: { - info.si_signo = SIGSEGV; + info.si_signo = TARGET_SIGSEGV; info.si_errno = 0; /* XXX: check env->error_code */ info.si_code = TARGET_SEGV_MAPERR; @@ -3103,7 +3115,9 @@ void cpu_loop(CPUAlphaState *env) abi_long sysret; while (1) { + cpu_exec_start(cs); trapnr = cpu_alpha_exec (env); + cpu_exec_end(cs); /* All of the traps imply a transition through PALcode, which implies an REI instruction has been executed. Which means @@ -3289,7 +3303,9 @@ void cpu_loop(CPUS390XState *env) target_ulong addr; while (1) { + cpu_exec_start(cs); trapnr = cpu_s390x_exec(env); + cpu_exec_end(cs); switch (trapnr) { case EXCP_INTERRUPT: /* Just indicate that signals should be handled asap. */ @@ -3319,12 +3335,12 @@ void cpu_loop(CPUS390XState *env) switch (n) { case PGM_OPERATION: case PGM_PRIVILEGED: - sig = SIGILL; + sig = TARGET_SIGILL; n = TARGET_ILL_ILLOPC; goto do_signal_pc; case PGM_PROTECTION: case PGM_ADDRESSING: - sig = SIGSEGV; + sig = TARGET_SIGSEGV; /* XXX: check env->error_code */ n = TARGET_SEGV_MAPERR; addr = env->__excp_addr; @@ -3334,16 +3350,16 @@ void cpu_loop(CPUS390XState *env) case PGM_SPECIAL_OP: case PGM_OPERAND: do_sigill_opn: - sig = SIGILL; + sig = TARGET_SIGILL; n = TARGET_ILL_ILLOPN; goto do_signal_pc; case PGM_FIXPT_OVERFLOW: - sig = SIGFPE; + sig = TARGET_SIGFPE; n = TARGET_FPE_INTOVF; goto do_signal_pc; case PGM_FIXPT_DIVIDE: - sig = SIGFPE; + sig = TARGET_SIGFPE; n = TARGET_FPE_INTDIV; goto do_signal_pc; @@ -3368,7 +3384,7 @@ void cpu_loop(CPUS390XState *env) /* ??? Quantum exception; BFP, DFP error. */ goto do_sigill_opn; } - sig = SIGFPE; + sig = TARGET_SIGFPE; goto do_signal_pc; } @@ -3434,12 +3450,10 @@ void init_task_state(TaskState *ts) CPUArchState *cpu_copy(CPUArchState *env) { CPUState *cpu = ENV_GET_CPU(env); - CPUArchState *new_env = cpu_init(cpu_model); - CPUState *new_cpu = ENV_GET_CPU(new_env); -#if defined(TARGET_HAS_ICE) + CPUState *new_cpu = cpu_init(cpu_model); + CPUArchState *new_env = new_cpu->env_ptr; CPUBreakpoint *bp; CPUWatchpoint *wp; -#endif /* Reset non arch specific state */ cpu_reset(new_cpu); @@ -3451,14 +3465,12 @@ CPUArchState *cpu_copy(CPUArchState *env) BP_CPU break/watchpoints are handled correctly on clone. */ QTAILQ_INIT(&cpu->breakpoints); QTAILQ_INIT(&cpu->watchpoints); -#if defined(TARGET_HAS_ICE) QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) { cpu_breakpoint_insert(new_cpu, bp->pc, bp->flags, NULL); } QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) { cpu_watchpoint_insert(new_cpu, wp->vaddr, wp->len, wp->flags, NULL); } -#endif return new_env; } @@ -3905,7 +3917,7 @@ int main(int argc, char **argv, char **envp) #endif #elif defined(TARGET_MIPS) #if defined(TARGET_ABI_MIPSN32) || defined(TARGET_ABI_MIPSN64) - cpu_model = "20Kc"; + cpu_model = "5KEf"; #else cpu_model = "24Kf"; #endif @@ -3925,12 +3937,12 @@ int main(int argc, char **argv, char **envp) cpu_exec_init_all(); /* NOTE: we need to init the CPU at this stage to get qemu_host_page_size */ - env = cpu_init(cpu_model); - if (!env) { + cpu = cpu_init(cpu_model); + if (!cpu) { fprintf(stderr, "Unable to find CPU definition\n"); exit(1); } - cpu = ENV_GET_CPU(env); + env = cpu->env_ptr; cpu_reset(cpu); thread_cpu = cpu; diff --git a/linux-user/mips64/target_signal.h b/linux-user/mips64/target_signal.h index 6e1dc8b..5fb6a2c 100644 --- a/linux-user/mips64/target_signal.h +++ b/linux-user/mips64/target_signal.h @@ -8,7 +8,7 @@ typedef struct target_sigaltstack { abi_long ss_sp; abi_ulong ss_size; - abi_long ss_flags; + abi_int ss_flags; } target_stack_t; diff --git a/linux-user/signal.c b/linux-user/signal.c index e11b208..5bb399e 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -732,18 +732,6 @@ int do_sigaction(int sig, const struct target_sigaction *act, return ret; } -static inline void copy_siginfo_to_user(target_siginfo_t *tinfo, - const target_siginfo_t *info) -{ - tswap_siginfo(tinfo, info); -} - -static inline int current_exec_domain_sig(int sig) -{ - return /* current->exec_domain && current->exec_domain->signal_invmap - && sig < 32 ? current->exec_domain->signal_invmap[sig] : */ sig; -} - #if defined(TARGET_I386) && TARGET_ABI_BITS == 32 /* from the Linux kernel */ @@ -926,8 +914,7 @@ static void setup_frame(int sig, struct target_sigaction *ka, if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) goto give_sigsegv; - __put_user(current_exec_domain_sig(sig), - &frame->sig); + __put_user(sig, &frame->sig); setup_sigcontext(&frame->sc, &frame->fpstate, env, set->sig[0], frame_addr + offsetof(struct sigframe, fpstate)); @@ -988,12 +975,12 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) goto give_sigsegv; - __put_user(current_exec_domain_sig(sig), &frame->sig); + __put_user(sig, &frame->sig); addr = frame_addr + offsetof(struct rt_sigframe, info); __put_user(addr, &frame->pinfo); addr = frame_addr + offsetof(struct rt_sigframe, uc); __put_user(addr, &frame->puc); - copy_siginfo_to_user(&frame->info, info); + tswap_siginfo(&frame->info, info); /* Create the ucontext. */ __put_user(0, &frame->uc.tuc_flags); @@ -1360,7 +1347,7 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, env->pc = ka->_sa_handler; env->xregs[30] = return_addr; if (info) { - copy_siginfo_to_user(&frame->info, info); + tswap_siginfo(&frame->info, info); env->xregs[1] = frame_addr + offsetof(struct target_rt_sigframe, info); env->xregs[2] = frame_addr + offsetof(struct target_rt_sigframe, uc); } @@ -1777,7 +1764,7 @@ static void setup_rt_frame_v1(int usig, struct target_sigaction *ka, __put_user(info_addr, &frame->pinfo); uc_addr = frame_addr + offsetof(struct rt_sigframe_v1, uc); __put_user(uc_addr, &frame->puc); - copy_siginfo_to_user(&frame->info, info); + tswap_siginfo(&frame->info, info); /* Clear all the bits of the ucontext we don't use. */ memset(&frame->uc, 0, offsetof(struct target_ucontext_v1, tuc_mcontext)); @@ -1815,7 +1802,7 @@ static void setup_rt_frame_v2(int usig, struct target_sigaction *ka, info_addr = frame_addr + offsetof(struct rt_sigframe_v2, info); uc_addr = frame_addr + offsetof(struct rt_sigframe_v2, uc); - copy_siginfo_to_user(&frame->info, info); + tswap_siginfo(&frame->info, info); setup_sigframe_v2(&frame->uc, set, env); @@ -3017,7 +3004,7 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, install_sigtramp(frame->rs_code, TARGET_NR_rt_sigreturn); - copy_siginfo_to_user(&frame->rs_info, info); + tswap_siginfo(&frame->rs_info, info); __put_user(0, &frame->rs_uc.tuc_flags); __put_user(0, &frame->rs_uc.tuc_link); @@ -3228,14 +3215,11 @@ static void setup_frame(int sig, struct target_sigaction *ka, abi_ulong frame_addr; int i; int err = 0; - int signal; frame_addr = get_sigframe(ka, regs->gregs[15], sizeof(*frame)); if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) goto give_sigsegv; - signal = current_exec_domain_sig(sig); - setup_sigcontext(&frame->sc, regs, set->sig[0]); for (i = 0; i < TARGET_NSIG_WORDS - 1; i++) { @@ -3259,7 +3243,7 @@ static void setup_frame(int sig, struct target_sigaction *ka, /* Set up registers for signal handler */ regs->gregs[15] = frame_addr; - regs->gregs[4] = signal; /* Arg for signal handler */ + regs->gregs[4] = sig; /* Arg for signal handler */ regs->gregs[5] = 0; regs->gregs[6] = frame_addr += offsetof(typeof(*frame), sc); regs->pc = (unsigned long) ka->_sa_handler; @@ -3280,15 +3264,12 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, abi_ulong frame_addr; int i; int err = 0; - int signal; frame_addr = get_sigframe(ka, regs->gregs[15], sizeof(*frame)); if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) goto give_sigsegv; - signal = current_exec_domain_sig(sig); - - copy_siginfo_to_user(&frame->info, info); + tswap_siginfo(&frame->info, info); /* Create the ucontext. */ __put_user(0, &frame->uc.tuc_flags); @@ -3322,7 +3303,7 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, /* Set up registers for signal handler */ regs->gregs[15] = frame_addr; - regs->gregs[4] = signal; /* Arg for signal handler */ + regs->gregs[4] = sig; /* Arg for signal handler */ regs->gregs[5] = frame_addr + offsetof(typeof(*frame), info); regs->gregs[6] = frame_addr + offsetof(typeof(*frame), uc); regs->pc = (unsigned long) ka->_sa_handler; @@ -3947,7 +3928,7 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, __put_user(uc_addr, &frame->puc); if (ka->sa_flags & SA_SIGINFO) { - copy_siginfo_to_user(&frame->info, info); + tswap_siginfo(&frame->info, info); } /*err |= __clear_user(&frame->uc, offsetof(struct ucontext, uc_mcontext));*/ @@ -4195,7 +4176,7 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, } qemu_log("%s: 1\n", __FUNCTION__); - copy_siginfo_to_user(&frame->info, info); + tswap_siginfo(&frame->info, info); /* Create the ucontext. */ __put_user(0, &frame->uc.tuc_flags); @@ -4680,7 +4661,6 @@ static void setup_frame(int sig, struct target_sigaction *ka, struct target_sigcontext *sc; target_ulong frame_addr, newsp; int err = 0; - int signal; #if defined(TARGET_PPC64) struct image_info *image = ((TaskState *)thread_cpu->opaque)->info; #endif @@ -4690,8 +4670,6 @@ static void setup_frame(int sig, struct target_sigaction *ka, goto sigsegv; sc = &frame->sctx; - signal = current_exec_domain_sig(sig); - __put_user(ka->_sa_handler, &sc->handler); __put_user(set->sig[0], &sc->oldmask); #if TARGET_ABI_BITS == 64 @@ -4724,7 +4702,7 @@ static void setup_frame(int sig, struct target_sigaction *ka, /* Set up registers for signal handler. */ env->gpr[1] = newsp; - env->gpr[3] = signal; + env->gpr[3] = sig; env->gpr[4] = frame_addr + offsetof(struct target_sigframe, sctx); #if defined(TARGET_PPC64) @@ -4765,7 +4743,6 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, struct target_mcontext *mctx = 0; target_ulong rt_sf_addr, newsp = 0; int i, err = 0; - int signal; #if defined(TARGET_PPC64) struct image_info *image = ((TaskState *)thread_cpu->opaque)->info; #endif @@ -4774,9 +4751,7 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, if (!lock_user_struct(VERIFY_WRITE, rt_sf, rt_sf_addr, 1)) goto sigsegv; - signal = current_exec_domain_sig(sig); - - copy_siginfo_to_user(&rt_sf->info, info); + tswap_siginfo(&rt_sf->info, info); __put_user(0, &rt_sf->uc.tuc_flags); __put_user(0, &rt_sf->uc.tuc_link); @@ -4821,7 +4796,7 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, /* Set up registers for signal handler. */ env->gpr[1] = newsp; - env->gpr[3] = (target_ulong) signal; + env->gpr[3] = (target_ulong) sig; env->gpr[4] = (target_ulong) h2g(&rt_sf->info); env->gpr[5] = (target_ulong) h2g(&rt_sf->uc); env->gpr[6] = (target_ulong) h2g(rt_sf); @@ -5091,7 +5066,7 @@ static void setup_frame(int sig, struct target_sigaction *ka, /* moveq #,d0; trap #0 */ __put_user(0x70004e40 + (TARGET_NR_sigreturn << 16), - (long *)(frame->retcode)); + (uint32_t *)(frame->retcode)); /* Set up to return from userspace */ @@ -5196,7 +5171,7 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, uc_addr = frame_addr + offsetof(struct target_rt_sigframe, uc); __put_user(uc_addr, &frame->puc); - copy_siginfo_to_user(&frame->info, info); + tswap_siginfo(&frame->info, info); /* Create the ucontext */ @@ -5225,8 +5200,8 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, /* moveq #,d0; notb d0; trap #0 */ __put_user(0x70004600 + ((TARGET_NR_rt_sigreturn ^ 0xff) << 16), - (long *)(frame->retcode + 0)); - __put_user(0x4e40, (short *)(frame->retcode + 4)); + (uint32_t *)(frame->retcode + 0)); + __put_user(0x4e40, (uint16_t *)(frame->retcode + 4)); if (err) goto give_sigsegv; @@ -5473,7 +5448,7 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, goto give_sigsegv; } - copy_siginfo_to_user(&frame->info, info); + tswap_siginfo(&frame->info, info); __put_user(0, &frame->uc.tuc_flags); __put_user(0, &frame->uc.tuc_link); diff --git a/linux-user/syscall.c b/linux-user/syscall.c index aaac6a2..1622ad6 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -1214,16 +1214,26 @@ static inline abi_long target_to_host_cmsg(struct msghdr *msgh, cmsg->cmsg_type = tswap32(target_cmsg->cmsg_type); cmsg->cmsg_len = CMSG_LEN(len); - if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) { - gemu_log("Unsupported ancillary data: %d/%d\n", cmsg->cmsg_level, cmsg->cmsg_type); - memcpy(data, target_data, len); - } else { + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { int *fd = (int *)data; int *target_fd = (int *)target_data; int i, numfds = len / sizeof(int); for (i = 0; i < numfds; i++) fd[i] = tswap32(target_fd[i]); + } else if (cmsg->cmsg_level == SOL_SOCKET + && cmsg->cmsg_type == SCM_CREDENTIALS) { + struct ucred *cred = (struct ucred *)data; + struct target_ucred *target_cred = + (struct target_ucred *)target_data; + + __put_user(target_cred->pid, &cred->pid); + __put_user(target_cred->uid, &cred->uid); + __put_user(target_cred->gid, &cred->gid); + } else { + gemu_log("Unsupported ancillary data: %d/%d\n", + cmsg->cmsg_level, cmsg->cmsg_type); + memcpy(data, target_data, len); } cmsg = CMSG_NXTHDR(msgh, cmsg); @@ -1873,6 +1883,11 @@ static struct iovec *lock_iovec(int type, abi_ulong target_addr, return vec; fail: + while (--i >= 0) { + if (tswapal(target_vec[i].iov_len) > 0) { + unlock_user(vec[i].iov_base, tswapal(target_vec[i].iov_base), 0); + } + } unlock_user(target_vec, target_addr, 0); fail2: free(vec); @@ -1891,7 +1906,7 @@ static void unlock_iovec(struct iovec *vec, abi_ulong target_addr, if (target_vec) { for (i = 0; i < count; i++) { abi_ulong base = tswapal(target_vec[i].iov_base); - abi_long len = tswapal(target_vec[i].iov_base); + abi_long len = tswapal(target_vec[i].iov_len); if (len < 0) { break; } @@ -3278,7 +3293,7 @@ typedef abi_long do_ioctl_fn(const IOCTLEntry *ie, uint8_t *buf_temp, int fd, abi_long cmd, abi_long arg); struct IOCTLEntry { - unsigned int target_cmd; + int target_cmd; unsigned int host_cmd; const char *name; int access; @@ -3561,6 +3576,7 @@ static abi_long do_ioctl_dm(const IOCTLEntry *ie, uint8_t *buf_temp, int fd, } default: ret = -TARGET_EINVAL; + unlock_user(argptr, guest_data, 0); goto out; } unlock_user(argptr, guest_data, 0); @@ -3680,6 +3696,7 @@ static abi_long do_ioctl_dm(const IOCTLEntry *ie, uint8_t *buf_temp, int fd, break; } default: + unlock_user(argptr, guest_data, 0); ret = -TARGET_EINVAL; goto out; } @@ -4555,6 +4572,7 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, ret = fork(); if (ret == 0) { /* Child Process. */ + rcu_after_fork(); cpu_clone_regs(env, newsp); fork_end(1); /* There is a race condition here. The parent process could @@ -9334,15 +9352,29 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, { loff_t loff_in, loff_out; loff_t *ploff_in = NULL, *ploff_out = NULL; - if(arg2) { - get_user_u64(loff_in, arg2); + if (arg2) { + if (get_user_u64(loff_in, arg2)) { + goto efault; + } ploff_in = &loff_in; } - if(arg4) { - get_user_u64(loff_out, arg2); + if (arg4) { + if (get_user_u64(loff_out, arg4)) { + goto efault; + } ploff_out = &loff_out; } ret = get_errno(splice(arg1, ploff_in, arg3, ploff_out, arg5, arg6)); + if (arg2) { + if (put_user_u64(loff_in, arg2)) { + goto efault; + } + } + if (arg4) { + if (put_user_u64(loff_out, arg4)) { + goto efault; + } + } } break; #endif @@ -9529,6 +9561,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, /* args: pid, resource number, ptr to new rlimit, ptr to old rlimit */ struct target_rlimit64 *target_rnew, *target_rold; struct host_rlimit64 rnew, rold, *rnewp = 0; + int resource = target_to_host_resource(arg2); if (arg3) { if (!lock_user_struct(VERIFY_READ, target_rnew, arg3, 1)) { goto efault; @@ -9539,7 +9572,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, rnewp = &rnew; } - ret = get_errno(sys_prlimit64(arg1, arg2, rnewp, arg4 ? &rold : 0)); + ret = get_errno(sys_prlimit64(arg1, resource, rnewp, arg4 ? &rold : 0)); if (!is_error(ret) && arg4) { if (!lock_user_struct(VERIFY_WRITE, target_rold, arg4, 1)) { goto efault; diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index ebb3be1..edd5f3c 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -655,7 +655,14 @@ typedef struct { #endif #define TARGET_SI_MAX_SIZE 128 -#define TARGET_SI_PAD_SIZE ((TARGET_SI_MAX_SIZE/sizeof(int)) - 3) + +#if TARGET_ABI_BITS == 32 +#define TARGET_SI_PREAMBLE_SIZE (3 * sizeof(int)) +#else +#define TARGET_SI_PREAMBLE_SIZE (4 * sizeof(int)) +#endif + +#define TARGET_SI_PAD_SIZE ((TARGET_SI_MAX_SIZE - TARGET_SI_PREAMBLE_SIZE) / sizeof(int)) typedef struct target_siginfo { #ifdef TARGET_MIPS @@ -1600,73 +1607,25 @@ struct target_stat { #elif defined(TARGET_ABI_MIPSN32) struct target_stat { - unsigned st_dev; - int st_pad1[3]; /* Reserved for network id */ - unsigned int st_ino; - unsigned int st_mode; - unsigned int st_nlink; - int st_uid; - int st_gid; - unsigned st_rdev; - unsigned int st_pad2[2]; - unsigned int st_size; - unsigned int st_pad3; - /* - * Actually this should be timestruc_t st_atime, st_mtime and st_ctime - * but we don't have it under Linux. - */ - unsigned int target_st_atime; - unsigned int target_st_atime_nsec; - unsigned int target_st_mtime; - unsigned int target_st_mtime_nsec; - unsigned int target_st_ctime; - unsigned int target_st_ctime_nsec; - unsigned int st_blksize; - unsigned int st_blocks; - unsigned int st_pad4[14]; -}; - -/* - * This matches struct stat64 in glibc2.1, hence the absolutely insane - * amounts of padding around dev_t's. The memory layout is the same as of - * struct stat of the 64-bit kernel. - */ - -#define TARGET_HAS_STRUCT_STAT64 -struct target_stat64 { - unsigned int st_dev; - unsigned int st_pad0[3]; /* Reserved for st_dev expansion */ - - target_ulong st_ino; - - unsigned int st_mode; - unsigned int st_nlink; - - int st_uid; - int st_gid; - - unsigned int st_rdev; - unsigned int st_pad1[3]; /* Reserved for st_rdev expansion */ - - int st_size; - - /* - * Actually this should be timestruc_t st_atime, st_mtime and st_ctime - * but we don't have it under Linux. - */ - int target_st_atime; - unsigned int target_st_atime_nsec; /* Reserved for st_atime expansion */ - - int target_st_mtime; - unsigned int target_st_mtime_nsec; /* Reserved for st_mtime expansion */ - - int target_st_ctime; - unsigned int target_st_ctime_nsec; /* Reserved for st_ctime expansion */ - - unsigned int st_blksize; - unsigned int st_pad2; - - int st_blocks; + abi_ulong st_dev; + abi_ulong st_pad0[3]; /* Reserved for st_dev expansion */ + uint64_t st_ino; + unsigned int st_mode; + unsigned int st_nlink; + int st_uid; + int st_gid; + abi_ulong st_rdev; + abi_ulong st_pad1[3]; /* Reserved for st_rdev expansion */ + int64_t st_size; + abi_long target_st_atime; + abi_ulong target_st_atime_nsec; /* Reserved for st_atime expansion */ + abi_long target_st_mtime; + abi_ulong target_st_mtime_nsec; /* Reserved for st_mtime expansion */ + abi_long target_st_ctime; + abi_ulong target_st_ctime_nsec; /* Reserved for st_ctime expansion */ + abi_ulong st_blksize; + abi_ulong st_pad2; + int64_t st_blocks; }; #elif defined(TARGET_ABI_MIPSO32) diff --git a/linux-user/vm86.c b/linux-user/vm86.c index 45ef559..22a4eb9 100644 --- a/linux-user/vm86.c +++ b/linux-user/vm86.c @@ -45,29 +45,34 @@ static inline int is_revectored(int nr, struct target_revectored_struct *bitmap) return (((uint8_t *)bitmap)[nr >> 3] >> (nr & 7)) & 1; } -static inline void vm_putw(uint32_t segptr, unsigned int reg16, unsigned int val) +static inline void vm_putw(CPUX86State *env, uint32_t segptr, + unsigned int reg16, unsigned int val) { - stw(segptr + (reg16 & 0xffff), val); + cpu_stw_data(env, segptr + (reg16 & 0xffff), val); } -static inline void vm_putl(uint32_t segptr, unsigned int reg16, unsigned int val) +static inline void vm_putl(CPUX86State *env, uint32_t segptr, + unsigned int reg16, unsigned int val) { - stl(segptr + (reg16 & 0xffff), val); + cpu_stl_data(env, segptr + (reg16 & 0xffff), val); } -static inline unsigned int vm_getb(uint32_t segptr, unsigned int reg16) +static inline unsigned int vm_getb(CPUX86State *env, + uint32_t segptr, unsigned int reg16) { - return ldub(segptr + (reg16 & 0xffff)); + return cpu_ldub_data(env, segptr + (reg16 & 0xffff)); } -static inline unsigned int vm_getw(uint32_t segptr, unsigned int reg16) +static inline unsigned int vm_getw(CPUX86State *env, + uint32_t segptr, unsigned int reg16) { - return lduw(segptr + (reg16 & 0xffff)); + return cpu_lduw_data(env, segptr + (reg16 & 0xffff)); } -static inline unsigned int vm_getl(uint32_t segptr, unsigned int reg16) +static inline unsigned int vm_getl(CPUX86State *env, + uint32_t segptr, unsigned int reg16) { - return ldl(segptr + (reg16 & 0xffff)); + return cpu_ldl_data(env, segptr + (reg16 & 0xffff)); } void save_v86_state(CPUX86State *env) @@ -221,7 +226,7 @@ static void do_int(CPUX86State *env, int intno) &ts->vm86plus.int21_revectored)) goto cannot_handle; int_addr = (intno << 2); - segoffs = ldl(int_addr); + segoffs = cpu_ldl_data(env, int_addr); if ((segoffs >> 16) == TARGET_BIOSSEG) goto cannot_handle; LOG_VM86("VM86: emulating int 0x%x. CS:IP=%04x:%04x\n", @@ -229,9 +234,9 @@ static void do_int(CPUX86State *env, int intno) /* save old state */ ssp = env->segs[R_SS].selector << 4; sp = env->regs[R_ESP] & 0xffff; - vm_putw(ssp, sp - 2, get_vflags(env)); - vm_putw(ssp, sp - 4, env->segs[R_CS].selector); - vm_putw(ssp, sp - 6, env->eip); + vm_putw(env, ssp, sp - 2, get_vflags(env)); + vm_putw(env, ssp, sp - 4, env->segs[R_CS].selector); + vm_putw(env, ssp, sp - 6, env->eip); ADD16(env->regs[R_ESP], -6); /* goto interrupt handler */ env->eip = segoffs & 0xffff; @@ -285,7 +290,7 @@ void handle_vm86_fault(CPUX86State *env) data32 = 0; pref_done = 0; do { - opcode = vm_getb(csp, ip); + opcode = vm_getb(env, csp, ip); ADD16(ip, 1); switch (opcode) { case 0x66: /* 32-bit data */ data32=1; break; @@ -306,10 +311,10 @@ void handle_vm86_fault(CPUX86State *env) switch(opcode) { case 0x9c: /* pushf */ if (data32) { - vm_putl(ssp, sp - 4, get_vflags(env)); + vm_putl(env, ssp, sp - 4, get_vflags(env)); ADD16(env->regs[R_ESP], -4); } else { - vm_putw(ssp, sp - 2, get_vflags(env)); + vm_putw(env, ssp, sp - 2, get_vflags(env)); ADD16(env->regs[R_ESP], -2); } env->eip = ip; @@ -317,10 +322,10 @@ void handle_vm86_fault(CPUX86State *env) case 0x9d: /* popf */ if (data32) { - newflags = vm_getl(ssp, sp); + newflags = vm_getl(env, ssp, sp); ADD16(env->regs[R_ESP], 4); } else { - newflags = vm_getw(ssp, sp); + newflags = vm_getw(env, ssp, sp); ADD16(env->regs[R_ESP], 2); } env->eip = ip; @@ -335,7 +340,7 @@ void handle_vm86_fault(CPUX86State *env) VM86_FAULT_RETURN; case 0xcd: /* int */ - intno = vm_getb(csp, ip); + intno = vm_getb(env, csp, ip); ADD16(ip, 1); env->eip = ip; if (ts->vm86plus.vm86plus.flags & TARGET_vm86dbg_active) { @@ -350,14 +355,14 @@ void handle_vm86_fault(CPUX86State *env) case 0xcf: /* iret */ if (data32) { - newip = vm_getl(ssp, sp) & 0xffff; - newcs = vm_getl(ssp, sp + 4) & 0xffff; - newflags = vm_getl(ssp, sp + 8); + newip = vm_getl(env, ssp, sp) & 0xffff; + newcs = vm_getl(env, ssp, sp + 4) & 0xffff; + newflags = vm_getl(env, ssp, sp + 8); ADD16(env->regs[R_ESP], 12); } else { - newip = vm_getw(ssp, sp); - newcs = vm_getw(ssp, sp + 2); - newflags = vm_getw(ssp, sp + 4); + newip = vm_getw(env, ssp, sp); + newcs = vm_getw(env, ssp, sp + 2); + newflags = vm_getw(env, ssp, sp + 4); ADD16(env->regs[R_ESP], 6); } env->eip = newip; diff --git a/memory.c b/memory.c index 15cf9eb..ee3f2a8 100644 --- a/memory.c +++ b/memory.c @@ -33,26 +33,12 @@ static bool memory_region_update_pending; static bool ioeventfd_update_pending; static bool global_dirty_log = false; -/* flat_view_mutex is taken around reading as->current_map; the critical - * section is extremely short, so I'm using a single mutex for every AS. - * We could also RCU for the read-side. - * - * The BQL is taken around transaction commits, hence both locks are taken - * while writing to as->current_map (with the BQL taken outside). - */ -static QemuMutex flat_view_mutex; - static QTAILQ_HEAD(memory_listeners, MemoryListener) memory_listeners = QTAILQ_HEAD_INITIALIZER(memory_listeners); static QTAILQ_HEAD(, AddressSpace) address_spaces = QTAILQ_HEAD_INITIALIZER(address_spaces); -static void memory_init(void) -{ - qemu_mutex_init(&flat_view_mutex); -} - typedef struct AddrRange AddrRange; /* @@ -242,6 +228,7 @@ struct FlatRange { * order. */ struct FlatView { + struct rcu_head rcu; unsigned ref; FlatRange *ranges; unsigned nr; @@ -654,10 +641,10 @@ static FlatView *address_space_get_flatview(AddressSpace *as) { FlatView *view; - qemu_mutex_lock(&flat_view_mutex); - view = as->current_map; + rcu_read_lock(); + view = atomic_rcu_read(&as->current_map); flatview_ref(view); - qemu_mutex_unlock(&flat_view_mutex); + rcu_read_unlock(); return view; } @@ -766,10 +753,9 @@ static void address_space_update_topology(AddressSpace *as) address_space_update_topology_pass(as, old_view, new_view, false); address_space_update_topology_pass(as, old_view, new_view, true); - qemu_mutex_lock(&flat_view_mutex); - flatview_unref(as->current_map); - as->current_map = new_view; - qemu_mutex_unlock(&flat_view_mutex); + /* Writes are protected by the BQL. */ + atomic_rcu_set(&as->current_map, new_view); + call_rcu(old_view, flatview_unref, rcu); /* Note that all the old MemoryRegions are still alive up to this * point. This relieves most MemoryListeners from the need to @@ -882,7 +868,7 @@ void memory_region_init(MemoryRegion *mr, uint64_t size) { if (!owner) { - owner = qdev_get_machine(); + owner = container_get(qdev_get_machine(), "/unattached"); } object_initialize(mr, sizeof(*mr), TYPE_MEMORY_REGION); @@ -1152,6 +1138,23 @@ void memory_region_init_ram(MemoryRegion *mr, mr->ram_addr = qemu_ram_alloc(size, mr, errp); } +void memory_region_init_resizeable_ram(MemoryRegion *mr, + Object *owner, + const char *name, + uint64_t size, + uint64_t max_size, + void (*resized)(const char*, + uint64_t length, + void *host), + Error **errp) +{ + memory_region_init(mr, owner, name, size); + mr->ram = true; + mr->terminates = true; + mr->destructor = memory_region_destructor_ram; + mr->ram_addr = qemu_ram_alloc_resizeable(size, max_size, resized, mr, errp); +} + #ifdef __linux__ void memory_region_init_ram_from_file(MemoryRegion *mr, struct Object *owner, @@ -1246,7 +1249,6 @@ static void memory_region_finalize(Object *obj) MemoryRegion *mr = MEMORY_REGION(obj); assert(QTAILQ_EMPTY(&mr->subregions)); - assert(memory_region_transaction_depth == 0); mr->destructor(mr); memory_region_clear_coalescing(mr); g_free((char *)mr->name); @@ -1707,6 +1709,22 @@ void memory_region_set_enabled(MemoryRegion *mr, bool enabled) memory_region_transaction_commit(); } +void memory_region_set_size(MemoryRegion *mr, uint64_t size) +{ + Int128 s = int128_make64(size); + + if (size == UINT64_MAX) { + s = int128_2_64(); + } + if (int128_eq(s, mr->size)) { + return; + } + memory_region_transaction_begin(); + mr->size = s; + memory_region_update_pending = true; + memory_region_transaction_commit(); +} + static void memory_region_readd_subregion(MemoryRegion *mr) { MemoryRegion *container = mr->container; @@ -1810,11 +1828,11 @@ MemoryRegionSection memory_region_find(MemoryRegion *mr, } range = addrrange_make(int128_make64(addr), int128_make64(size)); - view = address_space_get_flatview(as); + rcu_read_lock(); + view = atomic_rcu_read(&as->current_map); fr = flatview_lookup(view, range); if (!fr) { - flatview_unref(view); - return ret; + goto out; } while (fr > view->ranges && addrrange_intersects(fr[-1].addr, range)) { @@ -1831,8 +1849,8 @@ MemoryRegionSection memory_region_find(MemoryRegion *mr, ret.offset_within_address_space = int128_get64(range.start); ret.readonly = fr->readonly; memory_region_ref(ret.mr); - - flatview_unref(view); +out: + rcu_read_unlock(); return ret; } @@ -1925,10 +1943,7 @@ void memory_listener_unregister(MemoryListener *listener) void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name) { - if (QTAILQ_EMPTY(&address_spaces)) { - memory_init(); - } - + memory_region_ref(root); memory_region_transaction_begin(); as->root = root; as->current_map = g_new(FlatView, 1); @@ -1942,15 +1957,10 @@ void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name) memory_region_transaction_commit(); } -void address_space_destroy(AddressSpace *as) +static void do_address_space_destroy(AddressSpace *as) { MemoryListener *listener; - /* Flush out anything from MemoryListeners listening in on this */ - memory_region_transaction_begin(); - as->root = NULL; - memory_region_transaction_commit(); - QTAILQ_REMOVE(&address_spaces, as, address_spaces_link); address_space_destroy_dispatch(as); QTAILQ_FOREACH(listener, &memory_listeners, link) { @@ -1960,6 +1970,26 @@ void address_space_destroy(AddressSpace *as) flatview_unref(as->current_map); g_free(as->name); g_free(as->ioeventfds); + memory_region_unref(as->root); +} + +void address_space_destroy(AddressSpace *as) +{ + MemoryRegion *root = as->root; + + /* Flush out anything from MemoryListeners listening in on this */ + memory_region_transaction_begin(); + as->root = NULL; + memory_region_transaction_commit(); + QTAILQ_REMOVE(&address_spaces, as, address_spaces_link); + address_space_unregister(as); + + /* At this point, as->dispatch and as->current_map are dummy + * entries that the guest should never use. Wait for the old + * values to expire before freeing the data. + */ + as->root = root; + call_rcu(as, do_address_space_destroy, rcu); } bool io_mem_read(MemoryRegion *mr, hwaddr addr, uint64_t *pval, unsigned size) diff --git a/migration/Makefile.objs b/migration/Makefile.objs new file mode 100644 index 0000000..d929e96 --- /dev/null +++ b/migration/Makefile.objs @@ -0,0 +1,10 @@ +common-obj-y += migration.o tcp.o +common-obj-y += vmstate.o +common-obj-y += qemu-file.o qemu-file-buf.o qemu-file-unix.o qemu-file-stdio.o +common-obj-y += xbzrle.o + +common-obj-$(CONFIG_RDMA) += rdma.o +common-obj-$(CONFIG_POSIX) += exec.o unix.o fd.o + +common-obj-y += block.o + diff --git a/block-migration.c b/migration/block.c similarity index 97% rename from block-migration.c rename to migration/block.c index 08db01a..085c0fa 100644 --- a/block-migration.c +++ b/migration/block.c @@ -23,6 +23,7 @@ #include "migration/block.h" #include "migration/migration.h" #include "sysemu/blockdev.h" +#include "sysemu/block-backend.h" #include #define BLOCK_SIZE (1 << 20) @@ -303,7 +304,7 @@ static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds) blk->aiocb = bdrv_aio_readv(bs, cur_sector, &blk->qiov, nr_sectors, blk_mig_read_cb, blk); - bdrv_reset_dirty(bs, cur_sector, nr_sectors); + bdrv_reset_dirty_bitmap(bs, bmds->dirty_bitmap, cur_sector, nr_sectors); qemu_mutex_unlock_iothread(); bmds->cur_sector = cur_sector + nr_sectors; @@ -496,7 +497,8 @@ static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds, g_free(blk); } - bdrv_reset_dirty(bmds->bs, sector, nr_sectors); + bdrv_reset_dirty_bitmap(bmds->bs, bmds->dirty_bitmap, sector, + nr_sectors); break; } sector += BDRV_SECTORS_PER_DIRTY_CHUNK; @@ -653,6 +655,7 @@ static int block_save_iterate(QEMUFile *f, void *opaque) { int ret; int64_t last_ftell = qemu_ftell(f); + int64_t delta_ftell; DPRINTF("Enter save live iterate submitted %d transferred %d\n", block_mig_state.submitted, block_mig_state.transferred); @@ -702,7 +705,14 @@ static int block_save_iterate(QEMUFile *f, void *opaque) } qemu_put_be64(f, BLK_MIG_FLAG_EOS); - return qemu_ftell(f) - last_ftell; + delta_ftell = qemu_ftell(f) - last_ftell; + if (delta_ftell > 0) { + return 1; + } else if (delta_ftell < 0) { + return -1; + } else { + return 0; + } } /* Called with iothread lock taken. */ @@ -757,8 +767,8 @@ static uint64_t block_save_pending(QEMUFile *f, void *opaque, uint64_t max_size) block_mig_state.read_done * BLOCK_SIZE; /* Report at least one block pending during bulk phase */ - if (pending == 0 && !block_mig_state.bulk_completed) { - pending = BLOCK_SIZE; + if (pending <= max_size && !block_mig_state.bulk_completed) { + pending = max_size + BLOCK_SIZE; } blk_mig_unlock(); qemu_mutex_unlock_iothread(); @@ -774,6 +784,7 @@ static int block_load(QEMUFile *f, void *opaque, int version_id) char device_name[256]; int64_t addr; BlockDriverState *bs, *bs_prev = NULL; + BlockBackend *blk; uint8_t *buf; int64_t total_sectors = 0; int nr_sectors; @@ -791,12 +802,13 @@ static int block_load(QEMUFile *f, void *opaque, int version_id) qemu_get_buffer(f, (uint8_t *)device_name, len); device_name[len] = '\0'; - bs = bdrv_find(device_name); - if (!bs) { + blk = blk_by_name(device_name); + if (!blk) { fprintf(stderr, "Error unknown block device %s\n", device_name); return -EINVAL; } + bs = blk_bs(blk); if (bs != bs_prev) { bs_prev = bs; diff --git a/migration-exec.c b/migration/exec.c similarity index 100% rename from migration-exec.c rename to migration/exec.c diff --git a/migration-fd.c b/migration/fd.c similarity index 77% rename from migration-fd.c rename to migration/fd.c index d2e523a..129da99 100644 --- a/migration-fd.c +++ b/migration/fd.c @@ -31,13 +31,29 @@ do { } while (0) #endif +static bool fd_is_socket(int fd) +{ + struct stat stat; + int ret = fstat(fd, &stat); + if (ret == -1) { + /* When in doubt say no */ + return false; + } + return S_ISSOCK(stat.st_mode); +} + void fd_start_outgoing_migration(MigrationState *s, const char *fdname, Error **errp) { int fd = monitor_get_fd(cur_mon, fdname, errp); if (fd == -1) { return; } - s->file = qemu_fdopen(fd, "wb"); + + if (fd_is_socket(fd)) { + s->file = qemu_fopen_socket(fd, "wb"); + } else { + s->file = qemu_fdopen(fd, "wb"); + } migrate_fd_connect(s); } @@ -58,7 +74,11 @@ void fd_start_incoming_migration(const char *infd, Error **errp) DPRINTF("Attempting to start an incoming migration via fd\n"); fd = strtol(infd, NULL, 0); - f = qemu_fdopen(fd, "rb"); + if (fd_is_socket(fd)) { + f = qemu_fopen_socket(fd, "rb"); + } else { + f = qemu_fdopen(fd, "rb"); + } if(f == NULL) { error_setg_errno(errp, errno, "failed to open the source descriptor"); return; diff --git a/migration.c b/migration/migration.c similarity index 82% rename from migration.c rename to migration/migration.c index c49a05a..bc42490 100644 --- a/migration.c +++ b/migration/migration.c @@ -26,16 +26,6 @@ #include "qmp-commands.h" #include "trace.h" -enum { - MIG_STATE_ERROR = -1, - MIG_STATE_NONE, - MIG_STATE_SETUP, - MIG_STATE_CANCELLING, - MIG_STATE_CANCELLED, - MIG_STATE_ACTIVE, - MIG_STATE_COMPLETED, -}; - #define MAX_THROTTLE (32 << 20) /* Migration speed throttling */ /* Amount of time to allocate to each "chunk" of bandwidth-throttled @@ -49,6 +39,8 @@ enum { static NotifierList migration_state_notifiers = NOTIFIER_LIST_INITIALIZER(migration_state_notifiers); +static bool deferred_incoming; + /* When we add fault tolerance, we could have several migrations at once. For now we don't need to add dynamic creation of migration */ @@ -56,7 +48,7 @@ static NotifierList migration_state_notifiers = MigrationState *migrate_get_current(void) { static MigrationState current_migration = { - .state = MIG_STATE_NONE, + .state = MIGRATION_STATUS_NONE, .bandwidth_limit = MAX_THROTTLE, .xbzrle_cache_size = DEFAULT_MIGRATE_CACHE_SIZE, .mbps = -1, @@ -65,25 +57,40 @@ MigrationState *migrate_get_current(void) return ¤t_migration; } +/* + * Called on -incoming with a defer: uri. + * The migration can be started later after any parameters have been + * changed. + */ +static void deferred_incoming_migration(Error **errp) +{ + if (deferred_incoming) { + error_setg(errp, "Incoming migration already deferred"); + } + deferred_incoming = true; +} + void qemu_start_incoming_migration(const char *uri, Error **errp) { const char *p; - if (strstart(uri, "tcp:", &p)) + if (!strcmp(uri, "defer")) { + deferred_incoming_migration(errp); + } else if (strstart(uri, "tcp:", &p)) { tcp_start_incoming_migration(p, errp); #ifdef CONFIG_RDMA - else if (strstart(uri, "rdma:", &p)) + } else if (strstart(uri, "rdma:", &p)) { rdma_start_incoming_migration(p, errp); #endif #if !defined(WIN32) - else if (strstart(uri, "exec:", &p)) + } else if (strstart(uri, "exec:", &p)) { exec_start_incoming_migration(p, errp); - else if (strstart(uri, "unix:", &p)) + } else if (strstart(uri, "unix:", &p)) { unix_start_incoming_migration(p, errp); - else if (strstart(uri, "fd:", &p)) + } else if (strstart(uri, "fd:", &p)) { fd_start_incoming_migration(p, errp); #endif - else { + } else { error_setg(errp, "unknown migration protocol: %s", uri); } } @@ -106,8 +113,7 @@ static void process_incoming_migration_co(void *opaque) /* Make sure all file formats flush their mutable metadata */ bdrv_invalidate_cache_all(&local_err); if (local_err) { - qerror_report_err(local_err); - error_free(local_err); + error_report_err(local_err); exit(EXIT_FAILURE); } @@ -184,18 +190,16 @@ MigrationInfo *qmp_query_migrate(Error **errp) MigrationState *s = migrate_get_current(); switch (s->state) { - case MIG_STATE_NONE: + case MIGRATION_STATUS_NONE: /* no migration has happened ever */ break; - case MIG_STATE_SETUP: + case MIGRATION_STATUS_SETUP: info->has_status = true; - info->status = g_strdup("setup"); info->has_total_time = false; break; - case MIG_STATE_ACTIVE: - case MIG_STATE_CANCELLING: + case MIGRATION_STATUS_ACTIVE: + case MIGRATION_STATUS_CANCELLING: info->has_status = true; - info->status = g_strdup("active"); info->has_total_time = true; info->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - s->total_time; @@ -227,11 +231,10 @@ MigrationInfo *qmp_query_migrate(Error **errp) get_xbzrle_cache_stats(info); break; - case MIG_STATE_COMPLETED: + case MIGRATION_STATUS_COMPLETED: get_xbzrle_cache_stats(info); info->has_status = true; - info->status = g_strdup("completed"); info->has_total_time = true; info->total_time = s->total_time; info->has_downtime = true; @@ -251,15 +254,14 @@ MigrationInfo *qmp_query_migrate(Error **errp) info->ram->mbps = s->mbps; info->ram->dirty_sync_count = s->dirty_sync_count; break; - case MIG_STATE_ERROR: + case MIGRATION_STATUS_FAILED: info->has_status = true; - info->status = g_strdup("failed"); break; - case MIG_STATE_CANCELLED: + case MIGRATION_STATUS_CANCELLED: info->has_status = true; - info->status = g_strdup("cancelled"); break; } + info->status = s->state; return info; } @@ -270,7 +272,8 @@ void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params, MigrationState *s = migrate_get_current(); MigrationCapabilityStatusList *cap; - if (s->state == MIG_STATE_ACTIVE || s->state == MIG_STATE_SETUP) { + if (s->state == MIGRATION_STATUS_ACTIVE || + s->state == MIGRATION_STATUS_SETUP) { error_set(errp, QERR_MIGRATION_ACTIVE); return; } @@ -306,12 +309,13 @@ static void migrate_fd_cleanup(void *opaque) s->file = NULL; } - assert(s->state != MIG_STATE_ACTIVE); + assert(s->state != MIGRATION_STATUS_ACTIVE); - if (s->state != MIG_STATE_COMPLETED) { + if (s->state != MIGRATION_STATUS_COMPLETED) { qemu_savevm_state_cancel(); - if (s->state == MIG_STATE_CANCELLING) { - migrate_set_state(s, MIG_STATE_CANCELLING, MIG_STATE_CANCELLED); + if (s->state == MIGRATION_STATUS_CANCELLING) { + migrate_set_state(s, MIGRATION_STATUS_CANCELLING, + MIGRATION_STATUS_CANCELLED); } } @@ -322,23 +326,36 @@ void migrate_fd_error(MigrationState *s) { trace_migrate_fd_error(); assert(s->file == NULL); - s->state = MIG_STATE_ERROR; - trace_migrate_set_state(MIG_STATE_ERROR); + s->state = MIGRATION_STATUS_FAILED; + trace_migrate_set_state(MIGRATION_STATUS_FAILED); notifier_list_notify(&migration_state_notifiers, s); } static void migrate_fd_cancel(MigrationState *s) { int old_state ; + QEMUFile *f = migrate_get_current()->file; trace_migrate_fd_cancel(); do { old_state = s->state; - if (old_state != MIG_STATE_SETUP && old_state != MIG_STATE_ACTIVE) { + if (old_state != MIGRATION_STATUS_SETUP && + old_state != MIGRATION_STATUS_ACTIVE) { break; } - migrate_set_state(s, old_state, MIG_STATE_CANCELLING); - } while (s->state != MIG_STATE_CANCELLING); + migrate_set_state(s, old_state, MIGRATION_STATUS_CANCELLING); + } while (s->state != MIGRATION_STATUS_CANCELLING); + + /* + * If we're unlucky the migration code might be stuck somewhere in a + * send/write while the network has failed and is waiting to timeout; + * if we've got shutdown(2) available then we can force it to quit. + * The outgoing qemu file gets closed in migrate_fd_cleanup that is + * called in a bh, so there is no race against this cancel. + */ + if (s->state == MIGRATION_STATUS_CANCELLING && f) { + qemu_file_shutdown(f); + } } void add_migration_state_change_notifier(Notifier *notify) @@ -353,18 +370,18 @@ void remove_migration_state_change_notifier(Notifier *notify) bool migration_in_setup(MigrationState *s) { - return s->state == MIG_STATE_SETUP; + return s->state == MIGRATION_STATUS_SETUP; } bool migration_has_finished(MigrationState *s) { - return s->state == MIG_STATE_COMPLETED; + return s->state == MIGRATION_STATUS_COMPLETED; } bool migration_has_failed(MigrationState *s) { - return (s->state == MIG_STATE_CANCELLED || - s->state == MIG_STATE_ERROR); + return (s->state == MIGRATION_STATUS_CANCELLED || + s->state == MIGRATION_STATUS_FAILED); } static MigrationState *migrate_init(const MigrationParams *params) @@ -384,8 +401,8 @@ static MigrationState *migrate_init(const MigrationParams *params) s->xbzrle_cache_size = xbzrle_cache_size; s->bandwidth_limit = bandwidth_limit; - s->state = MIG_STATE_SETUP; - trace_migrate_set_state(MIG_STATE_SETUP); + s->state = MIGRATION_STATUS_SETUP; + trace_migrate_set_state(MIGRATION_STATUS_SETUP); s->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); return s; @@ -403,6 +420,29 @@ void migrate_del_blocker(Error *reason) migration_blockers = g_slist_remove(migration_blockers, reason); } +void qmp_migrate_incoming(const char *uri, Error **errp) +{ + Error *local_err = NULL; + static bool once = true; + + if (!deferred_incoming) { + error_setg(errp, "For use with '-incoming defer'"); + return; + } + if (!once) { + error_setg(errp, "The incoming migration has already been started"); + } + + qemu_start_incoming_migration(uri, &local_err); + + if (local_err) { + error_propagate(errp, local_err); + return; + } + + once = false; +} + void qmp_migrate(const char *uri, bool has_blk, bool blk, bool has_inc, bool inc, bool has_detach, bool detach, Error **errp) @@ -415,8 +455,9 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk, params.blk = has_blk && blk; params.shared = has_inc && inc; - if (s->state == MIG_STATE_ACTIVE || s->state == MIG_STATE_SETUP || - s->state == MIG_STATE_CANCELLING) { + if (s->state == MIGRATION_STATUS_ACTIVE || + s->state == MIGRATION_STATUS_SETUP || + s->state == MIGRATION_STATUS_CANCELLING) { error_set(errp, QERR_MIGRATION_ACTIVE); return; } @@ -453,7 +494,7 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk, #endif } else { error_set(errp, QERR_INVALID_PARAMETER_VALUE, "uri", "a valid migration protocol"); - s->state = MIG_STATE_ERROR; + s->state = MIGRATION_STATUS_FAILED; return; } @@ -528,15 +569,6 @@ void qmp_migrate_set_downtime(double value, Error **errp) max_downtime = (uint64_t)value; } -bool migrate_rdma_pin_all(void) -{ - MigrationState *s; - - s = migrate_get_current(); - - return s->enabled_capabilities[MIGRATION_CAPABILITY_RDMA_PIN_ALL]; -} - bool migrate_auto_converge(void) { MigrationState *s; @@ -588,9 +620,9 @@ static void *migration_thread(void *opaque) qemu_savevm_state_begin(s->file, &s->params); s->setup_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) - setup_start; - migrate_set_state(s, MIG_STATE_SETUP, MIG_STATE_ACTIVE); + migrate_set_state(s, MIGRATION_STATUS_SETUP, MIGRATION_STATUS_ACTIVE); - while (s->state == MIG_STATE_ACTIVE) { + while (s->state == MIGRATION_STATUS_ACTIVE) { int64_t current_time; uint64_t pending_size; @@ -615,19 +647,22 @@ static void *migration_thread(void *opaque) qemu_mutex_unlock_iothread(); if (ret < 0) { - migrate_set_state(s, MIG_STATE_ACTIVE, MIG_STATE_ERROR); + migrate_set_state(s, MIGRATION_STATUS_ACTIVE, + MIGRATION_STATUS_FAILED); break; } if (!qemu_file_get_error(s->file)) { - migrate_set_state(s, MIG_STATE_ACTIVE, MIG_STATE_COMPLETED); + migrate_set_state(s, MIGRATION_STATUS_ACTIVE, + MIGRATION_STATUS_COMPLETED); break; } } } if (qemu_file_get_error(s->file)) { - migrate_set_state(s, MIG_STATE_ACTIVE, MIG_STATE_ERROR); + migrate_set_state(s, MIGRATION_STATUS_ACTIVE, + MIGRATION_STATUS_FAILED); break; } current_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); @@ -659,7 +694,7 @@ static void *migration_thread(void *opaque) } qemu_mutex_lock_iothread(); - if (s->state == MIG_STATE_COMPLETED) { + if (s->state == MIGRATION_STATUS_COMPLETED) { int64_t end_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); uint64_t transferred_bytes = qemu_ftell(s->file); s->total_time = end_time - s->total_time; @@ -682,8 +717,8 @@ static void *migration_thread(void *opaque) void migrate_fd_connect(MigrationState *s) { - s->state = MIG_STATE_SETUP; - trace_migrate_set_state(MIG_STATE_SETUP); + s->state = MIGRATION_STATUS_SETUP; + trace_migrate_set_state(MIGRATION_STATUS_SETUP); /* This is a best 1st approximation. ns to ms */ s->expected_downtime = max_downtime/1000000; diff --git a/migration/qemu-file-buf.c b/migration/qemu-file-buf.c new file mode 100644 index 0000000..16a51a1 --- /dev/null +++ b/migration/qemu-file-buf.c @@ -0,0 +1,461 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2014 IBM Corp. + * + * Authors: + * Stefan Berger + * + * 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-common.h" +#include "qemu/iov.h" +#include "qemu/sockets.h" +#include "block/coroutine.h" +#include "migration/migration.h" +#include "migration/qemu-file.h" +#include "migration/qemu-file-internal.h" +#include "trace.h" + +#define QSB_CHUNK_SIZE (1 << 10) +#define QSB_MAX_CHUNK_SIZE (16 * QSB_CHUNK_SIZE) + +/** + * Create a QEMUSizedBuffer + * This type of buffer uses scatter-gather lists internally and + * can grow to any size. Any data array in the scatter-gather list + * can hold different amount of bytes. + * + * @buffer: Optional buffer to copy into the QSB + * @len: size of initial buffer; if @buffer is given, buffer must + * hold at least len bytes + * + * Returns a pointer to a QEMUSizedBuffer or NULL on allocation failure + */ +QEMUSizedBuffer *qsb_create(const uint8_t *buffer, size_t len) +{ + QEMUSizedBuffer *qsb; + size_t alloc_len, num_chunks, i, to_copy; + size_t chunk_size = (len > QSB_MAX_CHUNK_SIZE) + ? QSB_MAX_CHUNK_SIZE + : QSB_CHUNK_SIZE; + + num_chunks = DIV_ROUND_UP(len ? len : QSB_CHUNK_SIZE, chunk_size); + alloc_len = num_chunks * chunk_size; + + qsb = g_try_new0(QEMUSizedBuffer, 1); + if (!qsb) { + return NULL; + } + + qsb->iov = g_try_new0(struct iovec, num_chunks); + if (!qsb->iov) { + g_free(qsb); + return NULL; + } + + qsb->n_iov = num_chunks; + + for (i = 0; i < num_chunks; i++) { + qsb->iov[i].iov_base = g_try_malloc0(chunk_size); + if (!qsb->iov[i].iov_base) { + /* qsb_free is safe since g_free can cope with NULL */ + qsb_free(qsb); + return NULL; + } + + qsb->iov[i].iov_len = chunk_size; + if (buffer) { + to_copy = (len - qsb->used) > chunk_size + ? chunk_size : (len - qsb->used); + memcpy(qsb->iov[i].iov_base, &buffer[qsb->used], to_copy); + qsb->used += to_copy; + } + } + + qsb->size = alloc_len; + + return qsb; +} + +/** + * Free the QEMUSizedBuffer + * + * @qsb: The QEMUSizedBuffer to free + */ +void qsb_free(QEMUSizedBuffer *qsb) +{ + size_t i; + + if (!qsb) { + return; + } + + for (i = 0; i < qsb->n_iov; i++) { + g_free(qsb->iov[i].iov_base); + } + g_free(qsb->iov); + g_free(qsb); +} + +/** + * Get the number of used bytes in the QEMUSizedBuffer + * + * @qsb: A QEMUSizedBuffer + * + * Returns the number of bytes currently used in this buffer + */ +size_t qsb_get_length(const QEMUSizedBuffer *qsb) +{ + return qsb->used; +} + +/** + * Set the length of the buffer; the primary usage of this + * function is to truncate the number of used bytes in the buffer. + * The size will not be extended beyond the current number of + * allocated bytes in the QEMUSizedBuffer. + * + * @qsb: A QEMUSizedBuffer + * @new_len: The new length of bytes in the buffer + * + * Returns the number of bytes the buffer was truncated or extended + * to. + */ +size_t qsb_set_length(QEMUSizedBuffer *qsb, size_t new_len) +{ + if (new_len <= qsb->size) { + qsb->used = new_len; + } else { + qsb->used = qsb->size; + } + return qsb->used; +} + +/** + * Get the iovec that holds the data for a given position @pos. + * + * @qsb: A QEMUSizedBuffer + * @pos: The index of a byte in the buffer + * @d_off: Pointer to an offset that this function will indicate + * at what position within the returned iovec the byte + * is to be found + * + * Returns the index of the iovec that holds the byte at the given + * index @pos in the byte stream; a negative number if the iovec + * for the given position @pos does not exist. + */ +static ssize_t qsb_get_iovec(const QEMUSizedBuffer *qsb, + off_t pos, off_t *d_off) +{ + ssize_t i; + off_t curr = 0; + + if (pos > qsb->used) { + return -1; + } + + for (i = 0; i < qsb->n_iov; i++) { + if (curr + qsb->iov[i].iov_len > pos) { + *d_off = pos - curr; + return i; + } + curr += qsb->iov[i].iov_len; + } + return -1; +} + +/* + * Convert the QEMUSizedBuffer into a flat buffer. + * + * Note: If at all possible, try to avoid this function since it + * may unnecessarily copy memory around. + * + * @qsb: pointer to QEMUSizedBuffer + * @start: offset to start at + * @count: number of bytes to copy + * @buf: a pointer to a buffer to write into (at least @count bytes) + * + * Returns the number of bytes copied into the output buffer + */ +ssize_t qsb_get_buffer(const QEMUSizedBuffer *qsb, off_t start, + size_t count, uint8_t *buffer) +{ + const struct iovec *iov; + size_t to_copy, all_copy; + ssize_t index; + off_t s_off; + off_t d_off = 0; + char *s; + + if (start > qsb->used) { + return 0; + } + + all_copy = qsb->used - start; + if (all_copy > count) { + all_copy = count; + } else { + count = all_copy; + } + + index = qsb_get_iovec(qsb, start, &s_off); + if (index < 0) { + return 0; + } + + while (all_copy > 0) { + iov = &qsb->iov[index]; + + s = iov->iov_base; + + to_copy = iov->iov_len - s_off; + if (to_copy > all_copy) { + to_copy = all_copy; + } + memcpy(&buffer[d_off], &s[s_off], to_copy); + + d_off += to_copy; + all_copy -= to_copy; + + s_off = 0; + index++; + } + + return count; +} + +/** + * Grow the QEMUSizedBuffer to the given size and allocate + * memory for it. + * + * @qsb: A QEMUSizedBuffer + * @new_size: The new size of the buffer + * + * Return: + * a negative error code in case of memory allocation failure + * or + * the new size of the buffer. The returned size may be greater or equal + * to @new_size. + */ +static ssize_t qsb_grow(QEMUSizedBuffer *qsb, size_t new_size) +{ + size_t needed_chunks, i; + + if (qsb->size < new_size) { + struct iovec *new_iov; + size_t size_diff = new_size - qsb->size; + size_t chunk_size = (size_diff > QSB_MAX_CHUNK_SIZE) + ? QSB_MAX_CHUNK_SIZE : QSB_CHUNK_SIZE; + + needed_chunks = DIV_ROUND_UP(size_diff, chunk_size); + + new_iov = g_try_new(struct iovec, qsb->n_iov + needed_chunks); + if (new_iov == NULL) { + return -ENOMEM; + } + + /* Allocate new chunks as needed into new_iov */ + for (i = qsb->n_iov; i < qsb->n_iov + needed_chunks; i++) { + new_iov[i].iov_base = g_try_malloc0(chunk_size); + new_iov[i].iov_len = chunk_size; + if (!new_iov[i].iov_base) { + size_t j; + + /* Free previously allocated new chunks */ + for (j = qsb->n_iov; j < i; j++) { + g_free(new_iov[j].iov_base); + } + g_free(new_iov); + + return -ENOMEM; + } + } + + /* + * Now we can't get any allocation errors, copy over to new iov + * and switch. + */ + for (i = 0; i < qsb->n_iov; i++) { + new_iov[i] = qsb->iov[i]; + } + + qsb->n_iov += needed_chunks; + g_free(qsb->iov); + qsb->iov = new_iov; + qsb->size += (needed_chunks * chunk_size); + } + + return qsb->size; +} + +/** + * Write into the QEMUSizedBuffer at a given position and a given + * number of bytes. This function will automatically grow the + * QEMUSizedBuffer. + * + * @qsb: A QEMUSizedBuffer + * @source: A byte array to copy data from + * @pos: The position within the @qsb to write data to + * @size: The number of bytes to copy into the @qsb + * + * Returns @size or a negative error code in case of memory allocation failure, + * or with an invalid 'pos' + */ +ssize_t qsb_write_at(QEMUSizedBuffer *qsb, const uint8_t *source, + off_t pos, size_t count) +{ + ssize_t rc = qsb_grow(qsb, pos + count); + size_t to_copy; + size_t all_copy = count; + const struct iovec *iov; + ssize_t index; + char *dest; + off_t d_off, s_off = 0; + + if (rc < 0) { + return rc; + } + + if (pos + count > qsb->used) { + qsb->used = pos + count; + } + + index = qsb_get_iovec(qsb, pos, &d_off); + if (index < 0) { + return -EINVAL; + } + + while (all_copy > 0) { + iov = &qsb->iov[index]; + + dest = iov->iov_base; + + to_copy = iov->iov_len - d_off; + if (to_copy > all_copy) { + to_copy = all_copy; + } + + memcpy(&dest[d_off], &source[s_off], to_copy); + + s_off += to_copy; + all_copy -= to_copy; + + d_off = 0; + index++; + } + + return count; +} + +typedef struct QEMUBuffer { + QEMUSizedBuffer *qsb; + QEMUFile *file; + bool qsb_allocated; +} QEMUBuffer; + +static int buf_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) +{ + QEMUBuffer *s = opaque; + ssize_t len = qsb_get_length(s->qsb) - pos; + + if (len <= 0) { + return 0; + } + + if (len > size) { + len = size; + } + return qsb_get_buffer(s->qsb, pos, len, buf); +} + +static int buf_put_buffer(void *opaque, const uint8_t *buf, + int64_t pos, int size) +{ + QEMUBuffer *s = opaque; + + return qsb_write_at(s->qsb, buf, pos, size); +} + +static int buf_close(void *opaque) +{ + QEMUBuffer *s = opaque; + + if (s->qsb_allocated) { + qsb_free(s->qsb); + } + + g_free(s); + + return 0; +} + +const QEMUSizedBuffer *qemu_buf_get(QEMUFile *f) +{ + QEMUBuffer *p; + + qemu_fflush(f); + + p = f->opaque; + + return p->qsb; +} + +static const QEMUFileOps buf_read_ops = { + .get_buffer = buf_get_buffer, + .close = buf_close, +}; + +static const QEMUFileOps buf_write_ops = { + .put_buffer = buf_put_buffer, + .close = buf_close, +}; + +QEMUFile *qemu_bufopen(const char *mode, QEMUSizedBuffer *input) +{ + QEMUBuffer *s; + + if (mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || + mode[1] != '\0') { + error_report("qemu_bufopen: Argument validity check failed"); + return NULL; + } + + s = g_malloc0(sizeof(QEMUBuffer)); + s->qsb = input; + + if (s->qsb == NULL) { + s->qsb = qsb_create(NULL, 0); + s->qsb_allocated = true; + } + if (!s->qsb) { + g_free(s); + error_report("qemu_bufopen: qsb_create failed"); + return NULL; + } + + + if (mode[0] == 'r') { + s->file = qemu_fopen_ops(s, &buf_read_ops); + } else { + s->file = qemu_fopen_ops(s, &buf_write_ops); + } + return s->file; +} diff --git a/migration/qemu-file-internal.h b/migration/qemu-file-internal.h new file mode 100644 index 0000000..d95e853 --- /dev/null +++ b/migration/qemu-file-internal.h @@ -0,0 +1,53 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 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 QEMU_FILE_INTERNAL_H +#define QEMU_FILE_INTERNAL_H 1 + +#include "qemu-common.h" +#include "qemu/iov.h" + +#define IO_BUF_SIZE 32768 +#define MAX_IOV_SIZE MIN(IOV_MAX, 64) + +struct QEMUFile { + const QEMUFileOps *ops; + void *opaque; + + int64_t bytes_xfer; + int64_t xfer_limit; + + int64_t pos; /* start of buffer when writing, end of buffer + when reading */ + int buf_index; + int buf_size; /* 0 when writing */ + uint8_t buf[IO_BUF_SIZE]; + + struct iovec iov[MAX_IOV_SIZE]; + unsigned int iovcnt; + + int last_error; +}; + +#endif diff --git a/qemu-file-stdio.c b/migration/qemu-file-stdio.c similarity index 100% rename from qemu-file-stdio.c rename to migration/qemu-file-stdio.c diff --git a/qemu-file-unix.c b/migration/qemu-file-unix.c similarity index 92% rename from qemu-file-unix.c rename to migration/qemu-file-unix.c index 9682396..bfbc086 100644 --- a/qemu-file-unix.c +++ b/migration/qemu-file-unix.c @@ -26,6 +26,7 @@ #include "qemu/sockets.h" #include "block/coroutine.h" #include "migration/qemu-file.h" +#include "migration/qemu-file-internal.h" typedef struct QEMUFileSocket { int fd; @@ -84,6 +85,17 @@ static int socket_close(void *opaque) return 0; } +static int socket_shutdown(void *opaque, bool rd, bool wr) +{ + QEMUFileSocket *s = opaque; + + if (shutdown(s->fd, rd ? (wr ? SHUT_RDWR : SHUT_RD) : SHUT_WR)) { + return -errno; + } else { + return 0; + } +} + static ssize_t unix_writev_buffer(void *opaque, struct iovec *iov, int iovcnt, int64_t pos) { @@ -192,15 +204,18 @@ QEMUFile *qemu_fdopen(int fd, const char *mode) } static const QEMUFileOps socket_read_ops = { - .get_fd = socket_get_fd, + .get_fd = socket_get_fd, .get_buffer = socket_get_buffer, - .close = socket_close + .close = socket_close, + .shut_down = socket_shutdown + }; static const QEMUFileOps socket_write_ops = { - .get_fd = socket_get_fd, + .get_fd = socket_get_fd, .writev_buffer = socket_writev_buffer, - .close = socket_close + .close = socket_close, + .shut_down = socket_shutdown }; QEMUFile *qemu_fopen_socket(int fd, const char *mode) diff --git a/qemu-file.c b/migration/qemu-file.c similarity index 51% rename from qemu-file.c rename to migration/qemu-file.c index f938e36..1a4f986 100644 --- a/qemu-file.c +++ b/migration/qemu-file.c @@ -27,29 +27,20 @@ #include "block/coroutine.h" #include "migration/migration.h" #include "migration/qemu-file.h" +#include "migration/qemu-file-internal.h" #include "trace.h" -#define IO_BUF_SIZE 32768 -#define MAX_IOV_SIZE MIN(IOV_MAX, 64) - -struct QEMUFile { - const QEMUFileOps *ops; - void *opaque; - - int64_t bytes_xfer; - int64_t xfer_limit; - - int64_t pos; /* start of buffer when writing, end of buffer - when reading */ - int buf_index; - int buf_size; /* 0 when writing */ - uint8_t buf[IO_BUF_SIZE]; - - struct iovec iov[MAX_IOV_SIZE]; - unsigned int iovcnt; - - int last_error; -}; +/* + * Stop a file from being read/written - not all backing files can do this + * typically only sockets can. + */ +int qemu_file_shutdown(QEMUFile *f) +{ + if (!f->ops->shut_down) { + return -ENOSYS; + } + return f->ops->shut_down(f->opaque, true, true); +} bool qemu_file_mode_is_not_valid(const char *mode) { @@ -170,7 +161,8 @@ void ram_control_load_hook(QEMUFile *f, uint64_t flags) } size_t ram_control_save_page(QEMUFile *f, ram_addr_t block_offset, - ram_addr_t offset, size_t size, int *bytes_sent) + ram_addr_t offset, size_t size, + uint64_t *bytes_sent) { if (f->ops->save_page) { int ret = f->ops->save_page(f, f->opaque, block_offset, @@ -461,6 +453,22 @@ int qemu_get_byte(QEMUFile *f) return result; } +int64_t qemu_ftell_fast(QEMUFile *f) +{ + int64_t ret = f->pos; + int i; + + if (f->ops->writev_buffer) { + for (i = 0; i < f->iovcnt; i++) { + ret += f->iov[i].iov_len; + } + } else { + ret += f->buf_index; + } + + return ret; +} + int64_t qemu_ftell(QEMUFile *f) { qemu_fflush(f); @@ -524,7 +532,7 @@ unsigned int qemu_get_be16(QEMUFile *f) unsigned int qemu_get_be32(QEMUFile *f) { unsigned int v; - v = qemu_get_byte(f) << 24; + v = (unsigned int)qemu_get_byte(f) << 24; v |= qemu_get_byte(f) << 16; v |= qemu_get_byte(f) << 8; v |= qemu_get_byte(f); @@ -538,458 +546,3 @@ uint64_t qemu_get_be64(QEMUFile *f) v |= qemu_get_be32(f); return v; } - -#define QSB_CHUNK_SIZE (1 << 10) -#define QSB_MAX_CHUNK_SIZE (16 * QSB_CHUNK_SIZE) - -/** - * Create a QEMUSizedBuffer - * This type of buffer uses scatter-gather lists internally and - * can grow to any size. Any data array in the scatter-gather list - * can hold different amount of bytes. - * - * @buffer: Optional buffer to copy into the QSB - * @len: size of initial buffer; if @buffer is given, buffer must - * hold at least len bytes - * - * Returns a pointer to a QEMUSizedBuffer or NULL on allocation failure - */ -QEMUSizedBuffer *qsb_create(const uint8_t *buffer, size_t len) -{ - QEMUSizedBuffer *qsb; - size_t alloc_len, num_chunks, i, to_copy; - size_t chunk_size = (len > QSB_MAX_CHUNK_SIZE) - ? QSB_MAX_CHUNK_SIZE - : QSB_CHUNK_SIZE; - - num_chunks = DIV_ROUND_UP(len ? len : QSB_CHUNK_SIZE, chunk_size); - alloc_len = num_chunks * chunk_size; - - qsb = g_try_new0(QEMUSizedBuffer, 1); - if (!qsb) { - return NULL; - } - - qsb->iov = g_try_new0(struct iovec, num_chunks); - if (!qsb->iov) { - g_free(qsb); - return NULL; - } - - qsb->n_iov = num_chunks; - - for (i = 0; i < num_chunks; i++) { - qsb->iov[i].iov_base = g_try_malloc0(chunk_size); - if (!qsb->iov[i].iov_base) { - /* qsb_free is safe since g_free can cope with NULL */ - qsb_free(qsb); - return NULL; - } - - qsb->iov[i].iov_len = chunk_size; - if (buffer) { - to_copy = (len - qsb->used) > chunk_size - ? chunk_size : (len - qsb->used); - memcpy(qsb->iov[i].iov_base, &buffer[qsb->used], to_copy); - qsb->used += to_copy; - } - } - - qsb->size = alloc_len; - - return qsb; -} - -/** - * Free the QEMUSizedBuffer - * - * @qsb: The QEMUSizedBuffer to free - */ -void qsb_free(QEMUSizedBuffer *qsb) -{ - size_t i; - - if (!qsb) { - return; - } - - for (i = 0; i < qsb->n_iov; i++) { - g_free(qsb->iov[i].iov_base); - } - g_free(qsb->iov); - g_free(qsb); -} - -/** - * Get the number of used bytes in the QEMUSizedBuffer - * - * @qsb: A QEMUSizedBuffer - * - * Returns the number of bytes currently used in this buffer - */ -size_t qsb_get_length(const QEMUSizedBuffer *qsb) -{ - return qsb->used; -} - -/** - * Set the length of the buffer; the primary usage of this - * function is to truncate the number of used bytes in the buffer. - * The size will not be extended beyond the current number of - * allocated bytes in the QEMUSizedBuffer. - * - * @qsb: A QEMUSizedBuffer - * @new_len: The new length of bytes in the buffer - * - * Returns the number of bytes the buffer was truncated or extended - * to. - */ -size_t qsb_set_length(QEMUSizedBuffer *qsb, size_t new_len) -{ - if (new_len <= qsb->size) { - qsb->used = new_len; - } else { - qsb->used = qsb->size; - } - return qsb->used; -} - -/** - * Get the iovec that holds the data for a given position @pos. - * - * @qsb: A QEMUSizedBuffer - * @pos: The index of a byte in the buffer - * @d_off: Pointer to an offset that this function will indicate - * at what position within the returned iovec the byte - * is to be found - * - * Returns the index of the iovec that holds the byte at the given - * index @pos in the byte stream; a negative number if the iovec - * for the given position @pos does not exist. - */ -static ssize_t qsb_get_iovec(const QEMUSizedBuffer *qsb, - off_t pos, off_t *d_off) -{ - ssize_t i; - off_t curr = 0; - - if (pos > qsb->used) { - return -1; - } - - for (i = 0; i < qsb->n_iov; i++) { - if (curr + qsb->iov[i].iov_len > pos) { - *d_off = pos - curr; - return i; - } - curr += qsb->iov[i].iov_len; - } - return -1; -} - -/* - * Convert the QEMUSizedBuffer into a flat buffer. - * - * Note: If at all possible, try to avoid this function since it - * may unnecessarily copy memory around. - * - * @qsb: pointer to QEMUSizedBuffer - * @start: offset to start at - * @count: number of bytes to copy - * @buf: a pointer to a buffer to write into (at least @count bytes) - * - * Returns the number of bytes copied into the output buffer - */ -ssize_t qsb_get_buffer(const QEMUSizedBuffer *qsb, off_t start, - size_t count, uint8_t *buffer) -{ - const struct iovec *iov; - size_t to_copy, all_copy; - ssize_t index; - off_t s_off; - off_t d_off = 0; - char *s; - - if (start > qsb->used) { - return 0; - } - - all_copy = qsb->used - start; - if (all_copy > count) { - all_copy = count; - } else { - count = all_copy; - } - - index = qsb_get_iovec(qsb, start, &s_off); - if (index < 0) { - return 0; - } - - while (all_copy > 0) { - iov = &qsb->iov[index]; - - s = iov->iov_base; - - to_copy = iov->iov_len - s_off; - if (to_copy > all_copy) { - to_copy = all_copy; - } - memcpy(&buffer[d_off], &s[s_off], to_copy); - - d_off += to_copy; - all_copy -= to_copy; - - s_off = 0; - index++; - } - - return count; -} - -/** - * Grow the QEMUSizedBuffer to the given size and allocate - * memory for it. - * - * @qsb: A QEMUSizedBuffer - * @new_size: The new size of the buffer - * - * Return: - * a negative error code in case of memory allocation failure - * or - * the new size of the buffer. The returned size may be greater or equal - * to @new_size. - */ -static ssize_t qsb_grow(QEMUSizedBuffer *qsb, size_t new_size) -{ - size_t needed_chunks, i; - - if (qsb->size < new_size) { - struct iovec *new_iov; - size_t size_diff = new_size - qsb->size; - size_t chunk_size = (size_diff > QSB_MAX_CHUNK_SIZE) - ? QSB_MAX_CHUNK_SIZE : QSB_CHUNK_SIZE; - - needed_chunks = DIV_ROUND_UP(size_diff, chunk_size); - - new_iov = g_try_new(struct iovec, qsb->n_iov + needed_chunks); - if (new_iov == NULL) { - return -ENOMEM; - } - - /* Allocate new chunks as needed into new_iov */ - for (i = qsb->n_iov; i < qsb->n_iov + needed_chunks; i++) { - new_iov[i].iov_base = g_try_malloc0(chunk_size); - new_iov[i].iov_len = chunk_size; - if (!new_iov[i].iov_base) { - size_t j; - - /* Free previously allocated new chunks */ - for (j = qsb->n_iov; j < i; j++) { - g_free(new_iov[j].iov_base); - } - g_free(new_iov); - - return -ENOMEM; - } - } - - /* - * Now we can't get any allocation errors, copy over to new iov - * and switch. - */ - for (i = 0; i < qsb->n_iov; i++) { - new_iov[i] = qsb->iov[i]; - } - - qsb->n_iov += needed_chunks; - g_free(qsb->iov); - qsb->iov = new_iov; - qsb->size += (needed_chunks * chunk_size); - } - - return qsb->size; -} - -/** - * Write into the QEMUSizedBuffer at a given position and a given - * number of bytes. This function will automatically grow the - * QEMUSizedBuffer. - * - * @qsb: A QEMUSizedBuffer - * @source: A byte array to copy data from - * @pos: The position within the @qsb to write data to - * @size: The number of bytes to copy into the @qsb - * - * Returns @size or a negative error code in case of memory allocation failure, - * or with an invalid 'pos' - */ -ssize_t qsb_write_at(QEMUSizedBuffer *qsb, const uint8_t *source, - off_t pos, size_t count) -{ - ssize_t rc = qsb_grow(qsb, pos + count); - size_t to_copy; - size_t all_copy = count; - const struct iovec *iov; - ssize_t index; - char *dest; - off_t d_off, s_off = 0; - - if (rc < 0) { - return rc; - } - - if (pos + count > qsb->used) { - qsb->used = pos + count; - } - - index = qsb_get_iovec(qsb, pos, &d_off); - if (index < 0) { - return -EINVAL; - } - - while (all_copy > 0) { - iov = &qsb->iov[index]; - - dest = iov->iov_base; - - to_copy = iov->iov_len - d_off; - if (to_copy > all_copy) { - to_copy = all_copy; - } - - memcpy(&dest[d_off], &source[s_off], to_copy); - - s_off += to_copy; - all_copy -= to_copy; - - d_off = 0; - index++; - } - - return count; -} - -/** - * Create a deep copy of the given QEMUSizedBuffer. - * - * @qsb: A QEMUSizedBuffer - * - * Returns a clone of @qsb or NULL on allocation failure - */ -QEMUSizedBuffer *qsb_clone(const QEMUSizedBuffer *qsb) -{ - QEMUSizedBuffer *out = qsb_create(NULL, qsb_get_length(qsb)); - size_t i; - ssize_t res; - off_t pos = 0; - - if (!out) { - return NULL; - } - - for (i = 0; i < qsb->n_iov; i++) { - res = qsb_write_at(out, qsb->iov[i].iov_base, - pos, qsb->iov[i].iov_len); - if (res < 0) { - qsb_free(out); - return NULL; - } - pos += res; - } - - return out; -} - -typedef struct QEMUBuffer { - QEMUSizedBuffer *qsb; - QEMUFile *file; -} QEMUBuffer; - -static int buf_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) -{ - QEMUBuffer *s = opaque; - ssize_t len = qsb_get_length(s->qsb) - pos; - - if (len <= 0) { - return 0; - } - - if (len > size) { - len = size; - } - return qsb_get_buffer(s->qsb, pos, len, buf); -} - -static int buf_put_buffer(void *opaque, const uint8_t *buf, - int64_t pos, int size) -{ - QEMUBuffer *s = opaque; - - return qsb_write_at(s->qsb, buf, pos, size); -} - -static int buf_close(void *opaque) -{ - QEMUBuffer *s = opaque; - - qsb_free(s->qsb); - - g_free(s); - - return 0; -} - -const QEMUSizedBuffer *qemu_buf_get(QEMUFile *f) -{ - QEMUBuffer *p; - - qemu_fflush(f); - - p = f->opaque; - - return p->qsb; -} - -static const QEMUFileOps buf_read_ops = { - .get_buffer = buf_get_buffer, - .close = buf_close, -}; - -static const QEMUFileOps buf_write_ops = { - .put_buffer = buf_put_buffer, - .close = buf_close, -}; - -QEMUFile *qemu_bufopen(const char *mode, QEMUSizedBuffer *input) -{ - QEMUBuffer *s; - - if (mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || - mode[1] != '\0') { - error_report("qemu_bufopen: Argument validity check failed"); - return NULL; - } - - s = g_malloc0(sizeof(QEMUBuffer)); - if (mode[0] == 'r') { - s->qsb = input; - } - - if (s->qsb == NULL) { - s->qsb = qsb_create(NULL, 0); - } - if (!s->qsb) { - g_free(s); - error_report("qemu_bufopen: qsb_create failed"); - return NULL; - } - - - if (mode[0] == 'r') { - s->file = qemu_fopen_ops(s, &buf_read_ops); - } else { - s->file = qemu_fopen_ops(s, &buf_write_ops); - } - return s->file; -} diff --git a/migration-rdma.c b/migration/rdma.c similarity index 86% rename from migration-rdma.c rename to migration/rdma.c index b32dbdf..77e3444 100644 --- a/migration-rdma.c +++ b/migration/rdma.c @@ -26,34 +26,7 @@ #include #include #include - -//#define DEBUG_RDMA -//#define DEBUG_RDMA_VERBOSE -//#define DEBUG_RDMA_REALLY_VERBOSE - -#ifdef DEBUG_RDMA -#define DPRINTF(fmt, ...) \ - do { printf("rdma: " fmt, ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) \ - do { } while (0) -#endif - -#ifdef DEBUG_RDMA_VERBOSE -#define DDPRINTF(fmt, ...) \ - do { printf("rdma: " fmt, ## __VA_ARGS__); } while (0) -#else -#define DDPRINTF(fmt, ...) \ - do { } while (0) -#endif - -#ifdef DEBUG_RDMA_REALLY_VERBOSE -#define DDDPRINTF(fmt, ...) \ - do { printf("rdma: " fmt, ## __VA_ARGS__); } while (0) -#else -#define DDDPRINTF(fmt, ...) \ - do { } while (0) -#endif +#include "trace.h" /* * Print and error on both the Monitor and the Log file. @@ -104,8 +77,8 @@ static uint32_t known_capabilities = RDMA_CAPABILITY_PIN_ALL; do { \ if (rdma->error_state) { \ if (!rdma->error_reported) { \ - fprintf(stderr, "RDMA is in an error state waiting migration" \ - " to abort!\n"); \ + error_report("RDMA is in an error state waiting migration" \ + " to abort!"); \ rdma->error_reported = 1; \ } \ return rdma->error_state; \ @@ -148,7 +121,7 @@ enum { RDMA_WRID_RECV_CONTROL = 4000, }; -const char *wrid_desc[] = { +static const char *wrid_desc[] = { [RDMA_WRID_NONE] = "NONE", [RDMA_WRID_RDMA_WRITE] = "WRITE RDMA", [RDMA_WRID_SEND_CONTROL] = "CONTROL SEND", @@ -187,7 +160,7 @@ enum { RDMA_CONTROL_UNREGISTER_FINISHED, /* unpinning finished */ }; -const char *control_desc[] = { +static const char *control_desc[] = { [RDMA_CONTROL_NONE] = "NONE", [RDMA_CONTROL_ERROR] = "ERROR", [RDMA_CONTROL_READY] = "READY", @@ -520,8 +493,8 @@ static inline uint64_t ram_chunk_index(const uint8_t *start, static inline uint8_t *ram_chunk_start(const RDMALocalBlock *rdma_ram_block, uint64_t i) { - return (uint8_t *) (((uintptr_t) rdma_ram_block->local_host_addr) - + (i << RDMA_REG_CHUNK_SHIFT)); + return (uint8_t *)(uintptr_t)(rdma_ram_block->local_host_addr + + (i << RDMA_REG_CHUNK_SHIFT)); } static inline uint8_t *ram_chunk_end(const RDMALocalBlock *rdma_ram_block, @@ -537,12 +510,12 @@ static inline uint8_t *ram_chunk_end(const RDMALocalBlock *rdma_ram_block, return result; } -static int __qemu_rdma_add_block(RDMAContext *rdma, void *host_addr, +static int rdma_add_block(RDMAContext *rdma, void *host_addr, ram_addr_t block_offset, uint64_t length) { RDMALocalBlocks *local = &rdma->local_ram_blocks; RDMALocalBlock *block = g_hash_table_lookup(rdma->blockmap, - (void *) block_offset); + (void *)(uintptr_t)block_offset); RDMALocalBlock *old = local->block; assert(block == NULL); @@ -553,9 +526,11 @@ static int __qemu_rdma_add_block(RDMAContext *rdma, void *host_addr, int x; for (x = 0; x < local->nb_blocks; x++) { - g_hash_table_remove(rdma->blockmap, (void *)old[x].offset); - g_hash_table_insert(rdma->blockmap, (void *)old[x].offset, - &local->block[x]); + g_hash_table_remove(rdma->blockmap, + (void *)(uintptr_t)old[x].offset); + g_hash_table_insert(rdma->blockmap, + (void *)(uintptr_t)old[x].offset, + &local->block[x]); } memcpy(local->block, old, sizeof(RDMALocalBlock) * local->nb_blocks); g_free(old); @@ -578,12 +553,12 @@ static int __qemu_rdma_add_block(RDMAContext *rdma, void *host_addr, g_hash_table_insert(rdma->blockmap, (void *) block_offset, block); - DDPRINTF("Added Block: %d, addr: %" PRIu64 ", offset: %" PRIu64 - " length: %" PRIu64 " end: %" PRIu64 " bits %" PRIu64 " chunks %d\n", - local->nb_blocks, (uint64_t) block->local_host_addr, block->offset, - block->length, (uint64_t) (block->local_host_addr + block->length), - BITS_TO_LONGS(block->nb_chunks) * - sizeof(unsigned long) * 8, block->nb_chunks); + trace_rdma_add_block(local->nb_blocks, (uintptr_t) block->local_host_addr, + block->offset, block->length, + (uintptr_t) (block->local_host_addr + block->length), + BITS_TO_LONGS(block->nb_chunks) * + sizeof(unsigned long) * 8, + block->nb_chunks); local->nb_blocks++; @@ -598,7 +573,7 @@ static int __qemu_rdma_add_block(RDMAContext *rdma, void *host_addr, static void qemu_rdma_init_one_block(void *host_addr, ram_addr_t block_offset, ram_addr_t length, void *opaque) { - __qemu_rdma_add_block(opaque, host_addr, block_offset, length); + rdma_add_block(opaque, host_addr, block_offset, length); } /* @@ -614,14 +589,14 @@ static int qemu_rdma_init_ram_blocks(RDMAContext *rdma) rdma->blockmap = g_hash_table_new(g_direct_hash, g_direct_equal); memset(local, 0, sizeof *local); qemu_ram_foreach_block(qemu_rdma_init_one_block, rdma); - DPRINTF("Allocated %d local ram block structures\n", local->nb_blocks); + trace_qemu_rdma_init_ram_blocks(local->nb_blocks); rdma->block = (RDMARemoteBlock *) g_malloc0(sizeof(RDMARemoteBlock) * rdma->local_ram_blocks.nb_blocks); local->init = true; return 0; } -static int __qemu_rdma_delete_block(RDMAContext *rdma, ram_addr_t block_offset) +static int rdma_delete_block(RDMAContext *rdma, ram_addr_t block_offset) { RDMALocalBlocks *local = &rdma->local_ram_blocks; RDMALocalBlock *block = g_hash_table_lookup(rdma->blockmap, @@ -661,7 +636,7 @@ static int __qemu_rdma_delete_block(RDMAContext *rdma, ram_addr_t block_offset) block->remote_keys = NULL; for (x = 0; x < local->nb_blocks; x++) { - g_hash_table_remove(rdma->blockmap, (void *)old[x].offset); + g_hash_table_remove(rdma->blockmap, (void *)(uintptr_t)old[x].offset); } if (local->nb_blocks > 1) { @@ -683,12 +658,12 @@ static int __qemu_rdma_delete_block(RDMAContext *rdma, ram_addr_t block_offset) local->block = NULL; } - DDPRINTF("Deleted Block: %d, addr: %" PRIu64 ", offset: %" PRIu64 - " length: %" PRIu64 " end: %" PRIu64 " bits %" PRIu64 " chunks %d\n", - local->nb_blocks, (uint64_t) block->local_host_addr, block->offset, - block->length, (uint64_t) (block->local_host_addr + block->length), - BITS_TO_LONGS(block->nb_chunks) * - sizeof(unsigned long) * 8, block->nb_chunks); + trace_rdma_delete_block(local->nb_blocks, + (uintptr_t)block->local_host_addr, + block->offset, block->length, + (uintptr_t)(block->local_host_addr + block->length), + BITS_TO_LONGS(block->nb_chunks) * + sizeof(unsigned long) * 8, block->nb_chunks); g_free(old); @@ -696,8 +671,9 @@ static int __qemu_rdma_delete_block(RDMAContext *rdma, ram_addr_t block_offset) if (local->nb_blocks) { for (x = 0; x < local->nb_blocks; x++) { - g_hash_table_insert(rdma->blockmap, (void *)local->block[x].offset, - &local->block[x]); + g_hash_table_insert(rdma->blockmap, + (void *)(uintptr_t)local->block[x].offset, + &local->block[x]); } } @@ -713,7 +689,7 @@ static void qemu_rdma_dump_id(const char *who, struct ibv_context *verbs) struct ibv_port_attr port; if (ibv_query_port(verbs, 1, &port)) { - fprintf(stderr, "FAILED TO QUERY PORT INFORMATION!\n"); + error_report("Failed to query port information"); return; } @@ -729,7 +705,7 @@ static void qemu_rdma_dump_id(const char *who, struct ibv_context *verbs) verbs->device->ibdev_path, port.link_layer, (port.link_layer == IBV_LINK_LAYER_INFINIBAND) ? "Infiniband" : - ((port.link_layer == IBV_LINK_LAYER_ETHERNET) + ((port.link_layer == IBV_LINK_LAYER_ETHERNET) ? "Ethernet" : "Unknown")); } @@ -744,7 +720,7 @@ static void qemu_rdma_dump_gid(const char *who, struct rdma_cm_id *id) char dgid[33]; inet_ntop(AF_INET6, &id->route.addr.addr.ibaddr.sgid, sgid, sizeof sgid); inet_ntop(AF_INET6, &id->route.addr.addr.ibaddr.dgid, dgid, sizeof dgid); - DPRINTF("%s Source GID: %s, Dest GID: %s\n", who, sgid, dgid); + trace_qemu_rdma_dump_gid(who, sgid, dgid); } /* @@ -764,7 +740,7 @@ static void qemu_rdma_dump_gid(const char *who, struct rdma_cm_id *id) * and validate what time of hardware it is. * * Unfortunately, this puts the user in a fix: - * + * * If the source VM connects with an IPv4 address without knowing that the * destination has bound to '[::]' the migration will unconditionally fail * unless the management software is explicitly listening on the the IPv4 @@ -772,13 +748,13 @@ static void qemu_rdma_dump_gid(const char *who, struct rdma_cm_id *id) * * If the source VM connects with an IPv6 address, then we're OK because we can * throw an error on the source (and similarly on the destination). - * + * * But in mixed environments, this will be broken for a while until it is fixed * inside linux. * * We do provide a *tiny* bit of help in this function: We can list all of the * devices in the system and check to see if all the devices are RoCE or - * Infiniband. + * Infiniband. * * If we detect that we have a *pure* RoCE environment, then we can safely * thrown an error even if the management software has specified '[::]' as the @@ -797,17 +773,17 @@ static int qemu_rdma_broken_ipv6_kernel(Error **errp, struct ibv_context *verbs) /* This bug only exists in linux, to our knowledge. */ #ifdef CONFIG_LINUX - /* + /* * Verbs are only NULL if management has bound to '[::]'. - * + * * Let's iterate through all the devices and see if there any pure IB * devices (non-ethernet). - * + * * If not, then we can safely proceed with the migration. * Otherwise, there are no guarantees until the bug is fixed in linux. */ if (!verbs) { - int num_devices, x; + int num_devices, x; struct ibv_device ** dev_list = ibv_get_device_list(&num_devices); bool roce_found = false; bool ib_found = false; @@ -852,8 +828,8 @@ static int qemu_rdma_broken_ipv6_kernel(Error **errp, struct ibv_context *verbs) /* * If we have a verbs context, that means that some other than '[::]' was - * used by the management software for binding. In which case we can actually - * warn the user about a potential broken kernel; + * used by the management software for binding. In which case we can + * actually warn the user about a potentially broken kernel. */ /* IB ports start with 1, not 0 */ @@ -918,7 +894,7 @@ static int qemu_rdma_resolve_host(RDMAContext *rdma, Error **errp) for (e = res; e != NULL; e = e->ai_next) { inet_ntop(e->ai_family, &((struct sockaddr_in *) e->ai_dst_addr)->sin_addr, ip, sizeof ip); - DPRINTF("Trying %s => %s\n", rdma->host, ip); + trace_qemu_rdma_resolve_host_trying(rdma->host, ip); ret = rdma_resolve_addr(rdma->cm_id, NULL, e->ai_dst_addr, RDMA_RESOLVE_TIMEOUT_MS); @@ -997,14 +973,14 @@ static int qemu_rdma_alloc_pd_cq(RDMAContext *rdma) /* allocate pd */ rdma->pd = ibv_alloc_pd(rdma->verbs); if (!rdma->pd) { - fprintf(stderr, "failed to allocate protection domain\n"); + error_report("failed to allocate protection domain"); return -1; } /* create completion channel */ rdma->comp_channel = ibv_create_comp_channel(rdma->verbs); if (!rdma->comp_channel) { - fprintf(stderr, "failed to allocate completion channel\n"); + error_report("failed to allocate completion channel"); goto err_alloc_pd_cq; } @@ -1015,7 +991,7 @@ static int qemu_rdma_alloc_pd_cq(RDMAContext *rdma) rdma->cq = ibv_create_cq(rdma->verbs, (RDMA_SIGNALED_SEND_MAX * 3), NULL, rdma->comp_channel, 0); if (!rdma->cq) { - fprintf(stderr, "failed to allocate completion queue\n"); + error_report("failed to allocate completion queue"); goto err_alloc_pd_cq; } @@ -1102,7 +1078,7 @@ static int qemu_rdma_reg_whole_ram_blocks(RDMAContext *rdma) * This search cannot fail or the migration will fail. */ static int qemu_rdma_search_ram_block(RDMAContext *rdma, - uint64_t block_offset, + uintptr_t block_offset, uint64_t offset, uint64_t length, uint64_t *block_index, @@ -1130,7 +1106,7 @@ static int qemu_rdma_search_ram_block(RDMAContext *rdma, * to perform the actual RDMA operation. */ static int qemu_rdma_register_and_get_keys(RDMAContext *rdma, - RDMALocalBlock *block, uint8_t *host_addr, + RDMALocalBlock *block, uintptr_t host_addr, uint32_t *lkey, uint32_t *rkey, int chunk, uint8_t *chunk_start, uint8_t *chunk_end) { @@ -1147,9 +1123,6 @@ static int qemu_rdma_register_and_get_keys(RDMAContext *rdma, /* allocate memory to store chunk MRs */ if (!block->pmr) { block->pmr = g_malloc0(block->nb_chunks * sizeof(struct ibv_mr *)); - if (!block->pmr) { - return -1; - } } /* @@ -1160,8 +1133,7 @@ static int qemu_rdma_register_and_get_keys(RDMAContext *rdma, if (!block->pmr[chunk]) { uint64_t len = chunk_end - chunk_start; - DDPRINTF("Registering %" PRIu64 " bytes @ %p\n", - len, chunk_start); + trace_qemu_rdma_register_and_get_keys(len, chunk_start); block->pmr[chunk] = ibv_reg_mr(rdma->pd, chunk_start, len, @@ -1171,11 +1143,12 @@ static int qemu_rdma_register_and_get_keys(RDMAContext *rdma, if (!block->pmr[chunk]) { perror("Failed to register chunk!"); fprintf(stderr, "Chunk details: block: %d chunk index %d" - " start %" PRIu64 " end %" PRIu64 " host %" PRIu64 - " local %" PRIu64 " registrations: %d\n", - block->index, chunk, (uint64_t) chunk_start, - (uint64_t) chunk_end, (uint64_t) host_addr, - (uint64_t) block->local_host_addr, + " start %" PRIuPTR " end %" PRIuPTR + " host %" PRIuPTR + " local %" PRIuPTR " registrations: %d\n", + block->index, chunk, (uintptr_t)chunk_start, + (uintptr_t)chunk_end, host_addr, + (uintptr_t)block->local_host_addr, rdma->total_registrations); return -1; } @@ -1204,7 +1177,7 @@ static int qemu_rdma_reg_control(RDMAContext *rdma, int idx) rdma->total_registrations++; return 0; } - fprintf(stderr, "qemu_rdma_reg_control failed!\n"); + error_report("qemu_rdma_reg_control failed"); return -1; } @@ -1270,8 +1243,8 @@ static int qemu_rdma_unregister_waiting(RDMAContext *rdma) .repeat = 1, }; - DDPRINTF("Processing unregister for chunk: %" PRIu64 - " at position %d\n", chunk, rdma->unregister_current); + trace_qemu_rdma_unregister_waiting_proc(chunk, + rdma->unregister_current); rdma->unregistrations[rdma->unregister_current] = 0; rdma->unregister_current++; @@ -1291,11 +1264,11 @@ static int qemu_rdma_unregister_waiting(RDMAContext *rdma) clear_bit(chunk, block->unregister_bitmap); if (test_bit(chunk, block->transit_bitmap)) { - DDPRINTF("Cannot unregister inflight chunk: %" PRIu64 "\n", chunk); + trace_qemu_rdma_unregister_waiting_inflight(chunk); continue; } - DDPRINTF("Sending unregister for chunk: %" PRIu64 "\n", chunk); + trace_qemu_rdma_unregister_waiting_send(chunk); ret = ibv_dereg_mr(block->pmr[chunk]); block->pmr[chunk] = NULL; @@ -1315,7 +1288,7 @@ static int qemu_rdma_unregister_waiting(RDMAContext *rdma) return ret; } - DDPRINTF("Unregister for chunk: %" PRIu64 " complete.\n", chunk); + trace_qemu_rdma_unregister_waiting_complete(chunk); } return 0; @@ -1340,13 +1313,13 @@ static void qemu_rdma_signal_unregister(RDMAContext *rdma, uint64_t index, uint64_t chunk, uint64_t wr_id) { if (rdma->unregistrations[rdma->unregister_next] != 0) { - fprintf(stderr, "rdma migration: queue is full!\n"); + error_report("rdma migration: queue is full"); } else { RDMALocalBlock *block = &(rdma->local_ram_blocks.block[index]); if (!test_and_set_bit(chunk, block->unregister_bitmap)) { - DDPRINTF("Appending unregister chunk %" PRIu64 - " at position %d\n", chunk, rdma->unregister_next); + trace_qemu_rdma_signal_unregister_append(chunk, + rdma->unregister_next); rdma->unregistrations[rdma->unregister_next++] = qemu_rdma_make_wrid(wr_id, index, chunk); @@ -1355,8 +1328,7 @@ static void qemu_rdma_signal_unregister(RDMAContext *rdma, uint64_t index, rdma->unregister_next = 0; } } else { - DDPRINTF("Unregister chunk %" PRIu64 " already in queue.\n", - chunk); + trace_qemu_rdma_signal_unregister_already(chunk); } } } @@ -1381,7 +1353,7 @@ static uint64_t qemu_rdma_poll(RDMAContext *rdma, uint64_t *wr_id_out, } if (ret < 0) { - fprintf(stderr, "ibv_poll_cq return %d!\n", ret); + error_report("ibv_poll_cq return %d", ret); return ret; } @@ -1397,8 +1369,7 @@ static uint64_t qemu_rdma_poll(RDMAContext *rdma, uint64_t *wr_id_out, if (rdma->control_ready_expected && (wr_id >= RDMA_WRID_RECV_CONTROL)) { - DDDPRINTF("completion %s #%" PRId64 " received (%" PRId64 ")" - " left %d\n", wrid_desc[RDMA_WRID_RECV_CONTROL], + trace_qemu_rdma_poll_recv(wrid_desc[RDMA_WRID_RECV_CONTROL], wr_id - RDMA_WRID_RECV_CONTROL, wr_id, rdma->nb_sent); rdma->control_ready_expected = 0; } @@ -1410,10 +1381,9 @@ static uint64_t qemu_rdma_poll(RDMAContext *rdma, uint64_t *wr_id_out, (wc.wr_id & RDMA_WRID_BLOCK_MASK) >> RDMA_WRID_BLOCK_SHIFT; RDMALocalBlock *block = &(rdma->local_ram_blocks.block[index]); - DDDPRINTF("completions %s (%" PRId64 ") left %d, " - "block %" PRIu64 ", chunk: %" PRIu64 " %p %p\n", - print_wrid(wr_id), wr_id, rdma->nb_sent, index, chunk, - block->local_host_addr, (void *)block->remote_host_addr); + trace_qemu_rdma_poll_write(print_wrid(wr_id), wr_id, rdma->nb_sent, + index, chunk, block->local_host_addr, + (void *)(uintptr_t)block->remote_host_addr); clear_bit(chunk, block->transit_bitmap); @@ -1433,8 +1403,7 @@ static uint64_t qemu_rdma_poll(RDMAContext *rdma, uint64_t *wr_id_out, #endif } } else { - DDDPRINTF("other completion %s (%" PRId64 ") received left %d\n", - print_wrid(wr_id), wr_id, rdma->nb_sent); + trace_qemu_rdma_poll_other(print_wrid(wr_id), wr_id, rdma->nb_sent); } *wr_id_out = wc.wr_id; @@ -1482,9 +1451,8 @@ static int qemu_rdma_block_for_wrid(RDMAContext *rdma, int wrid_requested, break; } if (wr_id != wrid_requested) { - DDDPRINTF("A Wanted wrid %s (%d) but got %s (%" PRIu64 ")\n", - print_wrid(wrid_requested), - wrid_requested, print_wrid(wr_id), wr_id); + trace_qemu_rdma_block_for_wrid_miss(print_wrid(wrid_requested), + wrid_requested, print_wrid(wr_id), wr_id); } } @@ -1524,9 +1492,8 @@ static int qemu_rdma_block_for_wrid(RDMAContext *rdma, int wrid_requested, break; } if (wr_id != wrid_requested) { - DDDPRINTF("B Wanted wrid %s (%d) but got %s (%" PRIu64 ")\n", - print_wrid(wrid_requested), wrid_requested, - print_wrid(wr_id), wr_id); + trace_qemu_rdma_block_for_wrid_miss(print_wrid(wrid_requested), + wrid_requested, print_wrid(wr_id), wr_id); } } @@ -1559,7 +1526,7 @@ static int qemu_rdma_post_send_control(RDMAContext *rdma, uint8_t *buf, RDMAWorkRequestData *wr = &rdma->wr_data[RDMA_WRID_CONTROL]; struct ibv_send_wr *bad_wr; struct ibv_sge sge = { - .addr = (uint64_t)(wr->control), + .addr = (uintptr_t)(wr->control), .length = head->len + sizeof(RDMAControlHeader), .lkey = wr->control_mr->lkey, }; @@ -1571,7 +1538,7 @@ static int qemu_rdma_post_send_control(RDMAContext *rdma, uint8_t *buf, .num_sge = 1, }; - DDDPRINTF("CONTROL: sending %s..\n", control_desc[head->type]); + trace_qemu_rdma_post_send_control(control_desc[head->type]); /* * We don't actually need to do a memcpy() in here if we used @@ -1593,13 +1560,13 @@ static int qemu_rdma_post_send_control(RDMAContext *rdma, uint8_t *buf, ret = ibv_post_send(rdma->qp, &send_wr, &bad_wr); if (ret > 0) { - fprintf(stderr, "Failed to use post IB SEND for control!\n"); + error_report("Failed to use post IB SEND for control"); return -ret; } ret = qemu_rdma_block_for_wrid(rdma, RDMA_WRID_SEND_CONTROL, NULL); if (ret < 0) { - fprintf(stderr, "rdma migration: send polling control error!\n"); + error_report("rdma migration: send polling control error"); } return ret; @@ -1613,7 +1580,7 @@ static int qemu_rdma_post_recv_control(RDMAContext *rdma, int idx) { struct ibv_recv_wr *bad_wr; struct ibv_sge sge = { - .addr = (uint64_t)(rdma->wr_data[idx].control), + .addr = (uintptr_t)(rdma->wr_data[idx].control), .length = RDMA_CONTROL_MAX_BUFFER, .lkey = rdma->wr_data[idx].control_mr->lkey, }; @@ -1643,32 +1610,31 @@ static int qemu_rdma_exchange_get_response(RDMAContext *rdma, &byte_len); if (ret < 0) { - fprintf(stderr, "rdma migration: recv polling control error!\n"); + error_report("rdma migration: recv polling control error!"); return ret; } network_to_control((void *) rdma->wr_data[idx].control); memcpy(head, rdma->wr_data[idx].control, sizeof(RDMAControlHeader)); - DDDPRINTF("CONTROL: %s receiving...\n", control_desc[expecting]); + trace_qemu_rdma_exchange_get_response_start(control_desc[expecting]); if (expecting == RDMA_CONTROL_NONE) { - DDDPRINTF("Surprise: got %s (%d)\n", - control_desc[head->type], head->type); + trace_qemu_rdma_exchange_get_response_none(control_desc[head->type], + head->type); } else if (head->type != expecting || head->type == RDMA_CONTROL_ERROR) { - fprintf(stderr, "Was expecting a %s (%d) control message" - ", but got: %s (%d), length: %d\n", + error_report("Was expecting a %s (%d) control message" + ", but got: %s (%d), length: %d", control_desc[expecting], expecting, control_desc[head->type], head->type, head->len); return -EIO; } if (head->len > RDMA_CONTROL_MAX_BUFFER - sizeof(*head)) { - fprintf(stderr, "too long length: %d\n", head->len); + error_report("too long length: %d", head->len); return -EINVAL; } if (sizeof(*head) + head->len != byte_len) { - fprintf(stderr, "Malformed length: %d byte_len %d\n", - head->len, byte_len); + error_report("Malformed length: %d byte_len %d", head->len, byte_len); return -EINVAL; } @@ -1730,7 +1696,7 @@ static int qemu_rdma_exchange_send(RDMAContext *rdma, RDMAControlHeader *head, if (resp) { ret = qemu_rdma_post_recv_control(rdma, RDMA_WRID_DATA); if (ret) { - fprintf(stderr, "rdma migration: error posting" + error_report("rdma migration: error posting" " extra control recv for anticipated result!"); return ret; } @@ -1741,7 +1707,7 @@ static int qemu_rdma_exchange_send(RDMAContext *rdma, RDMAControlHeader *head, */ ret = qemu_rdma_post_recv_control(rdma, RDMA_WRID_READY); if (ret) { - fprintf(stderr, "rdma migration: error posting first control recv!"); + error_report("rdma migration: error posting first control recv!"); return ret; } @@ -1751,7 +1717,7 @@ static int qemu_rdma_exchange_send(RDMAContext *rdma, RDMAControlHeader *head, ret = qemu_rdma_post_send_control(rdma, data, head); if (ret < 0) { - fprintf(stderr, "Failed to send control buffer!\n"); + error_report("Failed to send control buffer!"); return ret; } @@ -1760,14 +1726,14 @@ static int qemu_rdma_exchange_send(RDMAContext *rdma, RDMAControlHeader *head, */ if (resp) { if (callback) { - DDPRINTF("Issuing callback before receiving response...\n"); + trace_qemu_rdma_exchange_send_issue_callback(); ret = callback(rdma); if (ret < 0) { return ret; } } - DDPRINTF("Waiting for response %s\n", control_desc[resp->type]); + trace_qemu_rdma_exchange_send_waiting(control_desc[resp->type]); ret = qemu_rdma_exchange_get_response(rdma, resp, resp->type, RDMA_WRID_DATA); @@ -1779,7 +1745,7 @@ static int qemu_rdma_exchange_send(RDMAContext *rdma, RDMAControlHeader *head, if (resp_idx) { *resp_idx = RDMA_WRID_DATA; } - DDPRINTF("Response %s received.\n", control_desc[resp->type]); + trace_qemu_rdma_exchange_send_received(control_desc[resp->type]); } rdma->control_ready_expected = 1; @@ -1807,7 +1773,7 @@ static int qemu_rdma_exchange_recv(RDMAContext *rdma, RDMAControlHeader *head, ret = qemu_rdma_post_send_control(rdma, NULL, &ready); if (ret < 0) { - fprintf(stderr, "Failed to send control buffer!\n"); + error_report("Failed to send control buffer!"); return ret; } @@ -1828,7 +1794,7 @@ static int qemu_rdma_exchange_recv(RDMAContext *rdma, RDMAControlHeader *head, */ ret = qemu_rdma_post_recv_control(rdma, RDMA_WRID_READY); if (ret) { - fprintf(stderr, "rdma migration: error posting second control recv!"); + error_report("rdma migration: error posting second control recv!"); return ret; } @@ -1861,11 +1827,12 @@ static int qemu_rdma_write_one(QEMUFile *f, RDMAContext *rdma, }; retry: - sge.addr = (uint64_t)(block->local_host_addr + + sge.addr = (uintptr_t)(block->local_host_addr + (current_addr - block->offset)); sge.length = length; - chunk = ram_chunk_index(block->local_host_addr, (uint8_t *) sge.addr); + chunk = ram_chunk_index(block->local_host_addr, + (uint8_t *)(uintptr_t)sge.addr); chunk_start = ram_chunk_start(block, chunk); if (block->is_ram_block) { @@ -1882,8 +1849,9 @@ retry: } } - DDPRINTF("Writing %" PRIu64 " chunks, (%" PRIu64 " MB)\n", - chunks + 1, (chunks + 1) * (1UL << RDMA_REG_CHUNK_SHIFT) / 1024 / 1024); + trace_qemu_rdma_write_one_top(chunks + 1, + (chunks + 1) * + (1UL << RDMA_REG_CHUNK_SHIFT) / 1024 / 1024); chunk_end = ram_chunk_end(block, chunk + chunks); @@ -1895,17 +1863,15 @@ retry: while (test_bit(chunk, block->transit_bitmap)) { (void)count; - DDPRINTF("(%d) Not clobbering: block: %d chunk %" PRIu64 - " current %" PRIu64 " len %" PRIu64 " %d %d\n", - count++, current_index, chunk, + trace_qemu_rdma_write_one_block(count++, current_index, chunk, sge.addr, length, rdma->nb_sent, block->nb_chunks); ret = qemu_rdma_block_for_wrid(rdma, RDMA_WRID_RDMA_WRITE, NULL); if (ret < 0) { - fprintf(stderr, "Failed to Wait for previous write to complete " + error_report("Failed to Wait for previous write to complete " "block %d chunk %" PRIu64 - " current %" PRIu64 " len %" PRIu64 " %d\n", + " current %" PRIu64 " len %" PRIu64 " %d", current_index, chunk, sge.addr, length, rdma->nb_sent); return ret; } @@ -1919,8 +1885,9 @@ retry: * memset() + madvise() the entire chunk without RDMA. */ - if (can_use_buffer_find_nonzero_offset((void *)sge.addr, length) - && buffer_find_nonzero_offset((void *)sge.addr, + if (can_use_buffer_find_nonzero_offset((void *)(uintptr_t)sge.addr, + length) + && buffer_find_nonzero_offset((void *)(uintptr_t)sge.addr, length) == length) { RDMACompress comp = { .offset = current_addr, @@ -1932,10 +1899,8 @@ retry: head.len = sizeof(comp); head.type = RDMA_CONTROL_COMPRESS; - DDPRINTF("Entire chunk is zero, sending compress: %" - PRIu64 " for %d " - "bytes, index: %d, offset: %" PRId64 "...\n", - chunk, sge.length, current_index, current_addr); + trace_qemu_rdma_write_one_zero(chunk, sge.length, + current_index, current_addr); compress_to_network(&comp); ret = qemu_rdma_exchange_send(rdma, &head, @@ -1961,9 +1926,8 @@ retry: } reg.chunks = chunks; - DDPRINTF("Sending registration request chunk %" PRIu64 " for %d " - "bytes, index: %d, offset: %" PRId64 "...\n", - chunk, sge.length, current_index, current_addr); + trace_qemu_rdma_write_one_sendreg(chunk, sge.length, current_index, + current_addr); register_to_network(®); ret = qemu_rdma_exchange_send(rdma, &head, (uint8_t *) ®, @@ -1973,11 +1937,10 @@ retry: } /* try to overlap this single registration with the one we sent. */ - if (qemu_rdma_register_and_get_keys(rdma, block, - (uint8_t *) sge.addr, + if (qemu_rdma_register_and_get_keys(rdma, block, sge.addr, &sge.lkey, NULL, chunk, chunk_start, chunk_end)) { - fprintf(stderr, "cannot get lkey!\n"); + error_report("cannot get lkey"); return -EINVAL; } @@ -1986,19 +1949,17 @@ retry: network_to_result(reg_result); - DDPRINTF("Received registration result:" - " my key: %x their key %x, chunk %" PRIu64 "\n", - block->remote_keys[chunk], reg_result->rkey, chunk); + trace_qemu_rdma_write_one_recvregres(block->remote_keys[chunk], + reg_result->rkey, chunk); block->remote_keys[chunk] = reg_result->rkey; block->remote_host_addr = reg_result->host_addr; } else { /* already registered before */ - if (qemu_rdma_register_and_get_keys(rdma, block, - (uint8_t *)sge.addr, + if (qemu_rdma_register_and_get_keys(rdma, block, sge.addr, &sge.lkey, NULL, chunk, chunk_start, chunk_end)) { - fprintf(stderr, "cannot get lkey!\n"); + error_report("cannot get lkey!"); return -EINVAL; } } @@ -2007,10 +1968,10 @@ retry: } else { send_wr.wr.rdma.rkey = block->remote_rkey; - if (qemu_rdma_register_and_get_keys(rdma, block, (uint8_t *)sge.addr, + if (qemu_rdma_register_and_get_keys(rdma, block, sge.addr, &sge.lkey, NULL, chunk, chunk_start, chunk_end)) { - fprintf(stderr, "cannot get lkey!\n"); + error_report("cannot get lkey!"); return -EINVAL; } } @@ -2031,10 +1992,8 @@ retry: send_wr.wr.rdma.remote_addr = block->remote_host_addr + (current_addr - block->offset); - DDDPRINTF("Posting chunk: %" PRIu64 ", addr: %lx" - " remote: %lx, bytes %" PRIu32 "\n", - chunk, sge.addr, send_wr.wr.rdma.remote_addr, - sge.length); + trace_qemu_rdma_write_one_post(chunk, sge.addr, send_wr.wr.rdma.remote_addr, + sge.length); /* * ibv_post_send() does not return negative error numbers, @@ -2043,11 +2002,11 @@ retry: ret = ibv_post_send(rdma->qp, &send_wr, &bad_wr); if (ret == ENOMEM) { - DDPRINTF("send queue is full. wait a little....\n"); + trace_qemu_rdma_write_one_queue_full(); ret = qemu_rdma_block_for_wrid(rdma, RDMA_WRID_RDMA_WRITE, NULL); if (ret < 0) { - fprintf(stderr, "rdma migration: failed to make " - "room in full send queue! %d\n", ret); + error_report("rdma migration: failed to make " + "room in full send queue! %d", ret); return ret; } @@ -2088,7 +2047,7 @@ static int qemu_rdma_write_flush(QEMUFile *f, RDMAContext *rdma) if (ret == 0) { rdma->nb_sent++; - DDDPRINTF("sent total: %d\n", rdma->nb_sent); + trace_qemu_rdma_write_flush(rdma->nb_sent); } rdma->current_length = 0; @@ -2173,7 +2132,7 @@ static int qemu_rdma_write(QEMUFile *f, RDMAContext *rdma, ret = qemu_rdma_search_ram_block(rdma, block_offset, offset, len, &index, &chunk); if (ret) { - fprintf(stderr, "ram block search failed\n"); + error_report("ram block search failed"); return ret; } rdma->current_index = index; @@ -2202,19 +2161,19 @@ static void qemu_rdma_cleanup(RDMAContext *rdma) .type = RDMA_CONTROL_ERROR, .repeat = 1, }; - fprintf(stderr, "Early error. Sending error.\n"); + error_report("Early error. Sending error."); qemu_rdma_post_send_control(rdma, NULL, &head); } ret = rdma_disconnect(rdma->cm_id); if (!ret) { - DDPRINTF("waiting for disconnect\n"); + trace_qemu_rdma_cleanup_waiting_for_disconnect(); ret = rdma_get_cm_event(rdma->channel, &cm_event); if (!ret) { rdma_ack_cm_event(cm_event); } } - DDPRINTF("Disconnected.\n"); + trace_qemu_rdma_cleanup_disconnect(); rdma->connected = false; } @@ -2231,11 +2190,14 @@ static void qemu_rdma_cleanup(RDMAContext *rdma) if (rdma->local_ram_blocks.block) { while (rdma->local_ram_blocks.nb_blocks) { - __qemu_rdma_delete_block(rdma, - rdma->local_ram_blocks.block->offset); + rdma_delete_block(rdma, rdma->local_ram_blocks.block->offset); } } + if (rdma->qp) { + rdma_destroy_qp(rdma->cm_id); + rdma->qp = NULL; + } if (rdma->cq) { ibv_destroy_cq(rdma->cq); rdma->cq = NULL; @@ -2248,18 +2210,14 @@ static void qemu_rdma_cleanup(RDMAContext *rdma) ibv_dealloc_pd(rdma->pd); rdma->pd = NULL; } - if (rdma->listen_id) { - rdma_destroy_id(rdma->listen_id); - rdma->listen_id = NULL; - } if (rdma->cm_id) { - if (rdma->qp) { - rdma_destroy_qp(rdma->cm_id); - rdma->qp = NULL; - } rdma_destroy_id(rdma->cm_id); rdma->cm_id = NULL; } + if (rdma->listen_id) { + rdma_destroy_id(rdma->listen_id); + rdma->listen_id = NULL; + } if (rdma->channel) { rdma_destroy_event_channel(rdma->channel); rdma->channel = NULL; @@ -2341,7 +2299,7 @@ static int qemu_rdma_connect(RDMAContext *rdma, Error **errp) * on the source first requested the capability. */ if (rdma->pin_all) { - DPRINTF("Server pin-all memory requested.\n"); + trace_qemu_rdma_connect_pin_all_requested(); cap.flags |= RDMA_CAPABILITY_PIN_ALL; } @@ -2351,8 +2309,6 @@ static int qemu_rdma_connect(RDMAContext *rdma, Error **errp) if (ret) { perror("rdma_connect"); ERROR(errp, "connecting to destination!"); - rdma_destroy_id(rdma->cm_id); - rdma->cm_id = NULL; goto err_rdma_source_connect; } @@ -2361,8 +2317,6 @@ static int qemu_rdma_connect(RDMAContext *rdma, Error **errp) perror("rdma_get_cm_event after rdma_connect"); ERROR(errp, "connecting to destination!"); rdma_ack_cm_event(cm_event); - rdma_destroy_id(rdma->cm_id); - rdma->cm_id = NULL; goto err_rdma_source_connect; } @@ -2370,8 +2324,6 @@ static int qemu_rdma_connect(RDMAContext *rdma, Error **errp) perror("rdma_get_cm_event != EVENT_ESTABLISHED after rdma_connect"); ERROR(errp, "connecting to destination!"); rdma_ack_cm_event(cm_event); - rdma_destroy_id(rdma->cm_id); - rdma->cm_id = NULL; goto err_rdma_source_connect; } rdma->connected = true; @@ -2389,7 +2341,7 @@ static int qemu_rdma_connect(RDMAContext *rdma, Error **errp) rdma->pin_all = false; } - DPRINTF("Pin all memory: %s\n", rdma->pin_all ? "enabled" : "disabled"); + trace_qemu_rdma_connect_pin_all_outcome(rdma->pin_all); rdma_ack_cm_event(cm_event); @@ -2410,10 +2362,10 @@ err_rdma_source_connect: static int qemu_rdma_dest_init(RDMAContext *rdma, Error **errp) { - int ret = -EINVAL, idx; + int ret, idx; struct rdma_cm_id *listen_id; char ip[40] = "unknown"; - struct rdma_addrinfo *res; + struct rdma_addrinfo *res, *e; char port_str[16]; for (idx = 0; idx < RDMA_WRID_MAX; idx++) { @@ -2421,7 +2373,7 @@ static int qemu_rdma_dest_init(RDMAContext *rdma, Error **errp) rdma->wr_data[idx].control_curr = NULL; } - if (rdma->host == NULL) { + if (!rdma->host || !rdma->host[0]) { ERROR(errp, "RDMA host is not set!"); rdma->error_state = -EINVAL; return -1; @@ -2444,40 +2396,33 @@ static int qemu_rdma_dest_init(RDMAContext *rdma, Error **errp) snprintf(port_str, 16, "%d", rdma->port); port_str[15] = '\0'; - if (rdma->host && strcmp("", rdma->host)) { - struct rdma_addrinfo *e; + ret = rdma_getaddrinfo(rdma->host, port_str, NULL, &res); + if (ret < 0) { + ERROR(errp, "could not rdma_getaddrinfo address %s", rdma->host); + goto err_dest_init_bind_addr; + } - ret = rdma_getaddrinfo(rdma->host, port_str, NULL, &res); - if (ret < 0) { - ERROR(errp, "could not rdma_getaddrinfo address %s", rdma->host); - goto err_dest_init_bind_addr; + for (e = res; e != NULL; e = e->ai_next) { + inet_ntop(e->ai_family, + &((struct sockaddr_in *) e->ai_dst_addr)->sin_addr, ip, sizeof ip); + trace_qemu_rdma_dest_init_trying(rdma->host, ip); + ret = rdma_bind_addr(listen_id, e->ai_dst_addr); + if (ret) { + continue; } - - for (e = res; e != NULL; e = e->ai_next) { - inet_ntop(e->ai_family, - &((struct sockaddr_in *) e->ai_dst_addr)->sin_addr, ip, sizeof ip); - DPRINTF("Trying %s => %s\n", rdma->host, ip); - ret = rdma_bind_addr(listen_id, e->ai_dst_addr); - if (!ret) { - if (e->ai_family == AF_INET6) { - ret = qemu_rdma_broken_ipv6_kernel(errp, listen_id->verbs); - if (ret) { - continue; - } - } - - goto listen; + if (e->ai_family == AF_INET6) { + ret = qemu_rdma_broken_ipv6_kernel(errp, listen_id->verbs); + if (ret) { + continue; } } + break; + } + if (!e) { ERROR(errp, "Error: could not rdma_bind_addr!"); goto err_dest_init_bind_addr; - } else { - ERROR(errp, "migration host and port not specified!"); - ret = -EINVAL; - goto err_dest_init_bind_addr; } -listen: rdma->listen_id = listen_id; qemu_rdma_dump_gid("dest_init", listen_id); @@ -2575,8 +2520,7 @@ static size_t qemu_rdma_fill(RDMAContext *rdma, uint8_t *buf, size_t len = 0; if (rdma->wr_data[idx].control_len) { - DDDPRINTF("RDMA %" PRId64 " of %d bytes already in buffer\n", - rdma->wr_data[idx].control_len, size); + trace_qemu_rdma_fill(rdma->wr_data[idx].control_len, size); len = MIN(size, rdma->wr_data[idx].control_len); memcpy(buf, rdma->wr_data[idx].control_curr, len); @@ -2643,7 +2587,7 @@ static int qemu_rdma_drain_cq(QEMUFile *f, RDMAContext *rdma) while (rdma->nb_sent) { ret = qemu_rdma_block_for_wrid(rdma, RDMA_WRID_RDMA_WRITE, NULL); if (ret < 0) { - fprintf(stderr, "rdma migration: complete polling error!\n"); + error_report("rdma migration: complete polling error!"); return -EIO; } } @@ -2655,7 +2599,7 @@ static int qemu_rdma_drain_cq(QEMUFile *f, RDMAContext *rdma) static int qemu_rdma_close(void *opaque) { - DPRINTF("Shutting down connection.\n"); + trace_qemu_rdma_close(); QEMUFileRDMA *r = opaque; if (r->rdma) { qemu_rdma_cleanup(r->rdma); @@ -2701,7 +2645,7 @@ static int qemu_rdma_close(void *opaque) */ static size_t qemu_rdma_save_page(QEMUFile *f, void *opaque, ram_addr_t block_offset, ram_addr_t offset, - size_t size, int *bytes_sent) + size_t size, uint64_t *bytes_sent) { QEMUFileRDMA *rfile = opaque; RDMAContext *rdma = rfile->rdma; @@ -2719,7 +2663,7 @@ static size_t qemu_rdma_save_page(QEMUFile *f, void *opaque, */ ret = qemu_rdma_write(f, rdma, block_offset, offset, size); if (ret < 0) { - fprintf(stderr, "rdma migration: write error! %d\n", ret); + error_report("rdma migration: write error! %d", ret); goto err; } @@ -2752,7 +2696,7 @@ static size_t qemu_rdma_save_page(QEMUFile *f, void *opaque, offset, size, &index, &chunk); if (ret) { - fprintf(stderr, "ram block search failed\n"); + error_report("ram block search failed"); goto err; } @@ -2779,7 +2723,7 @@ static size_t qemu_rdma_save_page(QEMUFile *f, void *opaque, uint64_t wr_id, wr_id_in; int ret = qemu_rdma_poll(rdma, &wr_id_in, NULL); if (ret < 0) { - fprintf(stderr, "rdma migration: polling error! %d\n", ret); + error_report("rdma migration: polling error! %d", ret); goto err; } @@ -2824,7 +2768,7 @@ static int qemu_rdma_accept(RDMAContext *rdma) network_to_caps(&cap); if (cap.version < 1 || cap.version > RDMA_CONTROL_VERSION_CURRENT) { - fprintf(stderr, "Unknown source RDMA version: %d, bailing...\n", + error_report("Unknown source RDMA version: %d, bailing...", cap.version); rdma_ack_cm_event(cm_event); goto err_rdma_dest_wait; @@ -2848,17 +2792,17 @@ static int qemu_rdma_accept(RDMAContext *rdma) rdma_ack_cm_event(cm_event); - DPRINTF("Memory pin all: %s\n", rdma->pin_all ? "enabled" : "disabled"); + trace_qemu_rdma_accept_pin_state(rdma->pin_all); caps_to_network(&cap); - DPRINTF("verbs context after listen: %p\n", verbs); + trace_qemu_rdma_accept_pin_verbsc(verbs); if (!rdma->verbs) { rdma->verbs = verbs; } else if (rdma->verbs != verbs) { - fprintf(stderr, "ibv context not matching %p, %p!\n", - rdma->verbs, verbs); + error_report("ibv context not matching %p, %p!", rdma->verbs, + verbs); goto err_rdma_dest_wait; } @@ -2866,26 +2810,26 @@ static int qemu_rdma_accept(RDMAContext *rdma) ret = qemu_rdma_alloc_pd_cq(rdma); if (ret) { - fprintf(stderr, "rdma migration: error allocating pd and cq!\n"); + error_report("rdma migration: error allocating pd and cq!"); goto err_rdma_dest_wait; } ret = qemu_rdma_alloc_qp(rdma); if (ret) { - fprintf(stderr, "rdma migration: error allocating qp!\n"); + error_report("rdma migration: error allocating qp!"); goto err_rdma_dest_wait; } ret = qemu_rdma_init_ram_blocks(rdma); if (ret) { - fprintf(stderr, "rdma migration: error initializing ram blocks!\n"); + error_report("rdma migration: error initializing ram blocks!"); goto err_rdma_dest_wait; } for (idx = 0; idx < RDMA_WRID_MAX; idx++) { ret = qemu_rdma_reg_control(rdma, idx); if (ret) { - fprintf(stderr, "rdma: error registering %d control!\n", idx); + error_report("rdma: error registering %d control", idx); goto err_rdma_dest_wait; } } @@ -2894,18 +2838,18 @@ static int qemu_rdma_accept(RDMAContext *rdma) ret = rdma_accept(rdma->cm_id, &conn_param); if (ret) { - fprintf(stderr, "rdma_accept returns %d!\n", ret); + error_report("rdma_accept returns %d", ret); goto err_rdma_dest_wait; } ret = rdma_get_cm_event(rdma->channel, &cm_event); if (ret) { - fprintf(stderr, "rdma_accept get_cm_event failed %d!\n", ret); + error_report("rdma_accept get_cm_event failed %d", ret); goto err_rdma_dest_wait; } if (cm_event->event != RDMA_CM_EVENT_ESTABLISHED) { - fprintf(stderr, "rdma_accept not event established!\n"); + error_report("rdma_accept not event established"); rdma_ack_cm_event(cm_event); goto err_rdma_dest_wait; } @@ -2915,7 +2859,7 @@ static int qemu_rdma_accept(RDMAContext *rdma) ret = qemu_rdma_post_recv_control(rdma, RDMA_WRID_READY); if (ret) { - fprintf(stderr, "rdma migration: error posting second control recv!\n"); + error_report("rdma migration: error posting second control recv"); goto err_rdma_dest_wait; } @@ -2969,7 +2913,7 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque, CHECK_ERROR_STATE(); do { - DDDPRINTF("Waiting for next request %" PRIu64 "...\n", flags); + trace_qemu_rdma_registration_handle_wait(flags); ret = qemu_rdma_exchange_recv(rdma, &head, RDMA_CONTROL_NONE); @@ -2978,8 +2922,8 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque, } if (head.repeat > RDMA_CONTROL_MAX_COMMANDS_PER_MESSAGE) { - fprintf(stderr, "rdma: Too many requests in this message (%d)." - "Bailing.\n", head.repeat); + error_report("rdma: Too many requests in this message (%d)." + "Bailing.", head.repeat); ret = -EIO; break; } @@ -2989,9 +2933,9 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque, comp = (RDMACompress *) rdma->wr_data[idx].control_curr; network_to_compress(comp); - DDPRINTF("Zapping zero chunk: %" PRId64 - " bytes, index %d, offset %" PRId64 "\n", - comp->length, comp->block_idx, comp->offset); + trace_qemu_rdma_registration_handle_compress(comp->length, + comp->block_idx, + comp->offset); block = &(rdma->local_ram_blocks.block[comp->block_idx]); host_addr = block->local_host_addr + @@ -3001,17 +2945,17 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque, break; case RDMA_CONTROL_REGISTER_FINISHED: - DDDPRINTF("Current registrations complete.\n"); + trace_qemu_rdma_registration_handle_finished(); goto out; case RDMA_CONTROL_RAM_BLOCKS_REQUEST: - DPRINTF("Initial setup info requested.\n"); + trace_qemu_rdma_registration_handle_ram_blocks(); if (rdma->pin_all) { ret = qemu_rdma_reg_whole_ram_blocks(rdma); if (ret) { - fprintf(stderr, "rdma migration: error dest " - "registering ram blocks!\n"); + error_report("rdma migration: error dest " + "registering ram blocks"); goto out; } } @@ -3024,7 +2968,7 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque, */ for (i = 0; i < local->nb_blocks; i++) { rdma->block[i].remote_host_addr = - (uint64_t)(local->block[i].local_host_addr); + (uintptr_t)(local->block[i].local_host_addr); if (rdma->pin_all) { rdma->block[i].remote_rkey = local->block[i].mr->rkey; @@ -3044,13 +2988,13 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque, (uint8_t *) rdma->block, &blocks); if (ret < 0) { - fprintf(stderr, "rdma migration: error sending remote info!\n"); + error_report("rdma migration: error sending remote info"); goto out; } break; case RDMA_CONTROL_REGISTER_REQUEST: - DDPRINTF("There are %d registration requests\n", head.repeat); + trace_qemu_rdma_registration_handle_register(head.repeat); reg_resp.repeat = head.repeat; registers = (RDMARegister *) rdma->wr_data[idx].control_curr; @@ -3064,8 +3008,7 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque, reg_result = &results[count]; - DDPRINTF("Registration request (%d): index %d, current_addr %" - PRIu64 " chunks: %" PRIu64 "\n", count, + trace_qemu_rdma_registration_handle_register_loop(count, reg->current_index, reg->key.current_addr, reg->chunks); block = &(rdma->local_ram_blocks.block[reg->current_index]); @@ -3082,17 +3025,17 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque, chunk_start = ram_chunk_start(block, chunk); chunk_end = ram_chunk_end(block, chunk + reg->chunks); if (qemu_rdma_register_and_get_keys(rdma, block, - (uint8_t *)host_addr, NULL, ®_result->rkey, + (uintptr_t)host_addr, NULL, ®_result->rkey, chunk, chunk_start, chunk_end)) { - fprintf(stderr, "cannot get rkey!\n"); + error_report("cannot get rkey"); ret = -EINVAL; goto out; } - reg_result->host_addr = (uint64_t) block->local_host_addr; + reg_result->host_addr = (uintptr_t)block->local_host_addr; - DDPRINTF("Registered rkey for this request: %x\n", - reg_result->rkey); + trace_qemu_rdma_registration_handle_register_rkey( + reg_result->rkey); result_to_network(reg_result); } @@ -3101,12 +3044,12 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque, (uint8_t *) results, ®_resp); if (ret < 0) { - fprintf(stderr, "Failed to send control buffer!\n"); + error_report("Failed to send control buffer"); goto out; } break; case RDMA_CONTROL_UNREGISTER_REQUEST: - DDPRINTF("There are %d unregistration requests\n", head.repeat); + trace_qemu_rdma_registration_handle_unregister(head.repeat); unreg_resp.repeat = head.repeat; registers = (RDMARegister *) rdma->wr_data[idx].control_curr; @@ -3114,9 +3057,8 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque, reg = ®isters[count]; network_to_register(reg); - DDPRINTF("Unregistration request (%d): " - " index %d, chunk %" PRIu64 "\n", - count, reg->current_index, reg->key.chunk); + trace_qemu_rdma_registration_handle_unregister_loop(count, + reg->current_index, reg->key.chunk); block = &(rdma->local_ram_blocks.block[reg->current_index]); @@ -3131,24 +3073,23 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque, rdma->total_registrations--; - DDPRINTF("Unregistered chunk %" PRIu64 " successfully.\n", - reg->key.chunk); + trace_qemu_rdma_registration_handle_unregister_success( + reg->key.chunk); } ret = qemu_rdma_post_send_control(rdma, NULL, &unreg_resp); if (ret < 0) { - fprintf(stderr, "Failed to send control buffer!\n"); + error_report("Failed to send control buffer"); goto out; } break; case RDMA_CONTROL_REGISTER_RESULT: - fprintf(stderr, "Invalid RESULT message at dest.\n"); + error_report("Invalid RESULT message at dest."); ret = -EIO; goto out; default: - fprintf(stderr, "Unknown control message %s\n", - control_desc[head.type]); + error_report("Unknown control message %s", control_desc[head.type]); ret = -EIO; goto out; } @@ -3168,7 +3109,7 @@ static int qemu_rdma_registration_start(QEMUFile *f, void *opaque, CHECK_ERROR_STATE(); - DDDPRINTF("start section: %" PRIu64 "\n", flags); + trace_qemu_rdma_registration_start(flags); qemu_put_be64(f, RAM_SAVE_FLAG_HOOK); qemu_fflush(f); @@ -3203,7 +3144,7 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque, int reg_result_idx, i, j, nb_remote_blocks; head.type = RDMA_CONTROL_RAM_BLOCKS_REQUEST; - DPRINTF("Sending registration setup for ram blocks...\n"); + trace_qemu_rdma_registration_stop_ram(); /* * Make sure that we parallelize the pinning on both sides. @@ -3275,7 +3216,7 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque, } } - DDDPRINTF("Sending registration finish %" PRIu64 "...\n", flags); + trace_qemu_rdma_registration_stop(flags); head.type = RDMA_CONTROL_REGISTER_FINISHED; ret = qemu_rdma_exchange_send(rdma, &head, NULL, NULL, NULL, NULL); @@ -3298,14 +3239,14 @@ static int qemu_rdma_get_fd(void *opaque) return rdma->comp_channel->fd; } -const QEMUFileOps rdma_read_ops = { +static const QEMUFileOps rdma_read_ops = { .get_buffer = qemu_rdma_get_buffer, .get_fd = qemu_rdma_get_fd, .close = qemu_rdma_close, .hook_ram_load = qemu_rdma_registration_handle, }; -const QEMUFileOps rdma_write_ops = { +static const QEMUFileOps rdma_write_ops = { .put_buffer = qemu_rdma_put_buffer, .close = qemu_rdma_close, .before_ram_iterate = qemu_rdma_registration_start, @@ -3339,7 +3280,7 @@ static void rdma_accept_incoming_migration(void *opaque) QEMUFile *f; Error *local_err = NULL, **errp = &local_err; - DPRINTF("Accepting rdma connection...\n"); + trace_qemu_dma_accept_incoming_migration(); ret = qemu_rdma_accept(rdma); if (ret) { @@ -3347,7 +3288,7 @@ static void rdma_accept_incoming_migration(void *opaque) return; } - DPRINTF("Accepted migration\n"); + trace_qemu_dma_accept_incoming_migration_accepted(); f = qemu_fopen_rdma(rdma, "rb"); if (f == NULL) { @@ -3366,7 +3307,7 @@ void rdma_start_incoming_migration(const char *host_port, Error **errp) RDMAContext *rdma; Error *local_err = NULL; - DPRINTF("Starting RDMA-based incoming migration\n"); + trace_rdma_start_incoming_migration(); rdma = qemu_rdma_data_init(host_port, &local_err); if (rdma == NULL) { @@ -3379,7 +3320,7 @@ void rdma_start_incoming_migration(const char *host_port, Error **errp) goto err; } - DPRINTF("qemu_rdma_dest_init success\n"); + trace_rdma_start_incoming_migration_after_dest_init(); ret = rdma_listen(rdma->listen_id, 5); @@ -3388,7 +3329,7 @@ void rdma_start_incoming_migration(const char *host_port, Error **errp) goto err; } - DPRINTF("rdma_listen success\n"); + trace_rdma_start_incoming_migration_after_rdma_listen(); qemu_set_fd_handler2(rdma->channel->fd, NULL, rdma_accept_incoming_migration, NULL, @@ -3419,14 +3360,14 @@ void rdma_start_outgoing_migration(void *opaque, goto err; } - DPRINTF("qemu_rdma_source_init success\n"); + trace_rdma_start_outgoing_migration_after_rdma_source_init(); ret = qemu_rdma_connect(rdma, &local_err); if (ret) { goto err; } - DPRINTF("qemu_rdma_source_connect success\n"); + trace_rdma_start_outgoing_migration_after_rdma_connect(); s->file = qemu_fopen_rdma(rdma, "wb"); migrate_fd_connect(s); diff --git a/migration-tcp.c b/migration/tcp.c similarity index 100% rename from migration-tcp.c rename to migration/tcp.c diff --git a/migration-unix.c b/migration/unix.c similarity index 100% rename from migration-unix.c rename to migration/unix.c diff --git a/vmstate.c b/migration/vmstate.c similarity index 72% rename from vmstate.c rename to migration/vmstate.c index 3dde574..e5388f0 100644 --- a/vmstate.c +++ b/migration/vmstate.c @@ -3,10 +3,12 @@ #include "migration/qemu-file.h" #include "migration/vmstate.h" #include "qemu/bitops.h" +#include "qemu/error-report.h" #include "trace.h" +#include "qjson.h" static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd, - void *opaque); + void *opaque, QJSON *vmdesc); static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, void *opaque); @@ -72,16 +74,21 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, int version_id) { VMStateField *field = vmsd->fields; - int ret; + int ret = 0; + trace_vmstate_load_state(vmsd->name, version_id); if (version_id > vmsd->version_id) { + trace_vmstate_load_state_end(vmsd->name, "too new", -EINVAL); return -EINVAL; } if (version_id < vmsd->minimum_version_id) { if (vmsd->load_state_old && version_id >= vmsd->minimum_version_id_old) { - return vmsd->load_state_old(f, opaque, version_id); + ret = vmsd->load_state_old(f, opaque, version_id); + trace_vmstate_load_state_end(vmsd->name, "old path", ret); + return ret; } + trace_vmstate_load_state_end(vmsd->name, "too old", -EINVAL); return -EINVAL; } if (vmsd->pre_load) { @@ -91,6 +98,7 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, } } while (field->name) { + trace_vmstate_load_state_field(vmsd->name, field->name); if ((field->field_exists && field->field_exists(opaque, version_id)) || (!field->field_exists && @@ -122,8 +130,8 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, } } } else if (field->flags & VMS_MUST_EXIST) { - fprintf(stderr, "Input validation failed: %s/%s\n", - vmsd->name, field->name); + error_report("Input validation failed: %s/%s", + vmsd->name, field->name); return -1; } field++; @@ -133,48 +141,203 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, return ret; } if (vmsd->post_load) { - return vmsd->post_load(opaque, version_id); + ret = vmsd->post_load(opaque, version_id); } - return 0; + trace_vmstate_load_state_end(vmsd->name, "end", ret); + return ret; +} + +static int vmfield_name_num(VMStateField *start, VMStateField *search) +{ + VMStateField *field; + int found = 0; + + for (field = start; field->name; field++) { + if (!strcmp(field->name, search->name)) { + if (field == search) { + return found; + } + found++; + } + } + + return -1; +} + +static bool vmfield_name_is_unique(VMStateField *start, VMStateField *search) +{ + VMStateField *field; + int found = 0; + + for (field = start; field->name; field++) { + if (!strcmp(field->name, search->name)) { + found++; + /* name found more than once, so it's not unique */ + if (found > 1) { + return false; + } + } + } + + return true; +} + +static const char *vmfield_get_type_name(VMStateField *field) +{ + const char *type = "unknown"; + + if (field->flags & VMS_STRUCT) { + type = "struct"; + } else if (field->info->name) { + type = field->info->name; + } + + return type; +} + +static bool vmsd_can_compress(VMStateField *field) +{ + if (field->field_exists) { + /* Dynamically existing fields mess up compression */ + return false; + } + + if (field->flags & VMS_STRUCT) { + VMStateField *sfield = field->vmsd->fields; + while (sfield->name) { + if (!vmsd_can_compress(sfield)) { + /* Child elements can't compress, so can't we */ + return false; + } + sfield++; + } + + if (field->vmsd->subsections) { + /* Subsections may come and go, better don't compress */ + return false; + } + } + + return true; +} + +static void vmsd_desc_field_start(const VMStateDescription *vmsd, QJSON *vmdesc, + VMStateField *field, int i, int max) +{ + char *name, *old_name; + bool is_array = max > 1; + bool can_compress = vmsd_can_compress(field); + + if (!vmdesc) { + return; + } + + name = g_strdup(field->name); + + /* Field name is not unique, need to make it unique */ + if (!vmfield_name_is_unique(vmsd->fields, field)) { + int num = vmfield_name_num(vmsd->fields, field); + old_name = name; + name = g_strdup_printf("%s[%d]", name, num); + g_free(old_name); + } + + json_start_object(vmdesc, NULL); + json_prop_str(vmdesc, "name", name); + if (is_array) { + if (can_compress) { + json_prop_int(vmdesc, "array_len", max); + } else { + json_prop_int(vmdesc, "index", i); + } + } + json_prop_str(vmdesc, "type", vmfield_get_type_name(field)); + + if (field->flags & VMS_STRUCT) { + json_start_object(vmdesc, "struct"); + } + + g_free(name); +} + +static void vmsd_desc_field_end(const VMStateDescription *vmsd, QJSON *vmdesc, + VMStateField *field, size_t size, int i) +{ + if (!vmdesc) { + return; + } + + if (field->flags & VMS_STRUCT) { + /* We printed a struct in between, close its child object */ + json_end_object(vmdesc); + } + + json_prop_int(vmdesc, "size", size); + json_end_object(vmdesc); } void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, - void *opaque) + void *opaque, QJSON *vmdesc) { VMStateField *field = vmsd->fields; if (vmsd->pre_save) { vmsd->pre_save(opaque); } + + if (vmdesc) { + json_prop_str(vmdesc, "vmsd_name", vmsd->name); + json_prop_int(vmdesc, "version", vmsd->version_id); + json_start_array(vmdesc, "fields"); + } + while (field->name) { if (!field->field_exists || field->field_exists(opaque, vmsd->version_id)) { void *base_addr = vmstate_base_addr(opaque, field, false); int i, n_elems = vmstate_n_elems(opaque, field); int size = vmstate_size(opaque, field); + int64_t old_offset, written_bytes; + QJSON *vmdesc_loop = vmdesc; for (i = 0; i < n_elems; i++) { void *addr = base_addr + size * i; + vmsd_desc_field_start(vmsd, vmdesc_loop, field, i, n_elems); + old_offset = qemu_ftell_fast(f); + if (field->flags & VMS_ARRAY_OF_POINTER) { addr = *(void **)addr; } if (field->flags & VMS_STRUCT) { - vmstate_save_state(f, field->vmsd, addr); + vmstate_save_state(f, field->vmsd, addr, vmdesc_loop); } else { field->info->put(f, addr, size); } + + written_bytes = qemu_ftell_fast(f) - old_offset; + vmsd_desc_field_end(vmsd, vmdesc_loop, field, written_bytes, i); + + /* Compressed arrays only care about the first element */ + if (vmdesc_loop && vmsd_can_compress(field)) { + vmdesc_loop = NULL; + } } } else { if (field->flags & VMS_MUST_EXIST) { - fprintf(stderr, "Output state validation failed: %s/%s\n", + error_report("Output state validation failed: %s/%s", vmsd->name, field->name); assert(!(field->flags & VMS_MUST_EXIST)); } } field++; } - vmstate_subsection_save(f, vmsd, opaque); + + if (vmdesc) { + json_end_array(vmdesc); + } + + vmstate_subsection_save(f, vmsd, opaque, vmdesc); } static const VMStateDescription * @@ -192,6 +355,8 @@ static const VMStateDescription * static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, void *opaque) { + trace_vmstate_subsection_load(vmsd->name); + while (qemu_peek_byte(f, 0) == QEMU_VM_SUBSECTION) { char idstr[256]; int ret; @@ -201,20 +366,24 @@ static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, len = qemu_peek_byte(f, 1); if (len < strlen(vmsd->name) + 1) { /* subsection name has be be "section_name/a" */ + trace_vmstate_subsection_load_bad(vmsd->name, "(short)"); return 0; } size = qemu_peek_buffer(f, (uint8_t *)idstr, len, 2); if (size != len) { + trace_vmstate_subsection_load_bad(vmsd->name, "(peek fail)"); return 0; } idstr[size] = 0; if (strncmp(vmsd->name, idstr, strlen(vmsd->name)) != 0) { + trace_vmstate_subsection_load_bad(vmsd->name, idstr); /* it don't have a valid subsection name */ return 0; } sub_vmsd = vmstate_get_subsection(vmsd->subsections, idstr); if (sub_vmsd == NULL) { + trace_vmstate_subsection_load_bad(vmsd->name, "(lookup)"); return -ENOENT; } qemu_file_skip(f, 1); /* subsection */ @@ -224,31 +393,53 @@ static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, ret = vmstate_load_state(f, sub_vmsd, opaque, version_id); if (ret) { + trace_vmstate_subsection_load_bad(vmsd->name, "(child)"); return ret; } } + + trace_vmstate_subsection_load_good(vmsd->name); return 0; } static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd, - void *opaque) + void *opaque, QJSON *vmdesc) { const VMStateSubsection *sub = vmsd->subsections; + bool subsection_found = false; while (sub && sub->needed) { if (sub->needed(opaque)) { const VMStateDescription *vmsd = sub->vmsd; uint8_t len; + if (vmdesc) { + /* Only create subsection array when we have any */ + if (!subsection_found) { + json_start_array(vmdesc, "subsections"); + subsection_found = true; + } + + json_start_object(vmdesc, NULL); + } + qemu_put_byte(f, QEMU_VM_SUBSECTION); len = strlen(vmsd->name); qemu_put_byte(f, len); qemu_put_buffer(f, (uint8_t *)vmsd->name, len); qemu_put_be32(f, vmsd->version_id); - vmstate_save_state(f, vmsd, opaque); + vmstate_save_state(f, vmsd, opaque, vmdesc); + + if (vmdesc) { + json_end_object(vmdesc); + } } sub++; } + + if (vmdesc && subsection_found) { + json_end_array(vmdesc); + } } /* bool */ diff --git a/xbzrle.c b/migration/xbzrle.c similarity index 100% rename from xbzrle.c rename to migration/xbzrle.c diff --git a/monitor.c b/monitor.c index f1031a1..68873ec 100644 --- a/monitor.c +++ b/monitor.c @@ -35,6 +35,7 @@ #include "sysemu/char.h" #include "ui/qemu-spice.h" #include "sysemu/sysemu.h" +#include "sysemu/numa.h" #include "monitor/monitor.h" #include "qemu/readline.h" #include "ui/console.h" @@ -72,8 +73,9 @@ #include "block/qapi.h" #include "qapi/qmp-event.h" #include "qapi-event.h" +#include "sysemu/block-backend.h" -/* for pic/irq_info */ +/* for hmp_info_irq/pic */ #if defined(TARGET_SPARC) #include "hw/sparc/sun4m.h" #endif @@ -264,10 +266,7 @@ void monitor_read_command(Monitor *mon, int show_prompt) int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func, void *opaque) { - if (monitor_ctrl_mode(mon)) { - qerror_report(QERR_MISSING_PARAMETER, "password"); - return -EINVAL; - } else if (mon->rs) { + if (mon->rs) { readline_start(mon->rs, "Password: ", 1, readline_func, opaque); /* prompt is printed on return from the command handler */ return 0; @@ -881,7 +880,7 @@ static void do_help_cmd(Monitor *mon, const QDict *qdict) help_cmd(mon, qdict_get_try_str(qdict, "name")); } -static void do_trace_event_set_state(Monitor *mon, const QDict *qdict) +static void hmp_trace_event(Monitor *mon, const QDict *qdict) { const char *tp_name = qdict_get_str(qdict, "name"); bool new_state = qdict_get_bool(qdict, "option"); @@ -889,13 +888,12 @@ static void do_trace_event_set_state(Monitor *mon, const QDict *qdict) qmp_trace_event_set_state(tp_name, new_state, true, true, &local_err); if (local_err) { - qerror_report_err(local_err); - error_free(local_err); + error_report_err(local_err); } } #ifdef CONFIG_TRACE_SIMPLE -static void do_trace_file(Monitor *mon, const QDict *qdict) +static void hmp_trace_file(Monitor *mon, const QDict *qdict) { const char *op = qdict_get_try_str(qdict, "op"); const char *arg = qdict_get_try_str(qdict, "arg"); @@ -958,7 +956,7 @@ static void user_async_cmd_handler(Monitor *mon, const mon_cmd_t *cmd, } } -static void do_info_help(Monitor *mon, const QDict *qdict) +static void hmp_info_help(Monitor *mon, const QDict *qdict) { help_cmd(mon, "info"); } @@ -1027,7 +1025,7 @@ int monitor_get_cpu_index(void) return cpu->cpu_index; } -static void do_info_registers(Monitor *mon, const QDict *qdict) +static void hmp_info_registers(Monitor *mon, const QDict *qdict) { CPUState *cpu; CPUArchState *env; @@ -1036,13 +1034,18 @@ static void do_info_registers(Monitor *mon, const QDict *qdict) cpu_dump_state(cpu, (FILE *)mon, monitor_fprintf, CPU_DUMP_FPU); } -static void do_info_jit(Monitor *mon, const QDict *qdict) +static void hmp_info_jit(Monitor *mon, const QDict *qdict) { dump_exec_info((FILE *)mon, monitor_fprintf); dump_drift_info((FILE *)mon, monitor_fprintf); } -static void do_info_history(Monitor *mon, const QDict *qdict) +static void hmp_info_opcount(Monitor *mon, const QDict *qdict) +{ + dump_opcount_info((FILE *)mon, monitor_fprintf); +} + +static void hmp_info_history(Monitor *mon, const QDict *qdict) { int i; const char *str; @@ -1059,7 +1062,7 @@ static void do_info_history(Monitor *mon, const QDict *qdict) } } -static void do_info_cpu_stats(Monitor *mon, const QDict *qdict) +static void hmp_info_cpustats(Monitor *mon, const QDict *qdict) { CPUState *cpu; CPUArchState *env; @@ -1069,7 +1072,7 @@ static void do_info_cpu_stats(Monitor *mon, const QDict *qdict) cpu_dump_statistics(cpu, (FILE *)mon, &monitor_fprintf, 0); } -static void do_trace_print_events(Monitor *mon, const QDict *qdict) +static void hmp_info_trace_events(Monitor *mon, const QDict *qdict) { TraceEventInfoList *events = qmp_trace_event_get_state("*", NULL); TraceEventInfoList *elem; @@ -1090,11 +1093,13 @@ static int client_migrate_info(Monitor *mon, const QDict *qdict, const char *subject = qdict_get_try_str(qdict, "cert-subject"); int port = qdict_get_try_int(qdict, "port", -1); int tls_port = qdict_get_try_int(qdict, "tls-port", -1); + Error *err = NULL; int ret; if (strcmp(protocol, "spice") == 0) { - if (!using_spice) { - qerror_report(QERR_DEVICE_NOT_ACTIVE, "spice"); + if (!qemu_using_spice(&err)) { + qerror_report_err(err); + error_free(err); return -1; } @@ -1116,12 +1121,12 @@ static int client_migrate_info(Monitor *mon, const QDict *qdict, return -1; } -static void do_logfile(Monitor *mon, const QDict *qdict) +static void hmp_logfile(Monitor *mon, const QDict *qdict) { qemu_set_log_filename(qdict_get_str(qdict, "filename")); } -static void do_log(Monitor *mon, const QDict *qdict) +static void hmp_log(Monitor *mon, const QDict *qdict) { int mask; const char *items = qdict_get_str(qdict, "items"); @@ -1138,7 +1143,7 @@ static void do_log(Monitor *mon, const QDict *qdict) qemu_set_log(mask); } -static void do_singlestep(Monitor *mon, const QDict *qdict) +static void hmp_singlestep(Monitor *mon, const QDict *qdict) { const char *option = qdict_get_try_str(qdict, "option"); if (!option || !strcmp(option, "on")) { @@ -1150,7 +1155,7 @@ static void do_singlestep(Monitor *mon, const QDict *qdict) } } -static void do_gdbserver(Monitor *mon, const QDict *qdict) +static void hmp_gdbserver(Monitor *mon, const QDict *qdict) { const char *device = qdict_get_try_str(qdict, "device"); if (!device) @@ -1166,7 +1171,7 @@ static void do_gdbserver(Monitor *mon, const QDict *qdict) } } -static void do_watchdog_action(Monitor *mon, const QDict *qdict) +static void hmp_watchdog_action(Monitor *mon, const QDict *qdict) { const char *action = qdict_get_str(qdict, "action"); if (select_watchdog_action(action) == -1) { @@ -1287,16 +1292,16 @@ static void memory_dump(Monitor *mon, int count, int format, int wsize, switch(wsize) { default: case 1: - v = ldub_raw(buf + i); + v = ldub_p(buf + i); break; case 2: - v = lduw_raw(buf + i); + v = lduw_p(buf + i); break; case 4: - v = (uint32_t)ldl_raw(buf + i); + v = (uint32_t)ldl_p(buf + i); break; case 8: - v = ldq_raw(buf + i); + v = ldq_p(buf + i); break; } monitor_printf(mon, " "); @@ -1325,7 +1330,7 @@ static void memory_dump(Monitor *mon, int count, int format, int wsize, } } -static void do_memory_dump(Monitor *mon, const QDict *qdict) +static void hmp_memory_dump(Monitor *mon, const QDict *qdict) { int count = qdict_get_int(qdict, "count"); int format = qdict_get_int(qdict, "format"); @@ -1335,7 +1340,7 @@ static void do_memory_dump(Monitor *mon, const QDict *qdict) memory_dump(mon, count, format, size, addr, 0); } -static void do_physical_memory_dump(Monitor *mon, const QDict *qdict) +static void hmp_physical_memory_dump(Monitor *mon, const QDict *qdict) { int count = qdict_get_int(qdict, "count"); int format = qdict_get_int(qdict, "format"); @@ -1371,7 +1376,7 @@ static void do_print(Monitor *mon, const QDict *qdict) monitor_printf(mon, "\n"); } -static void do_sum(Monitor *mon, const QDict *qdict) +static void hmp_sum(Monitor *mon, const QDict *qdict) { uint32_t addr; uint16_t sum; @@ -1390,7 +1395,7 @@ static void do_sum(Monitor *mon, const QDict *qdict) static int mouse_button_state; -static void do_mouse_move(Monitor *mon, const QDict *qdict) +static void hmp_mouse_move(Monitor *mon, const QDict *qdict) { int dx, dy, dz, button; const char *dx_str = qdict_get_str(qdict, "dx_str"); @@ -1414,7 +1419,7 @@ static void do_mouse_move(Monitor *mon, const QDict *qdict) qemu_input_event_sync(); } -static void do_mouse_button(Monitor *mon, const QDict *qdict) +static void hmp_mouse_button(Monitor *mon, const QDict *qdict) { static uint32_t bmap[INPUT_BUTTON_MAX] = { [INPUT_BUTTON_LEFT] = MOUSE_EVENT_LBUTTON, @@ -1431,7 +1436,7 @@ static void do_mouse_button(Monitor *mon, const QDict *qdict) mouse_button_state = button_state; } -static void do_ioport_read(Monitor *mon, const QDict *qdict) +static void hmp_ioport_read(Monitor *mon, const QDict *qdict) { int size = qdict_get_int(qdict, "size"); int addr = qdict_get_int(qdict, "addr"); @@ -1465,7 +1470,7 @@ static void do_ioport_read(Monitor *mon, const QDict *qdict) suffix, addr, size * 2, val); } -static void do_ioport_write(Monitor *mon, const QDict *qdict) +static void hmp_ioport_write(Monitor *mon, const QDict *qdict) { int size = qdict_get_int(qdict, "size"); int addr = qdict_get_int(qdict, "addr"); @@ -1487,19 +1492,17 @@ static void do_ioport_write(Monitor *mon, const QDict *qdict) } } -static void do_boot_set(Monitor *mon, const QDict *qdict) +static void hmp_boot_set(Monitor *mon, const QDict *qdict) { - int res; + Error *local_err = NULL; const char *bootdevice = qdict_get_str(qdict, "bootdevice"); - res = qemu_boot_set(bootdevice); - if (res == 0) { - monitor_printf(mon, "boot device list now set to %s\n", bootdevice); - } else if (res > 0) { - monitor_printf(mon, "setting boot device list failed\n"); + qemu_boot_set(bootdevice, &local_err); + if (local_err) { + monitor_printf(mon, "%s\n", error_get_pretty(local_err)); + error_free(local_err); } else { - monitor_printf(mon, "no function defined to set boot device list for " - "this architecture\n"); + monitor_printf(mon, "boot device list now set to %s\n", bootdevice); } } @@ -1653,7 +1656,7 @@ static void tlb_info_64(Monitor *mon, CPUArchState *env) } #endif -static void tlb_info(Monitor *mon, const QDict *qdict) +static void hmp_info_tlb(Monitor *mon, const QDict *qdict) { CPUArchState *env; @@ -1876,7 +1879,7 @@ static void mem_info_64(Monitor *mon, CPUArchState *env) } #endif -static void mem_info(Monitor *mon, const QDict *qdict) +static void hmp_info_mem(Monitor *mon, const QDict *qdict) { CPUArchState *env; @@ -1915,7 +1918,7 @@ static void print_tlb(Monitor *mon, int idx, tlb_t *tlb) tlb->d, tlb->wt); } -static void tlb_info(Monitor *mon, const QDict *qdict) +static void hmp_info_tlb(Monitor *mon, const QDict *qdict) { CPUArchState *env = mon_get_cpu(); int i; @@ -1931,7 +1934,7 @@ static void tlb_info(Monitor *mon, const QDict *qdict) #endif #if defined(TARGET_SPARC) || defined(TARGET_PPC) || defined(TARGET_XTENSA) -static void tlb_info(Monitor *mon, const QDict *qdict) +static void hmp_info_tlb(Monitor *mon, const QDict *qdict) { CPUArchState *env1 = mon_get_cpu(); @@ -1939,12 +1942,12 @@ static void tlb_info(Monitor *mon, const QDict *qdict) } #endif -static void do_info_mtree(Monitor *mon, const QDict *qdict) +static void hmp_info_mtree(Monitor *mon, const QDict *qdict) { mtree_info((fprintf_function)monitor_printf, mon); } -static void do_info_numa(Monitor *mon, const QDict *qdict) +static void hmp_info_numa(Monitor *mon, const QDict *qdict) { int i; CPUState *cpu; @@ -1969,20 +1972,20 @@ static void do_info_numa(Monitor *mon, const QDict *qdict) #ifdef CONFIG_PROFILER -int64_t qemu_time; +int64_t tcg_time; int64_t dev_time; -static void do_info_profile(Monitor *mon, const QDict *qdict) +static void hmp_info_profile(Monitor *mon, const QDict *qdict) { monitor_printf(mon, "async time %" PRId64 " (%0.3f)\n", dev_time, dev_time / (double)get_ticks_per_sec()); monitor_printf(mon, "qemu time %" PRId64 " (%0.3f)\n", - qemu_time, qemu_time / (double)get_ticks_per_sec()); - qemu_time = 0; + tcg_time, tcg_time / (double)get_ticks_per_sec()); + tcg_time = 0; dev_time = 0; } #else -static void do_info_profile(Monitor *mon, const QDict *qdict) +static void hmp_info_profile(Monitor *mon, const QDict *qdict) { monitor_printf(mon, "Internal profiler not compiled\n"); } @@ -1991,7 +1994,7 @@ static void do_info_profile(Monitor *mon, const QDict *qdict) /* Capture support */ static QLIST_HEAD (capture_list_head, CaptureState) capture_head; -static void do_info_capture(Monitor *mon, const QDict *qdict) +static void hmp_info_capture(Monitor *mon, const QDict *qdict) { int i; CaptureState *s; @@ -2002,7 +2005,7 @@ static void do_info_capture(Monitor *mon, const QDict *qdict) } } -static void do_stop_capture(Monitor *mon, const QDict *qdict) +static void hmp_stopcapture(Monitor *mon, const QDict *qdict) { int i; int n = qdict_get_int(qdict, "n"); @@ -2018,7 +2021,7 @@ static void do_stop_capture(Monitor *mon, const QDict *qdict) } } -static void do_wav_capture(Monitor *mon, const QDict *qdict) +static void hmp_wavcapture(Monitor *mon, const QDict *qdict) { const char *path = qdict_get_str(qdict, "path"); int has_freq = qdict_haskey(qdict, "freq"); @@ -2053,7 +2056,7 @@ static qemu_acl *find_acl(Monitor *mon, const char *name) return acl; } -static void do_acl_show(Monitor *mon, const QDict *qdict) +static void hmp_acl_show(Monitor *mon, const QDict *qdict) { const char *aclname = qdict_get_str(qdict, "aclname"); qemu_acl *acl = find_acl(mon, aclname); @@ -2071,7 +2074,7 @@ static void do_acl_show(Monitor *mon, const QDict *qdict) } } -static void do_acl_reset(Monitor *mon, const QDict *qdict) +static void hmp_acl_reset(Monitor *mon, const QDict *qdict) { const char *aclname = qdict_get_str(qdict, "aclname"); qemu_acl *acl = find_acl(mon, aclname); @@ -2082,7 +2085,7 @@ static void do_acl_reset(Monitor *mon, const QDict *qdict) } } -static void do_acl_policy(Monitor *mon, const QDict *qdict) +static void hmp_acl_policy(Monitor *mon, const QDict *qdict) { const char *aclname = qdict_get_str(qdict, "aclname"); const char *policy = qdict_get_str(qdict, "policy"); @@ -2102,7 +2105,7 @@ static void do_acl_policy(Monitor *mon, const QDict *qdict) } } -static void do_acl_add(Monitor *mon, const QDict *qdict) +static void hmp_acl_add(Monitor *mon, const QDict *qdict) { const char *aclname = qdict_get_str(qdict, "aclname"); const char *match = qdict_get_str(qdict, "match"); @@ -2133,7 +2136,7 @@ static void do_acl_add(Monitor *mon, const QDict *qdict) } } -static void do_acl_remove(Monitor *mon, const QDict *qdict) +static void hmp_acl_remove(Monitor *mon, const QDict *qdict) { const char *aclname = qdict_get_str(qdict, "aclname"); const char *match = qdict_get_str(qdict, "match"); @@ -2150,7 +2153,7 @@ static void do_acl_remove(Monitor *mon, const QDict *qdict) } #if defined(TARGET_I386) -static void do_inject_mce(Monitor *mon, const QDict *qdict) +static void hmp_mce(Monitor *mon, const QDict *qdict) { X86CPU *cpu; CPUState *cs; @@ -2228,7 +2231,7 @@ void qmp_closefd(const char *fdname, Error **errp) error_set(errp, QERR_FD_NOT_FOUND, fdname); } -static void do_loadvm(Monitor *mon, const QDict *qdict) +static void hmp_loadvm(Monitor *mon, const QDict *qdict) { int saved_vm_running = runstate_is_running(); const char *name = qdict_get_str(qdict, "name"); @@ -2566,20 +2569,7 @@ void monitor_fdset_dup_fd_remove(int dup_fd) monitor_fdset_dup_fd_find_remove(dup_fd, true); } -int monitor_handle_fd_param(Monitor *mon, const char *fdname) -{ - int fd; - Error *local_err = NULL; - - fd = monitor_handle_fd_param2(mon, fdname, &local_err); - if (local_err) { - qerror_report_err(local_err); - error_free(local_err); - } - return fd; -} - -int monitor_handle_fd_param2(Monitor *mon, const char *fdname, Error **errp) +int monitor_fd_param(Monitor *mon, const char *fdname, Error **errp) { int fd; Error *local_err = NULL; @@ -2617,7 +2607,7 @@ static mon_cmd_t info_cmds[] = { .args_type = "", .params = "", .help = "show the network state", - .mhandler.cmd = do_info_network, + .mhandler.cmd = hmp_info_network, }, { .name = "chardev", @@ -2628,10 +2618,10 @@ static mon_cmd_t info_cmds[] = { }, { .name = "block", - .args_type = "verbose:-v,device:B?", - .params = "[-v] [device]", + .args_type = "nodes:-n,verbose:-v,device:B?", + .params = "[-n] [-v] [device]", .help = "show info of one block device or all block devices " - "(and details of images with -v option)", + "(-n: show named nodes; -v: show details)", .mhandler.cmd = hmp_info_block, }, { @@ -2653,7 +2643,7 @@ static mon_cmd_t info_cmds[] = { .args_type = "", .params = "", .help = "show the cpu registers", - .mhandler.cmd = do_info_registers, + .mhandler.cmd = hmp_info_registers, }, { .name = "cpus", @@ -2667,7 +2657,7 @@ static mon_cmd_t info_cmds[] = { .args_type = "", .params = "", .help = "show the command line history", - .mhandler.cmd = do_info_history, + .mhandler.cmd = hmp_info_history, }, #if defined(TARGET_I386) || defined(TARGET_PPC) || defined(TARGET_MIPS) || \ defined(TARGET_LM32) || (defined(TARGET_SPARC) && !defined(TARGET_SPARC64)) @@ -2677,11 +2667,11 @@ static mon_cmd_t info_cmds[] = { .params = "", .help = "show the interrupts statistics (if available)", #ifdef TARGET_SPARC - .mhandler.cmd = sun4m_irq_info, + .mhandler.cmd = sun4m_hmp_info_irq, #elif defined(TARGET_LM32) - .mhandler.cmd = lm32_irq_info, + .mhandler.cmd = lm32_hmp_info_irq, #else - .mhandler.cmd = irq_info, + .mhandler.cmd = hmp_info_irq, #endif }, { @@ -2690,11 +2680,11 @@ static mon_cmd_t info_cmds[] = { .params = "", .help = "show i8259 (PIC) state", #ifdef TARGET_SPARC - .mhandler.cmd = sun4m_pic_info, + .mhandler.cmd = sun4m_hmp_info_pic, #elif defined(TARGET_LM32) - .mhandler.cmd = lm32_do_pic_info, + .mhandler.cmd = lm32_hmp_info_pic, #else - .mhandler.cmd = pic_info, + .mhandler.cmd = hmp_info_pic, #endif }, #endif @@ -2712,7 +2702,7 @@ static mon_cmd_t info_cmds[] = { .args_type = "", .params = "", .help = "show virtual to physical memory mappings", - .mhandler.cmd = tlb_info, + .mhandler.cmd = hmp_info_tlb, }, #endif #if defined(TARGET_I386) @@ -2721,7 +2711,7 @@ static mon_cmd_t info_cmds[] = { .args_type = "", .params = "", .help = "show the active virtual memory mappings", - .mhandler.cmd = mem_info, + .mhandler.cmd = hmp_info_mem, }, #endif { @@ -2729,14 +2719,21 @@ static mon_cmd_t info_cmds[] = { .args_type = "", .params = "", .help = "show memory tree", - .mhandler.cmd = do_info_mtree, + .mhandler.cmd = hmp_info_mtree, }, { .name = "jit", .args_type = "", .params = "", .help = "show dynamic compiler info", - .mhandler.cmd = do_info_jit, + .mhandler.cmd = hmp_info_jit, + }, + { + .name = "opcount", + .args_type = "", + .params = "", + .help = "show dynamic compiler opcode counters", + .mhandler.cmd = hmp_info_opcount, }, { .name = "kvm", @@ -2750,42 +2747,42 @@ static mon_cmd_t info_cmds[] = { .args_type = "", .params = "", .help = "show NUMA information", - .mhandler.cmd = do_info_numa, + .mhandler.cmd = hmp_info_numa, }, { .name = "usb", .args_type = "", .params = "", .help = "show guest USB devices", - .mhandler.cmd = usb_info, + .mhandler.cmd = hmp_info_usb, }, { .name = "usbhost", .args_type = "", .params = "", .help = "show host USB devices", - .mhandler.cmd = usb_host_info, + .mhandler.cmd = hmp_info_usbhost, }, { .name = "profile", .args_type = "", .params = "", .help = "show profiling information", - .mhandler.cmd = do_info_profile, + .mhandler.cmd = hmp_info_profile, }, { .name = "capture", .args_type = "", .params = "", .help = "show capture information", - .mhandler.cmd = do_info_capture, + .mhandler.cmd = hmp_info_capture, }, { .name = "snapshots", .args_type = "", .params = "", .help = "show the currently saved VM snapshots", - .mhandler.cmd = do_info_snapshots, + .mhandler.cmd = hmp_info_snapshots, }, { .name = "status", @@ -2836,7 +2833,7 @@ static mon_cmd_t info_cmds[] = { .args_type = "", .params = "", .help = "show CPU statistics", - .mhandler.cmd = do_info_cpu_stats, + .mhandler.cmd = hmp_info_cpustats, }, #if defined(CONFIG_SLIRP) { @@ -2844,7 +2841,7 @@ static mon_cmd_t info_cmds[] = { .args_type = "", .params = "", .help = "show user network stack connection states", - .mhandler.cmd = do_info_usernet, + .mhandler.cmd = hmp_info_usernet, }, #endif { @@ -2880,28 +2877,35 @@ static mon_cmd_t info_cmds[] = { .args_type = "", .params = "", .help = "show device tree", - .mhandler.cmd = do_info_qtree, + .mhandler.cmd = hmp_info_qtree, }, { .name = "qdm", .args_type = "", .params = "", .help = "show qdev device model list", - .mhandler.cmd = do_info_qdm, + .mhandler.cmd = hmp_info_qdm, + }, + { + .name = "qom-tree", + .args_type = "path:s?", + .params = "[path]", + .help = "show QOM composition tree", + .mhandler.cmd = hmp_info_qom_tree, }, { .name = "roms", .args_type = "", .params = "", .help = "show roms", - .mhandler.cmd = do_info_roms, + .mhandler.cmd = hmp_info_roms, }, { .name = "trace-events", .args_type = "", .params = "", .help = "show available trace-events & their state", - .mhandler.cmd = do_trace_print_events, + .mhandler.cmd = hmp_info_trace_events, }, { .name = "tpm", @@ -4587,8 +4591,13 @@ void host_net_remove_completion(ReadLineState *rs, int nb_args, const char *str) count = qemu_find_net_clients_except(NULL, ncs, NET_CLIENT_OPTIONS_KIND_NIC, 255); for (i = 0; i < count; i++) { + int id; const char *name; + if (ncs[i]->info->type == NET_CLIENT_OPTIONS_KIND_HUBPORT || + net_hub_id_for_client(ncs[i], &id)) { + continue; + } name = ncs[i]->name; if (!strncmp(str, name, len)) { readline_add_completion(rs, name); @@ -4679,11 +4688,13 @@ static void monitor_find_completion_by_table(Monitor *mon, if (cmd->sub_table) { /* do the job again */ - return monitor_find_completion_by_table(mon, cmd->sub_table, - &args[1], nb_args - 1); + monitor_find_completion_by_table(mon, cmd->sub_table, + &args[1], nb_args - 1); + return; } if (cmd->command_completion) { - return cmd->command_completion(mon->rs, nb_args, args[nb_args - 1]); + cmd->command_completion(mon->rs, nb_args, args[nb_args - 1]); + return; } ptype = next_arg_type(cmd->args_type); @@ -4695,7 +4706,7 @@ static void monitor_find_completion_by_table(Monitor *mon, } } str = args[nb_args - 1]; - if (*ptype == '-' && ptype[1] != '\0') { + while (*ptype == '-' && ptype[1] != '\0') { ptype = next_arg_type(ptype); } switch(*ptype) { @@ -4772,9 +4783,9 @@ static int monitor_can_read(void *opaque) return (mon->suspend_cnt == 0) ? 1 : 0; } -static int invalid_qmp_mode(const Monitor *mon, const char *cmd_name) +static int invalid_qmp_mode(const Monitor *mon, const mon_cmd_t *cmd) { - int is_cap = compare_cmd(cmd_name, "qmp_capabilities"); + int is_cap = cmd->mhandler.cmd_new == do_qmp_capabilities; return (qmp_cmd_mode(mon) ? is_cap : !is_cap); } @@ -5068,14 +5079,10 @@ static void handle_qmp_command(JSONMessageParser *parser, QList *tokens) cmd_name = qdict_get_str(input, "execute"); trace_handle_qmp_command(mon, cmd_name); - if (invalid_qmp_mode(mon, cmd_name)) { - qerror_report(QERR_COMMAND_NOT_FOUND, cmd_name); - goto err_out; - } - cmd = qmp_find_cmd(cmd_name); - if (!cmd) { - qerror_report(QERR_COMMAND_NOT_FOUND, cmd_name); + if (!cmd || invalid_qmp_mode(mon, cmd)) { + qerror_report(ERROR_CLASS_COMMAND_NOT_FOUND, + "The command %s has not been found", cmd_name); goto err_out; } @@ -5356,9 +5363,12 @@ static void bdrv_password_cb(void *opaque, const char *password, Monitor *mon = opaque; BlockDriverState *bs = readline_opaque; int ret = 0; + Error *local_err = NULL; - if (bdrv_set_key(bs, password) != 0) { - monitor_printf(mon, "invalid password\n"); + bdrv_add_key(bs, password, &local_err); + if (local_err) { + monitor_printf(mon, "%s\n", error_get_pretty(local_err)); + error_free(local_err); ret = -EPERM; } if (mon->password_completion_cb) @@ -5378,18 +5388,6 @@ int monitor_read_bdrv_key_start(Monitor *mon, BlockDriverState *bs, { int err; - if (!bdrv_key_required(bs)) { - if (completion_cb) - completion_cb(opaque, 0); - return 0; - } - - if (monitor_ctrl_mode(mon)) { - qerror_report(QERR_DEVICE_ENCRYPTED, bdrv_get_device_name(bs), - bdrv_get_encrypted_filename(bs)); - return -1; - } - monitor_printf(mon, "%s (%s) is encrypted.\n", bdrv_get_device_name(bs), bdrv_get_encrypted_filename(bs)); @@ -5408,15 +5406,25 @@ int monitor_read_block_device_key(Monitor *mon, const char *device, BlockCompletionFunc *completion_cb, void *opaque) { - BlockDriverState *bs; + Error *err = NULL; + BlockBackend *blk; - bs = bdrv_find(device); - if (!bs) { + blk = blk_by_name(device); + if (!blk) { monitor_printf(mon, "Device not found %s\n", device); return -1; } - return monitor_read_bdrv_key_start(mon, bs, completion_cb, opaque); + bdrv_add_key(blk_bs(blk), NULL, &err); + if (err) { + error_free(err); + return monitor_read_bdrv_key_start(mon, blk_bs(blk), completion_cb, opaque); + } + + if (completion_cb) { + completion_cb(opaque, 0); + } + return 0; } QemuOptsList qemu_mon_opts = { diff --git a/nbd.c b/nbd.c index a7bce45..cb1b9bb 100644 --- a/nbd.c +++ b/nbd.c @@ -17,8 +17,7 @@ */ #include "block/nbd.h" -#include "block/block.h" -#include "block/block_int.h" +#include "sysemu/block-backend.h" #include "block/coroutine.h" @@ -101,7 +100,7 @@ struct NBDExport { int refcount; void (*close)(NBDExport *exp); - BlockDriverState *bs; + BlockBackend *blk; char *name; off_t dev_offset; off_t size; @@ -194,6 +193,26 @@ static ssize_t read_sync(int fd, void *buffer, size_t size) return nbd_wr_sync(fd, buffer, size, true); } +static ssize_t drop_sync(int fd, size_t size) +{ + ssize_t ret, dropped = size; + uint8_t *buffer = g_malloc(MIN(65536, size)); + + while (size > 0) { + ret = read_sync(fd, buffer, MIN(65536, size)); + if (ret < 0) { + g_free(buffer); + return ret; + } + + assert(ret <= size); + size -= ret; + } + + g_free(buffer); + return dropped; +} + static ssize_t write_sync(int fd, void *buffer, size_t size) { int ret; @@ -304,6 +323,9 @@ static int nbd_handle_list(NBDClient *client, uint32_t length) csock = client->sock; if (length) { + if (drop_sync(csock, length) != length) { + return -EIO; + } return nbd_send_rep(csock, NBD_REP_ERR_INVALID, NBD_OPT_LIST); } @@ -351,30 +373,39 @@ fail: static int nbd_receive_options(NBDClient *client) { + int csock = client->sock; + uint32_t flags; + + /* Client sends: + [ 0 .. 3] client flags + + [ 0 .. 7] NBD_OPTS_MAGIC + [ 8 .. 11] NBD option + [12 .. 15] Data length + ... Rest of request + + [ 0 .. 7] NBD_OPTS_MAGIC + [ 8 .. 11] Second NBD option + [12 .. 15] Data length + ... Rest of request + */ + + if (read_sync(csock, &flags, sizeof(flags)) != sizeof(flags)) { + LOG("read failed"); + return -EIO; + } + TRACE("Checking client flags"); + be32_to_cpus(&flags); + if (flags != 0 && flags != NBD_FLAG_C_FIXED_NEWSTYLE) { + LOG("Bad client flags received"); + return -EIO; + } + while (1) { - int csock = client->sock; + int ret; uint32_t tmp, length; uint64_t magic; - /* Client sends: - [ 0 .. 3] client flags - [ 4 .. 11] NBD_OPTS_MAGIC - [12 .. 15] NBD option - [16 .. 19] length - ... Rest of request - */ - - if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) { - LOG("read failed"); - return -EINVAL; - } - TRACE("Checking client flags"); - tmp = be32_to_cpu(tmp); - if (tmp != 0 && tmp != NBD_FLAG_C_FIXED_NEWSTYLE) { - LOG("Bad client flags received"); - return -EINVAL; - } - if (read_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) { LOG("read failed"); return -EINVAL; @@ -399,8 +430,9 @@ static int nbd_receive_options(NBDClient *client) TRACE("Checking option"); switch (be32_to_cpu(tmp)) { case NBD_OPT_LIST: - if (nbd_handle_list(client, length) < 0) { - return 1; + ret = nbd_handle_list(client, length); + if (ret < 0) { + return ret; } break; @@ -495,7 +527,7 @@ fail: } int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags, - off_t *size, size_t *blocksize) + off_t *size, Error **errp) { char buf[256]; uint64_t magic, s; @@ -507,13 +539,13 @@ int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags, rc = -EINVAL; if (read_sync(csock, buf, 8) != 8) { - LOG("read failed"); + error_setg(errp, "Failed to read data"); goto fail; } buf[8] = '\0'; if (strlen(buf) == 0) { - LOG("server connection closed"); + error_setg(errp, "Server connection closed unexpectedly"); goto fail; } @@ -528,12 +560,12 @@ int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags, qemu_isprint(buf[7]) ? buf[7] : '.'); if (memcmp(buf, "NBDMAGIC", 8) != 0) { - LOG("Invalid magic received"); + error_setg(errp, "Invalid magic received"); goto fail; } if (read_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) { - LOG("read failed"); + error_setg(errp, "Failed to read magic"); goto fail; } magic = be64_to_cpu(magic); @@ -546,73 +578,80 @@ int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags, TRACE("Checking magic (opts_magic)"); if (magic != NBD_OPTS_MAGIC) { - LOG("Bad magic received"); + if (magic == NBD_CLIENT_MAGIC) { + error_setg(errp, "Server does not support export names"); + } else { + error_setg(errp, "Bad magic received"); + } goto fail; } if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) { - LOG("flags read failed"); + error_setg(errp, "Failed to read server flags"); goto fail; } *flags = be16_to_cpu(tmp) << 16; /* reserved for future use */ if (write_sync(csock, &reserved, sizeof(reserved)) != sizeof(reserved)) { - LOG("write failed (reserved)"); + error_setg(errp, "Failed to read reserved field"); goto fail; } /* write the export name */ magic = cpu_to_be64(magic); if (write_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) { - LOG("write failed (magic)"); + error_setg(errp, "Failed to send export name magic"); goto fail; } opt = cpu_to_be32(NBD_OPT_EXPORT_NAME); if (write_sync(csock, &opt, sizeof(opt)) != sizeof(opt)) { - LOG("write failed (opt)"); + error_setg(errp, "Failed to send export name option number"); goto fail; } namesize = cpu_to_be32(strlen(name)); if (write_sync(csock, &namesize, sizeof(namesize)) != sizeof(namesize)) { - LOG("write failed (namesize)"); + error_setg(errp, "Failed to send export name length"); goto fail; } if (write_sync(csock, (char*)name, strlen(name)) != strlen(name)) { - LOG("write failed (name)"); + error_setg(errp, "Failed to send export name"); goto fail; } } else { TRACE("Checking magic (cli_magic)"); if (magic != NBD_CLIENT_MAGIC) { - LOG("Bad magic received"); + if (magic == NBD_OPTS_MAGIC) { + error_setg(errp, "Server requires an export name"); + } else { + error_setg(errp, "Bad magic received"); + } goto fail; } } if (read_sync(csock, &s, sizeof(s)) != sizeof(s)) { - LOG("read failed"); + error_setg(errp, "Failed to read export length"); goto fail; } *size = be64_to_cpu(s); - *blocksize = 1024; TRACE("Size is %" PRIu64, *size); if (!name) { if (read_sync(csock, flags, sizeof(*flags)) != sizeof(*flags)) { - LOG("read failed (flags)"); + error_setg(errp, "Failed to read export flags"); goto fail; } *flags = be32_to_cpup(flags); } else { if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) { - LOG("read failed (tmp)"); + error_setg(errp, "Failed to read export flags"); goto fail; } - *flags |= be32_to_cpu(tmp); + *flags |= be16_to_cpu(tmp); } if (read_sync(csock, &buf, 124) != 124) { - LOG("read failed (buf)"); + error_setg(errp, "Failed to read reserved block"); goto fail; } rc = 0; @@ -622,7 +661,7 @@ fail: } #ifdef __linux__ -int nbd_init(int fd, int csock, uint32_t flags, off_t size, size_t blocksize) +int nbd_init(int fd, int csock, uint32_t flags, off_t size) { TRACE("Setting NBD socket"); @@ -632,17 +671,17 @@ int nbd_init(int fd, int csock, uint32_t flags, off_t size, size_t blocksize) return -serrno; } - TRACE("Setting block size to %lu", (unsigned long)blocksize); + TRACE("Setting block size to %lu", (unsigned long)BDRV_SECTOR_SIZE); - if (ioctl(fd, NBD_SET_BLKSIZE, blocksize) < 0) { + if (ioctl(fd, NBD_SET_BLKSIZE, (size_t)BDRV_SECTOR_SIZE) < 0) { int serrno = errno; LOG("Failed setting NBD block size"); return -serrno; } - TRACE("Setting size to %zd block(s)", (size_t)(size / blocksize)); + TRACE("Setting size to %zd block(s)", (size_t)(size / BDRV_SECTOR_SIZE)); - if (ioctl(fd, NBD_SET_SIZE_BLOCKS, size / blocksize) < 0) { + if (ioctl(fd, NBD_SET_SIZE_BLOCKS, (size_t)(size / BDRV_SECTOR_SIZE)) < 0) { int serrno = errno; LOG("Failed setting size (in blocks)"); return -serrno; @@ -707,7 +746,7 @@ int nbd_client(int fd) return ret; } #else -int nbd_init(int fd, int csock, uint32_t flags, off_t size, size_t blocksize) +int nbd_init(int fd, int csock, uint32_t flags, off_t size) { return -ENOTSUP; } @@ -867,7 +906,7 @@ void nbd_client_put(NBDClient *client) { if (--client->refcount == 0) { /* The last reference should be dropped by client->close, - * which is called by nbd_client_close. + * which is called by client_close. */ assert(client->closing); @@ -882,7 +921,7 @@ void nbd_client_put(NBDClient *client) } } -void nbd_client_close(NBDClient *client) +static void client_close(NBDClient *client) { if (client->closing) { return; @@ -929,7 +968,7 @@ static void nbd_request_put(NBDRequest *req) nbd_client_put(client); } -static void bs_aio_attached(AioContext *ctx, void *opaque) +static void blk_aio_attached(AioContext *ctx, void *opaque) { NBDExport *exp = opaque; NBDClient *client; @@ -943,7 +982,7 @@ static void bs_aio_attached(AioContext *ctx, void *opaque) } } -static void bs_aio_detach(void *opaque) +static void blk_aio_detach(void *opaque) { NBDExport *exp = opaque; NBDClient *client; @@ -957,28 +996,39 @@ static void bs_aio_detach(void *opaque) exp->ctx = NULL; } -NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, - off_t size, uint32_t nbdflags, - void (*close)(NBDExport *)) +NBDExport *nbd_export_new(BlockBackend *blk, off_t dev_offset, off_t size, + uint32_t nbdflags, void (*close)(NBDExport *), + Error **errp) { NBDExport *exp = g_malloc0(sizeof(NBDExport)); exp->refcount = 1; QTAILQ_INIT(&exp->clients); - exp->bs = bs; + exp->blk = blk; exp->dev_offset = dev_offset; exp->nbdflags = nbdflags; - exp->size = size == -1 ? bdrv_getlength(bs) : size; + exp->size = size < 0 ? blk_getlength(blk) : size; + if (exp->size < 0) { + error_setg_errno(errp, -exp->size, + "Failed to determine the NBD export's length"); + goto fail; + } + exp->size -= exp->size % BDRV_SECTOR_SIZE; + exp->close = close; - exp->ctx = bdrv_get_aio_context(bs); - bdrv_ref(bs); - bdrv_add_aio_context_notifier(bs, bs_aio_attached, bs_aio_detach, exp); + exp->ctx = blk_get_aio_context(blk); + blk_ref(blk); + blk_add_aio_context_notifier(blk, blk_aio_attached, blk_aio_detach, exp); /* * NBD exports are used for non-shared storage migration. Make sure * that BDRV_O_INCOMING is cleared and the image is ready for write * access since the export could be available before migration handover. */ - bdrv_invalidate_cache(bs, NULL); + blk_invalidate_cache(blk, NULL); return exp; + +fail: + g_free(exp); + return NULL; } NBDExport *nbd_export_find(const char *name) @@ -1020,15 +1070,15 @@ void nbd_export_close(NBDExport *exp) nbd_export_get(exp); QTAILQ_FOREACH_SAFE(client, &exp->clients, next, next) { - nbd_client_close(client); + client_close(client); } nbd_export_set_name(exp, NULL); nbd_export_put(exp); - if (exp->bs) { - bdrv_remove_aio_context_notifier(exp->bs, bs_aio_attached, - bs_aio_detach, exp); - bdrv_unref(exp->bs); - exp->bs = NULL; + if (exp->blk) { + blk_remove_aio_context_notifier(exp->blk, blk_aio_attached, + blk_aio_detach, exp); + blk_unref(exp->blk); + exp->blk = NULL; } } @@ -1056,9 +1106,9 @@ void nbd_export_put(NBDExport *exp) } } -BlockDriverState *nbd_export_get_blockdev(NBDExport *exp) +BlockBackend *nbd_export_get_blockdev(NBDExport *exp) { - return exp->bs; + return exp->blk; } void nbd_export_close_all(void) @@ -1137,7 +1187,7 @@ static ssize_t nbd_co_receive_request(NBDRequest *req, struct nbd_request *reque command = request->type & NBD_CMD_MASK_COMMAND; if (command == NBD_CMD_READ || command == NBD_CMD_WRITE) { - req->data = qemu_blockalign(client->exp->bs, request->len); + req->data = blk_blockalign(client->exp->blk, request->len); } if (command == NBD_CMD_WRITE) { TRACE("Reading %u byte(s)", request->len); @@ -1203,7 +1253,7 @@ static void nbd_trip(void *opaque) TRACE("Request type is READ"); if (request.type & NBD_CMD_FLAG_FUA) { - ret = bdrv_co_flush(exp->bs); + ret = blk_co_flush(exp->blk); if (ret < 0) { LOG("flush failed"); reply.error = -ret; @@ -1211,8 +1261,9 @@ static void nbd_trip(void *opaque) } } - ret = bdrv_read(exp->bs, (request.from + exp->dev_offset) / 512, - req->data, request.len / 512); + ret = blk_read(exp->blk, + (request.from + exp->dev_offset) / BDRV_SECTOR_SIZE, + req->data, request.len / BDRV_SECTOR_SIZE); if (ret < 0) { LOG("reading from file failed"); reply.error = -ret; @@ -1234,8 +1285,9 @@ static void nbd_trip(void *opaque) TRACE("Writing to device"); - ret = bdrv_write(exp->bs, (request.from + exp->dev_offset) / 512, - req->data, request.len / 512); + ret = blk_write(exp->blk, + (request.from + exp->dev_offset) / BDRV_SECTOR_SIZE, + req->data, request.len / BDRV_SECTOR_SIZE); if (ret < 0) { LOG("writing to file failed"); reply.error = -ret; @@ -1243,7 +1295,7 @@ static void nbd_trip(void *opaque) } if (request.type & NBD_CMD_FLAG_FUA) { - ret = bdrv_co_flush(exp->bs); + ret = blk_co_flush(exp->blk); if (ret < 0) { LOG("flush failed"); reply.error = -ret; @@ -1262,7 +1314,7 @@ static void nbd_trip(void *opaque) case NBD_CMD_FLUSH: TRACE("Request type is FLUSH"); - ret = bdrv_co_flush(exp->bs); + ret = blk_co_flush(exp->blk); if (ret < 0) { LOG("flush failed"); reply.error = -ret; @@ -1273,8 +1325,9 @@ static void nbd_trip(void *opaque) break; case NBD_CMD_TRIM: TRACE("Request type is TRIM"); - ret = bdrv_co_discard(exp->bs, (request.from + exp->dev_offset) / 512, - request.len / 512); + ret = blk_co_discard(exp->blk, (request.from + exp->dev_offset) + / BDRV_SECTOR_SIZE, + request.len / BDRV_SECTOR_SIZE); if (ret < 0) { LOG("discard failed"); reply.error = -ret; @@ -1286,7 +1339,7 @@ static void nbd_trip(void *opaque) default: LOG("invalid request type (%u) received", request.type); invalid_request: - reply.error = -EINVAL; + reply.error = EINVAL; error_reply: if (nbd_co_send_reply(req, &reply, 0) < 0) { goto out; @@ -1302,7 +1355,7 @@ done: out: nbd_request_put(req); - nbd_client_close(client); + client_close(client); } static void nbd_read(void *opaque) diff --git a/net/hub.c b/net/hub.c index 7e0f2d6..2b60ab9 100644 --- a/net/hub.c +++ b/net/hub.c @@ -245,9 +245,12 @@ void net_hub_info(Monitor *mon) QLIST_FOREACH(hub, &hubs, next) { monitor_printf(mon, "hub %d\n", hub->id); QLIST_FOREACH(port, &hub->ports, next) { + monitor_printf(mon, " \\ %s", port->nc.name); if (port->nc.peer) { - monitor_printf(mon, " \\ "); + monitor_printf(mon, ": "); print_net_client(mon, port->nc.peer); + } else { + monitor_printf(mon, "\n"); } } } @@ -285,7 +288,6 @@ int net_init_hubport(const NetClientOptions *opts, const char *name, assert(opts->kind == NET_CLIENT_OPTIONS_KIND_HUBPORT); hubport = opts->hubport; - /* Treat hub port like a backend, NIC must be the one to peer */ if (peer) { return -EINVAL; } diff --git a/net/l2tpv3.c b/net/l2tpv3.c index 3b805a7..8c598b0 100644 --- a/net/l2tpv3.c +++ b/net/l2tpv3.c @@ -489,12 +489,12 @@ static struct mmsghdr *build_l2tpv3_vector(NetL2TPV3State *s, int count) struct iovec *iov; struct mmsghdr *msgvec, *result; - msgvec = g_malloc(sizeof(struct mmsghdr) * count); + msgvec = g_new(struct mmsghdr, count); result = msgvec; for (i = 0; i < count ; i++) { msgvec->msg_hdr.msg_name = NULL; msgvec->msg_hdr.msg_namelen = 0; - iov = g_malloc(sizeof(struct iovec) * IOVSIZE); + iov = g_new(struct iovec, IOVSIZE); msgvec->msg_hdr.msg_iov = iov; iov->iov_base = g_malloc(s->header_size); iov->iov_len = s->header_size; @@ -695,8 +695,7 @@ int net_init_l2tpv3(const NetClientOptions *opts, goto outerr; } - s->dgram_dst = g_malloc(sizeof(struct sockaddr_storage)); - memset(s->dgram_dst, '\0' , sizeof(struct sockaddr_storage)); + s->dgram_dst = g_new0(struct sockaddr_storage, 1); memcpy(s->dgram_dst, result->ai_addr, result->ai_addrlen); s->dst_size = result->ai_addrlen; @@ -730,7 +729,7 @@ int net_init_l2tpv3(const NetClientOptions *opts, } s->msgvec = build_l2tpv3_vector(s, MAX_L2TPV3_MSGCNT); - s->vec = g_malloc(sizeof(struct iovec) * MAX_L2TPV3_IOVCNT); + s->vec = g_new(struct iovec, MAX_L2TPV3_IOVCNT); s->header_buf = g_malloc(s->header_size); qemu_set_nonblock(fd); diff --git a/net/net.c b/net/net.c index 7acc162..0be084d 100644 --- a/net/net.c +++ b/net/net.c @@ -324,6 +324,8 @@ void qemu_del_net_client(NetClientState *nc) NetClientState *ncs[MAX_QUEUE_NUM]; int queues, i; + assert(nc->info->type != NET_CLIENT_OPTIONS_KIND_NIC); + /* If the NetClientState belongs to a multiqueue backend, we will change all * other NetClientStates also. */ @@ -355,8 +357,6 @@ void qemu_del_net_client(NetClientState *nc) return; } - assert(nc->info->type != NET_CLIENT_OPTIONS_KIND_NIC); - for (i = 0; i < queues; i++) { qemu_cleanup_net_client(ncs[i]); qemu_free_net_client(ncs[i]); @@ -953,7 +953,7 @@ static int net_host_check_device(const char *device) return 0; } -void net_host_device_add(Monitor *mon, const QDict *qdict) +void hmp_host_net_add(Monitor *mon, const QDict *qdict) { const char *device = qdict_get_str(qdict, "device"); const char *opts_str = qdict_get_try_str(qdict, "opts"); @@ -970,17 +970,16 @@ void net_host_device_add(Monitor *mon, const QDict *qdict) return; } - qemu_opt_set(opts, "type", device); + qemu_opt_set(opts, "type", device, &error_abort); net_client_init(opts, 0, &local_err); if (local_err) { - qerror_report_err(local_err); - error_free(local_err); + error_report_err(local_err); monitor_printf(mon, "adding host network device %s failed\n", device); } } -void net_host_device_remove(Monitor *mon, const QDict *qdict) +void hmp_host_net_remove(Monitor *mon, const QDict *qdict) { NetClientState *nc; int vlan_id = qdict_get_int(qdict, "vlan_id"); @@ -992,10 +991,12 @@ void net_host_device_remove(Monitor *mon, const QDict *qdict) device, vlan_id); return; } - if (!net_host_check_device(nc->model)) { + if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) { error_report("invalid host network device '%s'", device); return; } + + qemu_del_net_client(nc->peer); qemu_del_net_client(nc); } @@ -1115,7 +1116,7 @@ RxFilterInfoList *qmp_query_rx_filter(bool has_name, const char *name, return filter_list; } -void do_info_network(Monitor *mon, const QDict *qdict) +void hmp_info_network(Monitor *mon, const QDict *qdict) { NetClientState *nc, *peer; NetClientOptionsKind type; @@ -1268,8 +1269,7 @@ static int net_init_client(QemuOpts *opts, void *dummy) net_client_init(opts, 0, &local_err); if (local_err) { - qerror_report_err(local_err); - error_free(local_err); + error_report_err(local_err); return -1; } @@ -1283,8 +1283,7 @@ static int net_init_netdev(QemuOpts *opts, void *dummy) ret = net_client_init(opts, 1, &local_err); if (local_err) { - qerror_report_err(local_err); - error_free(local_err); + error_report_err(local_err); return -1; } @@ -1297,9 +1296,9 @@ int net_init_clients(void) if (default_net) { /* if no clients, we use a default config */ - qemu_opts_set(net, NULL, "type", "nic"); + qemu_opts_set(net, NULL, "type", "nic", &error_abort); #ifdef CONFIG_SLIRP - qemu_opts_set(net, NULL, "type", "user"); + qemu_opts_set(net, NULL, "type", "user", &error_abort); #endif } diff --git a/net/queue.c b/net/queue.c index f948318..ebbe2bb 100644 --- a/net/queue.c +++ b/net/queue.c @@ -62,7 +62,7 @@ NetQueue *qemu_new_net_queue(void *opaque) { NetQueue *queue; - queue = g_malloc0(sizeof(NetQueue)); + queue = g_new0(NetQueue, 1); queue->opaque = opaque; queue->nq_maxlen = 10000; diff --git a/net/slirp.c b/net/slirp.c index 377d7ef..9bbed74 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -299,7 +299,7 @@ static SlirpState *slirp_lookup(Monitor *mon, const char *vlan, } } -void net_slirp_hostfwd_remove(Monitor *mon, const QDict *qdict) +void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict) { struct in_addr host_addr = { .s_addr = INADDR_ANY }; int host_port; @@ -420,7 +420,7 @@ static int slirp_hostfwd(SlirpState *s, const char *redir_str, return -1; } -void net_slirp_hostfwd_add(Monitor *mon, const QDict *qdict) +void hmp_hostfwd_add(Monitor *mon, const QDict *qdict) { const char *redir_str; SlirpState *s; @@ -652,7 +652,7 @@ static int slirp_guestfwd(SlirpState *s, const char *config_str, return -1; } } else { - fwd = g_malloc(sizeof(struct GuestFwd)); + fwd = g_new(struct GuestFwd, 1); fwd->hd = qemu_chr_new(buf, p, NULL); if (!fwd->hd) { error_report("could not open guest forwarding device '%s'", buf); @@ -681,7 +681,7 @@ static int slirp_guestfwd(SlirpState *s, const char *config_str, return -1; } -void do_info_usernet(Monitor *mon, const QDict *qdict) +void hmp_info_usernet(Monitor *mon, const QDict *qdict) { SlirpState *s; diff --git a/net/socket.c b/net/socket.c index 68a93cd..c30e03f 100644 --- a/net/socket.c +++ b/net/socket.c @@ -695,6 +695,7 @@ static int net_socket_udp_init(NetClientState *peer, int net_init_socket(const NetClientOptions *opts, const char *name, NetClientState *peer) { + Error *err = NULL; const NetdevSocketOptions *sock; assert(opts->kind == NET_CLIENT_OPTIONS_KIND_SOCKET); @@ -715,8 +716,9 @@ int net_init_socket(const NetClientOptions *opts, const char *name, if (sock->has_fd) { int fd; - fd = monitor_handle_fd_param(cur_mon, sock->fd); + fd = monitor_fd_param(cur_mon, sock->fd, &err); if (fd == -1) { + error_report_err(err); return -1; } qemu_set_nonblock(fd); diff --git a/net/tap.c b/net/tap.c index bde6b58..968df46 100644 --- a/net/tap.c +++ b/net/tap.c @@ -189,6 +189,7 @@ static void tap_send(void *opaque) { TAPState *s = opaque; int size; + int packets = 0; while (qemu_can_send_packet(&s->nc)) { uint8_t *buf = s->buf; @@ -210,6 +211,17 @@ static void tap_send(void *opaque) } else if (size < 0) { break; } + + /* + * When the host keeps receiving more packets while tap_send() is + * running we can hog the QEMU global mutex. Limit the number of + * packets that are processed per tap_send() callback to prevent + * stalling the guest. + */ + packets++; + if (packets >= 50) { + break; + } } } @@ -593,6 +605,7 @@ static int net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, const char *downscript, const char *vhostfdname, int vnet_hdr, int fd) { + Error *err = NULL; TAPState *s; int vhostfd; @@ -631,8 +644,9 @@ static int net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, options.force = tap->has_vhostforce && tap->vhostforce; if (tap->has_vhostfd || tap->has_vhostfds) { - vhostfd = monitor_handle_fd_param(cur_mon, vhostfdname); + vhostfd = monitor_fd_param(cur_mon, vhostfdname, &err); if (vhostfd == -1) { + error_report_err(err); return -1; } } else { @@ -692,6 +706,7 @@ int net_init_tap(const NetClientOptions *opts, const char *name, /* for the no-fd, no-helper case */ const char *script = NULL; /* suppress wrong "uninit'd use" gcc warning */ const char *downscript = NULL; + Error *err = NULL; const char *vhostfdname; char ifname[128]; @@ -717,8 +732,9 @@ int net_init_tap(const NetClientOptions *opts, const char *name, return -1; } - fd = monitor_handle_fd_param(cur_mon, tap->fd); + fd = monitor_fd_param(cur_mon, tap->fd, &err); if (fd == -1) { + error_report_err(err); return -1; } @@ -756,8 +772,9 @@ int net_init_tap(const NetClientOptions *opts, const char *name, } for (i = 0; i < nfds; i++) { - fd = monitor_handle_fd_param(cur_mon, fds[i]); + fd = monitor_fd_param(cur_mon, fds[i], &err); if (fd == -1) { + error_report_err(err); return -1; } diff --git a/net/vhost-user.c b/net/vhost-user.c index 24e050c..1d86a2b 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -18,7 +18,6 @@ typedef struct VhostUserState { NetClientState nc; CharDriverState *chr; - bool vhostforce; VHostNetState *vhost_net; } VhostUserState; @@ -51,7 +50,7 @@ static int vhost_user_start(VhostUserState *s) options.backend_type = VHOST_BACKEND_TYPE_USER; options.net_backend = &s->nc; options.opaque = s->chr; - options.force = s->vhostforce; + options.force = true; s->vhost_net = vhost_net_init(&options); @@ -122,19 +121,18 @@ static void net_vhost_user_event(void *opaque, int event) case CHR_EVENT_OPENED: vhost_user_start(s); net_vhost_link_down(s, false); - error_report("chardev \"%s\" went up\n", s->chr->label); + error_report("chardev \"%s\" went up", s->chr->label); break; case CHR_EVENT_CLOSED: net_vhost_link_down(s, true); vhost_user_stop(s); - error_report("chardev \"%s\" went down\n", s->chr->label); + error_report("chardev \"%s\" went down", s->chr->label); break; } } static int net_vhost_user_init(NetClientState *peer, const char *device, - const char *name, CharDriverState *chr, - bool vhostforce) + const char *name, CharDriverState *chr) { NetClientState *nc; VhostUserState *s; @@ -149,7 +147,6 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, /* We don't provide a receive callback */ s->nc.receive_disabled = 1; s->chr = chr; - s->vhostforce = vhostforce; qemu_chr_add_handlers(s->chr, NULL, NULL, net_vhost_user_event, s); @@ -230,7 +227,6 @@ int net_init_vhost_user(const NetClientOptions *opts, const char *name, { const NetdevVhostUserOptions *vhost_user_opts; CharDriverState *chr; - bool vhostforce; assert(opts->kind == NET_CLIENT_OPTIONS_KIND_VHOST_USER); vhost_user_opts = opts->vhost_user; @@ -247,12 +243,6 @@ int net_init_vhost_user(const NetClientOptions *opts, const char *name, return -1; } - /* vhostforce for non-MSIX */ - if (vhost_user_opts->has_vhostforce) { - vhostforce = vhost_user_opts->vhostforce; - } else { - vhostforce = false; - } - return net_vhost_user_init(peer, "vhost_user", name, chr, vhostforce); + return net_vhost_user_init(peer, "vhost_user", name, chr); } diff --git a/numa.c b/numa.c index afd2866..c975fb2 100644 --- a/numa.c +++ b/numa.c @@ -22,7 +22,7 @@ * THE SOFTWARE. */ -#include "sysemu/sysemu.h" +#include "sysemu/numa.h" #include "exec/cpu-common.h" #include "qemu/bitmap.h" #include "qom/cpu.h" @@ -36,6 +36,8 @@ #include "sysemu/hostmem.h" #include "qmp-commands.h" #include "hw/mem/pc-dimm.h" +#include "qemu/option.h" +#include "qemu/config-file.h" QemuOptsList qemu_numa_opts = { .name = "numa", @@ -45,6 +47,11 @@ QemuOptsList qemu_numa_opts = { }; static int have_memdevs = -1; +static int max_numa_nodeid; /* Highest specified NUMA node ID, plus one. + * For all nodes, nodeid < max_numa_nodeid + */ +int nb_numa_nodes; +NodeInfo numa_info[MAX_NODES]; static void numa_node_parse(NumaNodeOptions *node, QemuOpts *opts, Error **errp) { @@ -59,7 +66,7 @@ static void numa_node_parse(NumaNodeOptions *node, QemuOpts *opts, Error **errp) if (nodenr >= MAX_NODES) { error_setg(errp, "Max number of NUMA nodes reached: %" - PRIu16 "\n", nodenr); + PRIu16 "", nodenr); return; } @@ -69,16 +76,18 @@ static void numa_node_parse(NumaNodeOptions *node, QemuOpts *opts, Error **errp) } for (cpus = node->cpus; cpus; cpus = cpus->next) { - if (cpus->value > MAX_CPUMASK_BITS) { - error_setg(errp, "CPU number %" PRIu16 " is bigger than %d", - cpus->value, MAX_CPUMASK_BITS); + if (cpus->value >= max_cpus) { + error_setg(errp, + "CPU index (%" PRIu16 ")" + " should be smaller than maxcpus (%d)", + cpus->value, max_cpus); return; } bitmap_set(numa_info[nodenr].node_cpu, cpus->value, 1); } if (node->has_mem && node->has_memdev) { - error_setg(errp, "qemu: cannot specify both mem= and memdev=\n"); + error_setg(errp, "qemu: cannot specify both mem= and memdev="); return; } @@ -87,7 +96,7 @@ static void numa_node_parse(NumaNodeOptions *node, QemuOpts *opts, Error **errp) } if (node->has_memdev != have_memdevs) { error_setg(errp, "qemu: memdev option must be specified for either " - "all or no nodes\n"); + "all or no nodes"); return; } @@ -116,7 +125,7 @@ static void numa_node_parse(NumaNodeOptions *node, QemuOpts *opts, Error **errp) max_numa_nodeid = MAX(max_numa_nodeid, nodenr + 1); } -int numa_init_func(QemuOpts *opts, void *opaque) +static int parse_numa(QemuOpts *opts, void *opaque) { NumaOptions *object = NULL; Error *err = NULL; @@ -146,8 +155,7 @@ int numa_init_func(QemuOpts *opts, void *opaque) return 0; error: - qerror_report_err(err); - error_free(err); + error_report_err(err); if (object) { QapiDeallocVisitor *dv = qapi_dealloc_visitor_new(); @@ -159,9 +167,59 @@ error: return -1; } -void set_numa_nodes(void) +static char *enumerate_cpus(unsigned long *cpus, int max_cpus) +{ + int cpu; + bool first = true; + GString *s = g_string_new(NULL); + + for (cpu = find_first_bit(cpus, max_cpus); + cpu < max_cpus; + cpu = find_next_bit(cpus, max_cpus, cpu + 1)) { + g_string_append_printf(s, "%s%d", first ? "" : " ", cpu); + first = false; + } + return g_string_free(s, FALSE); +} + +static void validate_numa_cpus(void) { int i; + DECLARE_BITMAP(seen_cpus, MAX_CPUMASK_BITS); + + bitmap_zero(seen_cpus, MAX_CPUMASK_BITS); + for (i = 0; i < nb_numa_nodes; i++) { + if (bitmap_intersects(seen_cpus, numa_info[i].node_cpu, + MAX_CPUMASK_BITS)) { + bitmap_and(seen_cpus, seen_cpus, + numa_info[i].node_cpu, MAX_CPUMASK_BITS); + error_report("CPU(s) present in multiple NUMA nodes: %s", + enumerate_cpus(seen_cpus, max_cpus));; + exit(EXIT_FAILURE); + } + bitmap_or(seen_cpus, seen_cpus, + numa_info[i].node_cpu, MAX_CPUMASK_BITS); + } + + if (!bitmap_full(seen_cpus, max_cpus)) { + char *msg; + bitmap_complement(seen_cpus, seen_cpus, max_cpus); + msg = enumerate_cpus(seen_cpus, max_cpus); + error_report("warning: CPU(s) not present in any NUMA nodes: %s", msg); + error_report("warning: All CPU(s) up to maxcpus should be described " + "in NUMA config"); + g_free(msg); + } +} + +void parse_numa_opts(MachineClass *mc) +{ + int i; + + if (qemu_opts_foreach(qemu_find_opts("numa"), parse_numa, + NULL, 1) != 0) { + exit(1); + } assert(max_numa_nodeid <= MAX_NODES); @@ -222,19 +280,29 @@ void set_numa_nodes(void) break; } } - /* assigning the VCPUs round-robin is easier to implement, guest OSes - * must cope with this anyway, because there are BIOSes out there in - * real machines which also use this scheme. + /* Historically VCPUs were assigned in round-robin order to NUMA + * nodes. However it causes issues with guest not handling it nice + * in case where cores/threads from a multicore CPU appear on + * different nodes. So allow boards to override default distribution + * rule grouping VCPUs by socket so that VCPUs from the same socket + * would be on the same node. */ if (i == nb_numa_nodes) { for (i = 0; i < max_cpus; i++) { - set_bit(i, numa_info[i % nb_numa_nodes].node_cpu); + unsigned node_id = i % nb_numa_nodes; + if (mc->cpu_index_to_socket_id) { + node_id = mc->cpu_index_to_socket_id(i) % nb_numa_nodes; + } + + set_bit(i, numa_info[node_id].node_cpu); } } + + validate_numa_cpus(); } } -void set_numa_modes(void) +void numa_post_machine_init(void) { CPUState *cpu; int i; @@ -262,8 +330,7 @@ static void allocate_system_memory_nonnuma(MemoryRegion *mr, Object *owner, * regular RAM allocation. */ if (err) { - qerror_report_err(err); - error_free(err); + error_report_err(err); memory_region_init_ram(mr, owner, name, ram_size, &error_abort); } #else @@ -298,7 +365,7 @@ void memory_region_allocate_system_memory(MemoryRegion *mr, Object *owner, } MemoryRegion *seg = host_memory_backend_get_memory(backend, &local_err); if (local_err) { - qerror_report_err(local_err); + error_report_err(local_err); exit(1); } diff --git a/os-posix.c b/os-posix.c index ba091f1..e4da406 100644 --- a/os-posix.c +++ b/os-posix.c @@ -39,6 +39,7 @@ #include "sysemu/sysemu.h" #include "net/slirp.h" #include "qemu-options.h" +#include "qemu/rcu.h" #ifdef CONFIG_LINUX #include @@ -247,6 +248,7 @@ void os_daemonize(void) signal(SIGTSTP, SIG_IGN); signal(SIGTTOU, SIG_IGN); signal(SIGTTIN, SIG_IGN); + rcu_after_fork(); } } diff --git a/page_cache.c b/page_cache.c index 89bb1ec..cf8878d 100644 --- a/page_cache.c +++ b/page_cache.c @@ -33,6 +33,9 @@ do { } while (0) #endif +/* the page in cache will not be replaced in two cycles */ +#define CACHED_PAGE_LIFETIME 2 + typedef struct CacheItem CacheItem; struct CacheItem { @@ -122,18 +125,6 @@ static size_t cache_get_cache_pos(const PageCache *cache, return pos; } -bool cache_is_cached(const PageCache *cache, uint64_t addr) -{ - size_t pos; - - g_assert(cache); - g_assert(cache->page_cache); - - pos = cache_get_cache_pos(cache, addr); - - return (cache->page_cache[pos].it_addr == addr); -} - static CacheItem *cache_get_by_addr(const PageCache *cache, uint64_t addr) { size_t pos; @@ -151,17 +142,35 @@ uint8_t *get_cached_data(const PageCache *cache, uint64_t addr) return cache_get_by_addr(cache, addr)->it_data; } -int cache_insert(PageCache *cache, uint64_t addr, const uint8_t *pdata) +bool cache_is_cached(const PageCache *cache, uint64_t addr, + uint64_t current_age) { + CacheItem *it; - CacheItem *it = NULL; + it = cache_get_by_addr(cache, addr); - g_assert(cache); - g_assert(cache->page_cache); + if (it->it_addr == addr) { + /* update the it_age when the cache hit */ + it->it_age = current_age; + return true; + } + return false; +} + +int cache_insert(PageCache *cache, uint64_t addr, const uint8_t *pdata, + uint64_t current_age) +{ + + CacheItem *it; /* actual update of entry */ it = cache_get_by_addr(cache, addr); + if (it->it_data && it->it_addr != addr && + it->it_age + CACHED_PAGE_LIFETIME > current_age) { + /* the cache page is fresh, don't replace it */ + return -1; + } /* allocate page */ if (!it->it_data) { it->it_data = g_try_malloc(cache->page_size); @@ -174,7 +183,7 @@ int cache_insert(PageCache *cache, uint64_t addr, const uint8_t *pdata) memcpy(it->it_data, pdata, cache->page_size); - it->it_age = ++cache->max_item_age; + it->it_age = current_age; it->it_addr = addr; return 0; diff --git a/pc-bios/README b/pc-bios/README index edfadd7..63e7254 100644 --- a/pc-bios/README +++ b/pc-bios/README @@ -17,7 +17,7 @@ - SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware implementation for certain IBM POWER hardware. The sources are at https://github.com/aik/SLOF, and the image currently in qemu is - built from git tag qemu-slof-20140630. + built from git tag qemu-slof-20150313. - sgabios (the Serial Graphics Adapter option ROM) provides a means for legacy x86 software to communicate with an attached serial console as diff --git a/pc-bios/bios-256k.bin b/pc-bios/bios-256k.bin index fab9da2b3f1ed9209d9245138e5521363313b005..c6e25acc90092182dd7a47280a7387240283aaae 100644 GIT binary patch delta 63243 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 SSUP4e%$hqdYw==C^!^3XQq+I| delta 60103 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 diff --git a/pc-bios/bios.bin b/pc-bios/bios.bin index 8c718e1b92ae8df3e1e8f90dfe848da786c3d79c..46ca37b94cde5ad42842129b1cc55fcd79de4c72 100644 GIT binary patch delta 54945 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 diff --git a/pc-bios/efi-e1000.rom b/pc-bios/efi-e1000.rom index 776e217a6112e583a76df37b5692d8a47fbf8031..4e29d9d1adba205aaa8759136c0697035aba7870 100644 GIT binary patch literal 197120 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

    iCr5%w>vX3Z8$z)y#1QBa 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

    i8>-=r@|enkY}H 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

    CU=fM3W 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'_hv9F<^es8JR>pf4BM%=V z&x&b3IX@HSS(iDvqy(v~8kOpdWK?U1?z2Z!M_B47-Dx*w=H!NAnZE7!!k={ZT%qom zPtGMsO(jTmb`e4C&SIVsb+^QiCZglSR;$^h-Q_Y5b?N6nBB>gAT#`oyZQtiv@u~Z3 z&29aVm1nLN&s{9|mBag&E{^jlsaTeYF3BF|vJrd+d5g9`i$Qy&48V|n@Lg|}SvXSn zcJp(*<>3F+G*3Ul8sli;L*}Qt?Cxh`Lw9qpf?=D9KjB@H7G1S}Ygp=d$>aT&6Glp5 zFcuL~*^4W7(9h3n=}6X+MtY|I?N9Tt(X}PUL0t2OTvM>0#ir0lY0Ms1nbv7K6b_3I zt+WwS!gStw_`P;TK*ypm?~kGZp9DS><#H4i#Kt4F-lf zlhTcQ*tRpS9uTcD5Ys@$phY6=vG4QTj1tc|`X_e58wzwAa_5J-4Zl>C>i=GS1vaOPRm5TDeO$=$BM z3QK%pYOl1O^+`*<3+atgGSgYS?=^dIQLvC8k^Vcm;Ia0)e)!Oh^DoZGgoOCNfByq8 C>9BtQ diff --git a/pc-bios/efi-ne2k_pci.rom b/pc-bios/efi-ne2k_pci.rom index 9dd6d91e311786e1cb3933e68c1faf873480054a..6366017009892208117b3bfce95d41b21b9c4636 100644 GIT binary patch delta 194592 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 delta 192815 zcmYIvcT`i)6K{G#5|RJ`B1MXTAYJKAnt&oo2Z>ZQM!J#!(n1U%A;gCDYlAQ9*9L-8 zgLDNHQ9y)1s202-R7F5q9=~_aJMaE+@9t;j&Y8WtGiP>YZ{`Xna~^~AdzvS_4|;YP z=_q|4v~`7n;z1A)Xz_pTW0%SLp2;8(7;+BQq1T~z2kGgw-}{tj;6ChGIOvygEx4>0seg~ijyF92QxBY)i z3((vZfCwuy06L(#d;kn9z<_lT*P;0U&{1FmHb(6DAJ^G}1hAgI_Wzo$P)ZmDUxkB! zptxWts~O3fSfMmez$tYgLMf?U1Od816t>xsf)8L#2JjCEL}50IRsQ#w0F8^q6n1B` z2uophf>gJ-AyDAl|F}b;lyGB#@zVd5+dLuh9Mt-CO?x{6^nZ1}2HDc49rFK(7n+Qh zjKv1OA1omPph&t=gUSE&x(I2A2LWtIgC`VVLFyo4paZOYXN%AUY4C*try+OgNQ&_c z4)6t+p|@ZMNC5}pf%*leu;&31q+lFk4OT5sgnHXm{7+mW_;72n64yp(v~_F%GZ-8BmJKpp@(;8r(o^P*(5^YGNq?Zcu^&P%wph(0iY6 zeh>SrvEahz*Pt}O07BuI9f49fcA-!p9MZ4{wk323v_>S0JHnM_8iP=YC; zl#tGXOqeS2dDfP00Keb`OvkEJ9N6la^9J}XF0%rP#JS^502CMyZ_WSJt&Ro~==&do zSD7S`B`#B~Jq!ZU#9!C8y7U4Z{G{|Oe3gO+`-AYRO;|8qy=m%m!Fjlud0_l;niNG6c{BIm(+KPg*yxUQB#buO_jB^F2ZUnjN-Ruqk2Xrx48m^ z%W1awHk&xMNki6xK@}2MV?hN@jS{RacPD5>%vdv&gI(ZXXu2?d0l3i3?(>QSb=qGL zk5!7B5dmxefYLx9U44ot#fQRPsUv}aV<2%{oS;q>9xTtcI|y4IqZl{kcNi2~L9Q_b zjT@Baaaf&&f+Of8g{x$tvfZ;Y{AaHc{})2ew|I0mwptp>M8F83FzTsya2#+~JUb(h z2s}(ibh2F!br5o0VYw%f%l(^i5>13w;Qi*AO@pHS&naTFv*M*hGnnT=Rx^PzlmBJw ztM+ZguwsINQzk@REj(CmO?-k;ayi?T)%G8JS8Om}U3S_Wj5h16sIb{Qp4Wu?x zsJoBIQ3OlkP{nnj8q!S`+}iASlj-sm$+^A7)IP5$^!nw+tF|q35=l?LmQIw0O(t=-|#SQlaOC zW&jasfA08l^^DjOAp@dsGho@RB9Kz@@Dvcd3!)rM#l##hCJ_VG1G>azwZH0zsJFNI zD<(Z>_%4kF@(+9$F@2#vCpkwoq%X807!!i!Gz|g?x(w3&LKZxxM2fn@b6V<*ZVHn*1ym@ zp~sNMzixjEI*L?CfEd3+1zVH(#uT^sX4wkH9am4r+Rr6#Ea>lpj^4!0c*GsA?N^a^ zF1m=5#9~BdhFF%4SaMm?@;1$ z3d6H0p&)bJ7FN*h;vo-SDg=`F9{S9A>uCTZLZO_@R^S7axk^XG{CK7sN%Z;9y zcinaEAj84KhlcuFWiS=>t#VRb1D3P>2tJP6GAm9I`Xu;<1czh`5(l!EoEtK!;xi7v zwS3?)W7COHKNu_+<$(n*ZX($kx}QGFvf1o4RvUr2Sy%4`%)y#R*dqU@J~Z@(T#Kw?qMK+(v`ZG&h*3)Z$*LxYC!(D~4?K$kzJ3iR^oDNPuT3MrgeO3Gt=qqw@lVE$IjaC4#L%OlPrm_gkUVZJ<5>IU^Fu&9Tt(R=Ms#4m z1<^K?UiEWqY?Qeg_Uo?bxh)t#zRFzBJrEc9-@7ATW@`8=E-1JDjiuc=luKZ>t%xHK z!;;~b4&p4KPzNyxgw!E-U{X8ukav)v>Knu8kRQ5Wh{Y~w8D4P^1Uih_g7Eu!CUe;evA9X@quNlDIm#KpP*Rr?DLmR!%;Lps@ATZ@x##c1&*GR@M2y z{`vdCMYs_Lu9v!2UGFhti{&;h7CgfnxoAV2wB4<-a~Ufwo?>U`z*=YBNoumZ=z5q~ z99z9c)6l5Zkf!Dn8i@9y%<$IMtpG_d6DQsZlH_~=s2GH=Y69>b1!@EhNN+)bD?tO> zXPEg7LW4T=2>ZJWk_cJg4+ZOl5JvV`_3gxHUagEUCKu+%{y~+Z@&qGwTo}8VS4jfM z=V9+!6#{0CH~#%h8pyP5DFDaNX@Rq|v(?DI1X^^N+EJj5XwwQbB9=Za_3H2u;KW63 zNv;>#xar1X>hi1?1}a$wnSJzrwhitM=D|}FF5Z|2uJ)hJZlaT72N*Ls$0N!JpCuiH zN9E_+Hzk#(jo&>6_std6i(iF&^cc42ID;ze!Vu?6qN-WA;@oCiwAFS> zU0TkWT;)8AW=9FhF)khs%5KWqWVhZfyxvf&0@eUKWVO<;<&J*$6B~yMEjC2b9N9OD zy0V8kFmYr30))MfF;7VUwS)lEB)G0k6x&jGB7yqne0AN;$G|7$LxIBo+MQsmIq_IZ z-IWmRp#ir475Z@VBub}^V!51XE$FG5%}k%0`Uq*V$MvqCh*`lukOjA}`9QoJp$a~~ z86&LiLFx5_KkF#KiOX0-3lqcJ-=YJ7tJW#mDPr9cN+|4wdjDeNR9C# zh#$Lydt$L|sjPl~P4h)lL&RvS^~Vb{W&1NAx9N~Fx0CZ23hcX25!(+aQ&myUnW;pL z&tECroDG^bL){u9fM;`1vom3QWw$qvbibF0QQY>#I=pLH_%V*R0C7{}7LyK0GdO^n_PAIHQjdz75cUY;&T)rehwKb2A> z55y3|G>vWTylv(8b)6rJ)tk10(If+?Wv>*Kf^ySK=3QB0RB@9h)_I5Cx`QmM2_^ix zdCX7Iri#_;F3oXt8%dzF!&vchLujjAUl_)1F~^G6Tu7kCCJ9PE%_&XGc*t$89o|7I zQ?VG|mm3qUBuF8eemuXlyG%@2qY>1qD5t9I5g$~$v@Cib<;=*x#81zD&ywh5zxNq_ z(Q+OebqbgD(+gZ<6ceqffpWOp42~@k+}fj2i=*-O!D>wA@+z(6fWg(bGRKtIjFuW< zU`NI_z>lbN-%xqy$&b!&@Tw`t&s)4m`T;ox82$B7U z`+D{f<}82{v?DrlYKZ0YD2$fPtPjVbDsX(Mq|6%}xWBh~Rpnsw*Y45k&|&^^ck>E{ zFVRw`-!(FXGqTO7j%a((*Lma8>UYZ?i6UQac<^EmeTy?e>Ncwd$B5r*e+`1Fr0Lj{ca%4-! z{EFc_fbLN>cQ@RcTZ$tGd7)M_lGJ#5`6T8l+(j@XNRX0;Sau8|91jcxIYqJkex8${ zgpRk2XgSp-cjn1tp@>c^Il*IU7A-WX&KFWIk3zwsNnJ*zi%1a%)lPJyOR7jLmnlxH zzGYfi7bFdv2Sp#+2U};4)#mrCdq4T23x1*KWW#J(sH~e)v2wDnbzhP3&hxRasA?+N z(;SV=Z?Qiip-moMeY6I$>`p}6F*4AjY+O~MZo89e=4i`UV@zYYkH)Q355K20$|%aq z?f4m=CPru>lm3sB6;veG4h7APR$bWRdsRa3prfDw`L?!W!(N$Z}f;jlh zeO)_kRn|o(?fTlvl1h;I!P5{3oa2<*)l-|6>CDHs#g5m*n_sV4TA+#C_r4{zL;WpAn3P9@As11G=37 z2qRskbH<=6XeY{eMpx;{hs>oBMP1C1jJ+2EPqV(Q7msZ=4z;6YjRn~=G%L;_4T>rk zrpvL-m!*n@7h9&tZH7*BB#z-OGpk^S6#yzG$|v1>pp9vWi9ca(dPx%Mz! zcbTs#w&Pl8T>pnEOKp-$qipBETfO6NCUdAtF~P6GHk<$Z`M{u_t8hIl=MQb|lVk^dq3?2OtHX@>M zE@s zsABo68AhtJ!sSa%Y6*vTI7j{qM5*A7}+rW^e0Ov=?=$f_y;X$CG_Q6AM>qzksP_fmo{CrwB<<}6+c`# z%-ViYMO>7dn4B}zwXtHn#1v^i1>D&sVM&=}FzS_RXBZYzLywvA? zsHQeX4+=HXob!bxxIb(I|KkOX+LLm_V0lDree#TS62h7hwQu!{@H77-C@LUT5)sHt zm{v{W_^5IYe$K_>p}wXyQf4wn-831eD%Tao83lQw8`!cZ5i&!Bs*jIGLc6B`FU87) zq)(>A08^3?;1o5|LnV<|hX&@yO#BOzlfIIOXwCsuT|I%oeKPx9#=&lmcmLFQYUQkH zw{qYq23Ggk`AydBb+7YHM}=jacnxM-wcCa?4jQjogfX<>KK?SYfkhb4SV#XpkOV7s z*1>r08vZV_Q(4<+Zf@>^)h<-8;pI6qKf~<9%;`u|`Qpf$o+^7?d;90$f!oe;uRq0J z;Mk)om^K>a))cY0yks}(DfP-md;BSOb#V{FT2BLLI;Pg%!uJ?>ITh~5eQi4&(BlyrZ5arSr!|c6ZO4H{YU27C!KPC!5cNv+?=u8E$ZThsP2N4Cm!@jD)(pO&2 zn#GU0?JcG}J|LSYu^{+Q5Ts3j#h^_5m^`e<0R5OELE2s_0qLC7%&InW^i+i>GFnh` zxe03rf!pY3gdU~8Z$?yAm8iQ6FU;43Mbx9C^%j%1Y0F#CU@0i>hl0g8c=e*&S%ot2 zUW}7^qUJ0B=*(Rc4+IaQM4JOMOJE?hb+-v8vDPH?vmvi{{Cixj3~&8o14y`XFkv@)@h}_ZXAiI zE|+*3qWY2(U^kVlz&X7Cer&j=`teZJTu0sc2QTS8pR?Nq;low9<4^`@Ufc8H=caG@ zE;e>g>OPJhs`7@rL!ILKnVZU)>%H7KOBYpo^bt<1crL+DX9JR?nfMNs$bxgT44d>_ zPO935!F)<$<}N%0{}ny~`cOwMy5EZl<2@lFlR(o_;FVaJIH4->70yuffEd&Gt(tuT zoT@4$cUg|oFr37h97}=NBM@Ea>-|7V#aj*x6#=%7pTBpuu79;uFBz2W5@2m51 z!}9kTNpOQ-Ltw%ea>y%wW{#m*dlA22qUB!E#S7#Mmd%9Rp}N(G2}GO|$CdDg{VPgw z`UO3PZOM1%hZ%vJ*+>OqgBJ_I4xVOS6n-AemfXExrjDVg8{CQ8?9XWRrkaQ1Z|$3Q z^8`4eWA&_<5`qgDzKa0XMzm(mANV$N=CwBl2TN)lHNrS-ho7 z&4Gt%HI3I8q5uT?`ZcBwh<&-UhW}yD4ySBPWN#B((Ev(jC zYpwJm#)$@PSgjcdjBQOBlgpCw)==d%xn~#DtPxIKI|v7kh>wjI*0IC)EN6R4u5^0` z@D0>zHXgQKmgPQZI(EL?!1=Ki5F0(`>U3vua33=y`=~N#Zh53K8V+vLtqhdB8%Cxz zD)B17kI54(J4BdU?-GENJZWsG6dniYhu^_l);a3~~CH7S|cZ|cOLQk9gD&wdu zucePs5LBvjc0RP8#;(8DQ`(&+I&4)N*lpW>Zb7C!(*xShsxQwgTD~dM*-U+-P`lw2 zs)tElo-l5@gX-40>88oeM|oC-j-=np4a15}qxT>~V37%B6IHzgDMVIg`er%=EMRc2 z5|r5i_HgOI+_wjsw!3v`W8a+)T(K%#6U2B8^4S=!leUOZ!#W~P-5`fLzWJM=!HzJiRA$V|FJ*goQB;sMo69OK=D2S{8}Kd85e=Q+LY-|c z$s~4~^lfFtE0sfSDu!H^5#&^MJoVHoGiy{5VY!xcm%SjrV`V=0b(V4ZJf2l5Ax34W zGzLjOJPjrWRjP0h{2OuBt852|7m5>-h-Kg9-v$(CD{bK9ZT;*cQtb|mmEJ}^5xggf z<)TNTOSI9q2Lms<>}U1x&%3cx=tka`f7Gx1qi<>_(Onlt;?p&>T$5P62DpnGJ5a8y zW)kZqLAd{8{^@8vi0t1x5O6)>#_sexHOq`pb_g|?gU;Y84OREok2@fu+C(e32 zqj1Ws&QCW_z%T=IHSn9i^qarIxR*em?LFk5dugkp<$nEmiMfmb2dPVH4{|?V-*rUPdlnyY&n>!?*x9!{>h$w2x^*;iDlgJfn8)@LhB{AOYK^a4Cs=Z zV^3qCMNbX$r92!cI6&9VJ|uSN z;{>s!Yh~l7aefu0NWG$%a9zn$XU|KFsX ziSJ@D<3z3%Gehw07``!_QHP;g=e2ZPJfS@q)H|Mi1GtUJ>1Mva#bqpy5}bx|y6^Ar zY!*UX4HtJ-{(i{%CLH|zz^`kGTutJh{$yhW?%yD+TW*ZStac~CgO)BA zv;*6)uT80j>-XLJ&k)MILt!8Wt>C#AmanA$zzGx)tv7VqM z{d+JJwFqqbUk%|AM9{BJ9FK8)@%xw>agO#|6-F;7c>P`LqBm%`T5c7wjWg=S_)Ku5 z<8{LVziM5FihFONCHiQ6R~I`eNEz z+Piy1_21!!_58q{G?RIw-4;O-qyD#j;*HL99c{xgV)Rd@o% z@J&)-79@A3v=tcDchQlnxwydy>5&^6@8ZVWO`dh^rb-)D_OXibMV=?)o)qwNb-(zi zugZ)Cr5DlqKk1C^u&mNq(V2I6Ccl}yo2zHdZk~bBaL3~wV@fd5k5ND@+q!v<@f9%x zqhp@NIbp2{I;KRFEn6~MB%`^N0W9+9-AgYpo?!z)9It5tjvP_PNaLnl{zIotVmPf z24yerH&=bfW4r*WHs3v2HK-b5lb%HStFu-&l6LlFn^j4#`>sE*Y@dwS)AzkOUuVqn zoVOCET4o9$91ub5mSt{-9T(pG&?~%*I9MAO5MTrR+|hF6pPF*wj=dDJh=ZO^EWZ0e zCW#Hf9)R(r9}Xn4-C*D|SCgyaFpDx`r%N1ZYd35HW8D7L!MGdVL{vn#u<70Wo9tee z#kIn&c6;lpWa}YQxvXBRM@^Zl%ZtQp&8~#f+gxd9R)f8ZAP-@)ej#hdmz8Tm2BZbD0hGK4)Ni;^_xDyDe8Sp=$28 zSrhm?w$&|daz$*sYHoiO%%O-Cs@x;d%%Fd@G`AePzFE~}-J6bZZ-c6KvXB0QB#z{PY>&}A+=aWE>tw@ zwd5QCnW|c=j{PquPhFn%J}}ou{HV>oBu=w|djzz0YA@_KJ$YH!Fv1Psum{pc^4?+F z_om!XKz^p1E%PN;>+ndsowaC*Tg!i0Ek?OLPpy|dWA33cC*Fi%WRIRx{;7Xs-lZa_ z&3cALBu5O?@6{LKN6b9UMD>h4d{^qf=VqVj{Ic12q#3F2^uV5SV$US?jcjiBCj~Wy zPwB^4Z!^BR2oIxW_<~EBIc64n+u9D7wK`W{UEiLFviFF_`*LEG9>MW}+I@IIx_=9; z52Q3d)CQ9~$Cs=5UI?;%LiC&`fZF@}&l>G(DM@KwGPHf;`FZ(s^O_1nEpSNPb!inM zEWj9i)}$upY^GvVdYl84cE==Ko*8&~a#yW!>(I{gzk@dXqhE-y{XKq*z(=_FvBNvl zCoFZM57oUSQ4>oN8w3W~)mOa{jS9bMhp&`OA)(1n%xk^^wX66jfuwvKUAhjC3S9oZ zkFEcDM{`F|zrRnIuO=ho7j2fT9YBqKJ0|>WaQ2A3QH)lO#Bs~B)*G##`i(l?A~YlL zCjTPVhUd(Kj)*YSD#?{i$1}YSCU}`F0r@r-{G%% zuCyO6%QCt1SaHm#$x+nLe9zt3*&Ho-XDA_8(7#X8Y8dCQ{)-V~x2N`(V^WfCp|GW- z)@V(lWPs`q} zM$B{4c!LELWvjAcO!++g)n;CFz}P=GvxnilKhdR%HTa7s1-3PMd%S(DUHGG8l_`oB zs2XgnOQ7f)hf%&p7;{Bcds+dNvPZ~;x8_QATsES55*~EOp7~EBb>H`@EKET4)5quC zAZAZjxuqEh=GXPT=lU$zC&sz3imKTg7n|Uk>qx0LcmQV2a#T6mjz+tWZ8Q=Y9@7b) zQR(%2I3{-N-R$&XsC8Y8-(%7Xci4Oq3{fi?l!DD;9A?nZBGg$)GbPH3X?E}hDR7(R zr?QkMjrI?5U@HgFVwMG1eC}bkLPU%2e|pnQvi5Pmtfia=_v}OZ7SwfG9+KyasiGPo zNs{#O&_6L`e?Vt`@;mTcCYHXumEAwGm!=1!(&Vh@dz7QLsxbXWjtp-y2-c}WOSdrH zMTRJrV6MhsmuwpezL)x<*K#%CZj}5`RrdQzOv~x7^RFC5>}p5Ww4dS?RFTrkbYN4N z*eljL5hU7`+V>PLm1|**AvO46EnCi@Uq-kWC_>Pkj=w6TpndUdjkBI7j`8QT6q+p#@WV}*B0G&m)+gC*i zuLlWBEKYfrUt ztd1p3{tuFz%rKqyoxe$i#-LOAV*dmE&4+0_4v2p0tb`>N-TtqR1q7h%%`KCll0E!$ zSA&MXp!nnLhFBaka*7VSWNs@aCH-M-LJ=7EO7gc~tuE-Qt|#2W zn6bVC;FFnF33mW2?qhc$>`jokVK6bXIQT?HxAm`S z`xoHq9uDSTu}ss#!|FUu+39V-wm)!k{<5OGBbQ17&uN)Gc;2YV=V42t0)M?M#H3=~ zlO$y9-Z~Kws>Dk}-_JCAu{Ia?X!56=24Dv{Miq1+J2OCmSUfd%(5M}xhy5G{GH;5 z{E(M_?1U}RK(h}VNJE*`xz@D98*xiC?4r>E75u&Bh3N6jXLU(uI7DtSxVoEUUjXUS zlV&V-*kwv$&yHb~*>ZM@Rtc-pw=h^PgiU%mu>m3S?t(UcBkRZx1x#Qoe!8MMIe>0v z?StXYCC*~ZZqYIxT@Jo24-Tq8*rIB-Geu*Oy8C@NB+FL1>{-Y%zqCVvmmE$O{u6QZ zH`x52y1p-q*t7A>9EqBu{%LY;q<#}4`acV1h;~JAz7*RuB^@jLayi?FnKt2>%xo~Y zm+Yao|5({SyVfh8BzOVoxOP?sFYx(Ma}5p@GstKty=Dz4W+b4lYpoobZN9~@eUWI- zvR@xonX%UcPU1@Ie%-M1XHl`=zHrYN!5WQ#m11P%9^acNiq5qv%-iDH8*>bO`~8jy zX(sma?akR&UOZkBkC@A`X?Iaj6AdTUngsoA@(pQ-!_jZYKC-GZ+12UQ8PK+&L9w){ z*Hh+D9zwjY$Unfayo2jW41UBkfwTq>!s@>l1W-e}50FD|aU`;NGoy#Rs4wJ12&DBf zm7tbvp+9g>Ucyy}<$Xaeno%0;9>(P!(2WT7ldT9;?;V^C?=;?hP*KU~WlY0V(U5sO z_{%q4`#>Z1UygRo%#4EViCw&^Ow5k-vH5Sv0L?au%#^cGuA>AHUToBM2KN>B{)eX> zcEvl~%l3kc9&g9wJFF6=20^_aW%Mft4z|@{q zl|q)_?KL7}B&@AmY0H}+Yji-2n&tNfUXz(s{KrdJ`AC3gI3xZ}34B_* z%Vr0`sZ=2FFzMFWC`YupiWP$iWAZ_ziXo~(QRMPxy@sytBV)eyk?GSj$D`{|89PW) z>TsBuC_5iyx*P;szO5j!3J_Z+Gt-zBsy2A- z6un2p2&ep_JW}SGCfy`}Y9WYpuxOBqn(+kA^G`Kb=3YERX#PvE8(+DL_L$Lm4;}yu za$6s^<(T<=$K(e3SO^qmFc()u<*cB$;g$%@L&Rx1&%&dT{Goi^%`r53t>g4fQ8j4Y!!+(gH@g6Zd$0(#Tv^Nph z^6o-kFT$WI*ZVDhSjn^G!`&K>>Iy=nO%u+t(G< z))+78vua*gMQUTHt=vr5rN>yN1b)OWI!t!}Lq*oqiMaWVVBN4b0kBiN`P3oI>2v+8 z8)jNIDoCcK3A=Z@e*Z2oSywpvSF5gERase8_oD(Qk&u~GrzfE_t&*U}=F3SfR6r(S*ze#d^s@yU5T2zIz zM;g*PTe9_{n~%3Ywbj~aevgw6=257DF;N}9Pg24KgjmGII^H%|{mdGqBN$mPN};25 zwnd_pTBGv&Ug4z`#lE1E23uvL?FZ`p3*uUA+S~^oBWzVy5gOVD=&ww)_4GtLE%;S0 zO!rz6XdwqxMX&3s?(!sjcStnRU|OxSWl`P{8JnlOG-fVAJ}6X`7zG8~8_>-4n`Zyy zroV9CY0-O@^4F2jK&Pov%os8=PF5BZ|Kejsbs>Z&~shYE3 zLu_t1Ka&3VcO%B|fSafnt8c=&bO)YUsB|?io8?T<) zs_{jUe!^$Gk3a^Y%n#DL*lodlap@MS{g#W}*XZysh1n#J)jQZK%7OrI95MYl2l*&CiVstd%rqx*W(` zSp1Pll-OnPfC!D&lgmF972nqA^I)J z{QHZMr}3^XhlFrH#+6(VG6(aF+u*CRIwt;@;Qv^a^uqa;hTDg*X08>M)9}RQcB)1! z5%9H{P#TC_9V$l>01#636n}XC@Wf#f&<|YNr538O@?a9!3(JdT0M)g-C-f2Dy`M}zgS}Wz!Q&aCfSCr`aV?O_$^-CbUpeZ^kcSkXvc|HFp;}vH1*krJ}4V!=X z`7lE11Ri*B*y-Q34%wV_8IRHkMJmx`w?2LLvi^bfCnKb#7VwLz4VPpcfrt0d3enRgN0a;Pu>bbD;6Y` zr|@I3GxOV6Qe*u{vAyFWYhp~bIZv9w`ikt;>5eOnk2+<=- z_Y5H`nk~&xnU=!qI*l9rZ_VtE(1&<@l?e%I zqu`{oX?o11l{R;K$p!HQI#99qOKsdy>6L8l==!#3tOD$s!X|@#+D2n5sl)i4Ms#N{ zu&2r&Nq0`|3Hr%ixrW3g_En{K3~aX`Xfe<9Ne?XKjM}F@H)%a&IiL6pyeH(}orb{t z6`5W3vSGMFBY~gyWQSCg&FxPpCrTj9LfBeB}Xh&k$fy8HI*N9 z*l6sZ+wauhK<#bh+b#LcNIOR4m6WMeJ?{wTo?9|I~pVvx7*n7 z#=8=j{s+|&%s;R_k)r-knfR9HD$#R};R&eVVW`be^XE+k%T?CbsKPItSz-`oXw*Mb z{uxX>aF)4sa?|G`*`p<9Yd5`qh}Kb~QdpDh|@E1f3

    wJ&vk-dMa>SO>2B=-q$_dgPioqF1s=B!;$SNJ^yI0KJbfA@MU!!WzH{e}s(`xdwKy#i8o(Ge z2F1q!S`bVPMz~$Uvn{cxSSbiqh%~K#=!dBQ3%?ZwlsdORQ4%-ICicZ1wh<0#*=i6$ z4D@3A=zuZ`+#tPmmYVAT&OKjJF=rg(`mcGji(bsg9!o1}$GT++XnX8K-C0+*Ze}WI zyrs^cq2kwV$aM$X&O0qhy$UGc`7X$fBqm|j<{j9Qvb*2WHOh{f@sUnj0@zDL|)Xgf#ebL>)C+|%OWekKcd}Ni@#p$NW&cB?m*u~j1p(& z(*vYf>Db5AineUs;pVk(nAyuz0=6&R(}IyaeyQSlk3y{5`WMGUd8<{i*jK{M5j&dl zYdc?kX#bJNEfTT#>knmq8@r&`&~ZPtA=onJ0&iSet9HL^U)HMoy7vOS?Eq@Y!S{!1 zp|gA}3cIKL0O4`8$v`Oh9lH|~L?q6^4tQTniFzA@;7H#sT?~7|O!5jvEdL2}jAmRc z6UJ3YYb}3mMWuy6iag@ThL)amJcUQGT(=xx2Kkv?Qn~+}FhWGI#wn6HZ<@#d#Cgv{ zHCM!Qc6r;_qq33iamVn@lgJ}O;wIm^r|@mCwrk9|iiaM#7=9o@r&D&J-!DyPA*bsI z+Owq=8@Bl6@ECC86#;t^E$#c1AyrE}B>&(G68r15I?)cv*%OOTrGU(%KaE++aGVaO z>D;TbKOOg5b+8DPa^GxN^)3ii@bl@bjuF2%6wj~-Ui_t)(lyuw{dIPly!NYuAFtn3 zFv(H~8sQUzPdm=x-!qcYk=64Hcf>jdCpD2xwrHD=yhn6-v4dd!?#8SXW1P9FL`TVs z1F^iL3J+WEfD?kkn9|>m@;;CRF&1e*9*sY#q}hD38y!yv>1BH#ft(HAC#?T^n#kMy z9s{_&#nimi+XU{7`^ZYl#7ka`ogeoz)VaU)YI$yhO*@u#p?@2Xb+pEjrICxMZb`=G1R{7`X> zAX(QHD?>RvE4^r+=ZMO6`Xg8>mI5U^t$Pe(`r6KX*hj>#a9CEKT5lJ>tI)!Cn9q;r zkk)!>v&dlpyK(Pp+XeL1Dj(fs`1-P-KBkJ0VbnPHbU+9g6fzFt3eBHZ+y4t!<6M6b zmCBr2`8?b(D;*Q(7R2%Q6d4U(xOVP=$0NF`g8fFJ{7QxqH0hZ0g{C|aYLekBC*XR4y+t>m6?d9B`74GCR0 z$O27e4t$b$)H-m^?T_DcLF7o)qUjGQZ@fxjnz;GymGhVWbT3(%>{M#J`NST4Qe;ucmH$ex=w*=~-XWd{k#? zO9`vvunXkjQF5Hky7j&?{JH)MJ`+8$Gq$|EOtt2<*nNt% zFe33ZWr>?!QL8#g=}0UVwePElaAEA@$@ldLe<}45hIpgTeJXRTU*9_< zLA8Dd)4{dI3ZNC93ePoKXFE(ENcYrYm2nI?r89?FPdsbW0|}F3ae)Z`8|;?mDUvsw z(A*WR+~W=H3!s+nmOZ#(D5frot>?6_0>J{M8Z24^7f~QVj}X4T>Yu@{HEtI;h-3%UW~aI zqt?Lgw3K=wcChf~HhRe65#Iv~KScxIl~!oRxp$13o3=0R7zll+>p`!1WAU zinM13LoY5lwT3f!rbAuJk4xP3Qp3RL%r_^R{9`Uh_DX7o8U4HLsKz=Rz$p1{7+m~>7TTOX-P_J+r5gn zQ7K=p5o+^Vu)wlNip$9VK~@0{pI?xf@e7Q~0X~!;)3b81tRLLq9o)_yQ=SThH?Kmt zCSM+8Ay(28FR$s{*U)JL^*Sf5;lzkQ@xA3AL6GApDjU)~+9-%d#j_za?xnsyc>2eP zM*g8o!x;E@X2+4}+Z*1Q00^Wutq2cP8R5?>euujy-E8w^JQk_BqqR#<;T1eekQ5W~Bk^N3L(&}M(R7|;V@napOL?ZY4R7Kyy`hF$7bZ$?O zt~blq>a)pE;WvNq(6Zn3!=7M|(`!FDXu1M#xJ8fdVCBkDK`U-8;*(KT*2lm52w(gm z5sWNemA<`uE$Eog_c2@_IfWzB{q@_`3^i^z zmhaJeC%oo_zCP~{Z({{=3V8Mu#I4g&u$<)v{nLjcBy=3F0BbL}u-Zw=eojQ&)a3{r zuMo>Mu{hlu=K4ot2={zFGgz4C(%uTn0Q* z%lc?r)^+*^Soiyo{P*_=zfF^K3A<#AcGzk&ecbIo5$f)kyQKf&E%#erK{6E#9$00H zzCQs~tcLe%W05*UpqhwBM^Q0Ukuoq@rQ&FsvRyK~@0S*ogE?0e6 zN(5K*dlP6B#w6~Lf|2tXQdaaG$MJv`jiDjY&NzB)z zid0hvIdI+=k#OgjMcU3TSpo}WIxV5Qk|~UP3ZlG^Z~i=U?Ijmm2u)F$IHLKlnCDp!fqW9SaNbd{Q|>Qo|_^#%i8-5*~-AiE(U}>|E}|{h;ST@>%6T?R&8}>Mi5&Nz=2ub3nrn z$r|+iG^V?a?G1~}L>A+lg%QnIH_r{6`8P10fkZ?^RjxbIh$4jue^_#Lt74yYhtR(5 zDe^>1X9`qaGVj;xRxqog_I7JGa5-KLdS#kuy$Wy>j>oyQu4DxKfGmfc$DDd`a~8$O zbMnmH_q+Ci-*2TNC5Al|^ZMYNTp-V9+|1qa(EnlR%>SA2<2b(C%x1$d40GS2+*iXK zBBUiG=BO~tH8U!uQ@JXMqMVas?rx+K%8_a_LijJjyoI2=h%aq_o8DG&g+NL~ZsbeDG zr*wbW&mI1M|D1pdc6tw1bKX3I>)xmm=ghqv?oL+a&h5&l*uE0maAuOz*M)H~LTO0W9JaZs`)q5H@TF+p$*f{o;QACJg^HTfN^AE5#fAhVeD*x?pmI`164&ldCr=hgqg-#?xD> zCs31AC&*|{v+OIod&@DyjBX!y`2DvNmV=+}Jm>;Rb<_L~J>-6I;HlbYywnJ+dX(NF zOU>&7t9@|2(nf^lzfl2wNGV{g>0Gc}M#pi0 zg;%i41r;4T4Z?fW=7XfnaH|Wu5!?X_(90Uwb*W674r7?#5Ck}H8?Uz9-h9R@@$4=7 z)I{vQ^0zecTXbBqrAg8f-*-sV03Z&8t!_VS`3h*|yBeK#2ki|I+ja%1Fr@>GBTMAf z%I9Pbx`a3%SQAl6z#L> zXUJ3mhu>}nqJ)x>mKKTEeussl*q?@E0PXVXGzDGPvD9{deB)fL@MZGzHdW#IO!ZYY z#$B}CGc5PmLP|$l498Q<ipaEFJ#7@Byz{B ze)`LiW`vx%y{=rgCFn=r4XtCo+1UyJLkNH&_ac@e-mJ8@VE9w6Zn1j;cS^ z3~@pE6E%PjF(}ILjRyD~!0enH)F^9_Yc=PXB7NBF#zSL14XxqU``hMvG;Y!+sefPs$FNBes z!?Z82wMGS+;5EUNOwEzU(_#x7#u^)Kep>-O*(^!q4+kU&U)h6SlYB&4@u*A&1ro&=+utio<{5o(cKuSLi}y11>)0j@}j}S)PknU@u+hb!KbIKM|Enb60R>W2?{U zUS&6s*-upUOEC2G)0fP38hF!tvFZB9GAJqS0-Nx5x9E2gx2;_SBB+7>gO!P=x@{dfLr>_a)Jfb*LnBu3YrA1q(vIxrMR!Ty~*S?;tRftEbtzJ)+Mj)HRK(^c>RLf; !Bp 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�+nRI8JVDwJr}s`Y~||eiKfz{!vKpA=Yv2nWo)9k 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

    5AmL#lk$xFT%ev4i@BN7+(s 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 diff --git a/pc-bios/efi-pcnet.rom b/pc-bios/efi-pcnet.rom index cae3a85887b7ac503861d03b13d01f16a4e65d3d..a61f586eeb8beec38d06e35edf1b6d9154fa8849 100644 GIT binary patch delta 194284 zcmXtfcUTim_jY;@3B5Ndf^?-f=^}_CT|yO&QL2&v(gFsQ5P}7J1rZ+`2#-J@2ogX* z5d{Q9BGtAbRN(=n<>UQb*SCM{&YrpV?Ae_&vuEerlQoacTE!y$pXCc4fXXf-og_*? z+gk)E0R#bomi||cyi77UJPQJWw*{U0o%%13-p>1dPI?Eq;@-hQ=$x9u2o(gOnDAsn zOtlFLH4y>x&_dz%VFFs}4+Z34Z&E>k1gsrF1*vUIyPyS~z%S_KAtf25|AC)F^Hzb6 z&`Luf6`D5$G(d|mU_FF8v;Y9k71@AI5bFO!?I}6~);G}pUl9x?N014%Vh|8ij09!y zkc^2{GH(J-ZU7N0+_d|DR(V3252t32a;G4bXOFr@l93u}A?%De8u)5h~RHz9^{G`$f*S zUZAIF38riQ_|TuEYcBqWVK1;F@T1 z9XMSS2tOfF245o+zyTnFS~Ctz*wH+dU(^FP$3H~^$3*0nuVipI~< z`Q&i2H#t{C+gY90EzRs87pWrA;P@h4q!Mo9I(Qoisz-x>vZBLC1&PQ{AdpBU1Vj!d z2l_Thz=2<{GLYh6M0sPENN}j=I%q?f}%zk%%)Vb2r#X239y!*jzYX2o&}(`wyQ2b+!Jzzj<-9TOko* z@(zh2n>3T%$xNPfwP?9wb1tlqhX`E14N3=r_Iqxd@2eIU&ljyW$bslU6sSQB9xBVU zKLA@9TaFja*%ovfmL7$WdE>AKO9dy;2{K#BQgx`F`cuVQX=no>)4-oO<5wyH%|^h8 zpa{y9wDnp}4_pkmhYmVKLUb|r9jp=Y-*gi5_QCQ_AkodlR^Z{*nJvST{f+m!8{d;f z=4WBv2N*meKY~wxL!K!Z*nZjlp}yO-^3_KTr5G^&tZV zGSt{Y$eimQ?$+FzItG!Z>=wPL>^WjehTJ7b5S|c<5h^lbjA^-b&J2$E)9j!r*bTHn zu6JoaDw+|6XM+R*B1Rig6-g)|JS9h7U>(z(Ce!bAk=?knGZzu=>X9NDc!aUqCDW@j zbS+Uk!X9)H=7s@p?G0bK;sTQafm%!ap@sa?JlIP3>NnLWqcb86phE;Wf(m=@VEH1r zx!wQz5e?kcEm!Pa$ctSUEdz*C4(EY zk*yf?)Vv5Z6Q4dY-)dNp0W0XaeimlP)J}tu1WVX4LmJFC5Md8_M=x+I(O<^;L0V(p z*{^qjKn5(e!Ay%_TTerly-54uN5gMhd@w}3mu%w)qe0=YL_EvSU?hXYg4i6u(cmGd zq!TRiSX?<4iYDvo$@eB6i=AM%@UanzXNGv+F$cf*v1`e7$6qyF@^~tu@)V5Hc|y!R zHD;KSy{_AR20ccL2m&*RGx0HQM4(*0Ut|Zer7kXW!dLmNRw~Wws(}Gb=NxUsT9lID z=Pr(HH6X}`7ax(2goQhA^sH(oAv#LO&1(^GQK~)sG=_VmFhe_23526!>}im{4b}6X z(1*!BcmtC5&JVa+47qyqW<2b%>s6PxA zit@q%7q^g{Kei)GX(p4o&S>W==FSqcx8`8HH%wu`GZH6urFNAOR`UvYa7bR4k)cW_ zHF+-4h)5hNJ4iUPS{aLtx`VUp)6}FQ+P{R>dFg(LQUm(9jWWmi^Mk9ufm+e2fyhKC zw^ilGgT{<#b8S`Ng9sV|`m_l6fG%W+B4v}dE?*c165y)JrnR&(O$8~ta9Zu+T>M=2 zTEw4w!WVX60Qo9wBd^S&=JPw7gXZdlD{iP=vxGGH^W;ll_3daONIa=KsgfZd35A{% zfj~&7u%T9?PJQGHWaE#3Ay8Pi9vBik)Y)X|0|kK&VfTSRuu9^7iS1!pRMaM>3!{Re z$wdodrxAG#$s$f#5rq(_pQAzmE7#>B$V`KCH{YYAJAZB6$ ze(#ijO}FlFVO)!=ogTg`b6wRn<<1t`i;x>XCd^MXEolyTDkH83GdRoVkn4b1TmPHa zX_z-_Ngrz`NF4$*FBY-GLBH+`D_y5RB)P@h7s5=@p)k{wm~>|~R-pTh&odXry;#Za z?1K{N%=e$y5yL*2D{B&NFq%STkObXsV+bEa!*wt&0hxyEfDBx!ASiD%MIu+8?rE?*ZeszN6}7KUusqe+Qs#Ls;-=_J zW-38l%}1Soo`_`5TZ)GSXQW5bv3Yia|&iop<`_ z!mM$xsn&_T`^R5ofW~Q{or>q83S3%I0?R?o#&Luff=p!c|!hlXwMkfS- zy!^{70vIsqvR2NRBUmVzHR&lv&TeR+Y8$6;X#r3)B0bvp z>qjr~19Mi!2AcFw$!icZVtU&N3+XN91Xmw~uIjtQvegtfTp>-^Uhhj49g(0(evPPW zO(ENXcQ!OTRHX^4UZt{Rs8&y&nQ_(ngj* zst3Q(_Ae=$s^$4*-_4jI4zvQY+ z@%mpsyT^Yu1IH<(L~^uw(p-o_y|m1pE3vIyn3VUUHbU4LaO3fRsNx15a#!ArqNJ|n zX?>q&(eGTBZnGwL*S!-;aRM6Za=afAYJ*e8hQu({Cw1=D!|aZ|#%oA8zvD`2-pf?b zb>Zp)iP9G@?=*~+ekHKi3x72O=?iicL?sCJ1s{Fdt#{h-VNfTSb82C1vSp=@x2CGz z(#aoMaUCZydh)PC=$3{bBfB=1NR%NUX6cMB9Y$)*DZyV-4$7E9mD~;H@H>$uBFmCu zjJuh0lScoeSjvC9tz^AKv128>*-DqKzJ`nMJ|Fee&3@w{JU+Z_CcYLeGrl4;VxO6O ziBA6XXRrxXd!;49NODyuF(hfdaMAR|MRbo4UWa^3scGb-3M)#F479c!*lhX~~h7uK1$sPzL~cl{*>M>$g)R0g5LO ztJ#H5y6CvVl7`7=S)1e*%~dG%iBoiTcrYC|rniTG1P=TZ+`5y{1j(Ush7=#7oS1k= z5HZz=^gQ;@ZrRFCG1S`pa>Hh3GF_b;a#1dO4W1}23$f}9h_5MEL^ydQLxjI2O5n)z zp42DpPvff-obmaRIUZ85dCQjJHiW_j-8AtNA<(%b;n3Y*r^TST{3NC_4X>)vTB)d! z_BPwnW*`}1U`{z0^|@AZz0Vn_FJL&S+1qBfEZpYLsx+~k9qK)m$6n;qeI2SX$lmL5 zp)tsU7KeY(I>tVtH<|t*tDYoGors>+eqL7R}Vx&f`&1vIwaP+ay3qbR)>wQ4-aPO zs4?~^6yqzn(M`(wT#-)F+vrhcl0|j13jLb4BP@I_j>%+Vb8SZk`Hd=V?}F zx!wzE%ac;^VE}E;6?0gr_7e`V9S_B28^e+(rr@_rJR-}#lS>XZ%8$~}WQ+v&QT9J5 zCE$1*R<=uX48ItNzxm+zEGs}0>jHR6#Q=MMCmB(R#=~H)iwkgro-r>oMpEs9*Q4zG ze1R3k5xSzDU2zun2zGB_(7LK{`84DEMrjAk--Mq#L$zid)FeNRdgJ{NrU(2KJEFss z!qyK(eRZ$*x(viQZ;SvB6j?aU{cP8`UBE?x`S|iIG)&V$nr*9RwDMJqpl>$3Crb!9 z@|Kw17pZ>aV*E`#_s(X|8H$n#x)G7O7;s^fKI7=D8HNM&Hb-}rkbwTVO*NfJxqXfI zW*VgcgY;p!K`CQqud+Ggn~Xjn^M-ra=e$H!UGe2zdG_>y^&a{+A(@>3%isp6y}Glr zp@yfcW-1c7azo_{Hsmk;v!%z4J;t`$_bF>rd}M3KiM`(xt}B4vnQ|lP;(Pj=%;hiB zdz;mV^eTEe#QYfD_DYG-@rFW6AKM93K051fqA?xE;f#$U_EBYEo>Uv5HzJy93X7!* zJZo;qouD}$GbKD~!6<&9l4CH|en^(g^`9pTsp%`fs1g-FKT;Sfg{sF$rmM}(oRrnz zn7ZMtq(qFG)!1>;8bdV>h(%|x>D9)K*}p))u`yMSUi84Z-u|3B{3IWJDAZVM&JUJ& z%J!ka(;F(jTX1G9^sLC{#2JZXgiW-|nnHte>VQCJG0+{4m@QP!iLYn*s<94y_Qw&R zeh7;evZQg3qogy6UfRe|kd<-=TlFSEW(GLHEF$#N6rh5eovmlQ0O%osW^%@WbF=~; z8a2Z38l1b+jxo(jnv|2ou#Tst2|U#Fe&)W*JkZ1P8Jr3fjy@l!2A#y|J%bTq>o0g; zI3swqDzR0Zpqbq+22D_Vl$`uL-pG=TL#W%tEP5ovN}u?x2Ds?xwhQf5*J3Ml`lqeK z7wYyRv7yooS%+{7It>|DH4W6L6{jJF|WOpkSB{x#G`>y=rPo z6jMX#ADXFfRm@2>bEQ+mi5(-zlawYMsLM52*_UQUvtd;VVxxadCs6g~3 z^d(BTMR!Ey&4zRC>^5a3)tiQo1FPtaY`#F&kDG9v7_6=28Dk>{A3fW<+qAZNJ|4;` z)&Q0opm0oSWii?a{vjOTE-yXt#(4j#>=S&Ny&rDalTS_Tb!)Nii z%hti!T8H$UbdR@3LPsPAgdnT1gyk{4S{5goOZ3;>e1}Rp2OOB6g|l;v%pnml<;v=}zUAP*0vf#} zOHkX0I&#tTsT4B#by-@is9qerswR~sRyfA>G88_NVjc&lsR>Bk|IRdRT8&b%tfaTI zC;?TBMZySu#brKtvT*;IR`Z}vX4Ed-&*$j#?cKd=Y*|0ukDFGDXZB>Ce%=(6IIzb2 zshx^#^^7e6-I#dfI$v_JkR&$7qZ9Ln8`gr(A>y4``-nxwIh!>VcMuL%L!Lts#$X=xVN{*X@z&S=3 zM_fy)&nyQXsj0?l_RMvV);>5n%f5C14jd6hMeJ$FV$81OdgE7n)ay3OT7#aZwF*3> z*^dleo=hJcud)Vk^n|6M7(sUC*k)!}ZisSC3>@69R}+N47ZG=Xze?gC?bQBx?HHlo z^Ss-oBk#WGcHG^lebCMP%N)LB=^scPFo@YOk7KbZ(9@=WDp_N5>-SanASe`<+yZDL zm6?>Q{krkK@St^l(5#hA!MtQgmKSu|EG(y^1mrB)r8d|}SLSUxhv~mwpEogM-$nIk z-*nev7ofajpMmXfOF#9S|xsu4*WCl9G_rFng zm%H*^vmcMbv|KT-vy2GiL2*ex2+P&*k+9_m-gO+T#Q7>>fHKD~QacB9%)aMO^ zJ*G+k5iUUwlwuz?-Zal-?vvt8>R&s{+`ZNCpW(M_OsC{)riA@n8~%r?`)}c#LY-8h zHsA1liJEywWtVe(K9F$dRU1-9Uu77}xNSw}Q=7X`cI)5eo`ZyC2JBVHM@ z8O9KbW8Hc}Zo&yc4RNJj?rys5+zNAnT_ek^$uZvkA`=)D=U&R#qeUo*vUqS$l2SC0 zrOHD5WjaE>RmGDE!yrjG=DndBTX>blfJLzUAoECVhocmD&z*%bdNRXJUyh1yr>X3n zHEZYz*C)K_!A@azRMKhBFW)jSvqksNG__T~!Yw`}Gx`is`xr?<^D8_#M(!1&VE9hYK)M`3R9dI1Kp4Ru1{TEhZ_(NXQHOvPNf59v zvBMbApS!0g!CTMh%&F$`L41xTSBRjyw`$q<9z>`1#`v1_;Z|MtRZY2G@&CcAbJ1JgTM$3`108?z96t;7aH>)MCxST#uA)!4r z4R@Z$JqMSZHLN^ZN#Gb=6)b<`O~BwcHx`!Jmxz^a1|jPdZ#6>~^H7Y;1|6|0c!xFV zdKX@2*G=uxm27?G9$pgysD~R(E0jmI9nHi_^`mkf1L@{WRVF&AW$|1mLyW+8T zx%kf=g-n?`1w-#g>;I&%+i^GKoOS$J8C<`-^iMSa4dL7xLRUjl4gD&{Vfqb35q_Vi zdB=rM406diL23U(Y}R)|tlJ>vbynPVQVOGjfFSdZ9;$xx=R3y^L2tlPqI_#6f=!

    mp2kdy+mZe0osn_4BacT(+}MeMrwR5Q}kHf2*P3Qu5;~ zjG@>}QfioIAS8|1e!#iG>#-Bv+m4>`BNU2S0_gFcMt&EFLTDu{gyl7UZhdZ&Q2PGA zx2p6$R_MmN)}@t<=#nuILZ0>fa0$_gMmVn(*P<5%74^N@>%{)mfn9DezUb$75veVQ zjg>VKE_`f9VWKP^@U>O_uBTtev!H%G`f^y+Bw-^8ZZv;1>K@g!iMrb|7&_`C=9{od zKhqu3A!a88bfW~eGfj5{An-Al0uyg|BK9?L?0HU4-b`vMFv@W+3(7Us3F~J2kSL4< z+%Gq$o=~rcRsDxSlN=9(@i*!#=Zf#-V%m49ydD&4zM?l8eF@o=%$#46Vpy zh6x1knXYY1awev;Q!oE^vSj|}hE!r5Vd%3xQKKGhN#fam3r<8Bg8#!d_R9)%r=GUc8U0w_^W zMd&q+=JM7b)o_FEwYVh5s($4n-tFTX{ZgnHbZwWie_UYwS? zx8e?4!YwNwVXI*nTj?0o)r@SRA->%gA43t{ghC#ni2%@zQz99d^D)sO7;w+}*!(Xy zWkKEbNBLq|ePB; zf6n#$O494)*n43uU-IMkB+w49w$wfE)OZ(eF(wH4%!(6nc^gh&9}ecglzY)U+9U^$ z!H-&4ow4c5K*X-sE_t>?)w&qV?arbU=+>f4W)7zLao&5zP4=C%=Jz#Kf`aN}qL!S~CqJm^TC*1A|k$l7|}NU*pJL zf6Mr(jA7rtk-nZCGJ*EYcd^QY3THWS{J6u*E`gA#f6nLckYzNnYuDcg<@t&pw>y*{ z0j-~G8i~jMfE}Zyumf4lFX@1wG?(jf~IU@H2Ma*aWXMp(p+oOh@hs+2*MFl?`ZkT7EIZ7w|ekA~@C-q38a z?jPIF5;fO1ogd-I$A3{=pau6nDSXO^+M~{RoB7>Ma0nxba*|aT@@upFUg}ad___T7 z<0^%4s4`x|8Rf@{{U(hym05v*U zkfyf|@4O(*7v5S77UBluywAM3@4p0mgquBOWLXZ=dN^OCd?{~y+6;q&>BAB)Ik-{5 zcb&-9@(DrN@{~p0Bv7xyyO4yOuO&p&BpLwczLiDS(aRB~JA+FDd?W6EY=m3rl5_&U z#Ta2b54{}|d^Wb)rN8r&)|$;`s{+C*`z=E2C}A`6^EXqOqln-mLM&w$eswEQx$=|K znLfu32)J39lq=?l+JVTTr(}(cv_tJ{1Z%Oe4U*)4d^hEx zxw(j=U}0pkeCdQ$dty}dC=RMBG9SZM@8L#!Q(me|oiG!O=cg;y5lr{^_`YFxyt3gAjTTZgfv$yX=aO-5LBeax9B;FvTrp&Z z^bwMVjOk+IWn)Sk@loey=Vp%FRXL{XkJrjL36=<2mLvo#-8%k-vv!YR}xw%=pIl$sLB; z$i1A0%_rYpFXO-vxcozO8jy!W>`*pM`7)}QZVz7&2e(^&sw}y&hAk5VTRRGubS=T6 zquN2iPg?x`(Vy0ksLOTASzc>GH24AiloDz_l449R^Sl;;{Dr5D2fPd(ss(iCfBkqO zWu105jX^uIm#Pn=P-U!Xdz8&=9T|g1c7EHU-)mgDOLULWTN1_*E#9;Z7O0Pe-cNhk zXSJ4i4;#yGiGE*$Z8<$T|H`S2RKgQZ`zu~Sm5legYBjHB*d&z(kf6JEIsY)QG>TZ| z+c?t^RxM{Rvh~eY@68o1d`%*RRq4^|i;e~mv-ZhW#+G9rF+Un1$IOld-*G81b#D?! zIn74QNiDEk=S>A}k6xT+b^oqo_poJ`i)i@wRmT!8|8?s5BVOq7t-n=HcX~=zrE-PT zjlj?_b@JRfMTaQwR7aK%EsdYtF^j5~LRO0Ccb%0$ z_k%S_Y=hc%s{i1>f8&(G6c}SuMH>tv=q$3sp*ETc50(+ZiD0N`Mp1EO zAur3ufQJ4Tyvecr^tYQ3} z3*XD*Iq|)?Uxb%i;Ag^L6bQVyw{2xC7#ajOa7Oy<`;uFJXU78Fsh*{;S^pORM&>`d z@*S+EXs}Bbb|U^c#*kd{eg5bP0IuF z|G_<-*k6JRR_GKt@SL{!BUbq&2S$nx`V)8(n}#zOk%_dY5)o=r&ZmmHc!wEVORl9s z`K}joP&|{T<-O~>9OT&7;|AEQ$7#e3YQTzny%STywgCrfaSka2M@ z#!LB0`Ug050zos|kJ6QFL*jY&kLruRQ~Xd6c8F_CfAMb&!1ILzv*K}$>8K9)8?j|7 zZfRb~?KWPZfS=5K-hp?48<-;MAu>x2MUZZ2g7X)BiKWi7z-gSI<%DwwGjUDgC=SPl zIGev7V?w03_duJAlw>_rQ8thkMk$VcOHi;DX=Sd6WuEhjTagsI1jLIm60XX0XRjZ< zPM3X&Zkpr-XKCsk@@2_cwMsXT@a}X#Op#l8S|j0v;#EpT1MI+f62Vv#o`|D2fzM-2 z|M@meIy3X$Kl-fYg)8FBVv=i#S(bv})0a_SZ||$h9@cOLKsZVD+POdKSzKaiOZc5Y zAVaEyk=eY9@%BB91r;Bo1LMNrdg)C1S=4pyRsXsRFWqr92@aXF6_Z~N=>w5s6*Yfe zC`g0<^%A)5Fw7WWpE1U~F`mvnN_}nXcM}z(`}B?dZ4dtCIRl47ZnE7xQCx`?>q?D@ z43l~f1vS-lM!`z980%=-Tjr{fpp9SV{)mym$%M8KIFOph$3zn2`Oj}~b#QdJE2op~ ztx%Pg{5Eu~wtW5CI zJ#*aqeQ!n7VKTSs@JY&Xf5p$U=bb!VPV}1O{hM`?0ZnY1`p2w#%zz)Vzz#BI{$=Ud zg%c9^{9Ro2EbOn0(1JG;9dxaBOjhbyDBB4Q7!ZJ#7Pm;T_d;*`!*)Y*^JQ1v^tl?- zuGM#6)5;&2I>_j7tqs*Vp*Q!2q2j?xUHeSUi;e2Sz}EL?6e5@_StK-fukj|jwz;Kw zA9V!=QnyjmpR@v>(b~gE&;0kb!{J!|9F1Fn0u+=zDPu>&r-MNCV+pG6{ zrW$iKuiXq&eMh~?NHI)JAUw4)cx3F=<0I*Q4hf9R%&%3PA`LllW+t%d`}--Ew^C-Pc-e;8d=P!!J# z^0mCUDy$lhjjm<{zm2p)&~6FcVoUtGlvGw^vED0B<=PyS5a?$UwZ&I`;{ApFDN>g> z>M4(ldlD{G$N9nR5)yNOMh$^6L%7CH2ENMdQv( zvGP%N4{;7(SV*X zdsPhd=z>dP4X`SpSX)`GVPqso@1p`MNgpjUt(vI6`8;xfIV3=R2BL))nHobBcW@8m z*M{!WOC#F+&|4@(FGaw_)_2G1KN%mKvPJkw=^S1yKfS|{X2bSf9d?|-mcQhD7S7n3 z-dP`QItmf~uvJjYcalXCl7uTVLT;CWSSDTGS`sR3#=Cj8g)SfBemt$3jyK*2s<%1l z>k~jRw2_7f4H?hx*|7?dyM2nL19!zyPr4f~Nv(ZmH26vmf2ASvW!i4B@<$MVPlgtVhp~S4OE$2K^}UGoc!4 z58W@x`!y8&_8b#;C)&?^?8$!^R_a2FF=N))^Mq>5mAnzvI;v6AU|x2Z+z zbW8oz9wGlhUUqxO7sEV|hKkwkd&>B>CgOxhr^Kv$D#CL5vaz*f6>A=pf_UU$oPHh<8Nr{2!Rf);Y@ytNKB17x4BJ-y1XM zTXst_6NnHT3jD}t=YNY#dXN-xZ`?Bh)6-F-MbPgXO|0XJ+qp}{eUoSy?y~wS(IWQ< z=ay^3gpW^EK_iw#_@q}jy(FvNL`2=%*!n=LNpp{r4da~e`=`Ags#gyreqHolB2P99 z%ya;6oRIsmN^kmSX-EsvN814ASKxPSo+9rOwg<;*4!Y@h;i(B-ir6qvTD%nkFp%vf z!UmiwfK=1ZEaV5w#ysT$C6Vec)2kv*I>UV9=m1B$`Q?7c^NCVhJC4ft#>WD?r_?*h zm9b`pmi|989?bnH@bG?Gk)`3j3k`7c#lTQw~C!K-D_lYfX3Qk1sh`$FcqO zePFfZlFWLo^NfpDqA~%u^olx^cTMZ36cxWb`6H8~3#)6}rc??EC;61*sd7_Rw0@5= ziaPzPaFc$UwZ$9#c`UH>TjH-y+U&__M5lE4&bCJ?^Fl%F*2{7`b9Ym_5$v)q zp4U#HaI3rk!7mF)ky0jM(l%G+46ZIG+*C}krRcpYW$OaC_M>0f;=PYLBnru`Rx);A8i4#ODu0_7Ywu7GpV<1xL_jm%{Q8ueEd#4U*QR-E9?6TpU)mE zb#Pi@OpK}V@+BCP$X^&vQlD;5h>m41L0>i3xf1PFIMWg|XAf7IfjKP<(O`(s_{ z1ti17m8`*5%hU$=U7nRWD$DA{-sM%da9tj=GrWvIs5OY(Y|7Nb9A~7|? z-e+`vF?2-QOjr^HLm!$+?G$NsnN+$ZyBsMf-v=1|`h#zWK~-fuX$RM0CYg-)qU`vk zw}P&J-EaPtO_pSXx!y42N!8m=Tyem;Gcll>emIePg`+ zx)4{ZZs>TU8=BbK+A3Ziq)q8p|GoI?!^cJkp(9pcb9jCPju-zX0R?_E?V*pTR589W zKg&3Y3wl?wID8DZ(^Jr)w{wv%GyKG!HQ`M9F_H(lIS2Yg$> zh~o0Ir{};y3QPVChpj^s&3?S~(Wn8PP3j-({|x)rYLvhR+$;?Mg+_Mi7%CGz11SeT z=tzKo2@X-4u>frs+o5!?paBfs6~|DhpExypZS5*PU9H7n;;SK@Wg|a&ZFOcqaO0k8 zJ`7=Nx1%t4qy;UF0MmDX6ef9mlW$k?Tlhl@U7t;@JZ-#y3?&xrOHeBRsn@LoVlY_) zTFQuL6`Tr5Rra9m+^KPp+8jVljllGt;#Gge-UW`YVe34I{LOZON=fN>uv{U>A59`+ ztN-H0;~okg(L3PNO5CBt4eYZGwu0f`cEEB|t;Q-Hql^MKNyL_w?cJupvmL>|O^)w- zNV1re{aE=UZ9fcZJC*rRxqBFJJ3lkX16^Z3^tIshL9)%E5(csL2%E zfH&L+27Ae~{RD-8brovXrKiV56gheZP#>pRhnp-<=9l}HvVw`aGwb-|*zKQ|qOAuT z--WKq#=*0)s*{K`cSHn+e0>OzSKGSa-h2EA*J1~Fhc^TQg>3*^9C*%F3*l-rxEbBK zlgK8uP)Me`&L10;g007WzoEOk9lWQVvL|*srzcbsdjsN-!5}CkESNzbr{_5)A8)9E zq9z<+aOk1dx=no@X$ng*y&}GX5-klkRz`R)r7x47+J{W#X}*21{J9lzUyrNUEv=K` z4j-uv&+73&Ej#+1Q$ykQlsc6d8h8+Y5@rp|aOqvxV5v*dSssx20E(mFr-ZOe_}|eI zhVXC_B0Aw{Kumk3HvM~fZdXP?D{8e5qDP3w;;g)B1oA_xRa%@=)=^a{gy>k=lTY#Z z^KHCwsMhKRT{2J9Yo9OA9A*3>@O~B7?9%T!LNte~;hMz)VC{YVEdeB>Jgi^P)h-l} z)uf=iaK1YVlf_1cp0CF#{qqXrP&u^^cmH4n$T6=~!dXCftwYwuA=A~IzO(jB5mo>J z8Em0sc#h>^3`j@iIsa#o;=JMY?Y+sqSZx1c!YZpyL*eS)GZxx} z&Vt6xvljE?)LFJXfZM!3d5uZ(-SpKjM#KHTdIV&PyM6C_&Omg%oHiq^{(`#Y~-!O`NYHD zypA_EUc2A=w9+u$Hi;TSxf=QvbNK@)efk((ytu0v(Kakiyfa zc1jY?zsZ-$A-&GdbDq!o>%bBNr$70|cVa^VDj93)I6&g(FSiRzqu`2`gp;$;gPDO7 za<|>*u~rrg#&%|6>ueg*PSV z11FCEvQFrp>4yeV99bLb9~bmHMwIIGC(e1Dj}TOZ30}trpPE>bsO*|b_z_t0Rc-=# z`f1X@^HkksSQ7k7Y8-Ue6~)1e_*X+D(pC#X37U-3X9^q%JQk$Fdr#HsP`R5@3*dE@ z_^t(|@)!+0{kf8Eo~IfL`WTxG2eK@Nq(ig?Kd}(oO1$k1bYCQwns;!7ODmGBR zTO4a$57}l#X8hPyPi%ME1%>JnX^>GL{IHaaeoYyOnAR{NDFcDZN=fakhw8?$&oxd> z*Gui$=F&+SMw~NrhMlrv)x`kZNC%5cws<<+QacZXT6mgiaYgU0;1^*enAvmOB(?GFChxM4_*Tp<<2A2fkl# zxN5RS)nWA}68WeFH86%K1Ol%;H9XDs0q*QKjDcSW5*=yw z=Zx`x-8KmnW4WGP40k`6z-cl(!`DB;Z(U$@*E>jH(n1E^-}m|W?G&A6-W2C<3iCbS z%e_7QXt+t^BWriC8^rh)%6-q*v$B#FALwF;frc5#7q<6=Om%jISj88nvtQ1`IHLq) z)6bPJ!g5djpmR>+_v9Cv!F;gw6uv&JKxQ~ky)`_dF9IV&LDR*IF*Yr--`0mo-$|kD z1cmg`35hS|5@-rB63`M6fr#BliATg)}CWOkq)ZY*Pt-q`|bnwzQEPVWJ?bora zPM<6Q1X7_Dc{A?= z(}mvw-EzzRCS_|;*n<;xv#%PLA9v_*xU|&f!}Mux_j@94@c2$v5uFm{=c6AelaRC5 zyns6aL=RXMZ18kPI}ema`E9M_%%L=9TAxJL_idHt;I9H2D@ztw3&b z)6*^ZnSBBBx5$rh^yQ)1^j-F~OeppxVxW9`B@-Uj1=HwDUOK4AwCjW(fal!+@$$jc z-4596r%uHae+Pp}9NEFZUK>BFNMqtn6)`R8LjS2zZM5g=1<4toD5P|uj#Zy{&~@_& zUpf5#CBb*dQI~?R&VHxV2i){Yt5D_F*2Xjmz6Nm(keH!Aa}>jQehm+LLi8NgmQE?> zEz9!XIcB93M2&bKssI~SySUd>6o5;;h{8|RnvSGhPSMGTcsrXb$`lITj+Ny3!i~VM zQO~l{lq!37>0BtNyGUp8=o`2|;NxKCH8%~G3)FI^7LGi-|AX}&ATu!;ndARtMy1+0BcjO+Hw8EVaru$DH>fq6OXIL1TLhrYq zaZE$^WDlGwMv-xQ-;f>2=IRe$hw}HB7xTvGW)KKo;%p}up_}J^hv=cd>!(BPmN-K+rd(21%hvBqfP(N5y&2i59~;tkk<4M_QfLxyR4EUfmCbEx`cj zJ;IXnT4ZEO`yNRs7WWMy?_c-d~Nwp8M>X% zQE0apl2#BGeIt)H4N)7G4KR%SEI;|`t#^>{vBc5;hzMqkAbOrBugKbw*LO9Ds;AH! z`d%<#eCZsg3n%<#ms}ROO}w#su)~@;B%PWOw3Gd!eRM0}{3q_ow^BO*VDXBQUQ9)< z{M`?YfO_K-6sKKQTnJb3!39bDq31}M$S^ET#MdsLKiIC+_T%yxZK^R0b+_jG$-bu= zTlifbXC$M)%4NTn`w1#5yPwwKXc8Ed1JqYo5}|+YnKD3T(*oljCqU{csMR-F9T~FH zP#JuNC3*jjVnIaGGi`NF?=&$rI=pC7+2}9xj}%UWt>jf{aB5Ai%i$f$g#O}{8~a*> z&gQ&kFZM!)#^0MCL?T58O{GXN`So12<6zJZFs;VO%yiP&Uj7eB!|H3>iUbEosTWEo z+5&i$V7P_aptr4B@c(1zEW?`M;wZjmEMS8%8isUAhtz14a5PdPAWkGEj1ZI!MvXBH z6fBf52tkkzVKg=2UhC)$1t}TCqP8 zct5_W9;i#9etm((5#{ZyBsuGANxl}i5>FMCkIQE{EqG&9VFb;ekTt6x4;I#P`3qHt zevA(V41xZWAog*w$TX==70nkWv&6I7QgFzqiUH_CyJViRc4Q#7xv_6NS>O}y6 z2SnH58Q;$!#XyBCCPI8k ziGWwzGC zz1k9T-lO9-DM@gc34({b!6VM!)47vBb_A8W=N9AhlK2v?TaabZo>cJapA8QZz(Ylo5B(w|pBPPsOu96Q>twtHT`$icvO zQlXvBtM~)cS#m*6ajxl5=fp56`|b8J=+P}*)CE=)_wwHV$noWC)oM8=dnl=BF}|)LDLnHW+PtXA|51W|E@Mb8 z&=HzEDM{}*1$ZeRM6bQCZmmr4NDDQiKd^j%`EeLG+vbE$+#HfBS365xF4N?bH z7hj4jr?ne;ttsiK=3V$8z=!5bM429n_hcEMZnV>8>PKX{WS0M;U0u`z;z#WDrz+Mi z&_g8+Nze8Ab_JQk9NBt}<2E?14}Y#LShk}TI+EWU{IeU{{PXww2OPE|%czj(SeWXz zAh{nzg`@7o@jTre#nR6pn%^F~D+Voh?9u-Vk+ei=BsksU8*<7=PEiBz6&II%Tk zqfbM1PCtEMenCZQ0K=A{z9|*6-aV+V!x`UI7pW{6v6_^okYHa1a7mL-PpjT8u6JS| z596M*ddP|F(?N=gj6AYo9}KNISV+IMd__{1MYD|&T9tQ|)UxZXlo!`82B6c{&X-Ec zUtAs@gmH!WjJZgn_u}7SF4&g4-S7CFk2d$a&&-+2yTv0q1wT@!dN&9SApq#lh-W&5 zxJMn9wgpxFkCM5S>vg4KxQ44gIpZmtZa%Fl$}BCDPYl6&>B9>jJEXaaWZ++eH{qpu zE$HNqx$ttB4_drdY~BrqbV~ZKbtH(XXAGQ4(>>kXGnbi008rO@P-3|)yODGni@<`h zuD@Vd>iTvvR_&#b_6k3*h@rG{#w!54X8XpXTXpdJfVDMtx_5~66=v__+EZ% z$7BR$c;}6c`13iiOuZ3%n4VMb!dd?#ap`)@`=LhD_|tONm0NA7ogH|oFx49WCd7KJ z{AEJXh4D7R)qK=$J3`;ZZkpx|?R{{(BJRI&mPlugbYfk-Hlq4J*#uHkgp_-sE^)=Jz3d~Aos5eaTi;OKCy55W*;VrD;xvO z?ghf&Z!c`)9RC?>F_2Us2G_7WmLSG2wFM(!EE*{-3j8Yia+H%y8e^p$`x-iI^AZ{+ z$-Fi4LpfSnp^y1JX^i{Ns5NrOYzXp=g|#ePv|l|o+5ME7*#jl&Z{9D<#nZPAi2(E| zmDfA>?qE(l1x6h#8iWC$Q!?EAsmEm22~A&X7!T%;dv2!`^dc-_dc;7SXQAJT0u~s< zP7CwdRM?v=a1Ve|oaJK+K&OaGn#f{qNM=fbF%;#5)4E?b;5mGA5@S z+arCQ+t=;y7Wf`c(YW63c@-myKcZ*MNIknS-Jaupqu~-x9^t+0p5?j!DO~t&yVt`x z!w>(Jk8x_YKFGIH&sp#cAdPB66Ycl($q5}VN7wXGH`!nsP=uJrxn=(6y`h}BC9H+) zUran_D|k(Q`YW>k?Rn##cnMF0K4^Lh=8VTi)ku~?#zTs|km9l-U!5%D#k+P@`^M@v zqGdOUfcdATyhAd^fMfl&f7NfzisE@iG- zE%UMQ$XtHFv9rrvsv-OFCYyrUe9`UZnO1Y+>NM0ibN@!dV*Z!T1(~`Y)gK<3*!<*zr-w!S#9`=B^0<*&kW|{2SKk*wc4s1B zoTuz(uvpzzEgQqP(5{NLvdroa&tL*D%cYxN7CKoeN!-b&SSRRTHJku0s#3zaQ^f7L zi6S=%64_{ApW{>QJ!|uc)u9ERzMTGwMC0>q4^TzY@m={zj1C}tulQ15dv4rGs!7~P zCitVz48hYQW@-pK{(Rx}VMd4ZzJ*KP!K)Otu*tp!8=&-)nFotCwB2H?4v@Qxhs8=g zHQrp=0>Yg2nl;@53D{BZ0bh|{oZW%CJ{bs*AHIEppgi@@gX!1PIUr}~-g1Zb_d%25 z-|(BWU1QdFw}f?hE@lXsM1eivYASa)UY6Fia8cF_l88MnP0(uk`7ZQcmp?#jikHl} z!;3RV>fKy^LWvi+RLl#5-3gQqZJdz0<=x6W>aFNQ={Qn({0n%rGXikXD>z>0D-Vk1 zOA%SBI>=r~-{rfBXpB}^QWT;+kOYxS*VUS=647^Z9~9|vmz$eoo5j8e9WGggwvo;v zOD^2u6NH=|-XGEZ;w|I}mA2@S-3uUaL)vU3xnkAI+}bzemv5@Frnu#ZjO$`^GP-J) z_%1_Lo-DMSO0gO3FBSqYiyeYTS?*>~1yY6*zXJev}E|9!m$4);F zCM)*h-jlGCC2qd-U*{$+R@5vW2dpyVf-5lb4k{tbRiMG{qL!8~$eIaw{<{2s>OCv` z(!Oa7T0iLdkE$HL%@LY48{+_HR%39^+MU4j?hzm$iF-BQ2U2=S`&kLgwyyYd`*YmM zh?czuhFyICCR_1cJ6tG`*}-m?kHasj`F`jd@qKCRR>1}Q+GO*W2DK{f{@Z7IdvX-U zKsOZ#KLxCp1#|Mbs?UkI^0_I)@h2M45thHo{YK375V2r)+87`bp(JDH_3(Yvyx{=L zTO|A4?P=NRkP{V5Pnce?X+YI`_-9aaU~PEh=vY8Y{pWpR zT?L&g$8Jb6P@oOeUDYGWyE&L3t<-(V0wuTN#I8^qe|Qzy%<I>AtiAd@8Q)B-|O; z-{11l7am~Jo~M`gX{^DL(vo~H=ZdmO#`c8q3Ki^i+&PbtRCR3_cU~yZV^BruS^vkBW}>SfZqVlDVnRO z-S6QXPe%WyA9?|us==J$2(9T43N_Se`Q31f`nI%s{D5y8#xQz|j5u^N-NfhCZ8O^g zG~G6%w>4pt6gNL(6@$AKF?DX@-f*x@fS64q&-o)(Fo54g8B7+lp~HgSZ0iTsC8>^Z zgNxNjBR^o79XYfjVcWa?ul40--psPSfznX6>!tIXb9ps#13H|$uh!9;KIN8)>qKLs z(D;(%$VX`;nfdYEq<%YlbmQI#J#m}%;ru5#Q{~xUZoBNb51zpp7QNaf2fd=4cl*5Z z+`$Z2QhnM9+w-BI1biX6H9aXzw>|gQL;arLA`__KOywi#AEhM*46SQ06O)3QwnRCX z(U}Sy=Jzrv+u;m-2U7u$7jYsMypE93dWb5`#^=*7t9G(yAq+toZt3XPx(yn5C0gaR z33S2AVj*IG^1PFnz{f$os|Lj8sBGkl>isg;FElUj$19)Ke9aGsIWE73&|B$%#p=_p^ zRkTmYIgL5hHhPF$ZnaT4DqwfYwhb$`Ic4&VfsKix)h&SSEl?9N$?g|i$|0wP6KH<`sH zXTQMpY(?T8GQ*|NR~_8n7y$I#=$9HA$bk4+>(q8pDP%2`X#f z*9|HZCEhY+hrT@`YD#F7i2eC=@zn$mfn4g)u6j!R#)!bu;E01IA=_4Gnb+EhQi%b- zS*z+D#0vz0Ot^cwioN8Oq=>fp?bi$Up_UYH>t@YMc{Mhd1Qek2Dc=28_h$O6rKBZQ zlspW)iRV;=cAO$FnEe6myOYSGpYlaU9I^;EHNd#2wv zQ>62c4_WJ#UCHyQXmU7uST+gC5@Z%gORi%sD$ri{F`hnI9nTR={ZqQ|?H|8WuH@PG z^$OzhC(zEGj)(2kA>Z%f-*+#}P1v9JX5}Aeva?u)8C?^s4dk_(&riZJ^<7VG0P9!w zMI6)Ry?F^7ky@`(XSDk(zIQ`M)=IdaTm&P4ZTFr%VFX(UKR-5KqbDOBIsXMW z+?gfqL^?=#F+OaOb3}!HN^1P#xKK+rSHnb#RkuW6S!apoDO{H`!JILMMSgAasIGtH zfjf6HoOqL+u}dCX5ygjwN7}h9UW8$BFms{OS4t|0$cc>kmOblH&jv#mDuw(=lDxQgdcomeEOk6bBnK%-W+=5rSbyW zD$u7nXm7Y~29a*t)2cEiCAs^M+@$q8EwdD9{X7>|=x zrcAPabuJAR-YuYhrg9T>M`LPe@nMm=dm)Gn?y;J8u817b8>iqlpnM8}3%*ht&g*)h zbTx0(t;U)?-FwXc{O=R&L$0s$N0TlP&0s{&05$wkW4^iC@U~R@f`Y)I^B)+=Vp|`l z(i%^HU3il+#C+A-C&bO4>@T8g^;1;w$toekH#7f*!vr+ivD!i z6lD-Zo^Nq44W6p5TxRQA8wuHa%fsf<4-XVo1_L*t>3pph*?vHi7tNcq6}CDoBj5+Y zW>^JThOs3(Aq*l(KBZx0Y&og2q z5l5Q4+FuwpEEK_UfmoA%!#Twb8cU6p3<0i!BYvH<&kZ8?bko!a#p&Bx@miVB`YFNz zDz{)6c4BmkXy{J&D?!FzAY*dhWHC$x&$c{<^O`1w4?*W?an2V;X4;lu?b8L4^y6I}!OB=^f0?#L*dP|992;d*8p@s(enAKcSAe3b37*$%nt7A$^v zD53G_rl3i?)6(9Dl^`E(lH5)2a-6g<(|o00Yu!}-Aa@{^FP(THZ$*rCVdvS^s*AzE z=y$oK+7g!=r*SBq|2hDXZnu(l$998CThF8|1XI9vwBh{dfh+}W4%*a(A2&pg3+DMX z5Bl*Q)L_^S^Jr;EO(iTq%jVlD6Cn}(KT~nY2kK@DCL8h_DO2&~otNYvtZlP|fg7w4 z#qo+umY4ULb!CaYytG>o+;{Km!M=~a6_fpO%dC`2Jr8-qGH-$@T&}=F_Gy8j!sC`4 zv)nFPS*1JmIoiq}x=&9^W|@i#%m$y4)NuA2Hd4+NG)CBJh)65hoc5pEU+xZgO(o`f z3IcONV2nj8`j&Vil%5Iu85w9b4;Mr5<+iVY`3P3ISYs`!Q%_68-Ied6_WP)2trU@$ zd%%B5{Qdp?=dQye05Z71jPHH=+Sx$r0Ih6Gc{!NsEpcM!ZIUZX6xeIR9Z?IT?Hdf# z63>_Q#Q}RHq8i8wT9Xx z({H~pVO+1LzXl+a7;TCrywk!_js=$z3+U{E;tjZDTG7545zN3*K3M%WWn`$(_$Jhk z6a6cjGBk6ZlilUiJQJC)aSxkOpekczNbk5~f8IB^%ToAk4B;)q+>KIhr@JBrOnpSF`4(!^k0H(^0&LIywx{Ge z!h^V_<$YKS;hWw}Ftb54`W`Ja$5q5XUFI4O)Kyv;PX# z*KTIt0{eZ}E%o2nNFX#?v^RrNIv~P04;V6rAg^d6eH1g{PL_*qj6s=(b1w*B2FHKz zohi})*y8gr?eaYH`K38As`Y2W#9$A*fE)SS%vp5%{ zBQY^a@Q133bgV_e`riq;jY0giYA6X0i&?7$FG9r1kA{6@j`Q@|a}>W%w;-%y{$vXA z;3mTt_=V|a1^-6HD5oF(6E6yNqul-y5^sJV?*H&8ceeU^Ij@lM6=f12W;j^pv|O@r z^oQylPxCVy<1hmnW!py{#s z;rDZ+Zp(~}C%;8@0T&P~t0tuSZU+#f3&J}^gqtj&F*9#ZDWoNz0ir}Btn@=pDxji# zo3rz5+t^|ful5q>`c61*)Dt1%;2F_UsuWZIGC3v2he#Vrk>4GRaS z`^B|<4}?llQ9!XJ32a9sPb%rFE3kEz9GdF*#u33zA>9YS*P{X9f5H2bWafR?2?W43 z)RCri$Fl=}?BZp!O+Q{qa#cf3$#*8K8qtk0B_!b6_qSv40^POZ2c9Aq78PfuvIla< z)K#sT&js6v%J2acr ze9IhBC$q72i`pE2vE$%-V@)2meG#qJBW<^8iu}T5w_Htk3|&;PH>NETuw46+J7L_L zWkR5QyWJ~gYuzP*L^?&RxZvrrv0gMjG+?jL?DBQHwu+SWBbUj@8^#%2RiPO*TFX8q zdC5<#f9BBCMZl}^8Mx>vUxg-fFK^}#wJUZ%GzByB0DuN3B5RPL5wt#Bf~%h6g72Q% z3HC@SeWW_g5;}SR4(D<%-cBQ^w_Uzzw7_HU_VUIJMPSr`bL6$*IfZlWq;|A5@fLxX zGDY$?_ZDP(jR320jTOb;!sidtng}{2h%s(jAUvSPZwwsemBwRl_2n0lLrV;#c7<=HVj6t_fli$4(#i{nVJy1zZ3_;N4ZGk|A&A@@<>+3cF zAnHLKmdkkB-|Jt_5HoiiVQyk#bv3p#hu%D-u6$FE)R%%#r5z6Z1{Cc4bQmVNaUWp@ z3sW`(PCVqf>u?SZ9dV-Bi6o89*`12KT(S4x63ahQR;64um2iN6V)->s?!RV)*M;Yf z14LEfMm5O40d0rr$18072aK{8Z-<7Ez ztyZMZja!&e^=My>z=|2^w0s}pI(ro^j&LN%|leZQ?G4V%@|6Q4$N z8*a{{xw0*}6_Zzbz#w0p`e2^nmIYu?5DJ$1W<|4vW$|$FC^p0b-PGeUI0!P`iuk z@!n$<&J*w{vZt&WVj(%cr-z^{cp%BTdulgYbR>M8 zRw>1s5DIZ9V=P54WZ9t%)Rt~m#2z>3-aTb#4#1bTSX-#}*4ui~A05Y|%h=Jvk?h#; zl$F9NWI8AuAwWTN?|o1@@k#UR0<-J}*?bb)v~m9N&ku^5CI4jAA*+uEo)Bcx-wxr< za2kt)QAHe#Y>rcgJXSm4I^qsF3ARl8X{ZK7O7WeUlkAR{@5;2dG;Th}`l!6^K{h%3 z<8kxH_(n`Dn#+aPbIfsqD1KOwFsLh?_t%U2_qnOQ2&}LhWN3%|VIK%sr`~al7x6Fi zQb#_&EYuH^!uzG}@=)#jy>HslhRr`*-`NUcR(ZZUfTRRyoced0xiceRItV9 zK-=T1&+^uZ`BX3bl%|yL&3LP%C7sHF`(JF9+7n#DZla{wM1#7Ux4EZF#Rq#1oO}B> zj_*U1*jmckFU@|tdsn{dBn@q%M0rRR{O@$o)Qd|EYgLdpcT`Fr_99Phv&(F5@4YMk zTP9~6$w#QJ|KhkCjH$j|i@rs(k(SHXN1R;fuTG@r3!govVIOw4+4go6ObjCxXoiV` zP;?bk>J)T>TedOutCrE(C$g@Sv1hkttgi9VZ7E= zsEIlLEdnH=myQ=8KHl@sc8(p75E6P1>ph&fp#UdS-?h!cL>~222R|Z_N8RjllMzWs z70>gJFA&O^!Ea=oBY;9PPf8TAI;@cI`&Z5^v8h9+F(e)HWIDK+FSs*rCY^6Av{TYS zp30;8C7@`{OisWF$_4alttU7V3Y0Z6yGWlHl@BKK@PH8+Cb43jz+F^woWu3|}}<<)Ti$ ziE}PfvZ@+PjZkexa(}S$_;VE8ZY>L5t0*G2T{K*iqKN%*q5>7Y*e=sU(0lWdC%k3d z=V9Cy1MKIu;IB#j(h98{(ex?iU)70wGVPvE5yd{oR3nf_5*ydff%?^kRE}^26!rNC z-95pP4>OBs1?Wz0*A{JeJTury@`KwSu!vigms0MbTJtptlHnfaZMmC1{tH}Dsf~Yn zCg!o;J~|$D63-kIeAWFv`nXWQe_eYd=UAQfLCAz+S*G6APea_37a85xzJTq+Oe_w? zE3#7wQ4eyW_ariQO)QYoQKS}o7v5zP5q4pydi)}7Zc5aUDtThQa33?s651bS7`Rcz zwPcD4nFzQdLgEUx^_snJlZj!R-{ivhC5S_g*=`|3nkN0bkE<7EuGV%lcf8~1TnPmt zo5O`ApFIkknW3a~_&6>^&03f&!b=s&k*`Io=lxi$B;tMp&R;$3TM66WW4 z46Y@s{6(FHeZQHDi|Yu;ZDY+Rpbt9Ix~RLu@6@KUXmkzw&C2}t)64v=*IKRXz`(DUF9XpxB{j~iFQt#`?b>S%T8@sn~UE8 z!P8RM~AxL}zc2*p-xKaU9UJJVY z<-@!idF>t-2fu-endk|mn5-o5(RSzxZk?sL+OuL@eH%U?))>$Lb(<5go0_J9se!oc zWC#-@fK#!t+BOYPC|*p2D!#;-Izyyl)r6;0E1{kcvDgD{5JEvv*{5JrM+cEuK4nvQ zqAXxjacx8O4|72lZiD^0+so^cOWkQp>#sV?*Ek~NlvY+@sMcq#DSl+;R`u46wlED(#th6Tyd?yDaZh-_{Y>Xu!7II{W(wB+ zBC}AALq>1gV!H5T@B6eyRrYBiOYIK=`Jdo`99xsc`=FPD!((uJIWNgog)6|n6w2$0 zRN-_{D@45)4HrNeVc%hV=EJ{NQvn7#WLHhFm%SmfFOwfpoFpI7AReiq z9`fs~uYaq&+K&GM#XEdskPjfszwYf%o!d0^?)lv=U~jl-lqh~sD9)RH;Wr?OAmInk zosn2;ADzsz2GE?8KoHXNn1`uDyP5;t+wT_U4p=+)aGhPd(8q0Dwcv059e}TFNGHr% zN<2n}M1SYlcshBix69N%HK~xxR0GOtNay$3gCo3NLBDY%TXV(iJ!~tseK=N=c+j3g z>hMra-L7AY1fM%Jd`9B^TYS$KS!{pQ^D4urKP*fRt?kK>jqT{NeM5`7jTp@NW+lD$ z3d~jOTD0d>I+R6r6l|_gfsC+aP~~5yTA3jIMpRlE=_ku!Kp7~(Fn<&Qt?^_klTYN$ z(wxG}1$jUlQ)zgdOuVnD7DOn>`*7->G?AJ#Fe6}?o&dakx;-Rq>)(CJo;ce}dTuF9 z8f}|uc;*EPTy_=`{ykdrQ&nA%DaCnn`78_*gV_;`{|60r3|c+N> z28$X4yvxXM-m(<2u< z$^&LOF8fJ(T!glAkjLX(=7EWR{VFlmxWdr}w6}|h)9pEjzDFl}R=6}d9Zv0%4W-&E zs~Gk4mKH#R^K#~-@|P$;i8%bE{`s_`NnBvS0apA={X5K$?n+8Hlf;s>k!xI9EXto@ zqc%~jb_Kw@{M)9<%GvnQFju6DsJ<9yFJOf)w~4SE8>+B&}`E|k))*ysuYpvI&_B~Dk%M$B>#J>hUbwPc^sj_fIq-+Z%n z{evIDu+nvE9CTTgQFa9&=!6A)x{r@beOY$R74sU0^at9+EcTn&y>z;GFy}F}b(YcM zT<`#TTp_kuK0%gyQMsTMmv?U~50yL&YAY>!5V{gBe8O&SenblxtxZ#PJ9QivoA?v95W}%hN8OEkk(gyqI z7Sti1v+pLx|50;^M%UVQPze{42cS9xr=WQx5ZLXK`w_GkIK}PmGRsxNxX^Aa(P-MD zWMy4&@EZh%1&WNsD`yJv8RA9okVINXp$t&30}mAbd|x`c``TOT>w*J7=?*3PK9;s_ZviwdX#w4{H&g7RhPXiW zCwY3QZmO=!VRcTF0z*x-EpC0-s?he(=FUN0kM1+Viv*Cgqs1hbFqL`S)fs0!urpwN z5ctB8G~+xTvJDCww1(dS^9x!xJKZ3h@;yS}SFn2BE_ks|Tu}hN{}w3%XE_=_0Xu37 z`|2|!X+ZytZ9oD+Bv)8}T9BU;wf{`;MfMA(A3{$+aarQchYLr9cz9Vgv^GLEY6>@s z*E=GJHG>L|*JO${6AwxGd*I?u)Ge)>?f!&1%`}-PeO2}-vce;{p9dKutA>(7?tsqQiZha5(SygVD+Uteuhfcf?^>?@qNGwS+XhSj| z(=h{l2xQY*K#G!2Hj3VV47lTRMx#wE&uwgu^Rn1DTD6yL2KzwdXhRC<)a8}*&^ozO zrT5Z0v%kGw6k3_wtPqZ*^WhlswCu6kYk57}*<3~Je=_!$+r|aMSZ+U9*e6)7=oK%s z{XIE9V{Vp^{0O;lYYSak79N(8!4J?_=dRBNL#UMBEEUEwiqVI#NAbFp@Mq-wuot)B zhwk>XDN``GAcwhffMECZ>Tlk7Tuu7r!py{cWtAg;oUo4shXtQs`3|5VuM!T3zZ=V{ z^x!;5@SU5?m++nC87j#~_LOWc;1qz;o^+4STh90)d*?z6W`rDL7a+~U)lmi?gZB4r zz8IWCmBb|)S@Kf%1IqV6Bg2Ay0__^y#&V<~-&v4KTG&yL zyXQyoShV~JpZ`Ry7DFWAS#N7#RyL4b+ZtoiT_vl#HZLwaQRMQSAVK6@P&5J*ecSfT zsG6DPtcvdRI?7s?35g%(vC*f$&bRKp{JI?S97*bl)^4Lg!bTH_pS`&fEN}=Moq#ot z_-&6&HKWW7Ue3_+jIsuua_laXwtn5}$IMm+^Be2(@yCyT9sK&* z7&h@)@9t){`w4r0TW(cBj3aZV%5@jy%z`2Rvr$;l&iA^TUs{^M4^}8eC)~|ou;zZ6 z4A+6r&^FVOPO>8uOAWuDQ4RTuN3#5GPc~UMMi9|b@ABv^Rl3|CZQr{`)=zYWoqQ?c zl4Au{+ySObn2Cgc@rg=#RT$cSKty%}gl);nntB5-S3^kY%O5}E6vguMQDr9)EYc@q zShX;#@*Y-#H(=MX_~L6N=aoN*kE6nzYpmC zTA?2G9%A9*0$Kb4BFR-X!%ZeM10a&i6VlXPVDr1~^pnSv{?cAlm`ppnU1+x)*>n-n zBf)i^%aiQpQ`FjjjP%`y2%;`To|%%;69l1e(5- z3d{J)tk9zXm`aJatgQI~1{J>I8{Tqn9h}y&!DfRpe#JfJ=JCfWcVVo;y zKSQ)*$GmRRtUv{1GX#Du*ebB-kN5EbqniiKaMCQJDZzU$z&)SA?sh4J7Xdj^QHsbp zF#Q$jZ=u?xULWoU#gYNAnsPDz?+qjyCPk5{#f?a#LV%yXu&hCK?@Ov_`Slqpt!|U# zh&}5(878~LdxW>qY&$bK=?&-0@b2@#h`RAFLdnNT>dn@yt5{sHgt?840e>fkfVL6< z>^3b|zI#;0kQw_UIcG0>LuW*FbCdnNBEQL&4`6K)3x&Y&cW}@_U_cUj_AsuSr*3B_yQE! zC{+JWDAAxWQ@NYJ>0rf9jFgQU}x9%7g(e>3Jr{r+hK0=qdkI2K!%D3HkcB6tl(( zQ^x;Zc`d_zoAVKLW0)&Rh#zJ>hjPDv<0YSF(E1{aQzL#FAx z8%ywIalX3_ zT=OXuxXNIKm z9Xv(T4{T*PiisRWF;z16n~$H!8%>vpKD3{BOTC(${nuPQod+6g`kc0Qm_FX7e{!MY z+@0@JN^PF}_0)$~&)bagifv=WJh-!kBB7&%i`cZ&2kZl@!rw1gS=Og3DKOq#Y#c-O zn+)ztJ>k6&(J0#|Nn3BSBp5uK?x!7paQokA9`K8Po4jSDy=nZ~`EoW}G{z{f0{kjh zTi>&>FQs~MBRptUmux=b$H1~O>M@VrmUJjhKfzgVsI(LxQneVA$W9PwJNnw$|B0fP zbioDPuuF{F-?O4kRQ#ne3Q;aU+oBsAF3uOXYao|zm8u;rh*vYqMyanFz#X*K7#Fi> z#(vL&MGF~uP=<(YBp8ria6B&^@AaorT9bvgOp?z*`4@{wb#6^z6O3f^q@-mjnNI1m zX1DLbhHTq*T8z|0;<4g8De;WmN{kTK@_;Shd$bJZHlVKWL$BGH+U>vp;3iFOK>quN zAC+0lo_8K`*eAIsddKrUo!b9F61lXFJ5e9WOC^vCVKXhtUP|D(>&ZlO5;}m7bT!3p zr<5f1#loKVR#M+A70uvkz5Y_^r{pOe1Y@0T}m93GlW|f4at!8vd<$H?qhrmuQ^WzNY89+~rj&Mibrb*b|zG&#% zjCG|mFrLLGLaF33m5~?me_1h6=3pSQTX%L;PiI|=!~oxu&Uyii8KGP*g}1=K$UwM{ zEfZzdF_I}7_}5fCRGgfA7XTKz3%3ThMSAc76Kc5bW)YCtt`>#kNkW#2u(k{#4M{W# z7^1a(sZo3L@Ohl-h%Ak}g1or!m1n)4u<7tiRc`xU1)tx{Gkw+ChHIVI&|QsC(`R4>X`hk(9>dPTWG&d12&OlC_snPpGJ9c zD6t71`@}%+UFyq|NCgFa^rd<%pnQM)L{hQ{(PZ-2FPmEvtL|<)VljrHN#vLiv4nJ#W#QS*q+&eBk6)xi| z<(|Ncx-CoR(9Hi;h5Gn;W}Va+G#qL8>IE0IWQD!y)s)O(2XxOaalTww&r$6DYM&3( zU?x-K$b=K;r*pt<}D=^0ah+cBeKs9-j+JyATe9y3iG zLHaIcYro~(g=b{-Cj?nfom}lKrls5?)kICzFe-d#D;x+Ut=)boU$PZjTowxo_wUTepip#`nzT~&Vjvpuzw%8$=37#wg zi!r~_+@i}}fn@cROM?x)-DpYGp3wy%D;}m*JOzG8*u#`8tb`ST+BEEOMKF9fXCa=De3l?FP8)(up$sv{}^&*@j(M|8o3 zdqgY&`WizvtL4cKpkQ#aLKhlcRCV*F^amRGr2zRn|F9K*BZ+$|WjkEOH_SvU3CCD% z9+?y(y*XOH*DTcuP4A&4-=J<-sZ#~SW`gDjSlO>cTZ0Ji9kwg}Cayjf$omuVY%@_NMxub_O!_(>Z1?$G>xf(BnS;_evOXGAMi#C z4M6S)0tL$YI0yLhc!$tNIOXkdK5(2i^M}b0y2eKJgq(z4QW|bvc4|x zE0{WAYceMwafy-M;zF$cnduVm!U43{Z+^WtM>J zZ({(0m z>Cp8W0wA)P! zbve=t?=wyBq1U=`c56KrDj$GDCgHuxpd%d>zxrvL2(mwx4MkC>rmHFMALcHcW&dXC zS1H>DMsbtxZZcPh*)C<3edboTrk2&<84M^*+YT}A<1c>`AizfC8hQ>kug(L4(CBWs zm=UStRR#VP+T3y6k0vLZ+C6S=Z3i{CM`YG6UAr{gez&)UBBJ2<$Us_}3v5T-vW8rjk z-P#=t_rCKRT#qmHKV>f*W>lCR+7lua!O=xoi0mleK`fb* zklk}PRs$_`%wJz2`?gsx=ys)PFnVa5Etd3vzqmzONgUGXG=K7v=VG)r#}CoHFT4BM zU`Oj{+CE==P_pd@yP|;GRIf-HL$!%cRm&f1_u{9D9txzsZnooGmZ2&u#p6T`BlZPM ze#Lk4B%nuimK(c`q{>@@kNoD<2&mrMEMpgcyp2hX;oa0!;E#sHj{-Ovi<=9FMAN@E zT&L!pS^JMTUQ<@@`cv#*VW3RN;m(XT1y_@xXTZO`;IpuQ`Bt&rBZY7p0AM?>Q3wcu z-sPNC5i>C6el{!nOb@JS1BRkuUxlxJ@+^_2^kPgt$g3jaiC;}8RLu>r=M9Ox6~*r> zsUBIYcqc^b9WJHy#F|3tyr~U5Ui@CnK13qoxAqEdD|DEPcfn|IPk&rieAl;(-HS>< zJ&#~A956_)tPx3yKh0ps?jc_$L===T3fg_zCR8o$-!N*?pcVl;G9?~GS@HIwT)B{% za(~)agYZrwpq`MNA#pk)0K4O>AA3~0?esO{=fdlk1Y#h~U3+x~!LRS=Dj)Ids!T!WEZZm!ko zH(1fDWLxL^v=>c~!1O!u0(mqj=8>1XLYF!vG4Owi&ij$d|L@~x@58~d*Rl6ZBBOKc zj5^2Oq|(VsIR_Ev*ylJ-X)l#geI!I#*_*V;$X?aA!%>?|N9wF@}Ddv1rr%2hglN=HR9J01YEqdTaFX`r)OtZoi|VEmVfseUtvf)7+Qz z!=L%S&+Nr`sCjz8^5EW{697rs=9?=qI{EWwo+1$nmtvmS$-i+L(41emxcJC97w*=* z^gy(7O;iT#wUX-7Uwt{I&451)Z30H{^@_y-vDy__<;`f^)jr{rHx_PhYM+!{57@w( z_pN6|6v-}+m(a&E&P%*FE}IHJ#D^wXGPB)FKF}o+YW)@}CkM|P{*>|+Ynw}FxVv`K zHH=|lJCxDM7zTyi0S@3ZZ74k1I{`HphD{QRFT_cKD zOnh!)KVPO=7oT>#=p4;|wxT!_D!+q~g${ULRI1)_BgrzsXYk-~0hoEX!$az6eVK(s zHT(VSJ`Q?yTSUq?Mh^QPXQAP0Pm86ioR;&c!uJ#pWa7oqp1Rp`44kdcsKkX=d^y~m zV~~VwO6Afc!2lFrHZZ&~h{5Zwwrc(RZW34^|$-&<&p`%^rYAyJP+CDR>&mm;-0ifu3ay2Z9&$3bQWzRl-d zv7TKLf<&_lwVcF-`=EgCoMmXV_&UBPz1_fcPDJ~bFq%OfAUjSBe>48|)f4y5G;ghw zADugtw(zig)*Po5mfHyXPTR^3>6B)PZ$UgA>DV1kcQd&)%(>gy=W>k}KML$Y* zd!l@x0Sq^#PhCi8eIg4va2lRkLy7d58pjS^3~~?TZAt@ORRV>GMWM!ea3Y z^8rv%^Ef;`^{xa<1bEjvZOG+6 z%NA>&e+9Yu0T|Yogikxz%lWBgD^+J~3|g~g$VC%W3CWs zzZ67wu`=4_rH8B(&*dvG9v3E7D~J=z-ZmQlX5`_v0@l!4(^C4T=`XRfBEF;CIp~Qe zpV!U35^&@e>NhI=f!sCEC|=g7qn5JSTcL06r#j>fb^|rD*L2)epUP4 z$7_+mZi~NRp5Bzg}xR51$xe+ZKM9FRTJy ze>Yr>t(;xEr&7$SOb5~38ZXpcKIn~@BqnMzGLwU@ZChmMRVl?BhTdYFiJHA#B7Hjf z7rmOtPaP2U^9$iaMlH<$RvNoQ=(>&69AU>8#puW420x%MxDXA}ajQshj}02)UN1-G zEr;h|oEFpfv_`VK31vUn0SuK!mu3j{h}AHmg_5-?4UH$hGJgEXdb+RM5+M4f8j2l< z>Fj2`y*j^wj6i8ua=jXO%GR=vi;zlh%6;B6s^_gYIsfI_SMog1A9FsR>CynGT~M=( z56V2oFwOXiph-zF4YVIJpu7R)f=M+Z4MI(yE;48FB30C(}Zrsw0G3Ka5PV?8DV~ zYLX9QjgQ$I4<`2XCj=HZ#t3+Cos(ydQHXBZUd7mWr zxliiDwjhxGc*;s-6UG-p{4hGn{+M&;A+hw~Tz~jL055GLa{qZ}%tej2Tf=vys(ok=K zp^QZyHvT*6o}HyTAP&xZa=B2a@gx7d@aMV}v7xSa7WwPx1b4I$rQRz(__b&4!%|_> zcKHr#Pnm^}`YLC8vOb*K8i0N-x512IY!qaSaM!;9Yc9Mz-GDoP_rHTkLmYR%@=(eJ~iK&9rVZF zw;OKMvYi+$LCRCsmX6JfE>7Hckeg(RP0BC6;kN0_Dd?IvoXJk&iz-o&YfqDW?tE2P z+pc|9k_!GUTYw=~Ap0WdmOnH&Ojq%=#@`#R8YExJayqwUI#)2TgU#XHw^+lI!Ir)Yo1*rJ&so(>%LVv42DS3@&HQ92o2I-P2kt z+G%>QK`~w3^09;SH0alvpn3~vB9heOdjipr&(f@ru$Re*SNgeY_``;f807Jf5_gKY z*4`+DIIr~nTImt{k4b(VIj+}S2XhCg0Mk*2`ClLFOrd#eQ@WOUcSCgfjC-f5g5Cf* z8VawyI{IIzh0rl}PY`o0gZwy-hfm6slZb4Cceb0!!>{+c^A7G8vux()CjF1epf#d24VUXK_L4qh#XBzah~ z5b#bRgsjmAbCp+iDOcPx7*?C5(x`ZM;P=&x{nu_y4*^{FJ((O@`bG%_2)Un1TVbTl zw*X)*Z+4AYse^ik@Aky-VJ-8$jmPJaC$mdebLDzwC`i`$h0}#GPfP3W`K`n3QqEL=OMSP* zB5;pah^$FDVNg2PtDmuxLeSpX7H(}rHb6&-noi+*H2Y*SecKtwbpb>1;b-@R=O4X0 z0>$=!^w3;BQpX^d__SR!4U&M6k8|Dw4{MGxJ5y$(A~issgGR?Emf@a9AfK2VDht#K```f~PfR02%ujztUc1**Y{Eom>?NXkt}}&#_{W zff|PZqVZL$71{l(cNR~y3k1KXe!jIJOiNGa#tGe&EMzzmvtBfKAk4?&jGPN)C8o80 zCLR|8JMfozr*y8f8X>Y^8VSjgrx?f5iM(-lqzpv%mPh7s+dNHtu!Gmrri8ugg;URN z=`I6Buf_2l4Bu~bHigcv^W7Y6a8x{n`Q_?P;xS-pU0GGyZYksGyg&`^Joc08@W4_! z0nGKDZ_%@>2@$m@iMzyhueQ1wdwQyS)p>mAqW7y6F066K3J2A3cpoIEO@&-#r6($( z`cHt*D=K^|H7W}*LIvckVAj6*wC(O&Vw_R%BSZ2=R1$c6|6%8fNT9z#G%Zb7TEF_` zvntq-^qLr$OImCStm*#AfxewLYim%PD$ zZYaKHm5R4%5tG4*Ur#$a!O`!j&gRRv(~y3CnX=b+_)r@Sw4>Ur-({a~CXcKc)lU+| z`Q%92pzJwZP(uV(me}!K19~%xxb1D&a5&bi12oxw(clGQ{ZHu4PyaR1`1lCMje9?p z#NQYG8SvO17Xni!y_(;%Fc9w1!<&fWfZ)b)c95Jp4+A~8h4uia8{KF``EI?vT2eGF zTdnerr%=YWU!_AsdW^TuNk%f-O?5$Nd}ODXV)=cnxTQIkS2rZn_)>JHHIjdVx&Mu8 zeuEV-Uy-w$75#h5u+GjdPis2nfS)2DrrJqi*Znka0~|>>2mkFg2{@J>KBi3^eHYW^ zdEAMEmI`8V`3+axw|89)!35Liwk-TS>@LnCq>uFD{r)pRYYQ}coS|+nRvH}U+Qyw! z84|>*g7{k{SA7k*9b!*3&1ngSRHRmVcw>Ti%FFK4jlWuGXsWPCBn~9=;6Lfl{s{HM z99!wTKl0>;jYUoU{qzCV#pBuv=nIt9EQmtW=K|hw;zBCUlh-<5#QYC;gC-OTL_X!M z^FgNnjCdr)O2!m?NYnHxQj|Qg=bd{DK5^snh!%hnC2I&<#<6gIt3Iot}Sbo3iH; zF$wTXp$-cfJ1vPM51NWsw!C~2=Gz^=ZV9h z=k;+{1u9ftsR zEZ2;A!RECv8}shR=){r)4Lr3DALun_+h4s^jGk=NvA5yK324JceTACn8fdEH6AU;j zld*Ww#cqHG1A^GArs?__kK|ZWK)X;ax6BVLC&(G01~JGP_ip!xC*PecJF#M;c;?9BLkRfs9x!W+XlYvF+S*RZDV&0du*~WSjM(Sa(iBZc=6U z5uHTg74}u6$VzwBHNnqLVWKT!zbgc7Z(Z6z>DWsnaQf*LRue+^h&0_=bfKuUJpzb9 z4IFR);pF>nhJ6=1i-7_vRQ&Aa+VLV`D$uGRkjNwd{;tVrroMg=K@Td&fYPN&^=W#K zoBT@l`u*sZXZBRt5*b^8=h_F@)W0}R+$K|ds%b~Nh=7B0?Z%ZnpS zY;Vd~{I6G-ZeyhNyeGlFZ1K@sJ7X<=h<4opy-R|8uBE0H`|qE!G;7@2lB?{2LWA{f ztzF=LGxjde{CC0?Urm38N&|E0TMUs5T2JO+{Y(FB7Zz!8#5G#%vNZLFA_o(9I?I`1 z#~av^8A|DOlyoBk#l^r&WWuh06P=4sLn~aW{Z~O6VM!CDfIs;SY%IQpdBcJz7pdb^ zyp|_-H>{2kVC=jPhx~w?49_?Zn%64kL`=cVp(+VodFk(q85mi%^wl!Q!bKcHz=WiK zK0+?{(l|7(rfvFRHkf0j11lRyDpPJi^$QR4# z(~Nhp`_5*JJp!0uUEwwS1Gq!|oDp%&;HBghU%GHEB9p}`%y?x0Z6&wg3-4HhVI80c zrobeLq9V&|hUVt}114}c1KR%J?X^g)+a`Z{FVkUIo&Rkb;5V-R&2>_>NK`!swtJ$I zE?|(%&~YFZ`gIeVIV%xmovN0mpW7*muiF{`GMwxY2UqkSwIe$Va2bXhRsP`|k?^G4 zk%bd&`Ed<&MHcP=pdJC6G0k+g2J;aA8+R=h%q4}w+bc+9@|o#V651kQca~jWT0r55 z#8}NMQL0uau+Q5x0@n>nm2qnjN5n$_fNR>*Ii9Z&yaHWA&-q|!o(Yu)3!jRA@`};; z+UZ5Kzz-H&xc}LKt9{V<)p;2_E%8e*68HP*(VB*JQQoQV)u7#rd|6&x*f3w`!nZ}I zj%9}fygw#sR*{Uk9BbO=B9s-H4(42o`yzq5C331?-w+3&9+{p4#X|C&9{m~qAN@3P zexrks`lo65Wc6ol?cYV}DI)MrKrQ2Yw@ZM_JGW7{{*HKX4KQ|J21xi>sePh1ULIoP zN!MsmV&=GE{&+X?ve4EW@K}KtP8Q8OD{$Bkxu-WLZ4NyZg5$4uIq1pkcDr565C0$V zok$DOqvBiEJ|Ji*M3BKeX+PTn(iGP{z??0(`2+*Av)WgG={siCbJa>0Bz&XI3hWoN#YVA(J)LH+#GYkAO9 z1Rrz`S~gdlEC<}FcQQl9!8-m20q%O|%FG9JYkKU(Ae)EH=Z)UY@9>-Mrv{|UoP1Nz zzdj{VLrc&P2bV!~3n5AG(@MBt65t&jfs1}8$#L^4vPu+A+`CQKzmZgvDB%YQny8o5< z$NM~bcM_KY&4^6x==T{!Qgxc&Y~k-e4>F+1(tGVd-g_KCFr!7l9>hZoeXV$E|K2Ew zlq-~Yfq9Mxk;iaCb)Xn|nv~$==W})9U_A**;D~X$m{EJ*Bs!#61(-|J;vC97LRd-j zJ+8(cZhVH^x|DKS=5J70BOQ|~%JM1=w~+rRrn!du*LCnJjwyLCy{RC#kcVN^K7Xl) z;yBcCLpDtr!H)tnPB(6boXHkw?068&$7nhhajK;AEHXi+bZTbFNrL&8;cn@o<)n}{ zt=jSGehcWZ);*2@Sx zg}|3cQjk2dv^D$qfxwahc;rbi-m4+ul(?+75dX_d7thf0sc3VARMU+H}nFE9$6ASd;B8``EkV4Q zW?y=}?4`3ZRy?LCvfLt}5J$=cQLQ~T`dc38CTKPpzf8c+)jbh9LtKHw4uWM0pU*@D zBv$IBx;=;5l^mI!t~1|1IJ`VKy`-!gYu~u9H7m6fzN!@3eWO7j-A;mY85s`Z8mBA~ zD>4~=9Py-E9g$>84Jo@Ft$26;mJvk(lB>4v7) zFIgjl+%ss6a1m+Y>@Qgi#8&d3P5A`G!7>>Df3r*hFc*yMXblGr#+UIRHlzPJV%ZhV zCo=C>Y5JNqCyG6-de;8@+bPmzbgu0sNkGGQiHc;^ zd^H}Z@Um?O@>xu5orDN;(K|*^YXNMRtCF96&;MzKv3?BKrW9w;az8y=M;rj%`Ky@d z69IJZKD@^L13&J3bd7T`DuEFg_&OTF4=Z(D!Hsl3$Cv^ylH90)uF<2X9n zV9I9df7qJw6{X(WR9h^1$ms-s4I>_1I#Eb<=Hz}^<3dtod^Rff!Of+)^|kz3aV;y~PatdG~h0*kQXT z%n$UrBORW?02%p5)o#R~H9Il7)m(&J;>lMTTTzpS;uZh9TlOd$I7smUo=w=X{HMIO zf7ue7Mg-FdFW*W7y&n#N?=UeG%jZ1edmMw5cc)+L(Beh+R4Om$*Ap*NB?tzn%vrc< zCYtZJ$)$OlM|h7C&e9Mk_$Ds7?S`zL@E!y;G`(r_>R!N4`?;QDEf562Q#$e%7W$gj zDCCTTF&Ch+F+TNF$w@m!-+v`M)=^T!Zi<`o z(8gpicU@FqcA(xzAds@X|JA%5ap*qqlKCt*-(Jm$2hzUKHX&^#Y|4=1>ET!1rb^(# zpHG;aZD^By#XRRip;~B)$e+Sp!l!SqZ~d2e_Gvn-Be*0si1KW^E6zpU3RMOj*nd9^ zQKNiY4K(YC=cs?Ai-mg?Yf2rza$UDk{Rr3`g#bRWL)&y%1&ZEgp3(fs@zu?bfl-P! zH%IxQPp5T3F&znXTZRzJ{R|GTj?=;BufY^Cq67VcDDyE%VYYk zE!wya7=yvVu;^uoXRZmPWD=kJ)IBYbcnUXV^0v!e_37(6@<&tDi9)x+Bf31%G$n#( zHNif|`7Gchuuuvr|DTL-0>#8<8Zi$JFY#R6N2FWu!3YLIoS7qwwh{7du(N{bOTly` z;g|)!H6%~1JYu4=&`q+jmWy#Yf^%OrC*69HBmC0}lq(b5=^YWSCa3~KDY#xE_^}68 zoKlH;qYs7%U%~h4&?R1Z`U{bzHu-=tBF9S`|Oyfud562FbXGJR9@f^@@Mz19|8AJAv8-84=lfFa=u)Px6dp;hj4>XMW znP*iZ#*yEP&EbSVImN4?)4=X}eu$F_*PF+CWtu4|df_X~0?&Sv;Us?-)xUK8J+LXQ zUjhnq>m>^9S>Ue~3%fBUV-QJ#zaqlvp<15_OI~4I9>VkeOcT>t{Mx}wXGl4EH#(ta zZoC?WyyF*S425WlXPraM(b-9JAi9WCiKJuj5`Oh-uHuM1?_!u+Deqpl%pPd3)UH5g z-e_LM&aS!8eqPHfLOBe4V%{s>GR;@%3S#t{#9na_ep~I${yPj!P0w6>jE?r(a18i= zE9aMSqs??C|N{9wVsoCsTK!`xY<@w=8anb zn1mR{_2Q!eXaqX(aXMF`1um(=lF=lXmSdvwZMQo6qS##%nB0&=+R}&3Ul2N;R~S9S zBxjZHv&#iew^nyq8mXKUvE-{;R%S^0KMT7y!ngz=Af8#6ib_i^@J|Xqlv2euV0*50 zBlg-$I`jRgmrjZ0yv=>lo`XU@2Y^nJ#`Ohr)XRK<47hh1G%%PoJy_QByQ@v)yy=0jllKrrKM z{GsA+TzdK4{HT`2hG;RhPXSM>#$qO_E+x>#Ej)0;+S;kr0am0)A9N*$s@3Qj1 zUM!wf)`3u)Cma0cj1<9VHlSE9P?nh-P0u}Sbl8XW2Lc;T9Q*y}77xmFyTxxIn|NJY z{KT!$!t{_W-FqQ5PcQy=Uc#CWXZ)Exh!>8^`Cq_3^F$kq>!(kv-#Q%(34US#!Apo| z6=dZ-Seo7A%p5lyBqfbez#q^-$Bw46Qg9o6zN*%lXQy&8yaquy@LZan+7;k?QRAqb zmmvj)_wNl#3rh!Omxo@&T(s}?U^h#O;f0k$ihOP;v!}g8l@B{k@J^6D92w^oI=O0b zfnS@hReG0Nz=N0An?r0{E>`Z{hU1P>9LhC=DACr*y?7H!Mb)d zldq?(=1Dg#O1^okz?DhVvC=}$I-7(g6N4=Hq?fadxpg8MUz?W=j5gzUrtRXdJpKqe z3k!f9rO1ULep!`boa7U$8#pkbo5f5tGyAHEWdLvvnV$K~F_lq;7JA@TG}2d+t~{Cd z?^4kini&yC5bF?uW(y}yNRh=~-6Ch4sZTa0mOQL1YJilKA_MB5+^;G(6mN&74JrOM zWsd*-BJKs1nrq5Hw;~Uz&h|kq06G&h24PavqGklKq*>@`@oi2DG2oRZMEv^=V62aw zmhkm5D%8|Msj3GPEd_Exa5 zi7v)NqYw4&14EDBDMPrE%-qnKbmZ&d`s&q z`KCu~ziYiz?_%YDH#%wEfFT3ABDpUi5TXiayydu z4-s>$#SC3GueRMWoew<|L4M)n&10%%zR-Q(gLtR^te^SME|lGQudR1eNm_%0 zr!}y5oP3qo+$TG$MGoc5Sv;PSm48~reqs7jr81c z_-fp=u2iN!B%VB0#@I9|kFX=YlocaOE>i7@qggg55;Ngf7DcEv`Q{10xOajEZoPuEoFq3BFb!L7pbJ5Q`mxIendU zTi38fsHg(-Bfuj(Alz4bIVcuiFbn{9C;>tpv3oicJ)dWca;Y13L50^p9{JVfwZkt@ zhaHZqR&C-tDc9qtIXV70yn9D5+0BihP;TMun06XmDNg* zc=de}xOj*$q`rV!oY9qEFYECm=~B>JeW!g%mb@qGJ;O|g6dcQb?j{O|W!kt2=<~-& zYn#-z1O7X5@S1#&RhF$-3eGX z_Y}OL`kd~orEC99p?_L6yidSpAH^n40a+UCL33e>$%)UOl>q=f(gG~Ef{HO&9%__h z%$#o|HSoR=FLjCFhg*i_KEp~wJCF_>NR_$Ts}pAa? zFB!w~J`O3&9v~VzU!Iq>tI(GCu3t#s@7*ANiUS{MdYkTb^%H+zHhyiWwlf+d8r@l4 ze*s5B)N~@e(!eSx`0tOGOQ`*HZ5vJEdL2^cr>gnSdQD@`>jkiJ-d?NFbaFrAhHNr4 zmSA|-eEA3~>@BiYwh>o)U-`W$kivE$2Xbv<0@%3@xsE6@sff$OmFdy^9ksBQEnD41Y;O_SGW%>`1*^P+2pvV`q&Fd6#%iOnT3n;|^! zmcX$WYNE3kC3G5Qn)gR9ni#8pwN?BPz;P!F!J8MvL8uFdjjyris1m$=Vr@c8En1rM z{C5op&rz6R+pPmu@lG!fpR)T#jfO`GhCcbP#DjQUJi?5eI|KKzrxJ>fRL5HQh89t7 z5S%rgdaY6ZT@r(0TUheKcjaot(s$3&eXJ9Mf|GvZnT3!4Lcf!g_TYv zM>7tfv}1Wj^|7i12#$Zo;TQ+TLn%iK`J+3>BMyibqsBycR6RHV+lBNFVV z`6HDWyZ5jxtU#;@cE5CP^hozon~BqTlce;!GK^Nut02v{Ga^>@Y0<&cyxa{tu6;cA zr!$5U6M5`i@7#FsaVS9Y4d^7tADrv`N{y?mX}^CYR*6y|VUBs7opub9+cDle=4Dbh zAzftGL${dLq|o^`cVpKVh$GHs(A9)^&>b2bmr+tej*Uuty_86=R%9IY8mI6TYn@ld_LDZHG0FJDYM-?6^thpEpJ#CcCEZ zD|{vsU;8LS&{-&b ztFj1a89BuGFtGQ}M8&T*D@EBWZyy~`D4ly6uwDj^5?84~R1nSE9B_u}bXNS?z3--Gx zhw+HlIQxIkMeuC&1&NGJWn|;s(C56e^KGw|#wNmQN-|sjG7gWN`R!za*QCGd8GG8Ky3H*9D`Y3%LpZFNNH~a&-UQl5+{r~v@x(<+kQ9|v&DSl%)=BkOq@*`6siG0f0R^OVP_AN(#mnhEm119Y0Q2dp|CUEi?%yd#;D<>!w5cFA2$q6zo!ecM05>?b= zZ4u&5OrIeKQ!>X%bTm87U!lMfAdN%HWeE(QG73*MCmJ*-lfTvUifU@`gg&cXB}%xH z1#(Vam|rXCChddwU76+~qDGs=VMdQ~m{Kb*MleOPg2r^;zuEV@Ci{IX4!K}3)LyZn}H$bvG> z;d2?pz~w(Yw8;z~ARW5@`=++{$7{MfKt4Qf1A;JVJg8)yD4wAm!!uB@lh(jTQe{Bx z%|Fv5hbKkl00$irr@cKTOIz4|7vpOgZ0#l9gfw#VQTnq|mdJSUPS1uAb4&jdX@fsv zv?f7G(_0`lD(^iWzJ!}=8S6J9mSKt!gyoGz)xi9o_fycc4*P$46RoLtiH}NcsGz{| zV(eKblXnV5djuxi!{?Umm8~H7>5r-)G7M4BEIQf#&t71VH8sIKDBNTUFk(pX{zUU! zSv(Qe@An5e=vW6xU!fVG#V590-@bcIm=-F%{&LB@kpVG{Pq=e3jHe$=*gnWig%Bz)Q*m<@$kFz zK4Il$qMKpkIm9Zof<0q8*&BNcKScJ}e^8D}?OidLJSX$+uJSqAq;$CBl(6#hZZ|7F zIB1+bc`+Hg1gp+yv!A{)C|(8WgnGI!DZ1W8#J9f>z5A3%NNO-W_s9>U#@*AoZ|)&UYi zW7$#<^8wm+m7uaxAlX^t^W|xcL|scgf2n4MvJ>vME?}>z55=i3VigsCnEf7Gx_JEs zra#BT-AL+pLiwAA&hBbw zZa^P)^)IhVYybn?OfXlb7lS#4B8;$-ELLEk2QW44S{;bI;()ih+6}Ab+QXGZc@>8OOc3JNdoOsA3M~es(FRYN0gXr*RcJ3BRJsR5ZCGNHo{?rf^~JF7OGAQ@!A%zLWd=noL)w`>2&GVq@2wnI z_+V~ffjWCv5a-MKmmbOxQ|CiTDt4|Qvq6k2AUj)?p4XfBAPl{wa2nmncKd&e z{FH}{8nu79P4($2yyGhRWh9H$-=W{N^r{RJ>nm=zqu3Tv!8lBDcrI|VBhG_Gus02U zPh{jmqWbto=G0Rv~8h|5uHjQh%)%8Qe`cJu8H zW?LC|$qiMr={TG~VBGgP(5qYMg3Mi9h#?JY&-=BPH(}h*`Et+ZWgE|{V=g*&Hk^7Z zQu5?7?>INu2P2H$5lFQ3G8!M=Qun0r$DSiXDu*(94^!p6}3`vRr)a<*W?+Z#eHY4mr0f>PDXG zRbr={(Sy7P*gt{BXvPWumZY3zkyVK8tzq#(4aD&{1=|-r8qNXfI(Wr&8pDA{~l;_`8Yi*WafB?T=>rjZP1@i`} zeLH{i%g*7x78fURB4)aKAqPN5fOtY~%*6)rnFD+crb@5fxZO*rB|zsD7-+x{83YK| zvOORV31QW}1z$VO31QHhi9gaaA~C!o?f2`vKxFPW3H>9&bSV(N4GglK(D~M`@k6eVOlp_$(-hJWXU8RhR8q1yTl;^G2@-$J-#LVD%Hxce*xUCdGwr{qPA;gxe zcTmohALE@=r@tjC&vR|BvR4D<*I8r4ioVa5F)jM`|GkX*U51o^dFBC5z7H#JJhV#R zE896CL22v$vG!kX$ID<4hNRDG?w#w+SB67b?b&ATmz8R}@EK8_*BQ#3K`4~72($h7 z!IG?@adn?(p5h~HBcj=aGrBYA26$1XjwJkR1p=2}vIo@>q+g{6&HbF5-Rr6YM3tY0 ziilauwZ-a{wxvV{tuLW&Je@;GjVGw{4By;ZdpcNsfF@FGwFzcd)Qv5NM*cvO-hCbG zWH!7vYM;|zcr4pK#7MKO&UWDcp#vQE7bek+_6iOq%;SB~)Nb2Cp7LxH8ETQ6RP7t5 z*5iQglzqFaRz_tLOyvh`UC3|fd%Zl7(5EPV?n|Wrke3Cm9f|BYb$| zV#Eg5{&OYH{t5Fw)T?X~&i?d4X_8p;JsAM0d07s+!P~e26ze(D03EW-fJ2#wROm>= zbcUx1x$$_{@VH0iKhJilW6)H6^JM;k#&9X7uR`PL;`=T~PDMbO)};~kw}ykF3@5U0 z*{{}fTNq$8V+DN-#I>xw^TEPvH6{Ar)msrI6pGdA;5qRs+}urAthvJYNj1w(9mc@KJ49oX$2N=_pjVRK`Vb z5LK^|4xfPo3&qA`kskcdLXD`*LzK}Wn)%;f;d(Dsua?FD@aDft&v-=Rnieet?sEXW z`Tk8Tu=9w&6!|!Ldt7em#Ch}0MBEVF2AAXnT?`bM%xjM0WHcMJw_jO1Rbxsen(BFnNLhVknX;q!B_`dx?K z2rHqr|2MQ|<6pTc1Ik2+*;!>tj5X!dq7E+RF#C_+zV3RdEslMzo8V&XLyad9Lmzp@ zD%YByxZ34DoMisB8Uz&+-)LM%#E>At0#fsoivC%Ck01OEzu zAe_bFS^ST8P^WLHM3xn%#``3d^5@9BL)Oo799Z~1YxbQ_sOkFBgW47IG7&;@_$B%( zT7>8}V(vD5CI!;$**Lhx&^aDIeomtPlCS0uDcr-?W-m&HhD=jQ{1fdum*>dwz(L@K zOBi+Q7J<6eR(;6N%0L&70u=$25b(%$w**M>i70pyA>Ws|g|72I#w+0CsJ2(kA3tA`_nhh=K~W-HAtjE?B&CZvU=cH5t1~;Xi7Rg7?=YV7drjnNC5!aKEB~N z=r%}$LZ8AJ#9B} zgoQV+O@0u6O*5M4)|k&OyXiDX0gx<>5rORY*#;7FTxRYmuhS+eY16{F!W(c~v%L&* z@sVT5!*X+-P2uGKGLwz|jCs&ii`2N*r9#2Y;wfYY3v8AbYXqs8>K?k$rCLR+W~og#Evh#4 zZDY0CswyQvzdz4&?{n`T_de&G&-uLH?-xf>Vfjw1Cx$;d!p@_%iaqq?SoGas2>OTm z&(Z3O*}Ff(Io^EvGvoQMj^6mTio?EqpN92<3SiK+w6!lNGLr7ORt1*vo_Y2iB}()_;%j^>tbJV&K=5u@bWp+`+eo^)QyEtUra#c zRNm-O!vT+-$%z`U0^TR(yN>8HQYNNWj+*(cmVtn&iTACS<%(F1iA*gJ)sMbN+NUsI z%xXBl|HzZ^_!H6_ve<88gZa`ENf9F29`7vtUIh8O_efjZx=UQn{WxBWwrn2^nyGFr z{Lv;$rUiU-6!yF^-`d@ru`|#?>bY*x=RoNU^p!*FIbT}mizO)AU6JCD|K#PmfhD)N z_P{(BvbEET+ zv6!t$iYH{C(|g$ciy${ntoH7%&Mj2YoS;mFh1}~nyzi2*8KIfmP%k)~|pRBbBiy$q)w7h?>QDSaF^TENN>M;WP7xhe7cWZ`>pm z$gN^D1<}py0ocZsWSD&KrCQ3# z&M%1Pq;39R>}|Cpe)ZXoEF3CXx)KgSgAQ_&$v{l!pMyC(p7;oUKhRW({5m!=X6b)# zSZsP>8cZDlw@jY2>6iOx`fBqGDD+~(@!qa4XUYFX={y*i8rA`9>WlSmn^U+0CbN5Pgb$WN1nVv>_>N*?;wl8`krU36cOq%Uxz~CoXJ>_J{~NP&oDuG) zCg|QPQs58lA&3Z!Y8zx}Ze`t0D*|Wap5=xDmjruG%a2wto_M`|su;CgaWO!mEA3TU zNv@qpXj|ZS@L4*8_=799&*KywQ$7=M@!xJ!keF-1MF_bE->{_mD(Ue1jA}U_k^0Dx zp}=0&z>|K?BHjx_$O^I_z~Yv~By!bM_f1>UO_d9h)m|3ZBWCLhbsrvsCRDECTYBw2 z^c}wSUe@HlZ%|b~@;AY266H;NVYtd^Le`er2$$G?<}~tHw@$}i41$MxFit1O*+YKh zGgbL>K{hsa>m~3}+^P&CGV^p>AkEX-tgMdZqJVc_xpx`CMMQGm7-T!SBrSRk*CvCD zUaN_WS5S@k>oc~+-MC~$RK?06^Of2OQ$f(~1>6ugw#m~`ViMN%HP_AT+86S2aKjK< z&E}@MLb*nj@aaQ65yx`v$xh@zK01zz(qFP;+u)#w5$j4uO&IQ< zf4e?`YCtNo-4@cFL%!;dS8yI1EdJ!xkuA)I-L$B9wBE15Rl9n6ppicqTkujH+awmU za(}TFJ3DIQ=brg)$^mR>eEm5KnTQwKxkY8V&yBSg?MW#!ej>={gI!zERd39TV9>u0 z`96=kyBse*@cZLrs>joBSgKV^qE2|OJYdKN#tNkwbQhJu-j!G;txX_ZYG0xklCS)& z;F8F9QV5i?o7R@=hmikvzn`V(a`Y2%bLL@O+yxl={zX(sbCf9z z$jC@~3v`++CmSoqaFYpqGtKON_mwPql7kS8T%c6jYe> z?G90x@3~8VaGU)S-YjDuwFJ6pf_qBYOjqj*8$|&8*c^&IzONE-cL#RGMUhI3^rkb& z>~2ew>=>eQ83oRyCXCq4j3q*N|NeZGHIY=EB&ioWvE;K3RA^sI+GIz8U};To&GHwh zF-c+>->GyPY!6e7GIFtK<3*6mHo$mL=_~l%#+DCEfe*6&yrbY1iXYNuvlAS{dR8g* zXO_&{K;Sk+K1l1qv*&5$9-iJgTenkSEqpZOJS;_W);~5x!=~kR@gI*B*@>@co}0GJ304AKdSVM2`Vm!;sC zjOO*dgq~UuHYuS`ZQcDJ=PJ)Wwk7z(yz$R9%z*jpE|~s5W4Ayt;Hl6N=(GsQC)Z}< zW!cNM3ipyw{6xT5MY4l6x{vY?4B3fdo%b0>edK>2$PT1SBXJ6So1 zynTy4yi(Bc*`VJwSd3pTbu)>*!Mz>b4-OX5uP9%~3ZNCO9p zp?qte^Wb|N?K_N3rid)#Ti6LM%fQnSwl&>yBqrA;5X?lk*jnw z#9uvGEZW`R3TFJ*@Hk^Z@#25JMSmXA>56C@)bOHB&fMRgzrU>nB8x82fAe3Wj}Xqk ze_|wO6b`#x97b%Gcs8 z7LdLH#*Pc3u)>BE)m1b-_LKy9p=}U1*`J26+TviChc>law8auJo@Az9!$QUUa&`Ma z&UV~Lp}Ds;0@+~-o9{0ZaBnZR#uWA&G%-JT;;l=pz?rmxTo#wCuMUPoDm>P!ULU`Q zCk{QQ4VrokB=Hx29(XsUs;b%bx=2QnFY|uQ;JJyGWqsS^V}4D7Lm!`G%x+=K6zvk! z{>*v2c60TmJSDDq3>s^8y%~ZGFE>gsL+o>_ALI@E3LWNr0f`~9WJ#H0?62Ts@>b9E zz`XuN@57nF(JWK2<>1Bx-0fS{3<=bq_T`vhrJ=Z-&dmtEI4y7I9cKB5~GhALys}5XoAGzVWqxWk@0Q zJrfJllaPZ-fMd&VNeXg{MG8~JT&^dl@S7$_#R)#D(ACSa59CVbC8bJ`hn`u1I&cR+ zyZq@IZNQ4Ww&Dn@>FRf(qI0G!q+9x(X2m6^)~NSh`(a=n@+S78$l5h_TizlIbfJ2= zXrqtS(B<2owRBDpoil$(h11CS2g~Nus`HpVDp~B2c}rBYavqeurP5bf^hf1g4QiGc z2XUNDb0&D!0Fxg&;By(}PkqXIJy+8UH9L8UW=Wx}!W-Ja;;I9+uH8yDL_3mxyc zmeiw&RW3%HtjizO#xhF}`NF4AaBOb)3LfQ||zz^FaS@lr5;s z9O!a6UVEU}WOnquW!N{=$tr*QcF0EVGn~tU?87abV_P~;sv*i zInsXwg((m}rLBig>j-6EiP}(>e96g5eAtzV#x46(g&zZ}TySbGXth0ZR#j9_9zV!z z1ivT85*Nf9#Km(n8^7Gts*ux?Frzu5^6dMWxbOmzI3>@N) ztnzVhQQ01W<_^u|#;i7jJMCTS)%I+Z^cuRoKZwxWkZG@e*dtwp%i{4@Df&>}&!Y#~ za7eg6Cp~|P|CU$&Bd;|xYpJIcCQmZM1J(rD*LlheZoTHE8_I$f1bCpM5q$hq?MsDQ z^h-Kay@@0vQJ9ywMQFX-p`o~#WsEba;5D>>oShX^5qt~;bgRG$J?A`bv1%O$`DVe- z5pq1-u;5%C62+p0Pxiss{huD}+8;lOY~8uJ+q(ETnxO`mleI@=_g_+;9=ZDsYGNb* z%TfSToU&*{g&DN&DqHGx_~J@HKRo9&Jc=tkdY(zFTh+5cs_{SV1!2e`A-7xKzcQ_709$ z|8H5d*xHU+>?N;d&n&SXIgM7*6S%7h#Gq9MQ~?u$UUv10ms#kRNg4mTJcmjQ)C4zJ z%|2oM7Qi=5^&0FK$E&2{N;Zll?7VLme^-12801ziefH|{$(-OlSbPV5fn4e$0(5n~ z*7=qUy{*#%9}sl^an-9J-o zRIQ2K))kWXZx4$DKOXUOb2G$F;%FEYtnAlIv|XlOc2Q-g%7E24oi#!iX*#eN)tThs zz(*hP`8Cjs3QpTR*iIHzN+{%EyO*tWaubNASzm#qgF(1!2HbM+k^gwvsN#m#tVZYB zq=4;sxm2+dNsDlKKPlshI=9ZM#^*;7*30v)UmxvBTxR(WuYG7@(6~oHCq?^C1#Xj# z*c}9dkW!!JiARBAze|oGCA5l{#5&1<-JdafC&Id1+3}@_ca^1`!>l@gl?Musq!m@W zx*UDic}qK1JB%}wAb*^lD zuMWg@YG}lWw6-Vk+8+RY7C1dbvOq_n4-^Z>Or&x1hA_vAs@Db55S15|>zPW`Z3vMH zyVvw%3yEY{*D)-1mFRAwGBmkNZ11H_B`3|jUp|yz2{yJCgIP-$HL~Js7^4$;%ZeLd zlyFP2B>h7FmAio-m;bb*ZX{X#Y`I?rnYZ970fJ>us)GLOV#46~(I;Y^Untlf*tK%2d;VTaB_cU2HLXOZbveWkmwc5erCiK-qcwL~*ml+JP zNtu+;6o--cmIqb<=EShiX^RY?$?5=0?yEjg4+Y<(X`=9S>@H^Fe9%8QPnZAm0#dm>gGF-k9qv+<# zE8Qk#`bra>m;XkD4^~@!T&jZz@VoE{YFqmkBDRo+?|R0IZ>GrLwmYOouS_^*?xo&7 z$MM0mt%iuYKxxPB;M^{+XzX;n=%ltItNw4!;>5Ebs!=&z7zM@`7LEJ^p7PfTF|Axg zRo<%AA=&q?{IGsSi5CXPj?d8-SQ5}RhKzetmM;haZxO|v4RQ{ z^Tt8vRc=I}`BLFxIPal5-l6COD%v_+Fw=y4J2MGwtGbsGE#`OId-hH#(}LsVCR89; zOV$!|m$Z874;ZlQis0p~#Y9;*^nVbQp9+*Dm$V@+$b)u_NJ#>@f*r)0naSIrNP^xcQ;y<$DERu6LlY8+ zO}z0q_EJ^KvqO|7Ek@8l6{$=wN;d@s{aqfncmj~~WQBOBIEG4LI@IQ|x#;y+b#&Ds<>$Ex>CU@d@S%SyN9*Q-tGCl#)3QzGm1Synufn+~8vb{(L zOE83&`;Z;-K%?FyI`{81QhfytQG+dV6?)E`$QI}}hbqY#1jHyrSo-EvozU|8NvVKh zI^e8(;jhgszgGf(**v(*+ULcl@?x(W8*AZgd7tx$g3*8S4UF?|A2;mM@2K*Px>%M1 zW$l7(#37Qh`8#Ok580)1T=9nz+9ykTYvaY7T?CftQH%-(ErpwYhk0C%&Z|67gV7eS zanHB~x0Y*`fctN=v)#F2+Z|!DP8J_RL|84k#|{#lkfMeAD+F(yqoN`C$^`A5Bz(*z zq-Z;@qhGg*V9(AlxI<^d-7oC1ajS7}qe82Uerx8Ab01B$f(KllFP+~I7axGV*+l)a z@gDMp3-0WWuMqqjxn?s2%OU5DBA3`pKKZT=Y*mLEcowPj1BZeepa?{SbP{7&h!%%t zA3Z_qGoKDCU}X5EbJGmN8X+aAlHPo4tZKPMgLHI>^JbBKe-*d5YH4lg;MRkx4Rj1# zLXwDV`f>0d;GVZKgcJ~Y6$|0v1V>l1aeNoK)!-V9Ik>*7FxS+VYw~evTZ5l z{A)~b5FvTA8#0}j=AIF}EF&v62k5~p-k3_;c78jk!wt&Q=^)<;^w5q|AD5OZ<) z(Rnv#%V~lyIRtNRN4om;;XmNMQ(^bHh2fF~{)IKd+O8T`98^qy^N7S6aDnu(!`$as z7TK~@kj@IR_TPp%pBswlXR@s@s%r?Q%E}wB<#9QLoLDGFLHcA%?2)|H zG~>VXei>Xe#u4s#z$&c9&JoJ0(_YK+fa9tLw3oD5`be=?NB2NEqgq7WOh^x!@gk3` zxYM zXK#IxYS8hYxb36Nfu9PW6%^N4Kn;zY{?T9F}`C+2|noCv2uTW~2apk9Hrmou+ z*5^oagafiOz#tINsg6{3vY+R$&n-hL)_QVNqKZswQtp-I_vRCcHs(9h*o*(fVMN3L zVz<- zpxWob->G~Zr}xow|2Z>;RWHs%7vDxzzQ~? zR@)>yegEv$PV+Bgs3m%nxhE$CefVy(&V4V5xbT2oNiP234^DM$q>Rb@u#Q-|X}TzI z{CkW3k={EEYt29l0*R{^+m0&tmm=F}SxIqn z*=&~JTFklL@`HmF{lWr(X$MHZ#bo0IiO;&WqjI$?h^A<;2<(w}rrTZaq0?|t{Nu|l zW;qX7-ZG!odAu{5h%BjQ{*7rVsN{0f5w7pY(FEovwVaowV83rj7v zC)ep3k`q$ZazFf%no*_K!(2$=JodBa3VZ@jCyaVswAZch$ z0bQ9Hp6q?088W!YV~Cf@L}{v@j|WIy9_j-kOJLv&gz9B>OJXKG`)r z>;fBsr%ex1(?l(Qdl!o&;0CRJ$lh7E14;!P0LznkaM0dzo9VT%xSPZuJ*rt=C@6!sGU+)=Tp*ouJDoP+x2 zMNB?JNhnOqF*`D9VtfgfjG!7D>nGFn5o1XnE`B0XF?KAkzX>fR6*Y2h`yMo3kf?%% z{8+5EO;9+F5;qdwP#t)r`2MpEU866mD)h&Ha_Agqrxpjit{SGFs%#4~;-kJ=Y_cpx zpccTe*g-janWcLat@^YN0j`MSFTltQ&%`9UvE_Jt`a@w;HwOe;-c40>QhFpW>f(S( zht)C0SRcz^?&#Abd+|Vy&65If-=Jkb^?c~U*clxbUYK7^s?K(dnWd=7?r7As>lBW2 zaqC(}!FQksLSB5pT{Awvcewm*9xhhH@y8b)qTygxCs6PJmRQnSc6DCk`TIG>Gks#p zQEob0AE5oR5MNe(NUmFmyLQFX?qXm27$3SbQ{xKS>Qu`4vQAVfXS#%SbB@n4ShgH=tJOZ%EO$}|R{ zEPAs@f%!J9rN$rsU30i>P}2NSI_v*OVN^c z%E!%xsDbf{{q)k>Wg_2F(tl&k|EtAYs` z@<)5Pp2K@>aE0D6YY^9+XJy$D6aF+2H9oHESH5=T)cS@nBIEY}2WqOQG4Y4tv1r zoat%gBEDk}E8==|-aqRany;=viwQ3FcUX3kS6rSx^XN>EUhPS@YCiu*8z}RxO_m(T z83v-?f&v`N4Dm}-w@SGV=Tcz0++BjYS|zV+jRJo)tDNT^hVRtKz2=0u>9Kjnu~Teo z68mvCH?eNw*KxQSTTfkg?9(}x%;~=>j}_k1db%J6DJljrkM)l{AfRK|s?5MbS{W3* zu6d|QL0|*zK#kiSr(+N|y!~!Z)cSR5*wW;_@~KGB;UNxml4yEKP*HTO?Rt&zkxObUxp;xW+lnth-+ z#i`^;KvGQQCHe)`1Y*(rMHAYp14NhIDzb&^92J(WRH_A46q0XARE&ZL^xFky462^f z*h)EpMPUz14pI_KIT~~5TCah&W_kOjN2));DQ0P(-#1pHz`V-NuV4t< zaI!HXnIO!(MLY00iG`_n?$H_RkmH8V&j9U$JZkCs^^Gb@%k3P$R(3wO+y=MmV-^;n z&3Ag^E)vIP0!6R=T7~4!jHx1ijcmLo9h@f)+~&G0zlnLfU=vfRaPZF?@3(ZdTr9^% zohhO*Yt{RSXatdZgLns?(PNa8T-Vyvx27U0W+iZ<{ouXIoI=WrYtjfJ0ZAm>NH2&z z3)1Ppbh)yu-oX%i8>pq-b~jg%Qcj0NvFb%iAclfHdRySw@^0L6tw6Ct#hb%Ko8(5? zBX|Dtb&k@JM6v*v6ZNK|*QP3>C+Lk*9;Z+&C0_YjWs9WCM)mgZ`{Qzv%2wz86#|(a zp8+r63Tr}Ba9L2`A%H#x)xQ0eKS1h7@ts`s<9XG$s$8yDht57>*AaL$&ezC;=iUo; zdYhxi)q1zP_~N|+M-vs~A(iLeD|?=kb1CZ1WCjjwzmxk~=Himo@3$=i+)WonBz8LK zF)Qgzku@wc(&yPaBKY&_z+kh26*`}C^qyQ)F-zi*4(=Jf()>RC;D>?P!dhCd&~Y#G z*=kDaE3tgq2W;^uY_zkXXR7299kQ}d%PrP2dx{hdUJ0rfmkFQ0z^mN*U4J@92`Q&kiORqIE;vWDhr6aFoZ;)?3O!kb_t~38maqD>V22q z?4LXSH0a9m!RJ<-a`J^1S#?|y=4yGd4X$(QYyU-rC-(5yZ!<4z%Chm`hKnt^Vt)Fs zhR=8S62VGKNjpEAWq^N;A)2pVXy=#)xUn1qlGV;5BTx%4d60(f_liXOwMUXo#+yle z+o-fZJ51@TYgx#`XQ$$6h7FI-Tg#zL?ic>c`0W~H428%`|j2x zfbimC(1P}wg(pFaM|Wi%HFKvq8WKv-)ST6C)rV)yRKi6C!t-Wr&6NEZQH%DrEMqk@ zi#2fXXu&;G69m&llo9KqWl8;uxz_M$Ce<{_+*G6tuFt{P=b%J1h7r5TW+LnThj|Ld z?@~@K?3#isPzO zqUv`xL2Jg`Bth;7MPU-;0H`f9rfnZV`V?YD+N<}|@W(LZ=tP!htc%vJ=g z*C7Nb1`Y02dxbW-xDTA(;sz;bhDnxkQC4)8;Q1{9&MbTDa4MI+U0D$*RB}UO51`>A z!ruOL{0<_;#ht}6tq@UW#33I6qDFNxtF6dwKpKoVyC|TJbXj@AN5G!=Dru@rzgDJn zCU*Nq;g3;sO;J(g{kk)CIRxr}x`;4LFg@_aaqDJ-$Je`Z{wmmGkd2GB>!FcsMG5_6f!{rqD zrT{K$eNeH6gap>_w8S?`_DFO1s-R-rNm1&+p<6S?iC;_)BZ|}pZkGiPeoIOi8~kBy zGR-}iL_L}O)5Mpm>h_2vsEk}N7Hzwq?F}5Lj1ifg)hU4fT)X8D&r?K|w2e25Ny+90 z#x)h?C0fy!@GjDVB~pkn5kPRFY=GllBE`a8YcA_Phy0l$w`8;G-D@oDQ43E9dyKZ4 z0Xlx~wYF7R$gNl}3iBWo$&_9e%RWWwd&p}x$C>%^2O=$X9Y@0L(V9iG^Nyj}8pzOR z8@|CshJv@-w`AV1ugZD$BYNMghiLu;m)r{D?oRf`ojDv>1RciU=&fN=Rd;9hyl2PD?zRG0TO-(9C>cf*)sValEcuS}z zS14Apai$K*DN!%76Hl_5H3)Fu z3yZCRvrWZ>?y}SIeXN!Qj|*ZAMBhqu%$DZAutVOvrsaRL*eFIY{;QArB=UDq$5m_q{gU1N#S+Ad_J<+(#4Ab6E#K}c zq6OhuIJ+6T0ZLU~syg;iF{iWA>D7=hli#69ox)bCZXnF? znGMn8+pMBoTJ8w|_?N?YLU zGyv?Ytn0QIcnISS@cY+_$9j&`)>GD=is7zZ!E=wYegG2efv%s&9gSe=;CEX1<_R1) z=ifgkSU*d9#Q>24=OXn8BEev{F3ODus6^c`hZnu6UG=MY^I^)0 z*Rto=7Rzk@Qx*KUQds=GwGe<`T4GR~vL!$Dm+ylboP>6!86FdE3S_Z_t+e6TH%(KM zl&k#XCqOOa?cd5#a@J1Y#j_dF{gfi%IkadAm^G4O4wqWiRc%g)*MxV@!MCpKyN8yo z>U5V^Zc9%9!|=$xudhYVIK9UL1gVjfRe*$$xG>)8yM|<4cz93}GZFxTVnZ46v0=Qd z2j3Fng4BSTKpb5=!4QCg`L=kC8z2}24xrq8_h$h%z7&A&YYEuQdMtBTHkdudcS$r{Z z9~x#3Xd8lbfRu3rWK`c{n%_Hsh-W0#fw!4GfKp zDVC}<|7g|(RqlYC2`MrzC3ZiY*%+AG7#5aD!X$?wB{`&|={VGCatH&ik$N$j^dKcN zNHkkVIi6gdsy44MI;RC5^{L zs97fYrY8hNP?`4?o`Vlwto4!bU3-2ctvkus1c@Z6NogpCWN6GxHneFVA!c9;exh4g z7>O{w+sKGYFwxSAOh^mU?2gb1G12-GS3M<33P(XcG8wMLqdsa)n8LFwhj4M_&3!Sl zBs?RsI(n7V#7e4^Owt4-0OOCrENxwHKwkcAn6D=BZ8Hx9Xvv$955U0i5EDWbiUra- zAU}Otztyz1lW!5CZvi)ebf?Y*pdw=<6i1yC3P|Fk4W65%#IPg3peKTGu{UZldHI1% zq%1Qi8nv3eA{`g+5vRk9Ks3-9?O~DeY9+Oxbe4piNr+#1bX<Um^9euHAJg! zn#o?o7v1&v``W8Yp_*V~PiJO19^8={bQF+@ojRG!N{R;Dl7o>Xlr8;uXeiA$?&HZM zWQrbCGrQ$&Tk4!!Onj=&L6~=0^qbh4sc)%q?of2{J@rQWlZ|nYV-sRzx~aATZj@MG zR^kNR=>QFGN`<%!Y+R7AH)nvVpuxH^>G7{3Eny^ED|A2zMn6l(4-OV)9<6`eNZ{ZR zWwMSGYMDl^7WG|0v&YrDku7bIo)^7us5VXB(J}Grt=0V3R=A8~HV2$K&6PvkO$-n1 zk9#2C=9cQNF&VrXW$q*^Oi8wJ-1cV0Jdl4DDrXx&G1o^vKGiGWL_oTlYp2PZhn`-w z6$_=>gthrbmN#Fp>Sr*`OP@NAH3(as4#GJGrBM;QhdZ_WA>br)9l^#JS6z4WLv3-< z&0*G6HK7Axrgd$xul4u4YNuekBVuwUHV!yIBI?bJM0KT6W-FGKiAi`}16h{dxFO$X zJ;e}=`?oM%ub_}SqlD2r!mdt9`ab39B4lScxh(ExW_7A%p-wa1%Hz6o;?usEFT+=I z-ZK2>_|OWR3O)*xS3DM~0U_YuPT#118+j~9*OC+x#)s4>5KsIPMKvO6vo6GY*Lv4p zLzSUQ$+abBk^F&4abFmuglp1%cMFE6oqWL>#=b!b33pKNhMQelX&R}3#>lnAd}HC0 zF@)ku?fu%(`_JgI+E&1E6LPuxoOEy=*K7FU-!v!@%5^8SUg#U7f3t~C65!`dIk3CB z@^!u6i|2k$_PrZUqaAH^c_-BX*()G52wIO!#w29)nb(BEr-1E`#|2FQr3kfn0Dw&B zMPO6o;IUCAp`jSzM$a~NCpWrP?Kgg5G7%6lz^AX>2w^3j#0uO#ad(x^C--V_Omx_> zbrmVTrX7Hh6zG(F?ierET6g>ej3)dwn1S~xe`K=h z^YtN9JJ23=V@j~5QzVpxa-qOt>tMnsxNZcGg5)QUu9usq&xR(u(BV&ga%|UTrM@u( z{cm9|%Q~WycL@R(a~>X-ZtDYZv>@Sc&c6-_j9&l`Md3tw@ShMqBvvHtcGO4D#(wU^ zo?)ApKvMX2&hMYJ1pxz3qlhmXCzkF8~QEwZpD7`^`Lhf+!IjmIG z->~S9(;9WdTY+=Fl<)01d1C|HAI>%Xla#F9)i_Y>pju*Syv5l#QXm&}Jr@1{hZz00 zV|8xrpQHkhH?t&F2>m$x>A%YpUp6x;WWqcj_E^cOtY7;7eLPj_J^6FmJCdvqdjs`d zJQZ}SToB=uo8|UL|EEb?>-&$YXFlXF4_9%{(3N+()4mu452_Jz>JBT%goO?lsF5}nlx$}Gjg5+q42n3B5M(C3;ku0=nRb!ly3nj@$zZkO}w;ziMX`c{H z&-MKTi;hneMGJ>lUYKTzmdgJ)D}lf^M90kVhx?jl&V`ab;}t?(yjV4dR-c8D1jOwI zCC`rocEkPDr@0;vjQRJYy!%PsLd$@!ti;oNeG=6wtg?B0gP_KocGXDF-smGZItMwW zo_2gR9uy1Br}W2sQV9&1N0!``wlxZTQgSbh+3F25eVinzC^%AmpWu5CKNW8FgdxDd z612q=9Q8%1y_zxqu}Y>_nt#ck$ zbVGRrw>LC9*58py3@3+}DaiNSK>An&Uhwq^Q4f?q7THTOU`3xI3WtZp0Psx#D0(dW zEO2Qh`8@J|oI*6*?({^sKRj8`JrM_e?b6j1heDUUp+}2;Q|Qzn#mdgX{er_5gu|wD zuj`Hi1IHsG;|S=@4uznUumQOpzz}twxo+W)6l=`Z5RD6+M#d7Cub3l}?xq_4WQYrh zq*Q!2nqaaDUM-P?3u7((MeUE}g3Ml6WMZN{8BPU^YRB5y8xcuIogK9H6!t}gXmaCz zk(0;0*G8u-NPF*hl+j+Pzd8JY8gy=wcsoYJO2tM}sA*%G1oVk1gh&}c4u)G3XoCl* zEIVc2e@M8(Fi*HvHefq1r?9lm zBi4*UT0+J(j5k$)Kb=lrdKdrX15sYf_0oAQ_ItHn+XzLauJA8~LHkIF@_1MfL9I=C zb4YK(vG{6hEE36&(1|6>c3FQGrdhL`Tf=B#LUDn^Rt zmPN~gt3d~WG&5>osH8vgTsVkYk5n1LsR_VJr9uHhgi4%-+$`es5<~_0jb#|P?B*a6 z{Y{kKp3W#b&VOmly3b|;W;aIe1Z zX#=Mw&D*_a79wA^jd!e0x~Kv0#Y8=9m4d=F6~#0)f+r6JjU~hkbeIUrjTN9eiag=n zS8j#>4s-HGw8g_S_J_s+mi#2-EaM^ieixAFsC}Z{8*m6p%3RXuvFDSxz`7%DL#5-< zZ{;MgZV|SDZslXkKbc2gD;3H103PJWfXKtp>#Fb_P^?;Rq zI&!}e@#AIX2A`c_qgRA>mf0t`j=_FiCl5#3cPhJ|jm-^781o@k{}70K%Hn5abh$+D zWB^J^fT>0IK#B5j=p-^UCDT;FX+G=-FsHMmOE`99e&m2~;L}k*tMkHoAF(|7A%xcH z?SnS9cJ>aAPR_%wtj}~8*E$bRFYiM>av?L*?SuLeccY?XVsjH%dx!DKDXD3Y{?MVL z$Bfmi)=O3|e?tJIz3>f>%reUo#c|u6G>vS1!egkOFQmN9IPi~OC?n(}{1Q@(;rKS~ zJtH_i6$3f`1n*9z&rw|IW8B`j5i?gl7&3lJIxOHUC7uQV$)OYvD~3|xQ!O6WH#$g< z@Xr@W!~o~SSlwwQ^jy)bxSV6PsM7#Q1`H|fy_09F>vYokkP#I@nDjzY%%3D$MWfn~ zJK#`#WvK6GGkmF@M39T9ik!{(t2(I(Qj9WM>xls#dagJkwa#S<9<%ooM#iX|#vXlr z4&Xt6W_|#17%+KkC5Ho2W!t5VS#Es7DQk-G*I2kU z<3Rr9yAzNqc+4=+S{n`i%5Z}qj$ICdzmIK=^M`~93o1iyqJoIN1L#{lP}B*n$u3dW z4M+_Xih%ADmw%TjASZgcQgjc18+y97sbee<;<+E#zku4lR&MHKQDeRz3*iBgEvtlp z`v}{wXK~pC#(sEl@ENdH8@7R(8X;|B1)G5+5}Itiw3~AZcCA z&P7}8ul0?2;ui-eNI&#CkmGIGYQEjn)9RBR9RbtZTScetgdYW;0FJ|@Sy;}a<-;G~ zIcKqIm*Ft%$w2vYB^P8z zK97H4pXMC_hq7d{PR7-$f0PuRly$a^`0z*R-HcXTx|RWu(wP$1#yAC!h`x$aij2PU zXhQzJs1_V2WKc-4n|@37+kDZ295 zcCo_)V{ifh%>_ZwZv`y(OV;k!z9#P!kk9LctLEF}|G|J^+2o{HY(GvOp+EtUTX`tEkXrf#U-p#PTiC z=$#n(jU4%Bro>9qKDmhF`=AGb*MU=BTCJ!E(@jjns)g5c;p1tLIMWxS{{w75lfS_; zh%bX2gMS3;34=j*+!uGJ^bbO}+qQRa6G$T-&X=J>knYx9v=al<|Fs`zzSyQ;01cyh zbpao@(y+S$=^uZHooyP73*iG~YYXSF8A0d^ZkGekTMU1@jYm9j&pmuQSd3*0V+;S3 z7fx8hAqa)*M*!eK*he^p0RRAcywi1^YXiMi133#B2X*YQFkNs19GiSsih_Ls5sVj0 z;AI;g`NS`vh5Ml}9I&t>PRLkD0uQUj!>`bVyKLFBUN+VzOx9;pFnEym#Pf)&I&0zTCM04zozxWij?3poIA8T==NhBwT6*>1bQp&eDd3!d<|1oAJQHF)m$D+8bd5Qio4*~@2e_y=?Y0f(?bJ~uEY12|AO zGaRsPNC!wmv0f+pDE0zucC;Mg(OFtZlA2-M!{<9|s4G+)` z8wm*q1P1{I{iMg1Jq85_NT647c>w^sCEJ<{V}J`6o^cC1Yqt#yofm|NXRQk%OyIO# zb?1L)_vmFWAC^F7m)*(6Du{@Y`cmFVmSlWgo%Hn1|m=#4?P20IPt7L4+5`WDBBkkZ;-A7 z3lu_^eZ@91UFHizGJp%h=T7(ln?D=eQv(7gsi*-Bp_LvDLU*=|LA!1T5GUvW(%6H4 z5Q+f<0lywbgDe26Xo`%N@Dc;CNgIHQ%iwaky;es9rF!*74ofy&=N4yB>mI8U#H@cx zuIswz4-+r76Q?8c1KW1Z+5S%xy|gEhNCVCjq7n6xS2a|$!PecSz+L-ZO9CGt-;H<} zpE^OvIxqk_2`)wVRZc`n3Nn{X1i$|OZ@f1a0Rz$u4-qRoLPK|efr5jCg@%W(%LzSf z11^xeJd3N%I0=0mQvv%_i!(m*$Pa(>PgMauRRJMY0RTq_0K+e|t4naehCm?yPkK`7 zE}@qrq>J+cH|gC5K=*^t?6{LBkYi6mcLR1)0qZa{026`OhtP8aFY_QI024_(JJ^sH z^os+iphq(70%AwZMwhYsW*gTqg3|yv3;30qrZd#@`g;e`fWU3QvT3Ls`Qv}{C%Prr zHiNGu1C!5N*k-tHv+aA-j0b`?jWce`7NA32>xFBLimqD#>Ibkm)dzML8LT10Qg6#E zTL)t43~UBz`J$5t?`Z}btacB82Z-g@ogJ)@A2oFcm=CQ#7hN=6pa)ND_qsz(yMqH4iVuGWyU#RDlChaDtp}2(nx^R-e+PfPY!aKM>AKgY2Q}bm zK&kDzjvNb^5eID#{iq2xO|u7c>3CSD58HNiT?gSGYyW)}1A7OeX_}@!k_wo$z~#1U zXQ&6d7z0FmeqBPG&@ILF!?mrHL4s+``mSx#)F99L~86kfW6C0IfSr-*2 zLITdvZ?qBx%mHBt8k%Sa&+={0IHFY4fwLZVP^V2RojS2Oz@j%q^cuF1ZKlo;_<(%Lg>v7dZ3>1Gr<4 zZB`I&XF(`#7e+t_Hrs#h2DVI52d8HPvd?MP(a;$Smw3l!Yj*>0SAb28KzH{ixpzAz zy0aU-ke0iH8{9zIu5ZHX7$Y+f^n4Ky=+17xKid@tr4y zIx_;SZ_UFXCDk*dINw!D*`uDi0e9mO&gSZH;1%>2=;RTfPDineIpwTK6exg z1JDzD@4exzg6DsqJ>~$nFaRay`~Jc94>5z~k23&qx1<+Ftn0RPbPtG0eh;;Ai*4^&>p$68C(!EXwKjLj(**#tKRpWz8}9oF>20yO?L%SVUqx{9 z?^>y&_6Koq8=je4+jtGd4;Su72-t(^n?r#IFqw)_S0HQY=``aq~ zC-Ofd0979)Ip!KgZ}vyH2km^@!`9&=^)KRa^)KUi7n&>M2jwi;^cULR?Oi;5^bg#E z_W#^GUd4ZBZ0nZ~PX*_$`6U&W#8|8Qtg8Lhglb3dD{O%_; zmu;Z4?}hpk^`q7`1J)0B6a148X%ltObptfh!b^20+%kZ5O;=tEEE6OHGROcmL@hIb z3?L-{rtY#`U<+vYiv<(wL#*otgd|o96RjhICu@Hizy=Ky+jHRqb=_sLOp^KN5AOqD zvZmdD6Z{VZWj~V>+g-@!(bi-QBN1g!ChfWlRU=IU?2@0I9IX2Q*bi|N0g4hz-~ zWfMKi-@s`z$RKAE2D{iBV^csEWT*)Ts{;+OSg`1k+dH?%ux_xkc%KgwrVpVL3$`0% zL$7}iyLg|A1cDs~iSs8?oNb^L&6bkFKbf>0}~1l7ZZndBM%1xTn}jz1Sf}eXFEp^PZI?q z>&}vO4>>y%NrzO6!MZ^PO&r&D6UZRV$-jTEEq}fS8-zCl{{z7jCA&ce>kl>)z#BIu zcs9^*85{DW$FI|R*bSi*XEui;K6pdceamYn2@euGZDFtzb`NBDQvqXqV@?cqws&nAJE-UqZ91a`Lxg|hD*{E>8NZqfLmnn?0SiIS4mJW8??MX!3vf0DXCZHA zfQvYF1z3Pz<6i&F$sf%I+XD+0mt;@xBhGeBbZ7b-N%x*27hW@VXY30#Ezh6}I6z}+ zFwxZgX6s;!OE@~Wi6v7QvoeX32OpxErfDM#8Tncm0N62F==(^l z0L=*i005T(69F#-e^sh;mje_5DSvYS0009300000000Q(006*Z0RSi9k9WU%#FV$# z4s$b@7zQsfTx4iet14M%Os3e%4X`i-)thl}1d(alW2aT##GJ(?Rvz#!pU3v|cHYkJ z+qJU0TfKd4@}BKN14?0xU@b6AbH1G$sTu)C~@Bz z?M~F>10Xauc|*;klZ*fVaZk;TI>nP7av%8Y+yC$&@*|F##GH17IeD-D4*68Px^#|U zV-P~*KJWv455OoPEdcSebM{5K* zvvU|3(qG#;NZgx_XmvZF`z4_AQaspM+6U$I`amod?2%qhmrU5XhWRdfdu!oGh!M5g z&BPc>Xp6SH4{~ev-haS-LFJ%dNl@@U{`Z4MOu`W6T8SGcdv=-*PNRo27;c)%5@-Tqvt8(qStOC$601P^{Upj@F zW5%4cxoKx?8INOjzA=qCAnUlx^v*j28#;p+_3sCpw>AqKBbv(Lyy{nU;efs004mnk^ zq1Mf9T-8{m-Ie2FGoyy}1Fzw|Y$QS((-C;2RqSEa6N&Yt;D0EiNg);FkO@w#ZxcX4 zl(MPu@(_6K6My4mBe%94sf3mUW%I3yQSh+sm>2VHQQDZ8&yNvjgmCF6SkwUKs`!FK zFQ`7K?QIQ!{R%tSXEa8q>>YQkZK7lI;TXjv^pL>rv)d|Rzs~`^(RJyG^mS@WwMmfz zqTI%}qwKC*>>cw5G<;U~80EeZXK8lVOd)$cv5Ep-9ws9 z$d;@Sag1OEvIIAdX&SFFb=O+3dovS6?wk^7$`#UJURArtfonhxGzk61qj3j$6TbvU zDh^@1%70P=Crhhxfin5>Q{-4J1l36q?3r`3u~J0Hj$K6Ro-QTTi8nyF`qt#Xqvp}4 ze1s0qX+k(g^`U^;8e(KUFy&UtsX-&nQ^jyQK5;MD*J`V%N1@$wnAho8*XqOtG6h&r z#ZQEGs@QnZeBj)-j#QRf?m3AS`#2r^8|J`-GJo!SlaBNI*?!zUwVD}uKqQFN{t#`v zziZr&mRx+K&5Pd$mgCOcLEl7nIGxC6rHR$~QvJ~QXXWxE^**=JVCpKc0zg_|`ZkI7 zfd?;`+QSopokxbyn?)A`<}a9qgXp1kdBCIi9=|n!lq%u)-rUv{R3$z)RyquJ_N?B8 z1Aj`d^Pyokspnl@Fq{fdHYM`-g63q@arf%xu^C|TMz?YvJ@_jtI2;#NsyKIbS2WuX zFrQ8`2SBvaYHM=Dzfx(=9a&&=xE|R8Un`QoU2yWp8y-mBi8l_@4oS?l7=7Ys$vG-l zvT4}mmHT>T)yM8NJBU3-KsdH_Za)4 z@>t?=Wj7~aZjb$gCMNI+*z)X{<$uAwy{H~IW?-oPFQjK5s=jhXf-+*+VBw5P2ErZE z#cHL4EC#-(bN1daebQ!~qajX)U(vPf9%e~+v3F6}Tdt{nA>CeCR3#pmd}7nIJ#1A+ z9R#rSdhM{AzU#Sw9J4Xyqrba>wQO;?gzkCFaf$KSr+&zGt;cJt{<~MQRDW>Lat3>i zVU!JWw9JfUXyMH{WV-5jj#*e`M;?eV==IXhd~8e!5l_ZIG*Qc+-C!nA#|ITK7y?Rc zWS#8Lf#29k;S6x_hA{-{IPVAS%n_68gKGxiDOxg_ENH=!sbuKEk_mShTwW9SGT@o% z+R5N$V35Xa8oWeU9R57$^nbB&1_tvH`<8HSV`$9!7Ehi73msV2Pal@6o@Rr@?}3Bh z`#@Wj;k|A@MKUOn>xL0Pk7P{imO3PMyR$OUj(pfe;K7L9gag9Fuf>7AutNtCgi2xw zgYcl3_Vw7`ECoo_(?$x!mTTQ|tezlVv+kMeCSlx8x~_*|5^yzd$pt<% zY#uXIYj}`(5;5EmW+O0WAi79m=$8X%cAb-}80$(XQ{?)DBk#sMs;kHmwi?WbIuhO4 zH^lj9%JP9?s3ogD2dz_0Hh+$$Y z2IZ-9S4uef1kuM^NKM_*k?jC8kzClebl~L@XvE#eARh#&^ zQKa%bKu}K5ujLB&EJ>6I+&F1-cdClD670x4F|QMPWq*)YA%W6HhK~|DpwYrk>qJgE zyYcw9;R95UJ{8qd^7w_hQ8&`idLh8i24y~*sL2(jz zG?)m7B1yg{t|bJ9Dm7N{BkO}ZV-e7@A5)Y$E3)UMfXc*AQz~*?jk>evov!On&>nR+ zY`&eojDHOerR^BHwgfYePg>yQYK7lP*~qQH+*ppedw}_-@{5;lNwrKCl5dh%j|n$( z4s6cb1TuFwbeD6afpp-re3o>xMvQcq;A>^@>^Kz$joOWD$64VOLS3zg*$){F-xqvO~N|`e@LDG_F@;j@r}xNQbgYhy4TczR!$PI;|w*;Y^{XpAexi+D(~4F z4u4TeyQN@+o5A*;vyyU5)SKxitW@NXD|MNb@&ZM!QT6v)W|i7(ho5d^;v#bA57soT zRL!nN^Dq$&SV?ltdw5YY!OlW|wv^6+rs_TqnrPMWNy0&HeH0|+=SGGs4(0ya)z6N9 zydN~!VzW0XY@8;mEe)XP#TBr1>1BpqU4OT8#adK`MR_8d%;B z;7%v|X*uE87h`&EeqMt78lDGf~LSHti{EMlX zY5^X*reHI@EP-;T{eYTdY#E;!*9gy-5Pyz@z-58QH9EV*0*??vmM3B-&YtaXE-1@j>tKdq=rri&7wkW1D~uMN#ujg-Pz-2r{*%Y=0vUIJqQ z^Mgcdhq@Nl_C?D%R}c-sN8Gd9sxi=8-rf)zhn_VDf$E2;R%;wRAZ!Dm)ek}VnCLw2 zEKT=cpu*T`vG`jL*gFh+8-ERF79=n4uxS|=VcY{vQGZGAj?yO7%$LP z8tpe0CBI*RhRD#e*uqC{)~3}c%8GD9rja-T;Q0IkTyBRQ{BVIT5b@>Out_+*f=Pj} zwkGLG)H7gqt`*1a7qmDnEj-WY82kKa;w!@Jv7nfSDWq*vPyNy-(!NKsS}-SsA5 zbY7TZ_|!n$)#WKXp?|>tO3@NJ@DX4;dy)aMH)z((Af)RkJY*Ej#%s9!Uku~2Tk>(q z<=m(dsaUk`IE|{=YRM!3v`s4l{_7Oos)IH z*t7Rgc<3{qxrWR30DTT+wIH}-Sgo27w3c&GqUpupT!XpGKXwlhG{-eoH1HF?@Y2dB zeMpyNo%e$Sz6{Vh-Y_90cDINJ`_E$nbZ|7EOiq&D@V&Sh()cDcC5bM1Ct7%_At%&M_Qr%p7YJF?I6a(JCJZv3I)DqQ^aP_J=*H;>53tU@VZqfrY6i?#a9^fvGsC*7(f{th!-@Wzby^F*)FH z;_V_%07(CBuNVxvp3uN$jJ#%i3@|g;56oirz@Aab!hexlj1a0a2yg}yi!|?}!V5ew zy>FTUXWhP&yk^K4GWEq%O~|$)L+*4qaN!UXB}!)RrHd=UMf^BBUi%FL6Hr z>jy9Rfr;yIAuFLasx5Q_L~RYFh;su-avM*8JVL7x-*5xj75i}PW5SQ^xuMlCf5!m- z4OF^7P=A&8;={${LQeZbr$IGyq2Ta*JOJ=_sAih7kzMHspD{Ox?b)kx(9ljZdbd5* zy+?)KKxX&(o)Ri{)O&dIsyr!37~03Dr)eiGPLylfJ-btuYMdrpb5w6!jvS8%`GdE2 zb`E5wYLB_rJ{#4!Kol~);?;9fW=#RazWbfo^M8aF?ynti%an6oBxDrjRcOX%rw(27 zAPl`;0i`_DeS=Fs4F{0cIuJ_?gb$HyGMR(H;guc^RV7y71Rr4C_#4D`WXKvQ?Dt-$ zD^Tf)ysEW;({5%A+k}vo#GVRjX@w{SDbEO1&<1fXIAIXiiDpE+F z&VL;-i?XWDfx9M=s5@HuSz+_l3^M^#NNJl%jOK0}g2;-ZbnqS2p5>{N5vj~p`EFN? zI`j@{d(|~f-QT5*EO|o+&f&IKn|vF-fqwemQL7Zh`UBk6y7AVNf`NnbkR(e`R-8~% z1K}DSG@6&RG6o7}c=!-2ljl|o2`159Sbz900jA)@I`JmJI#d489Vrh0qnZMqjD2RB zV^#BSqL3$wPRO6~2D1_arJxs;W`>FbXCw$EFTja+<4Jo+*+;F&Kh7)!9RdMEeSkn1 zGGPREO^{fAk#`6wRlRJlx%Mu-g|y=q9y_qs(F8WPW{By%2q$Y`;XXl*Did|CsDFT) zyRrpW2l#{kF$6e|5DO0RDMN)c|G4X85wZD8G!HENBhltVG$9xcCs-;o4Lxg9l94Mg zTcbde7r$5eUV|nEQa{u>YvWL{Y$fXu909DGZKUW&o}>_E;7dJWa2-OlVYgB2sR#O4 zufk#l@gpMYaHm$cCC7L>Li#U<=6|AiK1_Yq;|yEACV(3U1Mkp3*<2bP+hWQm^~!6y zn=ueE%yD_dI`Z}wa!{QWDQ%YFf!!2J-a56+B!WK<>S0P2Y4SRzhG%`pQS6C1?(MY= z)yMsVFY1U$g68EG?m%8_I55onJY{g)cvhb9(MVy5!UOyzFcAX|N>=2hY=25tr}Rle z;f5!8dWMzH7_uE@5K3$FAp4TexCV9;b&|4o!3IcKNYO;e2Mnv9u+xh~le`nMS=QM( z)~@%o(UsbcFH`0lh7a9Z)=5Xz$u*d%;lTS0*X{$Z8@<|66^=3WZX{mR+ra05)G6{h ztUX=Fc7%KegRbYJx3L)cYkvj}%tdEsQ^QI@*6yQ`#ejp!m7*uc;-j~NS&2`MA9ndG zYA98WWp}AYhOq~WbWPrjMPV4FKkFMW%)xEtaS2C$7knCP(<&9FdWsSDM1t>nlUj#i zDEK|gB?Oj&UQ80Ajc!^+8rg2m?n|tfI1`@nhP+S3gL~!$$n8H^!hb+Z;8X0PxN-D^ zq7k6UV@ROyqpNY5Cp?_MFF(bJ1^HyY!c!OL!FOnsf|Mu9w@NS-FP<`98O198HOzLN z94zSXi6L$b^5elsAHiqw%soib~J-$Z&T^xH5jOh~Yx&`;$OA6Haz*5psUq%BCysc6&3_0=kZEXeE$;k4;;rC{K*oC3 z2@W{8@!mWa;v#*)J|v@oHS>c4#{w*h#%nDrRc1xYRLxjY+<(|pY2#RY8Bk9xk(d+_ z@dH1z!{@>Wo}jtbz=Bigrgp?!pUap1AMgZH8of z<#^ih;BJC8VSk}%$=8{@7;tD&d?0QQ(-ZZYhc(q=5t7pIBZI|BsKeHbsj-2>3GOHE z#pJkSPwZ$ZGQduAJ{DOYakc>YCqf;4(dYJX$|Gz7PZGCIR)a2bIZk<=8N zP~Jr04IBA=_+lOB$HQno0+7Wv5|UD?OdgKi)m7p(KfKkuf(TcB3e>Jh}_Fq{j$!$0$FR?2DSam|%Ls#SN9HN`r2fYEx_cEdsj7 z1=ey(+kapr7CJ8s3iLXRW?}=13QnMo{(rJE3>bG?!6q0-RlCpyJ#iSHQe(dG*f{o0 zDSw7;^*cq@#3!t55f5ba4Pf;UF0rvP7!hN&I8?`EhFsI{Yr36LZ(n9tt-<*iz1Llo-N5JfLZ?5oN+?&++$M(;I`Il_@pKSRK+4DzMJ!>R&_9umN@YA;x zV4|{Ya(Bn1;XCF~JKHn+>p6q_;Oj{+$!R+?&ktj)<{5bPlP85W>bZt>lPs#{5UiPJ zS1=rWD(z;&s|pgmqQAh?>Bje`;~JDD)Ot%2+4Zhv6h zv+gf~GDt$mQQuZ$N%W`6Mh_{Gd}hRwa$gy_Gwx8c3Wo<)c+u5)_c6txZ<5`UgOca^ zA0-()M={}IlD)B`fK|WfHCI@%94B<#EsvmUk3+v>?Xjw^V}yg3zSngOL&dpDtgtlG z6b;^DjIF}CKdc(9(Fm}3J0!3Zvwun+2-Nb$pFZuQ9D%w3w*-ZQobW<9cCTCZ01K-O zbZULpGMd;*v6aYUDXGvVMmYp`RaW%z;tmXj3uQ@d+y~u^xZ4JR#yaA%8tyn?-0QE$ zG6PmI*B)WDgLXNJz~&33a)Cpf1z>E%BKjU^tWX?KUCbZYg#b|2#pq7qR#Q+2`*ck+T>2y@9Q* z$3Ys~c$nH*Bgr0J;uG zw}1}EL(9OBU8QhyMWB4Nq)?I7xq@gCYqZ)9R<==0&{}fh8ef(3t_chseE2JJuG82Y z`~on0O`d>kMyWY0mL>Lbzi12QX>h(UaiZBLz-%AVUuMCwlYB?fb3`Pw+=g^`__r9d zFR&Yo*x;7C?qH@N+<(hh4FPY$x$Y%yV6mf3u)nv3*wRM^ktop5=MH#=SSP-8AVi<` zfyG6o1ImzxZyV&U&uLB`SFpy;QcYKB?+2>bd)Q;U3K%Utp!ZJu6px@UlC12i!Kin2 z9XE*8Van%WbEU6Bn!w?8Wu#}d}%UWQu|r&6MND2XyMKK zz{XD%u`^Z*YkxQKaJS)wI2L_)RCWfn=*S*iS^c5!+ap`z$5IJ50TuF-d+`7FpB8`G z4JSke1%V88VVWM(oe`yU3f!|`SCA@|0#!L!PoV=QYn$1@`ECiC4(;EE{fp{PA%}SA zh2Alryjou+U7|C*N_0)g#BlO5C%`g|KMD;SuwJIz=6@}*vDnEJ80?=-4Ld_#nhGA* z3j39Z9bAE^T!k)V`fT+LO{`!ED39-kA&K%{#<+;b!O!q0=vQE-ku4fZxGI{Gq)M{BD1G)|ol7YIL zZ4Pa(um#4x#8yLv*Qc|Kj20N(tEmYR)@nX9ynpi@Uz?HrA!*xsCZL=vqfFeinv>h zOjI|h8r6tMRukq$cXO-89pIKkujv$fNE9ynAn$QKiahogYp*#L9=l^*nIq~Lk4-w> z_w7KBj2HR#Ifb?M~Iw2l96bWVD$LF>G55DoMo0~#j#UTfG5tQ z?FcjTYsy`F$Fs-4joZ@fFMf5gCCudlM%#d^Rui~bgSIfnmFnSN%Y#u=FN5e*ozR5e z*l?o79!=>uR8c=7<39-VN>b=u(`Zt|*nep@7riK;y4=y8pllqd{*_stP|?xYn%@jt z3H!x=n5iw~2SLR`pZR^YhSq2J%27OdRp0DC!`KZlWqGJSNzl-x4b2y%F)WxrB10mNkq<=nO z-?CFJ^BM}On9JeTKl+VU>ZEO`!+3?Pu>DZ!eQV#SO3?pklzm7+{?e%Wl!N?|o{s!Q zNWV7&$9gjb&6X{)_k|gKF#^fLuMm%2(-6L!CPqO_ zdK}ALFYdkiiSuN>fLCFjG?Q5{hkrvR;&?XUMu|8rmiZLd${T?6dQ2tmfr!XNvzR7M6!(6!)wJGu2CyVsq!hY?E>)K17j~Gz%M7(3&_MxK<^(j@|)?|MccSHKEs>)+-29- zON!eM_(XKl$3Y!|c^WOx|LGPYSqF5|kTD?VKHOPReymP!Vq0zw1h(EpEGEze2^mEx z<(*p0RBF*&=on=BZXk423V(Zd>(Dkd{J))qvc#8Fk#|p#NeunZ(SkYC2`O&Xf%(oj$>;~VdtH8JnaKs#fTKd(llMSWcYmV__*!a#*^Q@8 zAPl)clngWD;^&51e8&3g^CVX!IXn8p7fs0qhMonq-H?!6X&{`Nn73fJ_3L?SYny%r<-vGcIUr*W53iKtuGAi+We~ zUyM3SM;p_S9-W8)pMUMPhH=%%iCEex5ut>}to zwxJNl0q}4AL!U4@PXDN8xFG{Q7tqyV^F`H4DK~ZySn`AZ>GGMSSPO>I1aK4GcyvpA zi0yM+rQlt!0_k`Pg<8wCXc$88bZD3dA?<*KxABTZiGW)oX@3`D!QR-+CF&T>1$>~V zi|lg^zY9ohmgROV8yKKx29dmC_Ti%NLbgm*&Qsd67DB14YM;d6g~?S?fwYI0Yo+x; zj(q|uyYUXA$(L#pY>6AdTK>QY;fhl@fqZ)wvYRc*8(BsdATTa-1;>BUv0vkEEKqf7 z6=pG7P7IE_;D3=CNu%AR8!)s(N}OlM|LbtvHwm%0ICi&PeXLFIpm+3zU}|0Fn(eIy zUi{V2QEIzazN;Dt1LEG|I^&QLr=~I;V8K%#`Hclncx7k50x^YiL~4o^ z=0qowFyYo!hH*sY)E$GC@xf%OrKJWB;L&jKG+U6-1ApRW0H0$~t)lJS@KP9Sqa?8JLEuLZRL18f(-8XLUpQmI_dXMFm??X~op@{RhG$Eoma0^3` z2FBIRIcAR4?2yZR6VxNMqLcJEv|&L)F+Q6>ivggZEc2FN3*C9)77+WI*GwU}xWKa_ z7UiUz_J1PqyH{`L;KkwN!mkzdb#Gm(0=E;Q3@sMwaM-2`8qi6r7iPcqWBq?ZDUX39 zS|kDqtUp}3@0Q4dLoEqzxvR7PgIE5c!S*jWXwI)2v1;TII3wZ;doYuyo@s?fV9zd- zSL@*mRkW)9-`*uNRdWOz>Yj@;^ygxyN4gz}heb3$_0(+SeF)DwB@t$*>(Xt5PpDPb&YExFY4~;TC z*gzE)zE;;+7Gy?CH#Ep$hL0(L>us&|fPXse%rUyRRRI{MC`&miGCW@&jkiya_NQcl zC?~(8>s*WLWWr~9`8c#%%KU5=Z$ELz3Wv?yDEBByyJFE~YPfJDbZ0N83AOj-#n%M{ zF`<5cy4&;~8C```ySm<^9&M;LD>qXkR!GQC9}O)J{+1cYS)+drR{Vk90lMt|{Y zXu$mLTv+_xZ8miYcB*@{;7(lmWdWf8*oRAnj&pmbV&UA~ z+FmGGw~wm4*0Zx`+`Y^Apt)gHI)I!IJ#{~@R9yIZD>gIb@H5*npQ6Ff9Dh)<)pob< zV^i0HrhwN8Evh^dlA$!>!9I@T=>$Z3NCBwh0*?BCj8edjt-)!`o8bNtWxhlc?YsG|W1XFgK!Le)LtA4!1Y-nPpIOQ7UpxHs)(4 zW>vbd0+>QR<=H*mrVj;yY=6T6K_mGLK)@@5`+y_v!b;7z5E=GdP&nVS4G!OqWhReO zfbo5z3r432s@E}XqMAJbn;I$$YA+s2yn*I}JQJq`6r?B4EbDu47W=hKEKiUyXuZ{k zBFS#nD?mGO&rOW~40FwlAg0{Fr)w3Xy-%DpzL1qQ6RQwkH)I@&dq}dz(Lw@p(j=&FqG;D*VsV{35G5L|c^qgmuW@;5oF>HM%}My0jYdZmFeE~8k{iHd(}@yYA?$6d3QBZo$2R%ARPP= zFGivk^si|N@{MS>a6$-ZY&l*r+%fbOGDTK#KfvTpub zY(Y9>c83xdB!9{A4kjESa|{pn_D2ELYsB`NqNT>i*G8v}$ia zwHGryFLPbOo`@AO(xX$w40Vuo%?TMffyf*(IE101x_@e#60s`=XMVcq)5TeWTON&0 zb}&&ZC`zWm-m9V6vC_;AEaO#Q8{PzZdfIb>1CJI~G96TyYCS+a_mh&Wpkxa;3rUnY zCd_1PWd`nR9)GMn_R@ltUkkw*xF&Y&m>PX7(g7CMG@30GweBKZM@s)qQ}JF*!pC77mAI@fx3{kd3`wvjb>Z zm4Bgd5I!{97-yW-vC9|p3s=aXfW`en)G~gN28InbI#KX%f#7hC(|Z;1evgP|5$O^Q zg_g@+9=XDF)SuX^^InATgYTn0zr+Q}95^9|6s-6?eSuUQA<%4p=V|$GOsbLJcSFHb zL9r(ZUpDknwopn4stzyH4armxw!mKK#(z?~pIl<%U@9q-Enk9|ZbuJL_^L^;C7PdB zL;C;*>EnzWibJdZe|F|euetiKFo3fWmH@{8)C}+d48bP_st~PQuieSG>+*l1^ZtO{ zRp5c9LjIPUuep)y?sLE2b1-elf=bwZ+m`)(&O86%9PjEQ`GaE=$#`@L3h_juZhwhr zvvQdYYyW^I6>z>;e|&d2yyNBl`79LuVMQ~vof9p#P|I!ZdcvJ~5Kg=~O@;qS+$o6_ zJFplYFCM^Uwv9>2H&&qfrU>saExoBuc??-WDLmc_5`UvpksYJv>=x7aq@8bquwMVz zFVt_7dfD|O=TTN86YDU=Ho(O^Jbz7@q>LKP2Rh+{6qxIix$6>gRI~dKdRt2jCL$W* z-UAhKEHf>oaOaqtUK@CVO)LNG1UEUIeVbq37MfB>_kvGYCW4eypRLf~8M`Wb3<2Ov zpkl&nOh8!WVZMB!PRL*jP3%?T?+m9m8_X4T`3>YCBDP!16w}(9$a_V7pnviR#_=Yo ztU$4}v}ubn+k6PNpRo#vM{VO#M03&(g~OO{Da{rG$!0#DG4(vh)U0Ws;F?w60|q;O zuLX$h*opIio~A?&fKZ9(@*;eq$GBwqE4|@`AGY2wY{N9iyZzX%afzEBrlzez^|UvQ z+k?*Gyl}<@j5m@~v!=gBo`39Y&qAH5Lun|bA-&6JLr4-RkbkFPz3~Qq z5>Y-^^M!y&X^H3a^56#gp3$S8l|6~eF#QOzkYdIRtMz{O1jU~ryEG>FlPWfAplmq} z+*=?6Kx9U!Wu0oKc=vm~J0+bLz(0VEY-Y~IEP9#dM#INZmmlbHb${H2{4l!hzNSE& zC9Rffcu1HT!+*Q$Fq{Y@t5QB@NlL0s1GwFK9gbf^dfH!XkrlSv_Y;_ zKiX^N8Zwvio-T&bhXF+ss4i_*OdhizVg6eWuic6I!)$AltW(-Ac3nyb^O@+tb9HMu zS>vx$!%G`c)XKoh)qh%r4aaxsMpTUH(lsA|=m@9NJw6id`IOl!Hb*SvGEp#mwmxA# zpNFSGZhNa)kj|~E?8$ACw5w{jw4}-TE9@9IA5p_;EBHjY*Srs_4GwrH_}Hs8PB3EA z$VJT#Zy#Uy2S3DPKc%QPd~{s%&e?88>$SKToxu3lwu?N~Ab**Z;9eLK(CMqo&qsO&X-SC<*~_Ag;8Dj*tD8*6+-FhSek&5*|)j%e&Bwj`S&7 z+$-m4LJW?fWt^XS8aAp=N*=gRB(}MOHh|&;Cw+MCo0@+4;GWx5)RN9ZtKn%-PFcub zuk@Hu_djbA5F=JoTYM@`TLQgdzR_o0KBT@8{Nmxyf`0>pRBL$PLVwM*0E!hb(;0#i6`Du5C7U?=g?r5)Ai4qEN*@RcAo0ykfJ~7c;di>kl)I!^ znV1Fxi(DyE<-0ySgiwq_Cm9S6{a-Zo4&gx2;RH zZEZVEwX6c_f-!?M=_^S|v{dbV#|4RPQC4~L|9|f}fX#1v-`~COo%7#3_xL^Y-r?HG zBaN>_*BGawP$Xzj<16CGD|Q2!b7^bJTNbJ$QrwF#)>5QvwWHA5>eB3PyTKnP?oi^2 zw`|fXNyN1y=SLv6*~KYJ&ZOGyVLKBk0-(fs8zu}=vS?(Bx)9NGY`A}2AwXzu>JrDJ zs(*p18XqT*XA+3Bo)wQ~wEuW60OeOGGip6P*twRbugxtRu(s&77mvP)D97(>^A6|l zYxJ5Z9qiwXS>*DL=nxo)C!GAg{HG)=Sja$T^@$6c>Q4-gt=tOjk!l)%55yc0`DF>k zL^qHaN=3pW>>uH{tn&EwkrWt+q0g}|pMTE5ijOKxmxIzk5YUb=7s*_1F41yN>gbZS zz#G;=dAL4npu2SE146*J<)Bl;Iin01{8hy(0IHTze`Oe%bwVSrAz$b;Ron*ojH*tQf)%P}csps8G4!gQH(6o_OT%SljU=Hfm zX<5A3V8%3a8VJ|?BH~`%E8GT>Iwxw#sj`(d6LLTgcSY-6rD(y3 z^uv^qz&?0$&ZP-a3(T^RKz{d!_c{@nkU?iC?V&Vef65ye=|nA()IpR|(_)dnr?Fy= zz#qoGyY5PaVT^53_GFQ0_H=R4Jk)ATvt3v$GkB!zxB$*NC+sXcD(T2$Gk-$CFi1hb zN%t-FLk1WL;exPK*D_JEp&%ELKFHDy) znQ!&#%2pX8Jj*FbKsaQ4J!%HxxIFFXde^L75ldn%PRx&_uVu7-HI9$V8B+~v?#t6o^>a+$mrQjsmX1pwT!_r9TR zfU429y5w!FDQ&1)Mb=z43?ri9ul*RsYf)XMfWkT!0At%xC`bZZG`5OanUJuyIv*>K zW-Dq1Z}l%Yy}HcbJn1-T7ExWx=B=jS?NA1;I9p=i>`R?P*RdupLg3 z)MVYvtVx@b*x8hG+2ga_tVpjq&_O)7zcRuODycLbC)(SDE#GGP3TdZoRvswc?5GwK)k;p>wU=s2d+mR(gzLqG2cN+R zK!T*`p#*MeK$@!Hj89AahPBZbZ#f<@LVgZ-y*j%xF&zZu_aeTbEi~u?dvt)$+<}4& z_T=+n1t|Nm7mq8)DEtahSf0`dY-)etFtX9vcM4=#8zvk6A;<)l!nuK_9a>K==3dT; z@%GwLh>INa79z-&?azNH=53T7(xgMGoejlzGhPAw1|XN!6-x*ju8r!nUY)Tkq@cPq zgVhWiShfXX$+?%deMEfa^@+kI4oqrzv~yNM-o_7frDf6zPUQMZJe>qNCz{XLpvs`k zZy8~#L(2Hu=%6we3U^He%ykgiFla`F=2xyoIRaA`lTP58Y^+MWSZ@(dA!S78+0AoPAZJ z#Tt^SO17b2_TD<>%1jcaya91&rbJQ-JkJiy#K!6o0~*DEmta%st;#kc__5k88SZ#Q z7EXp@aIm(hao%+`^Gr9$_Z9Vxzy)qQ7zd zQ5rsTo`UoeyK*&m(1i$2?j;NyU0>@AT>L-65)D*xnqZLzyqxh?FRn6^#V0`Fj`-C` z;>2}CJcx9fnHH8Pz4<%9h`vJ?OmZG60Vw4gfp#%_$>@JuC#7*O%Sl!(^?0#(PKmCo zegIP^YuX|XYJ{riM{?(3NFGFJh`1B5_KUAg^-S4cIbpu1)h4&&*hj%`K0wFcIQLn? zkeevexDh_3YRYpM>qJEwOY#Q`ywRqV@MGAIdA=5kieH*F9<-?Eq!^RNry4raPFQ*c z1>OuxkJx{dZbI#X-Y)H?u!?<~g=m@=Tp-Cr(vzKR+QqjIG-(%HohC3$YF$Qg2doLi zp0PE*iIQ2j{6Dd2J)7#D6jy%$qPq@mh=;>HY>Tc8Qg@>3xvcW|){#Ae*_DH*()*;j ztn`ec^vUHj>QOPMn%~qXK+2<4yoG`pgqG(|<%WNNm|IqT6d5H${F9`aZ|ZUiEoZHz z;fG_io8bw_x8Ug()(%&f6hMsG6U)JL;B;brl&WA ztHvq6^U1Auhq>F14r#A1rt^mLcJ*dbL%n}PgD~-M1BDV5|DVg_deLN%1yoEwH3Fz3 z&+>DZpZkD-XxT-NOG_-UHDOr%v4N}D6RWg)b>l;lM_FQS)&{PByuXmLe55R=M1yi` zP)b90Ev-cZrKmdyZssb=g0zbm$A_aESFc)Tjje#lJI65R=2#b9=Y&R+`LsZn`H6po zAUK=D{XDhat@s47&R9Z~Qho?SHh;kcfq-jkp`et_1%e~9#10O*9@tCOuF!4K9VJr4 zxaDjS11j-<8~jwV5)BeSImbBngwI->Bx)~u`ied^7V7$mtP5eZ>^?9|QTAx19Y^tO zo9%3h4Vv;;N6u`uL0KW_V;`FK-y!0@-@dY2L`F36VZwcnN>^I^`$T zt?+Sb;9*CMm!>tS_Y*=D7H$-d<4>sjK4`ibu2ri{TLXMe_Yvp)i)i+9;32hZdyd`~ zB~3dMbOlzVXhU8AmEhqg#2}GdAHGGd7Z!dk)i4(E{}5?qLJ+l1hUH&$RQ6+n-`p$o zX=6R&O>p`sMcb*gBW)8?XAgf8r`fTR1$I|F}?FoF4Gzp4<< zKV@&;1s>5O`bl5A^ejQoIvd2_{b$1FN^OH&0G6ar{Oi%s`2+tN^^h!RwS))b9_LZm zHtvy+dRug3-S*3g-Vk1}y1M$!N7=eRm;gb21L`NBC7@u!pE8PQv#H^WLnh~3Y&QhB zV2(ssVaX-rDn}skNCL1h`M%KSWsQWkLpP`V(_O?ZXo>z3b zAS$S_>tC}=bcT|Q0GL^>W_3sRv})BlR;pSAnAhs|)?MKT=&dDETe&m`XS%cE$;g0w zw^$Yx%{3gmh_Wiey;hUKr(Zw6k#*_zNX1=e9t{_dcP z_q~If5g_`@Lw{=UAlCvE|g@9;6 zDuTF6aLIwbL*>QsHqMgyBogKVw8hddU?HI+1l3ZRqSkQ@8$i(Y_u2Q;88Y7d9PFPhwYW=5Hl?l~TRC4WP7(wg}c$L-YBx0J??((uG9kMgW)~_j5Qz=VhI<{vVD))a55IT7EOgX>M0*580pkWKDO6#5quZxCD zO+3OAQ*M&^Zw*O3=rn@OD^`X5&~>M_s7>WDRY*~rbx~)pv+KsN}hZ0THGG+mEcNo=Oj_v7SpN@$sHIP2~VA)xOo`DV|Iy@?^#Rj8Wz|#r=Oe zI`K7RyNeJfX}A-eRna|XtmyJWpxxy#g*)|h76Q--#MR_p5)mmpxN};@>oCQIcQ){R z=%;~9^ISQ;bR2Kx-nM5Xo`I!8T?Jsr)J|1pVM^2|ppP$jP0ae7)EU(9W_;i}=L##b z&|iKZR0| z8e~caXUz=AQ2Ab|cJgH0l`5^``|+j)HLTLJTs0tLwOlw@n)2L7&-$yvQ-%yjf$E|o zzYvIt(r!n1GKi0#;z34%?W)z{&m^(H{du%b$tm$qP3ocuEV?P+;6$HEyvBbUB+$+S zV0d{ry5r#eQhfwqVQCh0Q|W~AEfl9!IUeNJ+`D@_*Cr=sMD^gR;Y_@@n%40ul-_-{!iI01PxvXJFC2ZC#+Fah4c-?a1_KInKRF#p^ z20eI%#BtL2xJ5L#1xJMxZqa{7`y_3|SA=h}R&65seqm!TsTHJiPnqb1ksC(Tu^K*F zpEJ_bRV+F*k+)>(bPkZ$H2SRA#H(ikjLPU5t4BeZjT&ri)k#Gf04abL#pnzhnmU!b zug;6P`@hVB`lOV5dRkefK4lkW@>faVOC4{xVhpoukup6?6MUoIc_n`)nV#&UH5_y6 z+xriC6$J<*e_{VTLoLn2=`g9e@wOoY1cKDqV!iD;k&W>dFpsVnJrOx|NBsIX%8*Nm zR&&@vz@os7>d?2USo4o|Jw2B$-x4Ajt-qj3>+I}=le&aRETBSUIT+{y{`s^W&X0UE z454*QzF^4Bme9*`+Y#PUt_ z4_v79P8IR}bT{K1nnhlI%hMVkMM|wS@?bz+_oN$CPaGyL#KC|1WF#^e(M*AZ8m9WH zYm}u0RNu7*I(%)eS=#|JK_to1RRtGjiic%UFL=c+c!o8KOa`6~Wm|t|8!Wu4r$$}M zcS^<&T(V{jV<109j7x062rB}tCaTjrRWkOczh{DZutE=l>P&{UP2J*kQ#zg;DFoyX z#}VKZdUdg}8G3)-aVy&eeL3UDVqPc5tG#KAjhfx+d;M)?Q*~kNz?x9w?sJwjlNE?s(CWgA8qZvt873T zWX^q|9Z8b{MB2t0J9DL#A{fMpW?>Tg_~-dKNtQE`eK34Y+rg~+6HQUA_A z?&#E^IldKXnxGZzqE&$GnHKw5_es#UGZd>N*az4RL?Me!?3w<$5WOoUFR2cj*+cwj zq?TX_YEAFYtKFvxTArL+)5E0)S6#LqDJ@b8bR`Knx{hHIs3%w{{7BU z`4*3FwaI_FgWFBLYz}$DPEfYdGK+#O27olH2n{XHo>!0aMD-DG0Ch;I zWt}cEIfwdg3#TQ*?2*s>RVPCK<(Y!^Z_>4xr$K~*)!^XL(^H7GYi`AtH52g)@HEir z>t27B4TK=AB6}n^QO}P?37WJ=SzC0h{kobXm8)=rTaSf9?ynC8U=T2)s(Y*CBl7A4 zqohTbjnG->KlPi-mG>L}m)RoG-kR6zLW=n=CF;5kdfa@XuDd2;r_+bU8&x`5Dv7Fk z2H$rf?1>EjS7NrqXuKk$Gfsl6)H?WYb_jn0Vz0q9X3`td!&ouq8i!jA?~00zX2iJc zr2xLe)n!M@DQkSvrPXIqOP*epjhU2oiW%kTYrAsHG;#3R7uW_}x^hZ*#bXFhzwUxn z{-v!LLF&ekQm(GYu)Zw8JvHTYG-qSbNjJMKHQkpM)cid4+5F%PbcTU+l7tdQG@*ag z<1fcUAU4<~wk~oogNH1c_I7Rdb`xlEZ)&G%W@P2tEe#xY?)WL3qFhEO$q?Dv!sW%b@|{PNuc;t-utUh4Kwyc>ZqYLr6meX3DV@eMh4RU+)HG(S9~(1w#{YxgZD zL&m_Rp0Sk)$ok!&=JjV0JfC2#Q-}^bCM+O2#uSO_C7%|T$MryqAZ&Xn(k}loGOZhg z^BsVM*FwFwu`6q}67bP&N=SdRa)8dCL|!<-;{hHx;o~dYfq{}J7kwQ$Kut2HPEqmz z)TwNuRH(pmGD`st?s}z@<-j2hm_!Xvi8&wv@Crg(zQg|0#?6Y;{B`mq4vs1n;*;{{ z{v^K91gUUmaE=V;h!?-9H{j*`m<1^iX^A;;C=pWUljz#Vv$daNfVF?0WrhlQMr0ZG6g2y#H29PyHLkPxokgtwT&=ACUeQEu(g)cxLWZHZQ*5*>uaOG< z5+Pa4A{Cs+`h{cyq=;4&c;RjHv|1I<``*d0S{<|_!{l177I)w%K1AOZB@wvu+xew6 zAz0T--4wO3MnK{x>L7n;P;B#*XnT+UYGDuhFi*N5F3115aEOH+OP;3yx1Z42B2%76 zm`u+jGNU{6{@t1zpB)pC^{nXMFz6Bm5sPz?7`6ajeegfheN0|4IzD;RRQ3MYCPus` zO3(rOh7fwCf#%#QjGsB&6B z1~7aSr;)k5jl?_7p*a9$CTm_y_cax6vbFG3B_`|5$6U?Je!kp z53Tf8AB8--RXhvWzbU!N{yV{s>_}OjLlhxo;U0fS--R(=Pe?y!u_9M29ii%jNuPI- zKw@6Ul12YJ8m!Yd{pQR9RvMHs`c8ap{hc8ujYHz8S+B6CUKYWa0J7Z!M_%kq|IIVs zK+pDY-rX_3Ea5#fn7C0zgTJEOplsXIrZfta>{QD2FS;<-$IWlj=CIN?(6pPj(MX#G zUmSlnR9sQ8Ru75iZ}sWI@kA_f!ugm(t*HQvHcy`%o%j(w_QX^2UiZKA61rD&3C$sAY`+lV6ry=YV4(bhc=T&8(R!Ymdb{jKuf%GVh0_ko@&V~_5K@1f zEOM_Wje8bpKg`uGGhDo1d1#^H^=Z*M#ZpgA^x8H?>64tFrmWvmY*Wt1( zF$>$sz!HWbdIZER*mdj*0`Z;=b%9P^Fh~ucM*_Y&%$AlzJ339y#|Gy`{9*>0mlx1f z{d8!+#o2N(rnlno@9t?Rym0DzIX{0#JHFB2huFZT%LA~j@7qdEq>Zp?7kZgq=@s$z z2z|f*F^Wjou@gMOd|J01sDp!VwS3Ocv;`06>)nb3tf;ItXX8dq97Q8ckT{ z*lQVk6A>5Xxrrtx|_HQ+u^8&(wP`$YrI&)H6FQqKp{U9y0tiTLcep5gN(I- z7UoF;BBXF@Wq5$>$i_4Wb0)K+K&EQNz=!ds|DNAxZraa^*n`l!M57ZFhuokx#h&3k z-;aU|)?VYkw>d-QhATeEzB7MTi2AfgZ!J0}{P6=fK~}UkQ(O9kY8$id|8W?< zc%2Q}l&x5q{n!Il{mC3(E56@F%VpA4Q8B4G5v5oIyI9h0EL@^oY*1&eA0B^(YlJ<@5iuXdoG9ZsgM6IRg&t=SNIk12Hl^N$8e?Bnv{x}O zHp(GgjElQu@U|H&VGIEZLc(}>8`@S8)rEIOM*@6%QwINrP%Dd9Fi2F)$82xiof}ZP zGy8*oWZ?;&TE|-xbSR0h9Z5VY(#81m4X!>j%w52gEltfT4y1o$5gP|;Smsx;V9{cO z{p@?y=ol?Q?TO}N;m4ODK^YQN^^&{mNSpLS@o;)B>M`4u{sgZ3ffB`n${MAF*6`b) z+&CE`lwrQJ^si8*W!7Oz8KfpWDVoz4DmH6mOA> zje&A|u_ouXGexNmO}8S)%TY$}zPh~!sV zjzvm9?93mHOmBd=G5R^^PWhF%?Qyo*_h65D%f=971VMif(zJ73&ZW(QvWCWLoo%sm zUFrHO8k9M(KFiLjKpS7$rG+%s@D8#uo_Za32XFrz z8*MzU`{s*4j zq1c+*Ic2l0G(E(yggegjyyAr|=U`{M1uHlxo`8R%8{7~RHMsmR`2>1y3++d~Y3sn3 zDpq>p6i-aNUhdEQ2iPuX4$=VEeN6+u2@Oo=29Wva^2zn<0mYycw1=XPL)PX7ZvuBJ zPQQh(%u*d#^iA7h(PYP-0Oi=TThCZL#!8N_;|h4^z8Y`!&gj)S|uO5m$JpPU2^~e(dHUtjh z*PjV&zxkrmSsb*q8+nBb%X9^m)N6I!A_{tMY#%rk(JI!i`*N`G3qn#Y0y{(SLpcdyS8c zfpm=4(V{a@q!nG3IfY?(YUtY<%{Ti2q2~!;^5Wn@wReY7bpZQplC^UZppUc8a7Byk zXoXG6ATl{?Z}H^%!BXHTkxJnF0>LeNzVsW*%7r3-K0O2HNFT~7d&1Rh?2fxL>oIr?E zgWP}3+hmNMhhnd)St`afjM+cbT1@_^I>H(}kk9Y7cx&j89nP=Tm_5N4?(GHjmW3kn z&<;W}HGOE=0%;=~epuR3v9S;O4R3$6PXkUmFr^i}cQ>#e5O6bjDd;!^=;l)5%*Bea zl@<4M3|Lepk8HPRE6eOKnX#EJ3zOj7c-nM3B0&(A#hR&QQoJ)r3NDdh0nroGE0GM? zB{5h|X#^H!slym!9COhC!Cu-5J8|N8sMf8Qig{-snjC1=6nl2~pb=T*3iyBif-SC5 z9!$U7p}zqu+(2(SZ8f3?^>|2xhX4x#$xc7K(ERgUF#$TX(=_+#{bK>#hC=&++O{rA zT#G1LJ)`5t{?(qF2Rv%I=x*GgVP|;bJRj4lJejd1x_*AORd*K;%d5v9Ixei&s?!s6 z+#28)Vokd?ih^r@7Z8=Jh`)abX7X<_h&O<9LtrnKC`u95BokS!$aXReow56f+B(4I?H=Sm%8WHPVAkDDD2N4=4Zz2*%Xc5Wl-_+30#i$=|un5-tQ zXQdG9RP&8yxk+h;Go61@vBqE;&o#dSOLBPx>IT3XP-P;ST$3<%!M&o7ilrHtj&lDt z>xHF`=)%L4ZEDHNnI&G1?L zjS{oN*x-qxK=jkttsibgu(c}L5OV%;KLj2bP-oad_b5Kx>nZwWD)8lqB1s$~lnoP^ zQ!wDIX0|s9Dc3zT+zSjbYOeIPR&i``$i_Dfdt^X0=UThMGpIE_7ys*jW&f*tNY?Rg z_szXn7nBP?yf}aJwFasi`kkRN%xdWI_TYVxgbqjCk!7_zlWKoBecwVm-mepsuw9}c(W6A_X}*7GZV`k!hPFTF?833iHWba2 z{Ss+ec7iBWuX{}tfSxb3C7RLGG!3>d&KZ@@gr;Yw6VAz|L3wx*fYIn==P-nlht&pj z%y?@sb-tvJjx}3@Lqht3B1E5Qta#S>|m1GHkW1^yf~{da+ zn)a~|noWB_*{RSqOuVrVn=S77t}XLSTjt2~ZOeb(;0x8L^#dER#6A3w#2}uRxDTFf z(7CbK!*=6GOWL@Ghodf~vtf99$~2M*(sE(beNU`O#zF6d{4Zp_Oop_x&px>Y#9E(Q&rK^3f za~yxfVr|I)moLSjzT6VHW&vzxDg85Jqipkj9HV`&>y#c`3r0Kux*P@a+I=pAvP%$@ z1bR08EJlYK4WuqiLOP`_-flCyiR+f2g=Vu{HvVq)ouA0eX?BAKY9w7}TVTa$6WOi+ ziBbUWQo6_`dya+jBc&p_psx5ua#pD4Sbcx;=MQ-kR`T}tG!((&JlnzZqQK3v0NL)E z#rj0{PK{?>@(76aA8m)Io05edQ|HBSR(Ow<(6oV@#(~i!A3lMj#*vy(FGL?F>@rAy z-FbR%BM65wZ+%Dh^mZF^N<4%2>MzaRQ5J8qwCsoz=J5DWoeH`F6C zj<6~6DBlnPS4aH|JeoNdptD=+KJtf_7JuI(d+afkC`))4+J_>xnHmpgM!tiPTS4{a z-4IcjZ`ics>r!A6top=D$MHW|0APPN^?k@jl8H(i+A9iik{{8GsTz@Z(m#Kwqbx*6 zkvO@4(;fUyFcD_`BKzk4%24qrlWr1e|2lD((E~?(iruE$Ru9KgdsfhZYr`J)iG60+ zYd`b8iSZL+z*1KsXJM3~x)B&Fm}po&*#l_Ja*vm%DLih99f%$VIHC9e2lsJ2l3;dW zsD&7VhgW_A!ybKECI4~wxbA<6$v{A({GbX)&^$4Di`IaKrU(?9qxF=_*yRexxJ8fn z7CYq(bVz*uF6?Pqf|%(KIZVGo@MwQ56z($N2**V7 zN}A&}e+iHifI|t}5_6ligB+x%^NQ&_G6+b?UZ>P06|Ox5B)M0Fog+}cb2qVg%J8S> zAzpYb1i0OKvzKYJKYS6YR*eWOOSo4`_OLzT+8?vHBpqL&mH#{bmnbNT5+xq;-&j>p zpcef3k;iNmh3@F4%@lt*qM3uDJ#YM6rsG_&+=cF4hf3r+2&D?NRudUvq8lx2zyq4j zt|X^Oy}o2SV(DMgM`4c3Bc*>Pv3_J`Z`lWUe8#DIS>>rmB*7HN_q8k2c1Sw@@c)Mp zwAJ686wnBqhNu#_zEMwV1$2wqB{?(#C5CSk^^e} zURibQL_m9qWEx_9>fdocY_lza2bOZ-a0}Dr-~qeq zxtZ}t23TDUY_)&RJg#uPvKyw2s$5p4e|Z<)zUB=%ZoHsWmK9~=kuM(&8~nU>CF9u- z7~4t8!|4v|X9I5zZWV)N;d_SkEO=x&R#{6qRAr(ul&N`*T%_lc*&hPsB$&^%p+ zSPjmSSL5GB#m#bww3lKvxT>LwL2_8!?$Oh%$6mvgABIps;F)uA}bpO1iD(fC8? z?@5U1_TsvlaJq22P;dKJ47vsMzOZYTL!J=A665YkG&!QYoKH8+tY(`}9G100OV{}iI@bLOwdbQ!!bGs!@N_kjAr%Gvj^s*KxhZ%Oty zC!>v=X;7ox&EEJuLYd2@9^5{p18ICDHkMkSf^$LoSEbA>^{P<8fa@^cH|C8e?@Zro`p#F|1V(0U8vM5&F1G#d3l|{+^6Y`xQo%qWq$t$TKX%479!~m> ztM`A3IfA3bqK=S7gS|e~h{0x4?!u0-(n{R#hzGdKo}9N=mKqV4LqaYiEKME$@7teA zU4IMTn)yo4KoP;QQT>u{cIVdii*bj*>JRAulx-_09}#hr^<$T3#9qZnTpF2RCn$q2 zVt75N3OnZ0K;@N}@Z*WI9%=FVf{WwtSPOqZ)jV56Ad)(F*AlSqy0S>KRzS>dm^8B% zS+ow~a9MD;?vGH2nfmMvYG>vgj8wT|m zgHpU@{>K zb#-Op3Fv%QmS^iaF*O*E2Z*(zI82`v!EkQy@~d4WBKwBu zCs}xt+s83{^3^XfgfBVM%1otagQ8bOFdFt)VcX#dWcbX+ASy}^n9JiL-3ou%!dt~n z@S=+~5{v?@4wlVYH{9FJZx-OSK;Z_+8$d~rg5@P&;Um@W3UFbj;Ea<4=y z{&?!j$6jqZHj|_*9TwnP69_|iUwzgjrLw%kIe~!U<|~Sdl8s|YBBonZGdT1Z#gIG2=+z|7W(&T@3^Zmg3*ReMoT`?_o z_`;NQWS=2NcnGGoir)x*EC{fMZZ3B$Nrp@2-RX8OxBf*Rv-qqH;we&82ABx=dn%vL zU~uOfR6ApYnhed+zrsnx4fPL1eZSZ&>>E@R?y1!I$GSm{O!#p&rB5uZ2Cp0t8VdeSHppTL(7}> zX+h+tysNl617vS;a3m4EWe4`dXibb$q$`)@)%mp5*`Xjm2=Klc_LRHB zLEMW=YJ^A5-dJF*stOT(zAywr+M{$*8*+*gYusCEjsg56TU34VY=`iVT?FA2IjP&` zG#Vq|rSpHFn=dM#T)VT&{G&97WLBN9>KGDQ4YYAqc_*y%1!F9M`X`wlta^1NK%?ce zeqWSVZ_$N--f|Y`(B} zZL(yhrDt%PRCSm*NCiYRZW5zTU}Ab~1ZA-5$tJifE9#tmMSTz51|_`AN{9#++5rdu2U*004TLzv#MfU>v@V%gTNtZWKAIW>L^}OfVOSy$uGB-Sa6c!FlT!9Kl!%SA%$(A_Ef1M8O?1V z5!S@}ZFRTrg#o=u4yyWgauHz8UTHDCC; zS4J;%^&;In94n{O8c^M0XCKNu`gIH~J6eBSmvn8A(h)WX_zx}y!!B>7E{@@1`-80< z^*tbUOl729VC4iBx2&LcDBj1w$t&2MS72EkN1%2H-sNEAmBu^h!TyTn9rR#iT`aG& z!0__s=_~6VAndlxcgLWonpt|k>DA)bLr0rxUk=93O7ua~+o@7r6m51y8|n6UF6V!( zKJt-K@3L;__1JykuqU~w{5 zaUwi>alK=Sk>v(Po;^L2aIO{2-SC@K0r5a_+@KjxYcj9bQ+C>;o=e@fXu=GIQOSD} z2`ndumw3_+O?T3M6}qkjz%CTOQ@wu>5*F|i?gVmUW)v@698^4{@C^!C<;NPNLcgR9? zmE`xOSt@Qe5)FMyQ(PRflfZwkthf>Tr?qbC#2seuj{`usrn%~y5djzmed)WD-rI)k z<5HdY&F5`bH9O;jj8-)ryM_=VSr&WBPeQK%U1R`(w$`xgV+u7kQF+rt)H_W{J5AZf z^;qe^W>IV0EwO5Tu38Togx28J)cV7wozaK*E2Po`(~;lZ^iH zUD1qq0W>(1LQXOPGuxW5n_;Z@$anX)Y3nYHLGlU;^sT}<-GO{Ws~E$CbkNe}QumA} ztdtU2|E$?92aMbsh%1|UP@P@F0%$N2kq{ZnvA^&PlW(0n^FLWXF_>hMtxo(u$amr8 zS9})#!^_)ropE9AFrR;v|8I|7m3^7tjRavlrD)fEy+n{O>t)8m8npxG;vmwWJe7ND8p z+FTcJZQyW|k1W652+9f@cJ#nVP)-~NNzhrQ_v1WXBXVZm+^3*F3ejLYv)ME%sot6U z#Jq|qFv-9oihX~`jtv^t?)~Ty?(e|&>@-UBL34D?5{{4Pwy64oRT_PcB+W!-jLSe} z8><*#l~QZe^Q#kj2UI&4L@eafmVn=DAY>|6290u60U^G?3O+IbqJ23O=NqykL>w{( zfLpCUBdS~j=s*Lcz9Y>LpWFZ)C3Lk6l5ZQ4O!MvK=j4B2^}){4Ru2gF;VRfOf#ZpH zl2BUMJSERyr~swC834jLL~7`MkqX*J>DVP~m%YnD8gnAi4`QCFkT z!8}WNS_!#yNf0Z3c>mBwvHR!!xM~rV%#;n0MP(uxQuw9XL1!-c2pK?mYvm@`zJH=C zRbaIxE|nq&2dQ@XLr29C4$t_fmZ?04xHQd!GWmZfD-y_Oz-J)p(&y#Qp9f%<)BPc0 zABQq{C%D~+pzlh9TZf}3(zM)i36$-(pF6hjI^>(9e8(qhk0&9b(?8RdvDRk{E*O4b zz~YH+jblSqjuT%KkSjHeCHCC4s*^1Z=AI%p+seaHY)Wh?l$lGjE}4TLU`~@@v&0v1 zXZ?TczvOhqG@cf_^aTf=TffmQ{96|C*Dw@PvJ`p#Q=C9;0CFUM1tZXFk-urQaNB`v zTghlEHO{PJ)RHG3qZ8P;PL;d5M-W4xYRUtU+3w&h3aX`icjar&c9uML0H1!y5So+1SEqjjC~46pCz2$7Do-Sb)RIpm$JCln9FM0U zHdCl0j6z)DCi%(Vh^CDMc^x@VfCH4C)uL3_Dbi+=zLe+6d(}0YY+R*!S67@W+#J4Q zyTf^x<(1R^Wlv2kbaEgiuM?Woq%%dmlqpMUL@2}gt) zxlM8v|CiWADOWB!Gb&bGiZr43P0g9`N^~*PJE>B!3txf$0d5FqaF%p`;7o$VM+5E- z@hC?zrA>|Q;{@g}7ZH?1>FnAg^|V$P!M{r-^Cs0iW+z~W0hB<{`G|i-sjeY}G6gxk zHCFV6z)I_*`)nTjlLWPt-v_YFy=~)jW$SRit;~4!*T^Y5>#0FhZxb9-k9-5GJqH9} zU0=R8%8z|>e@%(iM@GLqfH{Fcn-h>FOWLE$X#z$qLHYn+=g(Q=WZ(RS$9_S_=QW{3 z#a~AcC~)u%mBC!h2GxHN;(qIc2RUZYur7@efxTc*Jn~17=26*A9c0 z7U&rF$vyPn+?H6_Wtyrq=shs8R34e7`ty~}UdFfZPtkl*Dl6a~V3ZFX9jNPw@@l7k z8mYw;PDg4Z`ov1D1yvDh2Fx}jpjae##oA=4hW+<&7C>NYg0;UE{HBQsbE1o9r7^A;M7#rb^+ zEo{NskR%OL+5mr#5-1oLU>9$|jx}DqLS^GAJJPF_)t{aFA{=?|NLDsC(VRb-yq^GT zT|G_Q2=J8ssXAl0T`(x`cNhmu4!Jypz<8jF^toh$pH-lTSc`(^C#q%%C1H#??O&Jbvv>tdM*GLQ%3KZV7~wWL`{x zZY6)aS||S5_x@1mVyls7^kUia#b@MzTVGK>xGODgQFEp!E|RlWkva47=9#@s+!`I} zY80n#lHk6g9N9glf72_E#3i5$2_0cF;!pGtv@0WzSgy#P8Mq_PBe3Zx1S|*8S-gZt&0@_*!X$r` z&J3+w(qlkf8@-iYvJPE1<09%MryOkQ?K(o?TxLw~sg5_9tSP~6jrnzl7o#Yz!EUuCfyqA+^65AImY+OI^i&1~;%=hi;Les^U8}BD789;+x*1!#VH*O|JwsDTYUbEoPn?( zG=aHGW5bRW=R|?iiG&%z-CQ)RpB^-$dl@1ZsNVoWK)t{1*^~qNokPlfs3@Zr|NYB< zH}djD5}nOT-ZXVr;LH5q$y{r@$QYpFc)r_%)+_*=VcpWS{ocG5l@*YGf2*p+P|4OcVlchl?u{z#$3cS$HX3g?cd7GT zS>{X4cL=)TiA)=wR^E@QZ~%z=<3L|f zF=b{dtt&*RTAjm7#fN!j+uFBvc8&8boO)a<&z&Esm8aIC9jv8lUhM4o73D3p7T(S#$-U+tiQOL613;|24ZPeils^=@0Qb~!A6a-2pyk$>;; zFZ1)s^_K;zz4WAfyK!*xeWqddX?9BDJqX|Ul3PdSPEQ<&~IP<*^(CN zs?b^+D&cYI!o&@a|5p#+hj8X&{@a8{dhpF4M}*EX1!%+?>_H)LZ`vbU-sj&j4*r2E z9Vu6xJb&iyycOV*PIl^l0u}5SJP#N5mf@vZ6Hx@db!@^v^<<9$j3Z3c<6<-x^tehP zARBn=i!TQ126eXZMOqCGv@kI43KE@p?{;65j_TyjJ!_+S5LGPSDzjqgZ-PNs(If8IqrCU+k zri}0+bbKK~(5P;Nbq4~Rs7f>weC0x3FzfIa5E6VKORKt@7!j?v{grRmmk=TOb(xCv zzpdxYa74g*Kv5?_7&lT;jklc;l+r2aYk6%W@I!j!QtXu2pzg|(7MC+WY#Gb7An%Rct>NUETV6EX;&l3 zj?*PwTg+sSCoiULy!RhJ_VCbq-^P*;NW?+4#VG(hl(Rh}ciPeyi=|K7fubt5+>M2$ruQsHpEaHfl-pJb2L)lI8~ zA2kCxwlOz-IuKa8n=X>P!7Ku74f0R}y`CI6;7+&~;VlvgH%*bhpr{6z|PlS9Fly#o5%jd-A39hxVB&3x%l=fAEf}*SCID z#*-;fM4o=*fLZdkRYiJ=u2aO!tpR4!l1cYy1c;7*2-dZttj^~BOe0B^VA{ebCHA^= zjYXRmFQaP(qIx#&=NrJtrY5F=k3Ravl;N!*;=b|W+dc~w&|qFb-sIo8MzP9BIhU1l z10JK+gMqy@-Ps(Uh{01j=7LWAhcav+edOa!yS+ObNI^9wn7J4WLwS@j7^Xc660Sc+ z$z4)^Q<6)FaVDS;GoV}>=_v8Z2-}V}F7%`ll2U1tk#v(NNzHwDIq#bT9Gw7S0tfAM zjBe6|^UFvO{dVTj(60&`U$h}PB8oqZmjoA_oiGnj?l&9uHo`FE(9;57@j#2z;PH>5 z1{6ja5tR~1(zPRT3DNNQ2QyQQQWS?L@Bemxp@~o7n%rQMv}rs-ZkLm57I_qO zYx*=sy~4KR9u01r0H&WMPLfGzEz_N90Uip|Le!Tn8ewR>Pi;+r0lFq|ni)!n=fv|&rE7`hgtx;6>(zZ(_~JPLBJ4y$Fy=-S0=3sx9^ zb0=90U>NMlL8sOqK&&~|whUs1cSM&w-^A}fsp8VPT7kNB4N{CJ+b`L8+4sZ_T36%0 z%YI?E82Kal{=n+7qo5COf42z)xV+&Mmfp0TBG(HTB(Rd$z0`fQ*xJ28&()8e}JCbf&2 zuWv$p-aK1-Z~j}v5%D;&1t)Jz@q4y1eJa%`FGIPFTYM8c3imy)Vx;d()x@N4fC?A-^7) zARhDzQub<2u?kaqY@bM*bcfY{Vj55~(YMR;L;xg_eo5FobKnNqrK)LQ^E6!Aqsdf@ z@>wh6m(p&$;f9ok$9jA%?XYUFn`wZ%LyuhpCx~E|5 zVKQssIH1w?>0Yfqg0cw0Xs;CO&^QQ@z9sQGH!44J?N}KV9usiBo8$Ld0q}gn3%vIw zdlbPr-Q$-I>qKeQq}UyQJ0~Ni>AC<1?XVN#G@hk4&{?qB!#V+Lq24VDWL4`us|leMN$P2Tp95o{do(R zw|cmy=YwIcBZPvf;b%PhR2KM67SmyWv{?Dq*%s_Y^?Pz% zJR8c+o!baF`pq<;Kk#wb4!WGUv9|Zg&&ElfIH^UMb-VOs&uR3 zv2}Bl`=*oSja?gmBwDyc*?wBL{Q75A%cE;FzhEzkSMQ1KyNm<9ryO+tp`$us$A^Kd z^$G2x3K?uDFMZR3;bmaN@bri%H?ATXx==gVFewl!^Tj8rjXb^+iYYK9LX84d z#zV-iEIH~pjIunUfvRL%nc$cFp+?e;D1~agZa+raAFfw_4TF}Bt1E6AC14|B)43y1 zS{m>(+CKE51>Iw6Ens6Q@AI%@yOM;7Uyad1J0_QM)i}^b%3aHJr4Iu1NEWi=Dw7+_ zJ9iO$M$cXVc4Y$3%fBFH2V;YC)tFBr7_{z)`%4xpw-_aa?2HOjC|q_e0Fk^is)92P zL_f9fR?i22JRx53Zr}UDc9pVLM)$)eO(*?Aa!<5Rw@(#Uu9sp&x`CIAk!Yv1&!6#6 zBV$Sveb!|l443?uFy!2dB1>Cvs-!FC==~iJ{m2lX9gfxxI4WmDbs18+}!(EOVuNV+3m>D3qP}&mifX9u3$ORu6c8$B((~x=(DxFZe|HP`;mk-F?C; zKupcL${d|x4D&AVoAqXF4Iv7o;-`w+bF(MKYc0r3czaW=O0E=p#)`Mkelu|c6xH3u zNaW`nr64+)IhP^wONu5vjYZwBCKsGBqDv&Ll$mgB=tHF@+9U%Zht|=rGC@x>cEyPb zG<0TvPI(4X?oyv}p3%Nups@V0OA*vj#guLTgK%fmbE*c0Y;r!ZF z__-UL`9a!EDH?#d!M5`wQEJ+w{e*Y)_xYF@z?&!=*C73r6jN@vyQtZYx(hUY_{C#? zrX`@pqwlwyRalFt+kYB~rIEdl+BNXpIgn#!Af=mD9aUJ``>bU>^Y|>M6~|NGY#@o} zBq{H_cr7Zuu~#;)BED(QY{H)Vzk{F9Z-ok-@)VgS+su*k?D2Ca9Zg%F+8wxz@wc@d zxc~|aj!l8-q+!;Oeb>~GbIad5q$wJICzlK(=Bm*=@>T_Uspht+^@W%uy+VWT8i%>; zVE2iE;R?f~O0^zCZPH(GfF`k%+~S*l>ohBQh$U$sTL+RD+uJ2)DQ_-=nrPi6dnq+} z=-3kNQrEn}mPJ^|U=>Cio(epkJw5#!YM(Tq@#IO9A!GGnlPo49wsHx_Cq#mOHVqYz z8-TNPxao?-zE$ll(fS*ut8&#I@yl5<(p#SK&Pzy_x{uP^bSq>OEj9LOenQY;OpNaRf1IP5f zbGva2ZxX!>i2|~58bfB(dg1V!AWIZdF#6b1MkQ@|ZCint%v%6g#3bO|yRug@7gXNCDGh0V=_$DPaFvfEM;4wyRzPa1wQ?TNw5`FN$}-Jq~qe0htKfGs6}oRSp=Hk@96D)pD*qn6|e<^`9U zcwWIh0k8Vd->>pm;bxvV)VURQf!3zoPaYY;EeqM@z{g7XYTIECyFK+c9_FyBp}vSS z5NUFBa%xUFQS{O^js$)Bs6o$!FGmPsJ%7wG9R3y~(R_SG0>jJS&>r+-i;=8p4#i`- zf)X^dK%NGWWE6aVQlNhl-LvyW%CPBYzq7FQ^Vn{~*7a&*7uOwPb8=%DN^Va%BNO+gYGN7zhj+JxM38WkPP}CPf;P=?YToqhw2h zZ}6hShpSpBvchIw8ea`m;X6)JYZ2OgDsrx!p*SslW;T2`Kzf$_f!BxoU=yyKWGFl{M|HZS}^g1DyMLNpN9FQgBwJr8Y<}<*?2BANZ+&C zGe}NPm>8k(85Jik5x#7ml1paI8{G^XU2h?zeQH`sBz@(T>|$AB(Wmzl5%fUiJNJT^ z=^7ArXe+`-y`sFctmvz5m2^gbotK~p&MN-$`VqmMVR;;&2ij--;SlTO-;-RL*utMv zcEVPFc$~XXf4aZ8T|PlFHr_mARt-O4s)q_9cn-(;1Ctb^%hsyRO_pA_>t^qx11h7B z=6I7Mjl7v$1~86yR!Zg)j!W>Mq6Gf74*E2Wo+py9L2*8-7cJnBie2&)nPLFAVadpx zV6?3*t}0OeK#o@1!DJ>kL|dBPb2R0!ta0dnZ_}hW9cJDxFyJy;k)piFkn$%pDrbGd z@s9tD%vg0Ao*hq-O_Z~)F9KM#$vl%!<}?ZQC?pLHTQ-xDvePVV1LHD8ytSm)0IZ6E z>QaH`v}M`3c#Z^oA9X9(rZ{3vC3Htbkf(cQRpMYxV@00b3BtEiD^ylMdfR{RDELBu z3#{5nOlDw6riZssI!J~kxi*JJxI33H8c#pdie+PGpXfv<}oPP5U}I~)T1D;XbevN7OK5bXbW>@$~kJ` z$hn&Gl#ZXrXiXW>N19toeq6>UIMFwM%~B6JkVJ1*0fTMh^(ijW1fJ~Eo)eP_|ZvT6is@e<5!_x;iBr`?ozy`mtfkV*s$+O4J!N< z^(}3|(UH=RBNBRJ&X#E6lnTIQy>M_-!PV3Tl(ZJV1X}C+rI;nGFn&h1P8y|um0l3t zt*SWXicFV((-<|v6yf!MT)urGmFin&=hh87g{vLhnQ42qw~WVW&f zN-p``PJeBYBu<8UWhUiPdvGr&BpXbV-caft)Qvs{W)EjXa>~8Y07{*`G#}Ec4wev& z-X(h$W5Qt|Ay}b1bzl6-Wn0I8hTtD-fl`iby=Si&QTK;&&? zQ=0agCqZ*5#M1`gSaiy)05O}pD zF5xCnSC-p1V6oHqcAZ4TNCNRf4ibkQs^#|vn;zS0XkvCFc#&+~WJMr7uEY_B$BJSH%O7_Ta0I|fg6qAGad?gmo##a1w9%+Gp86D4a0 zukaXp;Dvb+t7oDD89%0>hy$(D<2aBYLc|x;tj8jd(>P&hqjM~Om9joL3aEFI6T{mL zyswYFCWRh327fU_Atn)@Iw4;;8zq;Sit&N~b5j%h%{g+L%+UJHCI=6ib1JW3o{`N> zigV>b0yIiYVrr;04DS@dZJY*MY( z_D5fxAw{9K3vENIDQ=UPA)fpvr^?G(vy{CMtS3l=AFWCjAc`M}+U#vRx-YDi(SvE2-iRNB zH5NQj#}qqe2}^?~Pytn?NP@kA6io37`L5Y!0@kD8M>`vTI(+^tbR_u)v^3$}rf8&< z98z%`0+YQex`PHm@Sd(Jhe+N_)5Z0YS`3QNTwxafi5>?wu&nu~tBICzOq?)_9e)V0 z-OJdq*Sud1`)|b`{M1PQ@T2K(3|@4&SD5#|WPLSLOUKe2*X0xK%fRozh;IBx;JUkQ zzhr6&qqK^DLZx8F=1d+d%nj^;l`mwZy{WNc3{Bx1I(sx9giYAEH!%i3Xi@{%J?R{o z2fY{D-@80;hWHZ)xAq==n1jXL)E3qZ^y9`b;neh%Y?=tELDDw&K799*zYZR33E?8W z1_qmZrz|6gZJPK)gW(CdL_9aQ9heBO>RO&yU+r^$oL=XmUgwuR&SYH^nn$K?z%`-O zsUk2ZbE&-%G(^8dNMV6g^eiU@83C zw}YU6JaE|P>aDjQsq`z~*fT80`Ycfru-NO1krimO%b^jHvWWt)V4c zo-Di_Ry(w9=GJZG7wqExg@*@~fNnSIJd&z^AyP}pDees-uW~~A@{TzU(~s1$hdNL? zeF<_y%}ucu>jXWP2X_cN)cb|)(&FgdCoqWMc69n4x(t1T+mpv)`mcacKVF)Nc(}@< za43SkwOw-c9mX9-gsL*te(LoZoH~i(sAeJ#SIj6N;A;}_Oivbzt<-?!m2cM=q`~8V zQFUuCL3K^yCWq!o5*eF@Py^f~hv*!%%S5lxnjfG(%-*MHcn9=o->nAaKLp$I-Kyrt ziPhZR(Z;cZluYm30HQ5uAdW28R*DegPH=xH-T!EFF>_*`!4Jm3?z~L{=wn(RmjHR` z_gyia`bOvJ;UhmiRZ%toL_xdnMcgP3-aA**&A z+0jjq@y>65pmO`Zj23&+c4O)1_CS4odfH;!|BIS^zQ(y2KrD9({3&2{N>5#3uXNkp zg6fC9T}$*wR5cJp+O@GKnH)8msoE@} z5(3#Mhsb^z4i|OBm({sKl4&oRx0=X=p^gm$ZM>MxTXNKT#oe7IK>qs-(;B1p*SwW& zw^BcyTh5G|N0*`9en#Ur-04?$?$X}!K5i-f z32ZhaVd?}v;fNKmMIb4O z@5YnfG(3?eY4<``>*{*E6OkViC;cMftr{aT7k?rWFZFtBEcyRKw-sAqRjJe~ihto1 zqhfq#3NznFR!ojZKwnIMAjlim{Oc@wJ@4$aV8hQZ5HF8~XRFSvvn+vocviYy+XfyL zWbK%XhR?=k&-Sdg-1j5fN(xdHo~XhKGffOTNUu9p_EP+-9+XZ^5m<_N09eHEZH=p{ z!)LjHR@XQ%2|(y_#Bm%Gbadxh$ySspe5Q~qBu$Ie5a-k(4rmU4;4hFraZu00c)AF$ z+?CKC9U%*QPifBs zz+J#7=zGG&X&at@D(D$gZuk-;wGkrf82bqPHU5;jB)f9`L>IXp)n3^UT8l#Bp;O7B)$7P^5@ZYNrre z2MiIETwFzeXlqdn#Ki#1Gt2>0WZXu4e7xUxy4$wzcXyiYw|3pF+1h0hwa`aFT9jGU zDZ8PjuQ9?=bwoi-{r>;+F$TMPyXW5d-Sh9iKR@5f_r31cNh5732dIw-(h)|fo2-AI zkXJ(u3oQ}N5J|yLE<6=GjGArgmXIcqiRb(h3CP}m_X+LdX<0YU!$I;X1tq4dYvGp0L{js1_eAU z5gSdnpQg7oZM%oOXCSTgXdng*5g@;ABcUN|dB?9$jG_(r&*6Nb}+mf zQq%^218Y{K`tMAd&#c19+$W&z7#JaR;KA<)zb;gJ=j0Uu8Z1jXPJEG$%LXNAk~JkX z!45Mk^ZW5T2wK*FHNeMx9$1~prcc)Th7D@0ry`%a3xNmvTCCBX+jkdkO8z(}qBdgl za8ZrW1o&PGx{U=2MzqXZo~{b=jbo#i8n5|(zr9~nP~r4`h&fUOoH{Fj(NcVjxi?jP zP$+t23mEn}2o`+hm}@<{?;3R(87M!c3%G81?S!U?9IL6e<*(lL9HXd=+kC*%-M7Mv z1^iX}%EkI|@rf^tL3}PRj5zpb9NSF4d(CdYd&JS{DNim)g-c38M_BzO$Q~VmdPLiQ z;Ek9zR*?Rb`3X{>I_&&obr3W>l*b02C@I-oABP~NEl3wQD~&Y}B%GWuL{LB204SIv zoLrxyk0R;e$rcj_on!u;VS(*)5gFOz<_#uHVVSoS|KEgZKm27Gxo9BS=UA`27ab>V zc)HXbpQc{!gMNzs@?pO``>Gd>As zf0<0oSv2ZqF^GJSxo}BX*m#jPh5w5ZUeh_B;xR>-1%C4Tcj*Y4!?0KY)&vKCUC4y9 zf3Equf5fal?~B_g&+T(x&KEAzefWwDug?&u`%oz-6cKcuiX1b*t3x{Hr^bWcqRoO) z>rigM4}Vwi4aK06k0R|8^98W6^%8!Ru_$IjsfpaD$h}hztj2)>qsWn;iWE)Dv+yEr zU$;+Llfty-kuJ~x^A@ALy?|wZDW{auhb$+Z4*xRE~;n3$9%s&Bug^!^yN;Zl5 zKZMNi=Dv|luvXOJWmFI>6%E85Nz!~GGpm&8zY3Duy!gQWBS*JSfe4*E2!4FYJ)o_# zOn_IuW!&Tw6+}Px`G8XWDFhR!8l+w@WTB_hDzBs^tZbCW~T4sW; zFmdZ)Vhhd^+eHVw5D0&9f({9uNg8gDHD zd11gl*9rT1bqzf+FA5ZYH~hi_u9Ee4N<8H!t81_e%=T=x%pZ76wlVLrtM{5LN=+mP z73yCI-3N|Ey3|@OLpZSp7CWUU`{88_Ak=g>vwN`%Co~f*6PWLmf!|@2hDK$ni@Vwr z6RGl$;8oNJ6?pxFiw?e%GzuteW8vq>bQ8Mb6a}vTMewlDo}b5m=oeE(Ak$Qz7C2!rjwP*LlMu*h6o=m7Rym)roBm{Wd5$`if61vy<;om`@k8FH5lYY zWZJ|A4ue0RQonPf)4UNh3FeJb4?VY&x7dXIQG_cTt;K0e1^x#9dt=S*z)DcvtB z_7BrTX=DL#P$|7d!8TkPaG|31KgwBVUpkh_eIzl{yeoz?7IEr_Dv?4 zME^Goj9zS?(~2BNNM6^^(F zb`QydY{rI4Ck(eDVFBJK&w~I*W`B2T8&RfWQ1&}mGYW2ad}uS{P9s3M2J=Hs4(?`+ z0|fgv#?#^}+_=;)_Bw-~V*qY%?J|wX^AR~(Z%aY2NxfluXhJdAY&SeU#6o6R9G*9{ zWU*Ud^K-+0EQ!YhRXd=FQRRkD^sxT0(sSs^S{&|AWwA3pC z0x>=Wo(ZSG4b9J6jRGiu+>es`O*7>eXl*v6%qQx6?Z^&i{($-Cl9~Qr#BXv08fGZH z><#Wy7i3NEa$!y9m7D88UrFBe3Nfa^K~6bfHPrNf3-Ylhn;@P@KP65{>k}J5qhD+- zNqm$0F(mjw54*k3TbTz!{V;e$<%|f?lO~$u4Zs=G;-Jx*PyZQ!`%i!=Qh(1Z)=vDo zB0M-X?74WYvAZQ%>={mCKWKH>J0DPbGa&Z$`2zhEW-s|1%zp*~Bc>VGifeUy}u$M@lxDz3rb44N}Q&lSAKQ?;0=?m5fK%RQnYFhN%XV(D0gn z9>S53^?u%|U?Y^U{KbqfuOlhTUaD3>$(Jk^#1p$?(Ql}(zOV!4)kWBs*0kZFE))CZ zAxSW{d4BWY5wLaWF0vp5rngV-VxQis1-U=C31_7U{f8Q1W9ASx%83?@zM`=Z%m9C6 zdT^yn3cDjqh{`wY*B1^>_4WZzWwDF*tY5i~cYuRigu(zW|oMbDOR!F3O=(4AdtH`!NfH`-XgdeU%` z7H~TS6=5&|DK|Yh?1-d(UF@D)u4i6zJ4A+5 zA{GBu?MMk`FDtLpCqMYe`X*zGO_MGO`5_--D`j)~nVkEv&zbjlFGT<{c)}mK*l-Qm zln`*>;79>#u2`kVV5lM&>^LJ@`v2IYK2|8tlKMra$r*IEJ0@4B<1k%+7@f?aG2I^) z57vuA(aI$B^ta`&8C{>=;aRuRhML{TH*Eybr#cVJm$*tE7|*1EHL| zcvL9FpuV+usR{po2i>qC1*PdpDP>g+)|2$3|_AlPq2oyTjP@xoC3gG;% zQJq(|;R|RzmlQGnF_SiQGort-@Mp z@Tkd7E4o3i4Jy=s_sgNI0wgULz>R2SW!epG=4d!?aNC7z_nbQZBkW==egYQ=t1Zcr zeAn4XJ|=naALp4h6n-LUX#G|WZ=m=DxqQemNKjj^-e(mm;KAOaOu{@w#;j=el!&$Y zQJ?3ko?MM>!~d;V!b^au_dRQ98ihuM=CsX1x4dg`!G6?#h38+rYrqTFP4QfbQD`iM zi_`2wB|U!E@Dm@8B$MO!#)EP$0|LSLDC!pzOOn{?yNqToN^&D4MlgKp#}XEjomhiP zTUM0Kty)vZxwdIg6LUB^xg8-fJM4@mCp32c(c5tOhuka{Cix<&2dXp6#-2j3u}yhX zA(gvb$e{~=gt7BpMLHZ8b0JZq7Ufg27mDi5TU$X0LA2m}b~zFK@ASCYPGw zI*_nc+4<%H9mP{I+9QM(BHnoYA76q@7d~+ow=%6YGc7ewbANLOiAV^K57rYfa^ZT8 zc23MgREzF>3iVL*ukM0{<*K*b66rFGMRj-1Y-Uk^9R8&i`lVb}R2Mq=9mNI1|D96( z<@BYhtOPppoOC-8Q};EC0F-#r#kI0zygMPNN)cAK5Oyg~UZVTuLqfS1*C;!^+qUeY z-(;gj(y-Q!=CQ|Y8$?51B_zWxZAj0NSQ zhRlo645S?Lt_Z%NeJE3wuImtKNVN>4=?cCCRc}6LW2t@bMCdE7sk#W@W0|3!)CfJ` z|B&$H^~grS2AZWp0O9caAeXuCDAXU+C$Ud|LFYn*>fY^27x8Ape^BVji^-O@=dCB% zW%?x6n32t`IvVY0z@gCBYdFmOG%-Wq+E`O=dfk;KKD|!&CQ^b2jP*>{)LLZ(h7g&t zNu}Kk4*R@PWipeh)C2yyNY#=EvPT}v`k#st774;3X^M8p#Bs)9vdJV^Ae=2S>i%kf z{jV^b!Jk@=jfAd^bgOmDt=HG$3J`gzBzv;5un{gTDJSNnqPw&x2U&rkSW3?a>R&|0I)(Gg=Bc3!^5}bksYfiw@M0cC5%qSV!vE3sN5qAtov3)z`}3D_dG0}gp;>`Bpp8)OxIv06xd;e4GN{zVJe zK1kkEwi85wvA=p8?7k>#Ry4ge%S(ZTmCLP~OX60l4{PwuTLav}dn(aj;PN^Jnr^^o zpn+1v*o_xWk|+cLbV>++Vgz8q6|t!b!Wk0f7K;CuJ-BwnE1~lD6pwc0mT_@^@o(*i zZh_AuxAnW64g3#o8NQ6=(RRhlFA4T2GvQ`tdnWmA<2i}UG>bkvf3w`+eGm{dn`>2c z^u;e;OiB>uyJHw~qrriYYCyV65?vU-c-}2n6;NIKxLR@_7w8P!j5s$@y1`baKePfFlM-#H9O%;t+*lM~`W#LbBGm1F4M;6J76~j~ zgGL64yY8~4pXU}>Pm68BcG%mb!KktEJ8=sM$ZU6H6`l7Cg0sZAMQfH`Lc`X-;K#w} z02sS)1S49(7t#Cv8G_)tD`bviKDGCC3U*0;_771D|y=oHwEb{q3P0L>_|3}m(>Z|~~@>i1SFw1Rb-ykRf`Nj|T11hf8H@}JH^*`m^U zL;Z*yV-BCouP~Hjw#5WxTy3*P5vGipgC>iFZ_O7_!!4aZP`DN9=w-Yn>|S5&R-Arq z96Bx3jDZ~aW5!K?hVmjK*v}yb@-T5?bc8u}eHf`uq^A#%nyYb&jVc98qwb0;v5+T{ z7-~J~Yf9_#`96fMmU6lI23&`k^j^sfk)o0U!^mMYkyXh-y65{C^4S=%fNQLykZB%{ zem-N8%Rae(Yq{?S|gc;#|5%SNz4nWquaU8m^wuw?ooSr;RyVJZDz-^SH0+<7rue0a>k z_^^|%5$_4@?Le|NiJ#t+J?#wNh0psDwNY>rD<-BTk{A^7L!CnH(@ACk2VwKek#53M zsQ&sig~4)vAHBBtSMPfd;YQWR0dQ{qhz63wm?HO5OIV2&@YO~d5In5_`sWprsKkKkF1<-T|&d9l$YH{&m`uoQQ%Q=}B1wEzG zoyl*z!tSjslHzu{f8XAia!iS-Ys~t%Ldj{J;{MWHu}x*$9yr z2t#9d6#JXIXg&ZX!G~@q0Odi8NFhxnB9!X@KT`xgMC6FPOHw!iKL$jg2Y5jx5JwY?)3|D zkw-h(b8Vf#4|`Rpoa1G%;Y7~^Z-m+w5~NobXYY?bh2t20wjkIFK)K4}hRpGj%* zjITu5;jrYV=twW#a#8SPg9EGn$KS#ZDGQ40;s`YM5MttiuTTKP{jNzF%@MLOHug|~ z8A5{L3IBrFK&c3t>>%a25;BuBU)F|yq=_(pMP6$bLj2`Zs)bZfwUT+N^uE9 zj>r>XN>D9)VKhKLK%I$jDq`U&5#44<7^PRM_mb&pgqUfYb6<9<@nGE*M#obFPpBM= z`rDI_l_bH}$Lm#Zu8C}@E=CtRY?kZ=zh8L2Z-7vlPOB{16=YHoCg>7~p?4~OQ+{%* zunSKHJx+;SqKixXq9t}$wOI0iE)Z@|MJ4tA=`gz%s`y2*DcD1pQ=MClVULOeS2k4@ zLCRB;YNNOhw>__Z2H1J++bTx4B(NSB8`;EHY#QNR5@oy1-0g^Ny0)202hvP3!;VHai%zl`Gz4RZ#dLAEBBg(KxNhpzAz()#{Yt>CQgUihEq9zDu}yj2>ZVb z5GGSkjWYgd4B3`PFsqKgdE+Hf3w~)_`UTuktM{LMP)WH-87j;|uZ<3-*OGm@6U!j` z`hz4`XlRkBhW%`~&(0YYwA^7NW1DHUhcr;0(-y044*t;Ub*n6VQeoWOd4v31j+;`0 zc);>yi~WS0UeNiTT9*cY=yKfKHM}RC(<9v>qm19L_8~&fK8xR+(0~K-RwB-nEyq_{ zu_^woj&@Yi@4*H@!@*4~`xNw%#RZ0`KO#9ml|2>FA4>k}M1e=<3n)?XA23t9R=prH z-PxdTn+loolw(2ReJ`R1uvBjtO1xZ@>onW0i5bTNuXEDlKuD8+L7Oym>yYV&Ca3ZP zpAhM&sNaG-hZjnJrDNhKt~Vo6mbSY$1qu#6ETgzg4Z6vs;CiRt04EV)XwY-t@Me-} zsZVc>P~zigxH#grJDd&MU%YG5Y>fZPhsY>|$p$qy%)=N(PcIpTHncMsxrjHMPBqx5 zf9e`bW5*io?+LDddrvzQA1<7Us|`IiDcX>Ln4$2}@-~74JIo$HkSKAGpScWW99L@A z0oJX002=kE)-cxLlAnWPG^FnR9_WX&QIhwoc75*q3|Zv_8xMap8|NGQi*7IcWS%3U zi;Cb_s`ia}-S?x02{l1Po!7y8o-%prP*T8*ys_h zT!zK4A8c^{K?a(K^{b9QDg)&>{Gyh6O_o5ATf$mWwk`n&jB6mYqL0-TfCUu)W1u+y z4L_2gnh1}%>KvhcC*o{wbcT9w?Ip{_zu4-IeRQAFEYh8IR!ps`{%jduREwb|XQ$qE zlq#pFpYFzgncc{B1UY`LHYX+#@YWu&fgHnOOC}L(*)Xb0KYlPy49#kfj$9uxx6%~` z5qkciP#Bwfzq~Au{WUr7JsMk8h*lu#0rQkPaX%xNQQXgdi?1^b2PBV4yq1%FN-xSg z^*qI*kFtG{vFfS1_#LG+rSfsu!w&g<7-HDFqf0Y?j8Np?sx!Eb1-{6tG*6OX=DS`O zefC;vEcJj%&ip`Y7c%!!`yQ5ZM}77*AaxVG_ey<%nkzHrtndmvk;q_Uwtj$njBpsM zrS3ref}I^5ye%T@PUNzrZlNSC(~~Xu`RYVgNNgL{uoTGWRyoZt_79A6nh0}0w5_^} zeVqhNw=&i5fAK$X%00%6VLFuJCBfjS{Hm#ofwQes1 z25GpHxEE;#dCxY6&oUn|d70~yOGTg*nRO{VRVgt50QDMh&?c@CVJ1XC@wK835GXNn zS&v@QdfAiZ%j?L0+?kg81@-UB#^1n2>lDAkhDm{CBfjbm zZcPo@g`g6H>W4+o80;+JI-13zno?-mQ_hby9vMzS${LSnCKTYXh<$IHHWYfqI51S; zxX^`gS6KWCA@39Q?@7$S62} zV2@2@v+@IakMK96M>#GVWwzmM+5tiu-R?G?9O9$4@j{z$&nLGMu4g65r)g;eP9#&& z`s7KOWFRc!3kjdDK|;&kGs-Fm3tgX%+7{Z)A9&>g2!WtQIS__ zHy-Ue+(9|DMeC34?YVN7^?vh-3Go1bE3MJetpPHV@gZO+y9?{c`A|1`2X5JQg_Q!M zrM;2x+k9U(KGK-(Kx3*~xkS>>Ji7l|7L@-I$Jz2nVC%nxC`x;O1sXBZT@v0*Mn_LB zj&Qh@sEU8e$)%^7L*q%U%5#6N6ef+*pZTnFN@Ug$?$5m2R=i`9pX1oa(2m!CvQ6K( zTqUpX(x2g2`U+8Uw?=`d#}0AfSkI|{tTp}&sx5frM<*T4od3kMe1Ao{+)`8gYZ@gi z+BL+7XZvE*1e@U}hl}n7M}i9uq)O*o>Tjad=f7bll1Mf6?V9^D#L3oy^*f|mPHqD~ zTJAwvN%?&x8m!mTvC0YB6YoBMIBdCw)6ecAEccW9lv}X^y-LooZ3?M$C%yzHv&l8x zr#*nATra56en&GiK;J;kX^u%Z{ePvwR->_;-xicg zgLWbQ)2ucPA~w;JIZ&B?f7BtgeKhzt3IhB%7mJ7|%>A#DPhUzBLK~NVgPd{*TPNw# zlamsUDc={vb8$WIF?$Xim>&riVZ$E=z`R;g-m<2_6QLe~(Dt=ha#+yjO1{FESRR~9 zd}ngsDm@Zd%v>c=X-tqZWxX@-^?>e+2zks8xr+FVa>2=Y{5u!gyL){{bzrDy@u{lodm3`^?v{@#I?WgwwRCPz{itn{-lK zZj$#sV+{dK>#Kc^L#CAl$g9zdll}Qr9DHJ4=dMLQwcRRxhPE?*a$nl49?K7kLF=&v zmZ=9G%30W__dS_T5**|lZnWckj2G1qe?P6=tE!qA!u{o9v@3mkKdo9?c&1bvx1IZx zbK%p=Ut?x0rds;%aR|@uk>3aGf1Kuj{u-R*q4%gG zlH@2EhxkK{XVG0<&Qmk94W3IuiB+T0Hif5>1Q;Rm4A|r?@U}EPbEM2`PA~A)e@LRA zZ3{@uYF;3grn_Ln))pZZy{7v$I8O_()Q0symi^c&E0jWml!X1dK2XT4`Vt6SS*E~X zsZZP|?yV+&h_A2U5-Yg)11WwqrsAagAvl5${OJi1br@wVUYVJT*I&Hmht@3y)A$Sb zl0F!6nltw3e1&Pzn$uYE8V*zafDu{MwsCK!98CvQtLhV=S5__jVBIIu&+phi$|v~+ zw>1_TU$c=)x1ah)@Z;ib?>si)GHvfcYEQZ1Ze=EaM5N;i3=l=))VV&GnD|X{xIl?y zheNN)if*x0@)oSE#iy1*+<=PHl)Y7U$v3LdOa&!3DcBV1t$>;fQ0OYYRd%T~(JUG& zC}n+rGZm4e9_pU09b{ym8ra1oav$+wK+uJw8pB2sui`}H>Pq#qT87PX8#V}R!jRm* zz5rK1sK4_n=J?8NzJ_%+w_rAJ@>B90H8G0m@pziLxV=#bg15aTLQ zpqg@$-(L^j4rv@}{siK$iPkW zVC4hJf4H!q^kM3FH%LycgY}k!KM|sX8!^pgZ1rYzXoM~ZJ=_q~#KmaON0|$?3x`7| z{y_^zcmJa+@PX?Ge|CpTvg3~R9c-I{=9lsLM*ZJNLx`m+{;5ErUR0LV_ao$x4%F1AY69KJ zA=F6Ksl6#w6!Pj9AXz7hOPn7k$<{*iFtRpdS%G!M#NI_!tT)ifj^ zIW4ZhC}AAr@(~T&IIEV6Msk;L?qgC}XpLgPiBwt?6gR9mxu^Twq$@g~HkZ;cfl;K|vb(V1j(R*1%@`>(JeTSl2n z;UhVIu|LdI1Hh<TO+j~Nzw6tkY!qM-)7 zVb>weVQPJzDyZ5%Qnb2=Q&FrS8i?>%FoKO@e2q=sKV#q^o-oU+63P4L^bfBXe`~)^ZyVAu} zis>nk-D`qMLs3Xd?d5VpFjY$XOQE{%}F7#TEBTSGqRFeeq|}OS;VK7OyWI} zdwZIh#%_!6#77XOe-^b2I{oH-Qd3J%nvz7;MqGNY!v;PNbe7H$7!UwE5m{l7z=ON6 zd64@#F(3&E-OC}%RD*>aVPWw?Ps_CN{+V{kI7S~}^9j?1Yl_q?MtNu)9dwWiL28>q z4|@h>DEUzrc9cQa;Cf{mHi$s>wEQ|+n599}k1k;S>C0 zF`#+z^&$tEU%c`oEcwIhNgIaZxd1hRY}QOnbjcsJ;t%a#v|49i0E~d6)ax9qorao z%DR`D<}{8fol*b2Z5>TRms-cqD37Um{i^BQR6dS7e?w9)PH3M~9}a`=vOS}zciY8c zX$P(kJ=I3D`&!-6Jff#?c-1t{o}c#PU)rjK>Ibx1#~GOD<}761oW_P)PLE|REM8J? zW~AQc$BcAycMcfRo_oB`TP*3_V~nj!&1PDTo|7#mCbg+}#mnc1(u_&fK=r4hJ=IM_ zan!CUf1RD8c3zB2PceAo&3x(OhpcF-7|}e|J{y#14&{XUEpVBxMCtfD=|T^79I zUr$8$TyuBYvE;!%>}{sH3T;cT75f?DKiH+7`z(A7@Xrn>$N3*y=i0PR?jw8n@3vSZ5zSW1gHZ zN@|)jdXKYn`B9&*cHq>8@Ta1qI(}`ZZ%;%1&Pysgf8;N& z7bu;l)9K}lg@!&_xxv~;2fWwIO}Puwak%K^IGF400o}jZ*ne|F^^g?jGf7bO?J?7u z7U7FDPXiK3gog@W79AtXp@)xh_8X2)j;4(8q`JCobJtr35_DsJNztCCoVnEDOGb`* ze4QD`xib%>-sO(c&gok*o7L33e>@^pIaC#GIjlu`iH96)BL@XF;4?7r#)dROqAwjg zHy_6w?ch^m&Y_w-HupE>gtOP2d#ZO8FONG}QhiC&RMc9x6xp+sx&-&hD8$&jA4shxge}_=c9YZ{I z4C&N4eVNog5dHB7?~AiFyJz7D=g02Ue);3~a6eKXzleVRG5hI{-_7AFJO97hu{@5R zmC)*URE~!WQaF0+Sx_0&eJiJ|X4_}HbsijKjE0g~NjIdQQinGTT$82@2k(XwJPUv3 zg_@$Or0negVy=d6eW}f+fAq7#rG6b@%3kFO9Zc+(1&PkOc~4hFJjXy%KBGN}ED72Y z`0dpMGI~izrib_2sHAx4kveftsp=<`ic=Zr0j;L`|93k%)@6&TDm8&7=}vJ`EcndJ z@jJXa|5MNOX8pG<{S3#oJ;tY`@eYk$ipKXpV({3Q{^#n|WO6fmK1IhvF*Sg*yd z^nBk>wfI_V|5inA?>(!N$KEsd8QEz3uQR^=w%f=aTMV}?ynN!i@2l0g;otmUU)AvQ zvOKuo8q?3je@?I2e;?&Fi-Gih%iqz`?EGr#b(@U`fR=HU^eXGmH_ypYetLD;kEgXc z(BOLCvyc9{{Z@Z}c<gCZ2fwhpR2*Zk7%p( zdbp*!AHlP4*YP@;pX0ye^E?mk__okXyuY_U$;;*|B@Sxw;D5ZHOnQIai$v|i!!sd_ zM`=yAT&RO=X@wRGIKy;A9l_)(LG=;*f`QxP z^1*rs(-#G$f6mauhAHrW^%Wg07Ot7WLI!9?;iBwMXD+FnF&iq7KzT+Xo7bGyvBet| zAjgS^1zK@YQu|mTg?B{`_+AWYoH;#-wfV74Eh~mEYH0Ps0LMiJIpq(rYwx#hl{?qY z-Kfw1x}Di@ndMKUaP**SKBlFOFx_rDoDxjg5NC#Mf3&H6O-QC9HRFaVL=H_6D3H!{ zdx~)TN}89h4w{D%W^Cwj1)2K+rb;wrj7FXLXKN^mnDfgMwbj)R41jku$*v}rIIO64?e7N;CJ6RzI>YRNcsKXh5f!Q+l~Ppop_`Z`mo z!y1QZf4Zo2d&)tvL^Tw{a?v`dD;kIjhf0d!av70({4Gb4tqsV5R+VAX2OTtlq?n9&8;rLVhm^G25oH-fLPM?=y>TO}DF#N7pEK-LtI%wM6+ln_% zLo4%VF_vOyu;EL*OFt888U-eYyr!0%7L^quC1Vo)EsZS1&dw=hrI^tMkGmQUl%o@R zYZKQsonL0`1vC{P8(=X%?iE%+rb7GL8O#z%Vy#gi%F!y~uJrL;%XH^aY zHQ{wRBa;^MdKhgJQ;~FzHG(9E0R(WWiO1P`&f@<>M4$Jve`XVzO0bx{oF3)Me{MuT z-}8`_cl}kdb3_~gNTv$kTVkXh2ff8w)38o>>CRLo~acy63G zsO5%~&WYN8ZqF`nPyrnwX>wQ9E&1(bK$I9+pHz0-p2g%#8BgpY2NlQEn`o(Y9k)*$ zByWz#P|Q=CvgF>C8yRV4IwN)6e@o|sSWxa4hp#fWv+w?97_?<%(5v*Yz&XxyoaZ^t zbDZZkF=2)q7-a^=(;E$pc&jEQN@P`(mZJ96Jes`KB&#nJ)hbo0)nZqR+afT@s>^?W z^PJDhJ^AhL&iB3Z=g#}zd-OZ?d-uEBNgT2^;K}xjLw8R3?AZ$?+bDv0f8LV(o${Dz zjJ)t@TSB>1HJvJ>dORXUG|{X$0ENs0sAZ3wgSnk8ZF%QGX|&Y`h)**G&lhmJ&E|tv zBnTNtQeiIwfz%S^ZU5|2c8O1)og}$madje+8Pt6B>bI-dbHTZ#NGsR{|l2()pW0xz` zLxX@(fLcM0+{sD=ftzgf;Uq)GPqTtv0lWrgY$O1Y@)$`ccnaV>Ump5u!ug^SV0O>{PT2u4Nso1;pd>9z&`n_uX?{suck}RXG+D4$lwEZ z)?NU|yx8Ho1`C?c5`4UplQR?=48DxBOz9^}F(#*UNtQ%NH|s+@UH%~^GgCyGK~&1< zT=-xiSLZo5ryQUJXBR0WBH0Z%^btul6qW%%dco@w}&nN6P2My{_ua(_G; zGSg{^dc6^XXlBaPU|qm4iG2%X9CkVKJi1e@7hHKm6~Hp9xU3R7ZQ*+J0j?rt9b?Ro zWiG};4~WFbf8w!t$t-R=A15i0$jaohd3?rXHS?R9n(Z@_dEE5m?tej{o}bZZ=ro#G zIw5`!EtOgD!{V**vy$?Jz2@>r6Pd?agB*9obAtd^#XpG;p#O-I4}`@RkRul{HJh2t z1g(;LGdf8;B#{}8$4NFpC;uPkpO>8^q%e+<46u%}}4Cf+~u%-B|erY7d?Ise;$`gMag*}WUQ+VPWF?7Oc$%@13A=xW`s zw%1AC2PhrlAcgO>CH2~OlJJTVM`@Vod-c$S+1*G@{6Pt>>IhEGCmO+V=;S4uS#;8<-%%w)2U*xl^0xBlT#0F zf4z0SHh({jN8stVBHxUD|DMR(oX)|)_Hx=@)99|9#%_L@|1{gr!4uc?v3(fno3%q2 zWQfxIaERkpC!7#5kQDOo)5(f?l0v&qpj*oxtP6LXd`75OEO+n5vH1S|KjePbWc@wX z1cdsq1CBU%3YIo*{6c;K9d+V7ksfG`e~64=9$4fMAmf7}G=ekPTX75AqF9&jN;&U8 z4*#c@>|pwOwa7`aT(O>hKfl_sT(P5mG_Q}%NB@)8?LfcB20O>ZY_diZTZAD!VH&)X z*SWDyrTTZlYui6h?E&kbrz9zt(PVK?c{pO0n2ZSn2^J^da|Vx&u^KAk65KoRe?DP9 z!H;_>;kyzO;;kVrAbd;t&Q?MWb13`yc{_w>Kzh-urSaqz|J(8r#EakL_q#2=)+_2T z#3xcG<;XpQ%TMAs!cGAHNT1giz9648C;deDkv*{SrTB6Z=qJkI;OPHgEAWN`z6tB_ z{T@$yLJNj|c#J$8Zzm}BPlOSSe|$M{f4P)+eFUTaAusc@aL?c7U%!_-;QJ@9@qo1= z-}d^xhoAQI1SC`8>3RXt1|e25A8s!7qI`g#?GydolpkJOZ%yiQEX@DEFg1Yj@Q}o_ z>BliYR3n@ge@q%()Ki`^6rWAtgscUgJV}?Z54W?0{x9*EgB^*l#xzE?f7^K8JnPmx zL-X&4H!piS_OsC9Kj)fUyUmWzi`bvJ6PLkkMDrnnOPEt^*q-~KgKxI~8N`JCx-p|- zJ^1HjjuE~S5q%wjwzx!j%GxV%gq zgZcY+w(htO^}O#nzlVaxe;>s7z5hdl?*08=?}N?LkcsKw*PtE`trhz58`zEmPW877 zD%J5UwnO+pP$&;wfJ33E+(V0KZ~fne?r_FqaiYaJ#yv} zRz>Lidg2km8;^g{_V?XXhO3*?!dl{zFp_2I9x1_yr{NHpAsh`LMxTQEQ^k8&&)0E1 zs3LMR!gzt|ho6OrFw5em5-3MHEPfa)bOkwcA)TlDmiquca3*xBLxsf__DeY4mF?pj zg>=T<@Zj=GZ^w#xf4A>(xTlRTA_3?lgI90f^6uN-RUEjnnE9bk0Qxu8j<00tnc2py z6V5Dq(c87h@ZjjZkOyjrGS3tT)4n{vO6-D{+cDAQ#{HllzvvI*4+nBs&*J}HDeFC+ zj=S0q?w07|zu!seRw~`z&3;*I@8}!&?cbiK2?_Us_L)xJe>dJEX7KsETejCL_E#x6<^ zr?q=;rT^mpb)(0NIb$OHW#7BY3FW#T4@d71m_dK_{m3gH)yYA@^q(`yW8HkvR{u>U z1u%a%KzWdmu&~guZ2S2rE5D)O4#`m(&IBJ%UnK=_fBlC74W82f5si3xZt{lLgS7Ox*f~GE zNWgxjf4$-TZNcv!4~{X|!awHX;tVIUC%D8t=J_`h9Zea0+*Ud3+-RJ!Gykf+|CUS3 zEZA>T|U-um$PO}SQA3Q3?I)wd~8y-x;T1{RY%Dd7s3GElotpCVy8 ze{}}gjLAS2XAJ+Bp%mj~f~M+%!^HCfdaR*@$BFC2`*417Yz>ndK?C&*{|>?I`rzAm zM;=aSguDs&LUOGCA{77D3HJLC98da%-{(a8upjUf;=-@_Z_oNaIe|xU;&@>?;(LSj z;-8)XoY6dE<@kmcRe^1#7`9pvCK}3IGuc#Hw3Cp$$_hPDF#u!|+ z#P`uXkPdS1TkxWO01t{2`=WV4Kc*-11oh&7d{2I*8ek7Uyz&FdX@qNu?P9!upQaO{ zCx{d61)sAM-}Zzi^dFqR;2JwG75JZyC*<)1abkUto)lvJx8e$q3LMvrFq<3*e;<2$ zc=Q3UKP*Mf1Qxycr7ljS!!Cvyb1=xT-9d&Ofbn>`ya3<}>Ym^W+&)KmF|~g8SP`Bh zWp^PxUfOpBb;MQNyft34#^ql0{lmx`)_4!8qMSGV*5@&G%~uVuZ04lHFkLa^omf6fGH@<98)@(w5Y z2YGxS&+Wkn)_)-Gf5<*q!F<1HZeO(r1=xUh@wM{{K?lwN<3-P^_WBxWga2@R(Y<`( z9sH~oRN+Bq^ujzofH^p>GnWj92hEKe4;=@0>qn33$Dh2%y`^ctm3A(%V+zA?~fbNz2VAIanEU4kNQ4v)4LYlv*TX}CWAy54+PZS++r|R4uA$d?;e1MvIkWXd~uRm;0$ITB$C;Ei@u{}_q&=)=5 zIE3Me>8@Y&vq;&kYLXDjBI~c+EPM3T_mWQl(AekP)v^!Alh+ zQFr?yU^J<@x` z_sP|#TAvL*GI8k0BO;8FGHA-8MJ18TBQA`}e=}&#qdbiQI%H^3qDNMau^KdX2+||6 zMwK0^G|26dr$(V2!aJmDQQjj|j`HE7l&T8{}H8a2rAk*`L<8x(AjB2cMAB@UEA zQ7J_wcS-_LX+|Y~NYs%@RGCUODD}QB+M%?%)_%1%~KoZH|$n1Ud4kJED@b! z6l`o5ie~Q225DH@u}sB78-_PbZx^axe7?zgRragXuTZ|>eOmg(=+`3kkHniH~Yt67W#M>ibj)*!Wf9RvG z=R_+KtXa~@Ws;XlWhu0$Ql3hIEizOoGIV*;2T2_!bg|iErcRqWcIhFFbaJ&6`0 z7{9P<3b8=OnTi%QY)i2+#M=|hNiinGs~Kh@Sc+oq#9I;SOrbV}>J!LNe<7wqi49^D zNovy8B}SIP36*xJQz5oPbcIqCXmF6&)1-9U>651h|DR$Vi6_-Rz#I*0nDH_8`YYgBn0r9@cY4Kxbrm7!LM7cQ|{FkLi?(yO(6yJ{8OK3+9S?}2*O ztHN}>Y!!xdx!`llhb*+p({7f13+0TxonoDe zmN$$%fEuuXH;C2)F>J;&A`nL=%&RiY<(VpUXj!9YUH4N^8cIuhmvsTS( zJSJrZoRt}?3;#iA;_05 z?m%)e%lnA9tC6lpxg_MPlFn-!qjO;8Bbz5FT-v!z=Gn@JH_lbLSmkS+H!Iw+a>>F4 zy{c0B=pqc$(LGbybn0Xec**I|s6%iJwxtc=GaDoc05ncl3C;Qq;u@GhPf`!3um_v# zpAd)amAyV{e~_x%)#_EfD!RC!O}o{8<#_7Ct#nxckvw|dTHd1AMe*zW>+=`t*uAlX zW42>d$9%@ojtd=2Ikt1`=-KXF*s??yp8P&mZ{%qXG$RIt?)2}xy>9R&vZObf9lU_*HATgl;3vdxz7>LAZVYY z=hEZcVJwMo=Xi&Iq1?0oGvX2I(eRSugH_Rj&)+Tat=_MrV_L_66npHkX3axemar~1 zDM`Uf92$nBS_OwaT?m?|5HDEw=g=QXZ*E^geabLMlB<(q^L z$q1XDQyB#;*(uG8hRx=RXdeC;Ey@b ze~O)$(7v=0uYSJ~d4@a2HOzW!Uzs=^js>qttV42O&ubg|TjDp%Z}e|S=Aq8)7x`RF z9qeV^6K$s4jk%k3H?B7T7Q5#R2f{asZzSGnzo;dfSXw>s>%NPX=VSq{_6ig$TjQQ*rjL8XPnOTY40_=`<4a8bnfmEE@6)Q z@u~t#$(yP+EaXnY9g`SWK6aq)%=z>z5UUKd@zLdmS$W9Y11tAruZc95Vw9p;e>AgU zQ@eD&bhrt9=@C1UZKp7wei`}q)A_=nWYo#2u2UqggX*i^m|sA?sedJZO?#^ygC2Vy;f`Afv)F{7?iu>oRi>K3wTx;}uF0v!w5@5v zJ$|ORZE>2oH;`{=-!Zj|Wf4Qp$&*y>%{EL^z96gAet2Lm8xkGgZ?txR+8-Y4d z?^y4U?pfC`hqjNAKN@~m`GfpJ|EORl;DTsNA)!OHL%u`IL(vF>_Yl6LoJYz>-wim; zrQ3zV?9%j7-KF)V`K8$>txv`UyXdFAPv}m)JJOgX8&eXbpjv%GpE{pnf1Fi0N?Mhi z3kBS(?*(!Hg?Y-tE!He>X4eq5Rcy-MQvb5|5Yd|q;hCCdd7lxTyE~SBrhXQ94E?O~ z8T|pd^$lGDgw|`^Ynau^yc2jf@-5CcuWq@%#24PA6YkN^LCxcw66bNoal8{<&Agi7 zxMOi<%bTY+&l&gWH%bYwf4e*FJB)Tj{>=Q@^JnMJ(I48M+#lSAA@#v3HAnmB{Ac{< z@DKb?@zMB^@`I3nh+l7l8l&7R>xQNNz`7n_+?Wey!s2&>aeno=3t$(`2O9n5?#yUd z{;YgVTtSww&J~X>OVUf#OOls4D`IGZ zwZbQ#PuNfDP8c#We~@Hdpny98wQ%7`GHYnV4zdB%TA;xnHG7L0kiZ{u$Ard7nTB;t z?HTgf^V#}>@_8)&%-VwNU2q-(ZozH7t?wf36%Ms2cc+}-87@LUgOHt)A@HUX{g+r)IAqX+`@` z$4@zWraqv>y}6r%H=vf4!<+I<=ULG+Al57}=aXt-tmwzP=Dr|fAMjiSaLg;l$_q^ICsYeuVQp5_e19o^H0y9MD3vhu}R(&z9xcgCv8(M zrl>cA#0D=y8V&y8pTB%F^K%UJ%`K!4W7Y?@NWHmxh-Vk84CCWh&99;knfJk(zv#tt z9ELw;F3jRXEN22wj%(R!jm}YUJd?_ggb2?xBR>>xe`pf(u?6$EH{=O->W;mf;n-uc z=V=1P$^E`t7w{;KrN{DoOuX&qw@ z{OM=qf6dVw+l80@Mw)WL&i`!dGq9)PPyUDL!<0Ld%tNe0NQOWvr(BP?jx;zIMx0un z($`hj2`Tj$`9lpi#=LN7e%n{3^B~aSTsSiOz;lY{>KSKv|Ly@gsRe)G&Bu>hNN&t3 z6t3(2W8A`>a0-FMhiPs{#UFVG-@y)k!Xa-*f2a@t$0OMzqy~?23EQ% zC+jEg2Nd{;+?2W~m@+=mN%b`fYt;P|>w|!}rF?*%I>l{ETf;9sveWD7UC&F5f&~P8r zBfAI@u1CsAWbujmVQ?UqF1N+<0T^>@`0M#g{K3FGU~l~< z7-Bd#_md1u{cT^H_M82I+2$Pn!&>;kX{}QBhDaBaOzu(=1KIW=n?LnqlE>)BsyJ}? z4E^B(d`7KJ-vVsq8_EXSd$4BYaw*QS$gqz2hbF^9`=>waoc@ZN;)o;sWO0HzZ2R3V zcmkbmEAb)`X z000Ak00000005K$0RZ5@0RSdYkGt2s>k?AmU^#)97zn(@MRKK_n<@#KGNPPhqW}~5 ztS!YANh*t4Cv~bM%$Ai=-Xxqk-0b(eZ+CaQyEgXS-D=(TzS{DT$mSS^3sVr(yHg@G zyvGs=Xa$t#=kND9g4cIpn zs~LUTdGrvt>(8Yd3B(Y){EU#UAAXI8|Jr}QTUuUzh%9wh?7#m8>x)mP@cdsRNP)+> z1P#Zr{rK~B{uhV$;CP4}jgcd&E&MO|)5zJ_|Dx3@GM7CNr@e1Ep9y@;Q^_h6@SSAF+r@;~24kv@$6*QfeU{eO9t{pM4@ zKNMSZ@5=)D9lSEZzUb#<|GsujeCyUb>6806y@^g`HhNGvnNIDwl+d^`lr9Z7y-RWJ zk=invn#dBiTFulrk@Tu1yBK&s5tH@eP8P}?+cQa}WIU?Ve9kQUQhy|AwZHKaX5|XF zv+{@~UAVg$%#5N}Bk!cTb%2%RB&|%d=Bx3`Bwa$#lDkDS;5vm<6G2~rPoBgE54tjx z?jU1~r0skQv{2PXsdRQJz2*qRD+C-!GdrGFqZ`dYw{n;(#am;H&9 z*pRH38!DCNRv#Il7A3h>oj~N4eBFW0(=5RreAykmEf^bKXkB1D&@LkM5CVk!-&6O5l0+$2jl%lMU=YoFNygbRtVw z2<+*UT@N0o$A5Knuf*v6OVr~!h0FC9-z=v^1WN)w)1lInQC?aftMy?WAkWYozbQv3 z!9}ly<+cXEiPO&c=YJXQ8p|p8&#!|szcQM6!&l%x;b3m(-=k-(-9zGlHCMfe>bhkp z+K*1DWIMWTr~M&)qc6Mq4YRpQ>_XZ1$DrDc|lNimL(ZB5`ba&xv*#g0#tO$7I)XDzh zNy!8%nK$ld$tqr_TH)$?Nv0G=uJ)Br9Ywx;tn-jTHQ9;oq=gJ_3d?WMf=Xr}PJn~~XS~K~-Y^4*Pl{z|#vG>!}r$^p^W@ylLH43?3 zSueTsH&Fh_#Iw;-;E$GMP)4?))$ddjA%6+vF5n>oU3|~Pg5ur+Un1?|pR6!ShN$d6 zni!3?J0gsxt;&V%LvTl%NAp|O`70lw!zX4E*5#Th9 z{$Or&9P=00EjnuQ{YO_(PSn%5tbgw+U&i5&6ZA)7g1m`|9BDK*21Lqk3zoDlDP2(N zqjp5aV&^26=Ly_Sf%;&P0h%gOApKeN^kgi(fWM@1Dcp z51yTUE{~DdDTiH6je&CPCS=FOOYR6I^a(#;?X&nS!I8rF~2($UBFA(24`-ZsEF1TQAF*OTMR zpczN)m@H4{s?_{sMk@9E-lMC3*bk=;U(IxRJV;12_mqK5lHRz)%;*zIHV<5lZFje# zJ{anY8#*DnIDi>eAh42sK9$^@JGol4YJLE(}>J)sJMko@A{3y+lK9nz$W+9x< ze`rwP=n5npPSVYp`F}68P`(TvB~x{JbyD1IPPIh1;z)97j$UzFJc#j&MVCAcA8bOG ze`@WBucLmEvrn32@R8f#Rm0>-haaWKl@SRD=-Zkf9jT$-HZ-t!7bw!$L(##t_-ABV zG$RK7BM?Rf^U7*HF#THpjs&fqvnzcPJG!+uM{iPpT`GS1zklcr>67GArXAhkIdDcz z;pJH}f6;1u%d4QsC+#fsxm)YNNGB7ykrnz}oXF=oQs)!Txti$oJ-0)lSG64Q=FJ9( zXp`i9-0?Ye!qoHs*Q3mrMg#*V%+MJ=c#NlhXgLoj%N;^onxiPvQ`MFbPvY=>LG(F% z$ZOqDg!FUzw|_1kE$6vz$n80P+`*Pm`5BpOkuF{>4kK-L4+b{??CBe0L3-w(m-2n+ z@qAoSjGNFv(}uI^!r(F{P9dSk%*$|5+i-RM(idZa_~ZG9-%+~WJhDPUFP~?r=;TR% z=OLfnkR5RNWr(r4eEPyD!oU95PvQss8#UE(udvEc;aq_a5ggC}O|rW{a&%{o5;2;Trc0T?@VBaXnqAkTL04$ihgn1uGHNon z+vxgmV1KPA7i0RfSZ9J;yDpW_hIlsn5f*dfLDf&0JvCCf65ou+>*h)sKKoN2und;~ zWjF%_zRAmz??Z4^$pAnTse!ORzw;CY5APvy!W{CKcEW#Q1nTWM=kMD{*!&o5a3!%C z?SCiHKDUglHIJzscO73a7xQcPWtCGCMCok_YJU#k6&h-`lE@=A{mX=A^=@YMj^(yW|laZPn9MSkWqwsS_ z;O39P%^!oBKL<2^4r)gSG`|J`vI0OfQmZ`BQm4;hn3V!u!zVo}`^htBT92p*n#qb& z-i&QQ(;r(tgZ@F3@#>9VdjE($>JWeN8h;K`$^S+{guPcFzMb)FT)Mh?r3+$a=j5MZ zgwpA1n~5R&1Mu-2N8k;&G*Yz^h~viC&f-@1!B9N!+)Q?@ZAR&8RVVbM%h}2_`OkZA z3C!hhV@;;{o7uKHAAGdZbybg3CQO7r_wl5eZ%Kd~X!5TXiyw9+m??=RAb&C6 z&68qFzu1T&rjC~5!1Woag8Lnl&~hK0_!1M0x8#t{NhtnVF?7L;uVAzhoGoTlAiai1 zl+~v5_LBQF^F1PASv9{v`F*Vly;tZUW&c-$!3fjd4FYizyVq;@q-Bky_{|TCnKZOT zv;shG3~>GSn#1vP*j(aqmZqQx34d4vMqq zMOVi~TBD+^VboT-LW=C9BEi_Beo@7RiJ3f+O5heAJ(BIgYDz5Fk;%c)%5doAK6G+? zbaJXXIaVD+YosWS%0fE&E2FKF=;|o_Y3bUXiwMzg#s*CVJCPZ5x|#PgDEeOZou!ta zE3UdHr&Po>de7vRl|SD~q^j2m%B)PHQjw|6I@fWEDu|i`LCj8sjR7`N^d#nZ&g0g# zTOJU_tbSQi@=AO3{<48HvVSdAkD|V-J?OEj9E_U$g~-`9j~**e!ZpOeMfaWoZy(US z&%6|o2Lj!W26NFR0*|Kyy$Qi4ltLph!bfc%j_GA?gI$RL;04)&x?QaJH35AoQ{X(%y@NaZ^z4Nc*RXtbg?}vsz09cNe2l1I zKhJh4m^4pLOEFL?g;utD?*aafnLcn9sb>C4ZljX+ZtT-)>T_Z$oUeBWuIhZOZ7dyF zo>@6lB^9!}a;3nRJ1X@)r%;G;h$3_?*mn7*^BZsWmO?m{nx(PD&(yDGxH_1dt42c; z)YXwNZh#v09tEC9VSlq-!MDCiF|MyF168R~`L50rs201d3Tt!Zlo$%OAmGmM-_VEM z8cvvP@${+isb>AdL72yx-?;(1)>nbvL6m$WCL-6TZo|GQtM7cgS(Fj_2qj?9U&r;$ z2Uyu&SuldW^rj$_IH<~v4v4VzmsP_3a>y{W<8eK$*|HHdpnuf})1Y8ILjYbtp}%y! zu&lTMPWsPMVg6JI%|u!(n0?QGhINL1?donG!K!L#G$ECET>VB}>Y8ClL**JuSUL=FeqGxYxWUd3y=edKOqf!{^COk*-9m^$E32w?-{{|KcJ}vZG(kf+gA9g#~IqQjfT^FZRuq4Zc((Y z6C&f)+YLpH46qsdH&_|DAo02}r=Lg97E!T#N}`N+9w2{jgFIo4N{tn`|K9-k1v;;w zlU8{uqUF}MS4=(Q*wUyS3RR!Turpk+XMTm+DN3XQ*vg+2^{%^7k_hL{csUcpUcIl> zc{u`BqqffiRZVdRxsro3o;H(sXnaP7g7YKQU+af=H@ge(jipcL>aWo~LXm>#K!?XF zO9<_|rm%l&89Ywa-t|9vw?=I@F}u*gA;7(58kt672FPIgvrsKr_Fkw8i;>Xy{W|!`bNN?2$mOc{nA)kSN$qj zS*QVuJYspIVt!nFd?|tQe znY7;Xa;lLhk4DqXYVQgMAsxP+T*^;!MCj&|fpE5Yu9sE91W;##sE|XQmjqDl^hry|l2KRjui5<6dEb%|Gl|AT#q=qf$FVuRU zy+of5F-f*g_w>|V-u_CAJKv?ilVl>VCddY$N-c6#W&^=zBk=_Y$uSDq?BM6pT3To3Fpf`UNQY+eiZ7^C)T!+dt6gO^9%C5)^>b^ql zOn)pT?QBq?Oe&4Y<};#3IQ&JfY~{{k@nV~fjw9t~*Viv|-$xLi0hr-fvO-0rNI6ZI z_0Gj;+;-($)+ATsY0@U$rdHU955gSv-o>%#K<>a8RrTW_nMUm zcCSN<8l16*fs@d)1Xu~sPhH+XW(cFhPYQM@YCfT0s6*ioiqu#-mWY=GGy_e3)?4*j}ZN4M}0Bp^DL@Slp#N7Hm`q#!{#DCrDIv8nxji5$B!8T7&llZ|;cWy*GLLZ%B@P3V)=IHFFq$&$Bltr2lzMF}IweBbV_#8)pe zMEijk>)1L+5+R#YTq)G{4_$xp-{j1oRJ{{cAre<}f?Q~+DdQm@hQ-1t+d+u)J`~$l zE77k)nM|c-lyn3$Bs%#(_$=n)c}$0bo(c=EBshg(b`2;oa;8z zYosb`7o=6mH}Ql#DTN^K#6%9)F_EgUP$Q>XX z8>{)bWvt!rCG7*G^G-dg9%ShdE%_>2g0QajWbneZ2x6ee(^zV`k*GY=x?&z?D33TmCVU;^7-eAePlm8Qu<~9;pxI zenJZ{yLYfVDEWVaC3|jB(TrYt8E^Vs(NlR>&ia>~4He_3SGWcsCssVnfDxq#pQQ+| zr3PN2X^MxdcNSCSCXzYKArh*1al8|!@#Cd`mk7=(F6)NE+|zhwcfo-#QWt7+HyC%S z@eT}d>SJZTqDD}Lt<9IwHVfMCm0HrvQN#N_0Ubc)>}G#5HWLkuwXVZ?1D1R(;hp=> z+fUf$@ssu$u-jO3`vss!h+3?bg~wXYBLY=@b@DclqF9twc?%ql!s+@ht!uA1Vr9jm znQM6eJ&r{yz8KM^cs_2X?nbVEGk*M`^p+Yp_vEBy7Q~3P`}m-3Wx=1!%?Tb>xB{JT zEqSQS%M5>O4DS8>xNGS-))arF(_?&aw;qn!nj!&qc)%A?O~QZ3C6dHp@6>l&2DU5C zw!M9rz6-T=c_*pzn1Tz?JdPw5#E*9B=$S9^E}fz^ zf?l><(>vfl#!u#ZQ$eI;c1y>-9mxwUKFxRO!PI|;Bik!u%EV*tI^CSmPCh~UsH)>_ z%8^F}gY~{zHso!f{p=S|lICQ!81uQX+xx#yDur?jz&#L0Qqr)Q`kfZ(H2Z4%p=d$e&|PIG!~NFYnJ7)8#$Bax z7c&wpEfuJ)F*c|Dmv0mYC!HLvt$B^gjIKD$gwb5{%_Of-O&0c;;tqEObH5?7z47V% z>6y==f-aQqT&VHDa%%KlZDR&RW1L?uOEn*@>CnLB1Go6RYh<^PdYGnjI1C9}SJ z*evajY2`+C=)vnlm@Bh6glqbO@YJ#%IYPKMsgexPtPSG!piwPso?~h_DBSh!%u#;` zBr;`FWwO0??TG1ZUX%$%PI7N|BBx7gB?h2N&K#2UPNWR5A;u~sk4g{<8y0yS-1G@v z>J1h}lzB~rC>#e+gMc8wWSr~4*8_bzMkVzVz4o&s-6;N5t#@yje=wD>1+UZ0Wd@CH z><2?eKolA4%0@^)%SXnO=z3xA^-_NoQcf_IBI4{%wVEBwZsB`=L6)sP4V1D6jy~xf zgwa_!3uPNB$nx%RPrAT$3M)%7rtS%-2@Vo5QD#be_KY+&((zh;ao|cbM59mh9#pzZ zgEEGVps}Ao%D({}hw(ZHTBpF9i(Q({KnypvkIF{Tle_pI(c;jklW9^m)xv+)tNxwU z&_-^!L;D9h$}3Lbx+1-&n4KOm=2o_f(=NsvtEBT8QRV}v9bnpZDq`CYYA1WmW<=Ug zdX-6@1$JvN{#|Hk;_!Uoy^<;dw@^Aoz1SH5F63z!pZ|uz6^}e*@)qT&?9sGzG#Ml4 z6P>jfYN%RbATjbOTrD3OQG|a?8!i>9AK200j6kg)ebC?sh}d`sc#glokqgo3)=;)5 zhUHIvyh8WVlrL_1r)M5B5}S_)?S=qR-xO~(JfTO8x}A!3oTW3&>eGFIrZqR%N@rQs z(I-EKFgd}Vib|&_r6A7EGy8nclUAIijU<(Za!E=_F^JN}k)>Q*6eb+$ub_uThK{& z_BqBvz3qhrosM!!!hxNRa!$g4aWO2ln?7_wFn$&(dj{qqlsnZ?C>}7Bz*x{r(@rrW0x3r&k# zan~R(Q!@dwTtt6i@g#?%TNl#S8TTS|JrLnA%{OYD&78m#rtUo5-B<{5g?ibUQo7>q zk94iO6G|Q@46l56e9v{ODUKe3&1}gnAp35|-c5G5<;pOrqLJZQl}WqU^UrQh?vW5$ zyuLwX2{E_ew{~6eDkp^HxlEyxT~LQQV2;G8V8r8lUh5KGR#X=J8j%`c@zJw8{Iv#L4P~E_rE_-N?Pm znHR``jni^or{oGvjUm`e6*vxGsc%Ecqqm{G1@eE5!Lsyi?dX1~@<>E*6;*eO(cgQ zN*}qYkD6dKb-Xxj1Fb8YZ=QnHlNx>Q#d~F~_e=k-m&JvWkJ!CBeG7kbbkJ$01>WjYQ+;?-hl|JqHG0*@H+ z&j?-<;DfK3=%8YO%tG?#GMF2vS8JYfoT$N5zk3PsD$j zjX%`1sdI7kM{@t0;*ravLc$tcOZx6)`Ze_@f>*PzCCf{UEo@=q%8cW5eAk7N@8L&d znPj^2dpQtnMLZh*uwYfGMUg@1XiXv81C+{0C-8T$A@}>X>zONAQ z^9h49aLqTpd0loSF{g0@v?+@@cxQi8UbRYK*+!XqgON8i2O?aas{2YG9tu>$Do!dquS9Ux43^YeVdck zlwMW|kcGWSA8fV-85a#)r_xoHLj&XD!v=`ee&LfJ<_L|Yk!$hT>Yshhf7_uEdZbWW)!)oz3Lc135pg#~2^L_KcOW15BmvS1N#Bb&rTBf>EH>g%O zbK+6u#Ce`6?CC(xr67YOR#n3B zwG!&{#JTZ>oD?i%sZ-@<*+0eRbk&@%+B zN(!XtB?s4pFeVUWK5>XFK3gDAgtDtnf?mdht4i)kD~#QR20A>wcbO#02U0j`C#n0i z&}b@y&o;n$dENpAxYU0VFnlq+FmaH6Ql}c^}2=WljZdXz8P9q z!y6v>W7Y45L~$REyuxfv;|E;b^1m2GHYOBWk(j*()q{V8?s5Y)r^Hpid7HB{GBg-M zY9^vhPBMM0T1IQJaHCF}&>l4Gw2NI`7}I-GDY*1bS5iOIxT1eGaUiBZs-g<0B1Hxu zTY2J<^L~w{n`eXd7ZOxFW2#j<7@=BdKDN}Q`TV>=l9HzlbOl`~`MK3^`5>BJ zz?WYvw;D4++!vYXU!odmfw5jO(P|!$Q}>^4wb*j6Me01Ka1#KizcliHV1Bz1hq^Jtct zeEpWueQwo%Rz9qk#F}51W9o4HPRVzC$oijO5}|)qKB|xP%6jj4TbeyLBSFZaBYei^f>qr1Ui_Tql%E_{V=d%>Z0~}}c#)@C00%3<8)ndywT-iBBMbjHk`y69@OBouf|S=&%`(t9JRk0=|?jQ(b>8b{|;c zKN<3>iLM0}3>ca`N&_+|2{iI4m!A$`qU4t;Mg(2jw=I_(T2IpvHxY&Ts4jbbu4IRR zGk{i%mEUt1oSxbFTB&_ZpAwlhM5pgl=B^@#ih@jeJp1qj$KE5i&$*W>uZ0o$L*59} z(b)|LPQ8U6+?}he0_SrBosWO{u_s6TC#_faPJJ3|@lLS_6z7h-AWgh=;Q|kw?ShV< zxHt%uM)COHb<`#AKpfTQPXI|#>m-nVIR=q@Vu}0W6kUV6zSl>>oO=iMG$yyx0&?8! z=qaS2gcW!r@onP%l5<&ICR&_`1jE9oN+-@aiTiE}YP@Vpkhx74jC+5QRjtoO>}`ap zH^c_OTsz``?5&XzEO@yrh* zRL0=Q(p;XO8fPr81T0YO@^LPNz2-ofjfQaf`Bd(-ZG-*e5~iANe9p3Rd* z?r+-Frgbc}R$Ot*;*_*Idm(YPD|RtlJ2S24dW*SqR;C4_EA!^E6C z!CRV8``if2ZP$N^;aH_L6djS?Pz@S1LeuTYj5v=EvE2X3;mh|w@OXHi&!Bi*$V|T9 zqIsT^J6Bv#db$Ui&;E}#_xZ0e=u_D7F&n_^d1m;e9!DHmw+jT1OLS1sv=v}UCm6Tw zKCK4)@jBd;W&bY#jGp5Byz<`(H?MJoUU?P#%E^kd{|kQ-DKhw0TM{<;*nNO^4AtS> zSj{f#v8cAm(2hEH_?C3C`O@)HDJPj8G&-1vxEk^DyBo|)oLzY?B7T`&)QM@9VXPQD zMc;{y?+rJ>JRX8H$lmDM=;~cUCK%bv$dW{XG`S-9-x*i`8u!{%X5|}9$h{=}vb-zF z6U_deUlxBppvok8sm}K|YJ}I){;pl*e*sDw&7_dMXrHQC+eXkkr?7N@=6cphF7>R) zr(93b*y4gol6Nl1WF$S~y?`_}7o(1g_&-=mAHr zISD5eMOFXA9&KOz3J!`o!kuFYmq~P$=RGS{Y5qb%84d2w`wP*?kqX@4g>HPF0@!pS zjlF4z%H?j5L3-=cE3U4ppvjA!#opx1r8MG`w%zL_D8Rf|K}Bk1d_QO~WfR>+GLR<@ z450js4?k|$@D+KP_qOqG6k;I`Rv(6FWK&TKYdpC#jnjT;GBjQl2o&jG-dYm!P zsjAs#kZ>XnhmOp9fW3QU)I= zIx;n9$g;&T2%prERX>F5DX-lTQU4&Aa2tQANm;aTAgohGK4iSd2tSlc;&D1k!l=zz zv*rD9DdF)YjSlizh^*LHf_s>TN;d@Wy5=%^p2btAR*O^81qJhp1gN_mqVlsK4a)>{ zsvtE=6GZ71SAI#jqX^)yi*~)|5>;248dUJf_*8DbV25IFwIxayMJMVWsim5wP^^F2 z)CfW*sa@MY@I`5HcfP*&TPJTP)tP9Nt8UDqQpKbx+2ut@P{j?6*^NmbSAI1DjJQ;@FAu;*lG z)eQD*tv-akKa!z_r<)LR;K%pJlxLoG){wTk0%xyL!jTavGCTDFb2J=r*o%Lc*5nuV zMMM{?CF1t}L$c0f?lKqJ#3$Y2?e05>n@742aKwc0iq}CXz@m+R=N_08m4dXdoV?uO zD|IdfrB&wk-4lI4I;`EvD^%>aTFC5L@42i52^N9`lzy^9g-~BmtlIjH&l6BtqL3lP z-mXK9?!D9NZCKXI4QM6e<%)kroW(6lb<}K6(e`;#$!?B!-^z%Pxp(ovUAnSk0PUab z0Gio#AvuAIvD&bK@B|S0>7bDvC(0L;Hx_0FDDj(A#)2n9+BGS5byWjFg~C38E4&$s zk_}G-sA@7(*@5+2O*~1e|2aLrAjWn)^!MW!&+^mWd3|GNj>XQN%gOXP zeE7x7S!w+aUDR*lULy|DH>6%VUqhF97>>$YjiKqi<`Uc;>zyPl8U1w|L6 zu+y&ya&08Vftg$_cJp!vpJb8(6ARw!8d6G?+>*&52wjRru*82zxns8#7DWY-8I9X6 z!`2rTC;=Q1VwlX1CMbp2MFAk(wLJShxHzU^i;e@T?l>+f*oZ(02?$9DDj+Je5mF+n z`r9NTm`DHzzrX+gU!)AjYr4C-w!5pVzAmr7yK1w#wyQU@YPWm74exukk~teuvB!Eg z4=FkIf#Fm+GRl7@EN;>QyIxcFWnfTkV7JFxlLz5T!D;{)hUPenM8o$;uM&G5S@vsu zf)-{AUg6An*c2YwIkP!}Dr~37nqfSYd66a@^};=1YXJ6s6qvP*FHq<$e9c^p=LrUK z-8~p8f`j<^#+zJ(dA(oMQ2jG#EFrvKh*2j&W#C6+) zz&q)40HP9+`h^<5a#b>Wi5S=A7S+6C)L?XgcvTdnIDG=R9?t{AXBIi!*Jcw%LF zH{qfIq?>PfGCF_{lG_Cp`I(%c_WMV_+ch>SwQpC@%9hh(sY*t*V$%#s9S-(F1A#iQ zdHa;mgCl<;9=o(Si!a3q(2W<>#5HLQ0sllGcg6n9K}LB~N?Z4*9&lT}5)JyU3$GQl z!t@=o3J`p)xNPqSR991xs*pi&@_j{g&kg@^R48104Z35rRxKz-Aao9CK!%A;4@q|g26>!>cD@!LJ)`bi0H3zEhjG$Z@=c%IS4AS!7X(tqf8Ho^UaM)}IE}F@t)x=2mWq=s=G)cWp9slU^Qw!3%RB%~ zs05X5!2wFqmU--`s`;X;Zuk@t7NoV?uLSEsSp3I8#HmQ36g_hNT;9yx*}IWgqQ`oG zzB_*f0etk5kuC0&oZ63RGcRFF!0$<{E-08L15=cWWd)|l3Uv_-&7&UyY3;ysH|F} zM_~nky~I+|JTwErUsp7DZ8bstR?;3r^f5Hk8hQ+l59lNUhlu>kd@%DqbX)L?Qz(8{ z<{Fol7j1e^H~Gsi3QbZS$7^HYEAxp(-yB0v~MeL233`5=GX zSS!}~=8zrxwmTmr(t3ix*3%M;T;T9F5x4!v1A7x#!@k}lq)r)%WdZuj1^Y=sFzG=) z54Xxe8YM(7bEE6>O+?ZqMEajzsGHBY0`lnf1NfJEN;kl&YzGe0r zzwbI974f0HcP$OgomFY`tM4ZMo@Rg0<}a`GX}|UfzuB$v_xDie+??+$gIKV`aQ{%z{=r-EjOQxB)Y@XsxpfQA&CN9h;mYe|1T4B@pjT}Y-;xVHC|%cW&ve63o4(wK9|c$w zyey>9=ndcT%Sqgn%+4T)0r`KyXp6fkVpp2+aOZWps`%v-d^tk#brWUDVXO-;N^ox| zol_g_OlpVLi;d)jmL3+2880<^RdNzUaJ}+=s0`56v`twRPXQAr_w*sPk>O;IqlvY? z=;gwE%QHvDJl~WG&x~y(b0Fl{^2yq0`vn*7EwapuS0Kxe_slo47cqYpx+F!erKUQi zGg2mCn@o2NqGvBp(j{4#eH#B`moU#`yXDL?eGCjw0(3zj1IFO|k-4^|AO9a_MY+9K z-GHyKj7HUvWQCCUz4fLTy6c0TSw(WgWwODlg(RD9J}9-+31+)(*HV5&>aU$NqIp)` z6#4m2qRPDbZR-_M`xJl8tKB?&u5cV*-3bKfsYPxi?XoL&DKbwjOe-n{Wg+AqN=$o* zMpUBD*E~|^fN+!w!WKf(h=z$P85w?#Yk*N#h_&355yjq)r*xQA`l|Dc8wQhTDbs=> z)q5vWNF?7X72Io3Nse|(J}?Pe22NG$eQdSeg?ui2RLLf%o~M6$umqbB$5=;ZU1tF^ zc5&?E-_&bh1mK|EXIl+(x%n9b@JU+oksD44!bm`I`x~xU9D6bA7VaBiC_T^D8*IdY z-Nn%y(7B+bK~ZlR0br+AC+$6=^%M1&mWANe>n}*!91UV5J7y6s!8XP!q>FRn|0z z%mGsxsS?;2t6!Yygo6?BC0l|@vO|kR;`N!?Nc;dfixuT$^ImBuN+=o6cS+4h{KArR zY)9=)U<-N|#mwb{511lf+|$}Nxav!7(c*b#&uM`}ycB;jO(TtFnzrgrGi0n_QC=vM$ zkD`qhwda4>xc~~~WCKtb=u;+pX#`J#MM5o(aJG6_oMifgGU>y5vX{k7#o0dxImj&Z zGKIdA@)a(g1fBW!h&eZ!YW=>qXij~=1AB9X3zDdh-oKu;61Bo^DB!O~*ExQ(lj#69 z^Vbw~A0BdM3coZN&teS)lm__VriVjsO|DOs$vS_*!6B`bYaJh}#s(xTR3tUz1}u!K z5tnZA3-oPhmxax>ImM5FA7N3Kt2~B0zFY6Op58uNyq0{ofh_ie;BQx^ARqG_B3E?W zbgQ;a^qNDZye(qU9W76N12OC=nBl;az^NypY2sj?H`dF~^J9{~a*i#mF3Nj%0)ur5 zf#-jT3k22j`AwVU@w0DgxVT5H@wZRBN8H<2>(cDsuSh94*hl0*6B|d<_HeChGkS}i z+<&zJivgn#+WQ#Q`nhP`yDEHnBHI2^p8;FKYD|r{I9xC)B$ZB8=$cY4^e*sez~r4WX9Rr4zz+uw?#U9pIBrXjch}| z$XtRRjeUh0`qW{98ij41^jPU=?U~mXJfwvzBJlWWjKW$-nGmr|>Im7BHoN+TE!dM= zhEbWAM6ov{(mAsfuBWdZ4U;+*F_A5Y)^=4o6VxoSLtF9W&5V&NIVjrWWjHrfu5+?-A+*d#VPzEh}4(%lbEZZ%7kjnk@hm#&?m z>C)-Z~!`rKEBbax%;g;d#A0idnRL#Bygoa?|E|-+Q zzM!wrw<*3P@rp5FRn_$^Ax3`?YUO9E-_I~epn~UKL?6A$QLD8vzUQ}wexl?=feVen z^b-+4X7CBbHE%hN6H>rl78s=-Qgh!2G(c}f?--wk-c31EwXsLuAQbfAqC<}=7SN87 zzdf^khG{k!YajNjZT|**a>OSHLW(Wn+n;5!j%nX4C(_2vgO(JD!7w{u>QBY|6`9_ULrf~a#nRM)vw`5Yx>F`h?v*gl0zHXIoq@)&jIg6+(>`RBx zB3a5o3ykRvWj>_M-TZb0q3iR9^n5U~NsA|IePn9U zZnplcPs*OE=~_q$*AwX^u-)nwyAXWYkSsAA1-**+oH?~m%R7I~*JpJXJJW~oSt|?D zG`d7zF7-09jXNQ%$&nYFL*Qre>(yDVZQ|;UkRMSt`gS#0%0ZuakBAqyPWMO13#{q= z^N_Ho_7|TKA_%3a0zgRkPFJf~^A!3s5_zL&cmhdyd?W2Ab0Hs6fYC^JI_%%+xv(n+ zpjayl(mU~{#>#(xJC4d@Awu_fhFHI*aeAvL=^uby0i{62vvQchsd!Qz1Q|e9A5z5Z zBb@4ToZPlLnUj>=qUTs=7nE~|_w~{aFWnCT4{v!pJ$90Pcm>%+XKc$xvM`?36KtY+ zo;dmBl;MFx_k=ZS@03^Q9=sh>eRURTY+q7hUT_j*9H4&)k=yyv#w90F$n=bs=VVSG zfsC?c`&B6kL%|G@Y+oJWiS7!idrAxk`f~BM->l79`I+q>90%S`djYt^^U*4?aQseL z61*Gh56_Sn*hA6ejjXQdoD;`s%JP*-v@-7&=eD@-99|{iOAK(k!DXRiA3o5Hl_*L0ZY%Uz}UIQIP7&J{|%yBN{CM(yoPI|`Lq)vZ}`BHd)IPK;AJ!<$zojgqpAT~sE z=F9R9f#D&3lBB)nx)Q(8vMM!7y4x0rS&iNRLT3rrz5WQ!RRYruF$EB3 z!%X=n(U1d{Q_g76YC4&Q6qC=*-No_JQYL|vK z_jq2#I=J~Fl4Z&}TT|y_%h#CR%#>x4QX+pB%xwIf4~5ZKn;$_H=!Ij;nPQ&CM9E6t z8Ji^wshQ4?xv;%8@`x$qYuJ4cm2vVM09HTpx=XWkR*IVT=lWT>vICwoT$5n<+1z9m z{`hfksFGbightz+*n(4fJXXjF`p_dFT-cG8%c*(wJ?zAs5xMW!Uc;dv!vV5+e2ITU zGmGx%%36@2O|6EIJ&b5Iwsa^l^N>u-Lr%Rk&z{nyTUeRFeF9|l<99Y+BN6zkuI+EX6>HaON=Bf+jD|0N=2o&q~7L*Ojw za2M=>sNiSrz=@sQ+zHEw5jhQ8_(uWi*|z|@=Jj;t0=!KWBG{vhxB#IF9I7kgj$zEB zd0dxc^@7YHv?tUzt1uX-T3?|zA0Vktd{!8Ia2CwEa{;J58p=uy1;%i5cI1DX!#Q$} z_+b!N&^BvFLBP&-Vw?z_1a$kh6!dGtl@#|m=fh=dU6l7dF&MWxVJ-!o9_4CaLLPAN zX81kp9gML`j|yTosLx>9IF1El;YaYtXzB&_8=JU=*XIMbBW!YnAO+lR>8f`EA0aZs zH;PbzNA}8(m@{-ptd!m$6x)9gxq($%q10ShqnQ{KZP2Ns5RT1(4^*5WH)QkDQSgf_ zUD98$YfiF~RP)=qc($r#}N^7)(l3uI}aPFcVy*Wata zeBaaJz@Ya)Mt;fnV_xjQq)Wci6%dD!%0{~cP>9>nn6fDFKv<7*3*vvel2A1~c>!y5 zvN~D|%}0%@O5#V%EM_^-8PKrgHiK`*NST}Mh`wAxzoftoV5J!(e6Zn^ndm@SXld4Y zBpiAq)`iRunMhpCI~!-O5k3wU} z$MRA$T+YP722%Jn(E@)gPsVLGjXlUX3jH-C1=rYJd=g1x+8}C+#iEoq6;lvh>TQb)}-Z_YwjDw4$tj(J_B_H-_^EG3Eyy zO9Gu9~rp6}eGR2hFL@}t4xMM@j#&X}oO zjhwS0rF!qdaGv+2{3f@!+(x@dsk>XgSgE2se6=KjRCQQ$n!L*OOxQd(SE8&gb(BGl zjk_E{`4N`6D{FjF+>viU5RVbI>fV_4sDV-eNwYdMZp4O!6Nxl}o`g0^PVXmi@hVxPx)E2CwMe%P!x@$2kz zD)F{uiz89$7fbd9)Hrgp+qk>N`fZ}SoUr8|vm444Wx9W_=dRgz8#q0b4;cQ>LhS=(0*n(ng zcJ8P!9?f{a1BJYin)t%zsmULi1oz>oz~RTjq^EpQ^K*LzY)5V|WMWI&CVrKowop~X zb>fIxh$UtnW3FS&e(J6kReiU>VG@U7`EP3t4)RZ zqFR56>+6q6@Z?uFE}vMMc~iopxNlvcbln;n!=#RI6dWHxEc=}MR*bORaWmsaXVW&? zSi^v(VYIXogA+ky0lWROAQ-ZTDtr=;IJOPcjbF4yZiOXVhMe#N>vQ^|DOK{nB0Jqu zN#jnK80#NvpC$lEWlHcuGsEEoWc^+z6)>ml)p-@q-*ZSaqzYR%Cwgo>D-aC@s`9{;m3@;RMZ z!Bpgt>kvYb2nnL(ZtO_!acD8U757oN^<-*!RtBpphW@cyB5|Iz1^xEqYdJ{(vu%H# zJi7bWk14N2c__6lGK-iXuYe;9W0W{uSn;;a6D0i60ij{^&&%#x>ucs(Xyr6lmeJ-& zRSEVyYjnNPqffErH;m!bs2OKsd1h*v9abFh(bf2O{f~X3<9kt;I8^Fp(YokO^0qDF z_wq#ufPG>i{kCY}ogT5(Ordc;@R)z2K90AeE7xlY3_Uq@)G(GK0aa zqx~j?NQ*S&$tk0EM%R#v(1ewfL{iWcS|5fqFkjNn=sOQLd*>Sdd+yhZCoE7GJ%ovr_6u>hfp64YRv@_T^1B{<= zv>%^B*GEK~c%Ln#ko(W`$==T$x94X#H zz%z2UkO8lg{i!J4eI76Nk?v*1UiY47i~0$|Yn7NdXoG}6RB|FFh| z=7SqH9xgfjC}z1bL;;Npkp)RJl(EQ(>WK>D zCT*OPtPME9a3d$~1@@d;a4PL0ZRKg`H!z$x*KXunx2W^Hgv)R*Gemqs9z^3=gK#K^u)51iO6-}Z0XjC;GAMpUwCW{ zbA;qCRSxd3Z>{y|-q<2d>`#tVa-Jo=9TdWT2KwW_mwrJ45C;Nvjd#Snsv5Od!A zWg;CqzE^`HL^=~43LKy#9$Z2t?H;e2;YT&{faxI5-tfMtHrdRR<(^AVTD_p#XK0k} zw@y@(aJGMLTAn8*h*#=-W_qR@x^=U6^7n=$4e-pWrGQy~n@)LPFv`BfS^M}~;79Cw z!7{Ymf7Mx=+3|_#QZ3IMF>ih7<%@T@APZF><0{zdMF9>g(uJDg;bC?^xG5IHzUH~ocD{9CFkWhf_M5kpJ;YLOv{`#hZJWR;?dUS?7CBwQjG$ z4-|tDhOpa2+e=5sL}QvBHusU}SL^rDIFkA9WHpMnkVo5Ej^MY};2X!nMBqD!#J+n9 zB)kDq&m1btgfWu{D=kMg5FuOg7HoO zi0Hzx)!_)rm(IFEK`CA%C#NVoYGw(Rql#a-jEu2`I9h_?>1w>!5y@xSsn+$ zD;q@>n)yW`CgK~vsPAlL4oqo5GM7ravCj49;=tP$Y zM{70uMhmO^LolB#zN4&{H@S3$i-C9s-pEC)7;z9M?IAZ7XT4$RF=-UB-lu_$>SF<3 z`N=`BoEVfn0I;;!9KfeE0RE~6NbP?tVo=N#Qkw}jX@8A+kt_h9uoad$LI&N&s!>r4 zkt4kMY$%R=zJrlw6ribb3ROj#N4gdR4)&y3w}hfCQA~?g8R~!z48^Fd)s}4tA^wfto z-o&U;wDO6HixV|i$>xAHE)lLv!|F()?7VL;%1@~iojq_*AnZX)f?AIZ08c=$zs5gb z^B>UD*;XuzPJen7B2QMx29T(?igmhwI|SJ_;>amEi2uHE4PrhC^H_=xno`Fyxj~M5 zLJqrQ>T7P}?n|_9I1MdtIiPg$^LfD$;Q4~;n0(}&W{1z{bWTPd9&4>Cd_5Ohl=y2M zYSZDebzXRVK+_!1krEoR8$Dl z*6Gt*o*LXs*Jf+i-ZksdWqQ^!y=;#&3q>S6E7hmS;DJ6!Fr{s1hmm6S1{cJk_%Vgj z?Rz~WhtgGXfDF5MD%s%}iD@K%#!cMysO^t8d2FqOOMMoP<0Wu3;Y$yWeMx%)(6I$Z z*t9xq^g49(IpOGWXwjt3%_errOzo7J+=Q7iE}n#CZ@XiZ8XQ89#G&d9C0o8jdCXIy z(I*F@Q)i-6r=m{}M5zx%i$*giqZ-C^V`O=Vnr@OD*yIWXkyU%Qt@XWsg%lR=>=cP< zOcx|}UD-_J-Pp+;o=EKSM`w~dJdxStj?W}^c_X%RM`_6dnn|x|+ML#~72F)x1ZQ}Xhs_u(dM=BwpXJS(OYJ~!7lwV6@2!u2;4AM@tF=WJ9Hg`G5Q_@NS%7>H zhcNeL-{J0fk-Y=LGfqfrQsv=6AJ7LF zSjo6J4`KGSVes;;^{?MRR0|cdbnu;_3z~1O$wBichYpJuH>sab`M)7~=xv*BL{u{Z_9OT=$3j2eZ80XNZ)S%iz1b)Dv z+~*{t^98Cd&ICCq?Wn;pKyZvb&&eC#J_-1~e35FO%LTjb^c8I{SU{xZrDMZ(YY!A} z*6_zhog#E15Fu{CY#g1bRv>574yQ{ zzHhFtdd5@~=L`MBHgIOftVSRaz{-uJjswKsXp4bDWTRz&H}qv4@gqAq^tgi@;vB`N zhv%neT3E<(yEJ96VhNu2s3$RR(?=wlJh@Tw_e>qs(<)uDWuKOsHxg;Uzmq=E*Dz7V z|3+>^q1594@SZgP6!XR8|Dt$fSW8``fbMQ4#;q`x+Bfm)u|YA*M(2iKmJ1%s74|HD ztd=r@bJiPwK$o%q@zTiay;1^`KWvRqEsT~zTED?&)Tfm}K;v}(VuA0R=j-=2*Qzg{ z{z@*BSecZ*Vj47y<($?W!`$EednX?LR=s~8{P$eoKZ!#x{mYKm{kF=F)c>ltFngQu zale|meYv=vo9tHm(7~LTlz-ex!LLkgJNi>|VqK1Zp--*!GXV(N>bF~z_G1L0 zT_?lzIwmz74K5nUfu?P&hel4dk-en*KzK^jNLxx+n!BQm(v@u?YfD|azRJ6$w52GL z)m!sGAc-IZ)A^-$O?HqpbL5JGqcI4AWfq@!2Ju&M z3;K(HE!aCeH*}))mGTZWE;E%5j?l9H4yl*k7P~UIJ7sWo%HZvl!P_f?wpRyit`6B; z9i?y^4qB@tjGk=o*|8sSsf8`>C7<#**;sE90%HG+d1zbfCRbb#@bb`~T0cGkMA+cY zXFhneeMR)A!x{tnhcYZ==5`h<98HfncANZvN;ZvHSiLC6XDAkkPjwz582pI9yhPwf z^$!LnQLYncHVN)?)8DzU}!8%j8Xn;%)DT-@4lxndI zli=R2akEC7I-;8M0r=!WNo33uZQCSWIO`Z_qmX%V#ZKYPRZSG$c46&yhYxdO8`>#< z!k2y`(Z%Xzfryn!a7v2RpJCAdw<`FghCo&@B<`E)VU2+b4X-nWm5Oc!UM`+Q?h|AJ z)k_qVgyp_)7ht7@%>2F6=K9;Gpl=&|7K!cW#T&DfGpF2IMP>tK7p>fsGHYmQATJJ> zLI>B;H`drFuG~n5tY1Z#S`~_1;DEG$QV}ZBDRYd1pH?oglJCpg6ZEaJ};3nI~(AAT0r&M!`0`be{>0Vn4AMqv`P-Zik4GH#;M z8}p=qr+NBj{yb87S~TCB^>hgnGtYdcF-EWHH8$8*n8olwpMa$cj1kC*Nd;Da7!oY4 zkQXZ68Re5dN62@b0KC@}PtsXo0u(bee(s?D|_)74_>PtVjpKOEw1MpFb@V+GYg?YtH0-D&wnSAwX1JV}*9 zwBx0jQ*3EjDues?cf2zKc1MJNqUpJC>dFsA=%2XQImC9qibpFVkS_vPs^jI4GNK8r62* z&lrohtuUm(O0>VgCV&(pZjh`B=3N1bbPlPD#06JJHfE2C>^a}&YD?*4^?YSEyjDV~ zS}o{P3F;=5Py6diLLY}VMZMG`%)q4hi#T*80_z;v^Cd>jOo1!<#W%}4(lee16p)6^ zIyrH)7OZJWzcIb!t$s0oBC7=Lm5%jNcoZ*ILT5^%%;@aXUMg3~+tqCJ`F7fVE%uP$ zBzcyHIRB#XkmajPIc|%@`xAbpDY~5(DK|}8%b0hRnWAUE8^TO75yxB~D|82zuVxaf z=`k{hiTe{6JilZ(-y7@tOnWO45gsMDI6Yb&eB`(WV`>| zL>*t`|4A%Wx-sJx>*qviu!wUp39vKB2PeKra(lHbH^McIqZwCZbmZ6t2ctuu%n(u* z%6>OQB+~!tx&thKAxej29?RVaO5GP^I*k?Q#Eg?4&P*0VLdnsqWcMXTudkEJ?7{d= zFF|WKREdzX-^vwx6X`9Am6Gg`Qx|yNtyHiUw(z>LGD_(rnee5h6tY?4CM&ttq{W&d zcJD>slOC0_g@?TOOO*N1iz5W_=!ifP_r~DF{}*HVm_>WVhTD1~Ob39XDb0&wz1tyPY1~@?U&Nc4aA~wRHv} zqP#(h3=g_s+ZnaJ!i}jWTTNuNjo|@fJ*g3X!H_cFL7=*YhJdz(*+T7E{bm5g{sP%I z5Lf&cQT`Yp`&hfh7Ch;_T{&g3D@j{xeZ&ZVeLl>fMn*TmBrg}|1EDq*DUv8ec8#7* zGG$HZgaJzB>S)#I&vLz4vuk~9==vK?72}mcma;q0>eOGj z+YQcU0MNm3MUM|(N_^~llOdMmFrp^@nhNlUVnGwJOmY_&iWDQI)&Uv~E3$(v!F)M? zjy6*b@?11CBB?M*Y0@$TqAyU?5M7C+qo4yMl;d<`ZS$A~D;r4AnEejIkoLCj2!ig< zBypILm>hdz!M}2f+=jPmTM|M$uT0Dtb|ja32gjq9dnW`BG#L$)8l83{yU4)|u1PRo z(8QELT*krX#{PpygmxbiHXM@cjf87|Q*sV3x*5@-KOmdD<^5jbr zrfZN5Rdl5U#DH0*2C{4Kb(5snBzpUjh=aDsoUoi9fWEXYhPCN^;;i|`v~?nXC>iTh zB{D&MAO0mi#KAWHB1S-v&&yx^Gx8r~H6!*)wP^Weq)5}Kl_U77LHX)0*SJ!Hbqk_< zL17OF2(}zmLry`)5f37x5enlU9%pU?sbX*(NA=W@3!Hi77WFEUZ90lhT`55&os$jq zQct}AQe?@D=x^ASa4M0kN~WKG=3=5wD-u8AE=zdZV0ebBV3t|8ZE(zw%eM2lawL9ZOw6uWPwsof_W z$0@Syt#3Iw;zjK?|7ODMKg#X7jIw59fK^Bk>Qfu~#* z756|;2a1j4AmM)=!mdq>%Sc@PH{f+ek@V3ENI z@^53pF`Y=K}_+v8)WU6r#>7h#W_8@Ertu7gA|jcWWOmBY{YqTosrs>2*5~* z`iITJk$RmrMH?Z1mYjPdok;P#AmOc1XY-cIcRQ33N=hp=Q-v-(w(m#K*QVXc{ocgN z1pk1o$wkj4H)5MO&C|JbQqaVNI-+*poZ!K`A$eTO-Ap%$TOT(H(XKa0bSX4 z(mWZ?3Y)Gld=yJ5A#I@s@o=Np&u1Cf+lt912^(3E<_M!v7%H-+TJUahfeM<@u<$M4rs$2qDM_hdfA+$66v{HC%| zX)@f0wDBi@+Cs(-^L|k6hx66qNRY$8@J$aV{>12u`uV9S`tS8_S#_R!dDx<43eTi{ zhG?S$iri-q?rCz#97Y1XlHhj&XnG-~}pEbOu=C)C!^}goHIPV*GiM_$e%g@pEz@DLOc6? zw7TU(drpqfrw&Fxu{chAk8G440d+$$5aDL#V5`0n3zpXWV{2n$jkp-R>TL+2J1j4J zQB+8OqUcjaX-5{~6tmF$1ML)D;2?`bhum1kY}Gb=mR>;$L_z0$hunKnqmWpW)~CV{ z*Kz~HdXJkTQ}Q>`dIsW{MGW}qDs0CJUiJ#VQZ*lSB0||W(h=fMi6$ejuXkqBmUae0 z$XRJZKrL8e0aT*e0Hi{x5U4`R-(pIYDN?0>RWYI>f~wgN5D+q3n0ot@F>@Ce6wFoX z=Tux%GZZKw6519~FbhEdQ9x<|_7DI73sQkU{(SrFwyrtnoBO}FfxNx*pS%s;?|3`F z-WKohV|#~dB$2kDsKwMA_KAzLh9a6WKQETJzqPMvg%o~w&b+4B7e%&suI=m`^n($9 zy30IxB2~v^%bl$Nc+*?AZc(3`0J`0WM^tUoCd3V-V$eaMFC$kY>F`PNCFueaD8Dn< zGo96td|-L+Ao&ZXk7uNF3yBuCZ^#OSy58Tb`=#Sedk5Jik2upV7EesS07?^F*hdCC z{=me%hQ%;XURPSY5v^VTEncu{+5i}T60ri-6Avsp27(!g>npQ?o*EoT>CKcWW#;EE z3|KyB;uK!_NENd$T=OaUfRh~GMCDwZ+Y%d98a3~6<(Th8tjOm6gDf9tqS#{ZQQmP2 zDs|E{Lezh=1^?g~oi)T^#U?~@!USUm_((H;b%{Mxm+IX~ulVy%+Itg~bT-L<7_ABL ztc!?IImwasl{JX#4yE@9QBW^L)ihgr3Z4XY4TQY49kx&WsBiLMl_$~9ls7231Zb2I zi_=;o(x;A@anl<<67FLD@RU1XPoJ3*UiHi-Y5^%*FDUe4 zblZ23E14-9U@bTFJq{u#JU7vQ&qjLWIsx@&v(3M4KG5y5KyEp7^vIg5WaC=0r(H5X z%N`doQ6d|LoVS$?Ie|H8c#=#aCk}i0Ka}_l;g}!#Jwqdpq&UCk6Vl-lS|96x{*%g z&PPE@et9!S4+~;uGABoW`0__bCVDElG<2z>l(^H#pOP#mlr&0Ot?kMjZAgZ8sa=WZ zLeG9p?{T!a!|sWr+ifl|`!=8B56H?85(NeP8M^BD_-%_ry!#)18>~$plkdZQ@9~C? zzT{n4utaO3T-jb*!pSzTH8SP#~kql$^481bLB^}Mvv6-n$*;oF7l#!`Mvx&`<5(dw|gx1-`LHd>7|s@0(YEM?4mniR>7}XTpvl^nc zuAg{QDmWpk9-I}1G))A#DkztFTlw!vzj2asbr%b$hi;n7%z!!SZW6zs*HaxTaZDfs*52&b~$77YMM;n;ln|uKCIQe+y z2D6{L6~FeLj$bzUPd1V~+6A_32Xv*aEm`83@lx4h`hb9Yf|$Ys*a7#O%f6Hu-Y}`6 z@W9pCXcL`(_PrpJ_ImT2VuZe$yR3Mr4e|k^C;hO|o28jX!7QBBcZzdd2>n+RG zcCpVrEtWjx(>lqS>n-OPRUbvM=&!~L=%mu|(3ppXHMZ=ek}khdURiEv8vJB&od$VH zc3gh(d6un-9&KV$^NHeQ2o!I@aj|%>NH^`r8F-j~+21b2ge5d+jM$q=g)k8vM)YPe z3(tju|0HX6WQDUT8W63DL#W$>lOj+$l}t`%V-e+W1SsK5qk0c8% zF~h&y#heFCFLBh$+X##M$BwwU2^T!fcgHEIj#h*0`_*!pczDM0C)?T3fY`t)gxv}Y z+XJckOX= zUN6w1)IkJiR3|AbjTMcN<_qn>oPtv2#5(`HrYn_r8k5Ae0`ZWCN#(dV%}uE*hZorY z$1zrPe;#6ge|qQbrw}`GBT)_weRvToOT~+SZ@B^Mn;-n{k7C<&R@))3`|U18taKQC z)rGe53$v(O|I4p6o*(|oDO)9!3oTQ&eI*0TZz4=kt&R(K>XR_0r~ZQt=uCbbzsp|kEI zIOmiD&6f@3A<2os|G;tI=@b!f9E^7SJ_kD))tZ-N=R)3V^FV0!A}2dayQ$LfjI8rh+L@e>>x zvqrBwRdXs5(1+KE!=ktD~QBy#)>@!ko)xd<&Mm(spDl1~73c&EEEFUtB zTNi$>)2+K2hgmWe9ZEk^NWFc3o-IlUf^mQ=_EnBk2$M=h3!A z9sg!P9ze9h)3#QyvQbBN z=x)R5O&tqSwHp@Hx6Gs$%!)BIOD~dLe+*de1Q#x-oQJKzTOw}>Er@-6TO1>b&56Z0 z=86*piUqiu>M@rn=%Q^7|Y>lU8W(xKI>2~#9fk<-)#uK)5dk3v?yZ2Bt_%NmNpk;Pay}TJigjJ*1Im^>p zo)M+ya`;S6nMd-bj^t*wAKX-dc20ab=r_Dj((|xg;jfOt1JeP2ZA)f4gWEEAv+{x2 zy>#zWa%-pg^mDScXl6xF$vL(c>O2jou37T<--S$wog~Ej(hJ!T3o@S&rTgD*lCgB*duD zp`HLk13z)K&havT0o{AL=XXofBK2Z)cP0hSUx){bWTgQlE-Wwe?3`E+R*M&f{(exs zXWC(>*PlY#{WIxL!Xpa-`VmZ&$j9PTCY+Z{I8dh>{}@ep?_dH z!ALB+gtpk2RAjXR?+5}0$>)qtR$-%Bje2GhVDdJ-*PQl$Vlg`gG$An*6kZSQtRx;>IzEf4)&JecvTuz2dP!1!F77HbqBU1#LT#t$hAt z1{HRf=Md(hhzENfSV2cM;re;8Pfpd&Ac@_C+Ak1)oc(|!4q4rWk45$gzVr$?u&2Yh z0?`#?NV7}YR#Bs+Z3YJ^2$Xhn z;L&9L!cj6nqCkA16}s6LSyB#oD>YB_BnGBu<}oNIb0qzKJH@eD^kDo5_8-=0P-uI? zL!ZijnrHdqZB)6cT1zksR)%-7n_yP=3IR-%U?j$Z&(mDYg%?uH@lF;a=URobE8V8x z`uG5X_z$9vdil?cw!*c8XX(bz7-Y)_@<);We+c`}D49Wn#YW-7{bU1=O5CkUlhSCl zrjM~dQOsY!7(;ds<%aN7aQqq;FILLz7%MG*ogSi*@cJRY??9~Gg9K%a$S}z5gZUr; zR@-S8w{gZTfY7)rugWjMSV5q2J0fE+N|3;|#i`V(GN#(kZV`B12gYZCkewQi`1Tyl zw7EsGIUiAPBiF%Se#CaG>&N)6HVfTg6G8Y^CO!{ zj0MgmGy?k6AZ_5`w<0ODHpqT+eN!`THVJatg|V7FTQjf^PsOl()S@?oZM0i7@oosY}?AU_Z zBHV=Q!>Ye$czsnbZVvd5{#!Y`8+Xk%NXu&r&&p4m5yFu&$@88sJec!==C)uZkO-iU zeD)Lj*0`dOKHI186WeiF#F@;Tj1BFd-MC%-&DO$YI!zb@=UF-FKD#}p=LRG=-ggjj zT==CXr@z2C`8~4wSTG|u$x;A+9Q;FQqm(`XeC`Cl?TTOcG@O{A{pezf2r?uOM$0_N z5{23J@L6Gl9CyLKFCfb6(s6f{;wQEUMyLA|h#qq{tR1=TyU_0Tgt${;$e?d77i?SP5tQ%OUEdri-$vAzb@8-xFBgqUy+84s@fxI zlU}d0Su|sdek=*K7hwAStT2&zU%EN%aAz22+qHLaE7=<=$D^q)tQSb>tG}(pWqyQBUre~O z??&`?-RUyBss_j5{RH%=Mik^sEMwbczui?4JZ?5IFKMHHjJ=$u?}*S*fYGJ0e@bOW zO0v@ThqQ1A{r6?y2W}cQ?eop6{jbWqSJC~HA-PhIM+S}`=_mK}W4Yk)*%V!S#nfvc zIEUV}B{rPZiFRGN-{;wyteUZ->!e=Kfl8loE~}~cFmOH{L6AM#Q(>aobJutO=l#%Q{!fZTXQ;>pf z+ny$R`j|?DFQhjMLO9=)j1DO+%ZrD2wtHUC*gB7sx=srFXBWIs;V@D;D(4I|zZo#Y zOoR_)m~+b-kR}tGT+@oL3|zlCFvW&e&P?hfx_bkEK712yG!X3~m^*GXxxfDwH1TSN z=%wuvOW=bm=mV9;MoZgFNX^X_zDsmk1SS)lwttTx$f9#6_(cUw&vDQEc<1zFm+X>J zJweu<&g49T`(DyG>3J0Jaqujheg#WNBk(dYeWk|yzw>SP<_k|BeqlRg>;{Om$eV%f z_!vlk4a-XMeIP{0m_twtuHU3|P|Az)CW=ucfqdmfB5EY9nB(Mhd&R^_0n9&Vs?A@% zt)OEPQGl=U;@Q!0eMSj$jEXP4+qmlB$ouI4R)-?`T~D-1s9?FCJ8WA|~-g z!=?@F=Ooa>jxIlQwd6ZSzKCKvEq|$SRe`)_OLR_nUE<9U@hTUvj@qOdg6COwcnqGLLfblr-Ca%TiTriI$f?ppGH z0-qSw=rq2q0&HFTBXq|N9}YA={#wW|?N3l(Uf{r$-l;WMrH>IFB&n<53#M+rN^-vb zeaUFsBwa5hyF1%Wjc@-&Dj?SP=k{sI96f*k#F@KBK5s1yK@@a?G%om~2Fap2uC*&b z(MKS$4nmE4VfFN*`(P8w2AhH){Z5U4jClKb(Z&dapl(j9S)3rvC3 zTPsf~4tF9<`hf~SiSb%M4W@C!%~bRCg;4*jQV+|dN`*KzU!8BQU}dl)KIo$XDOsc* zrpOCSnRr^+X$O!RC8GQ!?!$V_veHgkiq>eaU@kTaBu{$!n+ZLP%V{z0y^vp z{});x|JcMO2RvLruoxla@xlT**VED{wj_r#;NZ>=iP{RDd;Yj&F5tgn{7S=#QVuvi zc8Xorq_P?XZ3^3Xn;N$6Bi$##S|;?;Eqk?j)C%y3>R7r(r*a`huWCX5+CzhYaEu)7 zBzDa>*t>$I+y*zQEL8}VK7E;g{02iEZ_HPw5u*4H#xax;!MmD5UQ`J!2633Df%8VC ziAWG01$=5+*1GF4PsZDlgv7u~nO9NLMet|m{}Nm9Nfw{wBtbFZF(BspXrmIJ>zS6u z4g`#|KQ0t>(}OqG{Asb{%Eo)ETS^RlH>CB)ZBcv`uSf7s4hclwNjevQX9+SaZlKZh zT|nbXhI0ucIchbX<*5g@yeS8`khosc95Ld&0KTFZC>hr5E1lu)Msjl((#!Ru(BV3+ zi4en=V;8p`x|X7rtFIpd=(Rk^qSX}=KVY)xO&3PdfRy5!$$w({&rAd-;s69WD9on| zyONDLotJ$gdBftH2ejLN0dtrzWWgQX;4%)2BDFa)PXD$cxmw#X=eYhx$C*vBY0D&f z7i0G6SjWCf{rAogrgq1OjWbfR_$WB8h|YDTtwp(BRU8~`ofZoo9<6HTt*_=Id&IV` zc{K7+UEHj|t#NrL@8!wBg(Wp$R2+dXucQ5rH7$qrEi8RlPnT+c@0yO>JLPH5YV zd?Qrk&&rp*iLe%PeV0pIl6o{;SVbqa9`a&c8BU5vWcC=Oc0t4A*d!V-194RqzyZlo z+m8jA(NEzwt0ra)}zo|IqYJUF`txwT*LyEJj(BzvPP2*d(Z6J z$JT5c7BIx77!5lk>ZE_6+J;Uy9y9Je$Nep>_2CV*#U3I*m8ob*dbGc!LrXWl_m4;m zw2-?cX|yM0_H}7mY`vfY8&@$@N+h)6!j<*pUNnJ-FgXBmL|6+1TpHIsbS1*O&WoC`?@{`^jw{PsD= z_N99%=26WAf5fjQ?Z!u}Fqy|f{;t8TMqGX9Frx#1Yf-cn+6%M`Monr7|1md4T$RGZ>lNJSZL2}_NCy|V|7*9`Io z(llK;n#&jB5Q9QBYc?H|4w1T(kKS8rL+>wt>L?MAx|-&p(7+X~M8v`M{s+7d$>5xo~W<{R!SA(bngyVym7=3C3s{0*M>>FD=3zK zf9#3@eQJ65Q6sxeKC@*VeEzGUWItd}#isX0u+yHvtg+%)@!)Y8K;yDSG?MT@!JEMU zGq7j{ff6kW@$Q}D_@?pUTwW`#NWk0PEq(*AcExzwNS~i+A3-|TW^c&gqaAcQYx;GG zt+#b_HJwLWD8Rt^zLvaJE3Fh_xWCGOmte`t8oyrq^sSx~LDU7Xd=>6V{uxIxMYuYj z>Wi{9n|)SlwEF=9)2e?f7ED+lUZc4DX+BBD+ z^IP&IX(8;W(&*KV|2)K;t@x+Y9DxA(9%l077U*TAwZ zhAVi3TC|!dYxU%fKmSAv)Qsa)K)xe=BlpA$8)CdbxeK4+5HAm6aRT3lqoXpuOgcA9 zhelEAt{=q9wWj#uIOljR0ZWVV3%jV>N057Fw-A^*gFhf0R;_Zg+O&7Y){9*nLVC7x zT}Tyj?;Pz~)L+<;E@*utK(;P_G$^rXDq}UMy9!5;G!2+Plh+=+0)NJLiOfnr>XQ#X zJm(>tbFq$)bdp6Z_i-3^8N*A=($0ux;{o-vK;^DGGo0uM1(3ZKgny=b=aTS3%W@pV zB+RfxM&%*hJ9K|vnlx$%FRpQs?vD}$2y;gzOE3jX|Cl+u%@X5lZf3K8BN3|%*|ZNc zJvY{-8JqHy)(bP+e1&5B6q>OMPXQMp4x1jDKQnXe9bS;qqs*^2r3??G$~zdJm;+Ot z{d|41<%A@QC`ONpBzt70PxWKkaC;w6p`Fa(s@>cO$|0`0y&49N50h&|4iyZ>T8Xoo z%>0WUTC`1~^3T`JlbZQ|pjI;~4ngxchM$Y?95naA^oW725Hqcc8k6T|jeCx2J(GsJ zk@3fdx(m4*TWD)Uv1rirqrG3=IBoU+8A7zoLvMEw+l3or$BnQ@@fC4wlMMpn1IBgT zJXdINMW#9UJ?@x+;1SW(d}h|iQMPX&&wc2^;t)mtW|RnY^<4gcnc~>_aopAKAVZgW z=k9&oD7TW>oX>M z?P={J`?On}4CKQH%id#80ga@h`-O%{f)+OWXq@rF=+oPE?xUh^AoK+S93_;Vm2`f| z`0i$UCYT~G%j%bZe3M~;x9!MFUd1e*{=GpoOVT+3Co*MvHH1hBx3*T%>G__i*l`uf zhTmCoKWVFMuNiYor+J0YK?}#03~9HZYzJ$2y0mPe+a~3pO~Jws+NlFSUJ8&$#SSr_ zz^>^@pf-tc+YFg!AaCt4aQ<>sGr;Yxn))!n#_2#U?n0dbHZ+o)0o zakB?TvM!b7Pc@$CPc?T~NME#etp=>PmH9IjjdpVmx64Om)A!< z2f*|u5vwBdK@bvLH;UZa)N2AJqUI5;+PnY7jw&snY~yWo%`}KpvQVpHA`io{Nj<3| zt)I|;O|(>nL?SAgOd_8IRpwW}|APeXt^)*6D5}`CsAX328^}<~NO=IRdHI;z`Zae8 zyZyKXYEd6KT-J~gX9lrDjYEZ6c2)|EaX9PRxan8(6+CD6#iv`toib$OLd4F?fUjSG zyxWxoP#2V{<`Lr>>G|cyzN}&=&CwdD@DXW$2L|AumgLU+ zl+JK(oNBV*fl%3IcYld9RPCH|)o0DCD2}c|iB^08=8s7G97Wa`)l}UpUuJ}&BMs?yvgiyvZb4neUUisDLOB?2te#&R}GO8oR-ZoE%YI#v5TPHB_t+n^BZ5F9|cZ@r@so7n5gEN|-O z85^78MaGE!H3Q;>W_@_m5a-hx6FcXB!c9CX21h|6kY?vSk7&2&t@X4lHzC)YMc#j8 zk&Luwk;KIcRe58o`8q2i!5sE&bpKQCvIjwmy_rR??MK&=(W{Wut()ptk46R!AW{T2 zh}T9S1jBTbhw(weqio|eJSGc@jME^GSXjaobdjZWIZ{9(gFg^Po(Kk#jXKJI89xbt zK+4(`_KS!~*h}p49574_k3R@|2Wd8RQ2mqQOz9mA&r$UQ@EP8QDU+?U+_;6+=9*T? zW|{)8)}LvBVHtx7Auej7mn{RP(={q7TI;#>d?uM^5Tn$RHWZ)^*6^iD2ag@QoWpnb zaTol%1iDqJKl6y~TO_ir*!HD=?_5G1Hc3fMt|%#85o|Jw(FNkON029=0OJNX^+kZx@tu)JPRw7Hv)ceM^tnyA?OEBEYU8kK&f!P4 zAz9o+bNLQ38@RWPTS482KCKY zJ8JWP6|C0M4|K)c8HUw6XKj0p`?YDX;yjnC@~XS_#1O*>stckHDhl*}KpgAJZ|+e` ztp2@8s__*`MMKY>#4&sbedGcrDzGPei*!+r(4-f^ka{L`9nf!L3q1EN77Pg5qS@JW zeYUHsVDt*%Ir|eb$LJ@25!+c^vFJORvPPhR9{gLk26FfAhUx?*qYbi0>Iqh+ViosO zCcw&x5^wtBGqdGtk{-QsJ0moeucHV%6DM0$pTBf#OuKR+WR}zWn^FjRaSZ2ZuH_!P z+k(`rH)j|hgX_lt^L>FLWs=4(7*d%<-!P=9<7V(qx%&1m@B@K=vhQvV7yUc5&&&^j zoTL{cf1L$S8TLrVJrBkg8HqI9S1eSDM1haOv_$jEa*`2^PL0J|Xx`*!5kSLpl0WKd za_a(MU2iIQ&2)aTKl`7rxjze)H|1^3S@8FxJ+)pt_X6p57&S}^S3;G{orCf>0&%_b-W9# zSt2$tPIMpYTif$%Tw z{xSKdh5-4{;!-zf>N;KMrLmbdBCVMhEDjj(r`=%fo9SAwNre;)F%1q;nb4_bbwr5u{dGuh5hu^Mp4@$T}`5gN(B}e|VB3FUe92^JlJi z@2>;{2fE0As{B^UK;t%x!6s_cGD)PZYI_TkS?sGN#(zUAoO-*-NP+T6B!56;$*P$I z8+Ie=TamO`erLW_f)KkQ(O6Jv+Yjj`nmo9mq`BaeK;keAsDvSsIs{2|1&Y3y#snkw zQ!~a(_#BMS0?|Omn*{Pv`}B`@5=kTU-cPwPZnQjqCXazpGjf&|=tO7Ec!y!mUkNNf zfu!Cqu1?^*kYRfWn6@U8`a*tPNS1?M)}sbXc(yPM?gp)DJv2CgA0^D!N^haHHb;_` zOC=0QhBpy3)V$5qxleVHMH&ro)&*$(L|t(E4n& zPVudOLdGTjbbweWH}{C}I}@pWH9=FreJ~mf*%v;!Dx4)|oXHH0AD696A-lw3(`|p( z3v2$fUFBbTJ8fZnBKz~2T&ZoOu!83l82f6(Bmn0d(Br5dWQr`vNPU5ES{z0oD^b56 zaX^4vFymBh$D~dtOyYA) z&LVt7z%EzZNNOm*fEX$!k%hT&jO14IZIuQlrTsHbL6d(v!o!Z_8&|BfHxlVtD?shP)u1 zdTlrGCJU4+T1MHwu#X!^!`sm<>QoVbFWblRA}p-Q_+4-OZx=zZtVVF zGW*K40o@Kd&J3$3aNJRrcPO8tD>!P6>@(x_*p|tEhZ{HJ^-an|-~0H7P|v7;iI*0~ zLE#Ma!~{GWuk7@fKGJy7gnLcDJk~J^52FoZo6G z(ATDY{t9^cP?~HPtM`gW6OI`?IP;uH5bA{=A58h9!z63I2`5&7d2}Lx!koD$ll#pX zD32;<&@w0H`#+H2l+clZir*a~4Gp3l+wAR0*HmSp{6F5f z!(1V%eeSFxHUzEvJ8OOXTl}4=@$OLVb&0j-`m&UX5fwLaPJ;R~xk1(AIC!mF{{S#s z*EVg~sIKH*@q+h2uKZqq9rb6y%+tZsKN;|MlT>^i*qC;>Ofmg4!;hqJ=L>?3Q3anJ zq$h|();TQDwguPR^$)7QU+5a^)5*t9C3(t0&0)icVI?K!K9Ob|Uk+UI>yXQV<*M&d z#C~Cr-Nr;I_`b2@-Sd?5WC_jvfRtJMr3hO5KWdrAM4KU|vwKp14aD)UlTbA_GH~EI za0eila!NMwUU@M0a8hf9)|nQvafwFX`AbjKMEiJUG|J^0JtFT4LVgZ~D6 zaIBvQQRgoyqxBO);pF+A8u-j^uzVn z39by}5Xh8&Z!d`$h5ND*gI)ps(C1??d_=x?snhzAVT;2K&dx~Hc-QZ*`$fDubH#mh z4semG%wN8~X+_|dhLYo2DOVEZ%=aK!NDppjwseqk5sIORUMyEr(&l@88r2-x=9$FV z4v2}oFnz5G&l_W#x_Q#KZ_g^zM=N%V$pP?eLZ)(m{uM072I@6y#MzazdJ7B|#-9hm z{iQ@F{J<)cE7U1QHM&hDAxk%+<*;Ec!y)`MnLps-!vJ1&w0f4tp``Q`cYMt%EKQ*g zrxo3`_J`fn#W`#3D?q_oHbi@lgnCwGoNcsS0`z|?IA2M#l~n^pXypPv+3uWdRDsZu zqHCys-+5pYq|hiMg_TlwX#^|oJg)pjwsYJW8@^}qV~viM%BV{MTR zS+Zw%zUX&MD6-VKYC%VF@dIwazt!E@*izPi^Fuds&@of&A2U!Ex6LVhaGMBeh@ada zeaJHQo>DdBG|8c)hafaaaaC4-eax+2GtCn#-HYd^O~?&&WGjzl0sz#TXT0*cvxkzQf+lKHtpfw!Oz zG80foe|O&WlD^4g0Wj9R;Uwg@6VYJ9USx78JB5?|(M!L53bI&6p}(RtjIrZR~71Wl>H11^g}1kl+F zytzTtBJO)I+&p+nPkkseS`-+`a9~f2i8zZ=0R^UYGv2|6A|B`u5lJEQKzE z3~%OtGGlo#Z4f#n-5M4uK!n(h*nz*~fTL6?cz6hw6@=FNM%bsqsI;WOBQ4u88A_Dar0Qrg)n0)Zn(u0)&}T!|JTPE`(YsjI z#fSHSL}z7?Ef7SFnWUTNP%lw43G=!PvL}9Qm$TN0RG38V+?Z)v+#z~SC(u9zhCd!y ze@CP_hJ_h{BxO+>iU$HZZ!qbyB~2l5e@(y7l{DrU^4+-fMcgluKx#y_+}i7Ib!fj- zfn@z4STrCk)MYxjYFU699J^K!l@_0`cjsR2Z&tgGp=k21$)Mn^l?0WFgr!nc_eWTZ zSJyhM?)#aktp-cQ`IWE_QpJUH>G}khkl=9GqxF#Zn_&=NabPQ`%6e^u5>Yw1eTkDEg+9vCQWE!^F|7ApCWEPt_1zI6Y zK_0SNU3cpZKdaB2RK}dqDun)Nf8Y?w?;S#VH2R7x+@=M-sIXDG5dge374ApWnmybB zkouzCR9~)=JPMF>4Uw%h2M`;6d;!H5`iTIm0)dm}%3uMwglM2nvADBh2|h66+4klR z3O8m>MDX7HfcxYCa7gXdu?Th66+qmJPRppaB}j9vlOEPIXx7NH+1f7f{v*&@ zbbXjpl15OJf*0L;y2g{21(_yfGC~Gm1PlNd6oX`{q>T$^SW0FyAa@rc!2t>hQrM*( zfPzfKfFJ{sEa~0Kw`$s|f3&-5?R9Rty4nCL5FsQ1sK69O6o3dHu;>5)LJ%f*|L@=3 z$pzZpZtuD8;@#&x&wLE~ocF+&zmMD8J6mLtq*qX?FG%ekRAYMX*W5EawC*JubH9Nn zdixggmH=F~E$%jM3dQeXf?hY}Y{*Yx5vw0ieo=1Vv`yT8V-vI$e^IY=Y!;)kzK}k0 zu9LW7>+46ZdA7)ZknHRwFuOh5g4iA9klUZ2#|18H6ioZXawJ^&PY-3^gqP3Ye)B_e4($Hf5|B z-Y;gNG1yr#At7j5bGGZ}MvEP5rw%AQNJ{2t6jw6&8rxFN?GliSVEQLeR>V!C)GnIA zgo18*Ku&s9M1Ffxj2)~y^c9EpF~lO+>U4E!*QF<`!#IOze~y1%SFJmjxrg)okpeVt z&d8$&w@vp95A92L1mM{oi1ySNO&H5|QplHuXi@o1w@OebmbD@Au#WIdBuA(x8W{4b zb`uIY^ha*hpDUCjjrX;}9dgMyk!b%u8B2op$;@9o@FGEzu|459X#|_@^5FTd@M76c zFZ{(4ys3EtfBtwdiA}a~4TNa=0>CXu9pL5^7aS+w4fTRs>}s(}1%#&oX+>(TwJy$1 z7FRZeu59kES(+qzyiHRCaBb<%>K@C^7?!)qT}dE;*cU8R0Q4Mbe$AKt~l4P4N>dM1zahS?i>q~FZ{%fxlvOO6-E1&l9 zO`bhzIR>3lkZaSaWg~Z`uFshr>V5mtl=fo@21>vhqx9{*r0oXvj*>MPk?NctQzVCV z@>`uX=ERJu9Ttef#`v6vx?zKb8lWzFqntC)L?4l`Q^V=OP#mrlNQ{UK$^6311Xi^`^$^BX&$Pl+#nhH8SBHJ}M)~^udYhZ@< z=LI-r5>#h8MPq1Dz`+;TJJ$0AY1NAvq7h=KbcUU2%N-aIVNu)j?p+$WzAU6(D$H^` ze{N~{guaH5sgj?dj)j6eu9eE~m4v{Y7}`wgk$ASq&UKd}1E?D&%)t&&4_IJ3qS(Iw zm~zMe0?36ztX5QX%@1w1;ZV~Z1sIXl7@A@KgSeDD5B>1<^?$hBdyT^rG? zutjJ}@A{$Cn%#K@jpwQRO&U#Er(MT(e+x!=+pf6!v$q^(6h!61bY~XX*0{P5<@Lp< zk-JTjws}hQ;K|CU+QgZgM}uuA1qh2clUQtpB5-&cdJy^9FP1pktohL_*nK+n;G`$! zvt@-MnE$7mbEkAn6!B!oKD>v2w`C+Vf0e{; z&$jiFSgUerKo%q|=l2RVKRvP3chO~>80alz>9heJEqkT+Dd#v?lLKAkDB1NcX}{*2 z2NMv4_naoKCK6Cl@y?Y73KI$96`Q*z9y8*m4K4DZz1hKYjMizRen zlJ^PO-6NsqT%;}m#OaW4vSP)tf37~xgU_>|@9XGfZiSkmMD#@@Zr%jjqFBtLr@T>L zl@g}*2#Nu}jv1yX8ws0>vmTXQqBF<7PZn*_iO#HTO`RR6YO?*2x>NmQbm;10u7BP) z`9>th{)Pl)8q(A4L7ooIqp!g;wo{H#*~u}_y^Zq8j$s7RG0aj*Jl)|~e-&gVdEXJ` zj5gqGF)!q?K#?T=)tqG!&%&O=G>(doAZmGsB^szOsnAn-TLbn2(+(iCF#F( zmukGNB>y1IY*qK&!+nRFCW^3(g>v7N&YC5FYqS=e?bLnIyiNkI2r_K#m$J4y_XVOE z2Y}vhSjlg0sxOB~ ztvP^MBc5?|$GQ&=gz~wuNY0mZvJ^!3TJ}pfi8}K1L$c744h z0<>N}O&eLZa!_m-H_crihb9Ij{S@IpI`R@uUTEU?qasMtx`$PJn0Q?^xU6$6VoPbN zn&_d-kvC>E*7`V&*K~4_LjNH0_xD;RZ%7Zf7m{nW>)Yrfo?&Q8cqSv1za)a4 z*!vHVD)g1;i2KhjBsq6lJXi?$iZJ3JKV+i+f)xoX=gF&^Qn>7 zVTsDk@{zP-7$e^YERx)pxsJ7zY6351jZothwt2)Ae}*Y3D^jE$$23SgiA_e$Vp>9> z%!<;bPY_P#sqsJu+x$s>n(gmIw2|+x*o^#q1GYPV*r9sRRy>yD7oMc9i9}0B7LBb! zll+3)b>FTRz&#e0)Cgu7Osc55UJt3Trbw?r>+w@pF<+Pvj*+*Is67KW>Ks+Tg@B2b z%FiSte{JucIfM)^GT(S^P>GyIZ*f?BKs@c`4dxm;1bp|R%ZP)$q7XZ%e2WQVX1c;7 zxhB8Wb?Fdpw)93DUixUdBhE`K2>sWoF}*yC2|!wg7MP=Pl}zHOajxn{rLs<+R^lZq z)#%6Y@C5J4#y_i&`=f>BmNbW8A<4sj=T(TCe|=YK=C5884SlAOhKG140Qse%-`qM- zSlNi@Y8y@Bu(2D(+R86>#ZN?W7i#Es7vj&_zjLo`Hx(1nZKd<`6&AAttk%VFyF}81 z1W(w^#0yGom%ie&MZvOO?~2foeCEF2o^cC zXO_N2w<;RWF33>SZk4_j3|d$u!G#>Ne?p;%G|{ge5=|kGf-eQmqWgM_ws@w+4Xbi3 zB5)wa$n~mIh|yW8I)xtc^m60Iv7~76em#vN6;?EoCxT!Sk^j6Eef4Njn$h-|jGLOm zeFiT5Hv_`0rD(q+ z)s!36b63R{rUh+rTv)qIooypq9@<3oeZcDCycljVUHh8Pc$xm~{U9&of!dN$E^Q^USl4aj|_;)1RqHwm?g zb}WH>$7lZ;H2|*H9rarf?{%A4cafDnn(hTP)>goq>m2H$G#ibF*%C*ga;wk>TKsIo z^}hCCl3E+vn;nwh;w?mif-n+`@CMN1;yQ$jPBF*ynkWb>f1sph8)PY?e~0vy+awp} z7C;LY!F^FI#98#xl_1IweMh^%$p-Sq1`y_8YWeB-`@w!{>xp9e0!kI}3}HxrdCcqy zx@R-6rt5~%(VZ*{YGVrz}$?< z*ZPdhB6?djW$2>&4jLm*c{$ffB^|Eae$Y;3(aB0l-K}famnN}Ee?Y=J0gD#8V&r*@ zv0ZQw`N!jthnM>)8~Rq^MwN*q-HR|&A15@1ytrPm3TE+x^~zjemYZl#c9l3cv(Yba z-0IvtQM5^L4X}3`5<4|>rR@&M*Ts+^<7W-bSrV}FjBVW3Y{)RPi%do76b!b}b|MB; z&XsUbG7E~v?Zv!Me?eEZkKBtB|GyvLk^iq9XA5?}@8Hv1_%H(vt6txfGF?DdV*KHj zpyFs=I*uVmelF|bSj)%bip4j3tEZp->C++hY0=m9)2E*z>rR;!dK31hC5b5HBg4R? znFf$q37K9_T0UYyD9p*uz`S{58jDUZ3bA&MegP(rc1ZjAf7k|BYJ{%^DF(5+UAVv8 zbd=ci(S^D(tHh}OLhOx_31#Q=AaS#Wp~g4hUd+}#25T2-O2@`#v2az7+GeqFCaylp zHV)Ii<7G<8)PM9~hFu#t=+woFrOcY%`Sl2^U!=5mQ<)q&xe=%?N1Tdf#T;`;sW*#JaW=Yi4-bhEdBST<2>v_r$o0a5>AcR0ot4T_c zHfXGW4*tUd5g58I{AQ&b05;%CQV{tjE()bw2(?SrkgvieM41OE6Hd=@5EbGZSQw&M zWhXyn&<=R<-7adFqL1fIuKbq?%ZVf=wL_pQwOuCdKvt$sMr?mI&ECIVUFwAOXd#uQfX&kjQ}0+} z%8$QF`x+_3M*ob3WU@$`rb(LnlS#UI_Q!PYf91#ijijcNbdJirwX^OZt8H0p6YWK| z!8xq<$_yCS1yJj6r&DQWeU>@iv->B>It=VvY^*uB)Yfrr<@4_t+o>70t}Rr{O$FM? z^vQTN3QWv3+bc~o7ih(NTWT1SOjyN`-#*=3`-RtJr3>(5Q*YwixmzUj#BUbc7ZUCt zf8UqcKUSII_CtxE8eA%$c2^|vX8=1t=J#m`ph1%GZI8MxvR|5*X3_&WWuVyNuDItYh{6 zaPPhjsm`i}9~j%aomPpizYsc^SoS5je{71pv0CEavQ&;y}IB;lB(1M0$;|+H)#Ry2BasWcTtMvESqi*2!I7L zjH%^6T|=L7QXkR3zgL!S=f4%ua1V9A?duzCocWc`bwVp=**>XQ@Gk|gn;ulMf0FI3 z)~F}KhK4`UF6rHvuvVgFajqFCL}bzZ(u|=Ir*1~*4xA@>VhykT+_|4dH-(qSj874_ z2oZIziOLD}3E5(Md$svVw9M;TR=n~}`P(kd@{4b_8^UcHHH38uuRsIDVLwesp5aG0 zF|+k0NhfU|0z!bv*acIy(n{FMe-WBM=O5}a6Kn*Da&fzBM>_qsD=nYJHeT#FE&LME2_c3e`z|FSr<$W$^(Sl> z^h;!LhX27CZeRtHlKIC_(%_s&ZM-;YelmOo83xjK{N}EN%|lU_7CTcQf085PGMY#z zVm;50y zj?qHTzc6ZhivEt6)fO(_TF-9+O^t-n@Z_3}i8J&%4&(XFOz1FG`W)5yLyupJXV>Dn z`zbXv+A7>XlxNR_@*10zFr#=9l2y1z?Q+Vlwh;ReSA?#c3U`8> z2Ym%zIybwqrf_v8f!qL&412p%kNMQ>3U6NdnlHR&$aX^BChoCZmec2hh0hW9S?{{J zwiAnoys77dn{&lze^9D)-Ya%8(C~LSI`8`4S_W6L%}vcxPyHsOf46S53@Nis_=AMc zwFLO%SK4cgF*UC>Ka?vy(mCUakPRZv8mS{*#@i80)4FiV$q=V_%I+Yw_-i*VX#!?; zFRJ+u>wG35w}h>-{-hYYHxOd=6p8tlZWh^IRA3<(o)y%g2}F>nC?WoqfVd`c!RiZC za&EvGVmRcxO=jo%e*lXkkH%dXChulcq&-tv&kqyxr=ZYHG(Cg`%OWIhu4L|I><4+Tp@sZG}f;$F)nv}FV5GP z&dPO-B!@ZGymqy{PDr!$!GFI6an^EDGL9OvP+VNq-rq{K;Bp?MSV7 z6Qeg(TKQSXm;NG@fwc`~Om*AfS#QIpCY7DCqHo;cjDxi|3@iKc2| zIg7vLsXMdNe^BA#UJ7wO#o#8r`%h=QQUzD<-4##8M78j?$~=D3B_5)Us*$nnYU_f0 zO753m6^Do=*Ip2v#deV}+K$+}GUd=0QF{U1y%xk`*UF;0_H-)x!oA**rl0A&R?H3l zUN8Bh&gstlzO-l?$Hw=i7X>ij$(IIAalD@3b@A!x00v!%t%@$(Q3c14o2D)@c8I+6txtrKvlV(h zi4q8hf0=un`F;>oSD~#dRXP^5!oWp_tkfIOK|T+p-QqdbkJ!IB#? zGtw@V(@1TAC~NLl&&8qvRW?$lyq%R9gBHq+pc(+?Q&i}($s$>Z>En)sZjX5N&&Jox z91Yk;BJRvjtV%Vu5T|Yi#Dq;DO4EZu(7^~tf3-*9EkYbv#)7P?v-X`Wy8nck9+lBJ zHut0=7U6~iMO1b$FvU!EF(rgW+%G)@L#cw+sdiXs%A~9kYcjIBY74+k$SDJ2cJRd9 zXa#+3CVhrIo+9PW43@!=#8UJcJzKgpX3&Gx!2I6-Ip-G-UMVsW@SmrwfMSf7Lex4qjpl9 zXC@D3#enk3O&{ikkI;!;UPl=$vl5V#e^JA9%&7);#uVM$kQgyFW||+T*{M$6W72TZ z>LNO_Z;JF>G%47X_b1qKRpgeW$~b*F1!Q;3++dYl)@~_hfx{fBhbX}&kiUvUS}0`} zzKhIHxVP44tu8kvMI-iapJ%hcV5!)cVeIKk*%N465D*^mYweMW61tzA$ex7DJq;QEI}eo;kY3W_UwR~Z;nxG_7FTm-LRE#Ho?O8#$5TT!oq z=luS3eIu}I81FOvf-@jBNWz`bhk8Qdm5XgE2Q5))anD{|5|&|NAAm#V<;{NZ4v{lb zH0FT(MaB}xu(oLvngW96tTjH;f0DE56eg0=mf+#qTQYx<4|rQ{~4g)jUG9UC*B3rFzp?(e*!E_TA5=U8R_^e{YEM(_m9C z-Wo-&4`Tlyk!yD44hnH0E>vMCFEvQl+Y9kP0xtwh58_8QS(&QRZ4BIZ8+iU{2u*Ds z(M5QIJwfTW`vSd)XjDy;Z=9;x{p!2QRl6;j5N7QB2{<+QKcM zH)pFQ>9Iq%?=#zVb=&;~f7=Hy#QJRZ`R%TIenEEI4V8Xy2FEpI<5^r)dYRGbW*e!n2wkRTm^Y+(J4<}Ba;TnwUJsr-DKLdcozgk zu9NV%X(^_*g4l#B`jl3eSdD=8)LNHVztSgvyvoT5R$9L2I^X-PQtchaJ_^}pg$=9V zoe`qA zU6^dK@S|UP2dS#({^4v%U5qEYik|8EyAZGb#yqTE1=^8aqxLp9$2z9hXZx-E7re@w z*yNdW0bT6xzOTyvb<(7l+5^Q(3fNEtj6MbieX>IMQ-)-g#*W)xZHp9*#Z3$#=%?fthJVT!EgiXNb<575D zjPE`lh+6-I0AR6Q9gKUlIlzlO*lzwc7D#w%R9AQKRkTj96zKC6u^z8WhBxnh9uM|q z3WXi=f5ILY45+eP{5L5ap43|jMdlWhD5eF@SA2?*)01F18FR26!Fqz!c0*!V5G}1I z=pp1$X>w(XI${m*GB;mK#`yzBn|MvhCVYWO~{XN zEk3axvYG_qAoEHtx+(-dPXoka`A5L`9-kLNTRPEX(b$(vlV-q=7b2DjdN|~H>`1wW z*0VpQ7{=sz(}?Hu49ydUpkcR#up(Lme_C)kk|dh3_=p5Uh~FTGrw_))x zcjBmck{PfxbP*RjXjYAEZ1<|vBf;d7v#wf19L# z27uio$@0S^ftF+!xNB~s0dQiRyr{NR9Tj&={U1}MApbScqhYOLvn-V&>1l?C@SdRP zM0pAC3Z$(ug+LtfT(vMfZ<+5^Gf0J3fwt(dX#Zg?+ZV>=iAv>qLh@*^T!e>GrfW0%$v02s8?*&~gK$%`V7gthft&S5|AAx6Y~>y*!a zDJP=?s|zi$UguTZ2?WhP#+_!6ZmAZ+Q4`{_U**c>sO(#2ntf{*n?^6|WS9jm%Y%LD zC2H%T z7ZIE^R|hsgwJa4!e_iw*fkHiv_%Vr=z<1^AtJ(Tb&OFvBEwcGNDG5xZK_$!qDO)_R zSG(btb?)*V;It8XC2GQfq}j*h$0dAr`Tp#YTP%vwJZTd5Otz8QFr9-&nM%s(EQ}29 zwmnSzA5r)wdU7t0!R=efl70d{=obN>f(^PvWTL5Lu1yd|f0QZ@X>Zk_^K8&<`lt}N zO#nvM3eEJ6aS34`;>emAIozULM+DJ28-k>6_HD;I)E%v}1-5U64+RGaY(&@(Y$bXq zmK?-G*Cgi1T|&qZ>01Tp4cjY3YD)}O#Z8}e2()F+`_XjK_t;B=9jiZn!#-j%{UfyR zI<9*E;%6Jaf0T=%=fORE2*}TCUUP1J750`BX;utaAQ^Q=PwfvqbMOVDTI+FmMf8v7fhL}iB(fv6I>;Td8Htd(w zR`-b7fm_y+Td+fyLK!TCKrC#sEjW0joPCNu4uNROtFIybI#Qm@vBOltLR{$a zufC6SjdR&w`^JnfR6XSuiTdqq#m(RwR_A);RWg?J{i#qUc$^)@)3Q9xy6%~(Y)be3 z6sycGf9c9syhr!u(zQ)y_9hV+U>l@w`34EMABQWJkLnIIZ3auPzD9pHB__=F?q2O^&0(_tmz=BbxuKE#bM&Tp=i~)(rbx)gBfF_{J7%WC1GNyAc ze>Iy|Asj!1Iy99{vEWbb>xt5n5ZyzlPk9jgs84iHB4Xh^r<{oO`<+%oZvrQx90;{W zpPXt0VL&%Pbg|_=Ul}g5sIP1@Wzs$lB!g)>X5>m9t;_7J%$UcR@fCa~M@>>aCTEaV zOostxk-%m6k}h5I@)FugSH?@dKYUO>e^sC4km3UGvxwE}NXkw1=d15SSbtGVUue!$ z9)^IpVwT3y-xoY)&zS|E@$iqz^G>XbT%Tc|t)Y4RjvR(Emng3mWtM@K$vQi&jSg;p ze=jxQG;jn;&3Y`MGkYcdzIp5I z;Rn%d;FpG#^eD%JL9qH~I;v_kboFJDIxOllb4RU|WB3Iv&v7_^(TDbktwJYP@r}+Q zMx@KaSAb%UJZ%xFMn%Caz|JEaZQY6q{z{c!h5vaR8REs1a` zdO$i-(nlA^+2#c8+avdH4s6K{gZz?_ypPyvRD72!R7tBRAuX#e*w@|lLY@`MofNBf z5U4>VA?P5j;tpJ6$TPgSPix^h##hqT0iakzzQGaFgs#iCe~jAg1zc;yVnpLYl^52K z!!Rg=D~xvq==IgcJBio9TKxrVU`$7&CL~x8ZfG&R>axRMAP@eOZb7LU7Ik`6rsmhv zbw#;!=@5C3pv+(bDLsyIs)sq^_?Shpx!f~JNk)`K@aC+pk>UJu&_!n~`E-Oj6K{x8!j(X)ToY~i_QG8ALrM>C>Lr9f7qs>+YWEE(3N+e0`v zrpla-AMAdY5GCpJtm+*{r}Y!9wWD~}9spEct5}z~yGE?j3eo3LC?*`7V8+t()K^CDo$Eib#@e_a9T|i_rxssm z{akIb$3LUv>Hr`7P?qo=>u;s6t2nzy0KXKWe-Iu!nn1?PC39}Z{CVEAF2q_bT^?NQ zTnWB9)Nmi{c#?<7$y$!6sMr&HwW5?+0tEAYDoWUMcFF5=*}(Opo}ERjQD~1hNK$q~ zG<~`=_kzeC;q@$PQh_905$`g8a((WhLQ~qAC^*ELi%#>VyOEa?j%x!O>_?y<89%Tr ze?x1Ly|UBlA$PD73atT4_D)3E*2$4;W9{MLe?8*E!LwD#s&|}O1|fB`Z5Om2|Ljk{`!2m1 z&*|v@hZgGvublQSecjTLy>s8F$1a~aG7sk$++ze1GaNJZ&t+R_li9TKP(e3+N+vg(>)uUobXLvgd`5yYl+i)M|Ph^eiA4zT+!jW#m2@DL(g+0iF>gO+P5D z{C@zQHWE^{5fyklu-(}{@{>jRXGQp0o`<&|7Ccz};I~DXNS~0xM~49JXX^cizS@sZ zf+t|MOFO6Flq@v%=L;F7j8B^5e}z>OFtFKN!_bzjAi^l3-SM8Nmfi&)`IL)qd<7r9 zKGal9sj=irMd9LXtF|D0`u@WV5m4n9-l(qXdRCo`=5u$-RoV+V_-_IaF3NdN8g~OdtXyB|50~<=T)_& ztlGrCQj-W};VNDbIjsaq_30n6wTRvbePiL%bfsb5bloQf@hJt&3iZUwtrwMquJ}T? zxN8NkzPZ$t1f7h^&!q|sf3<`g_#$SG-)cK4xV$+*Q$!d+o7D6yqxwxuNTU?6a1|th zX@*={wjz!0@ETTcH67P^bZPx`JRk0luK74*#n!hA>_7KZp zN_{M~cVQ8{d`WEGY|FEeA;n2Z+B~|7)jKR0EPBipX4#*;No zV@aC;LeaZ|A}*~aXE9|+MTnh{jJt%oAnFuqQ8jE$M~E{CT1@H0Y=$xD0RqSOj!6`m z=}33_N+0D`bKun%PAx0O(f@$qeOJhb-IJ$O4Jj2r1{-qj$`&L~79?Kv4}_d+3N-TX z>uU=DS)$@iYud6ne{X0&q5htiI_PUYk~etNVjE}cWOK0cT^*4^{vU4vTQ~iUVaOJS zu;tN;e`QE;!5?ts?pKynZ#LXfy9Dy_Q8Wj|A8ge&ulyJBt!!l?Z1nsRrJ#)_yn-!D zxG}%Tq9+c4`q7T0Tg)-;ZJ)i|=rFJ~7=ZDux|5;uUL#SNe-^EIs_u#Om<@LoO{+IC zCW@)`_#_tUNyB#4W6D}GP!*=OQST-kx{G5Sz@c@Zm@jFK+=dqg z4Ew9_sX?*w&{pnUiw0II0^M{4c$2%j%Uv!q3g3VTt;7l53>Ci+FK&E9EObQ?sL(bH zQ2^}w@Kx?zf4%v$*pgMp07mQ6aazRf=~G_8d&!CV%N9oi^>Q%cejp+9i~pZ!EDlXw z*$yGEFklyV4II8|a{ytGhuLH%Yz~!m zONUEpJ8Mz_A2n(M#y%opV8-;PnxxSGKu<*&e5k3se;!{gqI!PZvFKiseS};L%ue)zUY^!bzOwxoYnMBXuZIyanBU&an982 zaqiT6X6CYhgJpB(JP;os(m|D$AmOHAni1&eN8Ut7BV?mrkr(~^m>4;$wmM)Su(eR7 zDxE=UAO$OaM0G&0u5xPq**iv)vdh9t@%8=jq6j7eiY8);s`3<2RX{-r-~Tz}pcWaK ztz6yv=iKObccI^RdcFJ+_q(-{M;mS>f5b9zpXcX{{5YdO))TgTQ_DFF)%1N~KN@Ky zp=DuG(ah&HkFRXRj5jlxNkpWXi6S=E)Am0gew(k+@qIXZR1FAStQy(pGnkk6m70nf z3`15A)#$O@-W`OBQeo0gFUJo~WCrGynAbrMLr=XD58T)1WRO&q(r(Hgh?2Ree_aTAe85Sh-nq45B!qI!N7S=Xv{X8+E4m1`+Kgg!AH19< zp^%>ta81>^=7y1#_;`hGeF7El%_gSF-@t-UiV8W$Dh)Mqkd{;h0oL?rz@c2e4pw>0 z2C}R4Symbrvg^rVV!uMaL?S`CwVuErt3uXbCEF^%q37GkPnQCj;#3Q6M24HfmpK@? z^@VCnE97Ion|yb{;gNCA*0ea*qfG+-_XWa{gU<-}C--qrBBMve+1wJWe>Y}iE=;1^ z&TStoa|L6W#8ruM_7+n1+rbX5g`^T??i7#AY*#taJR*JAw1H+CYkkgP+O2=fvx$ui z(@!g8v(U4oX#?Jgx*6$9Z+u&>Pa*uoD$c)0^?G!Naxd95f^wPQOzSQ#RFo+fi%kus zHz%D9(G5g&*{%Kp808HOf7GPXt|n6D%^<*SK`%c(k7tnpNfcjyEZWoFaVFe?yx8l?e&G$beMqa}pq%f2a2|43^G)m6z;v zDO^jH{QmJPz~ax5j1QblO|i1hNrrm<1^%T*kLvsY2*w8MjSo)4uv$}QpWeeBA6{4$ zEoZVx`Oc!AKiCpMt0;*}In~7s=4#b~(pJO!x_#9YSsttx74(o>rK<(kMH0rj8O9Qi zqzMy7;?qGiYM*Rqf9d(DOu44M2K2eHqS$9vpicdA#F7+XWQW-}G&pjE%r@djEsoVI zq)mhh96hj&k`x#@Fp;;nM$hSwB+4q<86aRXq=(;ZM2LYWmn5F5go^u|=d#2~*Lgzq zIk8JNQiO0Xrj4XdZQk`J-WV%IR@5e;6ksnK`nZB2C5Coge*=I35ZZ%rycji9u92{K_8 z(Z*i_GamSpgB+gVK3>Xi*K-0rcxer2N=Z+OBwIsYmuRt;#LY*ZJUX(1BL^ZhoO3 zc&CIq9VKW|kj7DT4apNnEXqM7 zE4`jjwXJ$tmGE)+cQ}~zq(+Yarxj;hu@Vr?TN6JE9wc~SWqmGe%OkZ>J(GKbOX0w% zjQCh=gy48|_!RF-US1i&$cF!Oo9k}YZ+=dK@kyj~Rck^V6^SWq;5$+xz0=XD(eJHo zum+vaf2o&I=%^mxc0v|>lIX~p@1-O`buzO_?l(~kqtPFezxM)<4IVWfR1{c3RYwor zA~yc7>_fEIlM8wFnIzczhEgD(NECX|ocw8*<3q|aogjUhriwIXqM!QZ%^>xo-eqcZ zKH&`OENhInrdV*hL}0#L(PI#mJ#JV#jwyWrf5L>N$NUi?eo(rGL8QPFxB<|<5UgES zYRigeM))u{#TWIL;GaMrl@vIqQB5Xb(itJo!l05-4U^>mZW{*ZIFHrC$#9JRVW&U9 z+uP26=MyO!FrJ3zHOUBnZ)ayVZ{&k=e_QXu(%rgWfHzC{$FJXhaqItWtXTE(^grG` zf7xZnk6t*o4nXB6DiMm{rwP|i0Bd1!Qq<=DB@m-fg}6vxqALAe?oax zWwN#^wd~+)?Y==ND~YuQi35FRnz*oM{ewgwC^R-cxi1YEOXG)qUyQv`AkKCna$?3w zj;rOD>U#Yj6ITyx0{OkJsv)F1aTAv``1lr?!1=ymToTR$ubn4f?(q)+lD1(U@tk`k z7n$2V61Z6?mE0gRh$vM+XV*tDe}TR<3!7`Lyo!*SX+#pk4^yaTFT+;=&b_ND#sVU(eXAg{3718HL91Blmw<$_KhG!Ee)Vzo?}-(0r-6P zc8ablDz~Zdd(kAf68&r5h#=W4H2A}4n9ARQ^cHb&juRZeLv&@Krc|GTF;?8fV8Q=Q zZLUSQ;aeLj*@kdv&|d>_e_fuARI0O`E<-5^g$U!R2fe-u)-F3&Z5}X+J+-gnpl}+- zCb4Z?kD@{~q)%pU%DyKMB1vcLvI^$&3Nwz>Bc6k4185RDOLUvsgdpsU!?ov#UH4mm zMZWnM{*V}g%|}$JW}|E&>msuq?V>x;KZo{?z%=)gSzvX?9&Y&&e^f$V@X$Y!G3yLQ z40KAFLPQwf-^8rCEYxPz4CD`}E+(~VudSAX$uDIV`vRWtZLSRc^vJ*|_qiEJVdMyw zxY;5sH#WH01Q-Uw$v2`pUaO@VxC#Ve!$9lFYDx}5ek`G*$t+R`0ASWZW}XEMk(yLA z`176c_|Hbn;Ow^(!>t&gNET)11J@$U*G;qa?bDR_qoA z6;Y0i~0iwA+$sQ=F)|79iX)=vdCE!Q!XzMudr@&u~zFuUZX)%?moHUex z#Bh($Of^;Yf;&HcX71XsPw(0pfu(8(7ToDRmri0dX)AqYH}WxpV*?Lg`|%o+iHYAV z%>Yq9E7>!K0ktwp&41G2w*Er3%<$@q@za`c^as@GtY)NvSQZEreh=yPl#?~}+38+< zL_AJd*QT9NpES-ZP}|Z^DW_MOJ*RR}3-YF#+!%f&qB4Q3ireXHW2eM5^}4Cka-&IJ zWI7=ssk(}ztFL9^_z&M;PL>F?^y^c!z65pfRgyr7W^tRi2ZWSPyul) zGui9lm|C+?0MO84Okg*yfnY=cQ$2Bkb17mJ#F|>pdS)q2={;^NMld`r0oP>!RyG|N z8gjpYY;}fpB!3cOo|hgRc=YRxQ;;=?!PA#I69D*Ulj;!WP^4quQLr|)ga4I32OdyW z??^M4-ZPx`t39aGhz~eqNY5_PYMuM+Qe{YBW1{|CSebHQW(U(rn1{^@rZYcTTB9+m z_fV(uS=*PvF^&u0+fj^q&%op>NEr5{4{Ga(A5(rrb>&N{9NPVEyX)Pkk zL(3GTIWZ{Ld*;J>TYMSoB7c4+=%?IljDGQ5di#u#{y~YtG>V{)*D4~!y>p|AHJrev zX1mDv?#^*bH0en>#R9VGSJ!bQU*#a!5>On@41cTkU_O5$$YKOOMJ2R>KJx`=WEN$N z=B+6p;v9vrg^AaYue`sQSM~IqkzGGOh-PG^;?JU@Fm!aV@zgPuts>+7oVk=GF3boi zD2>b^%lkT|)!ai9QA}(BwHu!(&o;?^NV*vY1c4zcEoF_E%?^=CUW3{5>XoRK%YPynC~7B`UzyHpUrB}zH@g^q(ww+CY|w~c zY}AJYN==5OwdPGg6`}xC2htX{8^Mon*wD8|6Y!!u?fMM#_hqcmuvAE`zi6Z*v@zw8 z(N8eYu#PGwQU~nq$yEg@Sx26IJ@Jx(uq9aa-MJNGCcF9MRRUYD$Vt~5gR?txDSy!R zAx*lP3P*m4D?e+=i8eS-K`UHrz1mfN zrzCEmC9V2gRU0^2V$jmrSrhM?`SWzYjy-C;l{+nr3VX0Z?|LkzEYrFzF2gLX@UpPF znOo~PUZkqiRKtA%(M?Q=vi|aP-GBKYQ{FXwSC(}Q+N_J8+nIw}DORlEB`ekx&{@*c zu_3umwS*jLbE6qtNZ_RB1SLjrr_~KJRf;(xQ1PII3 zC*^nqGG``;|H4ji*KDz16Y=}wG&<2pfi3m=sq^a6<5q!bZPIFQCgUHARDan^fPPpP zHf<$vYSwGZ+}g3ocohEo!2qdgMoLVk(5_E^>6w81vW+X7rsnLmGI2_x2Mweq^61y^ zHRdn?;~&|l5I@3WJv}k{WRt71WTiQy97F<}H3fw`9Ths{@O#LSHB#})5I-%nJW1_r zRVsOAWz88vlYk=m`)J@Z+}S*4hgie2AczMqT)Zkiq zvaA@58juV+_8>5au7CTK*vzgvNN~6jcP~VBMHxS{@`mH!dy{)ut;&70KAf3|+i(d< zLO}IriSsC4sVumOZ?CSkv8oj0jF?T2mnLgXHLEW1Sg}&GD(SeEtgNnv!3F48;l@Ef z;2V4fHXoWrV?Dqs8_`Rhf&CNawI`P2u8K~w5yoRH#`_8wAAiq30#Gi$#CSOYiOn!EO>fJPOXeNFJsJZR`J`=&F4l9HQ zNsRjY17n88*?(P!u@(=4zkjS$9Hq6o64zPSTF5W1`6Lp$4FH$)9PLXwCwi<#51)rL zZ(k7@gg?WZ1XH@-BQdNHVwu4C6Y9IN?2a6DTOe{NN|X}anl^t7axk({=+14P;DZny zk(gy)I2}IbX6&am)pv#PbJ_s-#P4Q|ODk`sny+P=uYYM7jkUL)j1z4P;Pyr(EI5Qg z>6y!FkxcACeZuxD6a2Rx+722yc?@(2vxT1~f2;*${f`wz1wX)!T>6)i0{98x%{&9o zB2E~!sqYmj3*xLBv#Aw7MBq$4JOVEjpr<^q1&{B zPu3+sjDHRAfFK;FTqHEzxEkn-2@G1fzg>(bG0MFoGp7=cT!dv|*m!-Bo+M&1&^_dA z_hMSEkpQU}5|A3mTSzmo_5lO39NEF*R&`KpP~R6z5+K?_^U9$mdJT+%lzKExHfD2d zAP8-f_Mi~l#Gt^WQj~tAU~0z0cJWjMtd?plp?^^=Q~=qc14(bwO*qc_S}5zxk4cqQ zgx{}Y5G0&O-zag=XR_0KSdD4Pb>%F-ag+o6<~efC=(RCpdz9!1&v9Mi@?|#tWH2C*_O}n$LQ+&*>5%u2%%6cKrW?>QeK$fMXUMZr_J>^V!PnRu_xM1#8C)nVaASjD zF5T_a$!JXL!AZ@3*MXTWwSYZC_4N?U)GbI9x0IGW!p~Yy292Aj{&ULI*S9 zzP4IonNtGS#2=v1>4FcIY#>qToY~L#%74d?AzKoOav>(fX*s)QvmfoJJ)tzIfwywQ zVfT7<9SaY&BL&BK$|kc&&fD9|WG282(6%sY3FF3-1@hAEI2zJ}cd#29MVRf}_#OHZ z;z$Nnc{D(fRL?S4@j(+zJSno_sZfs{;Er0)f$cPpZmxY+@ucSHXkuaVVo94tTYscy z6GdYIKc`mGV|L9}$)c}^AqxwGX9{&>4AuEQ|FasQI*dayG5D1F!C=Kxb+Mjj6A$fi z>PnTc*~Eu`h++`pP@BGj_Hl{UmXiH!Djn5_3#x$r^#S~(2lwjB-iN7`yaqp38KXPE zB*IOVhYyxnpyovm!xLe{kXfMe27j^Gb2c0`OEec#Uq>v^UIhqz`Krk*&`F95J5RWf zyTewORSc3bU&CTnx&nzG*mO1=K4mAh{5A%%+?x@5S!22sEm;^jhDe*Q;i7B@f@*p2 zd8uJ*_;}*8Gmnt_f=y25$9}Ux;tDY~VjIw4Y5LPcc1~cIY1$ebiL5P|z<>J#RKVXu zh*W4q*R0h}$0vQVGicY?lk0819zDEyt>4Lub7dd=WY-iAeHo2$wq*7N5eIW^f)z(I zTW<~=+gm_nw%g_)SuS2C&fAuO_08^4oYlUOiLLy%$dV z)+DRlaNC&9TQOOVwqrTEVt=uQ=vAwj^@<}LO^SZjql*&T=z;F~#UDka=K1on4#+u) zXrG`j7|wiecUYXl+PlPDmXz5mbU2%Oo808X47}hXn?^1QPhBTS_!Ua6wf!75Er_E}P#4{jC-jOJ$ z)U4;WTR476sb;zDmw($2!$8Ql&~?V2jk#H%&596Uk};UVl~)ANoY>56tT$IgZ1++K zs`pw!tyex1ePXDi<=B1~Mv!ppJ3K(k6|RDAB9i%T(y8~pso>}bQxd_!yGp_COHG*A zSbRfF_=cJ9BL}Vx3tpvD-FYPye)TGeo*fOE9u^>&`446|7k}0!aZ|JNxT*xb(VZIW z%c&BeuErpfH3FESG%k)kJM3Ngk_liGz9#PDVUZdtd6M64Ss-ZJf?vja0CARZLzI@o^^#azDFe0G!B?S*cVw?rVU%c&*QR$L@kV_`|12AJN*p^f& zci|_d{xXK?26j}rg=&5moTX6Ww@c)4VtUcV(W!aHfqx;bC^lWc;9>B!gTmf2VqsqC zaRs$Bl~|vP<{1srDU)r6j=H{yq$+GGjDErAJuZ07k$I)+a?xgj2?UdwrLX$ zMw|DF`ER7IEm$-#FYWvSQFZw|ikE3fnYqz%>WhmE>f{pUpyyX7G^O{MeT#kIcmyV} zTfa1QC4W!E(A-yjeK#3ccm_SpN|j%m5q*Z@A#y+!EHR3a=B$z6Z7erZ7LfKjl_qXy z*C79wYX?6loo~z_#{*9@A-0ra1CO0JVOQuLpnb@|+oJ@%G0~WoHTZRpv*)cD;<^ zY@|IaIW(Omnd^JfzWQkQ5yZ(pBt~& z7o_Zp_7eu8Fg6_nFvYpSYCMFs;l!?rWhwivAYKzgZ3 z15x$uvZK463hCIYGzzWUKiFslH+;CXX@3hF#T_Ejb5~g5&Nd9}nlQ3zoY;x{dFflj z^vZ7J`Y=7jW;hEq9U?F@t7krxv0Chl)|gkav|#Rxa0usMaA zhsuS19zZqPt6Yrc-mgV%&0BLJrX}G=(FDga8-yn|Ob6E5e?{*-^leP{VH7xqr+<$S zdw!%~;k?q%&T7$FkJ=+2STdf_KOVHB8^}tf zb`1(NgNn@!9@^<`jR+TyRE{S7D*L9Oh0)?o-sRVw2-PBNjUb@A0QeKX8p0?Wz8(ZN)yn&TgDGA`q1jMlsf=Ongdg*8JE4u9*z{lQ% z#AG9Wn?h*}A-~NV{s{*NIYH7Cs69fg*cVN5!9X*3DXbLM1$4Z^6;0i^OMj%oDecQ= z$vSlD2GumQ?{naWHxtB>|&~aLkUm)SVY8Uxu*Ze}~PU_oKI;9@c zaZKUr;Z^&IGu}IRx%2k)>xQ{l@$PALD~h*G|E=qHj@-JYe)Y0X9`dg0Tswazg#*Ew za@qYtN3giUK5p`=-&KkTK7VkMPMy1W6;q?wj)o|%~{QqQMlpJK%sIXV{(7gMB_RwD<4byZs91o7=Q4ZTe+4q5W&FCMOP z{pwWXQgrN9sce&{j^xm%aZyx;MyRSTAbE4w8Mp49xO};@wauQ>KYs%#067>Gn`VXZ z5qQ_sZr-?V@|oMG&M8+sJ}iOz{~t8p^VcqOqaP-OwAK>05&8H*kqP6pP?n{^Mfigg z@*Oi|oK`l@ATR1$KFuLQ|1v2qH8yQmHl?KiL*YuV3OpdHyH^j zZ-YVWhSe1~tNIS!J8s~6c~sjr{HS6VA&+H5f-IM@L9RxIiGK_KM!5zL9lLPcx6DHB zHl=ryLnhhSCH2{y;!Yo8rMl`_k@Q z3hNX^Q_-$CIBI7I4Nd#kN24tGzk#x;0veg394Uxg+N!Fd(wt?3t(mN> zteGOfnSS=6z<=nfR{$uBkZ9rEGF145$_Jpn2g7kZ-U@E-Cq@C;mGr|94sap=20}_N z9}2}qKrF)RNWDdItRY18v$xJhipaN+dYpbyLo1ca%$Exde1hi)WZWe8IWoP-JcJF$ zNGZg~A@BQ~Sdo7G+jTweN4!V6KGM5<`k@9i6d?yKo_~ZCk^vAFy(#S;)r~+`D!^ox zZt=Q6!E-ekr_Q?P63xK-EZrq3%^^|@Gy(6DmTrPhOo8cV*fc1y)J<0}%)EM#B)XEg zE83G_i!~QcK-xzeMK)j}u^@zn&4N5$x~exINugBq7{W+WPNWk%i_8MF&LESjmk%GL zg)_vSA%8ni+cMJ`{XIu3rABAj|8S0JZ8Aj=%gC=t;o!rc0BQq`wY#PaU3YgwwR zbu>svNh*<#x?L`mi_oOTp-90BMkrDug(f))OeTE_PLO@W54|pZ46Qq%$!@7#0P2;) z=Ty$Z8fRxbqX)M@ zzI~DnLyrL!_*&Ey&CC%E8A+}SQ^z`cQ&`wj97#(3x=V=3kf1LX_d~Ux5hP^~?%v{+ z>!R-_Dtbk;+Ep!#tU!+wdj6^Ub4`OTPj^fR{+$O7=3l;x#@KFWmRY3i`<4DU_vNv1B>yh?DmRrp7KFLX}z4AWR_MoWymu4b?<<{oJO(g)x)9_xy|>SPSu8U_w(xf z=6RWJcYi(R&sk)${Z~W$ZFLay3vhdS8-LRn>i?Td?(6t_Z=dGbd_4Z%>C;+vcYkl~ z`Y%~_QZW8M7lYw%e+|TQ%54)&1T1Xl3MGF3H`(9Ye(IaHALZgP-RitQ7uomn9cDw* z-_?4awyRyA@1^)%7MFKpHNEzvdCd%j=F{5Te80DI^Kf(C4?N>3<7*h-=04u;CVv5k z@aQ|OJm!eOSsJGgOo!FcRMARI7`F-$a{i*D zID=OYYF$1eA_N5O&^!Jy%+`E}RXi!?%y=gCjLf zkDMwCsvJ2+Dv8>3>i(n(o0g}WvNj@`!!N3VLEZ*XjoLB z)SJFg0LGcuz|gAWjrBDxhe%X9cHk3VAF0Tkmr$>uIz-guT$_qu&*OkXJbR*1_7b;{ zf6+-N`r$c9Cx0=OanQYu70Z;|hHk&U!dKTfYn-+U0Hsq!q0Ks~sx2Ec{xRx1&Hln+ zagaIE(SNn&080VSg-ypCH^X${s*YG;Q0SGX$rH<))P9#HK)D<0nD%te!WyGweYqYNVALaRQi)G;<8dLgdMi%Ejj!vmmiK4*3@piUv!`n)whZ zX&O3rnt#L-Xf#=DK-TE8%!}4fET@vFHiO~U5?7y_P8Nd{fWV=ZZ0SzQcu+x|=B8iO zDY2oLUB?KU@!MrxR_V34U2}qqcBP7e(SMgSev<@ASlSX1Ork-;(+FTN7!FGX&7OmB z91u$-?p1(L5|ksP$&j%oxDMycbM$plll`a>HE0E>iS$S9nbgSfebnQ z&Gf2!5hoCxprdHh7KGH^6Qsi7x?Y@cydpw6j(4mPz^+7b^7di{J zmG&(G468PRi;A0yxjZ3T6MY1&GGHXes26zPHI9WLnAnT?K-(URKA#}ceuE;{C4ZBl zqNB=Xa@|H|$mYSW{Tbih;cJiw>_3}^Z>nn4ecNlcQhn2}!YRBSRL3=`5q@|XY zL37N}nr{%$3Cziod_$At30wZ3P-en>p~O%)bxlv12}eOD^+-`LYJ}?in_OB0!XNed z{Vh@yY+9iZKHFQIrKcKUk6=MXG3QgbnO9+`es9H~@r`ue9b z>EJepfrRqKhG_~8(}l*dTzeMA=ym1fMJ}cWR94HKzp2cPJn#TW3*$fu9rFA&07nY- z3I0V&GJwQp9I4QgUse|EPRm6i*TOKMi}p~;S6}B(X^RSOD7!cR@fYBk{(o^%P-bB9 zYmS_-&0RHNqcBUlcG%B2IvZ5S_T~QDyj97sH>@*=e?Ojw&<~zbNBj6582dG?QcD-;u=p~)7(lP zXWY}#r|}wi8_zQrjB6d!56x*!(nn2iYFt6)f(HIp$1h zX799_pVl+@#WVf9*7GRSk6ue3cj+{rUPgn$b*Oo!_SjhI=Jb+`Jb&(Pf5fu8U3wmO zN1M&$u)986(ir@S^X~GuTxCxf4~f+E-EQGJtdFJdKhw=iZ0}tBV84CcsnhZ5)f$IW zpSbV*$?Ld_2>j$`-Y@Cnl6+r$(E3OLi@NoPw~OwoH@fKF|1z)T zev50gjenHsRQHEGGJkE+@8bK2Z`^ErrX^2&>HYJ^_bh*X4kqJwn*FM|KL1(o{d4>D zkMGLYT~r;MSRo>j_{cruKd;0$DkDu`94ubj!+<$*$rta?|rGEte_5QWM z9>DhcNY?z>_4?S>`xE2$(XH?K8;$=$e|fq~kp3zkq++IjzCRnDzq5!^zekcfH}DEq z(@`m3zCC@8X5fSMv?_b}@2X$UA3rY`lhE((Nj@%rEfzg=Qtv)L(NNAJMF;vL>-}8n zmHd99DW|IBkALxX_ovo3IWwo+_3WtQC_gQ6UdyplPuHI>>z=*f_5IV=(wq<7p1)#x z|8eX1`9&+qSSenQDRa~Ox$pC*=J`X{=CZdq|F5}K_@ZBx-SKd>-@50Y^>nu#UmXUQH&?G@Ge31kIqN@q5ty7 z?Q0IcbMt>(eZSZ}c1N$ZJ$!XiAK^m$wx@d$SLBRkx-~8%mY`(<@hmPe5?Tc-=8tT& z>hF_1x=NLs-!guvt+l$v-c5k!PlxOxNPqr}deeaMeduF*@{jJi-(R@>FJEzABVTi~ z=WJB>fPb;Lw|a1$`Tf?P-gN%+=l5QJdIS5=pWkSIeYSy8AO8Im+4)A_zS4i*{}iw9 z-IAXAbJw3edXv|tJ$iGz0lz(ZgV(+GcRKyh{{G+HaDg6=w^F<_`>yVk_uBU-YhSoi z-4WD*J=!Vnsr_nu>7c3aK70R{_9^e@?Ns-6g@1Af?zF18KJq$6E&L4`(!bzb<>H`z zYr(lX{Wg~21Nmd={;!ebV*Q_g63@Q=TQ4);*nIrdQT|uLQvV0xT(gtqA7@s3{#gF+ zhxgB)+bpwJLnN#H64rF4YsaRiww|x(AK*JAdoBguI!4{P`xRW45$B^R z-X^h6eFn7t_17AH%UN)u{mJXdp28XH$A6!3`!$ya@4h&XAh|@x?6fxnf2ex(C${!_ zB|ZCxx206KlW+N_-{3w!GG68L@fgS2{#t%~;Xj`N zSN}o%A@A}ZfBYbSm8Z<;zvHw2GPh%*ivK<9lhyO*w|mMY|H+5{?c4FjQIr_K|0}=z zgWvTVFUW7x9GlhLWBRR?%WMD4_kZoqKlYO7f4={r*?*Bf;~(6A=0CsuvHMRH;{Dt7 zzmK~pkSO>nzZ>}LvWVZj#=qwv#YQ@n{j-pNKzny_pyTn8j<5Wk|M?%yuvWqR`~6Xf zf6@P2|HU=@d-HPs)BiK*Y3<2B@%_d!bU7wZZh!iJ{cC@q-Kp<}e|{+yZ?V|$wM?lJO*uPNjHn?Bk-Og`-3f0-HQj#KUR-aXGB z&)!vXTTjj&zbXKImydGXe}8%TXVphY6rVmb#{8%M{EL_o`&;ss`E&cl^NPO_J+F~- z0RJm~Rru-rt^c--pNqHX<7fV;+5I*8=cVqqTcm~2RFr}eM)P#N9D(_>`@Kf@&PK0$ zjqddrk>xe_2ye=Ni}e;B_Nf-M~E<3+g(&M}1&+e))A7vMf(YX=# zN5{qX*Sb5L^l`Y~sJRQ*d1|>n8l`+b+px$N+eM}I!+xJO!6KD*mvvSHW20bC?1z%@}ZcYzDG!LIj-nl8gbAu7p+ z;4slZC_);B0Jz3=07C7s7YHYJcXjT!y5688EDY^*2NYb56A`S0jf5E10JbWvAddgt z-@Dt+?(-twd?nuro9LzNHFirAk6W#M{^J+J^Kdb~M^xWEuz$q=Z&=?yi>+^;WHxYN zJ&GHFPoydPQBV1fQ{MITML*H|0wTPj0Y569u&3??zwZr08}7aKL3PNARQSyfTQOr- zmNPZf;XcCn+DIiepBu01wY`sPqSJPrT&)x``$MAX`c8|ZB)Q0X2zs2mGh(^^MfMhF zip}RR`JE;%Hh;Qq!@bz|+b_4XWAbn~ng!;0&$wLYJ*}o?Oj1qu~<#enR-H!s{*mx}d%@FwD%Gj=A25$$cxZ3*wFT z`8sK%j=A2LbT|_*g8AnmANo!@e0jL<=YP0`-X)jL41ag@u|b~_#q)fyht4Ah((Xin z@u+{4?w#L%`fRqo@Gr*&_o00UsM`2rZ~xO9i~KOZEH_8^?=SbBT`;@=y5W4-U(I{J zxp{w0&ClU|^VRn^hq%7{ck{d9wR|tr1^RKbf4~d)V7K_*vuo^!f6pEC!upZ4KXw=N z1wVQhs(%;tg6IYPp8?x{_J!&GVW3^k_+Q`)_o04}J%4l;?85j~gfC#d@6HK+%u~Yj zhkanX0eS&{z8CQYpM~KEYx@9ShtKz4I8Fx({P4cGFYxmsju-O6|8Ot%p|{N+6iZ+2 zz^Cs7dwGtU|FR=b(j4>~JQ1n=)>}cxK7GkPeSgS4eak+5o^7N$4r|KyXLDZ0ACbtJ zKRu=1YE^lN?oLnR1GF@JsD+L>`aRsfZ#(pN_+*N6J-=Pe|1A6NH&M9iIzNLO zFa3U0=eINYo9{!I#q78Ed!N`+K(v##2M+s|FKN8;|9_f(+fV0by9`h5rjv@R)&0J( z+<)s&hZD2*+QVWsr2H?p>YMNTE-%%#zqIu4{(u1g5I^gw3zT_oEv*s!74wZkzYF)J zA&IM8FF=@t(GLsH4{EL#*|O7SO!!=*>vK&+{|l6z?E6ih3-{%eO=dV_c}O1TT0Y@& zkgV6Y!wc+8Gf322`PlxSrfLB=T%`|b*?;HUg~~*Bp`iX3)NBMs@X`NhbJ2#6=KJ?F zSGL?4|B2j3VNt= z0zx>G&un@;N2>k6bi16`9#5tb(mQNZ1IgR<8sopnV>^ZOfev$5f6e-h^Z8?$xsgcm zozjn`#Ae}_ls$A5oE!}0xy zv-R#;`@9=v^1{dR-lMazKm2{*xcc{~`8V%Y4g8IZw0z6deN)tXd#3sFL%;gQjdvIy zj5*)jyDMGKwPQE_uGQQ>&OJwnJN)C+cbiORn0NI%SFru;7v#r(mwL~`de7I5dHwL` zSbKS|S^X&Q{dcJOr>Om4yMOx!uYBH9KlKAt{9p^;n(pj=0Caw1#dtrPg~FrU6n}qi z^&g7MaH#l#$I|54`FC%AP2dMkE zv7FI|_-|uA>FPh-7CL)O2LIe1Kj)7}-fi6q{PW&FTv+?9rUoy$XMgwOzqR4s{`l|v zn@2*s=e->89sHjW-$&_Qqw~T5|3k9>x4tHjZtvEOH~p)yvHPIif7Q@%dhCRhIxY(r z-()YAALnZLE568G&%4fD-!IY#mj0%JwfWpe`O})~F~*i-IKG9xeU6N6rx{=Uw64?i zb$@~XKsCRU=|-=28c*Ce%kTyNcWySz`ozC)_^i_m94Lh(SUc5fF-mWF;Xj3CK@Ef(Ia~7k^N`fry%m^k5?e7)ZlL zA~8`p6;Z5>#BQT`6B0Ei=|D;fQPP%_#HOV^Ba*C@gsmlUE6Es{slZMOaMOsKWaFnL zIcd#Ka&!|pI?2~g%65~uo#f1-Z5R|_(Thes88l_ln?`jR^lMSEM%5d1ZqdC&#UH5! zNJ>OfDv{KZq<^(0C~}pQu%)FfsdY=r7^$kjRtm9Ij;xhst1(&XoT{qWR?@c>yshk4Mb}8DYa+!-i zS_;xul(e;`Ej4M&Pg;V=eURwIbF8z3G#a=SQJd#rHYnnTB&ZKm0#6?Rt;EHV%3jUMOigvRhw3QS`|*J zty=YNRl8R8TlGv})*rC}h%7^5D-qa|#I`1}J%gC6#fB|0af{3uVeyQ~%8HzV#~Ud{ zIx*4Co_}=%jyh4&&X{%5$+=BrNq`^QEQhp_r@tU`aO!y#)F_$WdlCp>{XDlq7noAL*0kbg0VK!uQ-V1doHgd1X}xeIxSO(AmF ztA>r#FA%*o8wgoK(iaGA$3j63^C!MBs5-^zFvvE@Am1w-IMAYX6QVT?hBQ$dbuvziR-hFfGA?y6-YX+Na^gIqdM2sXxOH_4?cl*WUNat?M#I@=)Y zd4HhpX$P2u9%B%DkU{RXI_(F0WFBtQzR-Puv?Zaf6=?(?TMufGeXg~ItgUHlWFbsk z)!>kYh|q_QyoKcBh}r9$LO!tx8w=T9(FuEiOuuML!XYx)l{kc*c7(1-k(NR}b~ur& zLQe4r*W@B$F%h8`FoaCv5p)Pf*J8;CyML@-VWYlHEPP_@vdoE01>!Y`amx_dVG5<~ z?E5LLdps7srIseQ?02cN>~~Uvydw41PDzD}_UYa$ZM($g6(Rd%hwZi@Q@**i8TVLa zif`Iu>?GwDD$J)ij-3L=dxeX)SFKLjX1&!XD&f~vTPn6QSx*3>n%KiM_$2Ijr^Y@$I|*5KJYpdCnq)5p z*KD2k*b6zP3DzbG+CteMyQo^HF&UwbXA>suqghJic`{?2>e`^Vkg&&-`O_wLT@oSD7ZYuM~XEYkmRA^#3&@gmYe z><(z>iU1{oARy54|N2oE$+})?AP^XG2G*m~qjL-C<+$JHgjbL=ZUGK*Ef;)_P(%>! z5*}`fD4i3qHp&8_%`m8;5YU4G5wMnY5D)_ELeN0UmC8HdI<%+<(1BeXmX}ogAJY=F zU=1L_D)oRKXaN@h!-}zBZNyb*5dicQTY(J`s{i9URh$gg(bfE4*A+^Mpb%ixdIf(V2n=?2Y4|I_O{q&X1;uprG|P=E<(gb0Biu)?h!LLa2r4+@-wl+ckB!+86PE~X{s;x4!Howefajq9 zNvj4o4?%$-a0v}XVfG0{01J>FrL+=C$!n#-^@Mtr#ZRC{=4f!UJPd$>Dbxc#&VEG$ ztZ75uxlgY_nSdUI!ZrzpQrI?!p}-MH^FG*)&=JrMu}8;?$}g6NQ9URDlt7BVBEnZh zi8W)wW=NWxbCnL%V29x}*v=yV@A>~J05pIPQ>aynAdm>P4dz9SgY=TgWTX-$jB=O~ z-dmgvQ${|`-O&x;7C(n+Sv(U4b$I1>0$+ud7C@=6V6qi}0$+qX^M83~poRoK|Bu1D z5)b4GE0yaHfq+cm^v2GRUMvDXF17$)rx3saAcAr$4ouk9I{T^kEZhYD0|^`zQdWm4 z;DN@d;&<>p;Pm1@a2N1}Vk?BkkrxOr>NiktGN$<=a_4OgwguZF)b1T{ZU-b9?yQML zKy$mf83{=2kSKX8bVSj6O51lB1^k{;{}i#acpCn92XI0Pp>~7P_5((#Z{INuN<)v% z4*aM1JwhHFTP#4xo0(k!@4&6k(IB95n{v1;1SC`p0#Wkj>4rtr{hg z-)1(mkTS7NL)L>q)o7d{uUbTf5~eA6D?}({Z0JkE&T-GRo|`-eoa<+O_KpVicKq#= zO@=HC3%Q1Gktm8`E5(h%YLloIh9~?S5uR&91a8~_Wr9HaJt$rjU*)w%>4lY=Ef52U z0yQeb!=zX?2VkocR`J3WVNs7>SrCNMHVJDqlXU^T9ud0Vj+|+8RRj>JU!)D@YtJ+UnW@dJd+PNEW$P)c}2IdYbLRBevJDCwn~N4OooFBn%jB9w$j z7${#bAP;Y=3%d|Dpz|;nu}i``&&ilePB2LjsG~FxdX86C0Nc>dj91FmPZFvE-8)te zIyZ1LZ(exD#QJ}hNC)@!N#FH4SF^>~Xaf+@c4v;7Bg8>;89k=WIsz#w6<0dH4NnKb z`ydL8qh->hpk3M%cx+(b0##cvKrWS&hrjvM&;6?)#}C?^l_2yz&JI-*JJ*58R3l-zpzq4~SoH2(dt*di!u-&`)1^7>D37{>0sboIctT+|1+^Vk z@}XqMX%r##?!AEaz3Y=W_EY}OjKrgSAdoIQrbVto*~&wYZ6nk;_+Bbr7zc)k4p1!pV00)PmW*fnU0rC_ zWkW0v;OOvBRLXJo%Y@n?EEG-A(v}%WJ{q^^G8Ko7NKWFm&0tRQU3dp6b;n*dUvPgU zjNnK$)7k`>5@lAnyp5LX2Znu(IuQhB5?4%3REa=^%x9rB$cpxRgQvgA>o8Yr-B1b) zXg*_aDbg;V0zcCoFAPIayno-P1cx7X-0WXdOF?)i%JI4paACR){N$KUcQZ!gv^)q$ z!`RRvf5Sr7j-$OPK0D6Vz#oTf;Trg26LX{u-Zk;+5QP@Yheij9`kB4c;q!^_^X$| zokKEO%<~N0iSn#vIuVIO6abAy6Fhp@MhJ5CxEv0rVc zDb5wqf<=R&am7_4ClQN{slwV*#hQdbojFAUSb>y_ps;jjufIb{^nBmG326Pt`tkez zdH5O@zMQd9+vGWKjpMW|7e65wxVS(ZH9ahGKTof9nJX)Nkj{(1@=7dZX^O(iSN7 z+h``R$ANf|k?>=;%#;hmqT7i%a+pu)y(M{73GEq@TgxZpB}@nklZ_sRTRmi3hPeDjI;7P=>!*lnkyoUY8y)MUAavd%0q+S4?tEJlVr%Tbvf z=yol-Vaw2yo$AIpD3-~3_vv)TgdKTxUCaeWm#+*GgQl-8-XR3haox;IUV#DwPJj*X zeZU1{lFGV4R$b8e+u+qG;yc4wBbL;GDnpS5Wylwxp}obfG8n5yL<6k$oN0}Xly2K~ zDBZYi4X19cra@rrwN_;yi)*KSOFGTnY#u^Nxb9-<~EBHe={uY*lG8H8w3UHz@;Lq7<#yx{5ewyLRSL@cTi6G0h zZ)^``4#}jzNz@44b>=ypP_wD}wXCt}b1i=h8vCu^NH!acCmq^gSm z_T97aJ32OquJjiV9SEEgjR@;Zmx5{EL&P-eCo0rP*gHOJZ)@_uM; z@QEdcLdIPX5@x1ntoX}l&`3Q7ee0FJEohPs+AVh`K^OK&m5y3GdGj^jh%Y((^9!X7 zfS(ts&8S&2oCl6)C&!-H=%|P6`2}=rgisVsrPD0U>!FVo*Ocg_4zH0gIumikPCcv# zj4m47roo+AG~b-ZWzMl1Y}QUdt^GC=t{2LfOex1PyHb#E0=|r8RYUs#73c7dC7i>; zY0nlyvBPCAQ4k zZwuiuVQ{`q52If8oZZuZ+q*BhGS^IkF1$rAyHWPbW$ayn0RFCDCD`5#N36^n7o2$Ce&75=3%0){RxGMNaOGQHtA4zE-k%;w)0d)p2kp>8(Y-`0~a^}z< z8kE@s`3|X4XJ)m3h0qRKps{gFBb&(W@PDc!Ek%(u$2ThI(7Tl|)2?0>ZQ>0O*?NgLMMvd28ek-^CFZqT55P^w9??YEV)<%q z;}}Wx$hT>8?3;|M5?$wl_r<>zkZS!JRY-hhU0q1ZR1FiNdqU%OJE#b!?IVznK^I@z@}nXj_01>pBehT3-w%pjnh1FZo@`(J z*tV{u(*ACe%&Z;buJ-3+x#;%BJ)6tU(_|_ah+a<0Wis$%yJ6%@3Y8Fo#<3V%_WD!`r>N>v8D4yAGqQ==h6KFcpdU_Q>{gJrr=pw@Zj;5h4=&Q zb%_`tg`pUfCjNk`$W(4wXI1=u$(D%y5{8|V$+no6zaUCg%&4v44HxcW2?4}A@Nsa* zoAJrxy9v#Z+;Efw3m;efywM&+oKW8z_)pKo{Dk{^)cWaEm4>Gr>6~?Vaz)LnT2U#8 zdCxGy!QFVW)oT_a`0){KAf-3jMCodb?JX2%PUPaV* zeGzCLFNLK*uap~FG)$VZe3N5lS)6)MuRoHh^y%b>`l7}Jt6WD1GHax${0-R>A#)5;%Yu3a=B>`%ee2^psc_~YC1;)txk6DJs2 z9{rx78J_-+X&#N+HI#9bx7*PqU%HH=e~Dn;hc_eH4Ryo&Mz#|Anzfe}VCL-_wQOw8 z38@shLdS^au?kshs{c$jC^+5n2H+ zO;*o}Ra52a*AjE)bl7gv7p9p5yf)y;W=^MAWad5dJ&UX%b~?r8)AFk(@|~Qm4~>FNqx1u&ao#*>(%`mZ)*T!+$eoFpE`yQ z-KAUl9!h+ZkV_sFK?b^ z!9J))i7Ja~35RI|4h%xDCaZ1OZ2?=)t@rczi@ne$zHjimWO1qI3=3{>X5}7>dA(8=WK$3SP`Iim@*v@Ea81OCqahytI?{VGA z9+PM$YzZiwM3_axkn-Z?ufu)Ul#PT!=Y}zvEv@XL&H8QahCh)rqvoz3wnT3TWq-+n zOd^llzB6i=VjK^by-3{mx%on?$`$Q%&jxDQJz8CHVrIVF@ySN3DO4dk`|n-@1`gr+ zx@jxG4(7q2Sqi)mF*GAs98KC|;$hQqy8Y25!u@uPJm%ilBzt}C=IQ}Nn4ZACXHmLBx97#CyvRlGnL|jcvKdr+Mp%pJLmy6w#pvI zkoep5%*^Q%I@!Y;`te(HBjfYFbfM+(B(YS4WlW0Y@<-?OLB3{v z;C(#85|v=)q|xZB%s%j`3P*^~?URT4i5v8nh;O@O;2kGqrATgI^MMpd!YeV+`47B&y|fi9i#T7knltQTk>a9Aexu_V=U3 ziUs#nSecWSR+qp=X(QRW%=^if@{)NFHM;gAQ_3!aJ)g2+N=F(?u_EoX?dccaO~f*YzdF*|K0y5(=8p{qOl( ztjlUlT?NXFn{658Va7 zHE$S_E?Wt<#jXMEvnDc;ye)s>moVTvO0aE@+W%~c-niF@ol(^1PZz2M%h~!L`84y9Q+QYH%FetMFKtJyW3hKL9J&StVXfvB8%RZ#;xlY6G;v7~k;9=%>zo0V)wB_@%Rqmtnbg3l)6c;}Hp&T5g#F1xcVq!E4H-i3ZPGqxypT zB-Sx-hBAM?nXz?_+-E*c!?IJ}ETH%_5jI&=^1l}tfd?D+rw!`h`rDY+tM#d?Z=jmV zSm#LjX}#Y%ee%ho`R}CWpya^~);Da2$3Bg?QoAct<>$yE>CTI#ciV^sBWDl^=Ns1@ z*{;OSSI;~{!$#y-=3EbMM7wF>mI1hpg=8XFVRJbQi$*WzdHmk};*Ap{`dMhcvxrtG#|%SvX#yws8pxCqEhtFRcI59)pT2M=p4K(%%VPMNxVCoBUPH=3GFTg zIT|M+GsJt_wj2-JWWow`mN~aj{TkQZ9yAhus&k4^UjGzC;DpAYeI}y;U#J@l$LNnl zQAEWd(qubg7-6_S6p z6eVQcw#W?M3aq-c z?f4HnSWW9UUrQ#_a;*bJxT%16sn1~=K&BQt-k!)yq&mMMuGUMJu)k1{tR*ScT*E-D zQghZKf%yzAM2VH3oC!>91@{{(u@UQ8yfZZ{d&t3XND7Ws@~D|Hb&mVW3Qm?8Vg;w^ zZ?T)Y?MdK$sZ19gX*#-#p0_9|Rn&pbGH;IFQdo;zph%htggueNrL6S&r1!6Q>txgW+Jyc~hK2w7d>O~rFK8b${F16IF45}p5>R+B&dEYh#gJ1s@P{+9d zs&vr}-8|u~tmkABhLQY**b}nLf_$|Xues;CMo4+@G1GGQy?8SV&bxnNa2W^0O)(b3 zdX00zBzcUovKl7auC&e8eovtTykN~>rH0kq3z3J|y)S;Pstmc6RXnaL!;pkD(#}zr zdda&sM$Z!{<{pT$JP4UIWjgO#x`7;vl-uJ0`vn0`gI&X!6VJNQU=Tql?y0c2ipW0_ zxCTThu;MjolJ@c9cR}x^KfJA>gLE`wGISlmcM{oKZ&*KA3+13&zOeJG3z&0WP!Szj z6Zi*h`$|jOpRDg1(c=tgf?uq3h>&UR6mf3MVUE7Xf>LDOz-X7fHrib$%R00YaNtU^ zPsz0LZOz1Td7Tx(srN)63E@c7o6|_n@kJAbcvl;ycqMg$QaVdHn!H(EM%%&yDTxN# z{nXG=@15G!v4SApOqB9tLdy|GBSzKsG?p6t?D|y5$FwqMM%m=En*N-3hWGK||8N+q z&-;7IY^#=mUL0incR6!LY=wL%efal%Fz4i&l>vBYi`XJq@7px*PlfaQq<8Y)zWdX# zh4U3nTk@EEc~k*h@QbW(&wMLn`0&T3e?42BwinuUIZkx0)M}ufKBJ>@fBg}_WOk}B z+j+9%;fEP>UGh^L|F1sv)#-rfRoNROOpL5kX|7pHX_hgJDff(t5I5ko-^{741eyk~ zAemWT!@?ix@Svz=V1_7wPV`UIU}2$b&q+erJMTA2jE`)W7W``1=G%^C9}q%@jU3J0 z81mjEI?xH)El}ad^HMmnoUOx3V>ajch@LE??>+~a!x1dKx2ZLh>~h0sC|$T*cUjV= zD<02=`t=V8n%qRqf(_vMG$WY=m9(nLy=L0JiBZT3Sa8yH0ifl%M0pm)w*v7RE&Ed@ zE*Bc!g(qWkwF^jWsp+#hyp_8 zE!%#==-9_qQ-?;cJK~NKwT#p91mf$+(%lCIPbu_b!+91EGM|4n_rM%6laqc?#4MKe zqr{BGepN6(%VFwpJWz2te~TV5^j}|TkI)1E;cLyZ2IJ%MOwzIDL*`&BQnJZf?+d!e zK||Jf-u$V@UdDr`dBz>%Yl2pei)?NeG{w=;eZCP2MwEfqP+hmJgNhp54-|mqqkZ`I z^I4}$$4)bQt21ReAuvPbjYshY4mI0{j9Ai-0w&%BL;L_Bo?=MI}z;ht+?xxv8nnw7Ym@KSN1V?A5U(hZk9d6mEN(awj2>agUu zzFn;IN~fDO-|g$RV{}U)+#(uOi1NojstUeYg$RWe=Q^<9izSqP6VsB$lJ zr3e$Z!RT@nra`xVw}KT%Bu^K-V@^+)1;vy+o06*(RX<&iK2um0SG@$XLZ&c~*)Wqo znD~r%^kixaTmI7+2L4`)sNO=rF|X9lH)3$gmkLl)SKlzd<%Dv13K-6fYg4@V%nh_h zp_6$5RIuQ^;uHv(t=XuJ|2MyImlX3|P=PN)_@K+KB0;^G6AaooVGBD-Pg~_Tk8uLo ztS^~kg>P})w&@9A*G1*V~&gha?m zC=hk8H7&YS|2#TRBiZT-2xrKVU&c(lOax84xeL0k)O(+`epzifc0N##zJG#pOoCAm zweLIi?*~~G=9|+}vLCW$T=<7D;#|K@v+^O7i_NF5JBKREoF*?9sop{59E>ISvE$?) zm@N{5>UcDKKzqqZ7fdqAYefEubvwv9CI%b&h~%|MJZ0eA-ty*dR7Lt*KkwB~Z5v82 zz7T3uf<_?xVyqtDooN0kCgWy%^%chCKwR?0`O)jzk{KPmj*;CpU-n%&`#VH{8@l`T zwD!9HGVmU5{AescVT_q=_J!n2wUq`s^@4Pl(w~GJALq|#9$C8`9{s#x*0gRKs8?*m zOC`viCB)Fh8UfKD*E5`v)!)u%LWBXnRwWsX2;d)0vSwgtY`ElwJ8vfVp9~o9w1N-b z_DHwf>M+J7LEa$LBMGY;eJ_o!B0_=%Sn6Z^+O|VDz11e^qkT6-o|zIWA!3i(g~*o595K4}^Tnr2jfWn}O$df6a}9Fdakg%av^wl=OEwu3?)nvrcg9=v z$E7|I39#8$Zy?WaufS;-CDt?I!Si)f><0ZRyA=Opd#SsQn3xCyW+?iGD;JKNcO^%~ zjN`s%v|yr}vrq=%JFt%Z(iow(dMWA4Cs!$bq&a$7^n=e$vP!%}$7SVw%G zTpgiKd7qE3rNu(+QW|&s993;+M^kAISab;zToJGJikcuttpcwkf*EAhiw0CD;{5^@ zr+q!a(RrhX7NuFvUu$w7PP7kJRJuV-9wTz5H$fBJGqy>e%~;1KIn=nZ1jSak`X*Ae zlK{XHjxS=*JI`S}COqeoUkR^Gd&OjZ-^VtxVeMsQjY2J@6KX3k&plv^sW1et@KBTS zst1D(6yOlM6y!1)HgGBG;{uM|dU84uv(scSK!^dG% znxqANpTbbE^>O^6Go#xKqFaP<)Ny_7WkEdARAnrzW|5&^bvxtvNAvaMl9+d|uRZj< zr4YhVa-5DTZAN`|-hM-E*W~+N+?pZQdwt}Daj;Qph37rDW>FMriS3-Ml3KZF#CN&> z6jfAL_lEIZxkzfIn2x_FIUBV1uS5SYbe(h%##LQw&X;U3fZeT$z|b+HujQ}vpz}{{ zlp%CUXxZ6jQXYI$6|1|;FOlxbl|7t3G0+V^%(go$lC_4Xi`P}~wxAON9MD!9=mf(K z?TK_*60K~-Vn$u`&5y4ILHG52|AJnY$!UyZOvPvV5>MV=JIU&5IZ~%3GJQQyNOQ2( znSRn!zo^FQzuP%fSZ0+cXSZso+4S9qo`I?-A!p_rqbh~B9OXf`L)6GL|Diu!lH?bE zOhfEOi}N@K=Yi$K*ByD&3ASdufK% zWT%H%`I(uWT5xcT^5Ziy@fDgP%$^l>P@{Z@_$ZzBl?qmhyNb!+j1*}`o91$7oT?A)XsdC4!dA#EtF3E zGZNQk?~a)H)!~9kn9VD%>3hU+2OrP2(4)Pk_tyXmY`3fQ?p~JCy>7+}soNAmr0zh- zulN1J=HX|1y&!i^|Haq!4Osq~6JulV3H6)c>EbWdCFW!Rnb#AbbAR9-4jig9_@_qO z&{(9&{RDhPOwcdq>ub6%qOch_o3BY8wHJv9Wm21Wtjl(bXonipSpJYsprl=u#@U;%e%yQC~P4=HETEHAm(Kn*N5h zrX>ck&zt!xX|R;S%&I*N?HU;tYPcKsi;WZ_gk!U_<<3<-94}JX11ohWRfExCSQEE6 zJE2;?jts2-!$u$Urj{)`jHO5l5Xh)wZUMk;tnO616xvlQ_SqGCE!QxQ2!5AKdE{Y# zhQ#o~5$O;e(O$#7FF+6QB5}>LrC<6_*A6Bxae@q3f7zPW$M)E^#w9d7&c=Sv3RLeh z+|E}*XQ!WnavacrF<}~mmCF-(_sz@3+d49;_1ZZXod(tK&eci3hF;w^vXj(ou+YmN zO*vv4j!FQ_ztQ7zv-MwugQL(HH8I(%jC3-(!Pa04?HW(ULTRhpAQf|P-OILJ*Q(3$ zRc+T~6|4<^M>_)z`aX2eKlAPL-BzmRK`Evkj?Z{n1KL&dT@Fv|SnjZAaJzIV1ZF6oxPz>HwvWc@S+e-icy>YUpZ=|ydmxk<1U@yy^X*{mD)c_Ck`u*%9R&Z$$M4&De>ZcrYigb!~N61 z9ks6{7xTt&ZJs|NdH5x*5r}2?rrZmCGIBx18z)D7dBpm&2ttPO5SN1{bRcSWA--U# z$hyWgxyoq)C!&i7>=w$xOggteH7&Sd!>b;YTeY|1Y!y3X>)z;U^EWULG*V3^?<**4 zzn5jF=%8gGi}CVvO36AbuI;rmV=a(Rz)1ns4XnZZ9%hv|v#GWGT%Qivm&CVNg`8($ z?@aEtdwx+TYV?UjD@z0Z$oKMCfoj21FB5Va7&S1VcNLI)`%A>^qb zK1I+gD{@-JLY&2|=Iw9AZ_ZZPB&H~7f1zd~E!}*50;nLYTz;*k!Q#GMj=mN5eiiJ3 zJLAzVAjGmryu(es^-s)UIwJS^Gw67ub*Uh|-k^$c$NR{=>M}pA<9h6ain6bt-K$IB zTXlH^K1Nt8t0L4i-RUolG<9?YyN_^do*Ubm6KUZGlm#t~H6;ybKUH)q4W?nZlEPI; zi9Ef1vQKUPLPc11aEC!i;O#H09}TdoLQdB6Qfee0=K42BteH;3cm7A2ceF4QG`ab_$C_Ic^N)itLFV(;DE5=|LA!AG2~5&MEXJ6jok zhUrr!?K3zrX&1Xb)$rV8BMM2El&@27Q$*iS{*==CrJvx-6Ms9&4GA*1m~-WrpNZV7 z@Y1{KORo)RD-!d}1%pc$6*7HlUIx|O^)+C~8)du9{yy7r&(xw{z*{cJSwDkn?dm?Y z`cGNT+CoXLTCNm~51SktX>Z|K%B#_|*^oD|#NcSumfSII0S34Cr&hQ5hZGDh@kqc{ z>R-o*AeTk_Ku=C*f$HbxO5b_^DT^4z=W`ga zOg#31^o=hX8MC6@IgPX8`cie*fg^&m-9Auh{AFtJ-orI=m}pia5rRX3f3q&SmwXN) zVQi_7@9)-wpK9lS_Kk6!oOQ8wBT-cnJ?5g#hjH!F_r+W!7StpsXRFW=Frg{80;io~ zK9G#4Tc6k%?C3vmv2fez%)9jnaYpr{fqX#hx0m=tMZofWH}J**xqq!Z#S}|NnhM{W z1h9sI`A|&$*Ep;Oj@{jkbI5sQNCJX#0?uFD4wy!YgiYk()B&Wj4)a`a{Yz6GAP$rs zS9uiC9JC17!aU;{xMd#Z(SFzbWKwSsL$oTP6EK^NdvYOapK-aF{{-XMVat!JH9vHT zNi?~m-t7w~T=3~P>>u6Og*fmMD`asGX@1;$0Mr3Y-9S9Mft|hGBJTOjPh$P?b*=Lk z-%8^jJG1(eFcVZi>Yo6|JQ?QnHrTNC6-)A+U2vJ}#cXBTN#LF#x0Uoz9`X^_OqKC0 z2l5QOd1*OT%rwn2yNnqJ;XU-P$=Oe$TU|ZWY3m|{Z>TqSu|~oH?pCAcl}YPNCnB<0 zx?M2(X5_9C_j!bg7~6F*1EP1V;``}4h{HN(NOjWE%dc?LAC&Lon32+3*P}b{EzIe~ z*n~)3=M3h5N2Zo~NRtwnpCROH9hqORqC;B6Q_>y2&qxg2s7hd1E?<~;(oR+&*m|6e z!xdan|K>--uN+)R_0WRVb)FV(2?!^6DgTb|VY6d&-YKAHaSPn@H`v>4U%wp{0;GzdbxmpH8PC);$lfFHN^XuisRSE2P7cEzp+ zv%lfOv^S9M5oN~K_dnrVomn7{vZsV$!V~F#j@>fvZHd@yxn4+8EFeic=6<9vA@+zA zII^EFa`(V6D|$@UDOBC1RGySsfCS%*ulf(7<3UcM#v=ZB^0P;6R{E(STiTCe`FsL0PZeMV&P^#OUd+Fj@n{Q>;Z2_)1^o*YU zZ1Ty;j*cOn4hL}q{?CnI%8zlI`}SDAd9bVCOuo!030hi`(J9it?Lr|F*ZObKaYhuO~t@>r@opyf+gk3&ym|ciC!O*VDvbQ3J65n;UKRu+Dj0zk5G)SOw z+v02pr7movMVCyxSqOF{9wz+qxsxYrMQo|*0ihf!<=;jqqCVRdo%<%Yc4LopM1<_7 zzg+pJ@(knLz7XwKu3Mf>pi?~93x@ePfdjZb>*rC;be~M1Zle2T7g_Tk5+P{9&_uqT z8-33eyE0pTBN%J)@S>&WRP@TV^;YDrxy#y9!zOG?)s9>3B8kv9Xk_kT=?P?U%4_H4+4M)A{~Qp3De2M~O(b$+8V!=J z?oQvm+ai8&w`wLNQUNpEHrO7ERa=H&iM*}oy7zp=(z5K3T7R^=a+EZ-dNTgr12F$V zH@ryRyOD!y&vCTkk2Ke4arxE-{tS!)3S3@pyV5MSbIzS(5B_NwXF4qr=Xy`DVs7zB z)PpSM(75`awjig9%vf+`X=j6!{IwIO!us_&9$@Q5$Kr;pjKX2RYRR!hdlo*bu`y3# z?_0XMi3(faa;hDX7ltNep>cQMxowzmTisBvDgP(kTlDiUwl|D|_&A@DvBDOIt+9Qvo6y8cNe(7I zcANxj=B=q4?(G8a>!R+9+o|sfl=WLHhDE?2C?q_RImrkcj-M~EhoYwJVQ}c7j;J>H zNZl4&4%k;CaVpuF3^&L`czI=1PPqRGoh?**b7$pK2cln4FxDrL+p!KW=dCd0^xRP^ z_I_uSQMi4&34WTN-HAU4E4|o|it_v%(uJ68cY80;IEMdCOwc=@9j-(B?VW;1Zes;| zAJ-IAOX{tLcc9irAkVB`>*CD4=mg3=^R=ORG&o0zg!r&seC6JIe12!!BvgG(xG3yX z*0U*;VU07t6WYFK4?V8zX9N>%;5h~$%OF_S=OP4_99Mw#3%cBUQROalU27@7uMJa9 zZ&jkz;}ikJ%R@LPxN^_~uTX>R2o(~ts*R>85szf=6R{zd&JtFEA9|3N*>Y#tj^|$A zmwG7JJ0bXec(}AqCTp_Pta9{+#>*ZWl62E6;$6ey#hUsB*Zp4^jd__;nlEK<6pckz z!Y;|aZLDU>&O7K4-fg5|Znefw@rC7j$JLRo*6Ih3D}19%37Z19-=9tuoy=L6r=z5V zLAZv|a>&>B$%#R{p^lf)Nud$AzsJWPLF&iyU(u{S2ziqi$9aLR!2_qJPm&t8r&8?T z5raUo&ZLT+emcN7Jl=>Gf?tCav@hOSe;=B*I34X;6&(z8rCg4L6^S__)VHhfD&aFB zLYp~rM_lD5DZ zl88!>0dQNlr{TWZFX_jo(}-!w#M2L7k1fQt{Cxa{59r+WT+AZ!uyK!~)0&8IFW1r$6L6O&Q5O8bvr~6Oj5WD%*Kw|K zzHz05f$nytV-s3K*a2PwgZm#+o{~Ht(3NHDPV1*L1BzEU{-f9IMhia*1L+EMuT_;> z6?~UZ3M)MHBP#sbBs*jZa;S=`nAL8X&2PggWa-ykV}5OnEls|Ee6iyc<&zNK>{k7>ngW#u4nYh&#qa~=3_QV5!w5&Y!^st$4|zd z@=#0XO2V0;bG|&|(UA+<9v6F3@-I;MZzRdgyO&d*%QCUYn-+h7=>`%{0fj!bG9saR zxw&-O8dm=_{%QZj`wsIoaj#cjDXT6B+g!cs%3|rN%3F1@8EXqV(STU`3MZ?&Ub#$w zXd7laS4X-qe#!TwI5$Xr{!F})_?f{UX&B#0U0S~O8~Y0OMI1y^E#JoTUgg8Ipw(Wo zPu1SjrQIWYjB^!446YO|NBk5DlZ>KuQOmN@S%N; zhhMU$&%YVMLvFtrA8AL(LsL;YEH=cQkdCVMvVCO}5N)j7Wpj%7wjFi$L^~+;Q?*5& zt5o*qxKuPBVOvSu%cm7Wtg0tV$*uUSxi^0OcTX3^2v4`F-%G|?jNAN5%|Y*}Cw4jP zfkL&3oF=6>btjT!V^=wd;qxb@QCt!Nl_HUL*Fz6gcvlj#=ITktd!g_H{coV8{jfCK ztD#an2L#+M6(eQ|=8l94;37TnW8g;`aqK)3E+o<{UCijSNr71Fu5=syxf?OnPRSO| zxZfyTRJNQybxk*uHBvDD=aMx7VLElGAMrcvrvQa6FdLjXbe1ulcdPMh@t0(R7_p2v zB}Nngvtnn7$2dX2m4L*cf4swmM@$r_+FSkGn*Z%J7wr?eGoI+W3o6oJ=BHpuA<(r2 z-SIZmL3gfTiX~n{U`iX&mkn1Q$7LW^XqqY1_f)IO$K*YdSz>Gi_io!m;-X^zAJ0Fw z9HyHejPDS|i16Fsn}gE|8TY(`_=f(U;+{-kZr(`Kyp_g!2l%#boC6_?=03CchPXib zjuuB7{Ci4D+)`Runb$P`5It@H`NHvv0DlReY}cK0^vgvy3s%1f@>;#dDm-++Do|P_ zOxU`JZxMBb)PxuH_KWFM$3<`>I3oIWb~-i~=m>+n#G6maC5LemWs@W18X~-w?KRgT zt_kMJ;h!|*@2(~ShF=!`IUhG!D209x)McGQYZG~Ar1dzcTlMK3UbW=ya=Wu+FCC#h znJa1Afk+fh{DBI~2|RIe4o|U;VJmdRqz}F%)}xyr6cna&ioS)KE4wTMhw#53)qn2A zFz(M(UR8*CY7sFM(r{pwyBajgg}1HWGa78qMXc%keEDe9K`p-v)N%mP%u8Jh65d|@ z76LhnqOu^d_?7K3R3ZyP<6QXs8P5HgEV*&;!YCF#ncd^_K9V<=4S+x@b8_%S(KVn; z?kjvP_4?N{J}na?^<_DKpC@ZuMXaYViiU(GY}TOGSb=D5x#vfg(^xl@bRFPMaL>0( z_u~Y8oXZAw7naeNjMa8W=4MM3r5Tj=WTQ@y0lZ#zhWusHXQ>DDC-}S359GW4JFITZsn3uQ9H8mLD_W}eugK^q3;O!Um$)_71(mz6z@SiKc0jxL=z}XnO ztkkG-$s@$1&Lbs^X*;|@j`=xfUHYr9+MsKs#WQhO1qkF3t)Eq3>h}Ndqs?5R2dJb4V_ zG-_Sdcoi>ezJMYo>tU%&A{Skl-O`pXGFErJawNHWhnw&G(U;X)G-B-o6M4EhrjiT2 z3nnVg%azRldDYD&+wdgUh#OTH>MVERZo`RMlE@DJRfFhd6wzK>;7HGh)1xfk5W?3SoGu03+H-dC~=Fy?dr$+H^!2Qo5b4 zU2A{lfjd#v+!KJ-G0dGB0%1mkZkuITUc>rZ0U8I};LYTA3Wa}s8@*GUnndYfzDxp+ zE`l_r)_4#P&I<@eN1&Lc2|&@xV|DjUH9h;1c3dv8-ZY{?CE;kmAs9WxPgod%llNq2 z7?gzF=<9)gpYsHsp~&5NQizhq@BV##o^F4#~gW$oF zvxX=!RueylQN(p4)pI{C(6nUjBj@(b8l0&OitWV-zQ5=Aats`O`i%q~T>E4e_ciBr zm!u~6zco$*uDZ8g(%n4iq2w)}QTOMS4S-_9F;M|LV*lM;a=e`BPH96@lW2A_k&IJf zepMP}s4SYvq|Jt(5F)bDPkgjAe2Wl|8J5m@E#2zTB!!Y;U_n9--Fh@CZlcVHLXfj7RHs+dH zDJ3Q4s^lojIbm+yluEA9@!gD2LWFNWeg1>bfn(!ZpZ`FgkfFE}9~qCq*9Gi41W=2*$r<*auo|fR>~Sf@FR$lY zl&W8R(dgXhJRGGl%ko~#tr!r@=$+o(<$)Sn%P0-5Ub<4-WEV=f};gQxe~C zRdcxm+hr9JfAR* z9`iRUkA0eNw8orX)0>7^a0@z8Ejk8&P&4pfo*|;{_{lf|NUwDOZXo5Y`5M{;dPB22NTs7G#exzCFS- zba7x+O7@>s&JCwiNAJvvHbQe{)4ZYL{sHeKQEdBX{OL%@iqQSq39Q_iyw?|F+HJXk z@mDh!!n;avB@Umd|DCyJn#R`w_{*ahIlPz*c!d(b^Yb-V7pNVDayPe{lxB;{mvs3A z9;Vs<;rmFopzF?`&~D+R#u{k$Tl6m!O{c>zZnJQboQG1-LEfA!#lQZO2M01q<51GX zJa6|CL$xrFYIE%GU*rDRf_7pzEH*sR$!5**lm`GXIQ{qHHiLSMjTojXI85B^dia~J z%j8!GLiO(oyrR`_YU{4j_T=W@Jt_6_ zEvW&6S-QFuG`19qtnbjM)UX}ooqBPty61bW`y&b#h%0N2q7hq(42R)$yXD&eOf)fVj zD{bBK?Lha;`hRuSc>j{xdWsSoV>y#oMSnU)$Ro$U4R20JC1*ijPQ7%m;~yR8I=_Iu z)Bn$G`|_lR>CqGoGh3qaL0Q1n>9}&PtaFUz9&4n>Fmv(Djq;y++&=)wsrQd704}n^ zer>bo&M_SO7G93-&goU{Ur4cjCFphmq0?vT_qd<(x~!M-0ToV7{aPzdaWaf1=e*<> zTYKH7gVu4=A{=u2E||DJq+11RpQva8&L<^wjXYu8ANDKFt*G9$hsY|t+L~g!eC37P zVnjKPlEk;hU(r4%@IP0ou%GLV>1+knbF&q*Rn#_;17tj>b!yJOK;qRXt=Gl}{$M6Z zo%C+9z6T`QOYl_#^@ToU$M=&xRCZSE4+m6>(R;v3gI;1n9EPRB()4AWK_jXb%JAkgF z5lwHQ_d?Dlqt!l`4wHt}G?>BB|GhNwUTgVq0<)s0_87%uh%8J`Mw%T?yus{|@uJT* zr~W}Xw~WiUx@f$u%^}B565sP)b9;VhU@silQXmlI=<9tU{~3nAF&JDx=;|o`SvYpF z%?{f3bHkCoQCAN({^E!&cwE?W@EHle{rYCWrC->er}?l@DUYUW7TBkSx)cye-86Sn z_Jbr8KdGC;icuM{jg-yd7$Bd{xC>dJx9CKS+~e{$u(JH8thT& zA&>crz*?B@-73<%@@6N-$x!}=d7}nvJ-&K;wgK1ei%w{x4r)Ha?26jQyRoNTt#z+I_ zr~*egAG@SCyVy!m_k$uTU&cgK^R+`Sz&@kIA7Y(SOuo9qzA26ab576>&1UMK&Wvg6 zo5ul2Ykf#8h3;3-zK5g3=%p`gsM=I3{Z)G@K6|p_&-|7vSmb0aAdA~(uT<6qzY8#k zSX*OcEwQWS>{Iqf=?!X_j;^RN3DqM zBwXsi`+j}1>iM*AK=C*z{6P@x26HbT{@NDFiw@?5Ehn|XFla2Vlp`7^pf$6i$g5g} zCWg}t-Ls!ip`*4K7A+v}uF})*@?{q=tco*Qs&{S_XDF>DD z;I0d_sz8*ob65lwsE*crx_=ve3O~&UP#T5-pwm+!emalYpcyScn?BJYwX5-V#-vwF z<*^rG;#^CEr^uH12A^$KRbXWG_eXWmvgp$A0!dltOEzew&abiy+! z#ZTGtxA_Uon9IcuuELJYT8Kn#WTUeS|L^i>&8rd1j8bOJ74%oCex#0OMm%SRIgfdB z7b$qR#OlDcuW>3OT=-7ahj3SXeJEpI--?x*_TXGGHm>UL|`<33NNk@=3PbpDy z8Q?}gJT`OdXooqA3hw$F+wg{k zlza1uTMiCiw@MW6*;5-BZ`zu*TvNN^(^JVkEMo#F&JX&fQTAC(bnkneC~M&@gB~(d zIE#y!6EF$yQjqAmKF-Q~e?>}W5e~T+&{-Uv6=}zd-mtfOv0l0bfY#ua@X`zuaNt<q?Bb9njCs0{OMEb5S;sM z&cQ|M!&k`I@LYux2Clq2!Z8MP^54pCuqh~h-&ft9kd4asmeXl66OoS+=giFS6x=b} zC3A96;H6_ppu#qJ%Rk>Wk-rwLD@AT6sMbf?MnRFfCpO9b05(Fag|a9PLye>F^!?ZL z@&O6+l$F+yNdtijo_^F3ZE<2DaIxIiS48Of!KoS%lNDc@by1)ZSc7$gQ5 z25_zkkOH-}9WqsVZ2Y!TvkE(bFoCb9% z!r9%X6iz=`ur9JU2YKH}Re7u0Pk z9@GPN7Im|4Hm|fBZU5K-YIz3X@`melgWud_NpCi(*Fb>0aQ#o(xx1KX6MsX6L`k86 zia+;S75+GBVL~EG%Xb9zf8_12a9zw1GRgep{n13(^d8P7S>U43nmHs{(CX(qr=`bx z@2h&a7g)Es30;~qZ3BFV68VeDxnVGDY+JO2Y0&Nf(bwbuNOG|9zCe>f^Mwkl}P&I*dICxF`k*inniU=#VX&$%{=Iz*kXum zq%?TORu#f?$tt49n_zVQ(ZnkgxE@RQ4-8y)RN|_-JdmUkW|4oW!l99kejle?-XzCd z?eJsDr_n!-*I&{nq0}ZH?GZHm2xK&_V)+7F{QZ~$o6tE-F;kCi6X>a8vc2Drw-I%_ zZv8rb`gw592b^={lpv)7C6V*%{3k}OTirgigq9Foi%xWC(faUEebCmfy%nz7Gj`K_ zQ{kIN-w)o&Oc9evR}fE))T4qsW3Zxz?kR}4a#w2+g!Y7LKfuLMx2O= z+!moRJuu;O?fb#*nu7jlBEffYx%eHki_TPFJD{@SlVa`tDyzVL&rOoIlln3i?3MSZ zGc+GB+GIj-wK9~tj3eVAni+}g>(4*82}a|ECLe1;$LIm7EQ}FOKVxd)$uPuyE$BV}UfpGFUQhU% z!+Vzv0h=35wG@&ZetNFy^`H^e)&NE@u=ejf!ufv#d zXTz`GZXFbG>oc!K22qawkLeaIL4j8BAZA?-vppzsWxX^{nb(hl*iPnYc(1dpu2yZC zWY!Opg5T63?&uf+eLbs54>t~r@5kgVd$DV!73qLWc%f#W0#$H{uz-KiY#ae&DnN2w zL3}5?I9W^11uNS+xYF^vSI!W5xGvw|z!9}!*(0v}rWQyoKf~#C5kL^PN)3MG7?qDB zx}~ZPP6woj_d8P#KWiSmegZn(h(5y-YHpf0GiPY~-*7)E?+4vwzItr5*q0JzsxLw^ z@GZM#K2ViU2z%=j{H$fc#zfW7U0flG(v*esy309|O89qSMA!#V%LiW5K%c`(ZuKd*|DtQxc zBCX|HjYm%|^F>*le)o~~@trG)=|@T4HTDbL_>eXu$JUnpIrREju&?OV9+l5HT{$RB zC5o(r9p_cZc6H;Ih>Hmh1tn#rgy}Qw+H#Z%e%&`f2HV8zrst>}&0<-OC+68SqCer~ z2F6*Ho8akM4BD>dP2Pj?EmYnE=$&bSepwB{wt{UQhkU!Q`89$Akc3mWU`}kStSR{*C`2MZ$(t;%r zEJ8^Z7O(rn5S{@#eHNKk7FU~Tf*CMDi_=)>uf4T7%c$M0A>Oy#40PO*RLp)dn@tP@ zCdyEJVy2*B4*z0!p4zIWH%>%gL{sgx<$)*)RkGpHM={O2C+tx0_^C5YM ze*FN4jw-7DP=!WzT4{$=C*B_yF?*BOCLS|r+9x{ViX0KLTZmsBJ8ElC(R#F_nZN!7=2BF#|^jb`M{FBB+ZY)a)#Jn8$$xpBE#___E#qaew?fjyGPAP38cE3z(#U# zs;vZSZpwd~Z)vh3wRd61B{cP$O{Z^R)42#f5~F*jiG}GPX=S+0ROr&KRp6x-cMB#H z11s>UMXCkpbhR9KH9721_k`&`n$f)o8=_*hzp{@FUZgpek5I&~2)#O#U0DA8sGXEM ztuYjQ%?E!$m14f>L`Wfn4m{kV?v#_4?U-ac0p*dt^8y?(?Ga#`!=QNB_u2qJ$JVJ& zhwftA2lCKsT`uK{d?}a=x{rBXZ;l|l`B9FA8354%UvbYNW0oCtMTyYYchM;SbYuwl z`P;2^1}mMK!jmPzI`BPc?fvYr=NJ{5bhGs^y<6XxEj+tA+t+I138a@R(>{g9{EDht zN%o9tdu~@TlDrWUTw~UvJVKxq^LIF*N=F2uft{Ko!Y`$ptaW|-b?FRVSktNyuHNAKmDB~W zq}r)Zj3E9gcA7+|%WBpy-2-tekp`*$W$#DMjj3dbj(=wI_=AKx`ZM;-V9YMFF<;ur zO$YZPCMHbar8*@N(~jp~cF1N|nM&QV^GjOWq>C4#Lxo-zNt)1)6oEAac|9@26F0*J-=Ym&iOHHvMe+r#(&giKIkYo`MtvX__k^2?k2}`-4iM}Lvv-7A_=mRB>QA@gE zCpl}w(3RBRadq9)9sP;Fu>!6I#a>cS*jw|B8hfI7d5|lh8&amqPZ#>}ci+OKK@q8K zy&$p@+!D=z&qxc(1I>T`{u16~@Iboe+Q4@qHRUYcW4-9uU!B&f0DljbK~Y>cCPTv( z>8TZ&Up10{cEfZej-T`3S{8C+NMsh!N;XV57Khp|g%AgLnseR~`MJPXOWq!;&4P1J zMxh=2Mw{lCIU*e**_n&-@O5*ShF@0aH;^(cWbKAlN0B1YlR_=JPhr?19Xjt!1+?Si z6#ZxI+|0(-#IZN&5krk{Xo5dZDx#< zyH9`Nyh$6Oz3OOzHC9IXfm&CaC5yyB(vZkjZ2E(a@(wqrf$X~CYpN%@akMjvXF@>I zX9(kp0lHeVJr8H16dh`z&d zXFF`3k(%5YcqA^`NXX&%TI^Y01sF184WNTIr?lc2{=lIF`yta8>Uwlz;0J*=Snb5Y zXj`O=Fi1LrE|dE?^UHxm+JdBo>AM8TBzKGpPXQT`8sRcfxgTGEmSmelH;>`iFNUvm z95JH&2pjR^a|E0X%^%VI$$9PmmloHfeV9F7eIlApFXXv6<2w|MpH4q3iSVZm9bvvO zdcwKxC+H$L_%C6hv_+v|Cl1j&hV+mNUJ0@CkxXo%oKFkLkW@xyPU|I z&&@1@V3e_<4aA0TLUvsux9F)4dTLuiQ=SSh;SQ@cId#PXijUeJMJ-L_o4J#odGz&G zp?Fg+(9NnQr|if0g`NFJjV0|yj8zK+Oc2Vp0_1>y>{G$xuSxgL=?2HS8ciZP>RKOlav7U21$(VDGv0w182-xQ9~ z60f3RVZfL97KUi**vO&p@_RW1DA{E>f@btyRaOj$30Ge>VTkuwhBToQZw1$}d1|sc4 z1Jj;Jo#tB<`y>*uRsE1}JCIo!QN<5k6P@rWLEnu6NUhjlH2itK_3Y)4cjF$i2RMv$ z!5APkPE5Ez$7e`{ng$rrbnJ(l@cAmrYwn?!+^NGdPtGHeAh8z#`|r&7!X{5--pjmf z5Q-()Xx%BA!%bG(5$7HI;wu+*=j|){zA3-`W7ot5BugS_YaIIrnMC5*6!?AJ{=6!+ zqRl^_L9!Yc#xdY+db+MH`syWsyPL z^_2yVba98Lki`cHkZWTq&|!kY<|&ahoC9~TyB`jSsYADtLq1icki71?CsDKQtnh}9 z!x41r18qS1VZJhbkf>91glYT6r=uK{V&;W2KqQ)hC5VM6hVRxMy>3D1WLQdl$i+&$ z+IQS)#zO>;Ci94crDz7>pYCgYOs}HrnqtW6z#lGcke+MCy@JlMBig`6LkQKOq;oYt zbnusIT;b$g^fQLJn&}8;+p?hNlB;tigusu#@3^mQY6#_@#^H z+in)#!v0U{DsoQdC(>y!GhC7=^I$jc# zHS&~7ETR_gB63QDF1ekR_N0NOIHM)(@}5Kb5h;`PQ_8~Ac94a{tUrGs-Uylz6OSGCLLv>p)A$@d*EteDX*0TcqGPQ8fo zQ*3*J$m(`4^P#P)muMDTA1(v{tg^``LH-7sCpJde$#uKV6-lP%JsOH){DTm*BiFvWASuzc-H&isU?AkXSUz(BY==E1a zxUgx|%Oj<72-yia`l7bu{=tE|Z{k?4)R`0#;%WD+FiWL7sZ5j?GeJqMz1OA8+b<#8 zJYEM>6u;bW;|6fG(!V$1)-*9AcblPZAVd%ryLB`1zmgLz#?M1l)txR%X*LfXo3=jd z001V71v~p7ydS2_tVStfS6&~e0YGWJMYJp*Nl9h0k+;d25r{jXBa(<@KrC~o=I?n7gUogl)>Y?XThvCh_3RmGFrfF-A7%L; z6WqyaJJr&{VxMoTf+fxLlg&Dto__T1s2$+Ijr9Bag3H>b;fYgge(sfp9}t`iVO8$w zNqvV69{`uPGj-r?tMnVK*$%e|41&X+Mq_n+@pNQz) z*ZRVtRVNV|TW>^dU3m1SB3sjW!I~IrFA^Y^k`xW`W(NiObC}fPHsl= zLO*7vLQV+pc(eZ?HKA$G&5nSKn1CpCTfuTtz0V@!j$@(-^7)mPzikBJLD(J_$vo)) zQf{I-#zajJ1imlF#GqHXe$W_RL)e6EERN_Xc{#8$RN&FoXN7SK9YdRV3zD~AS)#SJ zNc&{UvcBzdN0N&)gQXSD1{so@%7p5`BT{^Q)oo{tklS9%JgI9NTI*PI?x0=G@HSG6 z%V(V#`%G2zMbVSB$YYob^;Na~e39dFu35VW?`qI;ZT9m#xP}Z}_;~YT!>xyzWoQS9 z8UiAmGf3-bxY!^|(XtOA++w_!!lI=hME$maQnaFKRQqo1aSEDpm24%JcPh8kwbv|D zyEOO&X2f0>kgO{zDbKbKbhc7Vx=ZXQfBdoKvnC0}8edXOlx5hHnCQGI@hh$1hkmWn zE}ePK@lGuY(wLgppd@~wZOGW&BNN6+#2rOv;^czANyp)6kR<-m{ktD;`%*L;;1Ip76Q?s1?wn zg=S;>Y}m+YH2>t$hi-ZnJi*;EZd@QEl=&r*#3$vT`0F8Zi^%sX1MG|I#6`R3MqB<( zB&p4o$apC#`T@rR##-0(e`H{BX$ZyIS0o-O!5DKbb+8`U^a!Ib$U2Yx2K+11J8M>; z2e?KbGXtvcXIZyC@Xhf-vc*<$C;p3rJ4YIuj-NvtIrKizcx5`4AVE!c$zPK!jJ@_F z5QUUrla-=+-|mmQygvaPjtF_Su>|MH*Sly$o%YbBc37(!w)H*e;QLB1nO!C=zuDuoAk_s{iiYieIH>gmu9c`rmPOH1;D z`){y_`@D~->dS{)R|iw)4G++7tKpxkqBND2JFO3q4^W9cLLFVOa8cT@z-ysEK63r+ zx1N1B=W}|S!;nK)=T1P65rP0eToEIGaNkIv5w%4!Lea>klrWNn8=UD_8<*H8o1` z?`y`9Y+IuVmFGb= z_W9eGat!zNiIcznHE#@|YaX3z8Efhi3z6j8;ug>sO`1JiDto2Ce$ad#|oW8S$8(ahQ! z5B~s7dBZ!ylwW9}A?*>(=Xes;qGz3+DpEzKGIp=@n|_9{9J2dKJxk;ZnWheX4;$dk zf=-kXX6nB4CYZj#y_@xbL~+ zs0%z>#{OOpK3jbAfk33{B4iuhhvuC5tzio7{XKP zGoFZZ0v_Je&NOl4DYAAh;$x?z3aVN8-Ozk8iej!Pn=Hs9%GcbG|LTfgfQ=x2>+Ay2 zCt_=u2e7`j+}KttXW-Mh!Q{7(T|4{Zu#QlI4<&sAkb;$f&53qNtTD$WWa*&ZP&tZQ z{hrcL@9sO!{O8z|H{&6T zjS{U9|JC}K5uHJOs~^@@;$Z@b0^5Xf$U4n2n7o7rIyJujCFA-_XSiT9O?tW|@GV{G zBsvmR219DP&DBmWnK1kX^~)J4CKxT?3fpwsXmZqB?LziX#UbfFxk%3Cg9Bc)l>OZg zqySccn&E9mlu48l7)I;u_{{dX3=|QhApmDRkmP#C1gLQ*+rbGwdX<@J>tlD2a)?DP zSHB2QSD`g%hNQZT;|&y#)q86`2tg@hhXM?v9lF@NMK@b@b!*qNU)UttG&L|TFW5=q zFU@*VuE#ocvLpdc-e#&mIwYrpVIG@)l~glzoJr@~x`5P8VITFU+3QZcsbq>6w2@5i z_2756N7)RnQyA9F6MnA!`Vzl{Ecw^_QQ9#On8&8)h6KSnPZB1|{b-x{s+U>HN3EQ_YWr)}&r#$p0uV~3Z3U|qdG z%RO#Fw)#Qhn^d5ErY(y;ZafobQ%n+oX1hCXp}qT9}LmE4C=jj zIZJDZ3!Ay2`_s9N+JTw5I~rSe3d3`bUGxAg-j9i|V!BIa*N*+x6AVIEpjeoQUvSW$ z0e<=CE|Xf`Gv^Sy7H-m<))REq2%0jww-asbJqdQ1AkE;VW7Uz@FpXzgTs&HSEWczz zLN@s6vwEVyM%qYv30X#DtcT^Dv?YVhJ>#U^ zvNumYfxrZT2sTsrq!5phs0eRXSFcuy4De8wD3Inwu_v3+TO!XZ!{wh=7s~^sVplWI zCEud|Bk5%;Uo0%N7|S);P~|h%G)?6Yj7p33fqBszJ}lY9bDQ>-z!-aTC5nA2PZ24E zdniL5ne?E#+|{nwNXuM*^l>=>LpZGR?@ia?_UVJ99W6(bPU-WWbalqqj5z@gZ~Rqv zbZhM(i!|3ccMaQokV0!VgL!9cKF#t>-&Hz_<5dhB5LYUAhl}Kg1CH`X@#r9K{=POh zahc*Q?ez9O&Xy&dh&U_#dr##Uv}>>T5=$b9G3l-e&GZ+;tLo=7OlEmiuPdD}@MhYF zUsAG^gpwB-o?ewDs^0r?x$bM@K7w`xfGU=3WGv+Fo}WvePF2Z^eS1IG36d?Kl;!z* z=bqP{rN*i;RZuj6L(b(bWr6cnLnz05T4(M@jNLyt=<_&xrJm&cN>Z@3lH~yObo{6O z-*2NP46R5A0)+ji@h37E!>06c|-&f_F^$Sc@_N_kxT#geRqodAf4!U{G_hGqP3jK^i zXA^T66;e!z!?Q?&3Qc^vLiCNh4X8IuLRmDSDak+i0J~A>e~27qx%s-q&d3zFdyuOV zzSF?Y_NqqeVZrqxEt`{qgoAxN`J|%hx+36p{H2?cY`IQ8o@d$B6W!3#N7h_OlT_w?`SV>jg zqXL5FQ0x`n<;Sn_QUPB1(&9F`syM{}|AJt`OOIqb1BCrWl^lxddmXUAsKE0pt*$`L zu>|pViP3IO_Y!b^3X6GX%(}Qn$_m98#kj}NOh0KaXYE@d)uKbrp)v)3)0Pg^9+&1~ z-W(hdDD#mki|nrR1QSbgmH*OHG;8pz3v?4A%oQx~K&^3+LzoO`%@2=Hm}?CpZS=3t&D+@aLK0)XcI+p8@gL81w1G~(?vN6%Ikpy=f0Kus zru{V&Vo^hmnVNMSMr-Qw8Wf@#crw#kHYxE+`tQTICqVVS&A%?m?@EzS>Kpn3TGBsX(z0T$; z^Yk$L@#d*QN@r@1R`^R118n7i%O{34Sm`hD^dhc1!Ua9cYu?0!^2~W7H8+ zk6Ib|1~j_AubP1M%4OG-bM&|?r`?{?86(SK@!ViC@H@brayBh91s+UTtz!1N?4lD@ z*;lnJu4G6t(i2+pXTIJA{MP`fJ=5wn<6;9@TKBQzd;0-y`bo&*86tp`IwSx>qDogo`H)+0 z`A}qdB+&Tp<_`tCNFQ4Lt5q0se}-8)mE@%#??EU)-&+m5UNylMy);#6!W41!?KLi%Uh(BXh2wT`4iaT>hRL%cS=ATk z3XjG+MvmvfczXnlFdfKeVY(fI3tcVVV;-Gj-NwJ^0G35{bxz*(>EPJc>A5#)*72xO zCdp6sb%#eV{-s+pQ1^zr*}stWPVC_~~z7*>ibE!Co3z zWgW*m2v7VvMcc!T?nVCV|B}_ka6BxeJ1uAmpx$dewyy41^2!lI4N9pDxKZF^ZDJZ$ z_J%6Wqw?4oXyB)M>f*rxrdgzRh41yoy%L6mW_GCTwJbsB+?6?bb@$GgFt$;M6;%go zCRAtx0NfwoMBEq`+V^riUHWh9;FR#%t8KHd?sNfx9omjl9*!)41Zq%`Jg7NJt`LKe z+m$T13`-SZpH6y$y#4#NjUq+XRd4g8A8Ku{-^JRSec%gm=sF{#PI7{nzDT1Py1+W9 zDpBsSBS((&7l^Sq<#)#_HO6wGo(FGt_+(Dr&Bv`Dx&BRb#mG74=3@PeFjKC))X#K5 z+E0aYsg_s9Kgm5AK@NWYE;(JYXXV~;&G|??I#frj$ViI)yJJY2YRz=}U1riiY`3YShmo1b5-SADw~0RygzKJ)8DV=lmF6Ch*l*g-v~(?> zhNOqhwnNp6fy7cYUZxgyPt8p|Repc0>hYqbJ@+k2LgvnGDqH*Y;&rzupFx+kr%9}Y zHZSkdL$Jh|VO3_V_gVrC&V3Jq&xQ5HaLfLqTYT;;_V0A|5PP}E zWde*l_4)*d<}zueGg7({_SJH0evlj{6kt#!r2gWf24JlZ(&%wkagxZ0Zd)y=-DY2~ zj$L`uJS5RF1iwQ3k+>m8HndBWnr3(}nM`I5#g^^2Ci;R=GsVZfa>E}l&h&@Gc*Di4 z3M4gMH#PI_1@>la=3Uu-#SiKIqLg}8iw$SBMV)wXN_Kdp)4(RcH;QpjxY5SonL}vb z4iQw4CVa?qr;5-{I!gi@*2{2d-@c-AL!lI_4$`0CU0)eC2) zlTO#TU9a{1QGsPWlx&u~6@Mz=)cKF~zu(qVe}7>`wT0uV9Op z{pk@OP^|HZdZWZAQlkli^L@I&I=!v?cGYj}Wu}>OlZgx}&5LN)FREc6Qj|naiV`I% zb*J03E={8YJBa2g2roq!#R(0nPc``^ZTV%mKdjn0*iFQV z{7c!oxZ!-{oy$@xy;>oC>`OrPQZxbP>s@iv9A5VfAR0(s*Br5JuLjpWNJf?^_MmP^ zBPWQ}TT$lxQR7)d#*|lXZ2PoOqqeiTCfX?&>S~*(&9Qkc%7^BCu|ATP7rpHpwv=|J zgsTqidewX04Csb1Ga|}j(Q{;^iP(r#00Z|mAmvkqTsv=)-Pe%V%ykKbpReP6H?^pF zzBtLB?&N9naD~#4lf3kk8eUJ#&(sl?SYK5?_cwU(AVvu3c>mR=w5indQbMis(v z?>|^P$Xv8F@S;2EQu_qm4-#@^1u+`G*ng)m6h+|qZxY##){@)WErRj2cQc;prZsqf z5enc~@wXY^v<;acfnMiE!K6r0PrJ&E1dx;21X;0Q@4!b&3x_Ti^io%?GA=4u2SB$RlvbI~JU( zZ9IaqRg=R^SRrQE!;Qx^h!NIXrv$Pl;S#c%^o|W);&M{;(=8f;yC#r?xX0S-7T{GI zMXxfi46Whd$F`aA@jyU``o3SY(->Lx-VBFVQ84z@rkN8YLFp_ED8j-Sv}|VZ-dk~M zA}UD(f0}tJqQ`ky0u_}Y_L&e&KEnns68~i}o);1`^ybf)couIA&{grJNgrxg#};zz z=9dOsS{cpIqEl^Hz%1*C`2jy5Ck)zgW~GhkeaGk0aac6a#DMe?S*r0OjQoB-+Y)lo zTkq`>XJ;46thF}$y*co~Q}%U|_ds{yY7~zLge*J7$U(Es4{Yj}0BMZYf2$3pg#w~X zDErS&2_);o?6qS^YIltaKsm$#5En zh``{aK)*6K&}S}h2P~Z28?oJX+fLs(>b4$CE$ap3-Ogs}ureppSjyO`Vr}SzYLc)_ zR4Y-!{(W$}VY%e-fTO3m^U`wsrrka7&MTJg@ppMdan#S*4pA-j=luD)4X2=3@OZ6+qK@c;c@?HhU-f{6^|hO2U6t96 z0J5$fViLf=>KmYtizv|f3OMmGXt)i9>V;!NfTFKZV!NV4_ifJ!7K4IT?RVJx zla9N{bK7^AH=l>$3ukren3t*-U}(wwDKM|meP*cCALJsOFX+tnraIxfrGgOezp3^1 ztBMJ4=e48pFus0w>*iF={7|0PF2XJ?>9+WG$!;|Jt~0|FHC$1D3}Zg-`5wM0l;>8t zf%qEKO;!OnZ(ehK{JY^Yi-|Y_)Y{R4JnSs@Zuvqu7}m}*J1umz-2+_-Mhm}1Y^X$x zbg=wTZk!YdU3z>;mKrH33y~e?(GrXjhzCqd!!%Js#{&$CrOy5A2i+2%T&5UGF|aB3 zc*`O2rD2d3C)RB6L(3wTKce?Q7TBEHdV)q?#xZq@de!=$oT}{bP z!H>RPlZuCBK}+DbhO);20L!n$5p1mP@zddRV@3;SKQ;;Z{3!WM)@0Qb&3EVi*jSyZ%oV%)JmdgAmYwEBq+sY7 zA6BQ(NlOLw^6|5+(J;afA9i|tVfu5%(Bq6$+_*%b$cAH3JzeBYpy1!8sIRw~x1+D= zKUZFslRgV+>)CHI?7L+VE`#6?Rnoj2i>a6w{0^B9N)ye)!@n@%ch8tEgtn76`_p-V z32^RDR0cxrxogH^_E{);^^nVtR3xC;L=<|il?xr7xH80R0a?^>s?m%KNAE6(FYs&f zQ}>!6T&Nun`m{jg-bgrL{r$H$x=4HD*U>JM|E1Wb&s^{;QuBZ5W{7aHpasqUM?dOT zlBJ4%IAC$A5}T>0V+CVZV94XwvaL5O-1DQ~2!{VvVhcSTDox~au;1e1blMb#FXqfFUGb(% zKhud$2gXaB9{l8W0UnIcb(=shTsGHl&Ifrzds5u8ZhbRS9p{mOe-Bs-8G$LVZi_?1R?AgwTkQ@=J{+x2>kD)!Z zmiU3iJu{hF$A>O9N7LnQ?s!Vuxut{K^F+3;42STo zQjWD!ydIg1*S4Oo{pwu6Da#a-+7w-z9_88LlsIO~cG@uWWUAzD5#$slW-!98pJp_M z>&2(W_MSBVO;=b6skRF8+1p9evv^@U3W%Pj88zRc_rLrwF_9!IroI&^+CEM1=koi% zu8#b8X|s(wK*If_wyHOTzz@&D<0EZUekx z00RwPj0gKKtk=!>w1u+>{Z-xwVJT4yF}%Aw?F_mk0#LU1L_vRp!r$u8PhXCDg>@& z>dCK{ja_6)H@&jZ1fNhU8@Tp6A)Hx7h_*4MSf3Tj>~B>6cGXiFzxM)l;qLmBBQ_sU%^`Y?86GGayaJ?1JOk6N4`^MI!d!o|u2e zuVF4kdKPti@z0&VnOF;feIB3&d$=cpt=kCBl@s)Ti|DZk1Mj1s!ZHmoESW(k1+tUM zM_JbS^8{_BrjUK`+MU-NMligxlT9u8TEG{Q3Y~i$(7|y zpk_?XI2C2lNH;X|5OO`eFU$8Ia!`m>F0t_ufIXi2DkI2R%?d(~4JA2fWbY4c+hn=i zbx+R#TJ`9hgr32IfyTxVHt{^vgB2|ncXr4{j#>qJd_Pl=g^LdkK5Xq&ry&eTXTLo> zIfPn!Db!MbPN@5oT_#kTrtO>ox>SudunV2G2f`ib)t*>!n(;Cj2BbyTYHdGR{~=n@ zal1hcJQ|#JW+5UNo_40nI(Cf50R%n+t;#8cvJkzxW zBa|6vkW#^@Drz^gR0hv;er17Zp8VHjCDM^~?1zu)EqrWx}atpPu~f_!8uTt^X4Wo^w=CC^yDWQ5P@ z-C$q8jQcW)8c2cq#Z{AHfD^@!!dLjw7j})=>k)oJXP@puA4u8_Ab^;Pb94N3nF%$5 zvBmA9XxHdNf$4)s7I8X3EMq8m{MWVo%Te_1usxR_v9rECz~sWa?n-%N%q4UtT}^`= z>`*_etb->NL*)(#&BJxB98D`I6V-Pcof7y~Gl)M*6Cyy9?_IOj_Od%ZC#_~~>bGa4 z*=Q|D)XV~6k_4$a5?l`?kc18pqS*DUd>lIW81*-!QUAJWoPY2A?VWVH zYx{1th7{L8q=unJlXGz0Re7rzgyBP;f1@zM0bmWeQv4b<-oxkbRhUWr%OgJ&`kf-W zpM+gX0%{44*ro7c9h+9n?=Y%@0%NyZS01lhYlz`D-wr^1jL^=aA7=YXa8*N8r`FDDz1QX>kF4WDR90HY1>VvnKM==hEK^+bjqVg z&xz%v?>tb^~H zlD?}gtZO_ynJ#Ohk!#Z}lo%Sng#%LMg%3VHWOl18NmX2(m86Oz`Uv5WbF(vY9HhRa zcbPiA!lN^cj^{FqVLndjpHuG#^!{w+YdDRYluNY+f%$5P)`o3hK!fi#`dY9DB z+FhgnUHYK&CIiKp_r%rpijuj$ftVUnVQ_vxX>L7l(Np22@lo$NyScz53y<(GY|F`T zy4jbwp_Bjxi~cFmUEByMzi`E{+tWeAIGdx+ANJe!`R?eB{t)i2X? z4pJm08mwXZhhlinXykBeWL5DnOsp9u$s=}p^{I5Fr}L3Kr9&4tUj;8hmzv2et21wB zSOi|3W2fXxkTL2_SWpo#k&@on`9 z(9+#jE_1L=&v_`%an{PMZhy|Vs1MjLY4D(nBqQ%=ylRe>quCSTq0on1S;?rTL2)vS z*zF7z6GV$iYJlB~phClW;DFJ=!UK`gP-DT$5y=Aqe-?(ovzad2KVyPFtFq@%d3wMLx zX_3-r>iZKVTtu_XO({NDS%OMp3}bT4J!wP}%VLX3@WN$Zt4>x{c4!#O|8(kW`}^{4 zU7O{b2wENbPn_8^5#6OJd}2}L>FS7f#6Vy^+;j4HMoxWYb>`Mr2Tus-_w|N;^IUG( zDW*#B0wzcC&PY4DZCP@~YVyZ(?4)C(!d>%(xoB6To;e#a*9svRXl+XZ9E}mLA>mu2e}kOTsJYV2=#7a0ea1diyJ$Fh>=ZH5jF;nojXA< zy7hYrU~+r9->b_qa+q3hW48k1bgS5QsX#mHToQS?78K_k`WSfZYvUE^5$M$2NVlq9oRXeFH{@UYi0A0h$61 zySWs{08svo%4bbGFZir)y7QepF9XmUXg(~7X?v*W2=HF<%6A7_Bzsu*?fKIE-t|dz z_9vGg;v{X*Omx|`{S~;P?n!+cYO49DDrUzYT+Y*W>b`*+zmU-xm}E4F6|rIWh5#${ z<~pgo9Zo+wP%P^Zyx(*in1$_9s|0DuLCn;e3O$n!mxk}W8s){P;jI3`jQIWs29zdH zZ%g~UMNbF$T=qUIHTh%TVJF7vds5%&!Q_yi-O7P;zJ_boun+3vPed4-6~D#Dp)1Q& z>F`Anh-l2nF8-{=8Ib4yyzA1*E${ky<{yt?W}Mj}c(n4-ppBO)V-6m_DrcLmJ%Y`M z&8g~${UuI;Snmf^K^vD)UPM(?X%%}%_4|a2{^?3K>G-!?s#Y+cng*tsL+GhSo2DT7 zYZGaxRLK*Pw!GjBg4}N#Zu-w0z~%>oUpDVfAZTxF#&OMLiM9dpsNiXx(Sbl>rXa5| z5fPzI()m|gc%PM1RR}Z)>+&2{nGEG9z+^rd|~8Zhw#@_Lr484jMiHc3h^q#XGb{wgLy_*fl@4V z^XIcP&Nl+GbFy?lMc3jFzL7qM>tYKP8T_;EPB(u_!5V==od&v%hCI+?Ll{1^S&wUp zg3r?+rm_p5(YDV};Dj~HyjN-Z6%{H88dODq*z+Y_OmH>UBAeZ)r3r1515bZsVQaAO zkE)>+n<%~yxc1qth|~t>vwMV@Jq1!nFjASkJFtd~>_t9>Pf9mP_6AI zlpTA3|tuZS~4?NSrf+Ta~n+W*AX}`xJCqmI&_IK%PP9 z2@)Z-G(}E*cv+*=se|k+iFoBQrRh6Tcz5yizf;`gRsZ_biX_7l0$TF4b2V_!rz<)S zz=_I%syLXR)9#elGrkiE_d`MO?Euy$l;0QOL0+&?9InEQNiP{XmU%NkCqI~UT~r~b(WC~>R=Cc)|a zndtu%R*=o?(Pt0c0a&Lv(w!+%d(pd7;u=KHxkI?;7L7C+4lFykv2rJirBgH+0={lm z7}Yid&5V5#DkDL{pD(%mtS#{aCA&b9D`iHt965JT{`!?2E8XX5P^ljJmS$)9;$M!N zwr!gB2?;68hP5-Dv=f;OVj-V5E!Ky7upQ3mWSU~bN&$wzmq0f<`PrdFZ$$yT{rUsd z>>9m*C@v=I6sMqAT5mnMk*3Z$06_c=0v(gw@9(L^<)lez2;KCL{Bk@q4Ce{ z&dR{I&8O4GoRjR=WAZ>)KSrgSz?zn{*!R9{5$eDC?ZEkz<)Vls*Yl6kaB zUbwBl3vS^{(@Sy!$oQe61NhFX!#b0jbN*;$r0}>HLajIU{G^vbdhnqNve@Bil2Dp+ zT?r)uzQsVU#^@~O>;i(o#zwO<2gUn{W#NiOm5cZ{n4ZC1<^$6(VI-JGOXheWb~uO9 z_hn8ev`tq1tkP`Kfv-U{4psCg`i0n1?2^3^MLeC!kVc!$WY>32)sP0_y0gtfs@<30 z{(W)Oj8QS&@4BQMj1sOwF>ibGO^QJP`q{$5tMaw|4{mw!N zP~Ps?S^TnesuQ_4p@RbU`Qa1XQYhkPv%go_89sC z+sTsETE|CRTgO62KRJihlZWiYRE%0isko`OCcZ`%l95F7$@tO`1a7t$$tcr?jn<3> z9;4rhD*-2wo1EB?zFdVfZO;R*y#A5pMNE*QDk~Sy7t*?&K>?j&wla`@pVOOg^Q-9uL0XBO%P%g$5-M6|$MCR0Z*oFWJ}QyLRB^W%TRLZV`9x8?u+%F9A731MY5) z7h#SaOc)9p;6H*ZJ==^t_&2nYPeXBJS^niwwyNmKC9wOyj$IegKweJ%t?QiP!<#Wj zk}(LbOFi%4s-9I?6P=m4&R;#aF`k$!_9fU1QH^YqwMi#g6vB)fo}sH$KKhsLW9I>N zYwlCa9NV%|KX11q*<2m<@hwWd1cF}&U{mPzO^r#L&-F%~{ANZZ?Omf zcGhF@4clANDvIWm#A@dq3d%9gzz`H?2m!_3eIHZQ+N9ZwipD}d$u^*L2CsME;3HlZ`w9`*X!fcROxu)oFw#qyk+_>38mt+ zhbCuECcjGYd<=1}x3TOUn5`D@cJ)q?`P*B#j62+%Gwq!~NuI=fqxc`wrMN;s!s*eq zgY)e^leoBY!Rp>{+CzETZ+>jWd%@DqGC(*2-gv2RQR#TQyn>a zAI2C@%oyt?kGFbG^Zfe*m}X7M&c@Y*y6599Y7`i@ z`~Ok4pqH-J>qTJ{Wpcl+uBxWO=x2*tQ+HJ=0Ys~nYJhF?KmJ2U0ee#_g%Bs-j8lW` zs%M9@Z?-|$SvhHPaw%PJ3c?pBt=FfQywuF*!Tv8i3n&SJA?y z)dk7DVU_)|j_j#r@XB!OI{rQ81~bxl6UuTj$n-D{NPl)8uqi@3RZAW1t}9^x&kL!M z7A2rR2Hn&upkPLt!*H&k8|u{7t@?u-kI5njhqG+N72jv|V}uJEGxaTw(?zJPR-6z- z7OQ1x0p{P%T$=KKPj}QdMTu1&|JX}WnT~cW!V72(ERECJvwzMHpN{~CPkfbLL(jJD zI{xOCmN3tm1dgkISC7XwoM0}A>uiAn#*cZ>py;2m3ugFholFJ=w{0e}M-DL4$#kbiqm(_7l1g~xMciCS|@ zUufQ(JFMpdVI70-EV%k@cSx+C{KrRcn`$ud`b0`B0>$sr)^( zPXu>Jq1U$3Q-EF|p8EqZd)PUbX8-9$;Aepmb1iPD|D8DN^w%*ndn*w3Sf0Iz)@ZAxb}g( zg8i#JY9(p9J-{Z03Og)9XCAtJkK2Xr6%-xI(pv&?`7G-1Mb=e}viE}!Aynp6*2kXq zT@R;AieIE((?8WV9xXK@TvTVJsRz(INR68ro))9cx>@H~R$)7D>&DmV2f1|1hrYMX zq+1t*hJ{zsErVtwP#H?2@82?}B zpWbScH|eLgG)PfD_+c7sqlI$kV{=fSC{0QO@Q;+imdy(~psg8&%JNEoE6A|v8VyR# zsz!E0&>DW)y8Zd)HE%)iRdx{4Y!%-{r`9CFdWd1a6+gz^ajcfq`>B?novmGaTg|0zHx#ajD@TdsTe^^1l(?M(O2gW}k79PXl_Y;Bm8U2=ee09JKHE%{jUuSL{4p=F551 zaP8q8#~V+0EVwN+c}H95FR3!t7uO7Uq4chBo}o+(CwU{ON= zcvH)317mHOwKAW!uNr1{8E2xWz9qhR)aJ|srkd8krgf|rYb>2A33KbI()nl&7 z1*<3v=mz2`(oB9}44}-xVaC4-{ zYD5$D7zC8WCm+-~P7{{52hv^HG)j=>3Kf$-y5+of$$gt z_2?!_OUSfx5O+LAVlgsV%2^Xx^l$@z!i@i&Agx{dBS)Kd!iyat1s6Ys^d4%5D0vFq zlzJ0)e^!vDs`v8T!)-t`j9VAPskuzvHEc#5TP;ZLSFMYDQ!2bstfZ7%Tu#2O^gL-& z%h?(ClRRnQKTC*^AWl+J>@tHisIY-TJS+t+c}eS?)m2UX!7SN0Bf~fJZ{N4<6d}8S zwcd3BVhb>gpZV=YeS9SNEqA{`H&@=9`e`%D9tNc z%~$ZxC4(hUEMO>93|kv4_FHlAMez$^h=201=wiomn{FpkS?L`aRpixS!4QxFl9xH7 z=XG1^$(>8G8;fxOy1I@^kcqZ$r@&R_EYCH1M@&e?sS>@%6k+BM4_QFg{4%5HG60H~ zL+z!-`RR`SNV_YEi>npNYp|4DfHfN5Pl=AmY$dbHv7)X*Z2nJ% z)I;99|0H|$IepfCQhja2eZXYi5j#M->G9#EFm+wDDGAO~kIuH>wobL&S} z)>#REmXO#9zcXTOEqcbx5KpsL>XR~+8K>iEoO&;NR~|3U$2$l+S0?B=-|k3vNrY^-JBqy7>vc@`GW8%xFEel! zi^b*4Okj^{eBOe=M4FGL72Kpj`;p|xeZtcb-EhrsfkE}0`hrz7O4ukqtn(cF9C9Hp z1#0J!*Ldiyq743cdzyAwZehd-OJSSBGkI+_ic0YHks}B9bNpq6hbh(^ZB<`a;u(4< z_RC2aMRNOV%(ZP8cNx((AuPvMAA19^9a-AJ*jJ#AEUi)ZmE{8@Vd(9LdrYO?HHOPM zVQJNj&g#dHyO?AX+&golFZWcJs<%Zj8$$uqADHtBAHjfbsZU7;zPe%m{$0vk+W@8_ z(wj<@-hz37Qp`2!)IdpNOwQb%7J5D9S-pM)p`}=#oUa`J1cc;Xs&on>-|lsAI~to8&jZ3r7=UD0-DvfrcN-F!8v1O zZxB$58HJxtzh=9+es@JgdP>}btrKPg<9CYMGdFiMKM1o~POjzblKAd3tvkzR&y}Pgg)zzO$sjb*6nz0) zu2g`S_p`kcM`04n?*IH-gtW)zfZ-?@c&;iI_ZgDer~daVF6Wdo@jOIf?NjU+VfE0Y)d5DM(k8Ug0(of4|TN<8lXqNxsglGXDtxe8USBh3s4z@QtL) zt&1)!Yd67D<5`F_I2!ULWK3>Sgcy{D6K=2X^s9y6PKB7e{;K=%; zj7eaL%nfKM`t3^#t_RlUxnX@)%wj&qAroVQj}{8p4mMqSx*{7qqfXuLKQao3eki zRTR{_0Q30e9|Ha7Cf!$DcGZ_5>|=mMumAlCqU&fz@&qyK?9}CqXZFF@jFYF2<-XmS z{c6=hkh#w6;O`?-hs&bVpbr>_nH|!Ysq9Q|O1;A)E_9b%oQKuL@a6Zl!22bXb1pS5$XG_Zac^~wPczPWYOW@QNMQFgbyNSb%^7zd5Yn*ghlpXfsA z5tXs;OE}13`=HOocmg7QuW_lK;niAT_JSe4QtAMxU`<;Pq?|Wx!~yMLck7lk`-Fl5 zl`A;#_Rot}3>{!C`}>)s(;kYab`|2GMH-5FTx?+@d&4bAgUhSfFfDO;Fq?YkNuF5_0XKAi3eYFX`Tfo_IH;oLA@Ls69q@a-<$5vD(1oCGe2HcmD^R z1CUGU#l$(c3miTMhcu<)xAI1|(vEI!S8N*%5ARn~(*9qBzA)2Qi3!25X~t9&h8waEl0k4)E$~hf91SHFpP== zUe!OdGVmR~9z4+iYyrfBQlAq}x6O29?ZwfLQi90rAfiBuZ4m}IfM8gywv~5WzaGT? z`J1@7*}w+)+_)Zm!=hIQ`DEw*8@GD!!htahNlb$sN~ToT0qaLdK(M7{Ie5Ph5C*cn zzH4+=3(3IQLur|5kLWLSm5>1_rO}HRUDQf%&u}-vs=iz*#cRm6a@8!j?BKfE_g|}* ztD-O+(|_d-{m2oDzgNLQ(?@bR(AFzX$e-fTy%Ez8SvAH7I>0w7Bq|7db)Zs(+zgPo zR&;-|x~ZwBvUjWA;a7;J1(cey{&ciS!e(j=H}y}UAKwZf&LqaXB^xtdzgkYHOv)=p z-;g|ehDg4IhU)j26S~6>-t|@M0{JJ};M#0^L-bK`(qPN9<=ewS!+P!QU{fy4u0M3# zxG;5KMxu$X{aNOvd2A}c`lIaub1PNi2GPb~1Q>rgG|Mo2_Zih%$`oR{GD&McDb8{2 z#H3pXB0?{z5BJ|JGncH`Mb{|a9iE04(^jL+Qf@Qyz}`zHc>-P1@z4!F^G-L@E89CE zx+{v+AB4M4ruZ1SSaHl%!zbN_dT^YkesS@8@5%I=f<9fgA_(>w}?vi7t59XZsv@pq5TLe z-jwK_j{{t3AMD~`yBdKVE|SZcxZ`iwTisNR*?v!)~5iaXZ+K)ib)!z!BiDbunoM*SQkkS5u>v@*U@r;pL8_Z$B)YziD#0d4%b zr@&>ND>l3Q9R}ZA+-9>`+Q7GYn_N9h-#Nhi^Hg-vJCrr_D!7rGC3Ck2U_yWVAy2Fs zCAQcrbF+PAM5FhG7RjSGr)+}~cg&@)Jt)nx z$b$*LM=eH;VOODx5*I&X)IFO;+iTksP%5Wm-HbBOR9J00tTd@tHz0G7lWRveN7@>Z z5|tHKVM@QXZGOx9vjY~c)p~_;HI=Q3tpJi)y#JK<8SB>vy4$?5oi!-^nXSx+TQ-6r zMvrdt9LuAz|HTu>V2ze^1O*i+BZR?g-6N{A*OeFw^tu0*kQ|L6|KIJ;$ zzB(AqN(lQE)bgEr>1W0Y=o`JJSrdUOT-h;kKM=zOI{Q|&AqS6cGv)1Hd;lRHF0Xfr zUmff+3@>?-zuSg52t(hi)xd_Xs-l>tqK{0;jao~rg4nGaGI+Nbciaj(bSHbg$g0Y; zum!h8MAX`J0ZwkwAEUiS!XqPLB^w)khS>{DDNv7>HJ_Z*ds z4Yt#Y5%--B?(k_#&|~0Ke9#~K=Yok$T`Aif2p;j4;BMg75iwU*j7Q6@Nh$hmn9jjw z9y*A{Fr!(N=Jz32&^F>ywDxgkE^j$(`~gOjL@QSsR0S8)+V?d;zEVJ znvg>vR>DdaohDhuww4n<)$tQh8q3Y^A_e1`fDm?@%8k;bQN7c?FTW%t`CaLj6_UtM z&3f5&lEiM}&->+s78AMh_9d5QS*|}x^Mr@z=-+^l6fNwL4_&cR76KsJ7!U!mbiN2v z`94OH#Dy*lfAI8cDZ2kO7JExpIO8ecpnY$>lP=QK*~aDN$Z@*4HFdvZAJ0bo)y!tsa^ zu`h=E{4EcBr`%;wO`9`-|BiZf%ls_KVr2N)jreY>5ycW`9?e!HYpvw>-?gMg8>$Ah z4@*C-N<){XfUYu|$}AE2mg=WZO3Arv)?3?%J~Vc9^skv@eG6Au3JL_yrHo7PZf$Au zCJTFj7}NIz^FZy#n;jG6zV6Yqc>g#yC`VApUPHga_)>g9aND5}=mvZE@ZtfWlt>Gp z_EEMgu{zs(yKpSj>FoiPfy;7{Qd9QHbPLRmO{;hJ$r}3QM>^MByH0@z{rmBXboR8aFBMM@hO>H z4xzEYSc;4s{U~&@lE$|UuhyHKcKWaFuUKr`57cEd<@fk|CUre z+_sq{&@8<<@3?{40K5~<7qCx*O8~D~Tdky>6p)hnp6!>ylbDDP{RPlGUO-a$ssmDp zb05Y@NmP<^(^E4wFp#k~^sT8MG|$&647p-{qQ7Tz;3(%z)-o=+YRJ z8rAvzOJLVJ$4@E-ZvMH$r;lP(_sM*=EayDJO*nL){0BYQM0G1vB~q7x#|e9ogFC$E zb5?gEMk$IQj*y-V*)-IoJ`B?4y+{*ax@<%(?O#Dx6_}sE%-NAr{V2r0At4vpoE{sEThslsN&1JC9jLxGP85Y=txRqGwl zswm~ma-2oznaQZhAwcLC6^xLbi6m*B*LH&8tUyW@bk`J$6gNb^$=N(<({jXHd0FjLobF^1qh1XbGp|c<#Mx#cOc8w2>z-RiY6ZUSF9+>C z)|}|lj)#?Y`E6n~kk7xWJ|`2+<)a?@d8iJ(wSQ!lsQM_rS^L(*Ue|}g{?B~;K0mxk z8INeLy0G!+jH)-2-2UKvmpsNW{rcO2X6hLWZP*cy9spOK^ayuR@lk|-HNh(9zjua{ z`NkVSk>qFot_a+NqZ*r*xiee|Z>XME?A2TVeZJ{;hy1{Z?6XRt8|+wspL}^TyGQ%} zS3|<+M=CCzTnArO!%Umio2^JQ)=WQ>mPLaWEJWPG!W4<$8S$cSm!yrsHZsLJ$W%Rs zpf{B@sJYzrZWMwtos~EF*dn+VTWawi}@a5X5Db2$1S z5f&gkV!)ovj8Qpsr@aFj+~`cMFmQfgzExgOPF$18XPg3_rpXe1a}e1R^r@=#cE;cA z6F{m^Fo(>beWz-af-_(4I5!BTQ+^IE2(g=$!RW}zK>0RPRtE_tPW}lp!m{C z6s^N;ADEAv_+qs)iK6`Wl$+!~cB*sUs0=G5EOeMXBBeWc&w|0?_Lnplxmb?l={UOZ-v@Co@tw%q9!#fy!S#f-al!k1+)2z;VUxSdbe`~ z#b{SK<;jk1t0PEACtAvD0Fq)Vq;)wk*Dd8!lpLJAE_VH68A3el>1K65QK$BS${( zyX1l{plqjn5g}%kD@DjH@itRn6*W%59@;okkPC*>+UBA@bFL;(q@KttzizxI4rJq@ zp8%|Ko(A)6D2%YI5o<)Futf0?W#Lhum8n?l1`w{a)3^A|LczIYY$=G?wj;g3^+d&? zG~*2u4uN!?px7ul3|ugJf@WFU0jR%rE)C7(ZzHH*?cL$CqOcUwk^3g~7u-Av?JSo4 z`T^wvpgRG?R*jFkqOXF?@7WlD5F5r@@kg6jWB$9^L;*-TMTmTlP^ z6%JB*>T{=H-;O7;IQRr=o$3_lstM@JnkUg$Wpy3lV^6nJJC?`#?|=z_JsNMgks%zTB`$YPbb2;5OcF^c1W#5R6z!1lgs|4!1(Pt!R-O{1hgUcUn0mIoO6CX#S@_Gz%_lu zo^QmUSj%;nwVi;W0vZWBeaRj^c~#68%bC8N&+{8>3cr-BVk1XWF^&gMOS^;p{i3Bo zf-wcwsfJU^=&{CBbf3R@>{BMav^Ok4EXP!Irsq6p!D6$Nl>Pp zwzIo;b(3y0LWP1*Xh+eqz8gFQPv}b?EKTsFI43P((({~Y7J{UCdbGNpcICwpvm~~e zx4fq2^vsJbiQ6HHvqSnuWnH$n%VV*dvqa`WQqw{6r6{?S7vAP%#hIuP7YOgTt$^SX zDnAafpUaT)@-hQn?vSRUb|E5Y3E?^E`X44BN|TJxH@HqnFjz7#M&drkurd9;%nj_0 z+E{9r_}Lk2w-~(12u158@QBc+dasC{S{9uh z@`G6k{h3LaiW>sx6f4ihH3VSNF$*_G9ylu}YN=E(Bbiz0p=XgIj99#_M|x)u%S>TQ zv_%#^l{2t;;3?k8JFb`{2R$2|@A~8$%&D!c;`~E=3mruEbGkayhBj<$R=tw->!1B? zSpJO{CirQzlkt9x{Rgz@_9S7_@?)mP$-)~(3dL@@%0r!b`eqaY7k~XiSY3EA=fPg* zYGhNU(*Bu0!Y`Y}bzd%A)F)XTwI^A6`|7qkHy@Q|1%c9JkiD(K3`13fE5#?e)=X1{ z`b=B(9DP!?Y2BB19Z+~praoJezx|Us71LO$$$uT>~!r{x?i&aLKVgnx?tA^ETL?PZ^j7!O|I#vtgIl=C7AePkoO#PG zZ1go3!lY=H4^!X*rs~7}^M-iQUbGNKmszRHGJ**jiW2Hn3V$|;a zt=37|WET^1P0_62ue{~ka@XeQOFo`-8%R0uB_UGF<) z^ltGT+)JY?MZ<@t0K$GO{RGT3`XSJ3GJKcWeD*aK;JqmS1@uL`-|$%VX_&FUi=5{R zL;1%aHT<(Z9TYxk&j%Sg1c!v3W6*8R>1Dpqc0c#mz%m=8OMSx8z)Zh7R93F2H`h`K zH}^~MGrsbvw9lJV@1zj2^2mUwEX`JFaM8?G9ZLILgRjx0y>Yt8o)57^7r^qXdXzEt zrC;nmJq7Fyx%a%Q@|`^+lBk#`oTt@{AgW%8;h@Ym?0?+FE7gwRPDZ&bhra7{*q+R1 zxw&pwAF!+87?x=>#^6=fK%D;dAATI}9kRVz9yt5ONB#Yd*Oe_K?)wd)nw~Sb+!`42 z?dQJA6Ou)`qU!*eF(V3$ ze)i7qBZ64_rhI>2zEdUU5FlXY0xaT^1RIAuQRUt{RdyeqqPKB}ehS9uler+`P$}I}v@?0Wf!92BE+bRo5Qt}2ceUTve?BArUwEZ;%tdcu z*nIDEKY`=KrP{&V@DQud@|Us=P$2z9?6CV!&9TIg3*3DzKSV}w$KD6seGQfiO(h29 z|H)nNW0Ps8&K9d5V1j3$mxZ5^0OQ7*yGHT~dAy_t8T72R@LXZv0cN#PZ?intb>sA% z9%Iq34o^AgcAvcVRv_tKmgJz);Y9thOPr!iUb^HKZ6~Zl2Ppa?%{W$ES(YDU^Y@Q4 z4Y^rt^0e@t&7W&A%*z74kFD4z8t8iFQ;RgPeP266Ny2-5VUOOc0h`sqA)S)Et^X)E zAl{LmMVA69={LsJM7AO6s{ksU;+x1e1&s-x9NULT)sOK(hn6uy1i7B?0o8a_2+FL-*bTPVd|qMIGH$oVB^ShjUZSHVc;M9*ISqOOGD+S zIp?%fS>yqzsJ>My!6Z#`)1^DcUV5P$WpIJs1_gL(zK0hh0UnaNX_!{>IuEsny%MB2 z9ddS!OXhfr^ZPIQ)g|@)60T_#PWb6+vTu(t{@UvUy$P>|+`*R!kHf_2pMGF29T#=& zm-K2pFND1{X%}eP$vBvdA^-}n1|~mDchu%9}9^%+CQ>PoFv2 zFB~xi*fKu9f$aV@&~MHdo|vnPCio@IT-DA^kHldkp@-9~Lmmz8J!ACAKx6fqA!qTc z(1_ovOP=iLIJ58e*leafK_-VwTpV5!uHzQPPksCj%}!+M2rDqijFpBba*f+gU?gM$ z!>tx)z%0GeTZ41WS?>8UQ7`5D=~c{2r8b^pi5b0XMv;zMlXp^vPv8v|7Me0T(YIgi zd{Rad9oA*#*9(l!f1>OhXh1SNoT6h|Q*$T+AZfn{gw8lrQUcI`NyZ8ehmUTpuTu#D zLBWTFMZogb>mqbCc}IO&dovCzGsym&GIzgJhx>)SR`&4P9JQtOsEj#1j7~@)KGZ3h zk7eF2PqB4>nW(gg#Owuq`jw&o4Y_}nGS5hOzs|IW{uRC^_7R4z$R&jp&&Fq$z)v6R zJutBut6zkZKGyGb9RLw{_IB0p&bkPJ+?S+p8w03vOdj5C6iOgRN33!QB(R5U2(HcW z`O}ZDaW%{sd5`kA{KTW@K+WqKXyE5OI0Q3D-lt33b zF`^jTGs!J8+KkPEICvWiW$nUM(H7m2m#*J$dDOk{Ns?b%<*8U>z*UZv@$dJ)gF&a@ ztr5xL?eMcdQ2ugqsvy>w61@5oIojijSz^yvwPzw?)CywMXl;s^30fugN_FpvE;U-MSz5d3rnPEUSIp9% zs@BNs{p+0PI)6UbInRCF=X-xX;2%-OcUrIlK9@05udABrW%w1P z)mxGO^G=yGW#j87aAs_lFS!Ch4bSuVSSvt^RIsLfv>=(0c9R=MN^)U;?zCN?+Q!VcMI+kb^kMqxD@c z)jL38LkEy9gHl)baY$ANzTxrS{{ldBO-&dJbqDq+?{MWbx^l`rhVx0&)ojfZ`$k!@-M4t#oRjB_WY4wqeWav|m%I6$vlGlRksgnyC-^zAR;K zijkB&hl<_}609*@;ziNyT()-ScLckgu*Lsh&Xz`kKQU0m#+D^h(Fst1!(6~`b%#Z= z%i{4QhH?7=tx7O&tyD5itbK?(F#UBpRQs)iytJGapY?phl3BX315L^Ag%s{AV_sSS?VUWgo zF7NVvM;keUq@T~sa9*1z3ES8f>7&FKDa#UObgN*)JE=wBZcW&K51l*d~ZiasjpK*E8S=ho}*&|NWzg)Yq=_(7S;x3GZFqfyL0(L z;++>Eq#oKByAs5gbm+tJrs7D?VDbLC6Vx2hKF<7)&MqlHrNpsc2%|&&R=FS^5#?<| znSR02;0O65z?gyqf(<+Ih-0{x7Y9D}(oJ&#_irox7G2R$_3M&lIKJVDY*a_aKaF9o zlsj1nJk_n+p;1<>Bpi&Hsfx0}a0shqj8%#%e2e`GsFWz1+rqHZwgpo!-!6gH+nRfs zq3&Y`u%s*w$f8{DTV43)y)3_Xk*LPLE{D?bdh2Zo54ie4jLYIePyuzq3=7&V#7Dlq z(Cp%2x<=1i{ z1@h4r7D%Pq2XgE&&_m7f1tfMJ_xaAQFS3nvs}UUZ`8!TgjMkmys(-cbF{k_$Od0aE;9m6+>$SiKj&l#^m-=X{H zA=Wc09FqN4Fi;GCPE7X|Lw!%z#?5U%|4ab@3iO#;$OhVbdr$b?u_}6 zOT)=XN$YnbQ_GXt6}Zbxv`$UrE##xxq0QQU73T(ELZxC=l3y=@&{A=@w8iLm&*m3> zMKvaE3t_F*c>0pQ$65-TSX64P5l=IVrzdB&=j}#0|7 z=c8q73dqFLGII|*o}%>Hf&^S5AyYvQy#8(f8I=>4j&xyvrhn$6W}Mr!+QjBzqCn`P zF}{i_@%!_9j0PpczBGHG9%jVG%kc$C8r!YH1Ut}a38dLOuz&qg%giDjB+M2k3kc-2 z-T`Dl_tgZUzUbz94)TRBuah?#c6))zOCY=`befFV17rX$(su^j7N4$f!0$kn!Pa$1tK z4T9`Ic6nS!T+>g_uhe*%jP=H+USz1tS*IXbHf(3Zb^`(VgXEs86t*Q&e}eB*BH{mi ztp!`*^q@J;`IapN*pH>&qoham(W$o9ihL`a&!&aVX^pFJ|MdzDZ#3TwYUV_t*JiO- zq3z!l&Pd6WPu7;vzsDT!wZ)WEm0j_e?VJ^;SCs5;+~HD!)XSE!^ASa>V#e4+LQV)9 zv$ZJn!6>pbpFi(1Z$5w*h1#F#D_A5yY%1z+e9Pw@$_Kf?jsiYzq9%D@7ND z!i_lRj^(r#jsxH1(-0b(rKn9ue>2DU;rI(q!Ze|9f#^=}w{yVDc3R)bIQsXqIgS{R z!vi33f3l+Ct(#5uP(G)c9$5JRM~8hPa$xfH>?Tt*i7VvgtQ|j(U;4oTaa5tqlGf2o zV)`=^+kML1cs}J)KIFPd8=_OJ^yN%R-4I$Z|I_rW2;Zkm^}_Tse=WaX7tZn#!4y8iF-|KB5QrlCDgWdesB}t`xL@ZHs^@d3EAc^-%iPPR3Vo z40QM+{Huh^*V5%F1kx|a#QAHo`Z8>YT6IwihH0=+AAN|K43z#NhuNdlAKW}r+|BJ0 z@F6ix7Zt|Oa?G0hbEI-sT0ISI5y%cRK{`pSP`0huR-o{%7fut*)_UGqVgqJU`+-tr z8ti`IZov7J6Vx|mp8ZMu#gCH9#yb1aq8y1%u48(-^qVsimlq8jSY0KBhCZVX0_Jz! zvi&{_nE(CkIiQQIoKkiF;rYhoq)h1{%dW*HVXg1Urfn|fU}#e0;tR^LGQ|SCfs`n|R zd^qI~jkLd3QUWT8H+@9{f9@?7EyN5T98F2)H)9PD%Q=~IXN0pH#nCrzyB(Dz4Pvq{ zsRD{$-2QY8jBk{2Y&aqsdIt2=AmX5x-IruX4nf<9vQsOwZ}Q&d?4DI+FF1g>d=8dLktMNZWQkE?9ja z7S+xcHPbzcG<)^m(jr@UQ5L}AUvC)I*@lfH*)}=p#)^6Ek1;!g$Jyx|OO8ufK`Eoj z6>L)ct?<|(J^`&5rHt!+8ZNW2h{}aTF|9ng-Q=p8*+5ml<7Vlshl@6#fy1@1uPSc_ zvaNoXRY02UF0VY9tr$1|0GUe|z$)9y$o>X|FwsWVPyQ)A;p`y@nV7~uYf55>V{tNj zs09lsx~}>JBNa@%EWn(_pwddm9nUz+nHRj| z7q49<$aOdWsN04t%np}?k>P{9btAxlf9DL3M6l5|i1gOc|Fz3INodU8%ot{0%xp@P z{%OLId4(-11>O7m_Sv`(YM@0*397)hwxbstGbWfjckOycV{f@(G~Cb{Vp5u({sWQx z5m)7nC(M<^S`qY`m`5=f2;5G&(Zfg6zRhba(B?R(pL>$8Wzf;FzR-|tADa)k4XJ6c zGY9+fKWe{9T2&kT>3MjCkg68-v_Qid4gDSa5+1;{$Dfa$XtHwj+@pN+_}K{BbL0aob6 zYy-FZXzG(&fAa$GW2xa-cD|VUWGyVG@6&qqKkXo7>&~s+*7+xvBpvVDHNY3S{nx(S zoheU+n%XOXtOboJFb#6G{CMlGYPZ)Q5Er;t#MJv<8uU^twoC=7Mh=Kr#l1MMMa4ZM zc)%VNAhbxosH;^hC67+l7wQ`^Xl>5s2YDD6^b2H8SQbjX^h%!nKGi%fU|ICo#PE`^ zxBZL;Y!Xb$^3t~9ye|d$W}en6=!T5L%GNNCp)a4&Lc!=UPO6S$`5gaQCiEL;7TdY| zPS&;1OH(&ovzaAEEqk3^?5&{GVtZS0_%vEs%|i@m6hy0CRR=Wi`k6IO=09S#}*v5Z5^M!f-ztrnm<3otL$h zK7g}#KTe(PvaZvB|9d0&5m6>x+OmEC%DmBqb7`WQIsE3NyNkCf-&F}NI=6bv7+|4F z2UxJa`l1gpevS+&-he77>09dyF^n)+Tv@3@)TbH`stuEzc+ z-xk56#Y^SDzr&&qr3@!4Ekw3wPXw@VHB&qYL;X*3vX(Z6K9KIO)^FkryIlD5Y%>s* ziR8S}8%;r(#rf2q)?#x{KBFg&-5lFk$DrV}Q6PwHm;0Fvy2@Yx?*AAz|;-*~s0 z1mel;L04U|l$B^+ig<~%Ww?U>K9h;Z9!C+jrar$n7Ux=jJlqSh)HRQdb)Yh++(Y0X zF_Q7H+!%fuj~D){lY8uT(TS2{$Ys`vKyNS!*!O2F5E^1rtxDMY=U5?X=mgQEXW`GQ z{q?(BF6?{qedo9jQsgo=VNtNW-zI4Jnz1}vc=GbXZg;sGrdmBZYLHx~_rlB;>5Oyq zQ{}Nc7@d(>< zy6ySHyhWuAFiNDQ?~#Tcsq9r7O7L=az|AC^7ju;hCoA2!A|RMurVI1ig^76|@#B@3 zzW)QVnWCcAE|JGth+`lvTm6sm{ACBxgx)Tn@xCgZDPkQ<%r*C7kKM{r-r+Q%)x|*S zAZY4CvLKX@q0g0vn-&#Cdd@aR?_*;!Z%d&uy{17%zfUf6_j~6QEba0DaJ^Kq7Fl?!q6z#(^3WzCV&?zK2+)UvB*m}I+E7Xz%QmVyXqUU{&=c9&qMzf z{v(*bvPAftuV}NBRwldG;_BIj;1f#R4QqkeW+SO!JDbDZB$L-kLA<9!+Wy&6U3i)~ z_x1PP>U_Mqg9~3f?#JdAzkeg>LcWdQ0?C zqf@9NgN(U5(bSk^&t=IrV@!Ovq&QmQB_>EX$r#n*JJh*Ot2|;xLQ+E|7B+R@#6IRmIqvU9xwMWQ>{?p(No14ZVr8|FZA0O zS9NDU9w9Dz8{+T58Iw`H|8ZC}Z&qQ~6e<64Uj7vNgw$$5vRS5an5;hO-Te+maEYkM zFP}$84}FM{)RNd-TMEn$+y^ixvWWJwTFuQiR-#-3DRvMV$f%ufe%RuGGClpE&#Gdv zx9&q(jQIq{xX)n!y38!S03AJaDT6VrMIwq9M}3l<7%4)2vRI=1kjiUHo8>aTvh?#> zp6v%A;912xR`7O38v|g~fQzHpsm=)|hj2lAlui2sp2UD>G9`skCRZ`b$h5oSwqJc( z`Ht+e-4=yU61QBehhvwpMZ1t3W9+~Enc>jxc`DE$3ix8;!qE_uKq5nU|X!3K$uF6gu?tPy20O-vnZw2+R7&RL5?S*5wW{kB3<2V;=Gjn zd*St>#@j+E!dFNJ;uAV9uM%mpj~nu3@B9@M9;PU#BNA*o^roQh^@_;pIq8@4VJ#7F zee?rYWw}qUEWJ!tt2CkJbYA&8Xb@Qv5x(#k;wHw_5af5`^Fs{#K~MZcqBfIdzc*fb zfZ6JBPUjpzyA?Fh+A-g2+i4&<`R=)0UeVZTClNq7&b|KQ1kC7f*8D(L7c5qY%>*$c zG1O_qv}#C)`dkr3b*N86*=;>&a@|k5`$q9&#L1%J+j6BSofb`r8cMm8R^A3zR+j5^ zk*@O~a$4NrXtIVWL<0jw^Q9Ck-JF9%F`?-DIidmn6to2QPC6|RKbVB}Eu0LY`eV(* zMO?YhFLd?~KU%G2!I<82&&aIXy$8iL>^KSPzA&k1iKUH@k}+h_u^=8<8uWp+IWv<- zK8ouhhz_Ful>SR}6BxD1TtY)MVDro|NIADtvHNk6BDDQ*j3PtK z5c|TM!{T`O|6&X411K{5Sa>Vqd0BDR-y9y?jh;}ku==HjBMh=iS>2UGK*nyqAkBt?eJ!#>sS_2`F02F zadS8Yt&@&%0VQ1md9Ox758xZv=bbqa{wCRycWvTsUWLJKbLar4bJPq2)>C zdhHhmAFn)hT%kqDTG;hJ=@{HYv!kf24cXJOu)I{duDIQ2Kp)#BD5I?uOg5tj~RYxf-eHN`S2G?G*Vv z0+s&u*<;U>lC6h&J9Cui?U`AqN(k!`t@gLk7w5%bjwNVD8O|_Ji=2!t+L@`3*Ko$N zLA~JP8k&l!7H*%VsLxeR2ADGj6&)xK1XFFPrzV;Mn~)a|U0;)Q4a`%bGK_S)B|8!(|vf6RfC!e?386&)cOgEUegf<1l)w-H(I(@i!D z^SMc6HG{9V0B6BWzNxun8%WW_0J%gR2~C)3T%7tjl(l=a*p`EaT$+@REXVq($Q%xV zz5S=vU@0QPVDy0gO{!Il5_4xN0|shkAZ0WV#B<152y~pAY`XIrRybL8DN^k2WKo*_ zXl68KAmw!`!*7dCJ^|_yN}otGi&M63j!smz)_LVz>^$a&nw}^`KoN!ZYo?lSWT36! zY1KsxbLxRj=jrtNEa@(R%1MsM;Xx2cl|a(rxa7^9-JDy5l&|)fX_emaeUyB^bR|3J z@n?IB9T}mE+qSlyeAf`W(Xlui0$!PWNF(M9SnfZ#V#R0fD@prwYP=>H zTYyb+zo)P0y`~9X+e2cD)?cJYsb%7c!} z$8wbqvhz!8iZ}dUfNgv7`aTO)ao3AV?sbHFY&gbn21f8v_S&toy4Har4eq=2jf2oQ zLZq*FWdHiudW)Tg4tSIFW#IQ=^%TAi1=e<^YvfS&9CW^O65&sK`2>5+XCUty^``+D z(WI}7iV^x*BC?5Z=t&GqlqGRXCf$ay=;u!CpOS^XrS9USe{0PmE!UV_^-t1kUy~;$ z9a1p%KU)lfhc3qBM7Qp*Zk^6c4g*gLlI#f+WgFQx4vZaaJn4KnOrI`oQ|7x-;{Nc~kcGs#R7@2AMX93c_Qe)+Z%BItsBb}b z6Opm+DgT?rW~PQJ+}CiIHyeoX(sxCwNKa%xdt~x*U2361d-hXHEv9*0ir9MFldeCk z$CBSj%f364vBQ!UYGa#G=*LAWo!BD-u_Y2PT9JUn0-xqEyvJu$s{&{)c#k}&5_(+r zd;T%f(^#=}iob6nnoR%e8&4=g3JT0fG1a+OILFQ@BjQ*Inp?7Pu<%XMYctPt0GbeAY9E^bHoN*N6th7J_f?=U5w z9Vw&VI(nC-Z4TRY`x#v2$yPzZ%#!?@3-KrkYmEOKw8B}(qUj+RKug)iciQ_Mq6o09 z{WnM_*gxy$AMED2fdCPx)hb*E1BsqK@*&_(_!-zQ05JJXtS{m#3g_6NEGBg@^+im7 zZRboz^aCj?7;Bm$WMVK(2&`f`cshDAYeluZ-^UlX6~~w2RM=io6^5r^=(X212$96E zPh(Ftifo(?aJ2vM)Sjm6B}P_SYn4w={6L&@!s&Sm^ikA+zQA3|>mhFhT<&uVxJ|$p z>L}ZB-(rnEfFc^WBXVCp$OPYdl|(ZU-!9vjkTLUcLgXlTsP9=x8!4=d{U18&2Wv|h zuLl~E93=wxD$_G{Dklf5qb4Zl{Y;a)#@IL!ezKiNhEm0tVvJT!M^oN`ue1D@w!0Ql z@SO*Ioqf%ir9zYQdp%{Jtkz8sCjeKdO~t^-t1@4@*y2g6uje0Rsq@t~n9THwzgQHa zG9Ezt_(QTiLg>!gp@BL#IfWF^$JNz`ISa*^v^NkozL=y?cv}IB(zBj+H)rgiJ1?$( z{2s({9+aQ7JJE^(W&Dh{p~12ok(uKS|1#u#?i|5{{dr1~Ej6r07i520mAu?>Fmv^~ z%FpbpOTz2837vbVXgl+bFV94|MXFj?IP{1V^VV`a_C0Xn*kB>XK?prl9fr8}FBvr}BK*9}&=DL=@KaT}zO18&lwWeBcvxL=QJ< z_$&>CI;lOtM~9{FD}|L_q&?zJJSwTEkczxPA)sra6V^GLKU~b{Ex}n@R9=4eZRiwj z;Bt#Eck6w5WS~TV`56ESweobnRnSP!I&7n1i=pwV+SS2)q5&`LqwLRSkcJVSZ`q`x z;ug^td>gtS^k(NCW4i|De$2xl4aL%3Za-^4sD@|VJ!v*i&jXoWFhZv8WH*>4J^l_< zkwTd?a4tQ1nS1-(kTXDY!q4yW;!ziIe|l~5-j(Kr(Zg^q0w-qiWR~Y<-(SfwDw(&( zLem$rrD;9U$k9wzf}5qC{;T$ct+0NTxbrrJ4B;;3Gd-;Xe_R=@k}5k0OWJLgYHty5%)XK^FjJEfo7^Voa1 zPCY)yijovlaY?FZP9bVkv_Ps|FQ@hIh+F-Lh+OlAIu(i>26dO?Ne{7XLCYPYWWAws zR$x(Bb%|Z}gabYMGrHD$azctL{s40zCeO0Ujr0>$!Y`X3 zSa`CLFI4?Yl7OG+B-_x@Da!+$Zv*nAq;2hjJZsgI7uz`gtvT5;#h|cd-(9&e z?jbSu@Y6nT>RN@1YO;0Y|Et<~BmcM_+kfZc(pxdKu#)-PU#!(E>7uu+_Z)l8>xd|- zxpe96a@eC3giRDA;yrjqzt;&z#D!}bH&NTSBr#%rU^1P|CKwR_%(DT3Bm!m_q}%b7 zU`%N2azlD1oZx2OUfLyWnkl{EbVxo}+&x97k{YeAEbX2?@lUQ~X_vR%Ld+&crAs|b z;x9W5v2D=VHZEQ`cwM^SU3pK&J9%4Oy=`)3>}PphX}66=1X2aOC9HbLbEjhY+_teg zI!HB1VK09;dDR>%SPh_uvW&6 zIZn7@|8%Rnc9?6vUmE&@bf+P#_u5usE)c+#3Jl$=t|@&$o+9GC{a) zHc(etc>cUrxCOQuw*@&fHvkp)md9u`8C9(41EPjz7Aa z#D8_suKg~A(sjP5?D(iag*+cEvJq1qo6TM33W2#RFxUy>?bl;NqCKgH{86tn+ff}u z?-!B|fPOud1vcaQM7XbXvdj*9I3H%SG*m_3;rn3l|~&s5Rmc9UETw#W|=&`0fnib z(SZ2$y#KUW_t5ES0%Bm1y>HR11TY2a&FJM*Fz@r`#jYF_69t&Gm7O+|R!p3|o{*i;%^!>LtN7Lmllq~<;}^xfqP#{OXLJ4@N}-6Bl>RXIV1>&cFS&8>aLyJ= zEjt8CeX-QUsMIXQdp5(EIl>lE6qkM{%{mHun5n6m0lFul?1h>2gf=K zQ~#xs?Qs;jZtM#mv<$pwo9_=_{*=iU2X|qIlYuaKe=p;(&WU%6$qLPfxW~#K5s-jB z)KT<;v|qzkTSTso_^CxV?F`2H&_g7i08^dKZF0tHGVP#9 zrp%%^U@oo3<&nEAGG{+Yjv=k}D%f5Y6CAC4u7qQ$x1YEjZR01#@mbFGwtup3#jfGb znA*y{UEd_7Rm5kmOu#Q)L^>_?%cWDi#Mv1|MDPltCDp zMjg~``>-q7dWvn%GmKPc3*40gbqghX!HlS+WW}>ur`2ZJiy|A{LQKKPdLz>BhuR_( z-^JxO&mxazkq6S1qlJAUpXc4q9;VkmwdeCkSi5@%RObz*Xq2C9uGc$gD^#&>nOcpz zKO|mO`KNkX(uUBb^K-Fsd=6syhFp6&w*niqdDE?1yRG86)6n=k_B}U8@Fok!E%2)m zzyxUJN~TKl(<%!x5KP8)=sglSn*3GA2>964-p(@m!o~Y)3W264!;mski=34s9oV(J%1Q61Bx@mg zZ!zOLb6b5<}g3U>tDZOKauCQZ+o+XlPwYE+JfJ#Mme#yQK zSbIBvK&?bQ*?>`38QF028kY}CFh2U*A2A2g@6vAKzqUFGevbiX{aTuqPuwns_36O5 zNb^rbOafli2Yd~sa6dOEQCicOr6LHXo=xNyy;YXvYpG3!IiI`y69O`87T1GK?``BW z5s5V>+jb$+2-<%peF-&Pt^zg0iwh(}z#`3oDBVB*g89 zc8&zu&&)c#NaQnG3|G3HlCLjt)~U={0u+I|BB@nS{fnU&3L{g69?$P<3nP@ zLi7rG6Sv^uK}pOb01(6ojf`W2#mC|+UXnxjvS1)HjN~m9#>&WxxJASr+gP(!4o1Y=n%1;+bg1vsC`PztVY;8gb zIC$}!pPr75Zk}WQ&&Ku`m_ERW3k|clVg%9!k|U;Lc}+p6*tkSSPz(^3%4SN3F@%DT zIgB2d42TFj#ily70I=k=vPj@aKS3!ej}_B7&EstZ$Hi$lTf4f_fslCCkx;-q_z2T- zxM`$qiW-tC;Nt1zBzQPk-=Lj=+BP&YHX&Q%sT6rDc~ioX*w%@M4atoCaAsIoB0+!^ zmbc+oi;aJGh3SX@)e#b`oR@{y2zA#t(cN0OxuZbh&XgP00ZU9l_?)OX~H zOPndA8lx!nqzRQojt@hgPD-k=c;Yu68=(;&6hU$HDSOI%p!nQZ@>x)pg5PA4i766E zn3$e1*5D^9g&Zdmkq~n*Zz3+%l3&UrEQ}Bt6>qAoeI!1W9i}xBi;C-x&<-)xmL`Ow zAfo<4brw!_^x+bFx(>h7!p{$}xkYWn$yTt<4CYvAV}2p37tt zag2mG4SD%Mg_sW05Pyr6>8pO)k{iY`sW81%)9=RST~L7PlMFSqJrjRLCFp_|Bpyh^!o=jo~3|^c-ChJ4xGxa^19wz;aiA&M7gZY$3 zucX8>k|v_*P&DhlX6%!V<+EQH@i8i|DGq`jWSWbW`^3dbDx5-r&;_Ya((m?0@;`wF z>m3oBn4YkP5gcsLheHJ5!6k>$ND=1IXSmZ#H7^jQa?WH*sdLln>-TBokUA>FiS1Ux z_^9vfNwJU9>~~3nZfEBboxgA2OAD0{dTfH3zFr?I~*ok7$9BnPXaa`Bi$`@QWYG;Ly7eTXo`K<(+f5ejVT)MBbgSLMQlz7 zVPqRO_??4NDG2@$aFPY_&#eYsHnu6oU5{>YFwFKz$|!a z5mru+i28s+V!Va1te%W@Vp8DotOA^#p_;zFQiwp8?^Z8;B}~sdDCDjP^U=__yGx=d z$@Ni#tp=P_nqpn}c5=B+_bJWB^M-5U(AA*3BbT$@Mcx)4Z`3fSCMMEuv*VJnM+>x9 zKTN=4ypJTrYCY4{AsT)h9o5m7(o!^SSBqM)u95&`o}C5~wWL(f$371ue!r;}3WWHa zt#ii^XkV~ekEVZyuG3`pxh`kVIs(g_=92cLj^eIRS zkvHMkB=w6*SE7>9G*=IP`AZF9^_p0I8FzuZlB)H{+2Z2_-yf4kk}w6OW1&O{z8;)c z*Yxhlu^>Hbg62D)D_N%`5((8$$1ytW3vt&K@S1p3Dau?@8N-e_!he_$KXUz=$#oh3 zdj(*XhNh216ThJN_`6-&U%IlEQ;8`6aq4v+u#w%%ye=I64KVfjYY+q|M-0V6OveP032;0_B<@gXcTdf5=yOdM4^G&`iWA7e8_!F;98H}W;`!Tvo+uJ4%J@>DVK54st#;eKC&-3a;boA8WbS>ZBkS zQ3b${xc80ZrL8Aj&S8`twN!5=8iohO95LFk=Myr;9>LspW--rMOK4Oq1=Zt0zF2#MG_4H()Hvr&hEFN-NwqFRwFNlMp zFk(D5!HN$lB$|3BYSgQ#k2?`hP+VE#fqA@h_#GL^IH0O+)W1j`dpi1D!sVTll%x0L z5cPXA*1U5rJJO`F{;iL5dy606-+q=Gfg5!f2n??;<9w;G`7aMPZ~xr>1m?{w%%~JQ zsh3!r{EOH~D3A}j!9W{-+p*be1@q2T?no={D}?Ud*3=Ds`gUdF`{wa-*)Xr_9vgYJ z^-DZ;%6)~shrDwOJJJZvT^z+=XKx|6i)TMm&1x0rU#?1319nSvny^O{(DHZ%Q6%Ps2j>L{Cp6hg4(2LZ_`ex!rHH9oBq) zG@qBk^fI-__@EDL;|O7wzGvwk;6vUAoGVk9B>w0aL-(YCG2M{1i6!u| zl31EYT%y>4xXv21$ezm=?zewP;nUgZn7$j90=fJzLkX|%B-hp86hqyfg%NfSELV+! zOo0Q>D);8(A5l!!Fx3Y-75)QvOR}p&J_3IH1Pw-7iLa(JUV}NErZD7hO5AmJ4O*_X zAX%oIW@Gxd;lC)AVswxos!VXkpFpPTtH7q7~%rfwTpF#p9`oys#F9;&xAu&<|;mZJgQ!pEF z&&mW99E+{yJU^)z4L<;9v#`U#D-2^wLm15S(h>Hr5OE6 zE{>NVcK%>JjEuznWo$<|^)+=N%;4R@F40 z*+%+|=T}vVZemOMS?M*_Tm=chf0}{q84o9>5IPLB-T|l%22C5_f9K+wX}w_}k~5G< z;A33lIO>oe1fLyj<0sF`77;wiF6#ar$x=06ut}f8LM5|?=g6Kk(I@VPCju2MV>^oV z$t-IjQ1u1DDE6fwt#NRgjFc#MgJ>Rof$9-`G2S+qt$muBjhlLN!Q!KwUOiQM_FewE zOIYe=g6;9r0SxE5fDeUcD=oaS@IFqqIO4vM`=#^R4iPSVcfCcGyTS>H$KGPQ6`(K~ zMz^Nd=Aixr0|}HEsm1c?GPJOi<~)RyjqB5p#M&HukiCz|UD%1HQ8Is+qUL@>M$bO= zl00{|?YS#BAxqU6ZAN*3!2MjA;KRfOqN3(!pJO(0uc9gOSVFFv%ZrP{64(&XrSk{w zNqmUD$?JXGh@kGKng$@xROvu%Lsi~?!p4&~_ReiQc!ieftb#b%xH2Nog{UDtoJ6B% zzGpy&GzoMcQ$HA$&t2zoTAdFJ9jQWK-e7Yql2|S{bwCbK_JE4q=r`BE)xIB(CbC0- z07ALqq11wwuyjxmK}q*hZPs0RDXi>`w7M2h6Th0hb#%V?GmjT0cwy)`+chCXYeiK6 zK|lcNK%ly!rsr&?j%{+6q|@)e*C8g#YRU*7o;8HYMD{hl;^raGjXtWpwk^( zU4V_Iv2NfrNJU%KUe!Uh5c2;oEUl1ML_0-0OFK`y;C{j50_`I0674eW4(&dzf>urI z25Ifo+Bo=V7$4KxA7xBscLV~b8Xw&o^PM>6L+_n~C|n30PA^W%AOd_bOECr`Q!p(h zaV=tSd<<-*TIbb$Q|x~1Qg;Q%3;sK-E$FSAiw|OdX#R8)#0#+ICm?&(azrCfTNSMi!05|Ju>P+d!473x6eUe`4EE$ zZd}~qdr{FbjO_S?L=r1GCH3gBKUpD9)HH0?5kPP6h7+5x2gUGIn*3x*D|7gkN`&g` zbV#E7AoT;&(trFzBSS{v&RLEybmOt1$l&~@rq=vA9Gyb@Om?RYo4aertOxVKka1_p zan$kHQ%?YZ6-xHB(dK37i}YaqqWyrxUL8sP-@rMV&xxRC*#bW5uw zdn=PM5|L7+8Dw`P+Hz^EN=*geL1sE0@uyb>cm34&l*a&sq^~BwQ07X?4j@8h-k!t( z0f{KeOATn6;&X>WB%Sx8NJ7pZugFhB*a^v*E%y!@4uih^M{HM(+P$p z|SimE(4}I}@FZ zj;LtHWx@XX*9t+Jflj`^lW&9|LtkZaQ=(mF^f-5^E=JRo7wb8s|BQC12mGwW9kU5Y z=@~OJ>cp=sP4!CjOKj}z82bYK zf$a>QfY91m03c|Wnl(XGjs5&ow7tETwI*WV7d3s1dK40Ury3`2ru`QNn@IwN!)p}K zH$z5Q@Rvd&aLs^Lp`cOLrPF{MyjDyV4mpT2n!srO(2YYiP1NPHr(h7(bRO_%BOG3S zGeGN-@X`@mk^8IywIjr%{O{XA5%9qD_k#i(vYMl*Vv};N?M8pE{F)hKq-oE@#56Gg zlSs%i920#i2U0$RdKeLucJBFVD1-zDb2_0gB1xeipjh@AQx;GEk>1xl6Bi2ZD{X;$d$peLNyMoW3 zpI}Y#ejDvVqE(DkImrRdm;xrKEJQ{70>8vo*RBId=m^_JlMRCCFY(9l*>Ep-IG~!a#(K_TmDU{yjRQ`4eg@o(u9LuW-n3mU^dNDNR z;YBN_(Hnc3*mbzzx!;$h^v1$m32{qeX%ejuKN+zhLszQMncho*Q%17wQo(;#F9?mW zguW-<-y6`S8fD4`cDo2(yPU5rM7U*E3k3Q>BF)SYZ&O$I$6Zq>f&clo3s=v#&;KI; zhON3jAwi^vNXxC4hs*Iut7r|?Nl9)`986v$``BO(UE zhx#a(I~IUKgn|&LMUmdEtVOZt9To8`Gb!pj1pAY?nF_SC<B7o;kHpz%7~cZ*J9DOLIxCzAs>LMi%p6!yPG&y}`ZTU4T8vhRQ7 zDarN-s0WiNHJQxAOSh9ivQ5^qU&oxsrlu(K43HHRwfROTY0UbzY*1_9RF;lPDlTY| zT_-*^m4!+K5UsA)mWaWq?e_I|hGDba$({+@2Ox$6M+*Zlf8jWWuSyOl_k9^ByP^&OhggOy?F|86u)~rk zKX@NEAAfd`vko6*Ck`742?qoR2I<(+0R}w=1qbL?;n`rhpz~w!3x;jjEf)@O3k(fQ zz-f2Z;D%=%3(IrofW5`^I$!Na?&Q=pA16y_IRl#{UV^Xt z;WYuLv`?<-x&i1Hy92>JD{r=QyR-+4I3EkthXVlFZX>D{ySf8QJr7;CaN+{5Qyc{o z0}B+ciPIoL=W!4V{9+rKLvDZze*nck@EHmw55@xm&vcH18_Pf&2ey-Ub`ZLl2M`-L zh&x~hM%gN9V2HaP;UzqbpSXwvut*zmdl@g#h`)eF0~LAKx!cZP2M$UDC{ODi8xy^H zu4(Q?S4j^OyPC6I*Au5B@&o=(6D{7uBe(4*kw*j06H8u8t-D1znZRB9e_l(xJZ{9p zpdf!bL8v+~a4EX~06JApL`e!VcTEKUf8$N3Hx>Z{#taV;D?CC&cYuL{gM@|XVH@y< zhp)>bkh_eD#!2W)pBz#F@1%({kw<=L=TB4tJyZc202uZmQ~>}lt*ac6!8%JIPuzM@ zx=ZLKBlL;q0ykN^Zu{Wpf6VOc1Cu9^19nomu_vJc-!L=)6M+M4>_g};=O82i6Ob3N zB%U2aiUUZOJGL`D0ewd2GmL=K7Ye#>0F(q{SZ z)Gfib1|$O$$9UN8T(<17ZWz#K1WQhk-w0P;7 zu8Zb_xes^Q2m0uq`=ayrrI!yM0(GD1x;O9#2`?Q7(9<+c*B>PZy)CMObtX1V2jeH`mM1r10|vTqm#MQC)D(M7*EO97l>(n(2M7!e zCD=3=5f~r)5`=xpzXa57z4Qts7aZ4%zB5494wj1qf>qHR;egPK( z6|u2VStm9El>t3FL%+-cVF(%`$OkwoVvIk_w+}A|V>W>Ue?~7q4X&XZamzdsk_V~_ z!?_^3qz5?_2aao~d$t_N4;|SD5w7e9J9F%-JDzL@Ee}a9DbKosa0iDw2Q*9_QMhLp zIAjN~fC28=v215Sm={K{2k4fdyQ)mV2QT|N16L0w0O(iD^v7Qm(a~%J_YHHdyYY4` z@jweHc00Yyf0Vl>Y@#~@8=W+(7$a|X3Hme8)IAXoKXhl8pqvzD6%QBl78>}?cCXrt z4_CV!rX0Yr5zi0GB0C*7fDKq8j#@i>&h5fo3t!J?A1YL=6Nu`FM@A>W1C0h$hCM^9 zT-SA7lLyWNXA7SPtpjj&klv^U1`cPmc(2sUv4=Gyf4~bP0RstRGIra6hDYm2S6vbh zc1gA$n9etd!pCGX172&OYiI~q-w&z)qyy>fgQ0*xD=aosh!b#KC$Ri^H;1%>2uOSe z04xKHZyN+YH)IO~Afr9+!=165<68xe_gi$vkwkwIPDX+zOk;`Vi$Cd4~R*a4=uL68?|%lKiP68(6G5%+XUzb5;*|R zKRpY^yMVh(=Kx&^>*W{hTZnD%Bjz_*V+YKB2iMVU5XqSb7J=ml*JyQJ5h)2*Dv=jY z##-H75Mep)07Y<{2XV1mM-7h;o_zp{4;b5ce+>ujM+iX&b)juBR?7!=OY;ZTaINoF z)cf%dAs;s_d|M&hS@J(4%knGqE$|;Dr}anRQwQ&xYn$!iNeAWP0Lb(&??Q76B%BB3 zG%@rSZSVGO%+>P`oZ9vOw5+e+4&zvR0IFT4M`Zw5e>*oV&tCR~Xv;Ny8&!7` zf7$w7^5iAvX;b@NuDNX2CkZ3l`4eUN53sHou+0-}=?}jXu7j=_q!~-gCo%&78Nj-= zN1PLskO3KFrJ9|p89)Y=aJHda7Hzw}_b(F;QtP?_p+{DF69Y5ZCz=MZ0eur=b7@_7 zS?mLxkoj;AcLT7pT{mD8gb$1ZWRVlBe;cyySr1+ll@FOuChfY0-7nPxJdvN|U|ocd zBl!~ouW(qf?&S~d6FpNw207c`NN5uVL%K2UnkW|!v!4kEGXpqmH(23!4m)B%>xt@e+aLd z52&zikiioPLQ6|e?79JC43p~vV6zYi2VplxkCG3VX;TOTXNPrT6U-0U69gx>bti{) z55E%yq7SLgb|;4u2f4?@b`wqHVBIjg!{!seuNh>J=C$v>i`ETtvYchXtq;BvW$cCx zz$Z7Ab_Y(_K*O?+H;Y4gQ#NlIf4jkVs3Wd+S0}=5$hvtCf;-(0)OJ74HVk$FW9m=k zb0<+@$hyH3X(Mqv$J9+{)U*wFtAlF>6Cpd}SF=S6FLfnb0Sn04!3F{s8A1yIYU0^1 z0Hq;kvn!ij(RE!d7jIw8-~-Iv;p`w+1Y=JW<7XLwvmJDQxtH}J7tR?QLC$E4EV~d7 zVn0vRglofvLp)%&`{8M}9ZP?pyW5BdrpT^onx?C@p`yx1TY%2Ng(iaswyO+F00024 zPp5zc005U!`2nw&VEF-11b5Q~x2H2QjlFQs&0Tfz$-J{tnQ717;Rfl*NU&rm{ z_O`aJ?NYV2`RzXR?%OC0DTX>=Eij8Ht0;>%G2lq60E;~T|NYDXUES|{-uK@5^WT3* z-(TNr-+SM}FLw^sNZW1ZGsl1T^>N?-{Pp3-c*~;>sM&9t-{IRFzqXKEaoz?oaoL$* z_gu*4-N0l9hrpCvZ5*6m|Lgl<=0_b|y>B@W{B~`B_z%<32Ooww=RwQO|M++Bkn*XJ zIbn=J2SN9K8{hr`egA!>^Sqz#L;HV@jFAl(4{~Albdv7Qozcfw_r}tkb8yeltvzd)3kJSI50!9`yM4tXh7$=9^mRPAj1j3r)3pS*(9OA2}@3 zT059ei3o5X4g=<-v7_fvc64~J>!Zajqe<=@GD*G@!|w!^X=a-bHf^wazHQEF zwt^hlxr_YiFYBEoZcVkwbvv;8C5ZA;H!!o54~y*dfLJTJBD$R}m6>wQWjW~W??oOU zM%C)~Fkva=mv3|)JZZ{ImXdb0fc7^^;?~oW4!4r8OyjTdvxlvZSm=4XLtw4GTs?m~;?U>H_H`QY zDx|ef%W!ef-OBqe*Bnb)D3C!900|^F7i8$c+t()67sjfr3E2d;&M-AEMUqg1xNQ5_LBJoJ8xWlRE5$j37_@a#@gjZ5PB{;6T zMDYet?~%91LAPR$CmnwszOC(yB&;JVoMuvwg>P=KznWyzY+_?RJVk~H!+$!qpa(S7 z!x9;Nq4jCDu{Hza=&4VuD<(V#I$krn}IP0@LI98VH`R zSyfBwj-4icsdG*0Z4moz_fC+))@wH~c|qEK!0i-~E>)Lut3!@OGuKTu4LD@zXo-^kA zG6z=B#ZShYWb1RWeBj$Pj!c#M+jA0Y<`6P)Z&`v8%(s8;jyuuF%=ZEHtI*8L0Vruv z;)8DHdv54_vZLiDY+n34w?2mE4*nyN#O{MV3_h##O!s5qpP9#x)cT)gYly1I2?=9? z=*}nN1T4ODI@~?}bsiH!Z4_J&tiEyeuVRJOhXRq``u%mHQL4w|eQi`xP}F%nSm-g^ z?Qy*d2Bm*La*1T(rk!zivT`Lr%$KU+i;mNeM%CiFq5c9>F5WT?aM z4@ODJa=Vj8yCk1arfpn)=TW(X)QHpp*9yZ#;QN0(;~}A4WV?_?v!+hAfOH7;vj08< zSiv1xMPCM$9?;_~yCS{dy7X{UVkv9`i;IvN0U7DV9P0(`r06FLsH(JXknnJF7%h?T zt&!TJ*2h3>(`<}jbS}G|vY>JW08nD^h&MdUf%sNz>3nAA!Q`=Nwz4;P13M*X8Kd~F z(?)-yQ4JUXnxDnq2R#S}7H%yE-y4qwkCGoB@&kfSLXBKnIIf8{3y$K_B)EMN^&Qh} z1!&E7893s~v{s6&dDQ!iAaBtYHHSLJCNKnoL#!O3TqtH_k(!QY5Uu0Vi{lrFqpLZ3 zd{F_><*mcwN^VB9-3-`tF(9v7mtMCn4ex))0pW&b3yN^K@)i0_$tGfDhBhe3xN;WH$ z#8cazh8B-ow-a;S_fP|tU_7*U{5T_2w;P5=+nl!;jk^SImyXq_?R8(%YVJs!n%RF) z)F-B1V;Yx?-F>%1-|)0DoYy zp2J0fqv*~LOBWDeWHA!9VFubZjL(0TUiHv0b;Wg5^6Zwnd4cYcy#@#a>OpFihV7{R z6v&82ZI(nbJCQT1mgt&ow}xdZ-1)GH!-Em3$J+-Ivl0fvwEPT3ko9OIS75;#^Z7#Q zWH8tdt^=2Mx?F|A&#^K=n-%fyE0k=qQY^#}N{X>{E_lkv*@p^}bt(XKU15Lmcem&K z@KW~Q(F9C3R*H)!B_0~XuBrjpM7jZbuFM@56D-8zpArf@ znAkj)O*Xt}JgFG&7&85UGW=Z{F>}m;zB^34#dLLz6e;q3qXl;SpHXsS9&H(2P$yai z<2j;S;?p+v3XNY-D?ucV>O_AV0UK8T9nQBbXN z+2G{=*Mq&H@$?ZuByJ>$44fT|kVsTlQv`WyRw`beIz47}w5YKXSxKZNH8zQ@0=jw> zo^hVPdo;M}0=%_X)AmLFrs!D2^~B6P;}P8=Q}5% z0~kwHF~ha%gWlP;Eki*$94z(glmLtBlSFoi=i+npOW!K`VQ*#k{>bjJ>tLUQf)G%b zLDjN^TuApC+u`{_In007kqFY^)aMMJHh<4DK`gT;HYU!wTR1z+(Qt+?UM$9mmTa$S zmD13qKh@2cIaZTMuc?AHVhLXdnMx}S8dajy;#ewR+$F^lUTM6zC5<96fTE%}R@eT;`C?6R;Q|gOP zv7#}Tzt;5(HTHsVvOsW K$J1d=C2z5<+jFB~Lnn{LfI@p!~4u-;SD@lcT+iuv`P z=!$b>;wR#U%q0ag%)=G3RNpaYo5+L{v%iRLtN{n3cKUpGJ9?%~C+vutenj+F%t-`c zeYv^d^k^AA@v?thR|iqr6QCRo$Y$PgNo(BGjB)nTDDu4+<3{}mUq3e8s#GJ^HUu!@ z^+SlS9!jp)(1Dt^kc4p zlTd~mM4s$~eIrmnAxFoh&^5nTTAnCOz758wQO1dQql$l1_qn(F5GoJ4>E{ECSC^{5 zkyZm|%D`p{qQ647=#9uL>g&eUb0FW{jHbBlgK%&y5ZD;E70fKfUpSsOGet=_9ulU~1bC}P!XVNyM^P$r7e&-eRH}qTbrRi{c$;E8<&<-#*F5E#X!3Ll^Om2Ng)?|r2boHl3q^)uypb??ZAmH3pvZk1A)?#FP8?x~yxzwiC;0SIpSFKb zfV|jpz(1!w?u5MWE)mu8&Ow4tI!Nl%IRnboMg@9#iTh7~cco)jrN??UhC$I3RXBdJ zg=Cg#QZ<@@&^2K}VS=`hqGW@dg!^x>d}q8E zMRv^350uHtfqGLAS|&mjY}4{q?tP~hBu_nqE;zag4N_W$i2c_@m%ji?6l9qr^s1I! zkOX?(nSjiRWDDOe{0YRA=9BA5w1b`u3^MMSBnwK&xprjCo<{`Z)ZF?J<$B1FlA66z zxhcj#<%H}8Tw9Pf3>QXq!Zm->A`#G-46QgH9apmgj}StYseqG&j`2_~J)&9Ri>%Sq zTBiibAcjbiDNvr30;IJ@`|;EFW%GKeJnECC<{p`NRW#@Zr^pH7Qk?3OnwZEELqOPW zw3XpD=d!7$-1P=3pl8~Da;rR%20;=x4AUyv(RmPy9!#O+WJjlwd?J74mVvmFr;&6b zFInIsQQ;GU6ecJ7N#epZ0!9_*N}L%g+4KL_4YW>c_#AZ(daqf>iOTE6Zesz$CAxKL z#eYU9xhCoCLbd}>j>B-90gt}&?2)9k&l;e*&7~&=JDDNr17IBo@E(Jc`_0gL9Eh9$ z=NQCYhve;Wb{1o>+AV*u)$$@j|BZtf$h!{YAaH2;{lM)tA+*nDRFHmmcpkoB@Kt(V zL`zP;0}Xkl53YpMaLS^|2+De3L#8n}BL-A``7Sp@@$2wGEHLr-)qyQ%BeF>^_7-H_ zr8<6S4%MoAPxBZpz|~%J3?+oL_&SJVdXozFG zUnmcxV_NWEU->HU{p$3=v(3JG|I;1bV`yk7-;R+tLFqX{r|SxyVjI1GtsNJ57`fUY zZi2GU|6t%_3?zSb-z;Z^NQ0liHYV|=*C={ZtKjlaOEnp-x9a$Z9Vx&ixjd_6f^W*^ zJq?%J!;%)U)>x zZO>spZ4G^x2hir3zZYX_)381@{e;|Tx_wv{#mpuURSV2UORAdcXb9f;h)Sb+{R~n! zz7!7pGeGNSK?y4LoM1oRir5pQfurNZ=_%%|f#91%_6CPQ(`%_c#iN_*Uvo#hX2LWCU=_Z?3agJ;BvEW7HTvs{W z>6$IiT7$`|^*o4`kxT`7_z*_2Bg>+1OQ2* zJGKkKgD%@tv7HvgNOm0#95_em2{M_x=weFfQGW^!*S*3(#8t2YJ z1`>J`YNFcTHIX=MX2X~ow(!`x4;)~4_5d7FUo{TKJSxmu8cRb5_TV4k0vG@&N&KSB z8=-$?A~(7nG3ck94+lfx;D>{qI|Ngfitk87`pLa%xL}%WMFP-Z?M{1(N`DW%fWhDJ zT~l`hx1-!goloJ*K*m)&T#byJ#WE4D==ThcQIas1?hRl$P?Gp~Khhn$o1k+gD<+>G zS#&pQa{wq`dB~}`L4h;}68Es~&JaO};_ZLO6tb=B1dM{5rkxnlBy8Q!HUQC-@J&N= zPxuXHtRx;&DCj{%!1y0Zm_)M6f#HEFAl9n&KwBO{izFx0N1&RF@Waw%$B zxxPadSn`Guox^6G&FF6W0XG-gYdH}rHB`l=EizPZO9NxUjY)yjbJSsG@Z2||2D1hjc=(s8{6^#v6BcRvJ1j{ z2^TPevO~6M{C|R~*3U(_FWv^Wh#|Gl&j`V)51LkC!hIthoF=MVQ2{q|UJ8G%5Ag^7 zVTf@Z9v20oQU?lZboklh5wZD0Fb^#8N2AMzXhJX?NvIn`M_9<@Buc;*=+Gp^@6-6N z0h0jz^!;(uUYerYF)vtP;0<$EZ~6=j@B|rjleuc#_;m@-RXl8fz={u1pJD$yPUr>ZV}lIOv&GfOi#cho%@l?ZoIpRqQvnb#q@-#}Mr41aWV@nD7Rxa^ z#M7~>bhXOb_<~bk83)^zb+9wAo4HE&JTPR1e2o-IK;e^aJy0E)JFz<@ooA4pWaoWL z8CR#|bw07bp83U z&?O?q{i?*L8eY_Qj)nP+7h03~Ash+6n-~~4F=SpdTWNn8Cp0c%rDU?2=7P5uCb#os zK|Z!7V30@D4F=B-pAa63db}WDMbdkb4T5&FhKS*q0!#8dv)Ms5za|7(<9o&s(%qls zmnJZ65(Uyn{G$(`P}eE{!OtC{NrKPQ%lEA+c?020xIAt~>rEo?gFc>_dC)t+g8A3J zJNTP%nLdB@JtyfN2H+!Zz5r9s{U&cl6dEL73>%ZjiR>*xx~TZciI@?=;*{KC zZGV4%@Y`Md5*9hf#~mKy5((A_3BI-?U^PFz%;>OdyV`WIz=6#A15> zcyxbnzG+STLwBgys-_toTQG>5A)sXksCjj4$&SB^J(0quI~{V<^J{vP;@>{duG)k2 zF<|`&?xCMz&MgCOJ_Gdy-5K`nN`rRAnr45MH$a+(Wy2BgK)Ivn$oA~Gf<3(h)E<3W zGE#Mnp&{25kt4xYsKLn)y8d9kWo1T4`l;3W?6UICu%jkfjZ>yN>8RQ`b4(TMEip({ zP-B}9gU8{3{4XDc!SDm;oCD^GSNlMAdVinZ8Cu)bkEM`5vT2!f&1E4Y)WT`wYW07b z{;w%ViBRE5o@PbKPR0Z)00q&5hQ^mLfbB`XN0Pg@dLPUCcdR>ir}pnyxUTD0wDqf( z1+H+@wG?2YuV!*LtB`Ds>i8KO1>OCIQFneFX)=q{ldxR4J&v%-FY)T8mm_WH>#~e0 zrkCroht*9m*JT5bg;%TB^c8GkvEbV=EwzOu#u-M*o>5n3pBgH zxLyu~%@x#O#V2f@(kwDS&yzR28pJHiE^U|)$2n$j8c~U&b2NAl10eXCmrQ@d5-}TT zHk)h@UrTic*@ts_5tc$Vb`alHV+qGcn9>h1d2_7EB;2{yq|diLz$zl0Rh^F4XWPdX zg{-FVoH*865^viQ*;<7S-1 zJX@5XDL`qaBL2`|Hb1!g;@N+!;p}g3)3WG18dQD4&!3XTZb01tUo<|q)1U}RE|b>; zQ0VG|9i>Oewo@wK+O}7iwo@aZO$>7lH+43Y(c%sah6;DYv+hIgx@2!$KwBMASq*Kt zVB2eNhcW{tvDF@7oo=aaVX$)r(lOu{%8+cIJ~`eZq(}=yno8?Cahik-G`& z{?VJcnzatBnvd;~SB&Q}2!@bScNepr$E?1Zi9#T>08wbz*yvRoy7o=k5h)DNv8PVh7 z$YRvJziu-#f?I!X!v!$TW|=@@3w{;RaO!hKjT&8j^`tJ&kS^^qz`L|y%K-Pzgb0)S zFmX|70P^rNqi-#&rPAg$q2+lDC22VZO0jGLDwtcyW4j6&ELn%TH@JqLkl9?S#cP46 zcU1j1O)7Bfj9(zzuO$A_8{p83++5eR{|nJx(3{J81+0H;%L&M%EaLx5bPQbZ-fbFw zeh8T7z}XCjU7MR7Fc@UO1d05E8d40V?2nqG_b!1fSDQ5Pt1~sZXd3K9Vwv3;c(!Jx z2eWfbBu(g%GnD1g-cCk%iiO%t#?UV4?8X%7n3HM_jn{Ivwp2YB!U{<-V_U1xgkh2FoT z+7_9~Rql-MQk@fYF&;XMiSrDk&w_)d@E5r5aiV{?ja#vjC^6|Cnfhimhpj{k)jtBD zJU_Ix*Y&hGG{|@0wt|O2N&C#x zj5~?uL5-0Gu9ZvX7a@cSs1*Zg8Yj>#UeruhWMY-HwH_5|Mitwj zf!;hYnVh&jaHlFCElyokHQgg@y&D;Qbk-iiX>#Qw>GccOK<>kYUXX5{Glub(7z>?b zsI0dWuTP(7#8_{1o{%I_QIPome>}%u>*arcI9vApQ(Bi5*QIJ`tuN``HBmLB#R)f( zbj>>OL5v9uRdjes9!NWb8hzuOGzKc=4^tXiW8C@&=~t2xFj;0pinUXcOhh+ln$`G7 zViWB}ccY87o$Q7@ub-st_)y8k2YZL?(e=FMDy-yKd~C*BXh_b_9};M2foNyCksN=F zj<0B9I5h8j@ujY-s%0b5uBImKH_>F1qq;toFDKw)T%RIYSw?nt4xWU z+o1V_e7!7})@U%QBL%Ao_y1|C?Nkl77;cZ(Iv=VXC%bv}iCQ1;B`2@QKiWi|(hz@; zOVi*8sTbzdc<)b)Z#BMchvuBnnA4j0$+R~H%UkB?*?h41mzxB4_DtSRC18Yl-k62- zxdJZ=XVB)EX+6u&w4Yq3*b9FUGt!c8AQ1Fq8(N2$QNl(GH9nNRX=896pGjoT!Lkm> zXIGZk$%CDedbpU8@FTezeN2(i8$Eyb3r2#Vgp<_tzPXt`lj(;Q&Z+f5c=7~-H?5!r z3mlTP0NSa};InKc!Inwoej|?W!|l(+X6O2xk*2cBv{eN*VL_#6ixPj!$pmS4N#{Z; zV;CT6jP|zp%cmq41%iS*Qcv6hQtyTUP_i)tv>PN`U~dGdM^YK_@pHp19&24~@rcJP zIX*o}o+q-X8QyQ~{hEkW_Q`Y(^&1t$cRW|pv1#&Rf~S>euBv(-QINOU32ju2NApj0 zjdO)+7f_6opWRkHFM5AuExljmr}2D055kt*SW@Ec&)$X|We~kih!du97;-9eMzG_y z#*G|#5a*or1#u-!P*^>-eN!)hvQ7qGKlxFlDpj^P$ShZI;c$=geMZA31s4%&y9B)kU zdUjtv`)?#_vmP*@4l<3-U7QRmp%o#xol6xoJHLFkc$%}!@# zzWIsg3Ml`-_0bA6(=4&F_Y21N~hn0V2r1rsndIU{Z!W+kvD##>v z5;1;=J&*;m)Q#}M@^!5^GgFo}oD3|}EG+XyxIfLJ9$lcP6N@0KeTdQUn+@E|bT8ai4g( zi-bF14fTJ$O>kwvw;&@oOk>!Co`83+;dxt#mtFzAroTrWrNnM5`uHb*rQi$QfEe<>wKdzf@!KT^Vkjl6d zqcq!5N%~xhFrT_H9-9z}X^5Z{^L79W@cjUZaDB~cJ`mgjSXq$^XOd0(F-2V`@p>2$ zMR@S51$cP5Vn?{EOZ-GtT5;W7yaA=zuxDsj3@kPn{MEx&pN@l4t zeSp-|X~%P{HviU67iEvc3Vz^cKpEhVRK- zXuy`Ske}iAMYe(60P*{&jyZHH-rVCmYLI{1BruYeqXW1hOrm2zk!WAmtdnOJ=(=xh z#gCytg{I)@fP%R{2|M`_eZG$=Pb{wVA;m}qLwMS^{^v&BOyOP2?3ch`N?i`gtPHAJ zmPLFp=Kmmiurd1eKbSl&-OH~-AqVe{A`Q>%(b!&ZwyKOD4%;L*OFj>5MPzqIv<`m- z!#AQ{5r@4_zu`3^n`V|>!(Q&lD}oAuJhe&OsUBID ziyu?Pfg_?hT`)||z%C{DPoy*!aesfPZ^D*KY}fH}s$YwFScLv=kEDnX%_>LluI zI<8QVNl~`ToetSAkXjty(IdT}0-05sH6RCwBN>V9 z3~(;?n*U}xEbN*-fKfz@M|Uhovg1JxlV8#N4QoJVQwz}zpxZ}5*5(?**n)k}Fj8D5jBVI!Rlrib-jpaz z%7Z>ZuPzk}4#xLJxwDs-NM?UR$~1jV-m!&)4(Z-kG7ESWH)sjN1J~&KqM-|+%2+L* zGk>1VZ2uzzJaIzD&C;Io+Mc{JR0g+qXHVfAg$bsi1t-Vy0TCU)0Mv1TM}441DS$@S zptA-|(0+KbqaF$N)&?;uR!kk|#Lg%2Y=#f)_;~DgM$@e}R(Q=M4y1n!4d9p`?7>-D z-aJ*6rFD}(TG%OG_`0k1FZ7?`1`&aks2}{aa1mRpCZ~{K?BqQ7CUII{J z0mU7lXnVdWIv^&WiJSDXK(X{mo4Nqdr z+(Bz%{WM}U-bfcvfv2N%rb!xY2>Kb3-PkId6G{u86AA%qP!?S_1J|JCiiu0?n?R2k zMd+ew!Yv(>C>W4eFwXp|XCrNR&YTixzNnQnk=F<>j`@R;M`?c+P)aC3PFTd34fIVC zt4lmHV*&)Usr83y>qwvJ^~Cbaqi)s&`CZV=|6mi{J}zgy52{(Yk#-srGa*bg4?3AC zpHwlB$4vUe^@f=-X_2e*{oaq`5F{fyuS@RqZ4}lH2H{OTTmN(hia=J?&^uIhK1eEs z<(f6j213VfhGu^{n@9}E??M+9?NhSr8nA{^x9C`_eIn1_<${6u@Id{>JWxDn5dC4H z#qwgHeE2<)S_9N#jesC}(#YS-9yBfVg8&e+aQ5SE6Y>CRT*$}z=X}zm2sHw=GtruR z;=7(zy-WW2ao;!xKQv3hr=z|rx8(YUu+x|!1H3hyuNZ&k82SpCAsV{)lnm6$72Pn$ zI4XP7PNUBSuhuhDAm{3(!xN@AOfZ3Ph@Z%DFrf<|U~Kep&>;noQk zl)WJM3(qHplbO3ll^!4*G0GoGoQTj5)Xy+!Q;%ACJ#b0c5=eAg;IC465)J>XJq1m5 zG_v5RR1|+MJz-3?l^1|oiK@|cBvR&wAa(H2y-&RsAf{+X zFed8O+!}Bj083@fDkvib5~WE}%sbTRz2&-BLyI`cJh{E#N29E!I3Ov^%f>^glMP1v z4?W!Ey7(C4&H~a(oP#y8EoDaP;@*Eyd8PBZ4;p{TYxix=NtNu0KV1o`CCL(}J};B0 z9U66>paQilmcA1oxzp?VJJ_GN6`3!dle584NtS9%G)!v$SAqlU&rGvHVN2FZq;$#B zn}w3>@VTL;nLy|f>mFIjgr}FQn|l8ELDLuaspvug(nAUK>TA6QkuhPehHR5s5}cRW zNwj~c;fXW&Bt$m>3?n)B8;_kcV7+3;bNakMO}df6PyBr0fRGK3rRNKMl6Jh6EsX=q z6m7b)o1%OTP3pmFi?)}X^pB|);*L;MQ&%{z$~;5%Ga*z*4MXw`Eh;0 z(-Vdr9~;>St>59h(XYF(dY`gtryxfuqzZrYU_$}|V_V;eW)Kni&F<2t@C2WU%os;- zG&?6vDmd%JYU=ft8iL6c3t)lij@-i}=BaL2zoc5aM2tjzacT)aSc1a_kHFgh!L9#+ z!a2OyD~Xu#Ou`>8%?*Wq#n5`@$m4L>-&T(ZZC(zP$_cDJyea?6OH!jV&5J@_|#^tYHbB_O!9Pj8O=>b@g z`NarAnQdUAQ8#3lewit zpzR*DShlX;5_PCJU2Ue@@E7Sd$UA2B{<{5Q8ha}-L6|W^3lsdt0=I)#bmtsUVtUXq++U(oI&|_K20$rQVJ<&e25fdO^^(Zw=KkqQ7-8 zx6`G*r} z25FCf?^?BXa&&%~8nC;zaNRZaf_S)Yn=OGO4b+#>Hf!cLJHfI|CQTc0kpxw=of2U!)@rHd8Q9Vzl72ctu5w#ui<$w+Lwq}lYU2@ZoH0VW)e-bcc z-zTx*n6s03Xic!@RB?Z`mLE_zk!(8+0FfG@mUWU@<<57SW$HLDg1ds#YZ#0#LC`cuioQq4w7f<&;+8}FZgDQhrulT}##O$K(?UD^`PQpa4$O`20nr7v(G+j~b1 zkIe8(oo47htA2lq0HecPo2@9piA^CFG&#Q3vb=NZ*3Y{0&7U0?J#pI>;a}}JkD*m} zB+?Q0!uQ>xN$&K2ySuXKT5ijqc#w)a&on`>J4YSowvObEvdS&3OmO+v1(op^sNO63(-&)@&gJm6~Ed%wN@ z{QK|c-+#mRz3$dYBW)LrtCnDTgkH5xCbpK>;_`QlUF5v%sU3BlR*wqXjK(7`-Vkqi zLLlkOWMOz5<`-VK?H>4d2b@%bw;*i6UeRL~^ZR7?G52tU7*fBPv}Q2JVpiS3vu{#T z0zzkT7@L2YrPJ?#8*uADpu3R~@S_5K*44LuL|?ktOoEp&nQ;pg%(Yxk zBDIShHjz>YPI>X%yF~reqI+8B2Az5ZY*MWx=U#t<&wN%CJ?}M%2i;kyg1nT5^~hJO zXEd4FTc=Yl*XL6j*5Gdk4z?YEw!RCjwkRPp;9T~E3X8UfJ>b&1YiCLX5&5$=9Ivsa zkg%V7BQ5|7ap2P8ilR%OQ6(>#@I?+-TcO#9# z2z`G$A;@B{z+B^_XPpup1b**e%ZE`OdJsH67-QC!v#VK)Lo zq)7E}qxOw&Pt<+zg6Rz6Wv$5MLc^RiiRha5DEvwSjS3uRhD{ZRloKv-KP08FaH?f3 zz_QkqsQxdAJq_a662|zw@cBR7LyBu>o2GwMnRN9-=Sw8U*~%+Q-l*O!nL8IX3Z%q! zTV{<@w@As7=tV`%%OUlBLU_^ORHv9iss^ZOJiZ;oZ1HD1yKbqFzxM$uxZ#^pDs{w} zYT0YcM#$|s?S^^!kwHDas2J<%^=GJ%7=SB0{BOKdk(MlEa2bES zQsHxb3Q8eX{sCRCEkjZP_lJ!iEuuTAiuw~tWwGe{2fS=+{N5eRWkw<>d$y(X_?k6U z^-+`WzODd@jyT2P^0?exTO^iN=aag{o7G1=-kgo2Lv-@tpfaM)Q!^zPkt%4%=}#cVCNBAz?b%q(<}0eHt7z z+4mIg-phq|dT*79A}#p8us&c8%HnBx%@AP6UK1OT9G~<|a#f|wG&T9^&^UkXZb__B z>Ef=2IixhL;IqhGPv#m+(5 zjE~u4O}$kevRa5Tie7M1I@N!54N$NL@$l~c(NK&rtLt3YAi!yAcKa0y} zg0+50W1-4s@1Dy>yXO=Ga_9e#)>~k8S~k~AjkS<1wG&Lw08>D$zh%W}(m`oVPe!Xa zsj;qv(n2OIW7`2JPy$y%#)@HonUOLU+D|iwa4Tv>Z?ax%b}Tb|^Q7UVSw(jvl*XHf zus|A_VQpI-Y)RBTdV;-|nA5J#`)t4+FHoq;PfV~$8^gWX6m$K_{|?Q9L4NrAjyvSI zgNm+I2fgd>2Mc}e7dcw#+%e)~k1NIWK~dRe2aT!#=saGnf#At|MeoLc)8UhRP(zec z>eM|)TLI+PrzTY*)p~c@Vzme?w0Lc1%7Pj14NZ1>ZroMTHCCY{_#DZLTrLD}%!tXFnf=&*3M_|>{G*bS`S@G~tBvx2-e;z^iql>nd3MP? z^*SzaAmD}N7H{|#YFVp)dk)Q{Iy(yBaa|2eSNoEkCwLk4_O_Am@60UuQo=peAKjnA z7+V}3qyKfBC&?BN9)9F-fdxs=;RJ4JVa;{0#wSJkhPA9Ez2SIe3II8n2xIz4!-Nx; zkrnkR*G_;aHb@Np;20p!?~m6G3RVYDFCUx4DEvxMc-vGE*x>qq;f0ot&bwDK*wi7w z0vwP@Y)j}~sF%r1>&c6Z^aJc6j6_)H+O-xyPyZeEiL^ehs6(u!9qo@N$pHO>42$bM zRMHo-&%IX*)bmohOADe<`XPgh7UZl{FD5SFXOB^bVs?o`?1raWl4{&r+QaQtufIV_ z++WzM>Ci)xnjY?d*#=GEp6|n8YLN2I7TI7K8?}2>gv_-uTQo?F{LnU5j{O7d{5~Ec zHb;+A8Th_=a5SioQe$jOaP)ZOw%C#HWCxoBFvlOe+`1vqrr967g{a{o5FslU*J%9+ zv`pe!Jj^>o14-_(@GA7VLqV%nwZQB7pAnMfI&$h<5El)9WXOs|SKY{2sMB5QAY@>$ z5?CcVDUywhzAdG*=#2Zf5jks$fH`k65QC{b7VJ;u3jA|bRJyu5=qKUn5W))`IR@B( z?jlLC$C&x8=&0^%QD4*SQ5r^%nY6?*8|6nr6T8cygNyAJ7`gX)5y&-i3fB%q8uPF4 zm@lqBN#zuOqITD8YP2H6g-l$?bfUSGt&w|?+KwXM7?j7c@Q4X!3|3`~e*&6EDrw!! zauZ#vt1h%&6wf%-?cHj!WbKQTF^Z)++0eOju_TWmG(yDH~gY&R^r)p~^2 z48utnZimPi`{W+%I94N-QtX6J$=x=~&AqH5jV3sMg<_mG7ilk9p1AW_n|kH6J7{ zs#B4yg{Y@%R2riND}yfhT>^hMSfX1QWWEV?(QU-8_%RE1CD=YDgh0XE;2uD%tOAZ zhUiUl*;#s(Cz}SF2&iG>hssd#^)yi>Ewr9*3vRYT+r0C5T-BW04hGpRAYWG`{X)SP zS=u*x%wrM^Wso9}RG?n;FfBSQ6IjL?y$dLE+l4uyv09I5nP8I=kckEXIk7LbAr$unlE* z-NcZ#fTS(HM1zv7P)kLgmw7-`4_Kgo$;F<^^9sXYMT}NL9Ytm2u`^Ce*km2E$g~qJ z30>#JMzk6vK$!@bLIaDzKM#=0)w=;Kd&ZG6O3*_bGvVt{NEimYi4_ESori`1|1ZPdM%cJ9TSLI zvAHdNFicVXBvOS(@vYr#Ey@k6LhK{dY7pajIjenM_1BMXBnN7P4$6Gf=fz+N56M^f!@lcR#zc`BFFA<&t)Z7uJmmDE< zr5*DA_^IlG3s4P$jVx#U8LjU>glC@4BaN#Z8%B87yyQ!dD{cMdytzJgF>uTks@9&U zLwTmELPW$w#pXpqb=g*t2-A=0swG(`7AIQvM@=JXsnqj*@q>xq2Rw#awe#wH{e7rF zCYIHp-PLE_wLW)djpxySu}xPVn=HjNk}x%$y4Zj|PkzNB#0Jxb9_YmTTZL(`X)SxO zj3h|EvW^+dYqEL~(dj0C(_e`1(b$1J=;9=xd4kh<{LV8;eaQr<=D4`WXyuuDGys z+3cC(0;*a?i}ruJRApDnuR-VpP0od%WK51Dn z4~EMa(#0)LBK2v1HR53}2fnChJj9W1>s*XRcth56^my`$e}YWv_B-8AFL4{9CP%;E zt6JVN>Ie||(>pc9~;8*-!X z@5AO4e|tRvS`?rk9V%Aa>!~eBwy$+U*&@pSZ6P-WN(p2_?GF@xE|8;_Y9bTct4on; zs#x<=UmY)9m;Gw8s6NhHSF}=^_;c?B8)~$Z1p!^>pcZYZ>d1_f>#`(QUI?OkzLs5h zm+|@dF==>z1E6L`Unpyvg{5TfU2HeDKH+o}Vsc3}nTu+#@E}6+f;=x11g`!eI3fF#QVFwxz=ncyfgHb10pFGYdU0xxX798g z;>e7YW4u4$LhWClrM*L>^p?593AUzXm7RkDWY-!#W5QgN-Hq9QJZ?$N(vYT=vc@GkSS_=Nh+DkxNd|y2&)(j(5)7>w%EoWvA+FlhZMbQlG9_6 z8U%WIF=wdI;xnG+rAoSbo<-D?NN-Joo&FGiBO0ArJ}tI2i+|=AJxQx6>?`=KD_bql zt4GGRM%cYRqxF|nag~6&e6DVn6=~l4L2b8<5J*mL;Nz5lG{W5Bi*|L3a8$!SfUatm z+I5VFuoU|jXd9e*{}AABKnYBpbrVRP5_kN-QaaL!*H5&F;M*kk_$Grt=p_xhcS#q2 z=Q-}8?xjc6c@^^(miaRedsI{F_zwSjG7|6pCuymf@gCQ*JW-6!AO5?HiGX zi8Utesl*(iKJExShz6R|>nMB8TRTC}m{Xot;Y_$U5rdpXO8*Y{rImiVPR z#Hv&0FAhehFISt1lBrX?kuP|TjRK(5UBZ~-Kz1CVkQLd;Yd%`(J(IP)YfUG$%iESX zqzZ-U95M9dvZWwpmC#-()+fW6R<|rRl;#vZkaHL?s7E_8B$_k?jMz}s{_#V9K{9SC zwRekdqsusJsio$(s=~`^_pv%#Lfl8}zk4uXN(rI+AKr*`zbOzyruKr#=8+rs#Da4J z!56LTPl{mL4xH7K#mav%@_)S$4Chf#4;UnoQS{lnM2ze-gL^`^ihc*EPdAPTuEb$8!rVS<1j(1g%7OIk8DmmDfzr zhy7EZNGEpTX~g+m0P(-%E!C^1-=@5tm@vEYk|Tu~TOO{S^r-Rz zjl zppO;QeSrr(9qsv05-t!4&y*B{Wod#g=lOpE3s}V}{|K_gsx%0@Gm^bi7+CAAi%?+; zjLDJeSeyl4^9za+(0`#tL=h*ab?d+9RFn=by?Sn7gb$UG(_|ER^S+FRH3|! z-|j_8T2<{Z5BnD`sml9BNQ0?I*lqvt2{e@In)f{&6ya4MfMu{_;h7GyzJNeaM{ zAH{l)KM;`S7PTKfnNy}vXTXJSUT>a2g?Ky{taiVV>mkGWW$Pd>Pd=azYyEv7-6D8t zGHy4Y`$$E!GMb%#0`WLf1zgu1S}M8CtT_q+xcLXS0#5=z6wg(T^SDJs@l;G;wNH#) zNGE|Cx3*_=Xxy-iRdb^*M>WVNg&`B7Cl3wupRiWeknxMeaJ@Y8>KC;{c)N*^;$vkr zPi0gg+^BfURm|$Qu^a;YkKzd5g$=4Z`gnKr#fL1r65X(W1!`R1GW72zwuJ$oAQ)L= zzOWfJ#`;w|{Y;&~uGlafV-6I3#b@HtQ5F<~6Ak=>*cWN z4x{;F)FP5ypeMOU|0R~qNRss?kqB|>h4_aN`7OuZYL#>-<%Kp+(=@&!xU)MU7P2;aFD@C#I$zGrq) zA86%&P3}wpj}zt3R!CY;6c1E*Dcik1FZeOV!d%#rUe=o>!`m$p*26yb(de zyXR`WaA3Q%&ktQd#)ygb)S%-DasPP|g%)diK$S0LdNQT#ubUTKE!mc=7xf>@)Tr*u zbS@6PeZ3;_B~+u99Lb+xL+@ObOV*;_@Gh^W%oIT_RHCf{W7GipG)SKMx4r@eRgY+8?AMYjAP-tpR$1#>AiZ}4 zAi5}QHSCzmC|;uDa3fkCg%#6F9>lVpER0sg_5ifSsfsrIDfVmwfMbVBWQKxPwzk)Q zCm%@HajasNN{SvYq9e}PmF_qOfm+>xQ(gr#l~NBI4FaLVC06JN#%?@3v{IkBqJG8d z85xl*jt6l$1GtFhDNJb5bqb|FV*M5JY2#Y1p?QHL`h^oNryT*%jD23$xSZ{Kh>oJk zWu{uRqB1Jhk%~{|{>uq21Jr+mi8ele0-dHz@AagJqHRKOjOcDEfQStLoQa#1eGXuJ zzrT=-Dh|J)h#vSx*v`t^UxFnMvJN>mpdKc3Q@K)xX2Q3mm(t?Bs{D{Xo8TeV=pG5O zZD-0osj%Ur@ZS|BL8vKJpn(FO7n%!6s&@&ZCmz6F1lXJW4)8Y~MAsJ)rDX1Z9c(!^ zKJ!cbcBhf?#_z@@7J(1fQ?_uWbjV8nkk4MvT?i{jWZZ_DVEEGM+NyWbVHost6$N-!4XrR{emCwUz9^%khDEmk z0pIodo<{JrA^ip8<5ci%C5j}Ee_l*Tv;#$0YWJ}J)+=%!IxFNgmMiBN2<0a=O`xAw zP(nMf3@1WFRtwL6BjPp=8<74@Or8qT@n1092ky-HbQ8k^j^E0O!^7&0i8k{OehG)W z0GBDIcQLx-wQCMaEb|xK%1z%nGv_cV=@+b(UJJGY==#DvEw8;=L9Jt%q6d3t|0T)% z_wTbA?Ih?piu`PdPH2Q*Oa4|8ec$&HD6s3&K;5VQ-xx)I2YXAKM+G43F#6`%aqH=l zASx^Ave>I-1nVGcOJekiCZ8p7J#E3aN%NX%v2&LiX(MK)=7@s=K~WEa5St;yl@D9a-)evD-hKpyZ&$FdLSDVi34C0t~$?_VbYz9ur@?vFeGQ^Rv ze?dB~)@1{#3{3s@VF2IXd8ASl$jZ$#h_$iaLr_6~g_@7bR=- z2B!3k|I;-$tO;Xkf>`hnjFrz8$OELFmKJG z9s@}`AAQT3j`A!0&;NJdR5#qZH{Ib!)waujv}p*iMmOl9PTpPt=-U5%{KPH~0+~vL z)Rt2GTqnYDom=2y z8AZA^GtY96X6HH$D`RvDXQkW0n^PKmzFf8ASJQZa_Wdif+>S|wdS4R7@#uXlApZn^ zHQ(jG_`(c3<|ZMA!l_UO%@SF9CpD5na*C5YqPaw?WFG0Zu~K{A*&;5N24RU3#=kzN zI3g+D!*!ZA%5wB<{pCzlsnN)q9@&CR6@KyoNpVag?r?xQe!VC&>oK1Y;fya>a#p&F zKttq_E0s+MD`plb1uDAO!g38aG1sMkc;@5gbF{fnF+N65#LB@xw16I%Cx(KgdOm2o35~O0+S4%_+r}%_qxTrXfo}rG5pPStYn(+Uu0R zxC&DO4!A3cr%G&T0U61rN&;<;~r%r}yM@AV)&`86A>0kYdXH-{iljp_e56avjIjq-fAaX5?u zHpKA=INav<7?2vabw;Qs+E=j(_f|yval{+P|9rC%83;^unPP|of~-EVvs?6dvf6{zfc`Bq$iG6=`3R+g!%mu@~{+(3BHslaIa0ZMPAL?lnmN zn%W6ZuTNFkl&?;#`}S&o6|cX~t4Dl-1G0rNoBhms;94@X{rE*&!|eY%Gu6{xN8;bS ztlPRs2*t+0zCR2~I&X~MLXY<%IVby^y?*8&PU~SM0vC`{2``=vLq5)pqe>Mh?*O;~ zT$bMKNzB2pBY}8@uca6|UiA6D&(AwAWZ|TKU2@7g#1E)()Xk!QL7WCp7-65r$SNeY zB#QfJNU3OAGY3Oco8crF{Cvc7zJY{xz4j$(z{@rGA@xEB`eg22Sn~ZKD+OoJPIR!` zl54*|Ke4I9guoAkA(#z`wU&_aWGSPxZKy;rS-JRLJaVKN#Nj z#NoyMf1l^6P$9m_nfQYva)+xF84A;D&3Jp@1Om1Gu!3uDUNn0%U7qZCf;ND6LTH!_ zJ>R(>3h4JRPg+>z)Gy9G;GeoFpV~s>6Q!FJoV`l z^tNNhl0cDvtHeT+{`UV<_fIHU6j)x0GA9gW_UQEGh&%J<`Vg->@rAF~UeV#*z9OGa zDtkPfFKrghvt(X(Nc6bp-hBj#cRZq=9gEJr7nolXEkEwri6frE;iKQc=iYx2W^p{G zZr_EAxAjGvxMMAlnlDHwhF$D?w_q;QOYBKUEP2F#q3)CLkj4#YOOevt^t^vKMkSxf z?ogs1ctJ}@zh2H?%_I7vucb+!ei3_(kJrL-jMw?15a*+$7iE#T_2PJo>0FxON^dA= zdSc|hLfF7pP6_>4!pNd)le&2tq%^d4=&@v5BdQ&bs9hp4UjSmTEpmg*kjC`O~#<5<(C|qwGw^XKVKLtwtX}LBx6XLr(zM0r3 zKOg^czfBac-*Bgw@u58XgxcL|cH{sblNyQI7np4rHOO#B*l zsqgG0`Brr8O@dJjXg;TjxKl>rYmS|8#(RZp-RHpCb6ap`pEvrF+&L1n0fg1f_Km@R z9!A=f8yp`PvoQpgCT-ITOI?wLhcxEtv&DNxrGtnpkFYEl3|Cgv)sFDoYjbK+?^{d{ zh}{rE^iIjTB0pMNA{-|L4t2okX^PTz6=A7}26cl>zTtvj=4d z7T0w|J)4nCsJ((G;oq}dLZbZ)A?hWB!T~HU79L8>KJ*|0#GnmkuccXe zo;?0{QZpbJKzyy7|63|ns$8Q|wa?`nT~Fm3q@>%^L&_vSTm)&GsWkq7Q!1?fQ>re6 zJg<~E46vF<2El4}x1&-yK5$}0J)?lJq+`o8cPODNGOGBy8TP%<_0oB5>xo=HlEelCFQ95NAmePHVx?=`Qh#3z5HE zI@o4Xb^ETJY(C_JQHi=P0VKaQDn8wpTsIRjHFcysOP_GG&s;{MB>JuBVo3BDCC$3r36-6wH!@vU2FZu_Z#NpOQbMo z0u?s)7Ohx?6}vKnl^~uWJO0f)EVNDX6r@)qK?@4x?vR~gNrp)J{L@#H7natPgn9RI z1VuHBoNOkk**IH&=Mq#sBWgp>bw5pqp`67;kts1&c2;=M7t$pP&#VDV$v}Y%^bZQD z=uiWP4>#HqfSy_xrqO{zQ^1)v=mCvMYY4eVh<*o(%5LiX0&=EIoaU91wAbge{EV65 z-YZRZqx-}DpAni4;aC3Xo>ue@@}v8+%H&ERVNcknUx3noB#;S_Dsp)@x)Ft1VJ$uR z>Zcg7frI2nFhC)(`iZdv56_t`M&&yD=7e@V+44R^6GrpJJ}K8?kT-j!U+`dB>;B>b zyqITP#I2fKqvVN>M!gbino%1XcF7bOclXuuiltCLjsh?UtSgKXI4Adf zHgK+)2EZJD-#~#fLe!+bq<~U@U!F1@WfDObq1mSJzp${Vcu8{J4JEDn9Jk;y!9<$NAIGVXAg?!lyhWiFa#x% zMJo)U(vPg%jc5IK(Y_*VSe_#Ic66e&ci^>vYoTEN!UCY0ku zuk?-OlLNEv03HMb>icm$$n)sSm;M@IaoH1+fFK9oiDsZev=^1W*qlQuQy<@-si$Vg zELJ^#iY$9_$9)kuu!Ulp_}hi2SeL+C)1sAdrghG*X%8mCLa*i3yPn3-2k{7dg&dLy%T~TlcK>Nkj-Q+F>csf>g5z5Mko`wIH}1 z`=G*Ggud>BFB1p8NJYWZn@517bN(^LsobPf-C#t&U#x`Jt{X%1_C>+gnoO7V+xgQ0PiGtTbu=H!oH_%7f=-<&*lZLv5=@zIxVw z^pbYTi)Jhnf_FVJNX3PQ$af^Ae?29y2em?#=wqxFgAtH;C->B5mVJs)l4{d&kDZTA z)S#g}6aFrdgrbKmf7Qwzwm|+{%Rq&|E)Lda5!>eC;i@IB33b0k5O9l+PP*IfIxYY|lJQ5QpH^u1O!BULo;t32o&#+S{jyK-f5tctt zL*irh_PxN>hl3noN~EfqTK8ptMLmZ^(yB(*w5Yn6^hDC$UV7KiDo#GJ9O05V@oc&~ z9(^_uF&ozhRv+(W$xYd61|ro(B?0;1TKd_fIp5UQGLN02h;u!mIGS^nUfo2zg6&=L znXmg~lR3;=D5&uG#AP?dl=6ENWjtI)G(~F3VAz5*jNB0{h!`L4AX?jhv7dFC$nCZ9 zKBw4UANfblH0bF0>C7(sp;t$KtNYjxKzRGY46t{Xb&DvVBuxQXAY>0z~TCKpH@r zbQR(>Qk2+3yL`mU3MX5CW;_CGlzw$$J>bF3?I2*g=5g>^)|bFkCvw zH;S3UDf>UJ2%%QQ5c~O@J%6a<{V+rR3B+si+<&&q5$)^|3k$;qI$Harh*}uvuM1=4 z$D^Ucqw&1aM|xC!YAE1{usy@v_Mald8O*RaCM~rVDAkl&t4Rx-c49HfYx_k~~#qqcq z;KgzZdU!TnOkq{bTzm12#A5;CJ3?G!AfP7LNTcdqqi{0aMMR@QJtCDABY`Lvxj-JV zOgXp#@_)N7TGDd`M~^{GTu?>U5z;C<-2*YkGAxz{Q;PTl0?#6F}rVkSt{&ezi=3n`gq_edL*f5MB~vTpXJ$h=T37HM!esRw|o z;i$MHA`rHJ*w^5oZP*2AZhot#Kz5U%ti2+-hNX|?5sC$~6Hy58I4oK*;$-EWk`3UW zD#g-CZ{%)rmG~*Y!FadoEmHbyg8(=)2|J8-d3wyKYuRCklgE&45!sADR1_Z(`{OR@ z9c=AAqUPQri$fbW0aXRX=x$wmSCd;t5L>tiL9U*EM>)1W%m7SAyfi-Wb~Jin+wLvA zVn$EmRp^C#)(W!0c0Qjz(sadxp4%~?iQFT9NutS@%_UgHl{3hmL|Epy?DO=T<<)0O zLFVx?@KOQT-k)4Vv|gXO7FB>IZO_01=};~2Fki|8xx7EO_Sk74w0DJXe3X1VX{I}m z(jo#|pZcO2et!qK9TJDa*mgowYf8C-FE$tJ8k(u8|Dd{a?RU7+lvre+0Y_>Dx!cEo zZ&YqqE<7N%I2L3!VxjB{5!Ml3R8zMptJbi8VK9U@h4Mz3<-lzVF@p?|a|D@8&E+KW*5aW;)#&{hL+vVb#8 z9c?Bxvj5N56*MnCk+vb^4$T$zZTKMqYRFJidSj7S{$++7c?fzH*{$!PYcp(rI;FsJ zI0|n#eL;3wfb?&0?>X6%)jKxWRp{v!?W+r#6h8+_|3cTn+IB+8JoB^7LbYozj^w5= zmUzn4u5ZArrjHqHE;jqUSb7QxcAZgL4cmxHp=YmrinI>DXb^_m*d4geyPn*NhenbJnSUL63VJlo*%a!U81NWqLxnj$LD$P4Z;KfE**j>~9l5 zUE!MzcpWy7`PpU03f82d5!qotLrrRSr6GSTp&)}E)~9@b2?n)4d|M!VL)SJ6cVzcT zvdHH1hrvtdH#A;VUeR}F=={Spfo4{ni0T)zIlYWwR(^BVdIGX15`Kw)=0=0gYKnnZ zYxe)6&nvg-z`%NZNCl#B5hBQt;2Tlc+g`p%?Z?BL(B_lo&eLy5bg-P2j6CcQ{T`7c z^{^V9i?5RprH&!QJcG3f|JUY@Bra&p(+%&4A%E?f)XbkcEM-|3g!r91YU}KVM%|5J zD{ricS4h08_O(5g{sIAiX@-r!RB67aU(;A4CxKW=HNahO-f{C*+u)xR7+hx2u+TED z^ynGS0kj)aggHgIqG9v%WFvpH?t43{k}4KqRbGV_tY{ukF45hY=;wXf$TUZwy~vCo z{gEZlZn<*4rMcXBYGjQZ?CL09Q2_C++`%um=qxx%We_vHOF#U7+iOt3w$OX3)ewvZ zw~h$wWPP^6+xTLD-?}@nzL|*VaD>{xN&*TU2W@$Ya}Z*`=ZrgxaL*?5P~2IK6k0k@ z{9P;J7`lGwcS-CEr_mNr+I91fQu$v9LxgBhaFpzPI|-aM`y~OeH)fv&Co4sKop68# zAv;hfpw)tZNB)2&wMtgTlp&_3Emz#?6L7nBB9^D+ z%WM&SBzQLQqb8HCQxbUgbhvJ)ybwfNX5|y9y6|di5QexO?f+w4W}_I4qB)OkK=wZu zsX=nZ3p%tgOIb=tHUrll&@l#;hQpqRnrFidiYzkeLsu-hDI0y}IZ34O=o|_X7ojd-q+GN)9CN$;Ln|OM_OEY&6^w9pTlYnrpLvYAyu@f_DKN?*P3*RV*pOG(_3*sddH^ z(@BXdeW{{a4R}5HtD5=mG2BQwY;U~@UC)}%O2V- zTq#a0rL-_+tXn5f@XJp8FJ#o$wyIs~61Fd5mvwNqB}dl@YrxQvhrfi?K6M(kvTuwy zTU;1I&eLC)l&xZ;!fgu@mYv_e?Ls$y3)}A8KP%L)Yq_>pMTZa8eP*^YS=YnnTLNXv zX>cW7Ts`3_8d-n75tI}uD(JvR;7#M%MJO#4b-_1A_?b9v-PO{Wv+%7KQdZMgiB(SZ zPu?Zv@fDVw$^s930UR1t&82=AAjtZb8H`oCe1b`x+Qb9?!(IjX~ zc#vn!!t?SJi~w-_0BE1ad7zZ;fCh=(uR|iEmc&y8`)O(U2i>4^w2h0BJ)BCm4B2wt zSFWS-r? z_B$MQ@nRt^YVbWQYn>ZFLR&dx;{&4445k*3Gd9@3Fo=Z$K&P>EL z=>2!+iEjfzHZF-G1Fw%C`UzINcJuFsp&I`rP&Pvqe2898F*9RK*u@vgL0FzA^8-48)Wy%t9WV~MFQ=5H zh<+@Q;GXC9!h>8?9##&2jGs=|XUHZ~wcPyjt)BLYwo3J$GTAOpCpkRx%qwKhO9U;r zAHZO6#IZ`X6IKorV-xI(&1C(Jx0kk7VH0hS5gT32X|6S;HWb2fGSNlTFQaTp(rlM< zgAOeJy?0=A#W!_z^-Fi{%YPQd>Dy#NidMgkKgyG64WJHxJ&oWddJRHX$m^T6 z-&-C`jJh{kouBM%YpRZ z2EKI!Z`UQU`i|Ux9r%jpkV}!(llW8dL(36Pp6N5)T%W&HTd|Lmuuku%!mhu_ zWNa3~N!qL6GZe85>=UKQfV9)KtMJ%_N!8tg4VfkxBPfM0Tk0lzf7A-S6y^zq7I4`^SYVZ!Rry!eX=rtHc>daKH8 zrQ@VozWafH`u;RKo-p1aT>ifJLjhoBDl zH~5H0)t$Pmk%jNo#2<%_o`eb6v)JfdN8+5ZEJ+B5ZBkrW!7Uy01$NQt>Lr;ykDqz; z6%WCGBy+c^?t;JHcCd<1$PSFh6_*s1Fn!Zwe$7!%hB{|$lq(Rm{T@JDf*HgmpUdPE zAiC(_eZijsjggE>>fr3}&SJT7A2=%#n$%L~BD4X6doe6#O=`Hz&cO?Km_ec6p$?|J zh6whjwSx@l$kwxo&aTnW{t;-&_QKQ#W9IWrOtp!=CFy%P+ z!aB40w<7^-IwO3l>)JMNTy;^;G0z|lU{CT#ATpc0OX@}JggS%t0Ke)FS+B-C>lYpV zT!HnKsYJzLM*|vg@C}Z?V9TbpF`|JhCE_8J%E{i z8Bbn14=ydwaSqdU>%4E?ijK@>l$2^vdf{TIJ#%P2;mYAJV`%(y^j{Q;gqldml; zfa{3Sr&jTURXC!F!0kyT9HMWh9(1v8^Rz5qfsa1K--|Yktna%q1I|9LS&!=58+FAb zIdWEQzC4hhdQ{6|lIk7rF^dOm4!3-?bpeX$8VDw2MHUN}t1kQ54HX!H!(X!du- z^dzvf2V^~tHru-a8~P;z*$H$x7C6{#NTTyk&b=zimM65%kmF9eLcOV=_XqQTGjNnv7sq*MCokn34KQgXVf}hWfa8b0cOFV*o7SA3JN=aXr_{kb%bQ4vA4R?92$_}U`&+sA|}S?iLpBcDB1_S z4L{Ogwr=nsMj<#o#n|R*=rIaK_3 zkcel})s^Gk7d61pU!P!qu?w-USRG$RIKLi{?-w*<6`Vd>U|1zFHV?A=8S;qr=6#zG zw?%m4$IjjiVvKlI<^o$A?>#8C0g$A>Fo-D8i!bZpc2~(WuOxuEHjKWb-@4 zb`l}-g$d@+jm9P%O%ZziZW$X}-cdZ|AAjRchzmFteat&PJgonJAzA32MazpVZZ(6c zs4mmAT9rxj^5pYstH;zkveYTg-KW5JL^-oU2aqd|_~T*jl>4`b-Q~Gz{>{suPer77gv7^P;@159#tgzZ3@&^dWb5ZMtqv}yceTR^gLmNO0f zDRM_^vB!TmXik$ZAfG6jBe$CDh4q{dIOAOUyMY;Txb6;{Lb5RRi)#tw6$E-TW(_3( zh0yv7w#34Hba|&ZM3R%^D_bU@&=+RVWf-h|kWw(;ic%GSrw(k1?LJX(tbQ=>y^J(h z;3>vOd-#VK$2+Jcv%w1}wzi4F3AAu*Op_QwjT{N9kbN0eeI(f3OZZL1-Jb(Xeu$9T z7YIVV=|;yFbi`g~z2OtMD>M)EJjW&E#GS9#)wo|)-RI>kN8?BmzGTnU&v2{d-g;$A zKOkD>@yvpM4{(n+1w-H!2%v}*AH=9K@Td_K8^WN-6#|ZAON-sSu_`8li%Fwl!t9S+ zQk&4v{jfaa_naLOIcDH|(uVLahz?O(p9%<$CID%Jb#T+Oc6ir~?c&H^<9gYGfX_*z zb5XDb9!I?Ri#PM}WfMKsQu_GHzb7H{e&Dg3=1!8Iz1g)+j?3}w56spVOC%u=Ht!w(n+Xja<{b}J-p+c;nwH|f)iA!yr z@p?XQn=hxaUpG@fC?b_`g2mTCCz5K85LnF787m#zMr;2Up@zgU7>=fPSlQS9n4 z&-pi@AD8;QoHPIb{&H;8&$vSe0|)l1c?05#4#y{AENw6zQ@`lmo|P|XJL?k zB2^dj5I^7VuDuXIg*?C!Hhsfl=d$~i`9#6rD4J2Bt@_7)ABHc45gnL^p}T{f{t{Zc zT4lpv5l*!Nx`pEPk8RRYX~t%Ia)|8UOZUP0P$EtpHNPpZ0D@-uWwfpvF?KmFvV2B- zk$>@e_xb$_otB8TSHPT$gzZwkLt7PpyEq=4lV-?iGV{_fyMaj6bp_c#zy*?~#V+~_ z%UN<4F{1C0G>dizxeE+7KmA-jOdEpO0A4_$zmK-z3Eq4&G$Y35*@C#?4Voa1xi_*S zZQb(2aRRnw1Ei}mr;tS3ol_7SB3lBZ~K zf3=Yo^!Q4l92<2h#g=_`_bS(NBEJx>F&wsq+s-7pO61Z`;n=scxOQ*6HN!XaMKLeM zgD(O06<&}{yJZ8qV^%xLdd>~Y%vIr}G>VU>_(HmQW)O)P9yFH>yECBPl=JVvSP;VS6x?Pe>fi) z>~*G-QP)*E^tmyU$=BC&B2wh!Cz|ee?%r~+P=DaV5)a5iLB<6<07ewGog;ii?`jT} zM|uXEtJv}SJf(`}ASbURZfL{-BRNX-v(!(m7{P?5pLk3{?hz2j%0Bt>hSo<{;1FV% z)pc$qdJl1GL@Ay@IkXdIF5KNYe?VxaIOM>RZ2;hjwn1(nT8g^Ounm3(b`t4;y&bn# zInq8nt-FNC_E*}!=VKA0fN)cAe&_(P1_2A z%#b!GlSCweC*qFeRKtiT;up3%bht;v>O1C7dzB{JtE1^FOx;&4C18}ke@Ft}=bj$> z95pz?{nF4BjS^X+-!24t>=?#xK(=f92(LUrp31^2{LATD4p^B7DxVK}RKBqOo=$@0 zdQ={CM^x%tJSt;J6sp2c{rhlQiq>^ydWNog5@ud(vuU{%JQM;%Mg(hK)o7!+!$|zn zMOZeliN%esq}xGekCx9ge|(CW=F+omp5l}fT!~+ceMgk!2*B6=6T`H878{`ezTv&< zJaCn3gwSyJT2l@rUzOt@d5Re#iVPKSkGAhi(f4CP(B7~UN-7(L6 z*SK{k0}xO@Yo=pu^rxPtIEVhYHC~m7P?~+A6G)U%++_Fwz7;9JeCKh%-?4Tv2PV>- z6D@=UXej%PeGo9BF%*oukxH4N8?H`}fxbDM{9#?J4p836P_rNa@_Re-c9k!XpFkDt zZL1P&$DTzD^B#z$e_-8ffsY2a%>Y;rlBr7~yXMMHv;dfeX`yIKmX$e(OJ_zvV~S4P z4!np@7=1%BH43koy{(x9XRE8}B>lRxO(kv~_G1cRV8zh29oetXo$KSl$>B~`;nnLn z{Tpa)Vra_-WZN-}10rHo8hvr}3f7%zV$K+8)Jgqj|6{!Zf2)g3;d%zjP&7^;pKixx z;iuh@JIUV;|1J4~+;ilQIvFNV*gdoea|w`|61kg|O3G;by?d^a0ZxBdA;=~o5f@dsUu(RpcD?tQ1oYmK%Gq5ju9ZNN5 zdZw=>sdPk7!j#6rO*$bmsVg;fXEzv%Xiwf9x)tyA#|1Zwn)P7J~-(_92?ouj4S6`(>#&TqfI9Oe|%=UzC4xt1QWhB>A_)p6mP~w z{luG}LO!C*Vf39UD$tWc&G*X+xHfcT;LBKKHBdvHj5l-jbczn|peAlDY=}3+4~oy9y2%J6^QtWs;Q0g+e_8 z^`*(^!GNpq#{A1e>Mctg7(sQj8XITSCq+Jte>W&_0@0zrLK6;V1fL-c&grE7J?7!r zMsL3#G`*6TfrQC1gy926+b0Ei^cIdu`YUlxy#s)W8{uCRqj0Oo601GMD&(7m^j@F3 z&<}&=5MQUcE!!vw;m^2lD_kQ_t|q|fqFfG}*z^DoyAUW`Sv^W=5oW<_4EmI4p8`Jf ze?^7qUf-`3;*@A$*dnlNVc(m7?zN}=)}5EZYlVg-T{kQ=a)x}#oK$hy-H?DyhKi;N zGr2ReLHhz1f5@R=RHMPL)2`xg|1>74b`tWNEJrhek**DrB`BR}dGu&5Fd8jAg3)8= zUu0X*m(|tLbntI1RYa>N*mK-^1Ve}8e>{*^3kp%|ryeaES2YVJpA`NC(r1mkl%wae} zlLsgip_8y1Pd8mNi9P}8W`B?vCpe+^S?_X|;dm3V=7MS?pM@Ea#4 zco(5SwU8N9kltBa$V=ijdk_P~6bnBt^nsHdjt$~kT})77*X`3EX-A6fo%vxpz}n-& z@)_?sagRt3eC? z0YJ1a=iY3x#8nuXpKFade?1}@?<#nk^=NMzc!g1KRmE$x)|2A2*90as4T09BX6m}* zLtE!u6LA9+*7TvIa+7WojvY*-!+`lF1ri>{_q-+~!orMb;Sk%aFx=aLy;Yh;Fc}07 zLr%oW1w7T`He@Kt(V0yO5i{-+pKzwRw{Na!u2U2m#Vqm0w;o6vf6O-f!8uK_o7dcX z|AB0Zlk!RDB$L#*Qi-89%3?|+Ms@X@g>(Vur4vyG+4t0ghscu5ofAdQjhRy@sfVeDBNLb8B%+8(JA0B|97?M(j zuzhKn!ZE4#B%H&1e`_r%;3)Sr1pyc&Tuhj6sPPV)ZJ>ptOO6`uZI{F@2adKB#qJy-WQZ)f_gI&%lrrE7e>=ED8Uu2%)LTsRKXoK}sQB6v5 zcCm8(qv)i*2!*31^6#S3_!~_uv?bJQy^BcMbfCuTnrfT4e~xEpAf=jG1zwg{ zyqR(+NVfB6In@ZFpYhSL@T+7CuQ1e@PaS+GpHi=M(pm`Wo#;Y;vWh zzRfSl{@PwPb7}*s+C;6+PU7%1W+AOfFc)j0af?fVN3@?@YHJCL{Jam&OdGxNgU0WF z1L|dHhKo;zS?A0&Rgq|>mLmRLrX=W&>D(8oNp5~8-Nz3fN2hv~eqU%pGYqt8eycmagv{v&(Hp5-cAU5_){pBtmy5(+Y znom$BPQ>j1Jb^RMRgo1*d(jJWrl5F4W{{&&VFb7NtO65T69`k_1eB8b11CsZEh)s; zkpa-fb&od0eByQTw*na6C4v~D1!Uqi2FLWNpAJ z;u3fsa}j&}mg{rdgY8ClmMrenEcMC1KUA6|i}+5lK)}=`*n8%GMo)aH(cnGvJ4Fre zi1pt1R<;Sl$6+B7?-{+=D1s`7BXT0B>Wj>KSu0~f7`w-W4g_WEHt&UA-pFw%_N-?I3 zajMHVhsa`Egs-9=;u5}zdyLOM$ti|E8D>er%z1f5<=~*WT6{r^l7KBGfSi&AMmD5A zD)w07qn6?cyBPKGhUFN5*3jK(O-n zkRJ47i;}HI05lw(K>aFdkWTt9qm_12pntJ$+4+*?SRCpv?Cd+5EEi$$dlfXye`MW~ zlTt$&PHIax`3Rxt@-6BVWiMJoy?Qi694}m=VV(;{qJ-evY$ibjfFLkv?JSn2CWPJw zFp4xM(iElFX8eU6we|#9a5SsK7FbNn!%yO?n$gL@eQ!ZG! zk@!zCoROwO?A4`lyOJnxxq~d)fBgdnDW*IUfa%Q0cBq+{-2=voZYFfKlM^$spsY{c zTnLA#D_Rt7!({k_r%|W_6dS>+|ur&vtHG!xEqL6@s+?(dMktj5P1WCI@uVZz}qgSR%L7b)6M$ldpu&^Rn~-8O3AY_c|M^vM(c)0`7U{ zz=(GI_r%vGmQUx^^qFy|Nz1h-`>gJZr^qHoyO%8M0jJEVAU}kje*>}pK;*?}@)gTP zE3C*@bE2hsFLBDZ{%46YFwQfq0LBr{?#*n9af$ze3ZPHxWh>F8YUNB_iVO+$TETB5 zgi_AXrN@v3!wynF;{~T{Z*x?K@&t0V+>0SGx)R*ibK;cC;8^2O-lP$!)8n$fzC3WT* z*oVeq$$4vut^rvtfb}W8+anX0aOEzfYwvkDFwAl5%_Vk6L=dNY7@GkwCYcr8U5UcB zvkTPLy>!=m@2c!V3#Qjd%jBNOD2Md-M@bOGw?_6|uny(Zf7Em@5){it=w5yhRD}Hr zy_hA|ZJF3fzgyZ5PLsI*nL7#9lm<8WjZCdDU|gY@O)#OYYI%&xHUuF2;Wa48E7}7N zH^z%qX4JF=!Mzclr(QW1C16sK)Ol!=Msty-{hA~4<`qe_RCduY^O)r3vXz~obTz@C z_)HHjE|Uq(e`MI2?%>>pDY0qk(N>L^tf;1EmU@bMc91+lqLb;AO>~3Cv%g~D?e;Kt z3H0hEnKh}E79H6krDvYTrLVa1KRVfmn>Uvf}M4RDlWKJ6IKf20$Fp2e~DS+Q(XwPK~Zz{gVy z3YvKVk#=JusPiIM(>=w*-!OC^+hj!q!VbQfVDfI?9sZ#t#g z)cOTEP2m#Fx(E@nYrXS*a*yu)07AaWvnSgnqtd^N1N_lRRCl>&vOzZ_U7agHDrYsW z?2T*wqI?!_$l_o*8%YHwcSxy0btlB04ZyMLf0bAOV0eC?N2}6$2g8PF_k8QRz0Xq@6 zu=A^a@ePCZ@KfFr(kOGUr$y~rBN-G0bJZ0>75e@ ze1Aix?Od~|t$c!Z>vLuD!ea;V3v%W1ucWrQ!l#TL z;AJm>RbfVb6noN7;#RF5zTm^y1S`yoUo8j_$@%ISKsw#KGkD?zEJ1xu6gd=znZXN1 zE3CS9>3L)-q25SO52x?MOg>)hniP2Ce3hC)aqK2$=!z#AhkGd3dx0OqJC_nC0v zM>#R|lTZ#HGUF(>YNX?et0gDOf$Uh6nZn2sUBRk3G2%z%i!cD1R(hx8%_bxprYa{p?DNpTY!Ge`>Ld z*pcfDp*=Pcv-Fq*d_O!AhrGoa#J1E5!j|btavAT3IDD+NtW=y43c{4AGv_Np%n?JI zm|=~lcSY@*+2Cz6I3fq(4Mzit7~+Oyl5)Vw)Bsg!(xA6$MH4wf!e=tOA!}9GBb}Y4 zMru5CB>4xlG~wN*Xk?WfQgIssf3v+R&*$_8LFe=A3Zc?8W_zxCt5 z=GK*;)0|*Tvx;QhlsN0-K9Pps&O?sBcQ|}E?Y|U%^N^$ez>lZEE6)D{^Rk@#Bk9V{ zF&{{CU!G65F9W{<5xel8gDH5|dlGMhJa&;sRW2Ca63NQkz~0BW;`UBQe|0o0L5aL0 zZVzUC@d>*a2IdgQ=#+r=F1kl1LDxmz?Y4m74fhUDZ|!|tog`~1Ter<@V!d$(+(E;s z=`7Vm5>|txZr8s)d&y&m4>ksbNUuTEp535WM-be+;qZp%!W3?hcyD?;FcDwYwmh)E z+UEvz_djimo#YZp6fcLia0U3VM5bCzk~iq>b1@4JBU7kJhzX zHkvbUq8NhMRO|YDMDZu{>$0P6U2Oe2^&`u|QnpsruG+U{9LHxD^ZXb*tV40VL&++t z6-2y}d-0@x^w|sRf6F-JI_^JO&IIW|>B>)%9%^k^w^*U>vOBmz-lyCyuGkkw?MZZp z1I1K#{iEpH7gtA)#ocE-pnigHdT9L|RT}W33ubFN3{-m3t93AA#LWZtXIkTdqNb&ABe6Z#DUW@3h**h`bh37~JU8!Dp zw*Q;WKHq0vlkgThb-pC}or06tSZFibxfT}cOzXJ&ICVo+1dXL%uRO@%tesybU_p;h z1UKO0*#YcJFE+!Pd5Ua1)52Tp>#`pUqv(i6JOs z13=pDW%73aQm&cG+EU_A@8jgz)t|P#^y{tq(Y*TZd_>Y;SAVtqvjaEUXctNRTHBk) z@sDGstR|Pum$%np7Rnoelf?T0JK$|*jv8t7Opt0N1;G-=?d3qh4etTpbp9QTQCfU{ zJRhLge_!-Ee2(N1I1+Chik+Tr9f7S^%3Z6F3?Vls^%q7B(fz0Nw9nuj+Io=Gc?-(}Hp5;64-h$>@g5z0=N}l19*2jR~ z$>YKPTXMiwco0)YZNX*Y&}mxzWZ<7S2=mP?e}>F#Js5}FF#@^-W895#qcv5(f>PV3 z^}BiJ6@ukrVcK-&-jXn^+2^n#W;Xz-MIb4O*9A9RXc)3g)9!@tnAG)`=OR89Pw0z= zoM?=YUHni;zw-6hUE9ogLAkLNT1^_*N1lmVHD|_fBR$*uWXR;RW%SAPaleP6y62zNt6NxWx8rwnXGG*gd&e{mLZ8h;xU%_?uN4Xx_P*RYr^=1%J zn>aA(#kusKWiQIP=|tqvm58T+1&nAzY;C10O_t^bTVUYJvm=@uu^2}L-JLntvsoGx z{!>R4k`~462y^?0Lz)A)3*-;nk>?D@f9i7$`=71J;p*q^(S-~##3cbx9s*DT@mVF@ z!w`#1#OhRJ&M zvI)O8@m?1qVbsp$4eobGW>(z3yga4>UaV>daCUdc)D05*@*QO-kxQ70*FxQ@U5GtI zct?&?ODZ*Ayp$HY(uWV7f67}bK$C|>Z(OrC ze=1GYYpn$jO95jkW0Qeh$U-)|Xwx$^q>F{UWbWZP=Kv)&0Oef|H-y)9(9|hM7v}N7 zrM_>(qh`DYyP9USL*oix5#{jEzA%I0VmaLlUDpySv`)ZEnprvX1MZ4uaD#tE$s)VP|WDg(FNND(CO_|2cp)w|n2czjxnz z-+q6;E8XvQvPm0gxdXx8m!v(u2-$;WYOm6tAu3b{U-stBf7C$G@>3idf5<7>^AE$2 zQkKLEoE7Gph!Rdt7$PVjc_0cVD9x`;*LX$Kt0Y)V9(9f<=@=dUavM88c5u>U#hIIN zFg?gdlm9rQH!VaPJnI&Pap7QGU@38kQcs@;I8uD8)eJ*Zy~KpSdBWKCUDM~jYHoPvVNTG9BsiF8@ zX3V3>`uVjdMq;e|!Vxi<{Uw>=)Xc?`-OZaZi$mar%7RMAvzUjpJdE5qdranjL5eKP ziC5F)X5rty{%u`h)+_*P0t2r^FfIPS;_3eau*zoee+o1D+}F|uhDJDxv`@zt!pGD}`eMYDG8Ift zMz;81hb&-4UH!r>^sq zmfi2Z2k{y`!h8rs>EJ{2_G#?JZJKcj{UPfBJ?g*w(WeGjm z9Al(0${AJ8-t6ox{xeS0Y3vcLUW;}zci_s($bl_yx)SFjj5r+dHm3t;Ve))%d+o$v zWQ26e=lK2c;n*SqZen-flSnw@d1Nu8%MJnd<|fIShMt(0g$f(~K>^pu`Z~29a+BrN zfBmBKJ)=E)7ybi1jC1_64d#y$lUVWvdz8%G2aiR<JT@zcxhSLeokAj#*J0^-6*V%0;^5S}Ctm zYTZ5dEQ)8XMa647^ZTF~jyV|AMP$;{2IXRhO*&CJ^dE@WCD$ra9D#)R%G)Gba6t}8 zzPlHQUW18|rw4a5MuCcbn{#RL73@&dFg7}apW{Goa4m9f2=fs+T6=&M z3>GOktuIgB5YKz$*l@bg$(dquc<#xP#dh`WhYOAwY#Jz|L?!Hps>b@1e`mF9_|gi2}=@u z$i}}Y)xK@9E`oggNj-=|f2#7s>gB=FKujIYIb-t!P*6=Zh8p-YA25Sfi9p(z4V3p{ zl_(sN&2znS=4jyFyg!CsE3AJRDZLz~0k%B+o)OAe6NCY=tuFqU`=v*lmpPK=y4u=bkH5=G7Skc*!ofYF>JCvTB!OkaJ3XgUxpf3D6wYzg$&L%&^>`Z-k!8OuV> zXGbAOkFT8;t;+Ryi(N^5^#-VvpshL7Si9-8c#+8>b->7u$*L8+L~ zJRI|&d4lr50$%__#X772R%HWHuw$eEob$W_mbr?+i9VUZ9GGpt(8UZ-EjQMLzL9IP zMp-S6$(`}|+!r~gttgIf5zImPQEGZQG@l-pfBd!MJHz|DD>F%BO+DoUaF9(JgP{CL zZplUi95wf*X-zHZZ*wHQq-iA=s$poymJ}LFKwEWpl7$NQgAo0I>j5;$lryAem6>7k z3M>qIivojP3X2G9*H9&yxxpN1a%Fw_e4pN-a8Il5t>)Src}>n2)sDWy>gp%_!m`2R ze_GqL|1(uI^b6`&)iY}@x1_LEX>B$|6o(x6`+2Sjtg~DrU==nWR_^QZiyhV&8=z5x zdA+3}ZR-}UZhJQ8zmi6JdcT}Y>o3Qz>lu7wk#7xraK>K{01qDs>0HQ$k+;Hx{{NTZh@K=L^2k0{Q);; z%){EU5Eo;FeB~9iL?4CHGqE0L1XCl3GBxGjQ>Go^7L!DkUm&}gqHzSSbbLaT7`y%Ut(sUTwb>3F=4eK zaaemzxB+;n`4=S4>mg#?^_3*Ge}>n~COV!;EB9qh1=Lm362a{e)GsEHrLooa*t%Sl z=tcyLSozitBrPR6wFb1-&1u`)HK>ktZq}qG^w{d;bfm;yd|5FStH>6w90$xl;NY-^`K)Ae;dL^C~g`= zZQ9d}+YF`c{xZbU;&T)8g1p!0CzynJ6-@_dju2XliR1L0TS8!Q^N6;&lxwM&YpZ>n z`xq)pL_}w9f@cm~FHz3P=>#zdzvx|gD;3hevI-Zr(GTqlq{=S!_5AZ=GHB>ED7e+E z_Sw)}>?Ai79LOB?j5n9^e|F0|NOffW=^qF#&plpbGg+x8K0vQeSYsOQR-VqakzSGIW&xYmf`;Pf`vtTw-dnWH+r7aEjv zAxRMHN%6MaKPtH#;yNX$Xx5<5aXhzgKXU@fastb%{tqF?3rAb-{a3c=0ve35|}W{lq?T$bClV6NAIe z40F*3G#_CTGOT-lPN@ifAjnv-KmtA=i#U<^q2T6R1t0U27k9g1NKNKVX6K%rNl;`niJ% zGLymhf+fYAB<^I?ETM+i`KyJ^FO-^GvpT*fI>ij@6m*2p@`~0^j6jyqu{4Ww+wrxh z*ve0@M>@q?dIjmrl`~bz{&K~3D}-|YicFE2ig}=%A}X*ieyJUaIt&96g3qoho!&rS zCr1B}Li!Jie>arw_GFkt{<#HbILrpG`sL}bTwDw!fnRRcTNe~0fotHHvp3n4|5ngg zEjZm0%{`D9z#pKoHbX_mNt6EpZmI|+(E=Ia3VzsyGz@-pi&&52w#RFl`>$bBaoS5F@sp;01N=~De`?&+_DTBf#NH<|)+qSh{?5a- z@!cS4G#4!C>B?K?|3GmCe#R{2hl2Ou)d6W%q`obN@x)TkZD3tX$W$ae$Hp0>7I1D$ zg`&k2lK6$6Ld!suaaJU1W#Uz{C6}6JdmZo#ys(9q#cNq@r{7HJmK-B)46si_GF*PF zV5LC|f4(Q1;HG{I=tF?{NKdAXY_g4M@lGZdTUKxatMvUG!a z9mNhyZr>)3op|*7n~gpvZr)ARyG)_Q8LCs$Ap8IZ{zyrnpl$<9Cl|gbyfj#xDn-fk z_oYZJI~FM|V1rgBiEHe#rk}Vhu%DLOgz>qze@U2AOEG~E5S!A*JH;p@vSa1;CDJRM zGtaF(bPGu6JrDt|1OL&kVvG3g`B|&_`VzKCW^@am;^9unu=b%)!hXC@mmLF?9bjKT(~)+wNfSzj)eV)!iWVUNAech zx<tS$wJhk!T?P+CCWppM1DCyF?OKt}pnFxdk?BZh z9b~mKF$H-AXhImC!F22%e*T`rr`REZe^*0|t_5gaTwRXnr%uJl696Pr{2j;=8>6x* z`XlYyt|yMb*n!IM>Bd(WM`7Dyf-bT$-K!YGMkK|XRpK_LtLT+1=|4cY74r2aA~cK7pyEOfFiv`hD$nPMllmn1Tx7$P0~^}Ne}PB4 zQRNko$P=F!YCYL&O8&WgpF~$nIZXt6J~Q0{FQi6D)kyqR$YC{*S;s-b_!}7F*%`rr zYwV*?YaEV(I$w^@K)r|Mw5t*oj@X3Zkw`6d_y$}XZ~4Y$bVX9bos@(YpEDaR+Qbks z3}nDeN#%qnVhcEcgwQQwAZ!OKf03ZNMWPHE5gg6pe~Yt&CovLky&XdtKc)v>GOd2& z#fjVZgEQQWojC~*iyJ!&U@Il6Y$QpZ_NH&b_#J4s5ze7@>Ev@D z0|}I02k9}B`XFD%5VDSg^L)>Bhqcy$3No@Z3n_W=-6m2_a>QSkj-YJff6hM`QGtN0 z(__4c7%wT(F<=}~G?LXJ#GH{3;kH}o^abg z;BaJBX}P8G0{Tf9OROnX?!PWLbu`|EKQAI}61X(aLL%1C;W@Y-%8b(z0m^B431Q?e zvCydoib`EEL?ZVwJuXE+P3|DM8r~%;$pVTw{90MrlDlkIrK7$Gf973P?S~5z__{+o zemStd?~u4D5wpFAR{7r0c?StUZ>~tko$WyBLFvoa+k=P+%u?aMpFk~s@w`M0Tf=y%%Ku9sWFjhfO zGFCobB*Bspa|F^&e})`rA0HZA#Rni-(Wr!wiJPLyC>?nrihjRvNtc35?zrUirY30| zM?pws5VP|=+WYe&baSbU`zP^Y84J$Es<6-WKlC2m=l2h8Q<48c?eA0%Z_W39(DuR0 z;5>z>(=8kdPurh^tVcMr;$gguN_rBc^ldubn#k50&naSBe>i}xl9^c=+}z9~@@(!8 znM0Rn(p-F-JK;7kY&j|V5$rddbaJvGf!lAH|E~uWhcdqWH@M^2!HS6nrriO2yF79- z<{M;T?d+KX3WWzT3H~Lxfl?7Q*g?y3Bw8kBz^)BR5?`)?We_ElfnCfw{BHuf!?S61 zv>KpFTM1a$e<2t#k<<}n(E$Adb|)}Y#X?dey4;E}D=(ODC6d#LE)zE9zwFjy0kn$^ zj;023Y?_&^$;ZkPVC*Bc%Qw;u3JP11h0dELyhZ)skxOsdC`_kf3pRyY5`wdC+zCOD zyp$=tc{Tl_)4`8ZqE>j4(*FpFU7ht`cu)nx4ax|lf5UD2iG|q3m*OpnKC&FLoW9^| z4)P=kM%`ph2PIBTtBv44uwiWeKjm*J=5n1R8(;}EoAywofMDKTg7ffjxE7Lfrmc3!fj9TKRE zwv>;K@MLYw4e--2CP2(NbaN{`<{QrqDwtdIe@gSwF5-+~y!)#HP07ee*5Vd>=zSYr zIrj)pErafA43S}>qDG<{$-47DIAm7yQDl*ht>%`T)k1I#Sw+A*M@6aEEwRiK4*zB_ z!SpXjPpLt~Sa~wm9->ZfsJwkrmj?)Pom;lOC!N!yi$h5lzhUY^g*N^xesfZv1!adziv{XU8Y4miQwr#xar;GQO-|(O z;|Oomr}|bt5{kt{R0C7q8-0ol{xGAde@qVo&8*>#0_l8kapgkta5lw;@wZ9120age zQ3;2PXSPX+gciuiVNF=6fqb+aPA92+Dj<3Wk}&bXewFDpCu!wkgWFT`4On3eT|QC` zcpGyEEMrn`wb!^jEgn$LAwO~$b`{pywb9nix*!*&$kwsfVUnMNV=|=ayIffgfB2&t zZC{-Tj1;%s(BGhiB*)2whmL^^JMY;?&|EjgIL`24a8^M=Ak4 zbzfQ#;r>STx5L?gAj5##>S3;%WsE~(p+>oS8y4VtA;bL?8tNcdFF5?L4#4B`YFX)a zSpx3M>`6+5yaTi`u7)I{W!05{e*~)oW1%?z1w6abX3{&qK;;XPKNDlM(V66btd@rv z17oW>{n~kQUa5s2TkK-Q+l?0}_C?|C*VHW{7R2xsmE!s?R7U8*5f1dFs+N^?{ z2Rjd(nbV2-JhF~Hd-Q6-_2WvU;zvuqUN6c!O+UeEqv9X;wmX$KUjwqHw7*Z6k1*%o z#}H!6ab}gj8lk@4rX#kF20u7Mpo#J&)*0F;-(*JU-yYk$Zxy0 zgbjjo`@){MW|0Pag`a4nf1ezN1_){QgVEzazWpuT*QP1a(p*B)ueI&U)<(XPLfyJD z(2t*@8lYW`BSG_&GUFna zk3uOs^(i=tQep)Bsx&#MOVMnb((h8gx7!a-s`xlBK&eYK)lUgu~zIc%y4C6UJf*$Z9WKb_Uy)DRkIxIAHsc zKVG{@P3n#aC(T0bD7y?c(Igzi>4S}Kzk5riSahp#@ZWr2Ha@bL@9qQaDQ+TJlJiTi z_0dUB@a$vsf8(%iJ$O)*_Vv_g#@hnkOh!hASsAk8R>CJ=8zhws${zYb&%fjPp+aim zDgTPcHl|IYR_y=Cwr#~ZDgS!MX+k?*$vbk6&dXunB|pKj=qW`h>zV`q5IM(#V?L`F zjemnEi*I;{ulJo<&+-=!kLJ5wib{WrV@9Q$<-?L4f1m6NP!Y$1oyH%w6V3=G`jIM4 zccH$DQ=a`qn#iEr(zk8u-G)n|)shXjNVS~YG-J1IU<#K{&FLuBX4{(_n4I}PBi~h* zDzx*?A|JPt_6WFS1yGcoV%`;6_)oHspHC#)*-m@3q*m|g5rO_<)X@WdS=@xlCjYSW z?Ks@=e+Rg48xKCu4Z_0-P|=|M;%qg@>}NN{r4pd_L^_l!O@l}cv}Dd$CN-b*2x{HR zeb2t2DuaQ41aUrR>wJ=YU3f_aT)Z6PkV4%*n_f;#N;s!dV5^&n?|GZ$VV}D9NUz=y z_%;NNytk~OutfJm&^jL4uMSHZ9LZVMQtNfOe}`|P?c4T)(Iu?~!ci8q=>rzrQ@{^c z4(QaYoZWvAIoX>0Z(q3&5YG0nU<+=8Z_-c(Zj!Fv%h?;SKA%$Ew+1XQ?JDjkuM}Rq z#m4oyQ`YV>P>*#;MnTjlMJ@Wb7I)bW_T38aWiK~$T5s3GeUA(C)_?usaS@nL3rj(i ze@o0IRk8desvO_iX1#}xBHMH(bEZmwY=r-2=%kL_CAm z@PqmVce4>!GBbu7%GiV}y(>SKAu1S_e{>$p-ucox0O{q=@zVBF4ZSDWglG3i=xgFo zU*Fq2?}fBb7Z zrSkSw{P;`)&H468PApg+L?>3EWdnjjZmpN&!G1#*?n(A&&~y-E1||o&2`Sh}d=M|Y zHJXIH!>sf=rgktWZ9cd?*G(?loXdCK+HaJ99ut~eSbMj&dfG>Ee<7S zlSOBnT144tktk5dJ`* z^Z;uYQg%=%*%>Z%6ge*tP#JF0r{R?Jy0@KPMK^MKE*)fKp&Ho55OJV; zaIUmaiK03{m7Ii5PS~$Hb;twyL1o%_#;mv1(H=``c4{EcvySe*iC=2PPeX?ZSiet5pW=3DwYkQ(<_F{20l8Ydc5FX2vLoFb11~ z77&*a8R+A7E}?kg@6}*C5Og>6XV)D}v~;g!9h&BT@aWHZoEp5A^s(gYmqLZ0gVNrR zXR{I#FQU5GM z7VGmDj=v%9^$i8N9;4)hpd)vL%b;bM#hj#Cw3EDna~P7_R6+Pqm~FV_4{l}$YB7hK zp(|`A8(G4ZJx&KxF}1`LHwbNuS)Y~|8k|cfa9B)rf2tsA(@-YPAY|t9MIyE#pIU?I z#4ql-qtkmDU1|PtWu%X%5zC@o3E*u5m9ogVX#<}al+PhL5||b+RChxsXoFUTk<8ko z7@xwhbL?S~dJ;38b*cM>Ks*YFar)=?R_$jDlz5Wa{$^xsd#cXYZ1*z@)u(Yy?v$9U z#E)Jqf4?_@a7NMgvm!Ic(=f9X+d@D9NI}yLG;vhA@mIwO*)=6LcHk z$ahM9FJziA>XR~)5~*J>LW{Z7SIc{<(M`P|fn^^q_`R@*r?F^fcY^Xgsd$g1=&( zC%C{-9^{8(;KvbE$Kz3J9G}sSD!jQ{yFS3+%|L9G>rFDp_5m}1%F(RSby`SI=rQAez^`XBW+x+>n3yrkR71VeNSfMzM)_xreo+iw3>v7Zjx@#-U~gUdSQVAmT3|SuaAlaX{Na zcMo5nG%-BMH01xwwq$-rs1wQL_)%QgqwSA#{q|tpgs}wp8-=a{!E!xbONX=JchieV*0qw*Cv<%;1K{6tim$DG1xZanlVfDneH)ayK zEr)3hkZ#8ZfL{->yN{wsfkz}GWO;Va)p>NQI%TI?0@z} z1X_Mh?d45qTF?!A>OnzwYH^IJ((Qt95c z``R<=p8d6T4r*VUVh zyL^AwjeWILM^HYa*163_#Hz?2@y9xAq@qb)4E)8`BOV>1&TH2RyoYJzYR@Pd7BZYdT|IBmy-$lMO z=_zjhw>AHt2R1F9J4ER!T(m#KrxoNqZ5i%sDE0>m`ieK&^PE4{4<<(Q&o^a9OG-3D z;pJE0kAg`xn@dBEh;dIUHB&6+T?& zu4w7%GkFuLYN}}*kAJ$Slzv0<@XaziI@?Ti^J%HWo9NtWj>zR%9M-C)rOhE}ywfZk zOA#7&mX4$(3oT~A_b;<@UQH~h{S?zr&!(z+@5!T59b6f*P%v-;kce(EiZ_Tpc-~oYg#P_9KI(_wYL=z*>T{rB!E*d%U@>GW&=*&NoeV02;JEm^RYgttE z@QGCgP*t@Cu@&kb9C5IW92D1p%+tdfSy6_GJ$CTie}5Wt$DvJ+I*MfQ+wA73C7!_J z-PAd%etFv3qw0>Cr=!+6r_LOu&?ml2z1D_h7uhpNc<_mh`ccmKb?!^Bv14KuOS?&)2Q7Nv6e-nOJOtomnf znN2s(c!qZoH)UV8Sve=tXE+R3lBaxk+!z@9E%TceI-0tpv$O$;`YEaQ$2XeOPX?2C zHKdH@b>ovI$UM~}Fn=n76FwxD)2sJ^QA+jMA{64EQ`B#1 zRHie{Ls*UV|1N8Ft;?5ARO>=V(;VieRr#5J;ddBz9*3LwroD$wKK5tUp5oL}_(w)B z#bdjFu~=+OcW?D>9glI_{fmuZjqU!{{+)L(PwiW$&3MX3=i>hhKUs#$>dISrx!h#z zw|}Iw&zaUajA^GA5iqOQI_PW$w9 zyB_>SR=07X$|W|={uO=ik>=!R|2;zd&wuso&UAPmr_=Yp<3X#?Z|`1y|Kq^6+5T*u z|9$B6cGBH0OjmceTF&E>@Bd%%=KPo2FZIYd)B3y_%#5E?^1{kB{aG)^>NE5|7h>1N zqI3QnMgM}ksnUhU)opg#J)fmjbMj?-QO#C(KtmERgj7%NC8}m!@UoC~n53C?u%^o83RBr`R0b%i4!;i3Epv zRJ#lRg>s_}rKc2FEY}Ui5OIf!sRw*F?1~4E&&!3}A55HP z4}(G=sxHI!bnczYA+o7N1~h^h4S#{nt6b7DQVYmdi+FR_FMs!S zqker+vWKo-Q<5G%9d73fdD6QP=Z1hXE?QOU#ENkEEGx#b@zW(EgV#d<&N7)+$WY~H z4Uik9Xt1ni&Yijv6OI#gNlr)r?b4g!(MbSzZb(y~QPhMa*O%F_p?~)MN*XDbqJmZB z8T7#d@VyQ*Z$v%vq0J%b8pl7|xhWpGr6?e%>DAd3^;*|;J-a0xTbOcND`Hbh4A9bX z48sQ6>D^5(5^n}gX3gOGm`0OHP9UNvpvQOA(qN}kBH=Nj3$aUIU{lPRTYTmUzP=p~ zN~+l?%GEM=+X08=vVYlflns&7Cf4s9(mQk+Uz}0tI6RU|HxFnU1SY4wrlqjEY$;Yoc2P?QD$0yBZQ#%8iZD0Ev6}wWui*&;$Klos*{Zbysm*J%|F(mEd%AaL z><*w-Fl^Dy=-)mK5~a-@lai;UlRF>peD^O$caIcyS}2LjC5r?x|bmt@VbnVxs!>l60HwYkaVtfq9cbP18}N|$6ftz zZGNGmFZ|g)))ARWu$Z+rJ{>EZ{;eOD~)@%j|&FYf9~F-$bbCV zg~mZ&a#bm6^1BsHgYPS5)Gd2@Pz0clH1lDw&J&ueN#p2E!j zrvb@qp2M+}{Nf*RTzyftikCs#brG?#xcVu2g<=CL7E-dTv}Ws?r|(A1hV#>L&IC1r z$&aG&GZi1%{f5M+T+yB)5`CH{3RDxJ{=h&LMj^E5h?4WyIsa&2uzj zfDj5T3%Z?tYDxB0mYY$U7~Y#u*uJ#7g+5ErWeVBZ6ILIa4itjaaIl?2M(s|>svuZu zb@H#?DSwfjs%3?!sW2l^8V;@99tN)d_9%5~T@=?v|C2P#vSf)9ZVO_W!^4H7&dOyn zpIikLZ$h^Y2q%yIxl}aZ?W+jF%{Ee#%N$(asvkOSEdz}+X~Obw+d|o@zH?L>Z5-~E zvtj049CGGMgo^{4vQ+h@D$xR`DnPl@npG-sDu0-$%NeEm$L_?t^(xB(Ir!l7Gs+ml zo5S!c9Vty90L~sPi^xIGNJ#>4hQg+(?Fg8GSZ>GwnOaJ4&*P^jnzt|mER^?ft7=4- zZo;L8m%LS^xD75k7zqYyQj%_9JP6`RdJF!W4wx=_w)8dV{iABM3_P;J!y?@_$*To*?-jgY3VvMgN!aGDFMq=LRX^+%FGa zWo>9b{M+!_V#7w37_iBUoaZ^tbDZZn&VIuTHZ-uwqYRr&G+~QZxiBKEm5s^KLLTkNJ!xDl3mspyOYCV@e-Jx^Ba!G zhQD$%6FvS)YbTV-&G;E?yyjywntz?m=O5_B+&sQjYrzi{aD|+gG$rnLGDPKaPnw4u zcExj&@K?mYc@LfcSd#&KkuM+)4t4&m=*)$>Io(l%3EYFC&74itQt|xkiRv7LcTBOX};u$fH24b9Dlw~9W#~7 zlN$R~Z$*@HzGO%W>@#!>kh4VPGZ{Q^{oQ|*x10C-M(1V&PK7u8u_S+Lm}2lCc2}&( zTRB~Wu;0A6=3*SlCF50zxiaWR!kA0UPjdgaGBH`}$W2WQbX8tA66$aRGg%zd?rR9S zoTfGN%kv`ayMNzOUT2me-j$|nU90_Ho_B6Z6P?Yq$ZluLSroi zCx$8M1a;y*c#SxY_($9ki4inLI7UoHU`Hq;I|}D2zyJO2+sI6KiXt$-?eVHR$%`E} zQxOLDC+}K3b%{sq%;lIS=kbuHnAymAb(@yn$v|s*(z(w6;^$4_J%3(CV`y#9hYhqk zjvo0-ZIRj3LP6av`wsx0^Xu)zguw{+S0D3>{65niPJ?xrO=L~sg#H;Xn__ouipOdO zH+tqb91`S#f-ubR)BPBTpt+h!c+A^Iu5+hksx}a8A!feSeMga*ky90UfKM%ZvKBw+H$Xj@b!+otuJp|EzcaE_cmi zT>tTM(Jn4(NEUxPJa%eB+p}bU%1uK9o;w6ZY8W_j!LiF7Kb#GyRbT zIxP4IB6Y8$_rZwdhtG!wmxUDP40ue~NXn({xFLpP0DnL5bbqy%Of0g-q1f?6S$+MG zm+7~AX5F7yIcu6#?o-4iuQ&{!H`mayfy*5+wU~JhzGMFhFYmq%9k=ex!`|uh zxu5j7YJanuJ{9Xf%focC_)jOsmuJPz?rz`VFZ;9gdmEnD-?VFQ1fjaU-$>eXp#0~^ zO|<)$xkx^~{{MCL8P^Cu)C2v0N6~MI^09#Oz1|k*<^J5>*26Qa9t&^88N5g45&A@L zvXWCEagX#j{~mMU_WVcm?U~?-v0dPw$i1(s8dIA!lPr>mY*f$(j4^xSk zf0^|CjPK<3E8M#uf34z!$I%4A=s^$aym?-h&rf~ZTs!Jc<72MecpAtZJ@-EI1xt%R z?ay5vS?qWFG8BCbCJ)V-^zyWy1@k}LXe0C;D>Ma)IN+Uf^vMy$GvcU%mBf3!K*RJ- zWq*@|_8~&_CxHmWk)m*n=P(8lkuZ_MIWh693%?jC%b5iFpX^}y5FaEHid8l3 z-+yH5O?E%G4GrVtLw#d|@h{*0f}Ta+?y~^lx#xgATSgNo@Au)Hx=M|NGp1OKj17@129o@4}T`frJi+I?wGz{p)j4$oo;_BtQJo zy>8?$@6rlS5!(uWK%eas{X#xSkLt&t^J~6i{m}6rct3wx|KCL*{bKtrzsF0w{wz9R zEU~C|JYK!dhW*y4_k4leDNDG4dkw3N!N1zMEF1i%Pxg?Kg1KWX>`Zq>{BEn6UpfGn>+l5S#_%FDPS|up za>le?{=JdFcPlHF9^3~kU>&pK?8X!D7JaJB<)8S%82K}0v+3Ql`GS69xDWq|rT=zQ z%YfO!Kh1V-(9hMUc|0Ti|9|0HOeNMd!c7VCR-4)<9Cu?!3I80N7ck6g_ea*K6 zS7_F%;Cehsgn!&sdbdm7R<6-c%_RS{&Wk%937;*#*mL(g9Xx4z=T8mHfo?Sm-NnShM5ct+qJFF#oc7gz^DJeL`@}6_&Zl`#UJnu+r2-K%6lZ zZoCQTLK34y;i2fsF+YwrYaS;Jk6wHsK8$2CvSU7(*(FkaX@_Y?{rKqEwnQ4a)2yi1smA_3ijPyN{^H4MKV%sA=L$-2Rv z%p)%H8eDJu=3r)fdeQWsb?z%gq9N!v%YnN9kokknfle_~HxZ z2Z8WFsq^=jEw~`kAlm*x_MjhbDc$$)@LjwE_(t8rwEQ1# z0nLw&{nz>KpU6Js6}mz!;h(Z5$gg}6lIw$#73Bn8Vc&zfiv7o69yC|Tb}auq5`UN@ z$$!H*M(>l}K0XVe%A_*;ax40-gRuX0gLmlfE(5W;56W~MNCr7niQNPr&~VgAj~eyFdybRF@~d+}e0)3|-y7q1ok=YD%bzdz^`=VJ_y(vO}A-=E(A z`D?fi4bXp3{rT?yaGi30sVT{io)!tkw|~wG`N-FF{t5-~KSWpctO2}p=hh1Q8vu4E z0niHiO8|C90pNw_hj#a{;)0zO^R9#5yNCQyUry*fb zwtC~^jcNG}m*=vI@ZFE^Dl7NRIilt}arWa*?d&;m_fCW1giY~{{y(}A$=@@HA%8n; zgN(QF0d4(?xlFAG)O6gYW3=EM51F&L(Rq&3FT41sE`F$F63N>Tol9`tu$F(gN8P)) zOyy@@%CJ`W%%12+TECN)AJi%OL~xzq#)I!{t`J=aE3`)v1#l+yJMnFmKH_)~ojX9E zrYrSkMV>$YFXk=r{q1c3`f1dBbbsK-C61apbm{}NM`=!>I*9FA+T*Cs*&RrB)a|*{ zr&AqHbwS(%s!pmqigYpX=fobV?4Q6sr23=akcC1S2!tX~i$XC3P*ny{kU~ibG$jy~ zLRkrPCXk&%eF_9LNNG^jp{_!W4U!ayZBW~xN`*2M=u{!VLZuEW9HcqOR(~N{heZnH zE6{{S6&Pe;(TGMV7}R5tk48ZmBxKQ)Mp+qjW|5mlbs6Mn5z?csM`Vp6J5+Y)(xXg` zIyDIJ5vfM49!fl5YC0LbeT@VpG@Rmdsn4fCo|QTj^=Z>5PMt!01nN_&POUyle8l<5)+bt=GLZ^JD9VwlM=2hp zf|5!}sVJnXlFCb|Fr?Cxs!u7(Q<$eYPKuT45m$=AD-^6+v5K0iDu2x@I;`@u>d-5q zS4yo4v`XsL*DKCev0Bw*7G-GFqn3|aNohr;mY7;?X~n0OpIbz>m1-rmYf-JTTWYr5 zYGtX`;4M7D)D{_|SEm*fjYUPN##o>(nn&y;AjS@z>@rSiNfX%h#^)h(;k;hhim& zrXv_Ng;t9CDOjgsp^BC&n5$!s$0?3u z6^vG~Uc<&SjD^%@NHNa}6t}9iIbXu-xjl6CVxK`oYh;Avkw#{u8*={|#Cgj_cZdJKv<=dKW zZMnwhTb^!xy?-loP3qg%H%8qfdv@ttr*5IX19dIcH^py~-!Z;(XuW0ZYuN{~Z)G0J zy_S0}_F?SX*~hc5XP(eK(LK_=p?j-)u6suJWcQ9$xra~Vf!)YHF&B$LEu}w-ZCSO- zl-FSvg;=JiR1H?G4muo=YkO5^pA^o?Iz;xMr)Yss(|@Z_u87?!u**cP6tqGyAlm8> zj6~FU2FkuRdT@NTN7bK7gqm`R%BKzs)+N&OjZQf@^yUG2&8HvHq$ucC79>+>^OSa}MVnSaoUL6RVD|Ie)|3aQfUkU8)-+X${(&w>Fg8V`eNAryrO zRfz~!=%-wrdUHxmsW_og{y>kH5$fVR+(&s)*C>>)>W=xervg0ZlAlU}Ef7cD3*>(5 zl7FKK0i^v>2TqkjTm-68s!NKOB`#1%oq|MQf<)($pG1eeJ_z_j;ZJczF(<_z7PMm1 zs8t&X`8qR;POVh09a1e)YAY(UFO$Ah=*}WkRPzEc%8WYDpmbGLR#l=?R;Ia4lAKj4 z)Z>JV)2UFW0ZOGQ6xyk`QkhBhC=}5tLVu>+2t#2_hBh47gJMmK92QY$#vW&soA)_du>ZLtL)_wQ!TAw}bBO1W)T8I4>!VtV ziD8n(jW5up=8Y5IrP`-?R(hJoDxEqNrLdmr*7g?o7Q_U;;X+Sk*~v4Q7ruZbW|f~ouSu=@ zP@^#O+pce{-j(jOUS(nCJ@$jx&40K97ygIy!p!^>=|ST0lhY~StEX335L3Zc1$t%o zi=NHwWL@iDn@<8g<=dcdOyAmFFRdJdVrwzBvv8*O#^z1Yo52SU(KamOj!^80+S8}c z(4RzqIR1I|q%Wvu(9ogOL3DIsb${J2(I`aiDdf}siSAS7rfQf_uvoQHSAV-#5Uwp= zdfhdkSG6yk3TN!t#<%G=ye;R-9I4`N%--DJY~7jIBl4&E2bzb#hb<4S3+g-ccgc2X z;1l{&{8Q^Ec~P}7%4L+C8%JyseEAE&2 z*XgfnV&28gjY}KGJ~lbbL4Sok_e}VV>;jkg%=8)Qvt(-I(XC%Xk#Q|!+RHViYfRPA zzC&|Qa}Rb8c8`7mtR&)&S+eoHoqanrcaCwVrhKXS!~d{K-^hLe43ZgiJmh^4#s2jl zZ5@hUxZT)9@QMA2@6@)dsaBm|U@wMMY;x9R@r(0U@L12Ws2DEP&40C+rf%%{<9B^# zeU^E_Q?M=eZQNVCH|9Ovz26r5sB-&I*%NVw&6}Nkls(D5vA)5zAtbPeP6lyo;hUO7 zK7=j-mRby*yfzuN!%9Xe>>xEBJ6_dVC-VCSL zhEwALL2{u0^dSVh^*#j^R`jnCQx>!>gn=^DOuv%KcEmuh7rn%v(q_vscEi<97R|v5k7Ia+)!_OLpe_ajW-+ z&uu}OaY23l6M9Dcw*5B!z=OZhxTn3_*_XO<>)7K5=NFUw)6MSIQji!Ja#iXLTj-%i;kj+L-QGkqpP?7=Q7Y+%aQkAQ4OEhEjLT@mcx9 zCnz+#SO&1eL1#lEx3=)D<3cZdR{VzcE%;AM#mHUi;GOv$RFBAwr7Ln?vRmZ7M81G} z_B8Ct+f(jSzJ|s33H@ong;v7@-_j|sQng6X)&IP&8kPZFLyYko~UvUF+v>Gnz}rZ6{) zl2U1261})ry9;KP&n^8H^%l(nb?FUfx-i=wOB)6~HaIMJtoe-dS*#kr2GyOZ7O7t4FBU6nF(WQeNb?|&c(rqv7tOR zI&5`Vfo9Uq37l|B-r1h3HgJ`6+IkYRfollXEv*Y$_O`8Wn(DQ-Zq)YL_UIci%010J zB$I^qj5py3&967T3TNh=z(UPY_Jl4038{yp<-%C=sCi-L{tz`f#4L#2(ZHjM27jZR zM^KNT9y{VA_#P`3?lg>R*9de95o7r3u*WDdlgjC}k4;O|uLZ0lL)hkw@RPu(B#pY#HS?$Gj3)gk>*-XZNEsx5?s=TU-6 z&XxTy6f;Lqmjy1GU1YDx%P*l`h4ThKBL716 zh9Vg57+sBsFJEzvixMF%x#oQqcTD>X-I}>Kb#ENpz-8a&Y^=%!>k}YuT;B*YQ=~Ui zZoJ>>ZuH+|-@Mx?)}yXxUVlS$vq8DvDD8>f(cB>0Pvmq6QcsS4xHwC@KA_jUq{~_H3Fe8Hw35*LGMmT`K@=WO2*Ruc~ zqqsG5YWCJ37oMauXH?hB3)T(0Lw6?cuW`Wr{eW)ox$x;d*it*i7ZxKclXIj03*ExqpN?GGcGa!!)uU~;^sC$2_N@qlLo#HN zKvW?LfmK2fK;{@m2rMEXbNm1IIgkgo-uv%+fA53ukMDuLR`|QWK5p;Ncj$|IhifEl zw&`=of7kRxKl%Cv5(s#nbg~-Fj`6gdK2j2nFp`KN*7*#O<RENK;LCulJFzXC?~V^0>!E!#Aue^ap@ zRYri$-Q!XuW^2mvlV;@#t9O#fCtbC>8O)MVJCXO*-8#Qc@*;L=Edi?ha>E3PklCl=2X}jk*%ZAgxbgI4by* zhJD#jLG4me<9e%l7HD&==w^tP+t98}C1jII8DuMxPSvVfzJddjh3V9Ie=l?KvvkQD z>dYji7&G;Cfgv@&5eG1PM3mN$te+bmo#s9-8K4#>Id)xe|Q8BFIebAf1acf+@eY@hmXz2b#$+tl|NGa(cMJmdaLi2Q>KC?fg@wm z=}D-sY>(CY(GHMj=nh|$qLyHy*TnN117Jky?}GQjj{l;v%6=p3;mz+-Q%5|Nef%Ge z{?X(VcAjqDFAxE$-~cX1SfvwD>5*9wRTXBFEW3i&D-$Y0>_$ulf8R1z@1su@-0R%sA-X$s14@X$0`{LKabB|~!Vy|RncHbL@pn=E znNy>`r>SHM26C_=64Lm7<)PAAVNXsP;Hs17ZX3x1AE?M?y*WrL2V@`BL zEN#j62-ovv4ife{l2Oy4EA>5Q5bnse&sNQ$W#A77$n5Y2A$!HUZ2DsW)?sGIv2&$S zul?hPi-9x}Zhg;Sf*P!x{y=?de%$=?kWuxXh|Ztuy8`m`e+$XMV%O=1lzl;a_5&&o zCs7K2u`ds~KR91rG=Bn7a3Rv*Lu2?Ov$1sW;AQ6^Ytqvs`NNh zLr}A5d90Yqf32Ka)h!0)Yc%>2whyq>=_%gMC7`-TWocmhs;7nSKZl}ojCHv0ALhtk#m84s`Mm{}NjpiyDCIjjyc@3 zLgLC$=Tqx(j0VZw^cZ|0^UY=yojuH@`sk946_{0Zvklim=}0vaP=;d#)M|br)hBn) zf4{S?g4oehxRp=Aut$7DIY9wRY=@9jjG z2W0a%$HFvH2Dc59yp>O#D2wR5E)e_8JA|R;oXuHwK%OhXCML0khO+F-3)~o0TD?P= zuLBPWxhifL4|P-QyBR7pQdp&8N_s^-e=LFV651gr0o}_13@@`}3_n4=#{NBP_vGgFjTK;vyAQtLJsygdgr7P8`9U>GF6mi)!yE!jUJraf6xCB+{%M z**eWwPuCc8Asp2IaN%_X8Fo&>Zjf8oj! zK!`a*3|6bgkmj?a5!$mrk$l78- z>asg0J--%C;)TK6A|c`ai7*>`?!m-h!J2drzqDlqUZQZxh+))87`~iN#C7J%PoWsqOM)*CZ(V6)!f1sg!7#vE9>h$V~we`-kgts*fj?x^I^0Zjt<8J#T zco({n1zq(QG(~A2u`QfA{=>^;gnGdeIl0Lj0Ue$FR9-}=aoT)&aS))6mAX-Q?NW<) z*(reU?pjkA1JS}-zKPimjR?WzMks@SmTe<_1j?D}r8eugXB|2E!gc8W+EwH4uK^&QHOl0PjyEgP<9-j+P!?vK&MV)JNO9>y zeCBX-Utc6nL2$J*&*{tMOP7qqR~vhhYO@^;T1qtE{=qk5m31okZJ1~zWObNhmu-)( zbldeV3+Q$e*5kBney>CXf7p1R#R-srG5e6~xbIf<8?rwG1*CgphQnf%H|}Z4e_lTx zFbjoABR?C^+jH?TR)JVsNl6-YAi02{Xd&s_L!0^t{0uM5;1>g#tbvLElZ^ z{a{gY*;T-Zc%POD6271xDIviDnN*?WBEe!wG|7z0!$i0q1dN3tf1}ck5pV3UckT{B zcuKrPo9GHnTJEEbO+@Q(_QeS){t8IwHWo5Y#*}=op_F4o)Wc`f!%oq`OK=P{;dZd` zJ#l?Ixr2sD-H8|2GU+IUi`P*LM$LU6Lbgth8cmXSyXolDd@QvFWwhChICe?(APoHBwC*Iq=ibU2$xIr=XnD=~78%I8sHAu~e4Uil&)x7s7;uKMRT@iF0Sjif!9Kcc-f0E`u;lh;7A$TaXY=H5R z-4CqYGQV-5)g~bINlcUXd`LZUBRa2qgRnKZW1E7Jq$}SN+NtEBBjA#;-u87PZ}XJN zYWX`7YK-q}Wmd}x$3gA9pxt<>(IS0;t%mMS_IjJ8yCpClT*>hZY^Cx|`B_vX~ zZiz5fGFB{6fBLRUzu|+6I2r8tSW^34CO-RhBP{+M zTVjm&$E#u?KD0L#TTXdKR6S3Zv>9fM>L4F_^o{bAe@0u;5N|i(H^840Rja%%ku3m7 ztGudH91s7JB1S&KpuYJ8vKKe9=B0^Tm)cC|g9-7eT z$$bi8UOzmJ{ETwE5#aZm@jf-%)c#FYMB}&QA=UiF-~ESQQv`Jjc^pv$q%&CA+~qU# z66QP$e;M!klL%6P%q{dOq^xCFNm&6tH_y$xCKHlWhF*uSxjk70*q;v*`aGsbXO_K& zwL%;(o^S~5aHKcWNNxc-;4J8f-5luOe8Z`tbZ|X= z&~fl(169|bDLi6ncBuRt83d}vBx$rf5eRYibH)2QLuIJu+i==z&pe`Gxg;J0%c7rtSHUurv zf7OZv^k-ph9+Y`y&*iN-UWU`a@W{REYz|u`$Tyli0~lTAXi%1!e+{N^R$qP#ygfTo zcDTkoSMH)JE81QZMT zNCOk5F?k=Dk7$9jK3hn1I#C$6OazBQe~)V0 zJc!CP!1U$<>IZO=>dzf@ULAE{9d&9>x}>>3s!HVlk<{etx9UkQm?km?k(Kt ziO9HK_wt8xQ&ua74lzb>bcEXT^wWpwrw`LkAEul?O*to~oRiZ|N$IB;>9ux|e-~RQ z+1rm9#31q? zsZ~=;U-CiWk9u?<{0T$J=DztpqbKMW)4ST&4z7dwTOJrcJ{Tlrl_&ZkeJ`u8I z_sIy_V+-lst!|0>jWFJkDfA}Pe@>R9QRP`7)b$a%5Axa|wl1l3H?8)I#9{K%%GK6C z8QD^FE;kNMn0`#Ig)K-sc@pndw~=ZSD5@??LYYre+*3L1$^lgETu88z(F_)b(i{1d zEDr)&W;7>gfd?HZ!)#b+d&xVIu=?7RBJsq_uenIgGJlp~4yOSrBTQS=e}hGW>T$!! zwuKG9w8-@8nqh{x5Us!DVN0H^nsWhty#PwAe~?)C>jCty-ujPAFBi?=8qJ3@r+KPf z>=!Zw%&kAu{$<8b?D^!ndRlX?eLqf&$a#M3o?a48#OWsm%VJ4K2T4ieq@%;6qywa- zNzziV>aATRRkiX`T$ABnfAr`b=1{t`(*RfMEHWo*>9nhpW~8f4iB=pPRws_D4-Tsk z4yzR>OKQWat#p-D*U431QFOVnT`on3%ud(GA|_Er9!du4%(Xm|<4;W0Lc~S(^k8F9Q@j*vf96!pIh#lH?=)>J zba?$$(Mz%;;;&i^qSEL4#wjkf!KK!SD={fX)aI>gxc|%?@@b||&ry;9CdX!n-eo6p zD_X6HNjS}9a|+9vbN>(HDYg4j)o(oo^+xm<)ec5gen3ZI8MKKO?LTPO6A~BReq!E1 zp?jKo9w;~-{f-Aof9MjWNYR1bgy54$!iDhRBsP)9bh5WX;9(#|yH5l0uS=leT}^uX z=Y#JsKm^j*fJ_>kFTQkpCi#_amOWBFZ8%}YA-C^ykKyG%V5g=}3j2!(&?oO>$>G@X zG_MxyS+QwhR!hN7EydQhe(wGbdX(JoEmBVWRK0g4{O#IPf2!$wkwnuU-$U%O9$!^S z4^S%znq4IA+bgpxEGhHx@6C|X2h~#oCqmv#inF;p-$r2x0!O-r#}_>{du74X3aq)} z7@wJ0H6k-$*Rdt;WgTY0cPa9GVr6|+JO-;$C-PmKCr~{Pu?ls&8HpyVVh$2_(St%6 zj+Ct^+u|t=fAP{z`^iI^#3b+Dgx&iq!0+JBe~lvwEL6ZE7TgUpOJ95DVNy_1e-P0_ zpyf#RM1oC~<__>>6-n7sij1g`>5C89WM;szPbLT`bNg>RKfweVp&uv)1JC}Sx)rMZ zdH;p6m^knwH56AFFG~?$3!-(4g6;W)0kt9nY7oy%e-4I|B>hbYu@}J_N=SLU1fBco zBbWEpq1=JS%}Fj;LM7u zfu5_?Qn@L@WrH2sk;yiYeRolcVMLAQkN(_33_gTF2JVF2gd)1SVbAT!Y?`aS8y5O$U)r-0Pvc)H;tzj} z0AE0$zcDk~R#c}Lq(}GKCC&Mg9Klk{Hv^|es1%iTJ$nmZyatH}%u=W$8Npvsa>;%kRNA8tqw4-@Qraiv_ys6&PY)+AHe9;a zs`9=-RkhTDl=EV`07Zk%KW47`se$nv#AyPdQP@6}Qpi;?9bdsKr2rMTmjjZ}7B z>sWP+o|?_x(CIyYRswz`tZEVzUryGNd*G`B_>dc2x5Zjo>w+M#`P2@NV~9 zlYd$p387Y-2#(88?qJ_EH`!KB?jX%QHNw$oZJEpW5;;)W_e)vG{A+@ZICPAV85p{; z&pozh+k>9U-RpX@%qA*q$9`^e^KqXGXC5vPkJ2^-_(bqKxGs~xtAmVB1Gt13qsAwl zw+Pu|=KN9kk@crznehtHU5fCJsLaGvsz%w%lTG9FEVU^Y6P;^C2&kU7J5evPs=JQGUtxRB-WYDhbF?A+u=$M znFmljbb8~`tHbKbFfJu-LjG1#6LD`2y{Doj=7WWj`9@;9MalV9*#VeW%3Z0DW(43Z ziWGUJe-Nttu3$&qBZjrk(q!f@3V$s3XQMz>;`-!$2!dQ}@#c%k`a`^8azF)mL)S5G z(-u2&yJ2q9V0iUpYY1gMdr{Y63dZHhFE>nO!xVcG;sVdsRxOp!FzB?_sWu0e%{6nQ z$>Df*@uBB#Q(4+Mdp0CWI`=&l(vHNWY_iJeWyuoYv=g#Oz861hve`IN#eX}F$(0p< z$qCJNT6yoYGYcE8p=6!Ij^|4Ssb+gEWDp-H_uFiH1{Asud2ya#1Rp!Qt-}W6-wDZ9 zjFT%_98`qeT;2r`U^+BPhps_qNN)r(lwo(CJ064O~ z^oqGA{uqa!%M_)#t2s%%Dw|s9rznjbE7kLGN+)>!9c1+@FQB%_hB`kDB34j}6>8-Q z`BIP2Ldq#}Wj~LCdlO)?dlF4O7CVxRDVwm)u{X$cqRnblC2z!CbcI$_T9Kz+5>1suB_KmCPR1ZK`^f$QLn$ z5nHuguaZv2IDZ6k8kVn6HDHLXn`&G$3_FkR^43~RT4(O{RmI(bE?T&Wcg9UF?}Sid zcigAq&xyG`ub}jnpdX|!sv2<1O0|QAgAdb=_V_L=kdicSdpf@@xm(uL#qcYknu0_cZgz0}cBmYLwNm4ce| zLs3@}M)7oV%?hb2ZnJdIWs*m2x(?8X_NC7p7Wa&jd`twi0tszA@=p1>OhTkTit7nO zW*2Vu7i%LXM6YGbIx*4}Wb0Ts&AD9kSMYr6&X9`n)2rR{4_Si{lOP0DgiosouT~7b zJmo_5gMXIp>|PX_NagSZN~Y+?@Xnvdj+PEcBRHzhsv8Srr+O(n;6jsnapIA2%3RYBcdYo@VQ6HSs@&sn&I%sv+3Cws^( zo@=|DGs_O-Hr6Z6V$h^UEmkhVdw0JgeF{C z-6gH0adp_F?)XDSo#6Spow*vfnO;;9htgnZ%!a;`onlD&lARmlg}8)vOwkYvv%>(n^z2AHf>|sU9{lUm2MfLT zwtvLNfqo7MS`HDdRr{j>^ghYE{Rhza%Fgf|tEk1lH!X-DzcJ)-AhtA%w^u~Te}Q!E z5v&sRvgai4e+LZnE8C?Gjgi?dy}qC<$$go*is1-YcfbcHfDr6CN8?tzmx9QAJ+nE$ zrF?_-mg>Xy&vVs;1l=rn-y!$>*HIJtaesPDLEP+?_V4!N(5^vv3L^;Y+IXbUb5XX8 zaji{LReKxbvUHE`sjPmw_@NqHwE`6ZU*va%WtHHb_%=z23(s&K0q6;5j?Dru)TGZJ;kItAvKS7b` z2)+ zY@c{EYi?mW8ZrQ(&*o>@Aq%e-AHtq>gWtrTD;->6ER_!YPs!X4=0A3O#-fQqno14% z%z{`+uwVQ7zhW>*f&d$*El(@ZXge1n2IC8XBpac9z4ylx6ab zHsi{ZL2#2OsM&UcTBcR^=<7dm&_c}H`P+K# zk2|e(bP=1f5d*`_GPh}>E{J|MPwkHx6Kh)Aw5MYY)zPwV$CwVGc7G#jkW&jjJ)b+= zYH>+uHK9hF?iM5qzISqYsxo`#2&{?pjfv)K2BWq{ z$LF0Oy+1@wh=H$Z& zdUn(;iQ?q84hk4tVShsljOQJlxNI74d=8{BLXSwJjiu#^J+0L34Oz=mGR~;bFVyOd z0|89KI;X~Y(HI=?&*de}ETtIE%d_fvpC+i%+Uhw=47{S0a-$KZx{ak(zFt*sjfP%a znsOAK9hDnE+}{3Bd{MGTWQd^+{uyGYak^NOuwt_$;U99Kk$>s+a|jumkWv3G^!DET zfq;9A=M2>ITqQeh9-W2KOIZ244u23W-j@tQF+zt90HvkXRVe~$sp*?*n;Uu8z-8CK zluw2}WqSAw`uGhUMCjS0=fq_aV&UnT0FgDBL7Po!NL{70r)zCM%e7N4G-?S+4lSh_ z4`eN)>Ni^X$>tT^oIbtZ@cpBd!%l4h@SNVSOJNvUxu+>UXGpcs=m?Grp5u|Taqse6M+#|E;dwJH8Zsl?~0#+Eu zll)@>)2?f5I^+f8)?hYIhKybup!935fiYp4awkpBjuWDk?U}B2fRdp*apzi90EY}$ zgPER|CV$BH9Gl?U=?TL(On84!dHWqMJq4WkvRXm*-H*I#?Qh8xVv~g=60<6kcd-Yb z1fAU`Ays4Y3m{3My#=~*n3WU6a@?j`#;&MCok%0GDp)Zx)W>lQuV|5*3B)SDUE zP9~S61(TG1IES%8HiA0KQ|mI3^rX&9!pw;gi+>ler~_^$(YUXPrI{fFxp^I#to3(; z!X~erS5u)ck}{-CX$ zXW9lUL&<(`C{lB2^y+2`oIf+wwxQ(FTTtGESEuBN_1qo_(8qF+@;ejF6;Nny-u}C#g7feN(ay1LvOmDYd!w(^76Nj$B z_Ju=fB>I#mMM&0uveq(b`wQy7a8>ScG#_=63`1F|6&9p+kItdu-IXjp{%Bnd-c=JR zaX8MFo>(Qgqy#*BQ4DFg0QO~iXn#VWbK_3;zdqIReLJ}*j&!Y|GPyX6f>>p%h%8FS@D2}u6ouwjaK%b61rw6e$%=J{F znyM?~Wzp&!ygoQ0{et;rK~~6_99lPwizhCuuQ_^dP10m7qC?7a{KBj4o0udhaJ~7H zcFtcTv$$ecUJT15BxC^VdFB*lyd_%v#aXy8YDN?=l#Lb0$&vB|Jb$TTQf^Ce8;FuU z;yW$eTj5^@&{JT07C2*GCcm{HwtfiNy);2x*KklI2n)1c;ovT)^ClN^Vx8Q7;2LbE zs#-s)Ry;F8Qg!6r_n5T}pCdEDR>DEUq_+_Hm(?qw0yeqxP}aCa|hyPs>cVBdREi{SSI$+G@m zU{^ecK`CuP_9bnoGC*U~Vux+|%lEV?%Lp>@Lo*f^zfIVUs(aFM* zf>xykNvI`<*N8ACkYqk^$SgivAW(@isx<`8x`VJz?om68t%U|2M9p@ZQIsrz;iVs& zcS{a}q&*iM+6t#W03cHSNil1EA0jI=D%SY!XJveJ2KV@x<|kVe&bIm{FOTSte15dA zkG4JW_v_yujDO-s9eIZso9Z5^xSMz)ixUp5M$BH8YQew2cRdN3&jeQ8`!{JbNg50R zHCs_8?-<>^y&EIQX!GN&ks?{Za6RUdv~!BY&uYP?RgHJjhK}*2F;f6*YF{ z;BE5txC&@}?de_5J=x(vI7Qy{c(qo{^JjY>SUqei=nBYS8^tfM;V=)>M3aN*>g_&qITnEj8ckSB&^tvZECUo;OVlKLa3sQT{5nbkLZr(DSW*s*z!kz)-G7MDu=_>A>te(!K@vE12huCnu4xtA zo{GNIqAORO{0SeyxU9Q9*LZ$8GS#Q%@yR#>_C>&z@#1>kudC{%q2Ny?58KOD7T3JH zUeSiLt2?1^zM1)ynxzk3xn0Un(jG#d>&GOn!uR9q-nfw;Mftg+%LQ@br!xA(YyTnk zdVlkvvAuMU$@=Bg2_>L1Jl;j7Uq4^5Ut8<1>c`cJ_>+tB?0p^J5cT<0{aE^Vz9l;T ztbJ6U>z4iA;gT&S4#o$w1zZSpf$BgeNF<(XLfdM$88H~$*@Xw{3B%Pf1 zd~E!*`2Ay&xgf_RXmzzEq)EPu=qKW?kG4&w5lLwd;F{PCJ!*32Ob78pa@ z0{`jc*&$^#bLy=PsRcE)ApjPMU8t5>2v|_p*|<`b-n-T9>{_@LC89{}#;eQmigEkb16`vH1fo68c8Av%G>$oRKabZv zBV}=ayhsQwu5~S3otT2GDu+;Qizr`ZJBCI-p(ugt&|?62Rzl_oJihqXOiIB0`4b8L zlGOA~Yn~=zo|~HrpjE4M;WB@c@qY$?5%es|E}AdAd$LU@&iJ*s(<*h|4II$KB;b{> zB;fAC+zU!#5pc!1&XXpX>(%A`B+T(&O?vyF4~z9K&=R!=&J*7q@qgf$NtX~qZ{dFR{`gTLg+0PQcI9As7U@qb6ONi|#* zZZP90c}N*1D8Z}y)jS9j2i;novP~@?9m*SH@I5Cc5p(T|jP{*h=|+3w>QSDPR_P;d zpPDN3wot$js?Y$WQMBl;NC#=v6diaqcDmqiO=djGxOpYF=LJCVK%U^SROhJiNEpX^ zJ}E37P&>OI?SQIFZJHuYt!8r_}5@v9s7S&q6YrdY>SN<`q`8rtBC~?8k^NBqEAL?jK)1?IExzwnp0%|9GdLwOZqV$Qm<#N?(VjvSw zN|{idOy(!4ZjZgtV1LvK$96gc+##)QdLRRAB)h&KHWK0A5C>&y#EE0o$!8sk-}spy zZ0@1wm(=aZsj-3=JGt`lf~x{r9nAKx+eDmjK5V7uVklz1J!UQ~arhu9dBdk-k4C+1m#eaRTSoVF2bNE>E#6nTF zL)&t?E3kR_A;#p72_`IN@7?nuZP$)KPXESVMaV4p_FurDlvBSFS_@b9=_%kgDFkDD zi?lPY{Uqkzo`FxgB=f@H2X&;TH|8x@j=8?BJA&vN)0( zh^1;(t{O=gM1P^_Do3?C#ZCu3g9n&T;&iJUgps4URtM-NP|(1rEJ-xn-ckT4D^?Ik zaH37@AGyLKvq)28c<0Xdyz4pG(yODxr7IOXw5QTaS_M){9N2_tUQ zd5uQka0OOAX?Z)w{I^!$pQR`Ray}WJ!1BXn!3uSMAAjioHR0S=+?PTgyzOcRjTgeu z70Eup9vRoa{hlAn^Zkzw|Mv8c3z;dG-E_}0@@H!6iVs&v^KC!i^F;lt%W@R|Z*LX$H>ZbYD93&XgvntknKQER8A7nW1<7Iv9@>6>)QKqg0m z#-cIp$vuFs1?COLe$1B=yVNVJajd%b8o^e+d>*U7N_T*Fn+U8Uyy6Rg)efQ)40e+; zWAz|SK1jV)##f8*uY2J}Zc(zzi^MQnD^J9eIDfCD%;HGWB%)`CEbv=9R3^P&r_8*O z_LQNl*vbpYX7!6ZV%isa{vLoF&tk;|;GOKafI>VXJ$UiIp{$e@YQi^&wKVgIMZK6v z!riJw&qO5`tGpR9vb~1v@zEz};dW4mdnaz%%24D+KLkSt#F+U?w=9s_5^EC;2T@F~ znSZb(KLnCeMjpw-07L#7_aJ5VzHjCNk7RNZPAIomi|_}VSO1XrQi`yq)WBuc-6wg_ zPR*IU0znxsmSTy^5j+Z)9#g`ia@bKWtw@Z9Fi%LmZRxewLr+eTg~sCUI@FYM7| zD$8PxQ)AqRq7g`VR$+q3p5{7ALY_O1w|}eCOOrQ51pI2jH*ValCfm`tmLzf6{AY>& zoIso0@ek!VJe?8jD5Z}8hCq6p_U9DV)q<_Dt|I&U@B*Q^)fo6h8)O%7W*5My9;UYN zW0ln96F$YX9C~+m*7QE1mRGzkxc&Zmw(Wk zsYh9{a3H5sI`Jg*&Jcqvoy75|>k6KYX2Y5EMy89ym`W|=!x84Nuw?e(7KDBQ-)r=; zer;-}PF(Jns|o;l)ot~=739T08%7A}Q>|*8MuwwRT2F|UO@wP#lXkt|5>*Ge%0qNX z_q0yFK!(C)r6tN1^&V5dQm%55LVvPrP9X!Fq<3ov!xe)C*82P3HdfkKut{jti&`YN zy-22{IJ!803#;{IEef7}s94%YR*Y(=#)MYUj5TZN#@uSR@q_)vh{iQ@wz5A18w4sm z7v{VFSn$_ccJ#DLclnMf>J&i%fP>aO)Vl| z>i0y=Z(~r&8o7PcYq11sBjFA)Df=v=O6e}1;^UWbc1Fwc(_ST#xUvCk$O0=*C+NX*0vk_~S%COo{Xc+<)wYjQv1rJ)vs< zY zR@2M^byF#wc{jjm)M+;4tyo@9P}JC~_ajAv46G2NqV;i{A(u^}-J3-1-Jq*LEN8QN zsLnD`Hs99GwoqF~7k`5cVU=?sCF0VfVsw69UQQi(_WT(U5$_X)6l&`F)@Tj0U2xg1 z5~L^4Ho5&nC?5zx1)g*gBk_r>Nw^pIQimCiLL%Z;?KG4u0Wn-yDbrwwamT(?D?dyS zfcnvWY>{g}n&h!)bZmf?kg_m*dr^Tz?n*U$ZQ%89D1u+NtAEuRI#-+06}1g+#nVIG zxP|=7LoSP+>xL7U=%Swe?^?jZ;E_C(2C*|T@ zL@ZjjEBSbq(LbELMICfc&R#h`%f#KpF4>2z(->P_WIbuUU!$<&?cvx*s%gX>w@}Nr z<$2XvLP=k2tvN@(Xe?*o){bPLz5VyU4O*vAFHwFKT<{f?#2Up3xZ($ z88{6HxA;>`0MLsnB)*&9 zqhs(5q^+CbvTEyEUFhfye?Fm{=c9o$aFrM;f`jDKjW)J)^LnqOq4kYuEFryL$S9M* z@$KsO9Dmfu5AXm8;%rOOXNLh^Iw&m90L;mlEYMVo*eKQXxm5EuEaP8@TUPSVk$`)v zw*5VcOKZAf{*Y#y)j}2rr!;CjE-*|xL4SVm&z;&FME9bE)JB1&=~}de0zIF0 zJv|PTqc^9cF8iz=a2vf63v1j3c-7J}K>=EsqX+lR2Wd_yubAxL=~kga%TZ#O>J_V_ z8b%bO%KXNYiMEN7y%vzy(|-&z7BI%%W(llnr55d7gAYS?>{M0upbCf~1ASVI9o{Kf z&ws@(n|(^v_C_I2Fr6PX+4I5a3m1UkiOS6FiW~Qe+U8tD)|J-QtgNNN1j^}#^;`o% z;kQ-N)DjG=>;WF23pKXa?KNpaS?9Q-tJ4)*cc7Q5x^m|lFEAt0$L16GN`Q{cQ1;8= z<}1zI_RQ8OvEHArL4AI{VmU~693>`pBY)aFfa#c1jve1E&6y?#a8zDGtjT>%kQ2@U z5tv8D6MSC7qBmte%nhOPjcRviMmJ#;A-hN5+Lr34lYq9As>v4rc!9M=k88K9?M9c0 z*$;tFrgq+_ypn)2yUZCSK9xh?=Hg)4@^a(_&kKO6^M_YX=s~6!er}>16ztTm+J7z4 zLsrELcf64O38B^12d6=K2ks(|XTTU{&n5&u$)DD~i1Hol65nqQd<>2+mRbUP7l$&t zpcw?{`MAZ&lii*i!$|W+_SK~6-<=yns@IlgiqpOMho7xH;x+~Iqe`3qHw<_De?I@9 z;}(En8MNm6oH1x_R0Z|=W7G$JrGGmgH1$5fur<8G*tO0N17RC2xxm!Kx0rX{Iy&Ux zpMwm}f#H@5Fr5jRPK37T`Pvb(b|EJ^IG$1LO2q6;RvPbOTybwSE*&}-C(qEhh}spo z)@#dABTAe$nK*6NWp{@8TvMTo*(CLTevQl>R=m@zzD@lpn?0|*zxOG>`hTgv$aC-g zif`qo5rR$sCY%0p^8e8Dwr_{o9?hcX?ASixs~Mvr?YVdZ$-WWw0b@w|5)`TeyDAVP zy1PKFG)L9%NJDLZqJDSxCTeg}i{C_*DBlnR65|1^G|`N-hKHD~rP`r-KfzFx=dt>g zt1L1mnkF4+3+A28+Z5BRBY%CwEnh?i;7&NNz7wqR_jb{r+}^7mcqK4Y#p%sJ7>t(9 zKtak(BlCTscW>P=$lcHMv=~L%j!AC!ZXAF%>-Bd6*i99F-S;v9aOq6Y4K21QVSg8v@#T))!l?OV z6LmR3u)W!Id2<5G(VQD9CzQu~QXbIZqAjyJmKRAv3-Ok_s^ASsjwaxrpaUvBwv7#$ zRPa$)e|ywaS)LYJVGwNZ6muq@GR)DD9&d^T7p68sCBe7xWRuj6lu?T*O*PW-TQxXn!fwH2P))wV>oQOyx=XM2u;Mk%ZfG<`nnsx$_EMLirCnG$4xs z-f=gP0zruWxs~KrYsG!%>g`eJ>|+BEEQX8RG{X(D-<;|yE07%@kPTERBb#N-wLnUh z+hV%Y@+VbNCX`Pg+e(^Q#5=JyHhfh}>r*y|g6-%mS4jU|L*e~u5aKJeSPmY|Y+ymVYJ5{?9Xi^lqyE1Me#JxHmJO;7St zyjTK5f@8EJiGR3RU`-vQTS&LH8rXp~r*|24!<_gV&T;$>=#k}bjuG+$d%3pffxPs3 z)!f2tB?91Z;%t~?HqmrP&k$`)F+=*dmVvk_Rfqb|sJ(>!W}RdrX+6Syw*}aay7yh- z={O$2f$tKuibz`YAPoe)$M(`tBk>|~ZIX;yyU=DBcYjrL2d8&Y&Ff)dLbg0_qOnqU zWd=p{R?Gz5(^PhiA#@;ACaOcX1xDCqy73^KUa+fgsoNwtWIk7EmynCW1Cy~}R!>dc zWYI+VGw)VlBKe|{XJiBz3y59Nz888rzHNd~T3c4xKD4^o3uw)rvVw;kC}WyM9Lp_> zO~ddV!G8}EW0Mv>yi^lv=c41J+9TmKWuDy+JzTpm@4#KRxIM|0rG43kj4+J?V_2Bi0ZR;xzmyS|L)156oW&TTQrc03iKcG@SIMYFWdf!tgIne~1?C5It zaC@n{XY;#=Gp_UyZ>?Uhj<0&YCSm1AD}SSS-xI=npnK6Ze7zle<>-YcFQa@z|FN)p zIcW!HTb0sqEu!?V*s&Zi`L5Rzz8*>^+>Nv1#}GLD?VkN!IW<9^x8@m!lI7N4d-NoZ(xx`Hz$<_xe}8aO zsK@q(L6GLebt;lXk4C#y8vAr1g8D^m-eBk?qPC6BGW_jAmK5jt2K^PJqluC06wcvB zGXG?i8`vptM4P;VjOfBAhqpGB7^SrRxKT*+p;HYG+AMzpGfGvSjZ1K+Jr0e`+2C-kuAmir_Iz3R6s=x{7dN?&`N7|&YBVYUSbx@S>x=@F z4!H*UK4YO^DQ!$hW}P%yXx>d?-oZ4y3n-tM`35s=%c4g47GGq^79JXtw-5%+OekpF zj692@B?~s}IY-qrWXtW)=y^g8oT?Z$4?^u0!r_idphQyg&L&Bo=-TN?o^Zaxg%It^ z7~Up%!3X+3PSGy>^tER3Cw~!-X7aUnXR|oFc#>dFNX!>ObTbYX?_{4&{P1wFGEq#b zP690X2dEuR+rezS{B%%ed**;*Tt5yIW7|N7q_#v8OG%|{C*imENU?Qi%2mEsS|b&rr0egAi&j;2!Agzlq&2@Z`r-c zpQ0t`k|XhCxSpzFFAUxRIfkv}s07uI*Mos6NmQBmLCmk)&->`^)65t(G-XcK$scrp z7tw-|DfO7&qr&L&_Ss-!+d zreU;C!Vy}PZzPpwmVb8k1aPP-3ZE81qfzOWHNeU_bXx7v6s^YGOgF5e z7I+yh6b1F9htJ{KNI)CLXoj?$QgmHg)nyu_aJ(ZoED2*;GKlJO0`Bi*$awLQzn$S_ z+eGD(?yZQD@%ArU>b6*=?}8AYq*Lz=E;*;&#U=^K&Z&=We1CF_(ed(&UmTeEMB?`Z zgQf4sK((bTiTFFdgM>7d^S%U1P%*$Fj*e-Dz}aAi@kQXYiEF;RT2>?{dG<)t5@ z;6;)=Sv%XyBUX!IFKWa5NmX47MnZS$w5cpBbc(By2nC@6(G9@e>{vtOOe%g^eu}#< zq~LCf$=-`4X@7b|lSqsty-~L)#y!GB&YiVSC(@EPrR$IKLH;a*U9=14+S)2IVn> zcN}J31Q|e99pQc+2_smP3+Dw3u0TTrU;Jzt%)S5>l zN@TY>B7boN3?-5;-l;@e2k?xEY%JPD_XSkEqXq--ySSEKiJGbNMp_jy8o%8qm$ezj0_mgXA&6K7<{&d+& z#peqb;WV0sTB7vrX6>yCSPz{O+Vt4u-b3`I;3-eUiWS5rs>#gxV)4_)|FH5i84tEeCLbB67s*zXnC9 z0Ss=~gN#8$8KBc1X{fjX-?;PIG#rkmNrfZxQx`RQB$SDon1k6B`Tb8fMg?VgT3;gz znHXIQd`+dSgOq_avme$24@8%qOcD#E)_;AR8bS})QEb$03wn_`4o=tcdDoVuYX4t z%bzmu&t%NW>ha9Yl7-C7=SQ=wyEXEBZ1OeiK7Y{P?+yS9FYUT#qg@rBq`SHPSl2kf zJYls-HV>DV!$4oZ4lDK&OQ!W3yA%5mNn4Kfe$*7};)mH%AAUAC)!HMFQGa|K)5`pW z`<~{w&=zQr7wdtb;AQZ@ias2}`JMatcTHRP@QK8$Wk6_mE$Xsm0<1jr66m9humGzD z6pA(3u$XeF?km#lSAxtTvM19ws^1f)XPRE1w;v#>M~f_2gdy-FXBkjxdVJP2u@Xtp}kMTKwjde`JgBz<;89qNapGH!uW3 zQe=SQkIY9!=+;=5Nq*t2D#}Mv&D&Dd?BR3()=S>MJERTf^aW>+wQs7~l%$VW%3j!8 z7fAkQ(fLlg`Cbdw{=X^w@4Nyy_Y%UlQHqTv=42|74t?f$U(nSv9g6NU6s;r?V{hSyiF$l>MioPadyHhV*0Wy zE58_2-u^B0_%16r|8mZ2ajh^xRJ;w4K#LE_Hk`!ku8sn~U4KSG^6oBZJ}D?Y;7bj2 zNJ4Xaf`;NfS__Q6Hr9jUj!TWy3_AT4#ofh|^?vy$ZfOYG`sAj%^5I=uhIx9{ezo&N z#r@!M&E`_^Wdc0N*7$l&|FITR?T83CJ={(}lK7z+;L(Pcelf_=m64HAK8bXSksTI9 zcsod{BfYwF6@NzRzDh8vKe6yGFBR+kQ(N!$(JoRdrpD)nFncnc{FzSC&N9E9y%9P5nPfOwf&jPXKX?7gm&sbe65RS2VThQ~UYL15W&Y{Yj74o=oxSm$Y6#hG* zT@xT<`c<&sMPX%&SC?K`vOgA_l)qR?hYCh++OKlo%-Acp$ONPJB4~wKcB|>Tn9z5| zn7Y-r!+#Mbk>7-;ZWA$RSi;LeJ+sfUU#{o^Kf)wL5fIckq*){INcDOc+Jdfdi(>unJxbV7Edeinda9jf?w zpkKNi<_ej%5K$2$%e|2R4Yt7#XgzfOMsEbfsDGn!#3>@pIzVsf(quzeg(R-GJD$4U zM9`cazrKFa?g*Hh6}@&D6k5aTro}brjV+3dH)RW~1snr*IbyxtQxKd~VTS0Le*75E0ANe)32ie5H0>e{PhUQ+CB#iOo(QPbR!EB&EwHJfD~Nu1Vz zGJm(qDLUICJ@V2+NyPm8)RNmAB`iJ}~D*M`8Mo9G;OSKVc0wH&7PAOAHwu zVS7W(QM6Xh0=OvQL^xC<-(~Cw4i!N>u}FF4FIkJ#ZhJj-xUw*t6k9Otn*}GOrPBIp zFOhR5F^yHwMSIgkwHvdoJ}K%2K98SI)qf_QMDWP28`oysw?`#3kxl}`gZOIiTsIms z4nikSUXKqTwa;c5M;G{Vc#aX9`5nQY^~X_bl@bsy(PFD*!a_f*)hQ z(~(QhRo)|cr%1~Q;~^+A^4>(bFbS4bgT9-i??%kvk)Gw5)v*hyug+(#wi?p-M}JK0 z#3TFs!rs<`5!^T#hAt#(7U!sCy#8VHuViuSJ+mJjXQA2`XEyu;=^xN;i-IIr>pXGp zt(05cd5@(f>Kql)<)^HH3uGWRdg|CAJoYqN3~yz9mrYu&xKR6{06mANU9i zq<{#1?3M4g9#Y-F@=7E#6@NmAtjiWF zn`wG2itL!DAckagWLXZj!N1#_W;ym=^g0TMP$zvfr0TIAX-ffWlG1KoEmV5|CC1qDw)Lq z;nvhE=H;5=m&5jm@if zh*)z9?^;JH`e_(Gn3|DbB%Nmn+q0!F|6UQ}77&e>O49E#xc^n8^CCk&pVUc2^N7Ft zgnL+r%@3vuHZt*X)stWY1vz_YyTtul1h~olO(n7q@V{Zk?VZHT2ZRLi6f)e-L9V`@ z{v{~6iPYyw-tX1-#DA$A=rpQ+f`2FyV90hRh&a`hCoT0iE%iSS6HfJfCr@L(aR3Pf z;+0(!pD5y&D0pp>t(tq3;o8T22r%0%>3w>^9j1UxW59B^Nt!q zlp2PnY?(Cq!j(G`%>J!{CeEqb5PocmWiE1iBjM2;2eMG^B!5IuaIkB_kSc--N%y2v#K9`A&rxV)P9*$p&-i;8@sC@=jPw*0$w<}v2G(TzN+yi7I*R&+qMf;rv( zQjhMHUw=c!WY{%E?m0=CKC#ONPu-!gWfvR`y=-f}Us0rX_$-zHW^2z+Y8`khA!(cVLTH zwW47Oh`yz6R=<`{0A}3n!xtBxlI&bO#sEnuf`25fK}4V+rFt>Bx9-b}U z+>|%~ci)%k9FpkEk)WB1@W#IB>SN&Zd8sylK?S;}o<`X`kJk{~M?cqtNQ~#CnEZN! zCw~wkv1#Je$0&5$$Y;BvV!((RM z{^ABo(M*-F2KkC9!iae80^N|DC;?r&I+>)@tl99TAq|#M+Rdsjty(>uw9EIHg|PlG zc5Dc2HH6w8%1SK+HZTLmgl~op4TjZR_?jZ2-8y z9|}Sm>a^sTmrlrj;g05z=fbu78c% zHy%kq*H0)+UI;JnF^iHghRcDwKE$|kS%&4GQ%yInj;bhV7}zMRw)j#PA_z{tlfAKy zIS}Lox&3o}P{=7qZD5<~g;d>yK37GU=*IwYdIG|)`+*KL88L$TV+GW7qcEJZhhI4! zX@NlE6s4|zOwm8AmZ&Ad(>0AqTYuKeO(6&APq9ACYQEtL9K7T8MbC^^83y%0IcAvk z7tGt^gQyDGYf|DpI1724BbM_a)X!VYhxiYWOL+OlId(pBE@+3(-E&Aja*k<-&$8sP zG2yOrp)x0!Bzfa8T?b(!J+<{(tR&)Bd{w zesA&E6JXZ$Fu=cjw17;`C-!XpVa_Jq_QU&ka+=0=)k17=>_EZq)si`c0)@n=QBf4x zB*t)&8F_5EeFwTqDLYQ$42p|1i^3KIm#f}kGo*A-^|eW!F4ew@qX;rAgipc(CO5Q0 z`YFa6kHQjjWa$yVLM$xufpj%pm!2AvD4&Jd$Y5_%b{}$AytJ%*9MVUVYGS zjNyAFl%V<=DDrm4m!XeH0KgQ?F7{Qa#V+i}!44|9It*i)Lyq`cY0l||+cRhlxQ3Yi zagJz;DU6#$yu0H&1Qf|{FvpohaqBEjGGiM|HFJbuKOw-OPDP>jaeo?m3H2hmX7}a9 z9mp-jO?#g))tB>6=yFtx7G=oXR9g^i>tdV{WZ#7AYHU+v+6y40=wk6^P}UpLRS0Si znli^R_~DLwp(}08a@%tH`7o70d)3y6QVMZ;4E!&67YPS1v@jevB9z&c~$N{HMkp^d_xig;5{&k6_%@%M|f8-~uv zwq+mQ%S)X4cRPes>x_S?%XC_Df5n*Iigow{^6y?<_RgF9_3vn+{sp=#^{IrBu?fOM zH_~fP4+83)h6wtXM-O+N5=2fEkr%SH{2Y(`Hq6rp|NT{iMP{@8n_f`+6{jQo`xl?J z|NGbgBl;*a0inncJ=I%0Z>jDl;5U6@q&r$buPFUr%s~r(lh%JucH7OK^BnzMvCi;G zKN+n3!VRpD7WJHyg8#v-k)JBJ-lA_+#y1k?ZOxI?D(q3I<9EicIj)qogVzL>>-h-q8_X690DAPq3x`MyqJA0L#x=pn zPYb=F5xB^=)V_au{ZL6}xy!PK_6`BQryD*&SO~t8Mvlm_(O^^fSSAXg0{aeuwb?DpgZyiwbqVX5%IOp1R8uxB66*}(=-(9WYR9T%Z4 zQFL-NM@4Ny4JHLubsP=kPlV7z@adz9_50vAd%J+Gt=p>SPgozb=jAwXyt`|}S zUO+<0y~=+l|5?n7JjGr7Kc$Mfc=A{Hv`2^u?-W}^dkIFN9hLbhUM+en=Z~>^-&J7o zjCeR+59%VE1KB)nAOxyG&-OB`-dF!elve*r0tgWQsm^q5V|Ut79x}>~!K!n&Plu4^ z;XWECJS6ye?n+I=UDpj+UD<}Lpaj!aPym#>>XCnmMR&TrL}QRd3oB?#Dq6E*K%M)| zc83-MyJGsLSKeDff~~0f87qc}Shf%9XEhA_$t!5C+`J|f9Hp0$e`?UGh+U3>`oad^ zoG?8LWI$V0TlhUuW(IZoi`Lj(=uFczbvC7zaWv%B26!dZZV7c8f?Y=7mr=MS)NToN z8-jmbM&OrGxFynfBy^c`VZ1ym-}Uro)UhA-SBCXoecMz(j41E;q@#QC8wN@X+gHYc zu=S*x4Iuv^!`i-t-N`zCP^CqTrut!GvBTE%$7#L8p=i~Gi_(N9tm@FoLkEayHY{LT z1mH*Y3jQQ%rrrz-Wc^vCPSX^+v7O)~x6i`n}1-SDhOU7=)z9gJorcoGQ}L!ey3-lBf4 zMazfkwnLQ|uG-4$(9l6(~kzbJnq zJM3{7V^~5^YT6m}l}Fj7V@P!GNzDUau|-5vI^bWcWc&|I_DR4HDw2L->WVps>x+fN<`2A>9<&uAJjHVreC3~clu0SS&cmWJJ`FD#@J9%kT=4_?W zD`rr5c>5Bf{!*fOSybj}Gf;kylw|Pr=Q_r+K(XvvYuK5^Td1Qw3<`Ceg;YE^ZtV61rg0VH zxg8mz%a$~CfUgV@FGAOVjjlYsc>25(EU&xg$7`&Kn)GEw@E7;z%GCr670SvfE0Qwq~Kr%#@RcftjalJF2HCM&VPn^;02;3`|DpT2q!2<}aXN>5Jck?ed-cy-*x~aLJ z!*wJS!M$It?WD{^Gzc?W=!?U}fm|KzR2GPNEM&SoO&o7`8#ekm9(D^B5_D=jFz^=s&LK zssBaHM57;hE{bK&eN+Li{B;2J|L>Fn4@`_B2_AdHfEQmNUX`-U<^TDX`ZTtm^AnTY z^8bjOitvnY1-p5n5d;z(xI`EW`T(o$(h9w@mOHT#UcroerHX&RM!pc+u|HOghClDM zgv^8&osR->3Be~|h>w`o56{3d$F(5q(i8A7k@#^dYf26J~LvBDBe zc#{8=A;|9Q2@~v;t&@x|-CpJncr35m;F7|Og1R{-3n^#dN16yl2VBUA9!1 zxUHNXqmS{DO<#Y;B&s3&-1%};lSEmPV1Yy!Fqw3*Gr<#0WStR(C01waD=+`ZKFq22 zn8)hzV;i&}D#VG#JUN9XZ0=*4!IDzefjUKQ)=C6^DqZ+DxolL@g#X-ByBN8GVul0j zKj^#5a~AZYUj|-pa!i=B@{C?dgq|fBt{lz;ip$xCnsk3eeoDNN25I^*ckyz7ZZrII_;PVM72IXu$jqIlbRucZC~G&f8lg&t?gFUQvjm zVP4d=c}P@IdUYZ4n1vwyY9$}xsFb{Ac4G9Ry~QDqzVJjB99}n=L3*y+_ZC=nM_VHr zx1HoFG5>#9S#)Hr>Q%A-7JSO0&&EqWeX76lRyfS;E?YzLG^o>{xd2HG^Id=*755$| zTjz|;0a~|2y+7R~cXv=eJM9Sg@Vyc73s62YmI&oh7hBUc2PB6Z+;E^Qcw&<~7wY`t zamJLe`572eui@B7D82_I515M(QFX9e5qc7%p?iOOl7#W|NVMSoDmIXBidOUt^YmW) zAeJxREk$A>9}7G5edC0s9~!3u08BVLckF)4&f0~9uP5yz?$r>wSY8+jt64^v;{nM-5cqs8G zPB}3Jl((I?EF<7iUI65t8O0tIhbGD1Wvzd9rK0)uMm0K4pj*}hY%Nb;r#B(4?v`NVM@;26(m@I>|7V_OkVQn(FlJc zATLPQ6e9p~r0k&-6bpis9K)--<{6~(3zO-73LC&J2P<*=OY1n~b4Oq97O(loGWh{v zX7*+uvm}fE>_3wMlj|XE`z)f_|G|_{v15|Gkv3D+u|hD(Z_>eS+Hh!si}vCpEyNd3 zCv0J01i!%((I<3qQ$p=jVt$e<(=dND7p)J1;xD+PAV5XIgFQiRFBDQ|grw|tUOYAN zNdFK|U5y-FYKu*yYLobpFqm1}Yw!|gXn)dfYM|k=>-k>`hF$2}9rUHoWUh{y zG86R2`(3Fy8QPPPvOeiK9q;Llj$C~wj(17TBe;Ltk+Sa5gDpWMrW%4@EhK-U*!L8w z;V}gr!*7`5D7p=`;wZ1YqPs(S7>K|y!+JrCC>fb9j~n@kqT84^)fo2FowrfGCf=6S z?{>?AM0yOa2#*LR#Njak*$T|09>muq*C5v(GUG;ESj&n1A|R+HakxLTQi5g(1Rh{* z=^}y*ecvXTl-6Axd2Cdr{AhmxCS{HQM8D6F(K8D)jR7%oj_UkyIL+HYFjy6_Vv1<2 zm!%=>n2RQp^Q=*PXQ5}mK)1Ao*W%C%)MCA<=axu_Hexj9>6>*t4Y~;Xl@*rpZ^RLc zY_R$q^xMHdySNbylHNPabLqRA!PB_dv(pOU+=Qy7Ef5`_)k1Rpe0P5|4Wy%Yzq$m^ zDNV;HdOud$4@RP~U`+88+;YQpq1nd^Z15Y0jTXXhav61gjxK<80KxMfGVlW;6BuGN znZlMpc%&&v3l&PmV;j<7hVvpl95sa^0U;_}LEfvO}bczYKqyQEWSxjBW&7HbN8Sd?RZ6p_i*}T#?S23?ujH$@30I^?l}C z3T$Uq{r&PhsI+-@L&{iljUeHImHr?GgXy~g67s(ZHD@`^{^I)u{d#EVy07BG%xNn; z-sibSOFGe|W84cx7znM)aSMP`h>(vj)#j21agrndx|FBvf**fmdJu`VVD)-RNHU1s zP!jypwTmA5ZGwW+vBZ}e*@w#ZtK}!7k=2*GTQf~_2P}4HSkRAVc2e~^7*uZA1wKj! zBI35hq#HO_vF7t^aEpd2PVI5ZPl8gs1kls+Rl@g=s$D8{3{jQyp?=vi` z@hqrKu*<8RsDFQ8+G-ndDs4(mG=5`u9v9@=o4kosI}G{0>iq#wOgz1>?%bl4d!lG$lOKrKip1rmvB0ug@;i4ebn7E<;ksYaCq6%1&I zpenXZ0RRIbaXR<+adR^qQB}oN%vD@W#lfHeT2k9VP_z^Q1S%>YVGsZU+JIUA^XJ~m z1CDv;ySu#o^Pjv;;O`rEgTKK)dxvWzk+y?m3Tu7}Cp&VMy}q1`A$}h&7)4QY?S*=?KcLzMP1ndb*6>FQC%1dT-yPii{_bY*KY06qLt1=igFQmqmem_m9`m zN3$EOv&X^;pJN_e?P&F0OxE%1v}k6;=a&J|)!#96#cGhY(Mv%FguaVdj?XHPlUoSF znnc>aUUN5YWG0)+LWPXgZ50sbhd8 z39f9TPOJZ!)E`!}OQ|kwEnWE5uJ{(OUNvn10|{7RYYB&y{PRH!%w?7N;b;bj5oC4} z1THMx{kfYc4S{K0>X53YU%Tc~^8n5H-igOfCV(PrO&aFP0A+XrI}~=`tjKTMD7Fl{ z)OUZJGl(CgXod2B__d8so@$jG1wh8$uH9V)poyI;@{RG9whefrEdstqt}{qHB4E7wrxh%*_aF#BNbZ({5iv ztZ1ZKv1z@c@*G4?cyp(oiv8Gh1PzwIMS0tOq1$AD+;ZvdCvXest1WB0{Y)lD`eVS} zQc%Fuz039fPdtXMPb~C<|Tj1n&V^pldHAzeXh1hLD7wCnbV)1Osw zb9G49VW{3iL!;?e5coA|qsz1<#u(60F8v1%*M>fosd}#zHHd;(X)ut3xCpEg8EhIc z*foH_jf8t1kmL&n$CQGjRPK@R033f_n=3}_!6O{h;w@gd^atCrJD|@{l1w~~u95l^ zN1zmDG!fElNQb<9#ue(Y($a3aM7d5|!sVT__gi8=$v4^Ft|R)>M~VEg9N+uIc9I1( zu|Xod+skY}7Mt9sR}bvdM%(hk?*H!(&qy7X%A~mOgqtO#anb!Xy-YXmO&x!M(d^Sk zMRvUnMPB)9NkHc;&GsIqtM3i_@{VEa`b!+?ZZ9 z1ovE{CVsT^xkn`6#8IT)7>YGb^;_Z*MbQYO+Z+%EA(aHVIKg>p54?Y!@R?Qx-w;=I zywbyyei;`oMoE`b1vvdgqB`=?sM7f1aI0yxEJsYlH;EFRhn7)4U&-^&gE|Qkw|n;+ zYe1!Kbxnzi_}PA-jO#Vh$dxDXJqX5Gtvs?+^2k=%U=&9uRcv$KIu?%p$`5P-G=Cr9 z3!Wk1g$-Y%B2qiQ88d&0NmkC^$>^~=0ysiHJ|!iO0~59bDS24dZ*=$-6|{x+X(XZP z@vO+8T$EsA|1-GbOKxm5|J1jRxWX)0S^N7Y^xEbn%Owpc5k{rux{B(^k~PB;;3i$6 z3U~UKSG(l^#(*YjEkp)}&$ytZ`>~6Fr&rKNGbCrC@qa!DqECMWIU(F_vo&+#Po7it zc%zQ%BxQPedXZ~mXixQ<{oI(bR-Kmth=JZ%nORhI5EEEJfovv+#5`b%ZjD}cvOuUy zLLXiu32Ktpdfg(7^owIASI09}if1mZ40c^y!$$um$&QdN@JM5v+A#nChSnG1ku7v` zN&{DPhTj<7AXq!a;Al8fzC;*QwpK7jQLR{R93-ATPDgvcFF@twNWfp z`nCip2q*ENsJbKeq!q{-0@R=wK{1aEo*FeB%P#d?z%&;-bkpQ|$l62x`Sf3urUvZ_ zC^%M_Dpvrep`c^HUd0t0OM;1RW+{pBmh9ACeZjreTG)S^AfFi)5@w57MbazG*kuSj zK2JqDcIaAUG1J5j3oET=CgmTOS0i)D{&xD0&fkdX+uA1H^B(X?8m~KHb}pz_a&Sft zad7mY<2dY*tDJQ3)w~7o-o`&E) zl+)0+8&QA3k-Ke~N3od`V391wNpJkIW4IKhn#oc`+vTz*@cIe2UKYj%{^vw1d~-z! zf<*${P5w+wbYxLA)mspck2X+8=iDRS&6{x8gJ+rU3WIfqvf=V)B0$hW-@3Q~^BFSGHbL1V|Lw`uXdt zdTtq|=6dc-PMJq%n~f2xeK3;+kN|+<3Z7o=hZ z&$hwOw878H4;f23`*C@;s;!@F&i6gwR?mOREAM>#irM!TyZvXMTR$uMvHhEcD@>yU zi)E)+mfI5wj5`oNu|+`PJn@Fh135I?G{FkV^j(|JDg4Az5ne4sE*2x(#A)Bl4%eP# z2k3QBwmqm|wL?r&xco0gNLG|Ec*0bV4}ecTDBEJx@mdoJdv)*qo5-|mgwPZXhsA#! zkQ-8eVml55#o9bBWP4BM-{JXL*bw5-zQ*AW?!(Gdkq$lY{EnKp?{)!bN>hzgrPKl8 zxu|9Y*63nF7D~bXAdEGoWLdGJMu-50Lv<9)AIx=Q*9nk)XNuYk4o6~2J2~RTY|CMZHIqk%2~>%B6~%>1GEIXU)UZ$)VXc2~;)^6W zG+7z_WLD}){>qS~juGMbWiKdJYH#jm^0k~~LmB3Y`uumxVfJXJ%*L?!W`zcayfjt# zNv4CKCe=%trKDGIwP=sK8P^q1dvyfwDl&s8G*9cYOQBR@OEd!{UI_4^aE@g!_#UE@v>!wYHPkSot&$-43>U*!21rEv zxZ6bSopFm`HZBU*cr0*M0B9UW)R@c?q%bYPYIP}$PA2o;NZXM%OgPKaCn^)3t2LI#S0kZKWhrr7$O#t5}UQ5OT3j+-}yu z4)T#lep(D?J5K=fmnEE}Z}sJ!Z_+v4*%<@Opxj2{yA!$5uKuBL2Z4?m4`Z7N1!WGK zG*g?D5#1v$t~WXa4Y_|DHslvrPVw zfego{96=Vmuy8gtlndFSMfWnmaquoqx+7$aw8#$jAC8|q(Rt<5=cz9|kn@G2aLNVf zkwF^6*ide}5{iF7`)~F4$?dp}VfLA$kyXvJ`@WVR+~sTq@1og|Cy}GRlnaCUPHT)MAS9Q_@?rvsYef>^Z2%8b zO#?t2PifmO(*_vh!42_zysph8I_Z2Q_Q6fN2WW8v&QgDg2hpno0#o?+o_651clscP zTio8IPz}FE$yVs5@KTy2!b|~Ial?H2#JJdB&hO)Y{db`0-S<8WYrg{4+U~-gS$#=U ztI96PxLePbwzv+Wn-)dK`nrp@E%E65GYl^8h!k&Q&n4Y}Z5<26&&x4aL@ND;D%urW zWMrdN_KSZejB#yGVr@mMfqPc8Bwkzbj7^gBlKSG*9zGE$y`RP>xzfL@%V!A_6e%Y- z9eQFxOU*w*6q-53+$12`L~yBuoeA15GIdla_AjL7%#iW^EO^7nj{v~0y@IR?2nUMv zSR!_1zN-}^9lYN8$tDL)4(Iz6SHGu6-QXV2**AZt_DE0VupbxV*_89Yz6LS%C(V<>$;#ond+L6X(S*J@E5+M(Uz?i0~Xd zZ97-x)ygJC>6hULC10zz=6j@s(yvwG2~tWvHb{=|&vkgg1pK>3%$_^h=&r`=;s!cH zAJb|W6~BYFi22tTJHcJyLdX*O6>~Fp<9UDW;=9OI%?*`9(d3ub1?qU+@7C%nyVOly z3|$lNKWn|V?>UwoPBt|b^vfXCl$5E40eNhoqj^q3GP>GByQYsa#hj+@ha@QInBen9 zBUVHwXx|a%+!%lv$Y{^CWNlvUb&NH66>1PGDMyooL)9#~jysKp$fMBj*ioL~aSnfO z%#_x1+Y;QRKp7Ym#wRA&R|olMGO0WaP%GGpBI#=fNslL2NrnrVZSUE^q$C4Ln+*$}@eb zI7F}ocdoY%qNCCF6A~+vMXXj**%pi}r_q4fw&WQUZD~I!plKTFIp3cg%Km?rJ0z4( zK6R(Fxeu3tvuqsFb(r+e-Ay9T;YErk9ye+y_E3xk2D6Gi_{nu#9%R+!#F#cMk=b?$ zS_aBH-Vy1|@nh@iMP>0kplP@w4%O+|#*ed|7+`huZb{WkHGUbT2@<1iU5Fypc{l0T z3ew6}&g03SP^u%<1#yF7mc*QJ(^S@4@~DH?sJ^$-N`+tzSLc6R>fA!$YE<9X7t{5RV#$-gwQsh)y3`3aN-_8$t+6rqZH_?#2UJE!PRtz$W;{-kw zQtvfokb~UH+cAGOHEqO4xKDzUz#ZwdO6RKcs1@OnBC$Osr*Yv$uF5a*dPfHNm+O&? z*{54atEq7TjcW@PLM2b1W`7}&h~BkrLNqJ6WXQ%(N8fQYgt?K_OU`)0o)*?FP98)c zJSyn9I;Q&@F;CrYNkU=Y3l!<3i~!Hi{v@~Lk}bd5NUVR?tqF*}cGVPOQ|*gV*wkSu z7Hb_*88p|M>;5Fz@ug!u)wap9WwAG;-%i`~Y*S%x(fpHx?y&py7 zE;OiTFp@8pqj}z1i{kD|FWblU7qrIM(=g5j$&R!}x1vNY z<{88UvRZ#?+lnfF$b1Wm)Uz+ut=1J1Jke#+8Z8U_B&Ph|bN(3~8W59;01V`##7-A+ zB^P%)F9JmKSOqojxnKp#WXYo>cXuGj3M_imq|FcBDUr-?Y{zVmv}|_I*coIf{x<{m z&0AkCom=G?rh3nckBPZid-NPu!FDn0OL;^acPf94d{vzl`x+jn(05Yb_38*$S=SEv zG_p&*jXU_0B{g1D9Dy&7m;Mek>W}VPSo)-&DBbTh6an7LPGB|%gNjCW zkdY;ZooxBM?=}}AKu>FaD+^n8z?gX??yi<)YF7;S_-a=1+j=()iBW3h4 z=)He~%i`6<`BFEjvJG206nL{F1aVBbta>rR>a6Q!n*3V!5Wr)DR-=Y;hT)N_nLa0b zURtWHJXOsKVWiNQW%?>}WPCqd`rF~um6jRc>Nk5{7@d9Ro1WTN#^!&8!c5t+NA?=0 zBz{>w@*Qxqo9p^p6qCuN{3;1{^9BK~f?|JPSL(A!>!g3wxpA>0{{Cmi3KX*}K5K-JhX{Mux7)_n ztlSnc`$}P8)1ZV?Y_%t9893apKNH-yi)((+A;!d`!$cRnCc0vrlu>ENB9;ZrUOeA~boU?*B#QxHiGU#nWz=>aL{cz( zC(;~kQS?u!UajY=({A|e(9|zutYR5f} zasCNj>RFUXhIqrSChbK>zA%}`g8r>reKufy=HM{?H$4uqel8-f5bi6mmzO& zrvE44<-VGHTOpHIX9(&D9mbCHIf@<)^w-hzIg2kUZ&{I!xib!=tok9;a5{h4%@m1M ze#F&aqvrjgDl9HMlt~glp??-F`UJ)PADG4F&w&n@=66aZ?u=LV5e3%JLW$+2;iW9A zcxHndUxG9*Ug0E}VZW`CU`fJ|Yfa0dJFbffl9D}Bj`jx_c7GzTAFIm$Cz>}!%vDoa zH`7ttoPxLgVRc(N#d*!t;)H){nP0Yw@9A$l4cO#oR;ljB!>a^buVd*gO8_77`<+-#z^7w@UD6QtX=)?>GTBLHM&MK~{;Cf%d z$(=Rvo5TW6e4lA|KCcpSuKV*9=S!L>)VXCFZ3;XiW2E1K)U$}oG4p>Vmr0aNl3KD@ z=(ObkQ3yz{A~XY4tqy;C1XyuiuiIHYUlVJrECCf#g?O*0E5&0Mjyv-Jp|d~w-Wn$H z>^l41x2 z=I?&_*gpU`0tx=hOHF^B;{2P((Y5K`DE1cM@z_2hylv!9&-(3H&`&lwtI9s~ zKert>nYBG)Yi*FX8qT8oD9FJ0yq3IHBh4&QxWUU8VB4h)U$7ZOt)2r%)&;PD74Aw7 z4M%YWxH_kQ6Xi9Vfl_L<0{IHlu7fKQOjsaYXPrff@4VRNZzzA}#P&pk;}p050ExbP z^?ul&z-ca))2GOrGDF#6rO~e&{(71#)omc*->YVgr&@je=6!{u)K;V*x-iBX%p0&9 zK_233)cXrziry&Jy>!t-U9%)<|D-^gVVr9S7qnQ&^>j~Lh*p;Nk@PJahEQboC$9w4F<#-b zGLU-Y!_AF3$R|IgbYz3>q>5SI-YD)fhLf4@&WL8Bf%ShgfaQ)mQ=H@o0hhX#goCMm z?~?dEb~tkolPAt}*&E8a`uUW2B{FRIBQfk00JS0a4<+HU%@^XEHv-C|WIF+XKp@G`8jK(M? z0LN$07gz~#S%TDQz$y&?e33b>J&&w95tKHpp0T03x4`-ctAJL7*SR~m?JZXz3yqip!`YSHQnxSS-z zM8LqKd+2p9JD1AsjtKO}L;>Fs(OiBqMem*R^W9Wlbx;v-p&0KqFV)cMUoDLgkHG^0 zR6d1UF8$x*1G!Oe8NoTCdU%DUT6&x$OUShfQ*I!33M;#MtoNh_D)m6Mp4uYhNCeO> zAGUv@)E=wVr@bgQ2$B%9(+r?7d5b&-HIj+#78xc8SKRI)*$x;+s@t!%T@!H+?i2`g zmQu55dqt?>u z4#Vu#rW=iXar=3-ak9Kjf?Yk!E`tbQJgBM+iY~q%93_ zDm@+#&lJz!S9YY(8$h^i#z?b}H}0gke?2N0;&#`K|BYxE-WfYA+CHUh^>M;?0CEPo zh%kM^g9WH+tVu(H<4|y1bv>yFw>V>WR!6K8{s)wt+XV8g<8Yp2812iH^G3IbAy;_c(a4YrllJ5iq(91|X zv#)y}Jy8-wvA33c#M|P11MT7VU_^M8iUd?EwDPqgv{5>;e(!q&0QGEiyzoR;uK){; z0S3Kdkh9JiJT;PduPS-G_da>M-I0GGe%b1@8sg$uB+O=Ynd~F0BBwk$1d< zfG9rmp`K2HF1&C2>L;7(swdZa$|)mm(kEK=-s?fPS8RK7wd1~20A&bKN|==@!Ph-^ zo7V{R29R|1FmBEiT9*HyM_M`_{XI~=JD~2r$81$x4u!G_-3l#W(Dk%B;Ld-wv0<|n zf;Fy83v<4F0D223yC|HHL;RN!^0E!|+VF{}kOv3+*HilEOP^(}f z5W+J_J&7y%uJoH~sS1b_*c%9nYw=f^Uf=(Q3EcGq1W+id*oCNNR`FcN(8@>npss28 zn6@-({VY5I$OLLpA3HlQSS-5p%t6Z%~%myfxYeP~%T_0nJy8|3=VuKte z)ZjBVKuXk<2kd*LqEyBVMl<^ze>R}2#EU4Qie%=LJ4^egSG*Ny=9GTxHTkL-(T-1E z+Ork$!gkB8ld6bZF%W;=VjtJp5Zx~o`Y6ENcx#~PSS@hb#*i-007I<6aRTML^^8Jq z@!1ks-qp;sH#kGizY+Ys9~3J!)(I%9XVV%RUvuClo)rT_ppi&3)1H5pot6|FA#AAZ-`7^X_9 z&vfdd^_*>0C7ge33cp%?wegfzOr(Ul%8FdPD^}UbQA*!qWmCg#mvvDZq>-?t0eZKA zDo{N5j$P(l!MKRPCD|p?t;zqqSE}%m%D1E2C*C@!9X3fxw;+RN>^2M_6uBS}G!aF9 zbCgapz&#>{-hm@q2msZ=sI3rQD>OnA(13D78~l>MX}EvSNTa8wFUxuAurc(xO}h12 z*_W#0FY3}G&NB4H@7%@^oU-+?xF?~Uj};BiwGni<>OmB={t@!OeAQ&v zE}LsQp4g-q#1MLhbQQR7Z!=zF zl1r^ce*!otwtih7Z$X7@9)VmQ{^ZQFn1L-B!`5&ZZG%dm@D_FT8!UaU{HT_Z3+#Ir4nCGZz>!T9y)HeBZf`Y$b~Fi zEuFug6ewh`FE_mq{iF8X7~|rl8q#hdQtu9x>6$eE*gZ%V3eg_qx_rklXaGI(peUh; z%A)Jtp8~Dv#^#^I0rjNAByRuMbi4se_RO0RR?Q2R9|m|-4!m~F;H_6w!iokPtwn#! z{t49_0m9#I6z)d@uQw7-Vrej8HxbNdOTc23pp7E3&3=TTHz0=T852wR7&zb;|7?;Y zG09PR=MUZ3f&NBAt!Pzww+cezIEnX@HGfk?no8cA100rndQBJ_2^UES)ps(H0=h{f z`-3J;RLCIffge)jjiStRJ>skoh46oeMPPxaY`?skX!8PtBO!iG1E`=fvJi?$=n>a- zg^IwK#E@2Lndckn9E{I`(Kvo=6UavI(mmWsB#%<&eaVPPu3z-HPR}T=((qGai<|8RAZLIzb8+dU!hnDIWDRtt z_wRvT80Rks=J{H<7vXs{5cYi-$W6HH3jfGP1=0P4S3`V2a|l^7 z?hL{i;tNFYo9UJ@Ff-%=$w9xqMv%-SFU|Dc#|a5aUsKLHgK-XB^) z`2%HNyZdQwfy;zWqREYxsj@x6QxooEmd2&jUkx-gTH_-(IA2cd!0vx=kRzk5fhLFG z^B8~Nm+sT?BrX(N{V<~bY`SH_*X_@{R=LJt6*O2E6jJuAdm~LaHwTO#5dutp-9?IKH5TvK68`FPe3Mv>|HwO=KIoyto*R)~-&f)6Rl9R)`?<;59BQ3Z-y74^& z4OBrU{MuWhm@ZVRZ5QW*Um7*>ARC$`y!|l}>%M~`BIc$L!<=Czc}Z^FLulL#Brqno zMX@o1$5~N2-(#Px;5mM`i7s99>G#I4Gvw)8;C;r7PkQ=lt@?kcQl`>B!KnAW-*?>) zX2CpL(rKg$QxfJbp&@;Q-P-GXi^nUV1I`?&((Qytokr{ovmA?udRB1OOR#8OhQPNp z93b6ZcA(tKUH^L!^O^T>@FLjoJZ-P=ASRc$=l_cbM@$~Y-+a6xX-Wj`(>ifZulNAnXe+r6$FH_0h>aH1X8 z>lD3Z43N`ZexW5aheJ}~2BUheT3|!|^m5RLz68LN>oiWDzsw`+>%>I5)9ARcI>dT) z=h)L4h|2@7>&X@MshApW^eM~`!O}pe&1w@(aMX20H-~>|^`#46B*`iLF;bh>Y>q95>(XH$VAM=#dVHZ( zybU0t9ov89?a4`K%R|5O*ciKpHBYyC2#wg4zjbYIy^DTRZhU(>cD<5qdJEA?YKUr^ zy=Osxnf$2g@ti(ZrVo4=VVyR2SSYUOrU@6i1#Cs)cJzE4)jS=gNLhPdkoY-b^;ZG^ zC-l{E^o-2;a8auXb_b$QBZsVZS)ptTexJ@hs)TOIa!x{BuSVvSJRvyA+c! zWc7c!%g;Y-ZBtQc$&IsOgHaH0EIWT^8o+$&I%k82Vy1bN8563B;F0IYbL?HO@;bPVl2H;oJSuE(Z?BfnvPXr8>PAhMP0RfMD9Ua7FCmbHX8Lf6bpOwU zNuN6Y(Am`)(bQ><_$-)M=~;w)Q&FMQQE7i!f-i-%85(od@};r9h!2bFR8Z#yM2WYI z=VAyanxeL${|p5D4OxT|OX|C%V^1CF2b&9oL?br||DT zU&RvS@!#PB+#KN}Pno>^eA0{z-bjCIjcBDzIPPb)3xys%xt__=FWf~chGBTH_fD5H z+33|M?$0>ZAIdr+9{9ouv@1Margq)sO58tWt8F~3!Y3p=M`#r@m;IEp7#qm7Pm^X= z&G;;UTN``-+6!$pL>^!jm8I6AQJq(%>10Y;?Jq5pDRy1Qr|D$9H&yq-^QwQN)wnec z=>_cOd#K!-L7+}LsBi0!yLpO~-`7@wlC*6I_&p%>*=5~sG`#ZkgDW^+Nwbwz17&FC z0zKL8-E9$+4{B?*%0M^6I+$zNxNl5m&xVwB9E!@3v$T6)!tdx3lEr_^rhW>gBG$w9 zuyJD1K38a>yLA=ZzaqxDp6lmz@5#yAbsNv_QRnoN31iH7Kxe=^fDVlnvi>7#Vf_;L zyF!7tU=KnQlt-DAOYPhxkHo_|=fsmz)nL|LX^xO_e*j0e?e@ehz%AS+Tz=>65RW^o z7*IA-Wlqq^N(&JIq@sT$`gozkeXJ_gn);hY%$wIK3e}+mye~VVl?qWC;S;)W0Ly~V zK{LKlcb6zS1YpnR9EXkxPA>%pTVjI{P3p}`x~3dfQHo=~Oo`;l}NgzA4MTMo&RSaaL|!OKKb z#K;tgv1*Jd-W$lfrX_A_`Ca@#XU~6PB9~wxFv%bo-2%0YMzr_?o^B4HCjepQ2z)ls zBkLK4an4Z=qjko0z76uj4Y5mtQE5qlM&s6^G({;o1nNcQy+1IFUwgMwXfv7nJ|HlP zfNs`xQ7d|&S($&^p_|-sCY(ItP6hBq>K(Yfu}!Cx%| zm4bw&QdG9*m|lbl3ViR7BdVULK){B2Rq6OUl?>MriWLeO)RyUMSb%{-!naF$e zy4)L?z>8de;Dt}pcC3Su^jsci+76*8TSBt%V$nHZ6fo7d5PwAx ziLzR3n=&M4wSboRq`2g%%1oLLe2b zQCx8PlSIY@$c|BNx+m9>jRKPTHwf4o1BeYHuqS^MPwgZE*#!e9%$&2z^aN;NPrx9R#iW)V-JxlQo zmh-!Z>k(^9wf`RBLN66Y??Fm4_)uDrK`|W`#4!e`i26*VZ6<_G=QuXp%k}c3NMw*F z5j}qy*Z?JlH^)bL+MJQ{g|!PJ^v_Y`$2Y`t62XV$rD9Q~J@^8Ms;q19RS3B28vAAR zKz2=@)SL5%zqCInwlI*72D!#_r4+A~M!~_5evAC6LGhDt4GIemHiaOsKHOp-)Ljok z(2cd+HYEhOF%7Y;#9k6i=q8$XDEuvLtx|vHrTx(l!@|gd7aWciZ-&LW0L&4P&gbRe zb)=sHUCCt@{RIoXze$J(PeaH%DCw$?7pf_&r__9|?K4$~N+UT`bp}yI4Xc=L>@qr7 zNq!OshL15YA4!rJ1AMHa1qR>%ibNuw1Ld&u2zh27hQ48&<@6W@5%XW;G>D?l;PZbr z(L7Lvt~HY;)GizKa~M?>%wkcjvM%q>sQi>8KH>7%NrZnwqgW8MMc7(^>?71i@3JPs zSwQO_EI+YrpOTLy4)Ry@g&A*Ska#HZFXg(@vlh(%d5|b0g8EjIVK&L$;Da5ZEy);K zQ_z~Vf)~d!$f;UG%d#f@CtIW`G)RBh6ns%pMjjL!)&rjef@fTS#|`~3+OuNS$PLH1 z>ccxE6F7*Q?X1uxb@RH`@Vx`zi&E9oX|4~4wIJj7og?*6gp8hS~bKLj9&$-Wh348ecy~DM(Ng7{PKvJvjmYgY7u|44p zI4ims>Igl#gJzTTMlC$Kf$=F?0w=v3C`zb`D;*t_gq`zgSJy(Q2!!+>G4LFJ+u1KV zbj~&JMlB&K>RbJ8JwkonF4Btf#e{0GUtkU^I>BZgsdBW9T=OnP##+ni=|78>%~ z^nLB$irF8QPm1ULyi;e_UQSAXr&OdH1nOBx-O+2adXDKn{q;(VQG|mfU=30Jt9!)q zi+aaN8joo7&JQV)Oyz`@>&<%6BRZEwqA;>fGF#a&!a|Kw7f;hqBiCUu!V7~OaZ|V9 z9?=(Ux&(x$DPCL&HAcpNPhl%;MJzsfWvH3Di+93%`wKO$kBWFHqvd6P{+$qZ<%M`V}JaEz!m5?t}?a zHcg#^9I_s;z;{Klf0LGfa>(?HA{7d}S-+-edu{0jN=$Sw#E+kerl0sbfkVLm-j81U z`i;ooo1^eu^Z(5wd^MH`uL)ia;B{v8zB$STkI-n*>q=%NNpfPGXSmB|+RN)oLL(`y=wC!1?=HFmUT|BT3#~f~B`Mkzfv_cBqNo?ZnsPO5?H)h0Mr&Ld@qDisraqVqreL+;KxI4CrzLU5o{eVxlcL5 z#F#AZAx6geRQQHE}SKf{%`Ls4!TVPtI0uT`=*XCEi6%1?JKVoQBYk zDp(yF7?t+xl36RJ44}A9&gmTwGU*{;4mV7Lfs+=kk#Y6(9sPX?&#R%8yB2bV6VVir zyLc0>iDM#)p7BL~6iS=gVkidw$Y$W6Y$k3l%z9OJsPK<}e41I;MkhumM1&KgwGCEJ zt~W}5Slv20n5&=njs9VYF~6gM8Ai0V7J$zuXHog5S=%YcDD32z=ii3;WXCdbi80Jl zN<6*6SQTU@dEW8mj5gqGQ7|O2IFTg{qd3YVpNBn$Y26hbK-BY2N;Pm{R3N7FEj#ss z)2|R(7=aysD(@q|G~>Ak(B6t|8=HwK&->VNPJZn^kNo6poQUxcU-fvC$TP?TjruS0 zF7g6t8hknA1k{{>8vBt$XnLWc+^1jZkf6GR~^YDZww1Kc5a1?o1u$>RM@Zw zba@E%%LGf8igB6fA)(zKN!P6|>(^I}sKHj?w(H)1dg+Yv_|Ky zOo1k;RpHqZf!U_3%F!dfj){dBSL?3dmPFJeG9PECXGm;B$9T_eSx^3cMi6 ztm7|#Zfv+N3q�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 diff --git a/pc-bios/efi-rtl8139.rom b/pc-bios/efi-rtl8139.rom index 477f9b9b916842dac92e19f4d653ba0d83bdc814..c9c77ea7f011be51327ad86d653ea62b64878799 100644 GIT binary patch literal 200192 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 mZGd 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�E-t8wT9Xd-U#@P(Z*K%X!E;Bv0mmr0;*wX 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%0A~1;AFHYiOj@I(=my- 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

    Wr8oZA$ekDc{Rh1 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

    E93tSv+ar&Pt>~-3!Fw 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

    ^AZCtxkf`}b{bEoa;fZ1}?y~h6p4p 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

    M%nXZ5-je0^0uJsfIa)OpPC^iz>U6h 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 J*nE+F|9^td3Nioy delta 42518 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 X`AK$P=7(!~_DQz59RK1;_VoV%QqWG! diff --git a/pc-bios/openbios-sparc64 b/pc-bios/openbios-sparc64 index 61bd46bf440ce9b6e2b4245d967312a08e4563a9..7a0cdbe6d72c4c85775145d1e7d5ccdb260db39b 100644 GIT binary patch delta 53000 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 delta 51683 zcmce;3w%>W`u~4s=;ai-ZFBN>Ie;qNc)X-9^p1tai~gzt5a=9&YaX{r+FS|No!Y zt7qmt^UO0d&s?547YKY%8~C6$xPaN7^RFAnhkg`_U(heZ78v?2Mc$%F8%18C$n!Fy z+5*G4uJ>Ioxlw^g}?>qK~N3oN#F$ZB5(kD6X*aNfgPX` z*Z{c%DxeR60?4yqSQnr#K?;yh&7P(!A4T4w$SxVdZZ^~frSk!D zYa3b(xQ@UHxSqfPSU{iy`~-Hu4Fop8LIM@Ah(G}>wqQsX;6{QJU4FSVqtaxRoFRSWeIaXq2$-vTXK!sL9jFJ}|CP<0_2nQsX+sxQ;Zg z1)-+NjZz-h4~%QnxC-OC)VNMDt|N_WfpP5~Qm<-ce>JXe8P}(b>z&5+TH|_&aji10 z-9zfthCHrs8S$r#>z&5+TH|_&ajgn9&AcTeywWxxwEKcW3}IC>;>_x0RS>`lngHDh z0svaxRSf`I-c^18E$^y&04?t-AApv3l@~zEyUGKg>nf7~@^zI7 zKp~060p#l{+X3Y3Dq{fhb(O6E@^zIFKnW>m0g$h&3d!2ko&A~0?2()CdpaF0@fgiA%pdPS> zzz1j|@B;22@Br>4a0BilZ~^Wns0RFozzMjAzyY|IKnDZ~?0~fdHo!Up6%Zm&0P8I{ zwF__`K?<;epc5bnl7MD{1mJ#xIN$+-cEE!KF~CNGR=`695x~O)Er3S|!hkSAGvHBz zAYcDX z7~o}sR>1EGB7j#2S^%#SgaI*vX25F%K|mWp6X11%0AMFU1KX3OAE2GU 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

    s`LXpju9hAxMB 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 diff --git a/pc-bios/optionrom/linuxboot.S b/pc-bios/optionrom/linuxboot.S index 5bc0af0..ba821ab 100644 --- a/pc-bios/optionrom/linuxboot.S +++ b/pc-bios/optionrom/linuxboot.S @@ -76,7 +76,31 @@ boot_kernel: copy_kernel: - /* Compute initrd address */ + /* Read info block in low memory (0x10000 or 0x90000) */ + read_fw FW_CFG_SETUP_ADDR + shr $4, %eax + mov %eax, %es + xor %edi, %edi + read_fw_blob_addr32_edi(FW_CFG_SETUP) + + cmpw $0x203, %es:0x206 // if protocol >= 0x203 + jae 1f // have initrd_max + movl $0x37ffffff, %es:0x22c // else assume 0x37ffffff +1: + + /* Check if using kernel-specified initrd address */ + read_fw FW_CFG_INITRD_ADDR + mov %eax, %edi // (load_kernel wants it in %edi) + read_fw FW_CFG_INITRD_SIZE // find end of initrd + add %edi, %eax + xor %es:0x22c, %eax // if it matches es:0x22c + and $-4096, %eax // (apart from padding for page) + jz load_kernel // then initrd is not at top + // of memory + + /* pc.c placed the initrd at end of memory. Compute a better + * initrd address based on e801 data. + */ mov $0xe801, %ax xor %cx, %cx xor %dx, %dx @@ -107,7 +131,9 @@ copy_kernel: read_fw FW_CFG_INITRD_SIZE subl %eax, %edi andl $-4096, %edi /* EDI = start of initrd */ + movl %edi, %es:0x218 /* put it in the header */ +load_kernel: /* We need to load the kernel into memory we can't access in 16 bit mode, so let's get into 32 bit mode, write the kernel and jump back again. */ @@ -139,19 +165,10 @@ copy_kernel: /* We're now running in 16-bit CS, but 32-bit ES! */ /* Load kernel and initrd */ - pushl %edi read_fw_blob_addr32_edi(FW_CFG_INITRD) read_fw_blob_addr32(FW_CFG_KERNEL) read_fw_blob_addr32(FW_CFG_CMDLINE) - read_fw FW_CFG_SETUP_ADDR - mov %eax, %edi - mov %eax, %ebx - read_fw_blob_addr32_edi(FW_CFG_SETUP) - - /* Update the header with the initrd address we chose above */ - popl %es:0x218(%ebx) - /* And now jump into Linux! */ mov $0, %eax mov %eax, %cr0 diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img index 44873ad1817f44a5610ea556f86394b2c4a4c1ff..3c6b01fc8ef792bde7f84ec4cd9676fe4ed9811f 100644 GIT binary patch literal 13616 zcmeHOdw5jUwO?mul1v`pNr*TRl#>uKfP~>u1wolSKtRyprNPpYK!zj|GGQ{|)s&{n zhiLVRDY5oyH7Yt3A5C+qB~?hQxvg&_^+7EvIAYO^I-yZfG3Wl)I_FFt;&=Pqd;hq9 z^nBmUT6^vF+H0@9_C9-(pUhq`$7WL~_Sk7SaFgKZH0d4#x}yR;G>sC;L#d>q9gk0( z(GQyO41&rjgKR-^qHV{=VbE>U&J5`+gC2wI7k?~!X7^w)zTTi%klhqH_7%@WArQ?P z1eKA}t#n2O%J9er{J)VSlRpM|d^|R5d~*$bwn31x4SEbZN`uF}eR3B(YV;3=N{T_Z zAhFyc&=L&#KmF~5++uK>etuV6V&k&LsZ6Y_ST(V-^x~O zivR4n%n8ZA*l}O;jSW3zt|9#7$fEU! zpSk?VkROh_;U&<&^Fbo9E<9EqNkdVIh6qNUiwr2=>7Wl4%m%FQamMHa`NyPM{8Vgm z*E7sWi^V(6Wp<3DmA=xFUJF?IbUI*qc!Q9Var-+r9P`9{YkqA{Z)o2Xo`>o||-{0Utb zB5_k9X%li&mvxK+CEN98NTv8tkXD3tkkZ;nKhA54?hYL$3f-ma4;>^$Bvseg$a(Hr zWJo2pOl_i;EYfypG%}Jy>GrL6DA8Yswo!6q2MwdGVKus!8YAb>kopaww@3*+p}*4( zkB97G�th0?~ONFh0Z{$d~+s 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 diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile index ad55a14..009bb8d 100644 --- a/pc-bios/s390-ccw/Makefile +++ b/pc-bios/s390-ccw/Makefile @@ -9,10 +9,9 @@ $(call set-vpath, $(SRC_PATH)/pc-bios/s390-ccw) .PHONY : all clean build-all -OBJECTS=main.o bootmap.o sclp-ascii.o virtio.o start.o -CFLAGS += -fno-stack-protector -# XXX find a more clever to locate the bootloader -LDFLAGS += -Wl,-Ttext,0x7e00000,-Tbss,0x7f00000 -nostdlib +OBJECTS = start.o main.o bootmap.o sclp-ascii.o virtio.o +CFLAGS += -fPIE -fno-stack-protector -ffreestanding +LDFLAGS += -Wl,-pie -nostdlib build-all: s390-ccw.img @@ -20,7 +19,9 @@ s390-ccw.elf: $(OBJECTS) $(call quiet-command,$(CC) $(LDFLAGS) -o $@ $(OBJECTS)," Building $(TARGET_DIR)$@") s390-ccw.img: s390-ccw.elf - $(call quiet-command,strip $< -o $@," Stripping $(TARGET_DIR)$@") + $(call quiet-command,strip --strip-unneeded $< -o $@," Stripping $(TARGET_DIR)$@") + +$(OBJECTS): Makefile clean: rm -f *.o *.d *.img *.elf *~ diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c index 115d8bb..b678d5e 100644 --- a/pc-bios/s390-ccw/bootmap.c +++ b/pc-bios/s390-ccw/bootmap.c @@ -33,7 +33,7 @@ typedef struct ResetInfo { uint32_t ipl_continue; } ResetInfo; -ResetInfo save; +static ResetInfo save; static void jump_to_IPL_2(void) { @@ -80,7 +80,7 @@ static void jump_to_IPL_code(uint64_t address) */ static unsigned char _bprs[8*1024]; /* guessed "max" ECKD sector size */ -const int max_bprs_entries = sizeof(_bprs) / sizeof(ExtEckdBlockPtr); +static const int max_bprs_entries = sizeof(_bprs) / sizeof(ExtEckdBlockPtr); static inline void verify_boot_info(BootInfo *bip) { diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h index 6a4823d..ab132e3 100644 --- a/pc-bios/s390-ccw/bootmap.h +++ b/pc-bios/s390-ccw/bootmap.h @@ -15,7 +15,7 @@ #include "virtio.h" typedef uint64_t block_number_t; -#define NULL_BLOCK_NR 0xffffffffffffffff +#define NULL_BLOCK_NR 0xffffffffffffffffULL #define FREE_SPACE_FILLER '\xAA' diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c index f9ec215..584d4a2 100644 --- a/pc-bios/s390-ccw/main.c +++ b/pc-bios/s390-ccw/main.c @@ -12,8 +12,9 @@ #include "virtio.h" char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); +char ring_area[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); uint64_t boot_value; -struct subchannel_id blk_schid = { .one = 1 }; +static struct subchannel_id blk_schid = { .one = 1 }; /* * Priniciples of Operations (SA22-7832-09) chapter 17 requires that diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h index 2b773de..9b3868b 100644 --- a/pc-bios/s390-ccw/s390-ccw.h +++ b/pc-bios/s390-ccw/s390-ccw.h @@ -51,6 +51,9 @@ void disabled_wait(void); /* main.c */ void virtio_panic(const char *string); void write_subsystem_identification(void); +extern char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); +extern char ring_area[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); +extern uint64_t boot_value; /* sclp-ascii.c */ void sclp_print(const char *string); diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c index c0540d1..57ff1b0 100644 --- a/pc-bios/s390-ccw/virtio.c +++ b/pc-bios/s390-ccw/virtio.c @@ -11,7 +11,7 @@ #include "s390-ccw.h" #include "virtio.h" -struct vring block; +static struct vring block; static char chsc_page[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); @@ -362,6 +362,7 @@ void virtio_setup_block(struct subchannel_id schid) struct vq_config_block config = {}; blk_cfg.blk_size = 0; /* mark "illegal" - setup started... */ + guessed_disk_nature = false; virtio_reset(schid); @@ -378,10 +379,10 @@ void virtio_setup_block(struct subchannel_id schid) if (run_ccw(schid, CCW_CMD_READ_CONF, &blk_cfg, sizeof(blk_cfg))) { virtio_panic("Could not get block device configuration\n"); } - vring_init(&block, config.num, (void *)(100 * 1024 * 1024), + vring_init(&block, config.num, ring_area, KVM_S390_VIRTIO_RING_ALIGN); - info.queue = (100ULL * 1024ULL* 1024ULL); + info.queue = (unsigned long long) ring_area; info.align = KVM_S390_VIRTIO_RING_ALIGN; info.index = 0; info.num = config.num; diff --git a/pc-bios/slof.bin b/pc-bios/slof.bin index 69b0a5dbc3ab423528dc10817b37fde720ffd0aa..ab72cba80c528aea2545974e9c717cee583c254c 100644 GIT binary patch delta 214648 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

    iKlZPFAnyy=ma-24lUa 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 delta 227126 zcmce9d0drM`uI67+ZFYCFQVdp-El#1%Zs>NHU&Yr0w!$}W)`-|lucCVWy)#LOi|2( zlNsd_X*1DeVbkI*wP=+lE2?QC+b|WnJ?&U5s&G(Pr`}yeep65Kq&d{j4*Rt*B1M#ue#!@}G8U7$r3w zNAqf~3A5My?pRjMR{(zkcuhEzUx6~fp9GvjTWiW*1xh2yhyW&j8+oYarzRkd0{9w0 z6TsI2_5k<>z-WMP0_+L!Er7iMz74Q9z~cbT0Gk2!0r(C;3&6hvv;uq=U<|+$0Q&-b z4`3|7_W||;_yNHF09ybK0Qe!mIDj7k90>4ZfP(=31K?nQp8y;J@SgyO0{j%0DcEB0pJ;clL2-BjA$?t!KIyfPhFEZ$ZEM}s(t#htEOF(G;P_6i;|a3 zeX81V(6BGO^#+@5nr&**lA5fuaQ0XGHYZ=XpY`cUhmRlB(RE~a7Z*DFWfFe&DpqoK0wG;r z*-Ur-(erugU1SJ|xFO1c_1LSji zajjfja@2mdyC=;Y%nSVAo$J5;92!BmOm0TK>5f*UQQIZf8w|Cy(ICMyz+U0VTXIsC@*omw|p7%AahSnI)-iR zO`{_ZkhYgcRkNgCbhHnmRwCR$!G$&LXUBWfX+rLE{B{g;_M+h|$*k({W%JCc{!(v! zSaK$`-3%u0@8X{Oz)Y{D2iUwmG!eim2_EW0Cqn5BSvuE;=0ItIh0de<*&i)*Ec?Vl zdrR^$%wna{a6i~eNA#Hu{%QkXC66bTr=p-2o1h0@1qeV0zXWtJ3`}ebngXt(^ikmqH(}8C2m*|$=(oy4Vbfz(- zh?UsrpoGb9CYwARd`xkdo>eBV$qw6>R~c^O`*W*dt2cnKmjjcGAX^xK8>!6oT{)C4h)Kwp_!!rg zFeLFYu5Ho@c4#QQ2&&Hvr88qDcG+7AS-Y^NysORAwsz$M<;Hi&+i z;>NHW28$7V+h97p?}W!^J?-(V{otwcWbjZHAwx>kbOA)hj7!+_gXu`RkbOEB(*D2+ zv}YKMEmkuc0%Xap+eg!p%B<5c(%RON$SksC$@U2}j^&P~=D@l|qAqD1HL*GyjfA!j zA44Y^MSo_N1;F&JO<4A`jSe@8R)Y9`?5)va)IBsF?mI@)NTcXA<{V3HQIO?r$q`CG z;d@-bz)>%&3|~TM^85p27DOg@ny_w{n9kM+3%(sm&5)fz2Hif2+DzqpBW*C(En+1O z8fOEv0TTz|p5r1oI~UU?L=<*1%T$;Pn^S3|lwl|t;UJ^%C2sa8h-cyV5)z)A8O)Hv z6=29{;muVe!3BjgfN#hUPz_@x7(>B_+H!E)xsi07QSfKp%l+7?c;Ta4#?X02!7_Gm z40IVJ<1us+ogw_fhK{9C+~DxAGFj4ARq0Y{NJ@f*m_g&$%c_v)LjSY;qVC@$q58p!TT0MpCHieLf5>w z^*}tGPJ6RvsA2I7V08kSkfTPhN#kievpWPzwD^j5c=rzLCG9xf#526-`qtr-pf2&n|$Fx!`HZB3FLN4o#qA z!M0B)(0Cfbn&;9%Mr#%Ob}pDbd!mT?E63Bp#x*<8BYav0_X6sa2zX`#Mgsy~AWN4B zr*EG`&BpFum}L^=)Ri2JG|pr)%M|d^jgv%Fh^F7Mo<|pJn1pT|VB8744dgyO4C5Ev z?s`nb*^mSp-CubM!(Y@_T6y@74d8ooJ-VrPU>#+Z!Fb*}D|jH90(o5T2s;Av2-sN3 zb;U8rBfP)!=g}d(yUs%LYFq2+$RZWvW!ODGS@`wU$*|nM^F0=qD0*OCB7|&aK

    c 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 TlRB5)Te$lt!yowj%K85riurh) diff --git a/pc-bios/vgabios-cirrus.bin b/pc-bios/vgabios-cirrus.bin index 0c4d25346b352dc94802558f89c2a1d1044b240c..02227d374f918699da6f26fb9ae9811726df8305 100644 GIT binary patch delta 10231 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 diff --git a/pc-bios/vgabios-qxl.bin b/pc-bios/vgabios-qxl.bin index 4e08e1362d8328d12dbdf1b1ee459ebfcd8b3281..8a87c2388a2d1bf2653bdc0060620cf8f508b492 100644 GIT binary patch delta 11576 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 delta 10580 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 Te*W}F^YdrSo?gK9zdiI{bzK1S diff --git a/pc-bios/vgabios-stdvga.bin b/pc-bios/vgabios-stdvga.bin index e5e5b14e4d2e6d83badd8c5f0959e992c9bfe0e5..00cb73cb8c1d9a57b70e475edca7e98ad6564be2 100644 GIT binary patch delta 11576 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 xk}-Am)Ql`6K_*$d7wx{WmjzyX61? delta 10580 zcmb7q3s{p^w)XdZge1u23s(UV6*b}o1jSp89hAbT6pSKDYqeV29mW}_JsL_otxYtc zCZHTUduIM~I-PUW_RQ12r>CXZQJ@`5)GFHk6&-tY?2TGSlM_U$j&iZ&f7ec8wLSlN zo}cGQzHjfn_F8MNz3##Orec3hNw0ikk7CbsJ?1+gbB2%m2*6)cwmx zB>=eib+dQY5Z@d*J!#%|zL9`X_pb~u^nS4N0&6`ZV?my2QN6c&$Tf-jRH&gdtvTdpBFmrtK6W1x`_<9s;Da2VC+nM`I z=DDN?kyA{*W^|{l#iX%5#Z8ZfnRSx=f^1#s7}7tz%-*l7Ewc}fs$B@^t*ha&_wiARuw!6TYX58fVLo*x3fb0DKL zd*9;2hRw9O4}&?NeUi{NMfI=+-OSO|1%^rkEUKf&KB%vWV{JO(v_*?=Kjgc!;ydg* zO-H(omv*!F*F#|;8Qqz6lG!^7_zpKEF!%W$fITNO_dqv5JDPMfMI#|$8_v7`Gv@h5 z2dGEVWY8`oj2LOa*@1Qv&k%dB5R@AaW7Zrnx>B5YH#Vd!mIx!DPP2UQkaY{QuRbXK z==6?mkoT~*^AVEN-Q3=7aTNz#9X-vSZkNH+d>&Jl`r<&3tAn|__gSLt3O|{tPccB? z#TW5a3F&@haaZCX?$!Ls@nf~gvf2CE0RN})3-j2)BftvA=7Ywy9@h3D5wSED_ZTg% z2cjc!gN}Yp&6mwbz++GIhdkamLwV>nUuaw_pXUGDI95~E+vwq)#^fB2(bDi-A!~^z zS-S_Wvi#%N8*fbxVyk~3dYHK5DDXMJ<0mA`0X|_u)+mC!8G>0peJo^hnz?bMn+I75 za}4q73HRA*dK-@#E%gzsjo9rG1E`~WAoQxRwokra(BM2PwRCYsB_stebZpB-=I+Dw zD5^6@W?L^yvLsnw0NGyFnn4^NT!e1|^afR7$N9!{*I#QqParl@QefHnYzAv3En)|q z_zr`>eD<`7y}ga(1ZuoSi@PKX62l5>hP3)Cq@nqOR`zZ6hZV>@kL3I zB-P2Kra~C1;7A~19}rvo&Fs0QTfRr$0OYNOSZMSZUu_FwZzvxZBIphiY$(qrg6_B& zIZO$>G5IK8FmbVRuAetfoUarN@KY1>6munym^8#M$0jRPFY%$+bM!bDw~7y{QCYcc zkGYc7wbx8Adv9DPOG_k6gCgF7DVx304L;5=L9qmQnPFOD>y0qHHBa1RR)Fs^%;$x} zv~l5;no6^Gc zEF31=?Z?Ye3yZ+)!|WlxdrWG=QQ?a2zXl58bjz>tPva*ilgfurXSIoy*S_P1gq-Dz z!@LP*$oCOqI?AWvqrk7hSG1kP5w70hGYomx8g4k~9{5Ccj}~59-R|PQPgoFRGJ9D7 zVlaDO=;gkI94$l&Ps-a1()hH**~-)1{1=HUmFyn=Vd6h1f4ahFCoNYVy}}!lwke-= z^MvF{$^+hg_b2aBls$jj_x127S^K+-h$=DsRBQ^@ri~k(eG|ed(ujjy74Ldtg`XTT zR(UzV{~^6f`*=TgjN|hsr%a<8ai6ug%C5sV>2a^Uc*fBETIwqemin?Jj8iebhGzzz z-}3Ac)2BoR*ug>w1qQxnv1D-1HxQ zkb6rE=}Fl&CLhPBOwZik3So)3$X5GKU*JpAGvz$~RQg<=n~|ms+zRoVY>NTIu1?np z&;Yk(*T@mXU|_(YN3bi7QU;dGdJ*HJoyV;?f*}`R>Tyh08F2LDp?95fi3kVjg?+it z@cqFWW|HGSaFfX-McCs(Nyop*7^ggTlShp#vlW6Bpy>ow{hg>3+_ZmS&MnnZCBv#I zUL#c3q&Sa1n~*0Q&7SAscMma#$L@~}xdQkannDam9EQdf#9Q1oCz!`8qC*Q}877O* zn7t3(W}eFt7WbA5wmUTU)DfS3B-xKivX!0c+lO!`2r9>lJ4tmd_AN2evx$&Lk=CSB zf7cEu#6EX%C%JTNE3q)J|E}VIW77@b299udi>tf5%;x5XN ziqG6{_Fnrtt<*mddkKnhg0eX;#-~)k6i+Ftf1UWtfBt zbep|a@)xPJ3J;6;3Q*d`Hpn@6;GDJL!U5{b=HUjaRYOf2=K3Y`&rrhW$@&>gSGWl2tG zctcsY5QTLFTM6#2GqdXys30aFap@XB3j45sCb4by{=Jv)7`;rLA$Bet3 zlFJjie|ffN^9Hui;wnO-_tU`;^HmV4g3N;wPe|oDB()4O-XIe(Gi7VAE#m)AH-p!5 z2by4(5flN6tpphb&|dPONqk+_q=FYIzbx(pjO(u?n;XPB9QEZPwhDU&AfA3m1T%er zY*>98*(yJkp29!PidQE0aBtRA@-+U?*pbSt9w1ETk(2W&2bn!NKekIYjkoG^8&axX z^1aWWo@_0A*f!FXt2%om(|Xl;Rx*uO>vCiP&@{fsS0I}+AWt*N->ypT^Y}N!< z^LoUSkDoGwus97ze#(@bH@&Ql0yKCdiQm5Le@<)*jdyzeKgYNilMuN(J& zJa`)1eaMX;!(F2wu|}CgLGm&@yE~;E4j#}(SKMRtc2Fe2?r$-!mv)>)I$&T2Z|}lQ zQ?3#H@OD(*jfafv)#oMqV4OA8K4`E` zrAmDE9l)&+T+E%(=6kor$o{xHYtTUd1!%QzxDrgS(rY4K*LSHi?WIEMB>p0HZ3f7QP|NK0w0*-QRI6H^#OIIOnPxDFGKtqXw z5=7`!sFWZ*L@8mRu;=a&X*z1Q;MA5&NJ_<2q%Am?*BAEn~Ad$i2fm6Z|rk%Ak2+DUB z6Ifdmp&*!J{KE9HSwWOdx}N4wsWb9tYKkPuk0`jD{nf}8A7xToq~ z;VZrtbE9u!=3!y|6Z&wO|K#TJGbTO$x_eG<&B*3HGL);$LsxPe;@)u2>8KgDqeCcN z<|}U2b_l0xI^08yX&`opO4NJ#?YI)w0y_?n_{H$by!Ixm%Bs5s(HH;D*7&mdTQkO( z%$0(u@E!wlE^C+x%(&!)#V7Py8Al%IeNDBBP`fiTVD(g1=kimbD|AG62q&_ zw;3u->aq1AjEX!@2QTOD^?1?v>KcW2Pmdn0!Rql9q->rW6eq*yK?^Zdtquz+%N5)$ zTqfmI#l0b@IQx27Wm_R2V60Xe*n}@6yIN1O@_3U#r-D^47BtonjhHZIO&GIV6ux3e zn;abq^qXqTkUn35%k=#AUeb?q|Ev+qSeqeuk~+AJ=C$a8!H2as_u~alti5?DUZ@e- z6lL7|gtX%XC7Hsfyzjo(Io#ewto5ZGiGao-U zUxBVx%`LZyPUK;W{h4_A=_t#wqKqZ74`q%08G~Hgdnav1<#6*zA*z(4MI$48b|^rM zkMhS)EcWN(k$e>ytmC16DWWE^wm93-7!GL!tdCp@ZlNg2Ki|?&ngmn+HVi!Tw6y`y z+kXH}cSKhf-i$5nY)-FQw>ltre94nyb9ARtP_8e`Fh#1)0&VR$6MA9KMApVS+pYHx zb*RpNlzqR@n<7-_A0ka?Tpx_|t=60Ls&lEXRyAo=XCl>j&b?Y+iQY6!bzah$49LKA zzFB%pLlL|s)d>eG5L8*K@U`xuz{ zKS!vT<3s@58+_-qBv}}ws0}tF2T~6_mx=csD1W}$n+;(BFjWg)?SlH zFa-i8{t7u_pRGgLU+L{xGpxq7Q+)OYRuk?j_+1?Iaj<#7aJJ#LPjYG=UD6+fHiL8O%QF`IMJfZOr9xo(?IH{knqX}0@3S#(oZ-RuMMjpT;9@I<_xi*Nr!z+Oy3DI7LWYEqrKk*tk;ENhvHZ{Ord zl7Ew)Xl1ZAN*#h5eesbX{R~vsUEIY!5LBwIxAz;Y8*4M{{c6n;G#oCgL4m~ z0*}7^2_^U|3ks!|Kc-a2g^l(ty#QE-r9Lgu@%ooeaP*Rxx{r zU!e9jW!ko646mVlME|wOfZzi~#c@-qF-}h7E_bL!e+1v0n$O~@V@N?`mnF;~{ z9TCU{pROlm;?E>oi#BOZcD3$NRQ<#!zTYO7dJm@pHzMoX1K=iUwm=ngMsPhr_Duof zz#T7+VRNO(RKEp?HRAo#Anz7-YfCS2?;CS>;-(MP!LsLY-ru|YloZm^>9R1oFEfUGE{4rDTBbr{V`!%q3z<}^OVDN{I zyVA2TD(f%#@dvVO{s&m=Bb0Wx)xeqfsqaH_$=H+tvF6@Ux!tXjKOO0TAy4&>226Y~ z*@OY@#Y!C1G#;)%q&WV?;wkwTuvW9cybG8`3Yh4lB}w*A2zxvJ$Kts7E)o)X$Zqmy zzL>#qsbJFwvyhRdXw`WY+V-YI`N(m(b_3-j4!Jhl37KTc$VlqO#*(Zm=nnUzt9g&Q z{`Cl@Dv!xXRq6&~SnEZ>*Kfmot@eViB=B&oC{=tT{UlCNp82|CD6Q$1YZEE0`3S$- zo(NECb4>O%$C#p2XR(3=r|yr4S3j_krVFNc)p;ti_C8ab>b#=Vjy1)o&cli+R;{az z32GI67p0oL%f5mLGkmrfld3w;E0ionMfggW#+cH9Vp#1cQ-bQ$M41xRx_LjQ^u;%( z4AuE*w7M-a+O$NiOaC#*0dD+hmF-C%I+gKgTPEWUlYGA?r%Y+4u2xUKB^vjv#qTJ> z4E7hro4s34fh%bK{v&uM`~ESSWVpSu1qwFDL1a(Bs|xb--ud-ejAL(Ru9Tv__qCL?!CfapW;7RIDDlM z6$S1W%>AX4hzmG3QUY{9u@A;h|2WY7dC&(~=a7j7o3QYJ4VB&2I9B~zLALT6R^?9< zUgvBl|M!J?N@5rPV&MWM{RkhkXw=LJM_3Vapf}+4MGO<2G}2C?$1&e8d81hC8KF(F zh|<2J{OLtw($QwCugD-%y7vrSUHqLz)8!5PZ;Li61BdzY#nWRSJ|YfeFeRzm-{Zeo zJXYCuh_@~tudF)6yB5z&%Gdj~!6F~OW1(qV6z|LA`K2S210G&pnlb91JtCTy7<}h- zKT^T8!`xjuE#^%RYoUtD?7iQ^kCje*G#(KTeg*8oZNgDO2}Qs_N4di6{Z|iiz_~i& zpS2xD^4;Uh(FNb|t1AM+C|wxeiFTK#-G^ilVh^_n_qX#U52i;8q5KC4rGr}^oFnhy zZ#_6ysehmQADp2N_6p11=hU*=Q|+`QPE|$};pp zpsnxm8)ey9uC26Vsn`+x-S%EVfB9#AfB6>0xt{;Ie9R2m>d&|AC`E&icKXME{~jr6 z{3*d<9T6KAT3XK&ml$J{H56dq5&H5LFIkeMT#De0OH%c5qzY}W&Ac9SVD_ygS?h>d zNb$^e>PeX{l(g>2MX&N~!6iInjrf&C3CWAHG_j}n?pgJpLb=zdw zG(&YhC7Widb#*dp{f*e)P5Ui8-#kP1a*KJ5{;Xgr`!oK3%vti=yw^NIM~yz;Hi@S# z9V@Tl^Oj~U^JkJ8;+HQtt!uzl2&R#!Pz%t5crnDHI;jijPh_pH2{L~J8KL0XFDU6q z@V$;*PO)^IICzo2yL5u>J3-rkmKc7xNV;R*yAjLi7d?vF*qrWLgw0m5c^=}AZ-RVR z<@X$Z8y;vx@kcztYg~T=oi{Rl(HXp}iWlV_Y5@AD4rp0rwzk%FFNb_=?O7LGfQe@kiFM;xk@aksnv}(!pOW$*tG)lx!WUU&8Av<|*Y~e!SvA z#ql*B3n!!o%--s6nfr6-`J?zhsntk(s1sdva^Q9*Gv_u^$=`CCzqmX<(rDRXp2Xi@ zo*j1?Nk*5Y(L4!$X04H5TRt@|kcWT0dMblejXKG8?c#-1t@|cdzM`Zrdt%+=qt{oj z+cf%TzZ`AZwC<_upRM0KdgGIiKfym-`JDKlkzKp?-LBGWYz6BisU1hnLm$$DXA+(g zJ~viNR5wf6QktZRf^oM5r2h$)N(l3R1KSHcg!c@dVZ6BNFf1bAU5NiXU=7$PlshCv zkDf4KZ-f}`1j6+|^e&#e@%artk^djCTEN;BTZbh70P(I6Vk@3&@mY=!{ma&!(=`R8 zcOMH%jTmNFn~moe*?r- zfO!BPf=vj)YW@dULQJrUl14~EACd(1y0Kj<#2OuL+#}L-$$)_dpAA?U9Z{wHzYu#@ z;eVt6ckYpW&5!QQi3oJ5eC5uG@}hlPcm7N_dRFd?-29xp9LwxE(`P>Z*kd`<^70Gv U^Yf=anx8*oR>2&u|Lvjw3mUut-2eap diff --git a/pc-bios/vgabios-vmware.bin b/pc-bios/vgabios-vmware.bin index cf2576d3442bd30a90ef72e70b17b64ce8f3710c..c9a94f9eae13a00b231714b5b50a41dd69abf72f 100644 GIT binary patch delta 11576 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 delta 10580 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 U{QT*U=I76toj-@`e|zY^0Cm~`dH?_b diff --git a/pc-bios/vgabios.bin b/pc-bios/vgabios.bin index bad187dc2e2adc24318addb37021400e54d8eabe..3e3335df6a52ffae6590a9c5aacab47dac0d8d9f 100644 GIT binary patch delta 11389 zcmb7q3s_V~mjAuC8)&5Ef+B)~;seCQ2lz^iZ3e~0SI|}gNgR#IXtg`ZWHYflB$Hsb z>1L%tnC!nYJCj{A>uYwivrdAKC}2iuaR80RXI7&oL1nbtCV-eIio*U*-3HBOXTR^C zFU`HT>eQ)I=Y3A;+^#rxC}FvOobSK4_$9vcg@HSVMC(##F;;X$b#KzKT;};JTgvj- z66R`N$vpK|Rw|9NmSluli_O2~OLR*es{5|&yr);)A1Kayit7FsdE-ey*(8 z|CqY3hIt$H7_jeQavSqpu=tiguriPRcUD$!{*ZvM#Eh>}ym-tztR?2(npE3hQ`jTl-Mu<(;Cyn%(ECptc~7SDR3pOtl+Kg6dQLXe5AuEGxuYS9N>{$I$T5mHD8 zAJx4%LjhW9{?Ow4r~CZ0Zeq-)PJbdG)V)i4CKlf3@p`k8*~wSx2P-L^yih+uX}`yJ z>Q@gay@!pmv5{=w!Q@+k3(R$RC4V|0Hm8nNXW#(khf+$16iKd5gFTUXyv*wlv&E#H z#xN_5eOrQ5K+UVpKqSsqUPFUMnM4H^t9xtIk87FS;5o!RC)qN#Vt0DT4f(|$yxUe1 z_ATOXN#q9Ls&!tL)uQ!Mv6s0n52dZ2#@5}2*I#zt4J&{^4|#7GnCr?97;{)=%XZHUcPczDdVmu89{)x3 zd3wz5w~Wsk@Z0AE)^SjggnY7uqSLk*R{c55gjMRHYOT9Ye2c@%Z22KY(kLLWwMfY5 zP`A6rKFZ?T+5uTr^lxD^iIO{zlg0N@2S46#l(MCZ-|jad`jxvDpBd1$G$^6Px1x(r z>pvq(!*a6=SaL1CQ+N2g{RcSOEBcZE;A#ljO@XWqIK3o|+;)vg>R!x*A0qbu22@Jq zH0}lp1n?&h;2GF{?F?Wtm~^3)Rh^(Sd5(T-^_bgPrJ-jWBk;ehGCVjig2eo)=vclmVPxdd+c2(7y*NzS9lkyx zhL;aF1M7mm7zVAJU6@i@#EFjYy2!Mr%#(nn+2V`-j(iPJ5!&2a%+mqE z<8f%5wKTI22~YTun}EuW!sl%!69er=G2G5%#3D~NEWb3nS;(=+idM5f zaWvxt9PKH-WJtVn@ivY($bnuSOl5Xt-mkV8=g z5zNa*Be;3MsiGt6LLh#72($Q$B#Om8Y27>$3A3;~NTQuB#@9_UPYYgdF-!3&fXYeA{+a;Y%R%*Cg|+;fSDHKh%ry%vl$)_IAQq~wv-se78QF5 zDco8QvYTu%GP*}`kiH9;e|>-rqkwERpB3GYuZ#(GxSA({OT9ALA_B#|sr9NW872TL zBD%~5i|^cZe43BJiIJYeGFtC4?{%HkQ+Nfk*iu9m)&>xJgRmp4Is&HzyK15&@FaN= ztFzE3y*Utq&I~n$6+~K{s~tv%n5KM)r9+gZ*uv`rjUF@fb(+0FE&D*SmeU73-Z5xL zLYhdMT!kkkTh^nd>bgY}CA0cogRJ37orLpD2Po-8*2RiCY zYp@^ZpCnCE2i{^?)^2l?|G923X%acoWlKnbV5-b>!de0?FhdKLKsfW2UI$P*MHuF7 zXpik#oGcXnbuh_B6SR;&x#0O-WJG}}Bs`1nW($9REC=M2!mM*&EtP$*pw$A3_xRq%iWd|GiD2N z5s$e80L5QIR=IFFWoG!9&~CO2>IgNFp`FT>fI`baVTvR@yBJeEV zL=i#Vi*$S1^38>_tjtkr-emD1?SpdFnYY8+LU-J!ZPC3NW;K|%LrwOOLP4Lt#tHB} zEAgTiOJnE8-xtodg>b?#(`VK&i1Feir z&D~^s3^uSC+OOcb9nbJ58*F`9CR^Vr!FKF>0(v$xZ~`(5gyT=~d27k)JrG@fkIU>$ zo0bHVEVwKKtNw18Bn3vZ>ZE)=Y*=Dn`h4vh;8}lf&k8<&*myo>q#-tj$b}hukRN(J zqYM@A*}O4gL};vR@eR7l!;N#&LG28gRUbw=*uoC{MP!A@oyyHEDPTrKh~SRiety?s z5J-6yUvHeO{I!W!8du7vczoXx`ogyIdY;jDz|8s-Yw6kyRvAqu>}hXJI|9x|+XiUZ zN@hj$5g9n@B1{Uz@%FD+^)YPPjBwtyzKj2^@9?3ROYaV@$(c3+Vm&RwVt5mq&0O95 z%f3%JvfIkN;HNG~x%d!P)S|sANJHsJ+Ls=u%nPMdJu{2A;)4~~o3tcCp@Bx#JyT}u zuaQ)2>#%p57D-rD0-fc+T>QnFLf^*o#s}iZ=0XN`+Am^zP zs-{&$t5}?3^~@d)HfmMX+Y%ASJWEH?_ZFk9ea=oL*V;Hn?0CoABq@@ly8nQTIs-%H z$mKv14^d|?vx|ZOM1k$VTP5bbDch>aG~fkJbv_*XlnM$Ev2F#T%MFo?k7vwS@cl)VLgxQzf5^Mt16IOwl0u`K55in061 z<5$K2x5f9XOS>x`SLN|JjJ^ub!ATecL)e=AglsC4(Cu=1EMQU0n&B5@ug4LG3=f%pO?5tKSN zYT9gN>kHci^9-wylK1 zA$LN)RGRC!WbqY$-W$eHd0TvjbBJWyekPTp&H`Lx$~GiXo(^;xoDhPmLuTC z@+ARs>jpqp+)de9jAzPb5B_4-k+Rhz0y>>iO1l^|)H-MeoWxK2S=dbGx@U0Mufr*9 zTL4c1T|i&pMQP&^=Di%ns&k658}$Xkk80-oHbUI;MrUWFEza2)WlKYMz&^DX8jdMo z?EQNEf2=jifymxTXjtGf2=S*XBZMZ9c-?VIP==SZ@MF^1AJ^i|_VL3VvpWNiLnN2< z_L1yPJz1@NaL^pqXc~V6s{ew}qILlje!6s?UP3R%@x))a#?>lGZqqsc1$SfdnA=zu-)= zx`PwOeoR}0`8s|)ZNwV%fy4-6>KDLu{8}GsMib>2LJ4BG=%cmnl|Awrid}~ZHzn@^sC4ds}F(XirN<1R1bzz62%UP zegiCqV)uU052=K$!popP5*+qi_;z|tg-vB^X5{vvQ#60iR`|E_^a%qTN1(%^`-@mJ zB_SW0wQ8eq9BUEySS1eKn8LQ9%Uy|;nusra3;sqS(-M9QmcX_>!w5J75GmU-@J`6) zFtR_dFNWM2;bCijCC;_td*KSJNa7BnF{o=G6VC9_0dx}@6n0SS@}M`Ob$Ut7E>O$I z_d{LP2Cw}Pfd|VDB*9%mch~9hCo7c7D5!cCc|1OSVnpOC$WKx13k}LT&GRN&!hSC( zUB&-2G0s7Gochjl-4A)QU00C-^Dd2zqR8*Ma)P;zCc)*==dML?2=FI0y9{_K>Gcdd zNZOBjh7W12a38Dj23RI-JW3s1awL^6ZxrBYo@O;5WntTPa7sYhXq;Y}!92XU3Y!yE z_Xf%l%*{B|yZ1?fC^f4jvQbN`?qvv!NI>77bV&Ie!P?#2e3&l}JqFs#`~MT9`GX$e0)C0$vyk%v67fUiuBa5&$HmR}FI)@LRx zl>HweguD?Y7qn^GB$!++Ij?2d4A#2LM4i8kc!fq00^%{N^Q~yO*GzOo%(raG7Tot0Okl3H^sN|(;f}@kHUSq~pBB*TgI6R$RSY)f&;ms# zPF$V{=N4=tPQ9rXE$kK)OMGSQV25VWD9zB+FgKe*kWPgZq?&NUlNM6Y7u(G3YEa!C z*?$y`902H4cV37I_dsul`1ct=Z?i#lFAt-+0@W0XBrU`grn-L}>VMy0>Z7{*8F0Dd z9%S&B7=$~pmRgV>tNZfx<%d|6yH?y%rh^UK`E?`J|s8|1g}heFp^Y; z<@IP5DHgyoL!sGoSn#6{JZR={YV3KhQD>`zh2PPp-ik!EGX=XP`iwqeHp?I|0xqi$oPw~NU5k60viHVdWM03bD-j5#Cld%-HlW-y_>S;JE+1%S2efE zc~7>@el6K~PiN0ZkPziwGdoBzf!tdYo~W&+;cs9miHew$(Pz3;p}PMwBe60rbFC*d*EB!dgw7%#)WIghz1A8$)oOyfkf|M4vaI7u{qH|CEO`Jn@i*C$-6` z+T$r&geZ@uShU^mfQ_)TYLjrt;Mqwt#z{#7W#MUdOg2ZrURD;i>QPHvND0J*-yslPZeQL z?4|j`Ip}-kLygsaug0-Ebq*?G)S?KTvr|zw=yiyTwOXqKb=8iml;#w-DDI1D(bGs7 zJIDyN-TdRwUZOLhx%*vD)5qW5p$#HH0o^B$v@L&dpMXRenP?lDyK!~ef|86V+=jgz zM5*q};G3V0cL3_BOB6U`WjGrNRa>Y@qoN?(x`0$o=eMFGOJu4W!e#JxNe>V9=A$n< z=9n9++kz#{GY0pu_?kYW6(34`V#e^`35#b$TkB^K zFB>FQ^;1f%s_jtO0L?=^G%lsRmIM(j=_P-273ghLH6{UtzwEvSUY`+_rurTCd;32c zBsfWIDOnngR8qw~ak+O{%GN5$pVzw%sZn4ytFj0R-US6%Vn@pWdOXc~#E=9T zxl7UDz9jCxMTm2)(%GX@|Vjqz{p z?F!OAxxVDhd-jJco%k7lC39fz@h^Jl{EvD93c_Q*>_;vdAGxEM4BL#vHDiD6S`pfK^djyT zalAkhZXodeaXr73nWnU!=aI8!E={dv)ER?z&^<8`f^(oLN42%kn8~&1!n->4cAvis zwFss(aF9Rf;ndY|R&`dWnu0Re@8$nKYuKQYli+^>_)jkL5wHEhm;B1CiSlY5YyO2| zsO3fGiG9}~SVACCrWkd@Uj7&J2<2TbzhO>R{?5yzv!O$|g9?T<(1 zt?pYJgZe?C8wk2o5-og?_Gt}wWF-v!eGP7Y5Fn%cUxfWgYeya8)mama)io^GNyx0> z9a*EFAL=Di=7VUpM{nW$#TC{Pi|a8>zQ#S-8Q$|GsRRq`Gru^_>*iG#SPxo2IBmE}2!mC)R&M z>sO=Ont^@GQHlOcn!ZP8H^8k$>Eu8tJ-KuKK=~^F-TW1XfS@@4BJY|%T>cXu_RJ_H z@G@Wc%n11fzV?~nxp4N>atus)7!vT$vvB7^x=al-5e=wY8R*ZdwhI!@g9KqL6Td=9 zoY2qz2Tp@5!$rP+L3(89dk5An z9#^7o$y(pFX)&)^FkLx!op&#IM)}9fc+3fj>$dn_<^Q}eF$6t<%9H%lh3O%w){T}i zynSJ6WGmc3qqW>J22O2dDo?eH@58!ZO|X{7vHTOe6BiXIykg0l{6)+D-J^3N^bTp2 zB-Mb<`zsMx@NC5M7=AVQk>f~};w61Jyh|k#!T$pmmLXaG7uZjMhwyI3vj;C&+ym?Q z0pkA+SPwEX0j{6_Ruk zzn^0!NtdtuEyQ~OqaogX<+-h6fkxWBp|~Jy$dlvJ$EJ<7PI_V03#(pSHFiQ;`sB2U Y=~JFhdvfyBNfUPudH0IUD~c!lHxOB3$N&HU delta 10439 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 1.1 (since 2.2) # +# @refcount-bits: width of a refcount entry in bits (since 2.3) +# # Since: 1.7 ## { 'type': 'ImageInfoSpecificQCow2', 'data': { 'compat': 'str', '*lazy-refcounts': 'bool', - '*corrupt': 'bool' + '*corrupt': 'bool', + 'refcount-bits': 'int' } } ## @@ -183,6 +186,22 @@ '*fragmented-clusters': 'int', '*compressed-clusters': 'int' } } ## +# @BlockdevCacheInfo +# +# Cache mode information for a block device +# +# @writeback: true if writeback mode is enabled +# @direct: true if the host page cache is bypassed (O_DIRECT) +# @no-flush: true if flush requests are ignored for the device +# +# Since: 2.3 +## +{ 'type': 'BlockdevCacheInfo', + 'data': { 'writeback': 'bool', + 'direct': 'bool', + 'no-flush': 'bool' } } + +## # @BlockDeviceInfo: # # Information about the backing device for a block device. @@ -199,6 +218,7 @@ # 'host_floppy', 'http', 'https', 'nbd', 'parallels', 'qcow', # 'qcow2', 'raw', 'tftp', 'vdi', 'vmdk', 'vpc', 'vvfat' # 2.2: 'archipelago' added, 'cow' dropped +# 2.3: 'host_floppy' deprecated # # @backing_file: #optional the name of the backing file (for copy-on-write) # @@ -239,6 +259,11 @@ # # @iops_size: #optional an I/O size in bytes (Since 1.7) # +# @cache: the cache mode used for the block device (since: 2.3) +# +# @write_threshold: configured write threshold for the device. +# 0 if disabled. (Since 2.3) +# # Since: 0.14.0 # ## @@ -253,7 +278,8 @@ '*bps_max': 'int', '*bps_rd_max': 'int', '*bps_wr_max': 'int', '*iops_max': 'int', '*iops_rd_max': 'int', '*iops_wr_max': 'int', - '*iops_size': 'int' } } + '*iops_size': 'int', 'cache': 'BlockdevCacheInfo', + 'write_threshold': 'int' } } ## # @BlockDeviceIoStatus: @@ -389,13 +415,20 @@ # growable sparse files (like qcow2) that are used on top # of a physical device. # +# @rd_merged: Number of read requests that have been merged into another +# request (Since 2.3). +# +# @wr_merged: Number of write requests that have been merged into another +# request (Since 2.3). +# # Since: 0.14.0 ## { 'type': 'BlockDeviceStats', 'data': {'rd_bytes': 'int', 'wr_bytes': 'int', 'rd_operations': 'int', 'wr_operations': 'int', 'flush_operations': 'int', 'flush_total_time_ns': 'int', 'wr_total_time_ns': 'int', - 'rd_total_time_ns': 'int', 'wr_highest_offset': 'int' } } + 'rd_total_time_ns': 'int', 'wr_highest_offset': 'int', + 'rd_merged': 'int', 'wr_merged': 'int' } } ## # @BlockStats: @@ -405,6 +438,8 @@ # @device: #optional If the stats are for a virtual block device, the name # corresponding to the virtual block device. # +# @node-name: #optional The node name of the device. (Since 2.3) +# # @stats: A @BlockDeviceStats for the device. # # @parent: #optional This describes the file block device if it has one. @@ -415,7 +450,8 @@ # Since: 0.14.0 ## { 'type': 'BlockStats', - 'data': {'*device': 'str', 'stats': 'BlockDeviceStats', + 'data': {'*device': 'str', '*node-name': 'str', + 'stats': 'BlockDeviceStats', '*parent': 'BlockStats', '*backing': 'BlockStats'} } @@ -424,11 +460,20 @@ # # Query the @BlockStats for all virtual block devices. # +# @query-nodes: #optional If true, the command will query all the block nodes +# that have a node name, in a list which will include "parent" +# information, but not "backing". +# If false or omitted, the behavior is as before - query all the +# device backends, recursively including their "parent" and +# "backing". (Since 2.3) +# # Returns: A list of @BlockStats for each virtual block devices. # # Since: 0.14.0 ## -{ 'command': 'query-blockstats', 'returns': ['BlockStats'] } +{ 'command': 'query-blockstats', + 'data': { '*query-nodes': 'bool' }, + 'returns': ['BlockStats'] } ## # @BlockdevOnError: @@ -673,6 +718,41 @@ '*on-target-error': 'BlockdevOnError' } } ## +# @BlockdevBackup +# +# @device: the name of the device which should be copied. +# +# @target: the name of the backup target device. +# +# @sync: what parts of the disk image should be copied to the destination +# (all the disk, only the sectors allocated in the topmost image, or +# only new I/O). +# +# @speed: #optional the maximum speed, in bytes per second. The default is 0, +# for unlimited. +# +# @on-source-error: #optional the action to take on an error on the source, +# default 'report'. 'stop' and 'enospc' can only be used +# if the block device supports io-status (see BlockInfo). +# +# @on-target-error: #optional the action to take on an error on the target, +# default 'report' (no limitations, since this applies to +# a different block device than @device). +# +# Note that @on-source-error and @on-target-error only affect background I/O. +# If an error occurs during a guest write request, the device's rerror/werror +# actions will be used. +# +# Since: 2.3 +## +{ 'type': 'BlockdevBackup', + 'data': { 'device': 'str', 'target': 'str', + 'sync': 'MirrorSyncMode', + '*speed': 'int', + '*on-source-error': 'BlockdevOnError', + '*on-target-error': 'BlockdevOnError' } } + +## # @blockdev-snapshot-sync # # Generates a synchronous snapshot of a block device. @@ -792,6 +872,22 @@ { 'command': 'drive-backup', 'data': 'DriveBackup' } ## +# @blockdev-backup +# +# Start a point-in-time copy of a block device to a new destination. The +# status of ongoing blockdev-backup operations can be checked with +# query-block-jobs where the BlockJobInfo.type field has the value 'backup'. +# The operation can be stopped before it has completed using the +# block-job-cancel command. +# +# For the arguments, see the documentation of BlockdevBackup. +# +# Since 2.3 +## +{ 'command': 'blockdev-backup', 'data': 'BlockdevBackup' } + + +## # @query-named-block-nodes # # Get the named block driver list @@ -847,6 +943,11 @@ # @on-target-error: #optional the action to take on an error on the target, # default 'report' (no limitations, since this applies to # a different block device than @device). +# @unmap: #optional Whether to try to unmap target sectors where source has +# only zero. If true, and target unallocated sectors will read as zero, +# target image sectors will be unmapped; otherwise, zeroes will be +# written. Both will result in identical contents. +# Default is true. (Since 2.4) # # Returns: nothing on success # If @device is not a valid block device, DeviceNotFound @@ -859,7 +960,8 @@ 'sync': 'MirrorSyncMode', '*mode': 'NewImageMode', '*speed': 'int', '*granularity': 'uint32', '*buf-size': 'int', '*on-source-error': 'BlockdevOnError', - '*on-target-error': 'BlockdevOnError' } } + '*on-target-error': 'BlockdevOnError', + '*unmap': 'bool' } } ## # @block_set_io_throttle: @@ -1147,6 +1249,7 @@ # Drivers that are supported in block device operations. # # @host_device, @host_cdrom, @host_floppy: Since 2.1 +# @host_floppy: deprecated since 2.3 # # Since: 2.0 ## @@ -1624,6 +1727,10 @@ # # Creates a new block device. # +# This command is still a work in progress. It doesn't support all +# block drivers, it lacks a matching blockdev-del, and more. Stay +# away from it unless you want to help with its development. +# # @options: block device options for the new device # # Since: 1.7 @@ -1826,3 +1933,44 @@ ## { 'enum': 'PreallocMode', 'data': [ 'off', 'metadata', 'falloc', 'full' ] } + +## +# @BLOCK_WRITE_THRESHOLD +# +# Emitted when writes on block device reaches or exceeds the +# configured write threshold. For thin-provisioned devices, this +# means the device should be extended to avoid pausing for +# disk exhaustion. +# The event is one shot. Once triggered, it needs to be +# re-registered with another block-set-threshold command. +# +# @node-name: graph node name on which the threshold was exceeded. +# +# @amount-exceeded: amount of data which exceeded the threshold, in bytes. +# +# @write-threshold: last configured threshold, in bytes. +# +# Since: 2.3 +## +{ 'event': 'BLOCK_WRITE_THRESHOLD', + 'data': { 'node-name': 'str', + 'amount-exceeded': 'uint64', + 'write-threshold': 'uint64' } } + +## +# @block-set-write-threshold +# +# Change the write threshold for a block drive. An event will be delivered +# if a write to this block drive crosses the configured threshold. +# This is useful to transparently resize thin-provisioned drives without +# the guest OS noticing. +# +# @node-name: graph node name on which the threshold must be set. +# +# @write-threshold: configured threshold for the block device, bytes. +# Use 0 to disable the threshold. +# +# Since: 2.3 +## +{ 'command': 'block-set-write-threshold', + 'data': { 'node-name': 'str', 'write-threshold': 'uint64' } } diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index 168b083..2227420 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -76,7 +76,8 @@ static QObject *do_qmp_dispatch(QObject *request, Error **errp) command = qdict_get_str(dict, "execute"); cmd = qmp_find_command(command); if (cmd == NULL) { - error_set(errp, QERR_COMMAND_NOT_FOUND, command); + error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, + "The command %s has not been found", command); return NULL; } if (!cmd->enabled) { diff --git a/qdev-monitor.c b/qdev-monitor.c index ebfa701..1d87f57 100644 --- a/qdev-monitor.c +++ b/qdev-monitor.c @@ -667,17 +667,74 @@ static void qbus_print(Monitor *mon, BusState *bus, int indent) } #undef qdev_printf -void do_info_qtree(Monitor *mon, const QDict *qdict) +void hmp_info_qtree(Monitor *mon, const QDict *qdict) { if (sysbus_get_default()) qbus_print(mon, sysbus_get_default(), 0); } -void do_info_qdm(Monitor *mon, const QDict *qdict) +void hmp_info_qdm(Monitor *mon, const QDict *qdict) { qdev_print_devinfos(true); } +typedef struct QOMCompositionState { + Monitor *mon; + int indent; +} QOMCompositionState; + +static void print_qom_composition(Monitor *mon, Object *obj, int indent); + +static int print_qom_composition_child(Object *obj, void *opaque) +{ + QOMCompositionState *s = opaque; + + print_qom_composition(s->mon, obj, s->indent); + + return 0; +} + +static void print_qom_composition(Monitor *mon, Object *obj, int indent) +{ + QOMCompositionState s = { + .mon = mon, + .indent = indent + 2, + }; + char *name; + + if (obj == object_get_root()) { + name = g_strdup(""); + } else { + name = object_get_canonical_path_component(obj); + } + monitor_printf(mon, "%*s/%s (%s)\n", indent, "", name, + object_get_typename(obj)); + g_free(name); + object_child_foreach(obj, print_qom_composition_child, &s); +} + +void hmp_info_qom_tree(Monitor *mon, const QDict *dict) +{ + const char *path = qdict_get_try_str(dict, "path"); + Object *obj; + bool ambiguous = false; + + if (path) { + obj = object_resolve_path(path, &ambiguous); + if (!obj) { + monitor_printf(mon, "Path '%s' could not be resolved.\n", path); + return; + } + if (ambiguous) { + monitor_printf(mon, "Warning: Path '%s' is ambiguous.\n", path); + return; + } + } else { + obj = qdev_get_machine(); + } + print_qom_composition(mon, obj, 0); +} + int do_device_add(Monitor *mon, const QDict *qdict, QObject **ret_data) { Error *local_err = NULL; @@ -772,8 +829,8 @@ int qemu_global_option(const char *str) } opts = qemu_opts_create(&qemu_global_opts, NULL, 0, &error_abort); - qemu_opt_set(opts, "driver", driver); - qemu_opt_set(opts, "property", property); - qemu_opt_set(opts, "value", str+offset+1); + qemu_opt_set(opts, "driver", driver, &error_abort); + qemu_opt_set(opts, "property", property, &error_abort); + qemu_opt_set(opts, "value", str + offset + 1, &error_abort); return 0; } diff --git a/qemu-char.c b/qemu-char.c index a8b01da..1074a78 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -1112,6 +1112,9 @@ static struct termios oldtty; static int old_fd0_flags; static bool stdio_in_use; static bool stdio_allow_signal; +static bool stdio_echo_state; + +static void qemu_chr_set_echo_stdio(CharDriverState *chr, bool echo); static void term_exit(void) { @@ -1119,10 +1122,17 @@ static void term_exit(void) fcntl(0, F_SETFL, old_fd0_flags); } +static void term_stdio_handler(int sig) +{ + /* restore echo after resume from suspend. */ + qemu_chr_set_echo_stdio(NULL, stdio_echo_state); +} + static void qemu_chr_set_echo_stdio(CharDriverState *chr, bool echo) { struct termios tty; + stdio_echo_state = echo; tty = oldtty; if (!echo) { tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP @@ -1149,6 +1159,7 @@ static void qemu_chr_close_stdio(struct CharDriverState *chr) static CharDriverState *qemu_chr_open_stdio(ChardevStdio *opts) { CharDriverState *chr; + struct sigaction act; if (is_daemonized()) { error_report("cannot use stdio with -daemonize"); @@ -1166,6 +1177,10 @@ static CharDriverState *qemu_chr_open_stdio(ChardevStdio *opts) qemu_set_nonblock(0); atexit(term_exit); + memset(&act, 0, sizeof(act)); + act.sa_handler = term_stdio_handler; + sigaction(SIGCONT, &act, NULL); + chr = qemu_chr_open_fd(0, 1); chr->chr_close = qemu_chr_close_stdio; chr->chr_set_echo = qemu_chr_set_echo_stdio; @@ -1387,6 +1402,7 @@ static CharDriverState *qemu_chr_open_pty(const char *id, } close(slave_fd); + qemu_set_nonblock(master_fd); chr = qemu_chr_alloc(); @@ -2781,7 +2797,10 @@ static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len) #ifdef MSG_CMSG_CLOEXEC flags |= MSG_CMSG_CLOEXEC; #endif - ret = recvmsg(s->fd, &msg, flags); + do { + ret = recvmsg(s->fd, &msg, flags); + } while (ret == -1 && errno == EINTR); + if (ret > 0 && s->is_unix) { unix_process_msgfd(chr, &msg); } @@ -2792,7 +2811,13 @@ static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len) static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len) { TCPCharDriver *s = chr->opaque; - return qemu_recv(s->fd, buf, len, 0); + ssize_t ret; + + do { + ret = qemu_recv(s->fd, buf, len, 0); + } while (ret == -1 && socket_error() == EINTR); + + return ret; } #endif @@ -3290,21 +3315,20 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) opts = qemu_opts_create(qemu_find_opts("chardev"), label, 1, &local_err); if (local_err) { - qerror_report_err(local_err); - error_free(local_err); + error_report_err(local_err); return NULL; } if (strstart(filename, "mon:", &p)) { filename = p; - qemu_opt_set(opts, "mux", "on"); + qemu_opt_set(opts, "mux", "on", &error_abort); if (strcmp(filename, "stdio") == 0) { /* Monitor is muxed to stdio: do not exit on Ctrl+C by default * but pass it to the guest. Handle this only for compat syntax, * for -chardev syntax we have special option for this. * This is what -nographic did, redirecting+muxing serial+monitor * to stdio causing Ctrl+C to be passed to guest. */ - qemu_opt_set(opts, "signal", "off"); + qemu_opt_set(opts, "signal", "off", &error_abort); } } @@ -3314,20 +3338,20 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) strcmp(filename, "braille") == 0 || strcmp(filename, "testdev") == 0 || strcmp(filename, "stdio") == 0) { - qemu_opt_set(opts, "backend", filename); + qemu_opt_set(opts, "backend", filename, &error_abort); return opts; } if (strstart(filename, "vc", &p)) { - qemu_opt_set(opts, "backend", "vc"); + qemu_opt_set(opts, "backend", "vc", &error_abort); if (*p == ':') { if (sscanf(p+1, "%7[0-9]x%7[0-9]", width, height) == 2) { /* pixels */ - qemu_opt_set(opts, "width", width); - qemu_opt_set(opts, "height", height); + qemu_opt_set(opts, "width", width, &error_abort); + qemu_opt_set(opts, "height", height, &error_abort); } else if (sscanf(p+1, "%7[0-9]Cx%7[0-9]C", width, height) == 2) { /* chars */ - qemu_opt_set(opts, "cols", width); - qemu_opt_set(opts, "rows", height); + qemu_opt_set(opts, "cols", width, &error_abort); + qemu_opt_set(opts, "rows", height, &error_abort); } else { goto fail; } @@ -3335,22 +3359,22 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) return opts; } if (strcmp(filename, "con:") == 0) { - qemu_opt_set(opts, "backend", "console"); + qemu_opt_set(opts, "backend", "console", &error_abort); return opts; } if (strstart(filename, "COM", NULL)) { - qemu_opt_set(opts, "backend", "serial"); - qemu_opt_set(opts, "path", filename); + qemu_opt_set(opts, "backend", "serial", &error_abort); + qemu_opt_set(opts, "path", filename, &error_abort); return opts; } if (strstart(filename, "file:", &p)) { - qemu_opt_set(opts, "backend", "file"); - qemu_opt_set(opts, "path", p); + qemu_opt_set(opts, "backend", "file", &error_abort); + qemu_opt_set(opts, "path", p, &error_abort); return opts; } if (strstart(filename, "pipe:", &p)) { - qemu_opt_set(opts, "backend", "pipe"); - qemu_opt_set(opts, "path", p); + qemu_opt_set(opts, "backend", "pipe", &error_abort); + qemu_opt_set(opts, "path", p, &error_abort); return opts; } if (strstart(filename, "tcp:", &p) || @@ -3360,27 +3384,30 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) if (sscanf(p, ":%32[^,]%n", port, &pos) < 1) goto fail; } - qemu_opt_set(opts, "backend", "socket"); - qemu_opt_set(opts, "host", host); - qemu_opt_set(opts, "port", port); + qemu_opt_set(opts, "backend", "socket", &error_abort); + qemu_opt_set(opts, "host", host, &error_abort); + qemu_opt_set(opts, "port", port, &error_abort); if (p[pos] == ',') { - if (qemu_opts_do_parse(opts, p+pos+1, NULL) != 0) + qemu_opts_do_parse(opts, p+pos+1, NULL, &local_err); + if (local_err) { + error_report_err(local_err); goto fail; + } } if (strstart(filename, "telnet:", &p)) - qemu_opt_set(opts, "telnet", "on"); + qemu_opt_set(opts, "telnet", "on", &error_abort); return opts; } if (strstart(filename, "udp:", &p)) { - qemu_opt_set(opts, "backend", "udp"); + qemu_opt_set(opts, "backend", "udp", &error_abort); if (sscanf(p, "%64[^:]:%32[^@,]%n", host, port, &pos) < 2) { host[0] = 0; if (sscanf(p, ":%32[^@,]%n", port, &pos) < 1) { goto fail; } } - qemu_opt_set(opts, "host", host); - qemu_opt_set(opts, "port", port); + qemu_opt_set(opts, "host", host, &error_abort); + qemu_opt_set(opts, "port", port, &error_abort); if (p[pos] == '@') { p += pos + 1; if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) { @@ -3389,26 +3416,29 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) goto fail; } } - qemu_opt_set(opts, "localaddr", host); - qemu_opt_set(opts, "localport", port); + qemu_opt_set(opts, "localaddr", host, &error_abort); + qemu_opt_set(opts, "localport", port, &error_abort); } return opts; } if (strstart(filename, "unix:", &p)) { - qemu_opt_set(opts, "backend", "socket"); - if (qemu_opts_do_parse(opts, p, "path") != 0) + qemu_opt_set(opts, "backend", "socket", &error_abort); + qemu_opts_do_parse(opts, p, "path", &local_err); + if (local_err) { + error_report_err(local_err); goto fail; + } return opts; } if (strstart(filename, "/dev/parport", NULL) || strstart(filename, "/dev/ppi", NULL)) { - qemu_opt_set(opts, "backend", "parport"); - qemu_opt_set(opts, "path", filename); + qemu_opt_set(opts, "backend", "parport", &error_abort); + qemu_opt_set(opts, "path", filename, &error_abort); return opts; } if (strstart(filename, "/dev/", NULL)) { - qemu_opt_set(opts, "backend", "tty"); - qemu_opt_set(opts, "path", filename); + qemu_opt_set(opts, "backend", "tty", &error_abort); + qemu_opt_set(opts, "path", filename, &error_abort); return opts; } @@ -3737,8 +3767,7 @@ CharDriverState *qemu_chr_new(const char *label, const char *filename, void (*in chr = qemu_chr_new_from_opts(opts, init, &err); if (err) { - error_report("%s", error_get_pretty(err)); - error_free(err); + error_report_err(err); } if (chr && qemu_opt_get_bool(opts, "mux", 0)) { qemu_chr_fe_claim_no_fail(chr); @@ -3825,9 +3854,7 @@ void qemu_chr_delete(CharDriverState *chr) } g_free(chr->filename); g_free(chr->label); - if (chr->opts) { - qemu_opts_del(chr->opts); - } + qemu_opts_del(chr->opts); g_free(chr); } diff --git a/qemu-coroutine-io.c b/qemu-coroutine-io.c index d404926..28dc735 100644 --- a/qemu-coroutine-io.c +++ b/qemu-coroutine-io.c @@ -45,7 +45,7 @@ qemu_co_sendv_recvv(int sockfd, struct iovec *iov, unsigned iov_cnt, if (err == EAGAIN || err == EWOULDBLOCK) { qemu_coroutine_yield(); } else if (done == 0) { - return -1; + return -err; } else { break; } diff --git a/qemu-coroutine.c b/qemu-coroutine.c index bd574aa..c17a92b 100644 --- a/qemu-coroutine.c +++ b/qemu-coroutine.c @@ -15,31 +15,59 @@ #include "trace.h" #include "qemu-common.h" #include "qemu/thread.h" +#include "qemu/atomic.h" #include "block/coroutine.h" #include "block/coroutine_int.h" enum { - POOL_DEFAULT_SIZE = 64, + POOL_BATCH_SIZE = 64, }; /** Free list to speed up creation */ -static QemuMutex pool_lock; -static QSLIST_HEAD(, Coroutine) pool = QSLIST_HEAD_INITIALIZER(pool); -static unsigned int pool_size; -static unsigned int pool_max_size = POOL_DEFAULT_SIZE; +static QSLIST_HEAD(, Coroutine) release_pool = QSLIST_HEAD_INITIALIZER(pool); +static unsigned int release_pool_size; +static __thread QSLIST_HEAD(, Coroutine) alloc_pool = QSLIST_HEAD_INITIALIZER(pool); +static __thread unsigned int alloc_pool_size; +static __thread Notifier coroutine_pool_cleanup_notifier; + +static void coroutine_pool_cleanup(Notifier *n, void *value) +{ + Coroutine *co; + Coroutine *tmp; + + QSLIST_FOREACH_SAFE(co, &alloc_pool, pool_next, tmp) { + QSLIST_REMOVE_HEAD(&alloc_pool, pool_next); + qemu_coroutine_delete(co); + } +} Coroutine *qemu_coroutine_create(CoroutineEntry *entry) { Coroutine *co = NULL; if (CONFIG_COROUTINE_POOL) { - qemu_mutex_lock(&pool_lock); - co = QSLIST_FIRST(&pool); + co = QSLIST_FIRST(&alloc_pool); + if (!co) { + if (release_pool_size > POOL_BATCH_SIZE) { + /* Slow path; a good place to register the destructor, too. */ + if (!coroutine_pool_cleanup_notifier.notify) { + coroutine_pool_cleanup_notifier.notify = coroutine_pool_cleanup; + qemu_thread_atexit_add(&coroutine_pool_cleanup_notifier); + } + + /* This is not exact; there could be a little skew between + * release_pool_size and the actual size of release_pool. But + * it is just a heuristic, it does not need to be perfect. + */ + alloc_pool_size = atomic_xchg(&release_pool_size, 0); + QSLIST_MOVE_ATOMIC(&alloc_pool, &release_pool); + co = QSLIST_FIRST(&alloc_pool); + } + } if (co) { - QSLIST_REMOVE_HEAD(&pool, pool_next); - pool_size--; + QSLIST_REMOVE_HEAD(&alloc_pool, pool_next); + alloc_pool_size--; } - qemu_mutex_unlock(&pool_lock); } if (!co) { @@ -53,75 +81,54 @@ Coroutine *qemu_coroutine_create(CoroutineEntry *entry) static void coroutine_delete(Coroutine *co) { + co->caller = NULL; + if (CONFIG_COROUTINE_POOL) { - qemu_mutex_lock(&pool_lock); - if (pool_size < pool_max_size) { - QSLIST_INSERT_HEAD(&pool, co, pool_next); - co->caller = NULL; - pool_size++; - qemu_mutex_unlock(&pool_lock); + if (release_pool_size < POOL_BATCH_SIZE * 2) { + QSLIST_INSERT_HEAD_ATOMIC(&release_pool, co, pool_next); + atomic_inc(&release_pool_size); + return; + } + if (alloc_pool_size < POOL_BATCH_SIZE) { + QSLIST_INSERT_HEAD(&alloc_pool, co, pool_next); + alloc_pool_size++; return; } - qemu_mutex_unlock(&pool_lock); } qemu_coroutine_delete(co); } -static void __attribute__((constructor)) coroutine_pool_init(void) +void qemu_coroutine_enter(Coroutine *co, void *opaque) { - qemu_mutex_init(&pool_lock); -} + Coroutine *self = qemu_coroutine_self(); + CoroutineAction ret; -static void __attribute__((destructor)) coroutine_pool_cleanup(void) -{ - Coroutine *co; - Coroutine *tmp; + trace_qemu_coroutine_enter(self, co, opaque); - QSLIST_FOREACH_SAFE(co, &pool, pool_next, tmp) { - QSLIST_REMOVE_HEAD(&pool, pool_next); - qemu_coroutine_delete(co); + if (co->caller) { + fprintf(stderr, "Co-routine re-entered recursively\n"); + abort(); } - qemu_mutex_destroy(&pool_lock); -} - -static void coroutine_swap(Coroutine *from, Coroutine *to) -{ - CoroutineAction ret; - - ret = qemu_coroutine_switch(from, to, COROUTINE_YIELD); + co->caller = self; + co->entry_arg = opaque; + ret = qemu_coroutine_switch(self, co, COROUTINE_ENTER); - qemu_co_queue_run_restart(to); + qemu_co_queue_run_restart(co); switch (ret) { case COROUTINE_YIELD: return; case COROUTINE_TERMINATE: - trace_qemu_coroutine_terminate(to); - coroutine_delete(to); + trace_qemu_coroutine_terminate(co); + coroutine_delete(co); return; default: abort(); } } -void qemu_coroutine_enter(Coroutine *co, void *opaque) -{ - Coroutine *self = qemu_coroutine_self(); - - trace_qemu_coroutine_enter(self, co, opaque); - - if (co->caller) { - fprintf(stderr, "Co-routine re-entered recursively\n"); - abort(); - } - - co->caller = self; - co->entry_arg = opaque; - coroutine_swap(self, co); -} - void coroutine_fn qemu_coroutine_yield(void) { Coroutine *self = qemu_coroutine_self(); @@ -135,25 +142,5 @@ void coroutine_fn qemu_coroutine_yield(void) } self->caller = NULL; - coroutine_swap(self, to); -} - -void qemu_coroutine_adjust_pool_size(int n) -{ - qemu_mutex_lock(&pool_lock); - - pool_max_size += n; - - /* Callers should never take away more than they added */ - assert(pool_max_size >= POOL_DEFAULT_SIZE); - - /* Trim oversized pool down to new max */ - while (pool_size > pool_max_size) { - Coroutine *co = QSLIST_FIRST(&pool); - QSLIST_REMOVE_HEAD(&pool, pool_next); - pool_size--; - qemu_coroutine_delete(co); - } - - qemu_mutex_unlock(&pool_lock); + qemu_coroutine_switch(self, to, COROUTINE_YIELD); } diff --git a/qemu-doc.texi b/qemu-doc.texi index ad418f8..0125bc7 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -539,8 +539,8 @@ storage. @item qcow2 QEMU image format, the most versatile format. Use it to have smaller images (useful if your filesystem does not supports holes, for example -on Windows), optional AES encryption, zlib based compression and -support of multiple VM snapshots. +on Windows), zlib based compression and support of multiple VM +snapshots. Supported options: @table @code @@ -574,9 +574,10 @@ original file must then be securely erased using a program like shred, though even this is ineffective with many modern storage technologies. @end itemize -Use of qcow / qcow2 encryption is thus strongly discouraged. Users are -recommended to use an alternative encryption technology such as the -Linux dm-crypt / LUKS system. +Use of qcow / qcow2 encryption with QEMU is deprecated, and support for +it will go away in a future release. Users are recommended to use an +alternative encryption technology such as the Linux dm-crypt / LUKS +system. @item cluster_size Changes the qcow2 cluster size (must be between 512 and 2M). Smaller cluster @@ -698,7 +699,11 @@ Supported options: Specifies which VHDX subformat to use. Valid options are @code{dynamic} (default) and @code{fixed}. @item block_state_zero -Force use of payload blocks of type 'ZERO'. +Force use of payload blocks of type 'ZERO'. Can be set to @code{on} (default) +or @code{off}. When set to @code{off}, new blocks will be created as +@code{PAYLOAD_BLOCK_NOT_PRESENT}, which means parsers are free to return +arbitrary data for those blocks. Do not set to @code{off} when using +@code{qemu-img convert} with @code{subformat=dynamic}. @item block_size Block size; min 1 MB, max 256 MB. 0 means auto-calculate based on image size. @item log_size @@ -731,8 +736,7 @@ devices. We describe here the usage for QEMU version >= 0.8.3. On Linux, you can directly use the host device filename instead of a disk image filename provided you have enough privileges to access -it. For example, use @file{/dev/cdrom} to access to the CDROM or -@file{/dev/fd0} for the floppy. +it. For example, use @file{/dev/cdrom} to access to the CDROM. @table @code @item CD @@ -744,6 +748,8 @@ You can specify a floppy device even if no floppy is loaded. Floppy removal is currently not detected accurately (if you change floppy without doing floppy access while the floppy is not loaded, the guest OS will think that the same floppy is loaded). +Use of the host's floppy device is deprecated, and support for it will +be removed in a future release. @item Hard disks Hard disks can be used. Normally you must specify the whole disk (@file{/dev/hdb} instead of @file{/dev/hdb1}) so that the guest OS can @@ -2048,7 +2054,7 @@ firmware implementation. The goal is to implement a 100% IEEE A sample Linux 2.6 series kernel and ram disk image are available on the QEMU web site. There are still issues with NetBSD and OpenBSD, but -some kernel versions work. Please note that currently older Solaris kernels +most kernel versions work. Please note that currently older Solaris kernels don't work probably due to interface issues between OpenBIOS and Solaris. @@ -2087,8 +2093,9 @@ Set the emulated machine type. Default is SS-5. Use the executable @file{qemu-system-sparc64} to simulate a Sun4u (UltraSPARC PC-like machine), Sun4v (T1 PC-like machine), or generic -Niagara (T1) machine. The emulator is not usable for anything yet, but -it can launch some kernels. +Niagara (T1) machine. The Sun4u emulator is mostly complete, being +able to run Linux, NetBSD and OpenBSD in headless (-nographic) mode. The +Sun4v and Niagara emulators are still a work in progress. QEMU emulates the following peripherals: diff --git a/qemu-img.c b/qemu-img.c index a42335c..9dddfbe 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -35,7 +35,7 @@ #include "block/qapi.h" #include -#define QEMU_IMG_VERSION "qemu-img version " QEMU_VERSION \ +#define QEMU_IMG_VERSION "qemu-img version " QEMU_VERSION QEMU_PKGVERSION \ ", Copyright (c) 2004-2008 Fabrice Bellard\n" typedef struct img_cmd_t { @@ -261,6 +261,7 @@ static int print_block_option_help(const char *filename, const char *fmt) { BlockDriver *drv, *proto_drv; QemuOptsList *create_opts = NULL; + Error *local_err = NULL; /* Find driver and parse its options */ drv = bdrv_find_format(fmt); @@ -271,9 +272,9 @@ static int print_block_option_help(const char *filename, const char *fmt) create_opts = qemu_opts_append(create_opts, drv->create_opts); if (filename) { - proto_drv = bdrv_find_protocol(filename, true); + proto_drv = bdrv_find_protocol(filename, true, &local_err); if (!proto_drv) { - error_report("Unknown protocol '%s'", filename); + error_report_err(local_err); qemu_opts_free(create_opts); return 1; } @@ -291,32 +292,24 @@ static BlockBackend *img_open(const char *id, const char *filename, { BlockBackend *blk; BlockDriverState *bs; - BlockDriver *drv; char password[256]; Error *local_err = NULL; - int ret; - - blk = blk_new_with_bs(id, &error_abort); - bs = blk_bs(blk); + QDict *options = NULL; if (fmt) { - drv = bdrv_find_format(fmt); - if (!drv) { - error_report("Unknown file format '%s'", fmt); - goto fail; - } - } else { - drv = NULL; + options = qdict_new(); + qdict_put(options, "driver", qstring_from_str(fmt)); } - ret = bdrv_open(&bs, filename, NULL, NULL, flags, drv, &local_err); - if (ret < 0) { + blk = blk_new_open(id, filename, NULL, options, flags, &local_err); + if (!blk) { error_report("Could not open '%s': %s", filename, error_get_pretty(local_err)); error_free(local_err); goto fail; } + bs = blk_bs(blk); if (bdrv_is_encrypted(bs) && require_io) { qprintf(quiet, "Disk image '%s' is encrypted.\n", filename); if (read_password(password, sizeof(password)) < 0) { @@ -338,17 +331,23 @@ static int add_old_style_options(const char *fmt, QemuOpts *opts, const char *base_filename, const char *base_fmt) { + Error *err = NULL; + if (base_filename) { - if (qemu_opt_set(opts, BLOCK_OPT_BACKING_FILE, base_filename)) { + qemu_opt_set(opts, BLOCK_OPT_BACKING_FILE, base_filename, &err); + if (err) { error_report("Backing file not supported for file format '%s'", fmt); + error_free(err); return -1; } } if (base_fmt) { - if (qemu_opt_set(opts, BLOCK_OPT_BACKING_FMT, base_fmt)) { + qemu_opt_set(opts, BLOCK_OPT_BACKING_FMT, base_fmt, &err); + if (err) { error_report("Backing file format not supported for file " "format '%s'", fmt); + error_free(err); return -1; } } @@ -889,8 +888,7 @@ done: blk_unref(blk); if (local_err) { - qerror_report_err(local_err); - error_free(local_err); + error_report_err(local_err); return 1; } @@ -977,7 +975,8 @@ static int is_allocated_sectors_min(const uint8_t *buf, int n, int *pnum, static int compare_sectors(const uint8_t *buf1, const uint8_t *buf2, int n, int *pnum) { - int res, i; + bool res; + int i; if (n <= 0) { *pnum = 0; @@ -1016,19 +1015,19 @@ static int64_t sectors_to_process(int64_t total, int64_t from) * Returns 0 in case sectors are filled with 0, 1 if sectors contain non-zero * data and negative value on error. * - * @param bs: Driver used for accessing file + * @param blk: BlockBackend for the image * @param sect_num: Number of first sector to check * @param sect_count: Number of sectors to check * @param filename: Name of disk file we are checking (logging purpose) * @param buffer: Allocated buffer for storing read data * @param quiet: Flag for quiet mode */ -static int check_empty_sectors(BlockDriverState *bs, int64_t sect_num, +static int check_empty_sectors(BlockBackend *blk, int64_t sect_num, int sect_count, const char *filename, uint8_t *buffer, bool quiet) { int pnum, ret = 0; - ret = bdrv_read(bs, sect_num, buffer, sect_count); + ret = blk_read(blk, sect_num, buffer, sect_count); if (ret < 0) { error_report("Error while reading offset %" PRId64 " of %s: %s", sectors_to_bytes(sect_num), filename, strerror(-ret)); @@ -1138,16 +1137,16 @@ static int img_compare(int argc, char **argv) } bs2 = blk_bs(blk2); - buf1 = qemu_blockalign(bs1, IO_BUF_SIZE); - buf2 = qemu_blockalign(bs2, IO_BUF_SIZE); - total_sectors1 = bdrv_nb_sectors(bs1); + buf1 = blk_blockalign(blk1, IO_BUF_SIZE); + buf2 = blk_blockalign(blk2, IO_BUF_SIZE); + total_sectors1 = blk_nb_sectors(blk1); if (total_sectors1 < 0) { error_report("Can't get size of %s: %s", filename1, strerror(-total_sectors1)); ret = 4; goto out; } - total_sectors2 = bdrv_nb_sectors(bs2); + total_sectors2 = blk_nb_sectors(blk2); if (total_sectors2 < 0) { error_report("Can't get size of %s: %s", filename2, strerror(-total_sectors2)); @@ -1189,7 +1188,7 @@ static int img_compare(int argc, char **argv) if (allocated1 == allocated2) { if (allocated1) { - ret = bdrv_read(bs1, sector_num, buf1, nb_sectors); + ret = blk_read(blk1, sector_num, buf1, nb_sectors); if (ret < 0) { error_report("Error while reading offset %" PRId64 " of %s:" " %s", sectors_to_bytes(sector_num), filename1, @@ -1197,7 +1196,7 @@ static int img_compare(int argc, char **argv) ret = 4; goto out; } - ret = bdrv_read(bs2, sector_num, buf2, nb_sectors); + ret = blk_read(blk2, sector_num, buf2, nb_sectors); if (ret < 0) { error_report("Error while reading offset %" PRId64 " of %s: %s", sectors_to_bytes(sector_num), @@ -1224,10 +1223,10 @@ static int img_compare(int argc, char **argv) } if (allocated1) { - ret = check_empty_sectors(bs1, sector_num, nb_sectors, + ret = check_empty_sectors(blk1, sector_num, nb_sectors, filename1, buf1, quiet); } else { - ret = check_empty_sectors(bs2, sector_num, nb_sectors, + ret = check_empty_sectors(blk2, sector_num, nb_sectors, filename2, buf1, quiet); } if (ret) { @@ -1244,18 +1243,18 @@ static int img_compare(int argc, char **argv) } if (total_sectors1 != total_sectors2) { - BlockDriverState *bs_over; + BlockBackend *blk_over; int64_t total_sectors_over; const char *filename_over; qprintf(quiet, "Warning: Image size mismatch!\n"); if (total_sectors1 > total_sectors2) { total_sectors_over = total_sectors1; - bs_over = bs1; + blk_over = blk1; filename_over = filename1; } else { total_sectors_over = total_sectors2; - bs_over = bs2; + blk_over = blk2; filename_over = filename2; } @@ -1264,7 +1263,7 @@ static int img_compare(int argc, char **argv) if (nb_sectors <= 0) { break; } - ret = bdrv_is_allocated_above(bs_over, NULL, sector_num, + ret = bdrv_is_allocated_above(blk_bs(blk_over), NULL, sector_num, nb_sectors, &pnum); if (ret < 0) { ret = 3; @@ -1275,7 +1274,7 @@ static int img_compare(int argc, char **argv) } nb_sectors = pnum; if (ret) { - ret = check_empty_sectors(bs_over, sector_num, nb_sectors, + ret = check_empty_sectors(blk_over, sector_num, nb_sectors, filename_over, buf1, quiet); if (ret) { if (ret < 0) { @@ -1484,7 +1483,7 @@ static int img_convert(int argc, char **argv) goto out; } bs[bs_i] = blk_bs(blk[bs_i]); - bs_sectors[bs_i] = bdrv_nb_sectors(bs[bs_i]); + bs_sectors[bs_i] = blk_nb_sectors(blk[bs_i]); if (bs_sectors[bs_i] < 0) { error_report("Could not get size of %s: %s", argv[optind + bs_i], strerror(-bs_sectors[bs_i])); @@ -1524,27 +1523,47 @@ static int img_convert(int argc, char **argv) goto out; } - proto_drv = bdrv_find_protocol(out_filename, true); + proto_drv = bdrv_find_protocol(out_filename, true, &local_err); if (!proto_drv) { - error_report("Unknown protocol '%s'", out_filename); + error_report_err(local_err); ret = -1; goto out; } - create_opts = qemu_opts_append(create_opts, drv->create_opts); - create_opts = qemu_opts_append(create_opts, proto_drv->create_opts); + if (!skip_create) { + if (!drv->create_opts) { + error_report("Format driver '%s' does not support image creation", + drv->format_name); + ret = -1; + goto out; + } - opts = qemu_opts_create(create_opts, NULL, 0, &error_abort); - if (options && qemu_opts_do_parse(opts, options, NULL)) { - error_report("Invalid options for file format '%s'", out_fmt); - ret = -1; - goto out; - } + if (!proto_drv->create_opts) { + error_report("Protocol driver '%s' does not support image creation", + proto_drv->format_name); + ret = -1; + goto out; + } - qemu_opt_set_number(opts, BLOCK_OPT_SIZE, total_sectors * 512); - ret = add_old_style_options(out_fmt, opts, out_baseimg, NULL); - if (ret < 0) { - goto out; + create_opts = qemu_opts_append(create_opts, drv->create_opts); + create_opts = qemu_opts_append(create_opts, proto_drv->create_opts); + + opts = qemu_opts_create(create_opts, NULL, 0, &error_abort); + if (options) { + qemu_opts_do_parse(opts, options, NULL, &local_err); + if (local_err) { + error_report_err(local_err); + ret = -1; + goto out; + } + } + + qemu_opt_set_number(opts, BLOCK_OPT_SIZE, total_sectors * 512, + &error_abort); + ret = add_old_style_options(out_fmt, opts, out_baseimg, NULL); + if (ret < 0) { + goto out; + } } /* Get backing file name if -o backing_file was used */ @@ -1619,12 +1638,12 @@ static int img_convert(int argc, char **argv) out_bs->bl.discard_alignment)) ); - buf = qemu_blockalign(out_bs, bufsectors * BDRV_SECTOR_SIZE); + buf = blk_blockalign(out_blk, bufsectors * BDRV_SECTOR_SIZE); if (skip_create) { - int64_t output_sectors = bdrv_nb_sectors(out_bs); + int64_t output_sectors = blk_nb_sectors(out_blk); if (output_sectors < 0) { - error_report("unable to get output image length: %s\n", + error_report("unable to get output image length: %s", strerror(-output_sectors)); ret = -1; goto out; @@ -1690,7 +1709,7 @@ static int img_convert(int argc, char **argv) nlow = remainder > bs_sectors[bs_i] - bs_num ? bs_sectors[bs_i] - bs_num : remainder; - ret = bdrv_read(bs[bs_i], bs_num, buf2, nlow); + ret = blk_read(blk[bs_i], bs_num, buf2, nlow); if (ret < 0) { error_report("error while reading sector %" PRId64 ": %s", bs_num, strerror(-ret)); @@ -1705,7 +1724,7 @@ static int img_convert(int argc, char **argv) assert (remainder == 0); if (!buffer_is_zero(buf, n * BDRV_SECTOR_SIZE)) { - ret = bdrv_write_compressed(out_bs, sector_num, buf, n); + ret = blk_write_compressed(out_blk, sector_num, buf, n); if (ret != 0) { error_report("error while compressing sector %" PRId64 ": %s", sector_num, strerror(-ret)); @@ -1716,7 +1735,7 @@ static int img_convert(int argc, char **argv) qemu_progress_print(100.0 * sector_num / total_sectors, 0); } /* signal EOF to align */ - bdrv_write_compressed(out_bs, 0, NULL, 0); + blk_write_compressed(out_blk, 0, NULL, 0); } else { int64_t sectors_to_read, sectors_read, sector_num_next_status; bool count_allocated_sectors; @@ -1817,7 +1836,7 @@ restart: } n1 = n; - ret = bdrv_read(bs[bs_i], sector_num - bs_offset, buf, n); + ret = blk_read(blk[bs_i], sector_num - bs_offset, buf, n); if (ret < 0) { error_report("error while reading sector %" PRId64 ": %s", sector_num - bs_offset, strerror(-ret)); @@ -1830,7 +1849,7 @@ restart: while (n > 0) { if (!has_zero_init || is_allocated_sectors_min(buf1, n, &n1, min_sparse)) { - ret = bdrv_write(out_bs, sector_num, buf1, n1); + ret = blk_write(out_blk, sector_num, buf1, n1); if (ret < 0) { error_report("error while writing sector %" PRId64 ": %s", sector_num, strerror(-ret)); @@ -1990,8 +2009,7 @@ static ImageInfoList *collect_image_info_list(const char *filename, bdrv_query_image_info(bs, &info, &err); if (err) { - error_report("%s", error_get_pretty(err)); - error_free(err); + error_report_err(err); blk_unref(blk); goto err; } @@ -2254,7 +2272,7 @@ static int img_map(int argc, char **argv) printf("%-16s%-16s%-16s%s\n", "Offset", "Length", "Mapped to", "File"); } - length = bdrv_getlength(bs); + length = blk_getlength(blk); while (curr.start + curr.length < length) { int64_t nsectors_left; int64_t sector_num; @@ -2423,8 +2441,7 @@ static int img_snapshot(int argc, char **argv) static int img_rebase(int argc, char **argv) { BlockBackend *blk = NULL, *blk_old_backing = NULL, *blk_new_backing = NULL; - BlockDriverState *bs = NULL, *bs_old_backing = NULL, *bs_new_backing = NULL; - BlockDriver *old_backing_drv, *new_backing_drv; + BlockDriverState *bs = NULL; char *filename; const char *fmt, *cache, *src_cache, *out_basefmt, *out_baseimg; int c, flags, src_flags, ret; @@ -2518,22 +2535,8 @@ static int img_rebase(int argc, char **argv) } bs = blk_bs(blk); - /* Find the right drivers for the backing files */ - old_backing_drv = NULL; - new_backing_drv = NULL; - - if (!unsafe && bs->backing_format[0] != '\0') { - old_backing_drv = bdrv_find_format(bs->backing_format); - if (old_backing_drv == NULL) { - error_report("Invalid format name: '%s'", bs->backing_format); - ret = -1; - goto out; - } - } - if (out_basefmt != NULL) { - new_backing_drv = bdrv_find_format(out_basefmt); - if (new_backing_drv == NULL) { + if (bdrv_find_format(out_basefmt) == NULL) { error_report("Invalid format name: '%s'", out_basefmt); ret = -1; goto out; @@ -2542,25 +2545,35 @@ static int img_rebase(int argc, char **argv) /* For safe rebasing we need to compare old and new backing file */ if (!unsafe) { - char backing_name[1024]; + char backing_name[PATH_MAX]; + QDict *options = NULL; + + if (bs->backing_format[0] != '\0') { + options = qdict_new(); + qdict_put(options, "driver", qstring_from_str(bs->backing_format)); + } - blk_old_backing = blk_new_with_bs("old_backing", &error_abort); - bs_old_backing = blk_bs(blk_old_backing); bdrv_get_backing_filename(bs, backing_name, sizeof(backing_name)); - ret = bdrv_open(&bs_old_backing, backing_name, NULL, NULL, src_flags, - old_backing_drv, &local_err); - if (ret) { + blk_old_backing = blk_new_open("old_backing", backing_name, NULL, + options, src_flags, &local_err); + if (!blk_old_backing) { error_report("Could not open old backing file '%s': %s", backing_name, error_get_pretty(local_err)); error_free(local_err); goto out; } + if (out_baseimg[0]) { - blk_new_backing = blk_new_with_bs("new_backing", &error_abort); - bs_new_backing = blk_bs(blk_new_backing); - ret = bdrv_open(&bs_new_backing, out_baseimg, NULL, NULL, src_flags, - new_backing_drv, &local_err); - if (ret) { + if (out_basefmt) { + options = qdict_new(); + qdict_put(options, "driver", qstring_from_str(out_basefmt)); + } else { + options = NULL; + } + + blk_new_backing = blk_new_open("new_backing", out_baseimg, NULL, + options, src_flags, &local_err); + if (!blk_new_backing) { error_report("Could not open new backing file '%s': %s", out_baseimg, error_get_pretty(local_err)); error_free(local_err); @@ -2588,19 +2601,19 @@ static int img_rebase(int argc, char **argv) uint8_t * buf_new; float local_progress = 0; - buf_old = qemu_blockalign(bs, IO_BUF_SIZE); - buf_new = qemu_blockalign(bs, IO_BUF_SIZE); + buf_old = blk_blockalign(blk, IO_BUF_SIZE); + buf_new = blk_blockalign(blk, IO_BUF_SIZE); - num_sectors = bdrv_nb_sectors(bs); + num_sectors = blk_nb_sectors(blk); if (num_sectors < 0) { error_report("Could not get size of '%s': %s", filename, strerror(-num_sectors)); ret = -1; goto out; } - old_backing_num_sectors = bdrv_nb_sectors(bs_old_backing); + old_backing_num_sectors = blk_nb_sectors(blk_old_backing); if (old_backing_num_sectors < 0) { - char backing_name[1024]; + char backing_name[PATH_MAX]; bdrv_get_backing_filename(bs, backing_name, sizeof(backing_name)); error_report("Could not get size of '%s': %s", @@ -2608,8 +2621,8 @@ static int img_rebase(int argc, char **argv) ret = -1; goto out; } - if (bs_new_backing) { - new_backing_num_sectors = bdrv_nb_sectors(bs_new_backing); + if (blk_new_backing) { + new_backing_num_sectors = blk_nb_sectors(blk_new_backing); if (new_backing_num_sectors < 0) { error_report("Could not get size of '%s': %s", out_baseimg, strerror(-new_backing_num_sectors)); @@ -2654,21 +2667,21 @@ static int img_rebase(int argc, char **argv) n = old_backing_num_sectors - sector; } - ret = bdrv_read(bs_old_backing, sector, buf_old, n); + ret = blk_read(blk_old_backing, sector, buf_old, n); if (ret < 0) { error_report("error while reading from old backing file"); goto out; } } - if (sector >= new_backing_num_sectors || !bs_new_backing) { + if (sector >= new_backing_num_sectors || !blk_new_backing) { memset(buf_new, 0, n * BDRV_SECTOR_SIZE); } else { if (sector + n > new_backing_num_sectors) { n = new_backing_num_sectors - sector; } - ret = bdrv_read(bs_new_backing, sector, buf_new, n); + ret = blk_read(blk_new_backing, sector, buf_new, n); if (ret < 0) { error_report("error while reading from new backing file"); goto out; @@ -2684,8 +2697,8 @@ static int img_rebase(int argc, char **argv) if (compare_sectors(buf_old + written * 512, buf_new + written * 512, n - written, &pnum)) { - ret = bdrv_write(bs, sector + written, - buf_old + written * 512, pnum); + ret = blk_write(blk, sector + written, + buf_old + written * 512, pnum); if (ret < 0) { error_report("Error while writing to COW image: %s", strerror(-ret)); @@ -2745,12 +2758,12 @@ out: static int img_resize(int argc, char **argv) { + Error *err = NULL; int c, ret, relative; const char *filename, *fmt, *size; int64_t n, total_size; bool quiet = false; BlockBackend *blk = NULL; - BlockDriverState *bs = NULL; QemuOpts *param; static QemuOptsList resize_options = { .name = "resize_options", @@ -2817,8 +2830,9 @@ static int img_resize(int argc, char **argv) /* Parse size */ param = qemu_opts_create(&resize_options, NULL, 0, &error_abort); - if (qemu_opt_set(param, BLOCK_OPT_SIZE, size)) { - /* Error message already printed when size parsing fails */ + qemu_opt_set(param, BLOCK_OPT_SIZE, size, &err); + if (err) { + error_report_err(err); ret = -1; qemu_opts_del(param); goto out; @@ -2832,10 +2846,9 @@ static int img_resize(int argc, char **argv) ret = -1; goto out; } - bs = blk_bs(blk); if (relative) { - total_size = bdrv_getlength(bs) + n * relative; + total_size = blk_getlength(blk) + n * relative; } else { total_size = n; } @@ -2845,7 +2858,7 @@ static int img_resize(int argc, char **argv) goto out; } - ret = bdrv_truncate(bs, total_size); + ret = blk_truncate(blk, total_size); switch (ret) { case 0: qprintf(quiet, "Image resized.\n"); @@ -2876,6 +2889,7 @@ static void amend_status_cb(BlockDriverState *bs, static int img_amend(int argc, char **argv) { + Error *err = NULL; int c, ret = 0; char *options = NULL; QemuOptsList *create_opts = NULL; @@ -2972,14 +2986,24 @@ static int img_amend(int argc, char **argv) goto out; } - create_opts = qemu_opts_append(create_opts, bs->drv->create_opts); - opts = qemu_opts_create(create_opts, NULL, 0, &error_abort); - if (options && qemu_opts_do_parse(opts, options, NULL)) { - error_report("Invalid options for file format '%s'", fmt); + if (!bs->drv->create_opts) { + error_report("Format driver '%s' does not support any options to amend", + fmt); ret = -1; goto out; } + create_opts = qemu_opts_append(create_opts, bs->drv->create_opts); + opts = qemu_opts_create(create_opts, NULL, 0, &error_abort); + if (options) { + qemu_opts_do_parse(opts, options, NULL, &err); + if (err) { + error_report_err(err); + ret = -1; + goto out; + } + } + /* In case the driver does not call amend_status_cb() */ qemu_progress_print(0.f, 0); ret = bdrv_amend_options(bs, opts, &amend_status_cb); @@ -3032,8 +3056,7 @@ int main(int argc, char **argv) qemu_init_exec_dir(argv[0]); if (qemu_init_main_loop(&local_error)) { - error_report("%s", error_get_pretty(local_error)); - error_free(local_error); + error_report_err(local_error); exit(EXIT_FAILURE); } diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index d94fb1e..1afcfc0 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -9,10 +9,13 @@ */ #include "qemu-io.h" -#include "block/block_int.h" +#include "sysemu/block-backend.h" +#include "block/block.h" +#include "block/block_int.h" /* for info_f() */ #include "block/qapi.h" #include "qemu/main-loop.h" #include "qemu/timer.h" +#include "sysemu/block-backend.h" #define CMD_NOFILE_OK 0x01 @@ -40,24 +43,24 @@ int qemuio_command_usage(const cmdinfo_t *ci) return 0; } -static int init_check_command(BlockDriverState *bs, const cmdinfo_t *ct) +static int init_check_command(BlockBackend *blk, const cmdinfo_t *ct) { if (ct->flags & CMD_FLAG_GLOBAL) { return 1; } - if (!(ct->flags & CMD_NOFILE_OK) && !bs) { + if (!(ct->flags & CMD_NOFILE_OK) && !blk) { fprintf(stderr, "no file open, try 'help open'\n"); return 0; } return 1; } -static int command(BlockDriverState *bs, const cmdinfo_t *ct, int argc, +static int command(BlockBackend *blk, const cmdinfo_t *ct, int argc, char **argv) { char *cmd = argv[0]; - if (!init_check_command(bs, ct)) { + if (!init_check_command(blk, ct)) { return 0; } @@ -78,7 +81,7 @@ static int command(BlockDriverState *bs, const cmdinfo_t *ct, int argc, return 0; } optind = 0; - return ct->cfunc(bs, argc, argv); + return ct->cfunc(blk, argc, argv); } static const cmdinfo_t *find_command(const char *cmd) @@ -267,14 +270,14 @@ static int parse_pattern(const char *arg) */ #define MISALIGN_OFFSET 16 -static void *qemu_io_alloc(BlockDriverState *bs, size_t len, int pattern) +static void *qemu_io_alloc(BlockBackend *blk, size_t len, int pattern) { void *buf; if (qemuio_misalign) { len += MISALIGN_OFFSET; } - buf = qemu_blockalign(bs, len); + buf = blk_blockalign(blk, len); memset(buf, pattern, len); if (qemuio_misalign) { buf += MISALIGN_OFFSET; @@ -340,7 +343,7 @@ static void print_report(const char *op, struct timeval *t, int64_t offset, * vector matching it. */ static void * -create_iovec(BlockDriverState *bs, QEMUIOVector *qiov, char **argv, int nr_iov, +create_iovec(BlockBackend *blk, QEMUIOVector *qiov, char **argv, int nr_iov, int pattern) { size_t *sizes = g_new0(size_t, nr_iov); @@ -377,7 +380,7 @@ create_iovec(BlockDriverState *bs, QEMUIOVector *qiov, char **argv, int nr_iov, qemu_iovec_init(qiov, nr_iov); - buf = p = qemu_io_alloc(bs, count, pattern); + buf = p = qemu_io_alloc(blk, count, pattern); for (i = 0; i < nr_iov; i++) { qemu_iovec_add(qiov, p, sizes[i]); @@ -389,12 +392,12 @@ fail: return buf; } -static int do_read(BlockDriverState *bs, char *buf, int64_t offset, int count, +static int do_read(BlockBackend *blk, char *buf, int64_t offset, int count, int *total) { int ret; - ret = bdrv_read(bs, offset >> 9, (uint8_t *)buf, count >> 9); + ret = blk_read(blk, offset >> 9, (uint8_t *)buf, count >> 9); if (ret < 0) { return ret; } @@ -402,12 +405,12 @@ static int do_read(BlockDriverState *bs, char *buf, int64_t offset, int count, return 1; } -static int do_write(BlockDriverState *bs, char *buf, int64_t offset, int count, +static int do_write(BlockBackend *blk, char *buf, int64_t offset, int count, int *total) { int ret; - ret = bdrv_write(bs, offset >> 9, (uint8_t *)buf, count >> 9); + ret = blk_write(blk, offset >> 9, (uint8_t *)buf, count >> 9); if (ret < 0) { return ret; } @@ -415,20 +418,20 @@ static int do_write(BlockDriverState *bs, char *buf, int64_t offset, int count, return 1; } -static int do_pread(BlockDriverState *bs, char *buf, int64_t offset, int count, +static int do_pread(BlockBackend *blk, char *buf, int64_t offset, int count, int *total) { - *total = bdrv_pread(bs, offset, (uint8_t *)buf, count); + *total = blk_pread(blk, offset, (uint8_t *)buf, count); if (*total < 0) { return *total; } return 1; } -static int do_pwrite(BlockDriverState *bs, char *buf, int64_t offset, int count, +static int do_pwrite(BlockBackend *blk, char *buf, int64_t offset, int count, int *total) { - *total = bdrv_pwrite(bs, offset, (uint8_t *)buf, count); + *total = blk_pwrite(blk, offset, (uint8_t *)buf, count); if (*total < 0) { return *total; } @@ -436,7 +439,7 @@ static int do_pwrite(BlockDriverState *bs, char *buf, int64_t offset, int count, } typedef struct { - BlockDriverState *bs; + BlockBackend *blk; int64_t offset; int count; int *total; @@ -448,8 +451,8 @@ static void coroutine_fn co_write_zeroes_entry(void *opaque) { CoWriteZeroes *data = opaque; - data->ret = bdrv_co_write_zeroes(data->bs, data->offset / BDRV_SECTOR_SIZE, - data->count / BDRV_SECTOR_SIZE, 0); + data->ret = blk_co_write_zeroes(data->blk, data->offset / BDRV_SECTOR_SIZE, + data->count / BDRV_SECTOR_SIZE, 0); data->done = true; if (data->ret < 0) { *data->total = data->ret; @@ -459,12 +462,12 @@ static void coroutine_fn co_write_zeroes_entry(void *opaque) *data->total = data->count; } -static int do_co_write_zeroes(BlockDriverState *bs, int64_t offset, int count, +static int do_co_write_zeroes(BlockBackend *blk, int64_t offset, int count, int *total) { Coroutine *co; CoWriteZeroes data = { - .bs = bs, + .blk = blk, .offset = offset, .count = count, .total = total, @@ -474,7 +477,7 @@ static int do_co_write_zeroes(BlockDriverState *bs, int64_t offset, int count, co = qemu_coroutine_create(co_write_zeroes_entry); qemu_coroutine_enter(co, &data); while (!data.done) { - aio_poll(bdrv_get_aio_context(bs), true); + aio_poll(blk_get_aio_context(blk), true); } if (data.ret < 0) { return data.ret; @@ -483,12 +486,12 @@ static int do_co_write_zeroes(BlockDriverState *bs, int64_t offset, int count, } } -static int do_write_compressed(BlockDriverState *bs, char *buf, int64_t offset, +static int do_write_compressed(BlockBackend *blk, char *buf, int64_t offset, int count, int *total) { int ret; - ret = bdrv_write_compressed(bs, offset >> 9, (uint8_t *)buf, count >> 9); + ret = blk_write_compressed(blk, offset >> 9, (uint8_t *)buf, count >> 9); if (ret < 0) { return ret; } @@ -496,20 +499,20 @@ static int do_write_compressed(BlockDriverState *bs, char *buf, int64_t offset, return 1; } -static int do_load_vmstate(BlockDriverState *bs, char *buf, int64_t offset, +static int do_load_vmstate(BlockBackend *blk, char *buf, int64_t offset, int count, int *total) { - *total = bdrv_load_vmstate(bs, (uint8_t *)buf, offset, count); + *total = blk_load_vmstate(blk, (uint8_t *)buf, offset, count); if (*total < 0) { return *total; } return 1; } -static int do_save_vmstate(BlockDriverState *bs, char *buf, int64_t offset, +static int do_save_vmstate(BlockBackend *blk, char *buf, int64_t offset, int count, int *total) { - *total = bdrv_save_vmstate(bs, (uint8_t *)buf, offset, count); + *total = blk_save_vmstate(blk, (uint8_t *)buf, offset, count); if (*total < 0) { return *total; } @@ -522,13 +525,13 @@ static void aio_rw_done(void *opaque, int ret) *(int *)opaque = ret; } -static int do_aio_readv(BlockDriverState *bs, QEMUIOVector *qiov, +static int do_aio_readv(BlockBackend *blk, QEMUIOVector *qiov, int64_t offset, int *total) { int async_ret = NOT_DONE; - bdrv_aio_readv(bs, offset >> 9, qiov, qiov->size >> 9, - aio_rw_done, &async_ret); + blk_aio_readv(blk, offset >> 9, qiov, qiov->size >> 9, + aio_rw_done, &async_ret); while (async_ret == NOT_DONE) { main_loop_wait(false); } @@ -537,13 +540,13 @@ static int do_aio_readv(BlockDriverState *bs, QEMUIOVector *qiov, return async_ret < 0 ? async_ret : 1; } -static int do_aio_writev(BlockDriverState *bs, QEMUIOVector *qiov, +static int do_aio_writev(BlockBackend *blk, QEMUIOVector *qiov, int64_t offset, int *total) { int async_ret = NOT_DONE; - bdrv_aio_writev(bs, offset >> 9, qiov, qiov->size >> 9, - aio_rw_done, &async_ret); + blk_aio_writev(blk, offset >> 9, qiov, qiov->size >> 9, + aio_rw_done, &async_ret); while (async_ret == NOT_DONE) { main_loop_wait(false); } @@ -567,7 +570,7 @@ static void multiwrite_cb(void *opaque, int ret) } } -static int do_aio_multiwrite(BlockDriverState *bs, BlockRequest* reqs, +static int do_aio_multiwrite(BlockBackend *blk, BlockRequest* reqs, int num_reqs, int *total) { int i, ret; @@ -583,7 +586,7 @@ static int do_aio_multiwrite(BlockDriverState *bs, BlockRequest* reqs, *total += reqs[i].qiov->size; } - ret = bdrv_aio_multiwrite(bs, reqs, num_reqs); + ret = blk_aio_multiwrite(blk, reqs, num_reqs); if (ret < 0) { return ret; } @@ -609,7 +612,7 @@ static void read_help(void) " -b, -- read from the VM state rather than the virtual disk\n" " -C, -- report statistics in a machine parsable format\n" " -l, -- length for pattern verification (only with -P)\n" -" -p, -- use bdrv_pread to read the file\n" +" -p, -- use blk_pread to read the file\n" " -P, -- use a pattern to verify read data\n" " -q, -- quiet mode, do not show I/O statistics\n" " -s, -- start offset for pattern verification (only with -P)\n" @@ -617,7 +620,7 @@ static void read_help(void) "\n"); } -static int read_f(BlockDriverState *bs, int argc, char **argv); +static int read_f(BlockBackend *blk, int argc, char **argv); static const cmdinfo_t read_cmd = { .name = "read", @@ -630,7 +633,7 @@ static const cmdinfo_t read_cmd = { .help = read_help, }; -static int read_f(BlockDriverState *bs, int argc, char **argv) +static int read_f(BlockBackend *blk, int argc, char **argv) { struct timeval t1, t2; int Cflag = 0, pflag = 0, qflag = 0, vflag = 0; @@ -736,15 +739,15 @@ static int read_f(BlockDriverState *bs, int argc, char **argv) } } - buf = qemu_io_alloc(bs, count, 0xab); + buf = qemu_io_alloc(blk, count, 0xab); gettimeofday(&t1, NULL); if (pflag) { - cnt = do_pread(bs, buf, offset, count, &total); + cnt = do_pread(blk, buf, offset, count, &total); } else if (bflag) { - cnt = do_load_vmstate(bs, buf, offset, count, &total); + cnt = do_load_vmstate(blk, buf, offset, count, &total); } else { - cnt = do_read(bs, buf, offset, count, &total); + cnt = do_read(blk, buf, offset, count, &total); } gettimeofday(&t2, NULL); @@ -801,7 +804,7 @@ static void readv_help(void) "\n"); } -static int readv_f(BlockDriverState *bs, int argc, char **argv); +static int readv_f(BlockBackend *blk, int argc, char **argv); static const cmdinfo_t readv_cmd = { .name = "readv", @@ -813,7 +816,7 @@ static const cmdinfo_t readv_cmd = { .help = readv_help, }; -static int readv_f(BlockDriverState *bs, int argc, char **argv) +static int readv_f(BlockBackend *blk, int argc, char **argv) { struct timeval t1, t2; int Cflag = 0, qflag = 0, vflag = 0; @@ -869,13 +872,13 @@ static int readv_f(BlockDriverState *bs, int argc, char **argv) } nr_iov = argc - optind; - buf = create_iovec(bs, &qiov, &argv[optind], nr_iov, 0xab); + buf = create_iovec(blk, &qiov, &argv[optind], nr_iov, 0xab); if (buf == NULL) { return 0; } gettimeofday(&t1, NULL); - cnt = do_aio_readv(bs, &qiov, offset, &total); + cnt = do_aio_readv(blk, &qiov, offset, &total); gettimeofday(&t2, NULL); if (cnt < 0) { @@ -923,16 +926,16 @@ static void write_help(void) " Writes into a segment of the currently open file, using a buffer\n" " filled with a set pattern (0xcdcdcdcd).\n" " -b, -- write to the VM state rather than the virtual disk\n" -" -c, -- write compressed data with bdrv_write_compressed\n" -" -p, -- use bdrv_pwrite to write the file\n" +" -c, -- write compressed data with blk_write_compressed\n" +" -p, -- use blk_pwrite to write the file\n" " -P, -- use different pattern to fill file\n" " -C, -- report statistics in a machine parsable format\n" " -q, -- quiet mode, do not show I/O statistics\n" -" -z, -- write zeroes using bdrv_co_write_zeroes\n" +" -z, -- write zeroes using blk_co_write_zeroes\n" "\n"); } -static int write_f(BlockDriverState *bs, int argc, char **argv); +static int write_f(BlockBackend *blk, int argc, char **argv); static const cmdinfo_t write_cmd = { .name = "write", @@ -945,7 +948,7 @@ static const cmdinfo_t write_cmd = { .help = write_help, }; -static int write_f(BlockDriverState *bs, int argc, char **argv) +static int write_f(BlockBackend *blk, int argc, char **argv) { struct timeval t1, t2; int Cflag = 0, pflag = 0, qflag = 0, bflag = 0, Pflag = 0, zflag = 0; @@ -1032,20 +1035,20 @@ static int write_f(BlockDriverState *bs, int argc, char **argv) } if (!zflag) { - buf = qemu_io_alloc(bs, count, pattern); + buf = qemu_io_alloc(blk, count, pattern); } gettimeofday(&t1, NULL); if (pflag) { - cnt = do_pwrite(bs, buf, offset, count, &total); + cnt = do_pwrite(blk, buf, offset, count, &total); } else if (bflag) { - cnt = do_save_vmstate(bs, buf, offset, count, &total); + cnt = do_save_vmstate(blk, buf, offset, count, &total); } else if (zflag) { - cnt = do_co_write_zeroes(bs, offset, count, &total); + cnt = do_co_write_zeroes(blk, offset, count, &total); } else if (cflag) { - cnt = do_write_compressed(bs, buf, offset, count, &total); + cnt = do_write_compressed(blk, buf, offset, count, &total); } else { - cnt = do_write(bs, buf, offset, count, &total); + cnt = do_write(blk, buf, offset, count, &total); } gettimeofday(&t2, NULL); @@ -1088,7 +1091,7 @@ writev_help(void) "\n"); } -static int writev_f(BlockDriverState *bs, int argc, char **argv); +static int writev_f(BlockBackend *blk, int argc, char **argv); static const cmdinfo_t writev_cmd = { .name = "writev", @@ -1100,7 +1103,7 @@ static const cmdinfo_t writev_cmd = { .help = writev_help, }; -static int writev_f(BlockDriverState *bs, int argc, char **argv) +static int writev_f(BlockBackend *blk, int argc, char **argv) { struct timeval t1, t2; int Cflag = 0, qflag = 0; @@ -1150,13 +1153,13 @@ static int writev_f(BlockDriverState *bs, int argc, char **argv) } nr_iov = argc - optind; - buf = create_iovec(bs, &qiov, &argv[optind], nr_iov, pattern); + buf = create_iovec(blk, &qiov, &argv[optind], nr_iov, pattern); if (buf == NULL) { return 0; } gettimeofday(&t1, NULL); - cnt = do_aio_writev(bs, &qiov, offset, &total); + cnt = do_aio_writev(blk, &qiov, offset, &total); gettimeofday(&t2, NULL); if (cnt < 0) { @@ -1197,7 +1200,7 @@ static void multiwrite_help(void) "\n"); } -static int multiwrite_f(BlockDriverState *bs, int argc, char **argv); +static int multiwrite_f(BlockBackend *blk, int argc, char **argv); static const cmdinfo_t multiwrite_cmd = { .name = "multiwrite", @@ -1209,7 +1212,7 @@ static const cmdinfo_t multiwrite_cmd = { .help = multiwrite_help, }; -static int multiwrite_f(BlockDriverState *bs, int argc, char **argv) +static int multiwrite_f(BlockBackend *blk, int argc, char **argv) { struct timeval t1, t2; int Cflag = 0, qflag = 0; @@ -1290,7 +1293,7 @@ static int multiwrite_f(BlockDriverState *bs, int argc, char **argv) nr_iov = j - optind; /* Build request */ - buf[i] = create_iovec(bs, &qiovs[i], &argv[optind], nr_iov, pattern); + buf[i] = create_iovec(blk, &qiovs[i], &argv[optind], nr_iov, pattern); if (buf[i] == NULL) { goto out; } @@ -1308,7 +1311,7 @@ static int multiwrite_f(BlockDriverState *bs, int argc, char **argv) nr_reqs = i; gettimeofday(&t1, NULL); - cnt = do_aio_multiwrite(bs, reqs, nr_reqs, &total); + cnt = do_aio_multiwrite(blk, reqs, nr_reqs, &total); gettimeofday(&t2, NULL); if (cnt < 0) { @@ -1337,6 +1340,7 @@ out: } struct aio_ctx { + BlockBackend *blk; QEMUIOVector qiov; int64_t offset; char *buf; @@ -1344,6 +1348,7 @@ struct aio_ctx { int vflag; int Cflag; int Pflag; + BlockAcctCookie acct; int pattern; struct timeval t1; }; @@ -1361,6 +1366,8 @@ static void aio_write_done(void *opaque, int ret) goto out; } + block_acct_done(blk_get_stats(ctx->blk), &ctx->acct); + if (ctx->qflag) { goto out; } @@ -1398,6 +1405,8 @@ static void aio_read_done(void *opaque, int ret) g_free(cmp_buf); } + block_acct_done(blk_get_stats(ctx->blk), &ctx->acct); + if (ctx->qflag) { goto out; } @@ -1436,7 +1445,7 @@ static void aio_read_help(void) "\n"); } -static int aio_read_f(BlockDriverState *bs, int argc, char **argv); +static int aio_read_f(BlockBackend *blk, int argc, char **argv); static const cmdinfo_t aio_read_cmd = { .name = "aio_read", @@ -1448,11 +1457,12 @@ static const cmdinfo_t aio_read_cmd = { .help = aio_read_help, }; -static int aio_read_f(BlockDriverState *bs, int argc, char **argv) +static int aio_read_f(BlockBackend *blk, int argc, char **argv) { int nr_iov, c; struct aio_ctx *ctx = g_new0(struct aio_ctx, 1); + ctx->blk = blk; while ((c = getopt(argc, argv, "CP:qv")) != EOF) { switch (c) { case 'C': @@ -1499,15 +1509,17 @@ static int aio_read_f(BlockDriverState *bs, int argc, char **argv) } nr_iov = argc - optind; - ctx->buf = create_iovec(bs, &ctx->qiov, &argv[optind], nr_iov, 0xab); + ctx->buf = create_iovec(blk, &ctx->qiov, &argv[optind], nr_iov, 0xab); if (ctx->buf == NULL) { g_free(ctx); return 0; } gettimeofday(&ctx->t1, NULL); - bdrv_aio_readv(bs, ctx->offset >> 9, &ctx->qiov, - ctx->qiov.size >> 9, aio_read_done, ctx); + block_acct_start(blk_get_stats(blk), &ctx->acct, ctx->qiov.size, + BLOCK_ACCT_READ); + blk_aio_readv(blk, ctx->offset >> 9, &ctx->qiov, + ctx->qiov.size >> 9, aio_read_done, ctx); return 0; } @@ -1531,7 +1543,7 @@ static void aio_write_help(void) "\n"); } -static int aio_write_f(BlockDriverState *bs, int argc, char **argv); +static int aio_write_f(BlockBackend *blk, int argc, char **argv); static const cmdinfo_t aio_write_cmd = { .name = "aio_write", @@ -1543,12 +1555,13 @@ static const cmdinfo_t aio_write_cmd = { .help = aio_write_help, }; -static int aio_write_f(BlockDriverState *bs, int argc, char **argv) +static int aio_write_f(BlockBackend *blk, int argc, char **argv) { int nr_iov, c; int pattern = 0xcd; struct aio_ctx *ctx = g_new0(struct aio_ctx, 1); + ctx->blk = blk; while ((c = getopt(argc, argv, "CqP:")) != EOF) { switch (c) { case 'C': @@ -1591,21 +1604,23 @@ static int aio_write_f(BlockDriverState *bs, int argc, char **argv) } nr_iov = argc - optind; - ctx->buf = create_iovec(bs, &ctx->qiov, &argv[optind], nr_iov, pattern); + ctx->buf = create_iovec(blk, &ctx->qiov, &argv[optind], nr_iov, pattern); if (ctx->buf == NULL) { g_free(ctx); return 0; } gettimeofday(&ctx->t1, NULL); - bdrv_aio_writev(bs, ctx->offset >> 9, &ctx->qiov, - ctx->qiov.size >> 9, aio_write_done, ctx); + block_acct_start(blk_get_stats(blk), &ctx->acct, ctx->qiov.size, + BLOCK_ACCT_WRITE); + blk_aio_writev(blk, ctx->offset >> 9, &ctx->qiov, + ctx->qiov.size >> 9, aio_write_done, ctx); return 0; } -static int aio_flush_f(BlockDriverState *bs, int argc, char **argv) +static int aio_flush_f(BlockBackend *blk, int argc, char **argv) { - bdrv_drain_all(); + blk_drain_all(); return 0; } @@ -1615,9 +1630,9 @@ static const cmdinfo_t aio_flush_cmd = { .oneline = "completes all outstanding aio requests" }; -static int flush_f(BlockDriverState *bs, int argc, char **argv) +static int flush_f(BlockBackend *blk, int argc, char **argv) { - bdrv_flush(bs); + blk_flush(blk); return 0; } @@ -1628,7 +1643,7 @@ static const cmdinfo_t flush_cmd = { .oneline = "flush all in-core file state to disk", }; -static int truncate_f(BlockDriverState *bs, int argc, char **argv) +static int truncate_f(BlockBackend *blk, int argc, char **argv) { int64_t offset; int ret; @@ -1639,7 +1654,7 @@ static int truncate_f(BlockDriverState *bs, int argc, char **argv) return 0; } - ret = bdrv_truncate(bs, offset); + ret = blk_truncate(blk, offset); if (ret < 0) { printf("truncate: %s\n", strerror(-ret)); return 0; @@ -1658,12 +1673,12 @@ static const cmdinfo_t truncate_cmd = { .oneline = "truncates the current file at the given offset", }; -static int length_f(BlockDriverState *bs, int argc, char **argv) +static int length_f(BlockBackend *blk, int argc, char **argv) { int64_t size; char s1[64]; - size = bdrv_getlength(bs); + size = blk_getlength(blk); if (size < 0) { printf("getlength: %s\n", strerror(-size)); return 0; @@ -1683,8 +1698,9 @@ static const cmdinfo_t length_cmd = { }; -static int info_f(BlockDriverState *bs, int argc, char **argv) +static int info_f(BlockBackend *blk, int argc, char **argv) { + BlockDriverState *bs = blk_bs(blk); BlockDriverInfo bdi; ImageInfoSpecific *spec_info; char s1[64], s2[64]; @@ -1742,7 +1758,7 @@ static void discard_help(void) "\n"); } -static int discard_f(BlockDriverState *bs, int argc, char **argv); +static int discard_f(BlockBackend *blk, int argc, char **argv); static const cmdinfo_t discard_cmd = { .name = "discard", @@ -1755,7 +1771,7 @@ static const cmdinfo_t discard_cmd = { .help = discard_help, }; -static int discard_f(BlockDriverState *bs, int argc, char **argv) +static int discard_f(BlockBackend *blk, int argc, char **argv) { struct timeval t1, t2; int Cflag = 0, qflag = 0; @@ -1794,8 +1810,8 @@ static int discard_f(BlockDriverState *bs, int argc, char **argv) } gettimeofday(&t1, NULL); - ret = bdrv_discard(bs, offset >> BDRV_SECTOR_BITS, - count >> BDRV_SECTOR_BITS); + ret = blk_discard(blk, offset >> BDRV_SECTOR_BITS, + count >> BDRV_SECTOR_BITS); gettimeofday(&t2, NULL); if (ret < 0) { @@ -1813,8 +1829,9 @@ out: return 0; } -static int alloc_f(BlockDriverState *bs, int argc, char **argv) +static int alloc_f(BlockBackend *blk, int argc, char **argv) { + BlockDriverState *bs = blk_bs(blk); int64_t offset, sector_num; int nb_sectors, remaining; char s1[64]; @@ -1910,20 +1927,27 @@ static int map_is_allocated(BlockDriverState *bs, int64_t sector_num, return firstret; } -static int map_f(BlockDriverState *bs, int argc, char **argv) +static int map_f(BlockBackend *blk, int argc, char **argv) { int64_t offset; - int64_t nb_sectors; + int64_t nb_sectors, total_sectors; char s1[64]; int64_t num; int ret; const char *retstr; offset = 0; - nb_sectors = bs->total_sectors; + total_sectors = blk_nb_sectors(blk); + if (total_sectors < 0) { + error_report("Failed to query image length: %s", + strerror(-total_sectors)); + return 0; + } + + nb_sectors = total_sectors; do { - ret = map_is_allocated(bs, offset, nb_sectors, &num); + ret = map_is_allocated(blk_bs(blk), offset, nb_sectors, &num); if (ret < 0) { error_report("Failed to get allocation status: %s", strerror(-ret)); return 0; @@ -1940,7 +1964,7 @@ static int map_f(BlockDriverState *bs, int argc, char **argv) offset += num; nb_sectors -= num; - } while (offset < bs->total_sectors); + } while (offset < total_sectors); return 0; } @@ -1954,11 +1978,11 @@ static const cmdinfo_t map_cmd = { .oneline = "prints the allocated areas of a file", }; -static int break_f(BlockDriverState *bs, int argc, char **argv) +static int break_f(BlockBackend *blk, int argc, char **argv) { int ret; - ret = bdrv_debug_breakpoint(bs, argv[1], argv[2]); + ret = bdrv_debug_breakpoint(blk_bs(blk), argv[1], argv[2]); if (ret < 0) { printf("Could not set breakpoint: %s\n", strerror(-ret)); } @@ -1966,11 +1990,11 @@ static int break_f(BlockDriverState *bs, int argc, char **argv) return 0; } -static int remove_break_f(BlockDriverState *bs, int argc, char **argv) +static int remove_break_f(BlockBackend *blk, int argc, char **argv) { int ret; - ret = bdrv_debug_remove_breakpoint(bs, argv[1]); + ret = bdrv_debug_remove_breakpoint(blk_bs(blk), argv[1]); if (ret < 0) { printf("Could not remove breakpoint %s: %s\n", argv[1], strerror(-ret)); } @@ -1997,11 +2021,11 @@ static const cmdinfo_t remove_break_cmd = { .oneline = "remove a breakpoint by tag", }; -static int resume_f(BlockDriverState *bs, int argc, char **argv) +static int resume_f(BlockBackend *blk, int argc, char **argv) { int ret; - ret = bdrv_debug_resume(bs, argv[1]); + ret = bdrv_debug_resume(blk_bs(blk), argv[1]); if (ret < 0) { printf("Could not resume request: %s\n", strerror(-ret)); } @@ -2018,10 +2042,10 @@ static const cmdinfo_t resume_cmd = { .oneline = "resumes the request tagged as tag", }; -static int wait_break_f(BlockDriverState *bs, int argc, char **argv) +static int wait_break_f(BlockBackend *blk, int argc, char **argv) { - while (!bdrv_debug_is_suspended(bs, argv[1])) { - aio_poll(bdrv_get_aio_context(bs), true); + while (!bdrv_debug_is_suspended(blk_bs(blk), argv[1])) { + aio_poll(blk_get_aio_context(blk), true); } return 0; @@ -2036,7 +2060,7 @@ static const cmdinfo_t wait_break_cmd = { .oneline = "waits for the suspension of a request", }; -static int abort_f(BlockDriverState *bs, int argc, char **argv) +static int abort_f(BlockBackend *blk, int argc, char **argv) { abort(); } @@ -2048,13 +2072,58 @@ static const cmdinfo_t abort_cmd = { .oneline = "simulate a program crash using abort(3)", }; +static void sigraise_help(void) +{ + printf( +"\n" +" raises the given signal\n" +"\n" +" Example:\n" +" 'sigraise %i' - raises SIGTERM\n" +"\n" +" Invokes raise(signal), where \"signal\" is the mandatory integer argument\n" +" given to sigraise.\n" +"\n", SIGTERM); +} + +static int sigraise_f(BlockBackend *blk, int argc, char **argv); + +static const cmdinfo_t sigraise_cmd = { + .name = "sigraise", + .cfunc = sigraise_f, + .argmin = 1, + .argmax = 1, + .flags = CMD_NOFILE_OK, + .args = "signal", + .oneline = "raises a signal", + .help = sigraise_help, +}; + +static int sigraise_f(BlockBackend *blk, int argc, char **argv) +{ + int sig = cvtnum(argv[1]); + if (sig < 0) { + printf("non-numeric signal number argument -- %s\n", argv[1]); + return 0; + } + + /* Using raise() to kill this process does not necessarily flush all open + * streams. At least stdout and stderr (although the latter should be + * non-buffered anyway) should be flushed, though. */ + fflush(stdout); + fflush(stderr); + + raise(sig); + return 0; +} + static void sleep_cb(void *opaque) { bool *expired = opaque; *expired = true; } -static int sleep_f(BlockDriverState *bs, int argc, char **argv) +static int sleep_f(BlockBackend *blk, int argc, char **argv) { char *endptr; long ms; @@ -2123,7 +2192,7 @@ static void help_all(void) printf("\nUse 'help commandname' for extended help.\n"); } -static int help_f(BlockDriverState *bs, int argc, char **argv) +static int help_f(BlockBackend *blk, int argc, char **argv) { const cmdinfo_t *ct; @@ -2153,7 +2222,7 @@ static const cmdinfo_t help_cmd = { .oneline = "help for one or all commands", }; -bool qemuio_command(BlockDriverState *bs, const char *cmd) +bool qemuio_command(BlockBackend *blk, const char *cmd) { char *input; const cmdinfo_t *ct; @@ -2166,7 +2235,7 @@ bool qemuio_command(BlockDriverState *bs, const char *cmd) if (c) { ct = find_command(v[0]); if (ct) { - done = command(bs, ct, c, v); + done = command(blk, ct, c, v); } else { fprintf(stderr, "command \"%s\" not found\n", v[0]); } @@ -2202,4 +2271,5 @@ static void __attribute((constructor)) init_qemuio_commands(void) qemuio_add_command(&wait_break_cmd); qemuio_add_command(&abort_cmd); qemuio_add_command(&sleep_cmd); + qemuio_add_command(&sigraise_cmd); } diff --git a/qemu-io.c b/qemu-io.c index 60f84dd..8e41080 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -28,7 +28,6 @@ static char *progname; static BlockBackend *qemuio_blk; -static BlockDriverState *qemuio_bs; /* qemu-io commands passed using -c */ static int ncmdline; @@ -36,10 +35,9 @@ static char **cmdline; static ReadLineState *readline_state; -static int close_f(BlockDriverState *bs, int argc, char **argv) +static int close_f(BlockBackend *blk, int argc, char **argv) { blk_unref(qemuio_blk); - qemuio_bs = NULL; qemuio_blk = NULL; return 0; } @@ -51,31 +49,22 @@ static const cmdinfo_t close_cmd = { .oneline = "close the current open file", }; -static int openfile(char *name, int flags, int growable, QDict *opts) +static int openfile(char *name, int flags, QDict *opts) { Error *local_err = NULL; - if (qemuio_bs) { + if (qemuio_blk) { fprintf(stderr, "file open already, try 'help close'\n"); QDECREF(opts); return 1; } - qemuio_blk = blk_new_with_bs("hda", &error_abort); - qemuio_bs = blk_bs(qemuio_blk); - - if (growable) { - flags |= BDRV_O_PROTOCOL; - } - - if (bdrv_open(&qemuio_bs, name, NULL, opts, flags, NULL, &local_err) < 0) { + qemuio_blk = blk_new_open("hda", name, NULL, opts, flags, &local_err); + if (!qemuio_blk) { fprintf(stderr, "%s: can't open%s%s: %s\n", progname, name ? " device " : "", name ?: "", error_get_pretty(local_err)); error_free(local_err); - blk_unref(qemuio_blk); - qemuio_bs = NULL; - qemuio_blk = NULL; return 1; } @@ -95,12 +84,11 @@ static void open_help(void) " -r, -- open file read-only\n" " -s, -- use snapshot file\n" " -n, -- disable host cache\n" -" -g, -- allow file to grow (only applies to protocols)\n" " -o, -- options to be given to the block driver" "\n"); } -static int open_f(BlockDriverState *bs, int argc, char **argv); +static int open_f(BlockBackend *blk, int argc, char **argv); static const cmdinfo_t open_cmd = { .name = "open", @@ -124,11 +112,10 @@ static QemuOptsList empty_opts = { }, }; -static int open_f(BlockDriverState *bs, int argc, char **argv) +static int open_f(BlockBackend *blk, int argc, char **argv) { int flags = 0; int readonly = 0; - int growable = 0; int c; QemuOpts *qopts; QDict *opts; @@ -144,9 +131,6 @@ static int open_f(BlockDriverState *bs, int argc, char **argv) case 'r': readonly = 1; break; - case 'g': - growable = 1; - break; case 'o': if (!qemu_opts_parse(&empty_opts, optarg, 0)) { printf("could not parse option list -- %s\n", optarg); @@ -169,16 +153,16 @@ static int open_f(BlockDriverState *bs, int argc, char **argv) qemu_opts_reset(&empty_opts); if (optind == argc - 1) { - return openfile(argv[optind], flags, growable, opts); + return openfile(argv[optind], flags, opts); } else if (optind == argc) { - return openfile(NULL, flags, growable, opts); + return openfile(NULL, flags, opts); } else { QDECREF(opts); return qemuio_command_usage(&open_cmd); } } -static int quit_f(BlockDriverState *bs, int argc, char **argv) +static int quit_f(BlockBackend *blk, int argc, char **argv) { return 1; } @@ -196,15 +180,15 @@ static const cmdinfo_t quit_cmd = { static void usage(const char *name) { printf( -"Usage: %s [-h] [-V] [-rsnm] [-c STRING] ... [file]\n" +"Usage: %s [-h] [-V] [-rsnm] [-f FMT] [-c STRING] ... [file]\n" "QEMU Disk exerciser\n" "\n" " -c, --cmd STRING execute command with its arguments\n" " from the given string\n" +" -f, --format FMT specifies the block driver to use\n" " -r, --read-only export read-only\n" " -s, --snapshot use snapshot file\n" " -n, --nocache disable host cache\n" -" -g, --growable allow file to grow (only applies to protocols)\n" " -m, --misalign misalign allocations for O_DIRECT\n" " -k, --native-aio use kernel AIO implementation (on Linux only)\n" " -t, --cache=MODE use the given cache mode for the image\n" @@ -315,7 +299,7 @@ static void command_loop(void) char *input; for (i = 0; !done && i < ncmdline; i++) { - done = qemuio_command(qemuio_bs, cmdline[i]); + done = qemuio_command(qemuio_blk, cmdline[i]); } if (cmdline) { g_free(cmdline); @@ -340,7 +324,7 @@ static void command_loop(void) if (input == NULL) { break; } - done = qemuio_command(qemuio_bs, input); + done = qemuio_command(qemuio_blk, input); g_free(input); prompted = 0; @@ -363,18 +347,17 @@ static void reenable_tty_echo(void) int main(int argc, char **argv) { int readonly = 0; - int growable = 0; - const char *sopt = "hVc:d:rsnmgkt:T:"; + const char *sopt = "hVc:d:f:rsnmgkt:T:"; const struct option lopt[] = { { "help", 0, NULL, 'h' }, { "version", 0, NULL, 'V' }, { "offset", 1, NULL, 'o' }, { "cmd", 1, NULL, 'c' }, + { "format", 1, NULL, 'f' }, { "read-only", 0, NULL, 'r' }, { "snapshot", 0, NULL, 's' }, { "nocache", 0, NULL, 'n' }, { "misalign", 0, NULL, 'm' }, - { "growable", 0, NULL, 'g' }, { "native-aio", 0, NULL, 'k' }, { "discard", 1, NULL, 'd' }, { "cache", 1, NULL, 't' }, @@ -385,6 +368,7 @@ int main(int argc, char **argv) int opt_index = 0; int flags = BDRV_O_UNMAP; Error *local_error = NULL; + QDict *opts = NULL; #ifdef CONFIG_POSIX signal(SIGPIPE, SIG_IGN); @@ -393,6 +377,8 @@ int main(int argc, char **argv) progname = basename(argv[0]); qemu_init_exec_dir(argv[0]); + bdrv_init(); + while ((c = getopt_long(argc, argv, sopt, lopt, &opt_index)) != -1) { switch (c) { case 's': @@ -407,6 +393,12 @@ int main(int argc, char **argv) exit(1); } break; + case 'f': + if (!opts) { + opts = qdict_new(); + } + qdict_put(opts, "driver", qstring_from_str(optarg)); + break; case 'c': add_user_command(optarg); break; @@ -416,9 +408,6 @@ int main(int argc, char **argv) case 'm': qemuio_misalign = true; break; - case 'g': - growable = 1; - break; case 'k': flags |= BDRV_O_NATIVE_AIO; break; @@ -451,11 +440,9 @@ int main(int argc, char **argv) } if (qemu_init_main_loop(&local_error)) { - error_report("%s", error_get_pretty(local_error)); - error_free(local_error); + error_report_err(local_error); exit(1); } - bdrv_init(); /* initialize commands */ qemuio_add_command(&quit_cmd); @@ -477,7 +464,7 @@ int main(int argc, char **argv) } if ((argc - optind) == 1) { - openfile(argv[optind], flags, growable, NULL); + openfile(argv[optind], flags, opts); } command_loop(); diff --git a/qemu-log.c b/qemu-log.c index 797f2af..13f3813 100644 --- a/qemu-log.c +++ b/qemu-log.c @@ -106,10 +106,12 @@ const QEMULogItem qemu_log_items[] = { "show trace before each executed TB (lots of logs)" }, { CPU_LOG_TB_CPU, "cpu", "show CPU state before block translation" }, + { CPU_LOG_MMU, "mmu", + "log MMU-related activities" }, { CPU_LOG_PCALL, "pcall", "x86 only: show protected mode far calls/returns/exceptions" }, { CPU_LOG_RESET, "cpu_reset", - "x86 only: show CPU state before CPU resets" }, + "show CPU state before CPU resets" }, { CPU_LOG_IOPORT, "ioport", "show all i/o ports accesses" }, { LOG_UNIMP, "unimp", diff --git a/qemu-nbd.c b/qemu-nbd.c index 5cd6c6d..7e690ff 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -142,11 +142,12 @@ static void read_partition(uint8_t *p, struct partition_record *r) r->end_head = p[5]; r->end_cylinder = p[7] | ((p[6] << 2) & 0x300); r->end_sector = p[6] & 0x3f; - r->start_sector_abs = p[8] | p[9] << 8 | p[10] << 16 | p[11] << 24; - r->nb_sectors_abs = p[12] | p[13] << 8 | p[14] << 16 | p[15] << 24; + + r->start_sector_abs = le32_to_cpup((uint32_t *)(p + 8)); + r->nb_sectors_abs = le32_to_cpup((uint32_t *)(p + 12)); } -static int find_partition(BlockDriverState *bs, int partition, +static int find_partition(BlockBackend *blk, int partition, off_t *offset, off_t *size) { struct partition_record mbr[4]; @@ -155,7 +156,7 @@ static int find_partition(BlockDriverState *bs, int partition, int ext_partnum = 4; int ret; - if ((ret = bdrv_read(bs, 0, data, 1)) < 0) { + if ((ret = blk_read(blk, 0, data, 1)) < 0) { errno = -ret; err(EXIT_FAILURE, "error while reading"); } @@ -167,23 +168,25 @@ static int find_partition(BlockDriverState *bs, int partition, for (i = 0; i < 4; i++) { read_partition(&data[446 + 16 * i], &mbr[i]); - if (!mbr[i].nb_sectors_abs) + if (!mbr[i].system || !mbr[i].nb_sectors_abs) { continue; + } if (mbr[i].system == 0xF || mbr[i].system == 0x5) { struct partition_record ext[4]; uint8_t data1[512]; int j; - if ((ret = bdrv_read(bs, mbr[i].start_sector_abs, data1, 1)) < 0) { + if ((ret = blk_read(blk, mbr[i].start_sector_abs, data1, 1)) < 0) { errno = -ret; err(EXIT_FAILURE, "error while reading"); } for (j = 0; j < 4; j++) { read_partition(&data1[446 + 16 * j], &ext[j]); - if (!ext[j].nb_sectors_abs) + if (!ext[j].system || !ext[j].nb_sectors_abs) { continue; + } if ((ext_partnum + j + 1) == partition) { *offset = (uint64_t)ext[j].start_sector_abs << 9; @@ -228,8 +231,7 @@ static int tcp_socket_incoming(const char *address, uint16_t port) int fd = inet_listen(address_and_port, NULL, 0, SOCK_STREAM, 0, &local_err); if (local_err != NULL) { - error_report("%s", error_get_pretty(local_err)); - error_free(local_err); + error_report_err(local_err); } return fd; } @@ -240,8 +242,7 @@ static int unix_socket_incoming(const char *path) int fd = unix_listen(path, NULL, 0, &local_err); if (local_err != NULL) { - error_report("%s", error_get_pretty(local_err)); - error_free(local_err); + error_report_err(local_err); } return fd; } @@ -252,8 +253,7 @@ static int unix_socket_outgoing(const char *path) int fd = unix_connect(path, &local_err); if (local_err != NULL) { - error_report("%s", error_get_pretty(local_err)); - error_free(local_err); + error_report_err(local_err); } return fd; } @@ -279,11 +279,11 @@ static void *nbd_client_thread(void *arg) { char *device = arg; off_t size; - size_t blocksize; uint32_t nbdflags; int fd, sock; int ret; pthread_t show_parts_thread; + Error *local_error = NULL; sock = unix_socket_outgoing(sockpath); if (sock < 0) { @@ -291,8 +291,12 @@ static void *nbd_client_thread(void *arg) } ret = nbd_receive_negotiate(sock, NULL, &nbdflags, - &size, &blocksize); + &size, &local_error); if (ret < 0) { + if (local_error) { + fprintf(stderr, "%s\n", error_get_pretty(local_error)); + error_free(local_error); + } goto out_socket; } @@ -303,7 +307,7 @@ static void *nbd_client_thread(void *arg) goto out_socket; } - ret = nbd_init(fd, sock, nbdflags, size, blocksize); + ret = nbd_init(fd, sock, nbdflags, size); if (ret < 0) { goto out_fd; } @@ -386,7 +390,6 @@ int main(int argc, char **argv) { BlockBackend *blk; BlockDriverState *bs; - BlockDriver *drv; off_t dev_offset = 0; uint32_t nbdflags = 0; bool disconnect = false; @@ -429,7 +432,7 @@ int main(int argc, char **argv) char *end; int flags = BDRV_O_RDWR; int partition = -1; - int ret; + int ret = 0; int fd; bool seen_cache = false; bool seen_discard = false; @@ -440,6 +443,7 @@ int main(int argc, char **argv) const char *fmt = NULL; Error *local_err = NULL; BlockdevDetectZeroesOptions detect_zeroes = BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF; + QDict *options = NULL; /* The client thread uses SIGTERM to interrupt the server. A signal * handler ensures that "qemu-nbd -v -c" exits with a nice status code. @@ -631,7 +635,9 @@ int main(int argc, char **argv) * print errors and exit with the proper status code. */ pid = fork(); - if (pid == 0) { + if (pid < 0) { + err(EXIT_FAILURE, "Failed to fork"); + } else if (pid == 0) { close(stderr_fd[0]); ret = qemu_daemon(1, 0); @@ -676,32 +682,24 @@ int main(int argc, char **argv) } if (qemu_init_main_loop(&local_err)) { - error_report("%s", error_get_pretty(local_err)); - error_free(local_err); + error_report_err(local_err); exit(EXIT_FAILURE); } bdrv_init(); atexit(bdrv_close_all); if (fmt) { - drv = bdrv_find_format(fmt); - if (!drv) { - errx(EXIT_FAILURE, "Unknown file format '%s'", fmt); - } - } else { - drv = NULL; + options = qdict_new(); + qdict_put(options, "driver", qstring_from_str(fmt)); } - blk = blk_new_with_bs("hda", &error_abort); - bs = blk_bs(blk); - srcpath = argv[optind]; - ret = bdrv_open(&bs, srcpath, NULL, NULL, flags, drv, &local_err); - if (ret < 0) { - errno = -ret; - err(EXIT_FAILURE, "Failed to bdrv_open '%s': %s", argv[optind], - error_get_pretty(local_err)); + blk = blk_new_open("hda", srcpath, NULL, options, flags, &local_err); + if (!blk) { + errx(EXIT_FAILURE, "Failed to blk_new_open '%s': %s", argv[optind], + error_get_pretty(local_err)); } + bs = blk_bs(blk); if (sn_opts) { ret = bdrv_snapshot_load_tmp(bs, @@ -720,17 +718,25 @@ int main(int argc, char **argv) } bs->detect_zeroes = detect_zeroes; - fd_size = bdrv_getlength(bs); + fd_size = blk_getlength(blk); + if (fd_size < 0) { + errx(EXIT_FAILURE, "Failed to determine the image length: %s", + strerror(-fd_size)); + } if (partition != -1) { - ret = find_partition(bs, partition, &dev_offset, &fd_size); + ret = find_partition(blk, partition, &dev_offset, &fd_size); if (ret < 0) { errno = -ret; err(EXIT_FAILURE, "Could not find partition %d", partition); } } - exp = nbd_export_new(bs, dev_offset, fd_size, nbdflags, nbd_export_closed); + exp = nbd_export_new(blk, dev_offset, fd_size, nbdflags, nbd_export_closed, + &local_err); + if (!exp) { + errx(EXIT_FAILURE, "%s", error_get_pretty(local_err)); + } if (sockpath) { fd = unix_socket_incoming(sockpath); diff --git a/qemu-options.hx b/qemu-options.hx index 64af16d..319d971 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -37,7 +37,10 @@ DEF("machine", HAS_ARG, QEMU_OPTION_machine, \ " kvm_shadow_mem=size of KVM shadow MMU\n" " dump-guest-core=on|off include guest memory in a core dump (default=on)\n" " mem-merge=on|off controls memory merge support (default: on)\n" - " iommu=on|off controls emulated Intel IOMMU (VT-d) support (default=off)\n", + " iommu=on|off controls emulated Intel IOMMU (VT-d) support (default=off)\n" + " aes-key-wrap=on|off controls support for AES key wrapping (default=on)\n" + " dea-key-wrap=on|off controls support for DEA key wrapping (default=on)\n" + " suppress-vmdesc=on|off disables self-describing migration (default=off)\n", QEMU_ARCH_ALL) STEXI @item -machine [type=]@var{name}[,prop=@var{value}[,...]] @@ -66,6 +69,14 @@ the host, de-duplicates identical memory pages among VMs instances (enabled by default). @item iommu=on|off Enables or disables emulated Intel IOMMU (VT-d) support. The default is off. +@item aes-key-wrap=on|off +Enables or disables AES key wrapping support on s390-ccw hosts. This feature +controls whether AES wrapping keys will be created to allow +execution of AES cryptographic functions. The default is on. +@item dea-key-wrap=on|off +Enables or disables DEA key wrapping support on s390-ccw hosts. This feature +controls whether DEA wrapping keys will be created to allow +execution of DEA cryptographic functions. The default is on. @end table ETEXI @@ -237,12 +248,24 @@ DEF("m", HAS_ARG, QEMU_OPTION_m, "NOTE: Some architectures might enforce a specific granularity\n", QEMU_ARCH_ALL) STEXI -@item -m [size=]@var{megs} +@item -m [size=]@var{megs}[,slots=n,maxmem=size] @findex -m -Set virtual RAM size to @var{megs} megabytes. Default is 128 MiB. Optionally, -a suffix of ``M'' or ``G'' can be used to signify a value in megabytes or -gigabytes respectively. Optional pair @var{slots}, @var{maxmem} could be used -to set amount of hotluggable memory slots and possible maximum amount of memory. +Sets guest startup RAM size to @var{megs} megabytes. Default is 128 MiB. +Optionally, a suffix of ``M'' or ``G'' can be used to signify a value in +megabytes or gigabytes respectively. Optional pair @var{slots}, @var{maxmem} +could be used to set amount of hotpluggable memory slots and maximum amount of +memory. Note that @var{maxmem} must be aligned to the page size. + +For example, the following command-line sets the guest startup RAM size to +1GB, creates 3 slots to hotplug additional memory and sets the maximum +memory the guest can reach to 4GB: + +@example +qemu-system-x86_64 -m 1G,slots=3,maxmem=4G +@end example + +If @var{slots} and @var{maxmem} are not specified, memory hotplug won't +be enabled and the guest startup RAM will never increase. ETEXI DEF("mem-path", HAS_ARG, QEMU_OPTION_mempath, @@ -396,8 +419,7 @@ STEXI @item -fdb @var{file} @findex -fda @findex -fdb -Use @var{file} as floppy disk 0/1 image (@pxref{disk_images}). You can -use the host floppy by using @file{/dev/fd0} as filename (@pxref{host_drives}). +Use @var{file} as floppy disk 0/1 image (@pxref{disk_images}). ETEXI DEF("hda", HAS_ARG, QEMU_OPTION_hda, @@ -953,7 +975,7 @@ DEF("spice", HAS_ARG, QEMU_OPTION_spice, "-spice [port=port][,tls-port=secured-port][,x509-dir=

    ]\n" " [,x509-key-file=][,x509-key-password=]\n" " [,x509-cert-file=][,x509-cacert-file=]\n" - " [,x509-dh-key-file=][,addr=addr][,ipv4|ipv6]\n" + " [,x509-dh-key-file=][,addr=addr][,ipv4|ipv6|unix]\n" " [,tls-ciphers=]\n" " [,tls-channel=[main|display|cursor|inputs|record|playback]]\n" " [,plaintext-channel=[main|display|cursor|inputs|record|playback]]\n" @@ -982,6 +1004,7 @@ Set the IP address spice is listening on. Default is any address. @item ipv4 @item ipv6 +@item unix Force using the specified IP version. @item password= @@ -1363,11 +1386,25 @@ ETEXI DEF("smbios", HAS_ARG, QEMU_OPTION_smbios, "-smbios file=binary\n" " load SMBIOS entry from binary file\n" - "-smbios type=0[,vendor=str][,version=str][,date=str][,release=%d.%d][,uefi=on|off]\n" + "-smbios type=0[,vendor=str][,version=str][,date=str][,release=%d.%d]\n" + " [,uefi=on|off]\n" " specify SMBIOS type 0 fields\n" "-smbios type=1[,manufacturer=str][,product=str][,version=str][,serial=str]\n" " [,uuid=uuid][,sku=str][,family=str]\n" - " specify SMBIOS type 1 fields\n", QEMU_ARCH_I386) + " specify SMBIOS type 1 fields\n" + "-smbios type=2[,manufacturer=str][,product=str][,version=str][,serial=str]\n" + " [,asset=str][,location=str]\n" + " specify SMBIOS type 2 fields\n" + "-smbios type=3[,manufacturer=str][,version=str][,serial=str][,asset=str]\n" + " [,sku=str]\n" + " specify SMBIOS type 3 fields\n" + "-smbios type=4[,sock_pfx=str][,manufacturer=str][,version=str][,serial=str]\n" + " [,asset=str][,part=str]\n" + " specify SMBIOS type 4 fields\n" + "-smbios type=17[,loc_pfx=str][,bank=str][,manufacturer=str][,serial=str]\n" + " [,asset=str][,part=str][,speed=%d]\n" + " specify SMBIOS type 17 fields\n", + QEMU_ARCH_I386) STEXI @item -smbios file=@var{binary} @findex -smbios @@ -1376,8 +1413,20 @@ Load SMBIOS entry from binary file. @item -smbios type=0[,vendor=@var{str}][,version=@var{str}][,date=@var{str}][,release=@var{%d.%d}][,uefi=on|off] Specify SMBIOS type 0 fields -@item -smbios type=1[,manufacturer=@var{str}][,product=@var{str}] [,version=@var{str}][,serial=@var{str}][,uuid=@var{uuid}][,sku=@var{str}] [,family=@var{str}] +@item -smbios type=1[,manufacturer=@var{str}][,product=@var{str}][,version=@var{str}][,serial=@var{str}][,uuid=@var{uuid}][,sku=@var{str}][,family=@var{str}] Specify SMBIOS type 1 fields + +@item -smbios type=2[,manufacturer=@var{str}][,product=@var{str}][,version=@var{str}][,serial=@var{str}][,asset=@var{str}][,location=@var{str}][,family=@var{str}] +Specify SMBIOS type 2 fields + +@item -smbios type=3[,manufacturer=@var{str}][,version=@var{str}][,serial=@var{str}][,asset=@var{str}][,sku=@var{str}] +Specify SMBIOS type 3 fields + +@item -smbios type=4[,sock_pfx=@var{str}][,manufacturer=@var{str}][,version=@var{str}][,serial=@var{str}][,asset=@var{str}][,part=@var{str}] +Specify SMBIOS type 4 fields + +@item -smbios type=17[,loc_pfx=@var{str}][,bank=@var{str}][,manufacturer=@var{str}][,serial=@var{str}][,asset=@var{str}][,part=@var{str}][,speed=@var{%d}] +Specify SMBIOS type 17 fields ETEXI STEXI @@ -2790,6 +2839,14 @@ STEXI @findex -qmp Like -monitor but opens in 'control' mode. ETEXI +DEF("qmp-pretty", HAS_ARG, QEMU_OPTION_qmp_pretty, \ + "-qmp-pretty dev like -qmp but uses pretty JSON formatting\n", + QEMU_ARCH_ALL) +STEXI +@item -qmp-pretty @var{dev} +@findex -qmp-pretty +Like -qmp but uses pretty JSON formatting. +ETEXI DEF("mon", HAS_ARG, QEMU_OPTION_mon, \ "-mon [chardev=]name[,mode=readline|control][,default]\n", QEMU_ARCH_ALL) @@ -3160,12 +3217,30 @@ Set TB size. ETEXI DEF("incoming", HAS_ARG, QEMU_OPTION_incoming, \ - "-incoming p prepare for incoming migration, listen on port p\n", + "-incoming tcp:[host]:port[,to=maxport][,ipv4][,ipv6]\n" \ + "-incoming rdma:host:port[,ipv4][,ipv6]\n" \ + "-incoming unix:socketpath\n" \ + " prepare for incoming migration, listen on\n" \ + " specified protocol and socket address\n" \ + "-incoming fd:fd\n" \ + "-incoming exec:cmdline\n" \ + " accept incoming migration on given file descriptor\n" \ + " or from given external command\n", QEMU_ARCH_ALL) STEXI -@item -incoming @var{port} +@item -incoming tcp:[@var{host}]:@var{port}[,to=@var{maxport}][,ipv4][,ipv6] +@item -incoming rdma:@var{host}:@var{port}[,ipv4][,ipv6] @findex -incoming -Prepare for incoming migration, listen on @var{port}. +Prepare for incoming migration, listen on a given tcp port. + +@item -incoming unix:@var{socketpath} +Prepare for incoming migration, listen on a given unix socket. + +@item -incoming fd:@var{fd} +Accept incoming migration from a given filedescriptor. + +@item -incoming exec:@var{cmdline} +Accept incoming migration as an output from specified external command. ETEXI DEF("nodefaults", 0, QEMU_OPTION_nodefaults, \ @@ -3218,7 +3293,17 @@ DEF("semihosting", 0, QEMU_OPTION_semihosting, STEXI @item -semihosting @findex -semihosting -Semihosting mode (ARM, M68K, Xtensa only). +Enable semihosting mode (ARM, M68K, Xtensa only). +ETEXI +DEF("semihosting-config", HAS_ARG, QEMU_OPTION_semihosting_config, + "-semihosting-config [enable=on|off,]target=native|gdb|auto semihosting configuration\n", +QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_XTENSA | QEMU_ARCH_LM32) +STEXI +@item -semihosting-config [enable=on|off,]target=native|gdb|auto +@findex -semihosting-config +Enable semihosting and define where the semihosting calls will be addressed, +to QEMU (@code{native}) or to GDB (@code{gdb}). The default is @code{auto}, which means +@code{gdb} during debug sessions and @code{native} otherwise (ARM, M68K, Xtensa only). ETEXI DEF("old-param", 0, QEMU_OPTION_old_param, "-old-param old param mode\n", QEMU_ARCH_ARM) diff --git a/qemu-seccomp.c b/qemu-seccomp.c index af6a375..f9de0d3 100644 --- a/qemu-seccomp.c +++ b/qemu-seccomp.c @@ -229,13 +229,15 @@ static const struct QemuSeccompSyscall seccomp_whitelist[] = { { SCMP_SYS(shmdt), 240 }, { SCMP_SYS(timerfd_create), 240 }, { SCMP_SYS(shmctl), 240 }, + { SCMP_SYS(mlockall), 240 }, { SCMP_SYS(mlock), 240 }, { SCMP_SYS(munlock), 240 }, { SCMP_SYS(semctl), 240 }, { SCMP_SYS(fallocate), 240 }, { SCMP_SYS(fadvise64), 240 }, { SCMP_SYS(inotify_init1), 240 }, - { SCMP_SYS(inotify_add_watch), 240 } + { SCMP_SYS(inotify_add_watch), 240 }, + { SCMP_SYS(mbind), 240 } }; int seccomp_start(void) diff --git a/qemu-timer.c b/qemu-timer.c index c77de64..5741f0d 100644 --- a/qemu-timer.c +++ b/qemu-timer.c @@ -22,13 +22,9 @@ * THE SOFTWARE. */ -#include "sysemu/sysemu.h" -#include "monitor/monitor.h" -#include "ui/console.h" - -#include "hw/hw.h" - +#include "qemu/main-loop.h" #include "qemu/timer.h" + #ifdef CONFIG_POSIX #include #endif @@ -331,9 +327,9 @@ int qemu_poll_ns(GPollFD *fds, guint nfds, int64_t timeout) } -void timer_init(QEMUTimer *ts, - QEMUTimerList *timer_list, int scale, - QEMUTimerCB *cb, void *opaque) +void timer_init_tl(QEMUTimer *ts, + QEMUTimerList *timer_list, int scale, + QEMUTimerCB *cb, void *opaque) { ts->timer_list = timer_list; ts->cb = cb; @@ -342,6 +338,12 @@ void timer_init(QEMUTimer *ts, ts->expire_time = -1; } +void timer_deinit(QEMUTimer *ts) +{ + assert(ts->expire_time == -1); + ts->timer_list = NULL; +} + void timer_free(QEMUTimer *ts) { g_free(ts); @@ -398,9 +400,11 @@ void timer_del(QEMUTimer *ts) { QEMUTimerList *timer_list = ts->timer_list; - qemu_mutex_lock(&timer_list->active_timers_lock); - timer_del_locked(timer_list, ts); - qemu_mutex_unlock(&timer_list->active_timers_lock); + if (timer_list) { + qemu_mutex_lock(&timer_list->active_timers_lock); + timer_del_locked(timer_list, ts); + qemu_mutex_unlock(&timer_list->active_timers_lock); + } } /* modify the current timer so that it will be fired when current_time @@ -573,6 +577,8 @@ int64_t qemu_clock_get_ns(QEMUClockType type) notifier_list_notify(&clock->reset_notifiers, &now); } return now; + case QEMU_CLOCK_VIRTUAL_RT: + return cpu_get_clock(); } } diff --git a/qga/commands-posix.c b/qga/commands-posix.c index f6f3e3c..4449628 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -376,13 +376,33 @@ safe_open_or_create(const char *path, const char *mode, Error **errp) return NULL; } +static int guest_file_toggle_flags(int fd, int flags, bool set, Error **err) +{ + int ret, old_flags; + + old_flags = fcntl(fd, F_GETFL); + if (old_flags == -1) { + error_set_errno(err, errno, QERR_QGA_COMMAND_FAILED, + "failed to fetch filehandle flags"); + return -1; + } + + ret = fcntl(fd, F_SETFL, set ? (old_flags | flags) : (old_flags & ~flags)); + if (ret == -1) { + error_set_errno(err, errno, QERR_QGA_COMMAND_FAILED, + "failed to set filehandle flags"); + return -1; + } + + return ret; +} + int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, Error **errp) { FILE *fh; Error *local_err = NULL; - int fd; - int64_t ret = -1, handle; + int64_t handle; if (!has_mode) { mode = "r"; @@ -397,12 +417,7 @@ int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, /* set fd non-blocking to avoid common use cases (like reading from a * named pipe) from hanging the agent */ - fd = fileno(fh); - ret = fcntl(fd, F_GETFL); - ret = fcntl(fd, F_SETFL, ret | O_NONBLOCK); - if (ret == -1) { - error_setg_errno(errp, errno, "failed to make file '%s' non-blocking", - path); + if (guest_file_toggle_flags(fileno(fh), O_NONBLOCK, true, errp) < 0) { fclose(fh); return -1; } @@ -1317,11 +1332,7 @@ void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp) struct FsMount *mount; int fd; Error *local_err = NULL; - struct fstrim_range r = { - .start = 0, - .len = -1, - .minlen = has_minimum ? minimum : 0, - }; + struct fstrim_range r; slog("guest-fstrim called"); @@ -1345,6 +1356,9 @@ void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp) * error means an unexpected error, so return it in those cases. In * some other cases ENOTTY will be reported (e.g. CD-ROMs). */ + r.start = 0; + r.len = -1; + r.minlen = has_minimum ? minimum : 0; ret = ioctl(fd, FITRIM, &r); if (ret == -1) { if (errno != ENOTTY && errno != EOPNOTSUPP) { @@ -1875,6 +1889,414 @@ int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) return processed; } +void qmp_guest_set_user_password(const char *username, + const char *password, + bool crypted, + Error **errp) +{ + Error *local_err = NULL; + char *passwd_path = NULL; + pid_t pid; + int status; + int datafd[2] = { -1, -1 }; + char *rawpasswddata = NULL; + size_t rawpasswdlen; + char *chpasswddata = NULL; + size_t chpasswdlen; + + rawpasswddata = (char *)g_base64_decode(password, &rawpasswdlen); + rawpasswddata = g_renew(char, rawpasswddata, rawpasswdlen + 1); + rawpasswddata[rawpasswdlen] = '\0'; + + if (strchr(rawpasswddata, '\n')) { + error_setg(errp, "forbidden characters in raw password"); + goto out; + } + + if (strchr(username, '\n') || + strchr(username, ':')) { + error_setg(errp, "forbidden characters in username"); + goto out; + } + + chpasswddata = g_strdup_printf("%s:%s\n", username, rawpasswddata); + chpasswdlen = strlen(chpasswddata); + + passwd_path = g_find_program_in_path("chpasswd"); + + if (!passwd_path) { + error_setg(errp, "cannot find 'passwd' program in PATH"); + goto out; + } + + if (pipe(datafd) < 0) { + error_setg(errp, "cannot create pipe FDs"); + goto out; + } + + pid = fork(); + if (pid == 0) { + close(datafd[1]); + /* child */ + setsid(); + dup2(datafd[0], 0); + reopen_fd_to_null(1); + reopen_fd_to_null(2); + + if (crypted) { + execle(passwd_path, "chpasswd", "-e", NULL, environ); + } else { + execle(passwd_path, "chpasswd", NULL, environ); + } + _exit(EXIT_FAILURE); + } else if (pid < 0) { + error_setg_errno(errp, errno, "failed to create child process"); + goto out; + } + close(datafd[0]); + datafd[0] = -1; + + if (qemu_write_full(datafd[1], chpasswddata, chpasswdlen) != chpasswdlen) { + error_setg_errno(errp, errno, "cannot write new account password"); + goto out; + } + close(datafd[1]); + datafd[1] = -1; + + ga_wait_child(pid, &status, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto out; + } + + if (!WIFEXITED(status)) { + error_setg(errp, "child process has terminated abnormally"); + goto out; + } + + if (WEXITSTATUS(status)) { + error_setg(errp, "child process has failed to set user password"); + goto out; + } + +out: + g_free(chpasswddata); + g_free(rawpasswddata); + g_free(passwd_path); + if (datafd[0] != -1) { + close(datafd[0]); + } + if (datafd[1] != -1) { + close(datafd[1]); + } +} + +static void ga_read_sysfs_file(int dirfd, const char *pathname, char *buf, + int size, Error **errp) +{ + int fd; + int res; + + errno = 0; + fd = openat(dirfd, pathname, O_RDONLY); + if (fd == -1) { + error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname); + return; + } + + res = pread(fd, buf, size, 0); + if (res == -1) { + error_setg_errno(errp, errno, "pread sysfs file \"%s\"", pathname); + } else if (res == 0) { + error_setg(errp, "pread sysfs file \"%s\": unexpected EOF", pathname); + } + close(fd); +} + +static void ga_write_sysfs_file(int dirfd, const char *pathname, + const char *buf, int size, Error **errp) +{ + int fd; + + errno = 0; + fd = openat(dirfd, pathname, O_WRONLY); + if (fd == -1) { + error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname); + return; + } + + if (pwrite(fd, buf, size, 0) == -1) { + error_setg_errno(errp, errno, "pwrite sysfs file \"%s\"", pathname); + } + + close(fd); +} + +/* Transfer online/offline status between @mem_blk and the guest system. + * + * On input either @errp or *@errp must be NULL. + * + * In system-to-@mem_blk direction, the following @mem_blk fields are accessed: + * - R: mem_blk->phys_index + * - W: mem_blk->online + * - W: mem_blk->can_offline + * + * In @mem_blk-to-system direction, the following @mem_blk fields are accessed: + * - R: mem_blk->phys_index + * - R: mem_blk->online + *- R: mem_blk->can_offline + * Written members remain unmodified on error. + */ +static void transfer_memory_block(GuestMemoryBlock *mem_blk, bool sys2memblk, + GuestMemoryBlockResponse *result, + Error **errp) +{ + char *dirpath; + int dirfd; + char *status; + Error *local_err = NULL; + + if (!sys2memblk) { + DIR *dp; + + if (!result) { + error_setg(errp, "Internal error, 'result' should not be NULL"); + return; + } + errno = 0; + dp = opendir("/sys/devices/system/memory/"); + /* if there is no 'memory' directory in sysfs, + * we think this VM does not support online/offline memory block, + * any other solution? + */ + if (!dp && errno == ENOENT) { + result->response = + GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED; + goto out1; + } + closedir(dp); + } + + dirpath = g_strdup_printf("/sys/devices/system/memory/memory%" PRId64 "/", + mem_blk->phys_index); + dirfd = open(dirpath, O_RDONLY | O_DIRECTORY); + if (dirfd == -1) { + if (sys2memblk) { + error_setg_errno(errp, errno, "open(\"%s\")", dirpath); + } else { + if (errno == ENOENT) { + result->response = GUEST_MEMORY_BLOCK_RESPONSE_TYPE_NOT_FOUND; + } else { + result->response = + GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED; + } + } + g_free(dirpath); + goto out1; + } + g_free(dirpath); + + status = g_malloc0(10); + ga_read_sysfs_file(dirfd, "state", status, 10, &local_err); + if (local_err) { + /* treat with sysfs file that not exist in old kernel */ + if (errno == ENOENT) { + error_free(local_err); + if (sys2memblk) { + mem_blk->online = true; + mem_blk->can_offline = false; + } else if (!mem_blk->online) { + result->response = + GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED; + } + } else { + if (sys2memblk) { + error_propagate(errp, local_err); + } else { + result->response = + GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED; + } + } + goto out2; + } + + if (sys2memblk) { + char removable = '0'; + + mem_blk->online = (strncmp(status, "online", 6) == 0); + + ga_read_sysfs_file(dirfd, "removable", &removable, 1, &local_err); + if (local_err) { + /* if no 'removable' file, it does't support offline mem blk */ + if (errno == ENOENT) { + error_free(local_err); + mem_blk->can_offline = false; + } else { + error_propagate(errp, local_err); + } + } else { + mem_blk->can_offline = (removable != '0'); + } + } else { + if (mem_blk->online != (strncmp(status, "online", 6) == 0)) { + char *new_state = mem_blk->online ? g_strdup("online") : + g_strdup("offline"); + + ga_write_sysfs_file(dirfd, "state", new_state, strlen(new_state), + &local_err); + g_free(new_state); + if (local_err) { + error_free(local_err); + result->response = + GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED; + goto out2; + } + + result->response = GUEST_MEMORY_BLOCK_RESPONSE_TYPE_SUCCESS; + result->has_error_code = false; + } /* otherwise pretend successful re-(on|off)-lining */ + } + g_free(status); + close(dirfd); + return; + +out2: + g_free(status); + close(dirfd); +out1: + if (!sys2memblk) { + result->has_error_code = true; + result->error_code = errno; + } +} + +GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp) +{ + GuestMemoryBlockList *head, **link; + Error *local_err = NULL; + struct dirent *de; + DIR *dp; + + head = NULL; + link = &head; + + dp = opendir("/sys/devices/system/memory/"); + if (!dp) { + error_setg_errno(errp, errno, "Can't open directory" + "\"/sys/devices/system/memory/\"\n"); + return NULL; + } + + /* Note: the phys_index of memory block may be discontinuous, + * this is because a memblk is the unit of the Sparse Memory design, which + * allows discontinuous memory ranges (ex. NUMA), so here we should + * traverse the memory block directory. + */ + while ((de = readdir(dp)) != NULL) { + GuestMemoryBlock *mem_blk; + GuestMemoryBlockList *entry; + + if ((strncmp(de->d_name, "memory", 6) != 0) || + !(de->d_type & DT_DIR)) { + continue; + } + + mem_blk = g_malloc0(sizeof *mem_blk); + /* The d_name is "memoryXXX", phys_index is block id, same as XXX */ + mem_blk->phys_index = strtoul(&de->d_name[6], NULL, 10); + mem_blk->has_can_offline = true; /* lolspeak ftw */ + transfer_memory_block(mem_blk, true, NULL, &local_err); + + entry = g_malloc0(sizeof *entry); + entry->value = mem_blk; + + *link = entry; + link = &entry->next; + } + + closedir(dp); + if (local_err == NULL) { + /* there's no guest with zero memory blocks */ + if (head == NULL) { + error_setg(errp, "guest reported zero memory blocks!"); + } + return head; + } + + qapi_free_GuestMemoryBlockList(head); + error_propagate(errp, local_err); + return NULL; +} + +GuestMemoryBlockResponseList * +qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp) +{ + GuestMemoryBlockResponseList *head, **link; + Error *local_err = NULL; + + head = NULL; + link = &head; + + while (mem_blks != NULL) { + GuestMemoryBlockResponse *result; + GuestMemoryBlockResponseList *entry; + GuestMemoryBlock *current_mem_blk = mem_blks->value; + + result = g_malloc0(sizeof(*result)); + result->phys_index = current_mem_blk->phys_index; + transfer_memory_block(current_mem_blk, false, result, &local_err); + if (local_err) { /* should never happen */ + goto err; + } + entry = g_malloc0(sizeof *entry); + entry->value = result; + + *link = entry; + link = &entry->next; + mem_blks = mem_blks->next; + } + + return head; +err: + qapi_free_GuestMemoryBlockResponseList(head); + error_propagate(errp, local_err); + return NULL; +} + +GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp) +{ + Error *local_err = NULL; + char *dirpath; + int dirfd; + char *buf; + GuestMemoryBlockInfo *info; + + dirpath = g_strdup_printf("/sys/devices/system/memory/"); + dirfd = open(dirpath, O_RDONLY | O_DIRECTORY); + if (dirfd == -1) { + error_setg_errno(errp, errno, "open(\"%s\")", dirpath); + g_free(dirpath); + return NULL; + } + g_free(dirpath); + + buf = g_malloc0(20); + ga_read_sysfs_file(dirfd, "block_size_bytes", buf, 20, &local_err); + close(dirfd); + if (local_err) { + g_free(buf); + error_propagate(errp, local_err); + return NULL; + } + + info = g_new0(GuestMemoryBlockInfo, 1); + info->size = strtol(buf, NULL, 16); /* the unit is bytes */ + + g_free(buf); + + return info; +} + #else /* defined(__linux__) */ void qmp_guest_suspend_disk(Error **errp) @@ -1910,6 +2332,33 @@ int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) return -1; } +void qmp_guest_set_user_password(const char *username, + const char *password, + bool crypted, + Error **errp) +{ + error_set(errp, QERR_UNSUPPORTED); +} + +GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp) +{ + error_set(errp, QERR_UNSUPPORTED); + return NULL; +} + +GuestMemoryBlockResponseList * +qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp) +{ + error_set(errp, QERR_UNSUPPORTED); + return NULL; +} + +GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp) +{ + error_set(errp, QERR_UNSUPPORTED); + return NULL; +} + #endif #if !defined(CONFIG_FSFREEZE) @@ -1966,7 +2415,9 @@ GList *ga_command_blacklist_init(GList *blacklist) const char *list[] = { "guest-suspend-disk", "guest-suspend-ram", "guest-suspend-hybrid", "guest-network-get-interfaces", - "guest-get-vcpus", "guest-set-vcpus", NULL}; + "guest-get-vcpus", "guest-set-vcpus", + "guest-get-memory-blocks", "guest-set-memory-blocks", + "guest-get-memory-block-size", NULL}; char **p = (char **)list; while (*p) { diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 3bcbeae..3ef0549 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -14,10 +14,13 @@ #include #include #include +#include +#include #include "qga/guest-agent-core.h" #include "qga/vss-win32.h" #include "qga-qmp-commands.h" #include "qapi/qmp/qerror.h" +#include "qemu/queue.h" #ifndef SHTDN_REASON_FLAG_PLANNED #define SHTDN_REASON_FLAG_PLANNED 0x80000000 @@ -29,6 +32,146 @@ (365 * (1970 - 1601) + \ (1970 - 1601) / 4 - 3)) +#define INVALID_SET_FILE_POINTER ((DWORD)-1) + +typedef struct GuestFileHandle { + int64_t id; + HANDLE fh; + QTAILQ_ENTRY(GuestFileHandle) next; +} GuestFileHandle; + +static struct { + QTAILQ_HEAD(, GuestFileHandle) filehandles; +} guest_file_state; + + +typedef struct OpenFlags { + const char *forms; + DWORD desired_access; + DWORD creation_disposition; +} OpenFlags; +static OpenFlags guest_file_open_modes[] = { + {"r", GENERIC_READ, OPEN_EXISTING}, + {"rb", GENERIC_READ, OPEN_EXISTING}, + {"w", GENERIC_WRITE, CREATE_ALWAYS}, + {"wb", GENERIC_WRITE, CREATE_ALWAYS}, + {"a", GENERIC_WRITE, OPEN_ALWAYS }, + {"r+", GENERIC_WRITE|GENERIC_READ, OPEN_EXISTING}, + {"rb+", GENERIC_WRITE|GENERIC_READ, OPEN_EXISTING}, + {"r+b", GENERIC_WRITE|GENERIC_READ, OPEN_EXISTING}, + {"w+", GENERIC_WRITE|GENERIC_READ, CREATE_ALWAYS}, + {"wb+", GENERIC_WRITE|GENERIC_READ, CREATE_ALWAYS}, + {"w+b", GENERIC_WRITE|GENERIC_READ, CREATE_ALWAYS}, + {"a+", GENERIC_WRITE|GENERIC_READ, OPEN_ALWAYS }, + {"ab+", GENERIC_WRITE|GENERIC_READ, OPEN_ALWAYS }, + {"a+b", GENERIC_WRITE|GENERIC_READ, OPEN_ALWAYS } +}; + +static OpenFlags *find_open_flag(const char *mode_str) +{ + int mode; + Error **errp = NULL; + + for (mode = 0; mode < ARRAY_SIZE(guest_file_open_modes); ++mode) { + OpenFlags *flags = guest_file_open_modes + mode; + + if (strcmp(flags->forms, mode_str) == 0) { + return flags; + } + } + + error_setg(errp, "invalid file open mode '%s'", mode_str); + return NULL; +} + +static int64_t guest_file_handle_add(HANDLE fh, Error **errp) +{ + GuestFileHandle *gfh; + int64_t handle; + + handle = ga_get_fd_handle(ga_state, errp); + if (handle < 0) { + return -1; + } + gfh = g_malloc0(sizeof(GuestFileHandle)); + gfh->id = handle; + gfh->fh = fh; + QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next); + + return handle; +} + +static GuestFileHandle *guest_file_handle_find(int64_t id, Error **errp) +{ + GuestFileHandle *gfh; + QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next) { + if (gfh->id == id) { + return gfh; + } + } + error_setg(errp, "handle '%" PRId64 "' has not been found", id); + return NULL; +} + +int64_t qmp_guest_file_open(const char *path, bool has_mode, + const char *mode, Error **errp) +{ + int64_t fd; + HANDLE fh; + HANDLE templ_file = NULL; + DWORD share_mode = FILE_SHARE_READ; + DWORD flags_and_attr = FILE_ATTRIBUTE_NORMAL; + LPSECURITY_ATTRIBUTES sa_attr = NULL; + OpenFlags *guest_flags; + + if (!has_mode) { + mode = "r"; + } + slog("guest-file-open called, filepath: %s, mode: %s", path, mode); + guest_flags = find_open_flag(mode); + if (guest_flags == NULL) { + error_setg(errp, "invalid file open mode"); + return -1; + } + + fh = CreateFile(path, guest_flags->desired_access, share_mode, sa_attr, + guest_flags->creation_disposition, flags_and_attr, + templ_file); + if (fh == INVALID_HANDLE_VALUE) { + error_setg_win32(errp, GetLastError(), "failed to open file '%s'", + path); + return -1; + } + + fd = guest_file_handle_add(fh, errp); + if (fd < 0) { + CloseHandle(&fh); + error_setg(errp, "failed to add handle to qmp handle table"); + return -1; + } + + slog("guest-file-open, handle: % " PRId64, fd); + return fd; +} + +void qmp_guest_file_close(int64_t handle, Error **errp) +{ + bool ret; + GuestFileHandle *gfh = guest_file_handle_find(handle, errp); + slog("guest-file-close called, handle: %" PRId64, handle); + if (gfh == NULL) { + return; + } + ret = CloseHandle(gfh->fh); + if (!ret) { + error_setg_win32(errp, GetLastError(), "failed close handle"); + return; + } + + QTAILQ_REMOVE(&guest_file_state.filehandles, gfh, next); + g_free(gfh); +} + static void acquire_privilege(const char *name, Error **errp) { HANDLE token = NULL; @@ -113,43 +256,130 @@ void qmp_guest_shutdown(bool has_mode, const char *mode, Error **errp) } } -int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, - Error **errp) -{ - error_set(errp, QERR_UNSUPPORTED); - return 0; -} - -void qmp_guest_file_close(int64_t handle, Error **errp) -{ - error_set(errp, QERR_UNSUPPORTED); -} - GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count, int64_t count, Error **errp) { - error_set(errp, QERR_UNSUPPORTED); - return 0; + GuestFileRead *read_data = NULL; + guchar *buf; + HANDLE fh; + bool is_ok; + DWORD read_count; + GuestFileHandle *gfh = guest_file_handle_find(handle, errp); + + if (!gfh) { + return NULL; + } + if (!has_count) { + count = QGA_READ_COUNT_DEFAULT; + } else if (count < 0) { + error_setg(errp, "value '%" PRId64 + "' is invalid for argument count", count); + return NULL; + } + + fh = gfh->fh; + buf = g_malloc0(count+1); + is_ok = ReadFile(fh, buf, count, &read_count, NULL); + if (!is_ok) { + error_setg_win32(errp, GetLastError(), "failed to read file"); + slog("guest-file-read failed, handle %" PRId64, handle); + } else { + buf[read_count] = 0; + read_data = g_malloc0(sizeof(GuestFileRead)); + read_data->count = (size_t)read_count; + read_data->eof = read_count == 0; + + if (read_count != 0) { + read_data->buf_b64 = g_base64_encode(buf, read_count); + } + } + g_free(buf); + + return read_data; } GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64, bool has_count, int64_t count, Error **errp) { - error_set(errp, QERR_UNSUPPORTED); - return 0; + GuestFileWrite *write_data = NULL; + guchar *buf; + gsize buf_len; + bool is_ok; + DWORD write_count; + GuestFileHandle *gfh = guest_file_handle_find(handle, errp); + HANDLE fh; + + if (!gfh) { + return NULL; + } + fh = gfh->fh; + buf = g_base64_decode(buf_b64, &buf_len); + + if (!has_count) { + count = buf_len; + } else if (count < 0 || count > buf_len) { + error_setg(errp, "value '%" PRId64 + "' is invalid for argument count", count); + goto done; + } + + is_ok = WriteFile(fh, buf, count, &write_count, NULL); + if (!is_ok) { + error_setg_win32(errp, GetLastError(), "failed to write to file"); + slog("guest-file-write-failed, handle: %" PRId64, handle); + } else { + write_data = g_malloc0(sizeof(GuestFileWrite)); + write_data->count = (size_t) write_count; + } + +done: + g_free(buf); + return write_data; } GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset, int64_t whence, Error **errp) { - error_set(errp, QERR_UNSUPPORTED); - return 0; + GuestFileHandle *gfh; + GuestFileSeek *seek_data; + HANDLE fh; + LARGE_INTEGER new_pos, off_pos; + off_pos.QuadPart = offset; + BOOL res; + gfh = guest_file_handle_find(handle, errp); + if (!gfh) { + return NULL; + } + + fh = gfh->fh; + res = SetFilePointerEx(fh, off_pos, &new_pos, whence); + if (!res) { + error_setg_win32(errp, GetLastError(), "failed to seek file"); + return NULL; + } + seek_data = g_new0(GuestFileSeek, 1); + seek_data->position = new_pos.QuadPart; + return seek_data; } void qmp_guest_file_flush(int64_t handle, Error **errp) { - error_set(errp, QERR_UNSUPPORTED); + HANDLE fh; + GuestFileHandle *gfh = guest_file_handle_find(handle, errp); + if (!gfh) { + return; + } + + fh = gfh->fh; + if (!FlushFileBuffers(fh)) { + error_setg_win32(errp, GetLastError(), "failed to flush file"); + } +} + +static void guest_file_init(void) +{ + QTAILQ_INIT(&guest_file_state.filehandles); } GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp) @@ -395,31 +625,31 @@ void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp) FILETIME tf; LONGLONG time; - if (has_time) { - /* Okay, user passed a time to set. Validate it. */ - if (time_ns < 0 || time_ns / 100 > INT64_MAX - W32_FT_OFFSET) { - error_setg(errp, "Time %" PRId64 "is invalid", time_ns); - return; - } + if (!has_time) { + /* Unfortunately, Windows libraries don't provide an easy way to access + * RTC yet: + * + * https://msdn.microsoft.com/en-us/library/aa908981.aspx + */ + error_setg(errp, "Time argument is required on this platform"); + return; + } - time = time_ns / 100 + W32_FT_OFFSET; + /* Validate time passed by user. */ + if (time_ns < 0 || time_ns / 100 > INT64_MAX - W32_FT_OFFSET) { + error_setg(errp, "Time %" PRId64 "is invalid", time_ns); + return; + } - tf.dwLowDateTime = (DWORD) time; - tf.dwHighDateTime = (DWORD) (time >> 32); + time = time_ns / 100 + W32_FT_OFFSET; - if (!FileTimeToSystemTime(&tf, &ts)) { - error_setg(errp, "Failed to convert system time %d", - (int)GetLastError()); - return; - } - } else { - /* Otherwise read the time from RTC which contains the correct value. - * Hopefully. */ - GetSystemTime(&ts); - if (ts.wYear < 1601 || ts.wYear > 30827) { - error_setg(errp, "Failed to get time"); - return; - } + tf.dwLowDateTime = (DWORD) time; + tf.dwHighDateTime = (DWORD) (time >> 32); + + if (!FileTimeToSystemTime(&tf, &ts)) { + error_setg(errp, "Failed to convert system time %d", + (int)GetLastError()); + return; } acquire_privilege(SE_SYSTEMTIME_NAME, &local_err); @@ -446,14 +676,42 @@ int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) return -1; } +void qmp_guest_set_user_password(const char *username, + const char *password, + bool crypted, + Error **errp) +{ + error_set(errp, QERR_UNSUPPORTED); +} + +GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp) +{ + error_set(errp, QERR_UNSUPPORTED); + return NULL; +} + +GuestMemoryBlockResponseList * +qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp) +{ + error_set(errp, QERR_UNSUPPORTED); + return NULL; +} + +GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp) +{ + error_set(errp, QERR_UNSUPPORTED); + return NULL; +} + /* add unsupported commands to the blacklist */ GList *ga_command_blacklist_init(GList *blacklist) { const char *list_unsupported[] = { - "guest-file-open", "guest-file-close", "guest-file-read", - "guest-file-write", "guest-file-seek", "guest-file-flush", "guest-suspend-hybrid", "guest-network-get-interfaces", "guest-get-vcpus", "guest-set-vcpus", + "guest-set-user-password", + "guest-get-memory-blocks", "guest-set-memory-blocks", + "guest-get-memory-block-size", "guest-fsfreeze-freeze-list", "guest-get-fsinfo", "guest-fstrim", NULL}; char **p = (char **)list_unsupported; @@ -482,4 +740,5 @@ void ga_command_state_init(GAState *s, GACommandState *cs) if (!vss_initialized()) { ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup); } + ga_command_state_add(cs, guest_file_init, NULL); } diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 376e79f..95f49e3 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -121,7 +121,10 @@ # given value, then sets the Hardware Clock (RTC) to the # current System Time. This will make it easier for a guest # to resynchronize without waiting for NTP. If no @time is -# specified, then the time to set is read from RTC. +# specified, then the time to set is read from RTC. However, +# this may not be supported on all platforms (i.e. Windows). +# If that's the case users are advised to always pass a +# value. # # @time: #optional time of nanoseconds, relative to the Epoch # of 1970-01-01 in UTC. @@ -738,3 +741,153 @@ ## { 'command': 'guest-get-fsinfo', 'returns': ['GuestFilesystemInfo'] } + +## +# @guest-set-user-password +# +# @username: the user account whose password to change +# @password: the new password entry string, base64 encoded +# @crypted: true if password is already crypt()d, false if raw +# +# If the @crypted flag is true, it is the caller's responsibility +# to ensure the correct crypt() encryption scheme is used. This +# command does not attempt to interpret or report on the encryption +# scheme. Refer to the documentation of the guest operating system +# in question to determine what is supported. +# +# Note all guest operating systems will support use of the +# @crypted flag, as they may require the clear-text password +# +# The @password parameter must always be base64 encoded before +# transmission, even if already crypt()d, to ensure it is 8-bit +# safe when passed as JSON. +# +# Returns: Nothing on success. +# +# Since 2.3 +## +{ 'command': 'guest-set-user-password', + 'data': { 'username': 'str', 'password': 'str', 'crypted': 'bool' } } + +# @GuestMemoryBlock: +# +# @phys-index: Arbitrary guest-specific unique identifier of the MEMORY BLOCK. +# +# @online: Whether the MEMORY BLOCK is enabled in guest. +# +# @can-offline: #optional Whether offlining the MEMORY BLOCK is possible. +# This member is always filled in by the guest agent when the +# structure is returned, and always ignored on input (hence it +# can be omitted then). +# +# Since: 2.3 +## +{ 'type': 'GuestMemoryBlock', + 'data': {'phys-index': 'uint64', + 'online': 'bool', + '*can-offline': 'bool'} } + +## +# @guest-get-memory-blocks: +# +# Retrieve the list of the guest's memory blocks. +# +# This is a read-only operation. +# +# Returns: The list of all memory blocks the guest knows about. +# Each memory block is put on the list exactly once, but their order +# is unspecified. +# +# Since: 2.3 +## +{ 'command': 'guest-get-memory-blocks', + 'returns': ['GuestMemoryBlock'] } + +## +# @GuestMemoryBlockResponseType +# +# An enumeration of memory block operation result. +# +# @sucess: the operation of online/offline memory block is successful. +# @not-found: can't find the corresponding memoryXXX directory in sysfs. +# @operation-not-supported: for some old kernels, it does not support +# online or offline memory block. +# @operation-failed: the operation of online/offline memory block fails, +# because of some errors happen. +# +# Since: 2.3 +## +{ 'enum': 'GuestMemoryBlockResponseType', + 'data': ['success', 'not-found', 'operation-not-supported', + 'operation-failed'] } + +## +# @GuestMemoryBlockResponse: +# +# @phys-index: same with the 'phys-index' member of @GuestMemoryBlock. +# +# @response: the result of memory block operation. +# +# @error-code: #optional the error number. +# When memory block operation fails, we assign the value of +# 'errno' to this member, it indicates what goes wrong. +# When the operation succeeds, it will be omitted. +# +# Since: 2.3 +## +{ 'type': 'GuestMemoryBlockResponse', + 'data': { 'phys-index': 'uint64', + 'response': 'GuestMemoryBlockResponseType', + '*error-code': 'int' }} + +## +# @guest-set-memory-blocks: +# +# Attempt to reconfigure (currently: enable/disable) state of memory blocks +# inside the guest. +# +# The input list is processed node by node in order. In each node @phys-index +# is used to look up the guest MEMORY BLOCK, for which @online specifies the +# requested state. The set of distinct @phys-index's is only required to be a +# subset of the guest-supported identifiers. There's no restriction on list +# length or on repeating the same @phys-index (with possibly different @online +# field). +# Preferably the input list should describe a modified subset of +# @guest-get-memory-blocks' return value. +# +# Returns: The operation results, it is a list of @GuestMemoryBlockResponse, +# which is corresponding to the input list. +# +# Note: it will return NULL if the @mem-blks list was empty on input, +# or there is an error, and in this case, guest state will not be +# changed. +# +# Since: 2.3 +## +{ 'command': 'guest-set-memory-blocks', + 'data': {'mem-blks': ['GuestMemoryBlock'] }, + 'returns': ['GuestMemoryBlockResponse'] } + +# @GuestMemoryBlockInfo: +# +# @size: the size (in bytes) of the guest memory blocks, +# which are the minimal units of memory block online/offline +# operations (also called Logical Memory Hotplug). +# +# Since: 2.3 +## +{ 'type': 'GuestMemoryBlockInfo', + 'data': {'size': 'uint64'} } + +## +# @guest-get-memory-block-info: +# +# Get information relating to guest memory blocks. +# +# Returns: memory block size in bytes. +# Returns: @GuestMemoryBlockInfo +# +# Since 2.3 +## +{ 'command': 'guest-get-memory-block-info', + 'returns': 'GuestMemoryBlockInfo' } diff --git a/qga/vss-win32/Makefile.objs b/qga/vss-win32/Makefile.objs index 6a69d50..7c96c6b 100644 --- a/qga/vss-win32/Makefile.objs +++ b/qga/vss-win32/Makefile.objs @@ -3,7 +3,7 @@ qga-vss-dll-obj-y += requester.o provider.o install.o obj-qga-vss-dll-obj-y = $(addprefix $(obj)/, $(qga-vss-dll-obj-y)) -$(obj-qga-vss-dll-obj-y): QEMU_CXXFLAGS = $(filter-out -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Wold-style-declaration -Wold-style-definition -Wredundant-decls -fstack-protector-all, $(QEMU_CFLAGS)) -Wno-unknown-pragmas -Wno-delete-non-virtual-dtor +$(obj-qga-vss-dll-obj-y): QEMU_CXXFLAGS = $(filter-out -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Wold-style-declaration -Wold-style-definition -Wredundant-decls -fstack-protector-all -fstack-protector-strong, $(QEMU_CFLAGS)) -Wno-unknown-pragmas -Wno-delete-non-virtual-dtor $(obj)/qga-vss.dll: LDFLAGS = -shared -Wl,--add-stdcall-alias,--enable-stdcall-fixup -lole32 -loleaut32 -lshlwapi -luuid -static $(obj)/qga-vss.dll: $(obj-qga-vss-dll-obj-y) $(SRC_PATH)/$(obj)/qga-vss.def diff --git a/qjson.c b/qjson.c new file mode 100644 index 0000000..0cda269 --- /dev/null +++ b/qjson.c @@ -0,0 +1,129 @@ +/* + * QEMU JSON writer + * + * Copyright Alexander Graf + * + * Authors: + * Alexander Graf + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include +#include +#include +#include +#include +#include + +struct QJSON { + Object obj; + QString *str; + bool omit_comma; +}; + +static void json_emit_element(QJSON *json, const char *name) +{ + /* Check whether we need to print a , before an element */ + if (json->omit_comma) { + json->omit_comma = false; + } else { + qstring_append(json->str, ", "); + } + + if (name) { + qstring_append(json->str, "\""); + qstring_append(json->str, name); + qstring_append(json->str, "\" : "); + } +} + +void json_start_object(QJSON *json, const char *name) +{ + json_emit_element(json, name); + qstring_append(json->str, "{ "); + json->omit_comma = true; +} + +void json_end_object(QJSON *json) +{ + qstring_append(json->str, " }"); + json->omit_comma = false; +} + +void json_start_array(QJSON *json, const char *name) +{ + json_emit_element(json, name); + qstring_append(json->str, "[ "); + json->omit_comma = true; +} + +void json_end_array(QJSON *json) +{ + qstring_append(json->str, " ]"); + json->omit_comma = false; +} + +void json_prop_int(QJSON *json, const char *name, int64_t val) +{ + json_emit_element(json, name); + qstring_append_int(json->str, val); +} + +void json_prop_str(QJSON *json, const char *name, const char *str) +{ + json_emit_element(json, name); + qstring_append_chr(json->str, '"'); + qstring_append(json->str, str); + qstring_append_chr(json->str, '"'); +} + +const char *qjson_get_str(QJSON *json) +{ + return qstring_get_str(json->str); +} + +QJSON *qjson_new(void) +{ + QJSON *json = (QJSON *)object_new(TYPE_QJSON); + return json; +} + +void qjson_finish(QJSON *json) +{ + json_end_object(json); +} + +static void qjson_initfn(Object *obj) +{ + QJSON *json = (QJSON *)object_dynamic_cast(obj, TYPE_QJSON); + assert(json); + + json->str = qstring_from_str("{ "); + json->omit_comma = true; +} + +static void qjson_finalizefn(Object *obj) +{ + QJSON *json = (QJSON *)object_dynamic_cast(obj, TYPE_QJSON); + + assert(json); + qobject_decref(QOBJECT(json->str)); +} + +static const TypeInfo qjson_type_info = { + .name = TYPE_QJSON, + .parent = TYPE_OBJECT, + .instance_size = sizeof(QJSON), + .instance_init = qjson_initfn, + .instance_finalize = qjson_finalizefn, +}; + +static void qjson_register_types(void) +{ + type_register_static(&qjson_type_info); +} + +type_init(qjson_register_types) diff --git a/qmp-commands.hx b/qmp-commands.hx index 718dd92..e58dba8 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -1,5 +1,5 @@ HXCOMM QMP dispatch table and documentation -HXCOMM Text between SQMP and EQMP is copied to the QMP documention file and +HXCOMM Text between SQMP and EQMP is copied to the QMP documentation file and HXCOMM does not show up in the other formats. SQMP @@ -276,7 +276,6 @@ EQMP .args_type = "device:O", .params = "driver[,prop=value][,...]", .help = "add device, like -device on the command line", - .user_print = monitor_user_noop, .mhandler.cmd_new = do_device_add, }, @@ -661,7 +660,36 @@ Example: <- { "return": {} } EQMP -{ + + { + .name = "migrate-incoming", + .args_type = "uri:s", + .mhandler.cmd_new = qmp_marshal_input_migrate_incoming, + }, + +SQMP +migrate-incoming +---------------- + +Continue an incoming migration + +Arguments: + +- "uri": Source/listening URI (json-string) + +Example: + +-> { "execute": "migrate-incoming", "arguments": { "uri": "tcp::4446" } } +<- { "return": {} } + +Notes: + +(1) QEMU must be started with -incoming defer to allow migrate-incoming to + be used +(2) The uri format is the same as to -incoming + +EQMP + { .name = "migrate-set-cache-size", .args_type = "value:o", .mhandler.cmd_new = qmp_marshal_input_migrate_set_cache_size, @@ -757,7 +785,6 @@ EQMP .args_type = "protocol:s,hostname:s,port:i?,tls-port:i?,cert-subject:s?", .params = "protocol hostname port tls-port cert-subject", .help = "send migration info to spice/vnc client", - .user_print = monitor_user_noop, .mhandler.cmd_async = client_migrate_info, .flags = MONITOR_CMD_ASYNC, }, @@ -793,7 +820,6 @@ EQMP .args_type = "paging:b,protocol:s,begin:i?,end:i?,format:s?", .params = "-p protocol [begin] [length] [format]", .help = "dump guest memory to file", - .user_print = monitor_user_noop, .mhandler.cmd_new = qmp_marshal_input_dump_guest_memory, }, @@ -1094,6 +1120,48 @@ Example: "sync": "full", "target": "backup.img" } } <- { "return": {} } + +EQMP + + { + .name = "blockdev-backup", + .args_type = "sync:s,device:B,target:B,speed:i?," + "on-source-error:s?,on-target-error:s?", + .mhandler.cmd_new = qmp_marshal_input_blockdev_backup, + }, + +SQMP +blockdev-backup +--------------- + +The device version of drive-backup: this command takes an existing named device +as backup target. + +Arguments: + +- "device": the name of the device which should be copied. + (json-string) +- "target": the name of the backup target device. (json-string) +- "sync": what parts of the disk image should be copied to the destination; + possibilities include "full" for all the disk, "top" for only the + sectors allocated in the topmost image, or "none" to only replicate + new I/O (MirrorSyncMode). +- "speed": the maximum speed, in bytes per second (json-int, optional) +- "on-source-error": the action to take on an error on the source, default + 'report'. 'stop' and 'enospc' can only be used + if the block device supports io-status. + (BlockdevOnError, optional) +- "on-target-error": the action to take on an error on the target, default + 'report' (no limitations, since this applies to + a different block device than device). + (BlockdevOnError, optional) + +Example: +-> { "execute": "blockdev-backup", "arguments": { "device": "src-id", + "sync": "full", + "target": "tgt-id" } } +<- { "return": {} } + EQMP { @@ -1312,6 +1380,7 @@ EQMP .args_type = "sync:s,device:B,target:s,speed:i?,mode:s?,format:s?," "node-name:s?,replaces:s?," "on-source-error:s?,on-target-error:s?," + "unmap:b?," "granularity:i?,buf-size:i?", .mhandler.cmd_new = qmp_marshal_input_drive_mirror, }, @@ -1351,6 +1420,8 @@ Arguments: (BlockdevOnError, default 'report') - "on-target-error": the action to take on an error on the target (BlockdevOnError, default 'report') +- "unmap": whether the target sectors should be discarded where source has only + zeroes. (json-bool, optional, default true) The default value of the granularity is the image cluster size clamped between 4096 and 65536, if the image format defines one. If the format @@ -1725,7 +1796,7 @@ Arguments: - "protocol": protocol name (json-string) - "password": password (json-string) -- "connected": [ keep | disconnect | fail ] (josn-string, optional) +- "connected": [ keep | disconnect | fail ] (json-string, optional) Example: @@ -1791,7 +1862,6 @@ EQMP .args_type = "", .params = "", .help = "enable QMP capabilities", - .user_print = monitor_user_noop, .mhandler.cmd_new = do_qmp_capabilities, }, @@ -2083,7 +2153,7 @@ Each json-object contain the following: - "drv": driver format name (json-string) - Possible values: "blkdebug", "bochs", "cloop", "dmg", "file", "file", "ftp", "ftps", "host_cdrom", - "host_device", "host_floppy", "http", "https", + "host_device", "http", "https", "nbd", "parallels", "qcow", "qcow2", "raw", "tftp", "vdi", "vmdk", "vpc", "vvfat" - "backing_file": backing file name (json-string, optional) @@ -2104,6 +2174,8 @@ Each json-object contain the following: - "iops_size": I/O size when limiting by iops (json-int) - "detect_zeroes": detect and optimize zero writing (json-string) - Possible values: "off", "on", "unmap" + - "write_threshold": write offset threshold in bytes, a event will be + emitted if crossed. Zero if disabled (json-int) - "image": the detail of the image, it is a json-object containing the following: - "filename": image file name (json-string) @@ -2181,6 +2253,7 @@ Example: "iops_wr_max": 0, "iops_size": 0, "detect_zeroes": "on", + "write_threshold": 0, "image":{ "filename":"disks/test.qcow2", "format":"qcow2", @@ -2261,6 +2334,10 @@ Each json-object contain the following: - "flush_total_time_ns": total time spend on cache flushes in nano-seconds (json-int) - "wr_highest_offset": Highest offset of a sector written since the BlockDriverState has been opened (json-int) + - "rd_merged": number of read requests that have been merged into + another request (json-int) + - "wr_merged": number of write requests that have been merged into + another request (json-int) - "parent": Contains recursively the statistics of the underlying protocol (e.g. the host file for a qcow2 image). If there is no underlying protocol, this field is omitted @@ -2284,6 +2361,8 @@ Example: "rd_total_times_ns":3465673657 "flush_total_times_ns":49653 "flush_operations":61, + "rd_merged":0, + "wr_merged":0 } }, "stats":{ @@ -2295,7 +2374,9 @@ Example: "flush_operations":51, "wr_total_times_ns":313253456 "rd_total_times_ns":3465673657 - "flush_total_times_ns":49653 + "flush_total_times_ns":49653, + "rd_merged":0, + "wr_merged":0 } }, { @@ -2309,7 +2390,9 @@ Example: "flush_operations":0, "wr_total_times_ns":0 "rd_total_times_ns":0 - "flush_total_times_ns":0 + "flush_total_times_ns":0, + "rd_merged":0, + "wr_merged":0 } }, { @@ -2323,7 +2406,9 @@ Example: "flush_operations":0, "wr_total_times_ns":0 "rd_total_times_ns":0 - "flush_total_times_ns":0 + "flush_total_times_ns":0, + "rd_merged":0, + "wr_merged":0 } }, { @@ -2337,7 +2422,9 @@ Example: "flush_operations":0, "wr_total_times_ns":0 "rd_total_times_ns":0 - "flush_total_times_ns":0 + "flush_total_times_ns":0, + "rd_merged":0, + "wr_merged":0 } } ] @@ -2347,7 +2434,7 @@ EQMP { .name = "query-blockstats", - .args_type = "", + .args_type = "query-nodes:b?", .mhandler.cmd_new = qmp_marshal_input_query_blockstats, }, @@ -2825,6 +2912,11 @@ EQMP .args_type = "", .mhandler.cmd_new = qmp_marshal_input_query_vnc, }, + { + .name = "query-vnc-servers", + .args_type = "", + .mhandler.cmd_new = qmp_marshal_input_query_vnc_servers, + }, SQMP query-spice @@ -2858,7 +2950,7 @@ Channels are described by a json-object, each one contain the following: - "channel-id": channel id. Usually "0", might be different needed when multiple channels of the same type exist, such as multiple display channels in a multihead setup (json-int) -- "tls": whevener the channel is encrypted (json-bool) +- "tls": whether the channel is encrypted (json-bool) Example: @@ -3184,6 +3276,9 @@ migrate-set-capabilities Enable/Disable migration capabilities - "xbzrle": XBZRLE support +- "rdma-pin-all": pin all pages when using RDMA during migration +- "auto-converge": throttle down guest to help convergence of migration +- "zero-blocks": compress zero blocks during block migration Arguments: @@ -3208,6 +3303,9 @@ Query current migration capabilities - "capabilities": migration capabilities state - "xbzrle" : XBZRLE state (json-bool) + - "rdma-pin-all" : RDMA Pin Page state (json-bool) + - "auto-converge" : Auto Converge state (json-bool) + - "zero-blocks" : Zero Blocks state (json-bool) Arguments: @@ -3541,6 +3639,10 @@ blockdev-add Add a block device. +This command is still a work in progress. It doesn't support all +block drivers, it lacks a matching blockdev-del, and more. Stay away +from it unless you want to help with its development. + Arguments: - "options": block driver options @@ -3618,6 +3720,7 @@ Example: "iops_rd_max": 0, "iops_wr_max": 0, "iops_size": 0, + "write_threshold": 0, "image":{ "filename":"disks/test.qcow2", "format":"qcow2", @@ -3820,13 +3923,13 @@ Press left mouse button. -> { "execute": "x-input-send-event", "arguments": { "console": 0, "events": [ { "type": "btn", - "data" : { "down": true, "button": "Left" } } } } + "data" : { "down": true, "button": "Left" } } ] } } <- { "return": {} } -> { "execute": "x-input-send-event", "arguments": { "console": 0, "events": [ { "type": "btn", - "data" : { "down": false, "button": "Left" } } } } + "data" : { "down": false, "button": "Left" } } ] } } <- { "return": {} } Example (2): @@ -3854,3 +3957,31 @@ Move mouse pointer to absolute coordinates (20000, 400). <- { "return": {} } EQMP + + { + .name = "block-set-write-threshold", + .args_type = "node-name:s,write-threshold:l", + .mhandler.cmd_new = qmp_marshal_input_block_set_write_threshold, + }, + +SQMP +block-set-write-threshold +------------ + +Change the write threshold for a block drive. The threshold is an offset, +thus must be non-negative. Default is no write threshold. +Setting the threshold to zero disables it. + +Arguments: + +- "node-name": the node name in the block driver state graph (json-string) +- "write-threshold": the write threshold in bytes (json-int) + +Example: + +-> { "execute": "block-set-write-threshold", + "arguments": { "node-name": "mydev", + "write-threshold": 17179869184 } } +<- { "return": {} } + +EQMP diff --git a/qmp.c b/qmp.c index 0b4f131..e6c7050 100644 --- a/qmp.c +++ b/qmp.c @@ -134,22 +134,33 @@ VncInfo *qmp_query_vnc(Error **errp) error_set(errp, QERR_FEATURE_DISABLED, "vnc"); return NULL; }; + +VncInfo2List *qmp_query_vnc_servers(Error **errp) +{ + error_set(errp, QERR_FEATURE_DISABLED, "vnc"); + return NULL; +}; #endif #ifndef CONFIG_SPICE -/* If SPICE support is enabled, the "true" query-spice command is - defined in the SPICE subsystem. Also note that we use a small - trick to maintain query-spice's original behavior, which is not - to be available in the namespace if SPICE is not compiled in */ +/* + * qmp-commands.hx ensures that QMP command query-spice exists only + * #ifdef CONFIG_SPICE. Necessary for an accurate query-commands + * result. However, the QAPI schema is blissfully unaware of that, + * and the QAPI code generator happily generates a dead + * qmp_marshal_input_query_spice() that calls qmp_query_spice(). + * Provide it one, or else linking fails. + * FIXME Educate the QAPI schema on CONFIG_SPICE. + */ SpiceInfo *qmp_query_spice(Error **errp) { - error_set(errp, QERR_COMMAND_NOT_FOUND, "query-spice"); - return NULL; + abort(); }; #endif void qmp_cont(Error **errp) { + Error *local_err = NULL; BlockDriverState *bs; if (runstate_needs_reset()) { @@ -163,10 +174,9 @@ void qmp_cont(Error **errp) bdrv_iostatus_reset(bs); } for (bs = bdrv_next(NULL); bs; bs = bdrv_next(bs)) { - if (bdrv_key_required(bs)) { - error_set(errp, QERR_DEVICE_ENCRYPTED, - bdrv_get_device_name(bs), - bdrv_get_encrypted_filename(bs)); + bdrv_add_key(bs, NULL, &local_err); + if (local_err) { + error_propagate(errp, local_err); return; } } @@ -287,9 +297,7 @@ void qmp_set_password(const char *protocol, const char *password, } if (strcmp(protocol, "spice") == 0) { - if (!using_spice) { - /* correct one? spice isn't a device ,,, */ - error_set(errp, QERR_DEVICE_NOT_ACTIVE, "spice"); + if (!qemu_using_spice(errp)) { return; } rc = qemu_spice_set_passwd(password, fail_if_connected, @@ -335,9 +343,7 @@ void qmp_expire_password(const char *protocol, const char *whenstr, } if (strcmp(protocol, "spice") == 0) { - if (!using_spice) { - /* correct one? spice isn't a device ,,, */ - error_set(errp, QERR_DEVICE_NOT_ACTIVE, "spice"); + if (!qemu_using_spice(errp)) { return; } rc = qemu_spice_set_pw_expire(when); @@ -368,7 +374,24 @@ void qmp_change_vnc_password(const char *password, Error **errp) static void qmp_change_vnc_listen(const char *target, Error **errp) { - vnc_display_open(NULL, target, errp); + QemuOptsList *olist = qemu_find_opts("vnc"); + QemuOpts *opts; + + if (strstr(target, "id=")) { + error_setg(errp, "id not supported"); + return; + } + + opts = qemu_opts_find(olist, "default"); + if (opts) { + qemu_opts_del(opts); + } + opts = vnc_parse_func(target); + if (!opts) { + return; + } + + vnc_display_open("default", errp); } static void qmp_change_vnc(const char *target, bool has_arg, const char *arg, @@ -562,8 +585,7 @@ void qmp_add_client(const char *protocol, const char *fdname, } if (strcmp(protocol, "spice") == 0) { - if (!using_spice) { - error_set(errp, QERR_DEVICE_NOT_ACTIVE, "spice"); + if (!qemu_using_spice(errp)) { close(fd); return; } @@ -689,6 +711,11 @@ void qmp_object_del(const char *id, Error **errp) error_setg(errp, "object id not found"); return; } + + if (!user_creatable_can_be_deleted(USER_CREATABLE(obj), errp)) { + error_setg(errp, "%s is in use, can not be deleted", id); + return; + } object_unparent(obj); } diff --git a/qobject/qjson.c b/qobject/qjson.c index 6cf2511..12c576d 100644 --- a/qobject/qjson.c +++ b/qobject/qjson.c @@ -86,8 +86,9 @@ static void to_json_dict_iter(const char *key, QObject *obj, void *opaque) QString *qkey; int j; - if (s->count) - qstring_append(s->str, ", "); + if (s->count) { + qstring_append(s->str, s->pretty ? "," : ", "); + } if (s->pretty) { qstring_append(s->str, "\n"); @@ -109,8 +110,9 @@ static void to_json_list_iter(QObject *obj, void *opaque) ToJsonIterState *s = opaque; int j; - if (s->count) - qstring_append(s->str, ", "); + if (s->count) { + qstring_append(s->str, s->pretty ? "," : ", "); + } if (s->pretty) { qstring_append(s->str, "\n"); diff --git a/qom/cpu.c b/qom/cpu.c index 79d2228..108bfa2 100644 --- a/qom/cpu.c +++ b/qom/cpu.c @@ -71,8 +71,7 @@ CPUState *cpu_generic_init(const char *typename, const char *cpu_model) out: if (err != NULL) { - error_report("%s", error_get_pretty(err)); - error_free(err); + error_report_err(err); object_unref(OBJECT(cpu)); return NULL; } @@ -97,7 +96,7 @@ void cpu_get_memory_mapping(CPUState *cpu, MemoryMappingList *list, { CPUClass *cc = CPU_GET_CLASS(cpu); - return cc->get_memory_mapping(cpu, list, errp); + cc->get_memory_mapping(cpu, list, errp); } static void cpu_common_get_memory_mapping(CPUState *cpu, @@ -249,6 +248,7 @@ static void cpu_common_reset(CPUState *cpu) cpu->icount_extra = 0; cpu->icount_decr.u32 = 0; cpu->can_do_io = 0; + cpu->exception_index = -1; memset(cpu->tb_jmp_cache, 0, TB_JMP_CACHE_SIZE * sizeof(void *)); } diff --git a/qom/object.c b/qom/object.c index 1812c73..b8dff43 100644 --- a/qom/object.c +++ b/qom/object.c @@ -1543,6 +1543,85 @@ void object_property_add_bool(Object *obj, const char *name, } } +typedef struct TMProperty { + void (*get)(Object *, struct tm *, Error **); +} TMProperty; + +static void property_get_tm(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + TMProperty *prop = opaque; + Error *err = NULL; + struct tm value; + + prop->get(obj, &value, &err); + if (err) { + goto out; + } + + visit_start_struct(v, NULL, "struct tm", name, 0, &err); + if (err) { + goto out; + } + visit_type_int32(v, &value.tm_year, "tm_year", &err); + if (err) { + goto out_end; + } + visit_type_int32(v, &value.tm_mon, "tm_mon", &err); + if (err) { + goto out_end; + } + visit_type_int32(v, &value.tm_mday, "tm_mday", &err); + if (err) { + goto out_end; + } + visit_type_int32(v, &value.tm_hour, "tm_hour", &err); + if (err) { + goto out_end; + } + visit_type_int32(v, &value.tm_min, "tm_min", &err); + if (err) { + goto out_end; + } + visit_type_int32(v, &value.tm_sec, "tm_sec", &err); + if (err) { + goto out_end; + } +out_end: + error_propagate(errp, err); + err = NULL; + visit_end_struct(v, errp); +out: + error_propagate(errp, err); + +} + +static void property_release_tm(Object *obj, const char *name, + void *opaque) +{ + TMProperty *prop = opaque; + g_free(prop); +} + +void object_property_add_tm(Object *obj, const char *name, + void (*get)(Object *, struct tm *, Error **), + Error **errp) +{ + Error *local_err = NULL; + TMProperty *prop = g_malloc0(sizeof(*prop)); + + prop->get = get; + + object_property_add(obj, name, "struct tm", + get ? property_get_tm : NULL, NULL, + property_release_tm, + prop, &local_err); + if (local_err) { + error_propagate(errp, local_err); + g_free(prop); + } +} + static char *qdev_get_type(Object *obj, Error **errp) { return g_strdup(object_get_typename(obj)); @@ -1682,7 +1761,7 @@ void object_property_add_alias(Object *obj, const char *name, } op->resolve = property_resolve_alias; - object_property_set_description(obj, name, + object_property_set_description(obj, op->name, target_prop->description, &error_abort); diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c index 6360818..a66cd60 100644 --- a/qom/object_interfaces.c +++ b/qom/object_interfaces.c @@ -18,6 +18,18 @@ void user_creatable_complete(Object *obj, Error **errp) } } +bool user_creatable_can_be_deleted(UserCreatable *uc, Error **errp) +{ + + UserCreatableClass *ucc = USER_CREATABLE_GET_CLASS(uc); + + if (ucc->can_be_deleted) { + return ucc->can_be_deleted(uc, errp); + } else { + return true; + } +} + static void register_types(void) { static const TypeInfo uc_interface_info = { diff --git a/qtest.c b/qtest.c index 2bca04e..8d1e66c 100644 --- a/qtest.c +++ b/qtest.c @@ -520,16 +520,13 @@ static void qtest_event(void *opaque, int event) } } -static void configure_qtest_icount(const char *options) +static int qtest_init_accel(MachineState *ms) { - QemuOpts *opts = qemu_opts_parse(qemu_find_opts("icount"), options, 1); + QemuOpts *opts = qemu_opts_create(qemu_find_opts("icount"), NULL, 0, + &error_abort); + qemu_opt_set(opts, "shift", "0", &error_abort); configure_icount(opts, &error_abort); qemu_opts_del(opts); -} - -static int qtest_init_accel(MachineState *ms) -{ - configure_qtest_icount("0"); return 0; } diff --git a/roms/SLOF/VERSION b/roms/SLOF/VERSION index dcf7eaf..ea0dd13 100644 --- a/roms/SLOF/VERSION +++ b/roms/SLOF/VERSION @@ -1 +1 @@ -20140630 +20150313 diff --git a/roms/SLOF/board-js2x/slof/Makefile b/roms/SLOF/board-js2x/slof/Makefile index 5be9f07..ab3e683 100644 --- a/roms/SLOF/board-js2x/slof/Makefile +++ b/roms/SLOF/board-js2x/slof/Makefile @@ -76,6 +76,7 @@ OF_FFS_FILES = \ $(SLOFBRDDIR)/ipmi-kcs.fs \ $(SLOFCMNDIR)/fs/ide.fs \ $(SLOFCMNDIR)/fs/fbuffer.fs \ + $(SLOFCMNDIR)/fs/graphics.fs \ $(SLOFCMNDIR)/fs/generic-disk.fs \ $(SLOFCMNDIR)/fs/scsi-disk.fs \ $(SLOFCMNDIR)/fs/scsi-host-helpers.fs \ diff --git a/roms/SLOF/board-js2x/slof/pci-device_1002_515e.fs b/roms/SLOF/board-js2x/slof/pci-device_1002_515e.fs index 9f7d8c2..39a02de 100644 --- a/roms/SLOF/board-js2x/slof/pci-device_1002_515e.fs +++ b/roms/SLOF/board-js2x/slof/pci-device_1002_515e.fs @@ -460,7 +460,7 @@ init2_array init2_length encode-array " ibm,init2" property pllinit_array pllinit_length encode-array " ibm,pllinit" property meminit_array meminit_length encode-array " ibm,meminit" property 0 0 encode-bytes " iso6429-1983-colors" property -" display" encode-string " device_type" property +s" display" device-type /scanline encode-int " width" property #scanlines encode-int " height" property 8 encode-int " depth" property diff --git a/roms/SLOF/board-js2x/slof/version.c b/roms/SLOF/board-js2x/slof/version.c new file mode 100644 index 0000000..e69de29 diff --git a/roms/SLOF/board-js2x/slof/vga-display.fs b/roms/SLOF/board-js2x/slof/vga-display.fs index 0cc9bba..96417e2 100644 --- a/roms/SLOF/board-js2x/slof/vga-display.fs +++ b/roms/SLOF/board-js2x/slof/vga-display.fs @@ -20,7 +20,7 @@ value display_num ( str len ) s" ,Display-" $cat 41 display_num + char-cat \ add ", Display-A" or "-B" to name ( str len ) encode-string s" name" property \ store as name property -s" display" encode-string s" device_type" property \ add "device_type" propert +s" display" device-type \ screen-info is set by pci-class_03.fs contains output of get_vbe_info bios-snk call CASE screen-info c@ \ ( display-type ) @@ -61,62 +61,6 @@ THEN THEN ; -\ as of OF 8bit Graphics Recommendation, these shall be implemented: - -: draw-rectangle ( adr x y w h -- ) - is-installed? IF - 0 ?DO - 4dup ( adr x y w adr x y w ) - drop ( adr x y w adr x y ) - i + screen-width * + \ calculate offset into framebuffer ((y + i) * screen_width + x) - ( adr x y w adr offs ) - frame-buffer-adr + \ add to frame-buffer-adr ( adr x y w adr fb_adr ) - 1 pick 3 pick i * + swap 3 pick ( adr x y w adr adr_offs fb_adr w ) - rmove \ copy line ( adr x y w adr ) - drop ( adr x y w ) - LOOP - 4drop - ELSE - 4drop drop - THEN -; - -: fill-rectangle ( number x y w h -- ) - is-installed? IF - 0 ?DO - 4dup ( number x y w number x y w ) - drop ( number x y w number x y ) - i + screen-width * + \ calculate offset into framebuffer ((y + i) * screen_width + x) - ( number x y w number offs ) - frame-buffer-adr + \ add to frame-buffer-adr ( number x y w number adr ) - 2 pick 2 pick ( number x y w number adr w number ) - rfill \ draw line ( number x y w number ) - drop ( number x y w ) - LOOP - 4drop - ELSE - 4drop drop - THEN -; - -: read-rectangle ( adr x y w h -- ) - is-installed? IF - 0 ?DO - 4dup ( adr x y w adr x y w ) - drop ( adr x y w adr x y ) - i + screen-width * + \ calculate offset into framebuffer ((y + i) * screen_width + x) - ( adr x y w adr offs ) - frame-buffer-adr + \ add to frame-buffer-adr ( adr x y w adr fb_adr ) - 1 pick 3 pick i * + 3 pick ( adr x y w adr fb_adr adr_offs w ) - rmove \ copy line ( adr x y w adr ) - drop ( adr x y w ) - LOOP - 4drop - ELSE - 4drop drop - THEN -; - : color! ( r g b number -- ) \ 3c8 is RAMDAC write mode select palette entry register \ 3c9 is RAMDAC write mode write palette entry register ( 3 consecutive writes set new entry ) @@ -195,9 +139,7 @@ THEN 2drop ; -: dimensions ( -- width height ) -width height -; +include graphics.fs \ clear screen mem-adr width height * 0 rfill diff --git a/roms/SLOF/board-qemu/llfw/startup.S b/roms/SLOF/board-qemu/llfw/startup.S index 300f515..bbd3ce3 100644 --- a/roms/SLOF/board-qemu/llfw/startup.S +++ b/roms/SLOF/board-qemu/llfw/startup.S @@ -61,7 +61,7 @@ __start: mflr r0 // 10 mtsprg 3,r0 - ld r0, (\i + 0x160)(0) + ld r0, (\i + 0x60)(0) mtctr r0 li r0, \i + 0x100 // 20 diff --git a/roms/SLOF/board-qemu/slof/Makefile b/roms/SLOF/board-qemu/slof/Makefile index 7890a0f..283f77d 100644 --- a/roms/SLOF/board-qemu/slof/Makefile +++ b/roms/SLOF/board-qemu/slof/Makefile @@ -83,6 +83,7 @@ VIO_FFS_FILES = \ OF_FFS_FILES = \ $(SLOFCMNDIR)/fs/ide.fs \ $(SLOFCMNDIR)/fs/fbuffer.fs \ + $(SLOFCMNDIR)/fs/graphics.fs \ $(SLOFCMNDIR)/fs/generic-disk.fs \ $(SLOFCMNDIR)/fs/dma-function.fs \ $(SLOFCMNDIR)/fs/pci-device.fs \ diff --git a/roms/SLOF/board-qemu/slof/fdt.fs b/roms/SLOF/board-qemu/slof/fdt.fs index bd94a1b..8d4635f 100644 --- a/roms/SLOF/board-qemu/slof/fdt.fs +++ b/roms/SLOF/board-qemu/slof/fdt.fs @@ -346,6 +346,33 @@ fdt-claim-reserve device-end ; +: fdt-create-cas-node ( name -- ) + 2dup + 2dup " memory@" find-substr 0 = IF + fdt-debug IF ." Creating memory@ " cr THEN + new-device + 2dup " @" find-substr nip device-name \ Parse the node name + 2dup + 2dup " @" find-substr rot over + 1 + -rot - 1 - \ Jump to addr afte "@" + parse-2int nip xlsplit set-unit \ Parse and set unit + finish-device + ELSE + 2dup " ibm,dynamic-reconfiguration-memory" find-substr 0 = IF + fdt-debug IF ." Creating ibm,dynamic-reconfiguration-memory " cr THEN + new-device + device-name + finish-device + ELSE + 2drop 2drop + false to fdt-cas-fix? + ." Node not supported " cr + EXIT + THEN + THEN + + find-node ?dup 0 <> IF set-node THEN +; + : fdt-fix-cas-node ( start -- end ) recursive fdt-next-tag dup OF_DT_BEGIN_NODE <> IF @@ -363,12 +390,11 @@ fdt-claim-reserve drop THEN fdt-debug IF ." Setting node: " 2dup type cr THEN - find-node ?dup 0 <> IF - set-node + 2dup find-node ?dup 0 <> IF + set-node 2drop ELSE - ." Node not found " cr - false to fdt-cas-fix? - EXIT + fdt-debug IF ." Node not found, creating " 2dup type cr THEN + fdt-create-cas-node THEN fdt-debug IF ." Current now: " pwd cr THEN BEGIN diff --git a/roms/SLOF/board-qemu/slof/pci-device_1013_00b8.fs b/roms/SLOF/board-qemu/slof/pci-device_1013_00b8.fs index 778f1e8..c3ac2ec 100644 --- a/roms/SLOF/board-qemu/slof/pci-device_1013_00b8.fs +++ b/roms/SLOF/board-qemu/slof/pci-device_1013_00b8.fs @@ -89,67 +89,6 @@ false VALUE is-installed? 3cf vga-b! ; -\ ************************************************************************** -\ ** These come from vga-display.fs and should probably be moved to a common -\ ** location. - -: draw-rectangle ( adr x y w h -- ) - is-installed? IF - 0 ?DO - 4dup ( adr x y w adr x y w ) - drop ( adr x y w adr x y ) - i + screen-width * + \ calculate offset into framebuffer ((y + i) * screen_width + x) - ( adr x y w adr offs ) - frame-buffer-adr + \ add to frame-buffer-adr ( adr x y w adr fb_adr ) - 1 pick 3 pick i * + swap 3 pick ( adr x y w adr adr_offs fb_adr w ) - rmove \ copy line ( adr x y w adr ) - drop ( adr x y w ) - LOOP - 4drop - ELSE - 4drop drop - THEN -; - -: fill-rectangle ( number x y w h -- ) - is-installed? IF - 0 ?DO - 4dup ( number x y w number x y w ) - drop ( number x y w number x y ) - i + screen-width * + \ calculate offset into framebuffer ((y + i) * screen_width + x) - ( number x y w number offs ) - frame-buffer-adr + \ add to frame-buffer-adr ( number x y w number adr ) - 2 pick 2 pick ( number x y w number adr w number ) - rfill \ draw line ( number x y w number ) - drop ( number x y w ) - LOOP - 4drop - ELSE - 4drop drop - THEN -; - -: read-rectangle ( adr x y w h -- ) - is-installed? IF - 0 ?DO - 4dup ( adr x y w adr x y w ) - drop ( adr x y w adr x y ) - i + screen-width * + \ calculate offset into framebuffer ((y + i) * screen_width + x) - ( adr x y w adr offs ) - frame-buffer-adr + \ add to frame-buffer-adr ( adr x y w adr fb_adr ) - 1 pick 3 pick i * + 3 pick ( adr x y w adr fb_adr adr_offs w ) - rmove \ copy line ( adr x y w adr ) - drop ( adr x y w ) - LOOP - 4drop - ELSE - 4drop drop - THEN -; - -\ ** end of copy from vga-display.fs -\ ************************************************************************** - : color! ( r g b number -- ) 3c8 vga-b! rot 2 >> 3c9 vga-b! @@ -178,13 +117,7 @@ false VALUE is-installed? 3drop ; -: default-palette - \ Grayscale ramp for now, be smarter later - 100 0 DO - i i i i color! - LOOP -; - +include graphics.fs : init-mode 3da vga-b@ drop \ reset flip flop @@ -283,7 +216,7 @@ false VALUE is-installed? disp-depth encode-int s" depth" property s" ISO8859-1" encode-string s" character-set" property \ i hope this is ok... \ add "device_type" property - s" display" encode-string s" device_type" property + s" display" device-type \ XXX We don't create an "address" property because Linux doesn't know what \ to do with it for >32-bit ; @@ -306,10 +239,6 @@ false VALUE is-installed? THEN ; -: dimensions ( -- width height ) - disp-width disp-height -; - : set-alias s" screen" find-alias 0= IF \ no previous screen alias defined, define it... @@ -329,7 +258,7 @@ add-legacy-reg read-settings init-mode clear-screen -default-palette +init-default-palette setup-properties ' display-install is-install ' display-remove is-remove diff --git a/roms/SLOF/board-qemu/slof/pci-device_1234_1111.fs b/roms/SLOF/board-qemu/slof/pci-device_1234_1111.fs index fde9d64..a5c3584 100644 --- a/roms/SLOF/board-qemu/slof/pci-device_1234_1111.fs +++ b/roms/SLOF/board-qemu/slof/pci-device_1234_1111.fs @@ -57,68 +57,6 @@ false VALUE is-installed? 1ce vga-w! 1d0 vga-w@ ; - -\ ************************************************************************** -\ ** These come from vga-display.fs and should probably be moved to a common -\ ** location. - -: draw-rectangle ( adr x y w h -- ) - is-installed? IF - 0 ?DO - 4dup ( adr x y w adr x y w ) - drop ( adr x y w adr x y ) - i + screen-width * + \ calculate offset into framebuffer ((y + i) * screen_width + x) - ( adr x y w adr offs ) - frame-buffer-adr + \ add to frame-buffer-adr ( adr x y w adr fb_adr ) - 1 pick 3 pick i * + swap 3 pick ( adr x y w adr adr_offs fb_adr w ) - rmove \ copy line ( adr x y w adr ) - drop ( adr x y w ) - LOOP - 4drop - ELSE - 4drop drop - THEN -; - -: fill-rectangle ( number x y w h -- ) - is-installed? IF - 0 ?DO - 4dup ( number x y w number x y w ) - drop ( number x y w number x y ) - i + screen-width * + \ calculate offset into framebuffer ((y + i) * screen_width + x) - ( number x y w number offs ) - frame-buffer-adr + \ add to frame-buffer-adr ( number x y w number adr ) - 2 pick 2 pick ( number x y w number adr w number ) - rfill \ draw line ( number x y w number ) - drop ( number x y w ) - LOOP - 4drop - ELSE - 4drop drop - THEN -; - -: read-rectangle ( adr x y w h -- ) - is-installed? IF - 0 ?DO - 4dup ( adr x y w adr x y w ) - drop ( adr x y w adr x y ) - i + screen-width * + \ calculate offset into framebuffer ((y + i) * screen_width + x) - ( adr x y w adr offs ) - frame-buffer-adr + \ add to frame-buffer-adr ( adr x y w adr fb_adr ) - 1 pick 3 pick i * + 3 pick ( adr x y w adr fb_adr adr_offs w ) - rmove \ copy line ( adr x y w adr ) - drop ( adr x y w ) - LOOP - 4drop - ELSE - 4drop drop - THEN -; - -\ ** end of copy from vga-display.fs -\ ************************************************************************** - : color! ( r g b number -- ) 3c8 vga-b! rot 3c9 vga-b! @@ -147,12 +85,7 @@ false VALUE is-installed? 3drop ; -: default-palette - \ Grayscale ramp for now, be smarter later - 100 0 DO - i i i i color! - LOOP -; +include graphics.fs \ qemu fake VBE IO registers 0 CONSTANT VBE_DISPI_INDEX_ID @@ -245,7 +178,7 @@ a CONSTANT VBE_DISPI_INDEX_NB disp-depth encode-int s" depth" property s" ISO8859-1" encode-string s" character-set" property \ i hope this is ok... \ add "device_type" property - s" display" encode-string s" device_type" property + s" display" device-type s" qemu,std-vga" encode-string s" compatible" property \ XXX We don't create an "address" property because Linux doesn't know what \ to do with it for >32-bit @@ -284,10 +217,6 @@ a CONSTANT VBE_DISPI_INDEX_NB THEN ; -: dimensions ( -- width height ) - disp-width disp-height -; - : set-alias s" screen" find-alias 0= IF \ no previous screen alias defined, define it... @@ -306,7 +235,7 @@ pci-io-enable add-legacy-reg read-settings init-mode -default-palette +init-default-palette setup-properties ' display-install is-install ' display-remove is-remove diff --git a/roms/SLOF/board-qemu/slof/pci-phb.fs b/roms/SLOF/board-qemu/slof/pci-phb.fs index 4f8e702..529772f 100644 --- a/roms/SLOF/board-qemu/slof/pci-phb.fs +++ b/roms/SLOF/board-qemu/slof/pci-phb.fs @@ -232,6 +232,8 @@ setup-puid 0 pci-max-mem ! 0 pci-next-mmio ! 0 pci-max-mmio ! + 0 pci-next-mem64 ! + 0 pci-max-mem64 ! \ Now get the "ranges" property s" ranges" get-node get-property 0<> ABORT" ranges property not found" @@ -259,8 +261,9 @@ setup-puid r> + pci-max-mmio ! \ calc max MMIO address ENDOF 3000000 OF \ 64-bit memory space? - cr ." Warning: 64-bit PCI space not supported yet! " - decode-64 . decode-64 . cr + decode-64 pci-next-mem64 ! + decode-64 drop \ Forget the parent address + decode-64 pci-max-mem64 ! ENDOF ENDCASE REPEAT @@ -274,6 +277,8 @@ setup-puid ." pci-max-mem = " pci-max-mem @ . cr ." pci-next-mmio = " pci-next-mmio @ . cr ." pci-max-mmio = " pci-max-mmio @ . cr + ." pci-next-mem64 = " pci-next-mem64 @ . cr + ." pci-max-mem64 = " pci-max-mem64 @ . cr THEN ; diff --git a/roms/SLOF/board-qemu/slof/rtas.fs b/roms/SLOF/board-qemu/slof/rtas.fs index 41e30c2..2e10b0a 100644 --- a/roms/SLOF/board-qemu/slof/rtas.fs +++ b/roms/SLOF/board-qemu/slof/rtas.fs @@ -174,7 +174,14 @@ rtas-node set-node : instantiate-rtas ( adr -- entry ) dup rtas-base swap rtas-size move - rtas-entry rtas-base - + + dup rtas-entry rtas-base - + + 2dup hv-rtas-update dup 0 <> IF + \ Ignore hcall not implemented error, print error otherwise + dup -2 <> IF ." HV-RTAS-UPDATE error: " . cr ELSE drop THEN + ELSE + drop + THEN + nip ; device-end diff --git a/roms/SLOF/clients/net-snk/app/biosemu/biosemu.h b/roms/SLOF/clients/net-snk/app/biosemu/biosemu.h index 7ffd5bc..28b7ab8 100644 --- a/roms/SLOF/clients/net-snk/app/biosemu/biosemu.h +++ b/roms/SLOF/clients/net-snk/app/biosemu/biosemu.h @@ -37,4 +37,6 @@ // Address, there will only be a call to this INT and a RETF #define PNP_INT_NUM 0xFD +uint32_t biosemu(char argc, char **argv); + #endif diff --git a/roms/SLOF/clients/net-snk/app/biosemu/debug.h b/roms/SLOF/clients/net-snk/app/biosemu/debug.h index c056190..46a026a 100644 --- a/roms/SLOF/clients/net-snk/app/biosemu/debug.h +++ b/roms/SLOF/clients/net-snk/app/biosemu/debug.h @@ -17,7 +17,7 @@ extern uint32_t debug_flags; // from x86emu...needed for debugging -extern void x86emu_dump_xregs(); +extern void x86emu_dump_xregs(void); #define DEBUG_IO 0x1 #define DEBUG_MEM 0x2 diff --git a/roms/SLOF/clients/net-snk/app/biosemu/device.c b/roms/SLOF/clients/net-snk/app/biosemu/device.c index ba90072..514b87e 100644 --- a/roms/SLOF/clients/net-snk/app/biosemu/device.c +++ b/roms/SLOF/clients/net-snk/app/biosemu/device.c @@ -30,8 +30,8 @@ typedef struct { // scan all adresses assigned to the device ("assigned-addresses" and "reg") // store in translate_address_array for faster translation using dev_translate_address -void -dev_get_addr_info() +static void +dev_get_addr_info(void) { // get bus/dev/fn from assigned-addresses int32_t len; @@ -110,8 +110,8 @@ dev_get_addr_info() // we look for the first prefetchable memory BAR, if no prefetchable BAR found, // we use the first memory BAR // dev_translate_addr will translate accesses to the legacy VGA Memory into the found vmem BAR -void -dev_find_vmem_addr() +static void +dev_find_vmem_addr(void) { int i = 0; translate_address_t ta; @@ -153,16 +153,16 @@ dev_find_vmem_addr() //bios_device.vmem_size = 0; } -void -dev_get_puid() +static void +dev_get_puid(void) { // get puid bios_device.puid = get_puid(bios_device.phandle); DEBUG_PRINTF("puid: 0x%llx\n", bios_device.puid); } -void -dev_get_device_vendor_id() +static void +dev_get_device_vendor_id(void) { uint32_t pci_config_0 = rtas_pci_config_read(bios_device.puid, 4, bios_device.bus, @@ -177,7 +177,7 @@ dev_get_device_vendor_id() /* check, wether the device has a valid Expansion ROM, also search the PCI Data Structure and * any Expansion ROM Header (using dev_scan_exp_header()) for needed information */ uint8_t -dev_check_exprom() +dev_check_exprom(void) { int i = 0; translate_address_t ta; diff --git a/roms/SLOF/clients/net-snk/app/biosemu/device.h b/roms/SLOF/clients/net-snk/app/biosemu/device.h index ce2991f..425dd3c 100644 --- a/roms/SLOF/clients/net-snk/app/biosemu/device.h +++ b/roms/SLOF/clients/net-snk/app/biosemu/device.h @@ -104,7 +104,7 @@ device_t bios_device; uint8_t dev_init(char *device_name); // NOTE: for dev_check_exprom to work, dev_init MUST be called first! -uint8_t dev_check_exprom(); +uint8_t dev_check_exprom(void); uint8_t dev_translate_address(uint64_t * addr); @@ -143,7 +143,7 @@ in16le(void *addr) /* debug function, dumps HID1 and HID4 to detect wether caches are on/off */ static inline void -dumpHID() +dumpHID(void) { uint64_t hid; //HID1 = 1009 diff --git a/roms/SLOF/clients/net-snk/app/biosemu/interrupt.c b/roms/SLOF/clients/net-snk/app/biosemu/interrupt.c index f1137fe..ac3f5b4 100644 --- a/roms/SLOF/clients/net-snk/app/biosemu/interrupt.c +++ b/roms/SLOF/clients/net-snk/app/biosemu/interrupt.c @@ -18,6 +18,7 @@ #include "mem.h" #include "device.h" #include "debug.h" +#include "interrupt.h" #include #include @@ -25,7 +26,7 @@ //setup to run the code at the address, that the Interrupt Vector points to... -void +static void setupInt(int intNum) { DEBUG_PRINTF_INTR("%s(%x): executing interrupt handler @%08x\n", @@ -44,8 +45,8 @@ setupInt(int intNum) } // handle int10 (VGA BIOS Interrupt) -void -handleInt10() +static void +handleInt10(void) { // the data for INT10 is stored in BDA (0000:0400h) offset 49h-66h // function number in AH @@ -197,11 +198,9 @@ static uint8_t keycode_table[256] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -} - -; +}; -void +static void translate_keycode(uint64_t * keycode) { uint8_t scan_code = 0; @@ -227,8 +226,8 @@ translate_keycode(uint64_t * keycode) } // handle int16 (Keyboard BIOS Interrupt) -void -handleInt16() +static void +handleInt16(void) { // keyboard buffer is in BIOS Memory Area: // offset 0x1a (WORD) pointer to next char in keybuffer @@ -311,8 +310,8 @@ handleInt16() } // handle int1a (PCI BIOS Interrupt) -void -handleInt1a() +static void +handleInt1a(void) { // function number in AX uint8_t bus, devfn, offs; @@ -569,7 +568,7 @@ runInt10() // prepare and execute Interrupt 13 (Disk Interrupt) void -runInt13() +runInt13(void) { // Initialize stack and data segment M.x86.R_SS = STACK_SEGMENT; diff --git a/roms/SLOF/clients/net-snk/app/biosemu/interrupt.h b/roms/SLOF/clients/net-snk/app/biosemu/interrupt.h index 9c09086..11755e1 100644 --- a/roms/SLOF/clients/net-snk/app/biosemu/interrupt.h +++ b/roms/SLOF/clients/net-snk/app/biosemu/interrupt.h @@ -14,8 +14,8 @@ void handleInterrupt(int intNum); -void runInt10(); +void runInt10(void); -void runInt13(); +void runInt13(void); #endif diff --git a/roms/SLOF/clients/net-snk/app/biosemu/io.c b/roms/SLOF/clients/net-snk/app/biosemu/io.c index 3092c22..e9c0893 100644 --- a/roms/SLOF/clients/net-snk/app/biosemu/io.c +++ b/roms/SLOF/clients/net-snk/app/biosemu/io.c @@ -20,61 +20,14 @@ #include #include #include - +#include "io.h" //defined in net-snk/kernel/timer.c extern uint64_t get_time(void); -// these are not used, only needed for linking, must be overridden using X86emu_setupPioFuncs -// with the functions and struct below -void -outb(uint8_t val, uint16_t port) -{ - printf("WARNING: outb not implemented!\n"); - HALT_SYS(); -} - -void -outw(uint16_t val, uint16_t port) -{ - printf("WARNING: outw not implemented!\n"); - HALT_SYS(); -} - -void -outl(uint32_t val, uint16_t port) -{ - printf("WARNING: outl not implemented!\n"); - HALT_SYS(); -} - -uint8_t -inb(uint16_t port) -{ - printf("WARNING: inb not implemented!\n"); - HALT_SYS(); - return 0; -} - -uint16_t -inw(uint16_t port) -{ - printf("WARNING: inw not implemented!\n"); - HALT_SYS(); - return 0; -} - -uint32_t -inl(uint16_t port) -{ - printf("WARNING: inl not implemented!\n"); - HALT_SYS(); - return 0; -} - uint32_t pci_cfg_read(X86EMU_pioAddr addr, uint8_t size); void pci_cfg_write(X86EMU_pioAddr addr, uint32_t val, uint8_t size); -uint8_t handle_port_61h(); +uint8_t handle_port_61h(void); uint8_t my_inb(X86EMU_pioAddr addr) @@ -407,7 +360,7 @@ pci_cfg_write(X86EMU_pioAddr addr, uint32_t val, uint8_t size) } uint8_t -handle_port_61h() +handle_port_61h(void) { static uint64_t last_time = 0; uint64_t curr_time = get_time(); diff --git a/roms/SLOF/clients/net-snk/app/biosemu/mem.c b/roms/SLOF/clients/net-snk/app/biosemu/mem.c index 3a62c96..1a62075 100644 --- a/roms/SLOF/clients/net-snk/app/biosemu/mem.c +++ b/roms/SLOF/clients/net-snk/app/biosemu/mem.c @@ -18,6 +18,7 @@ #include "x86emu/x86emu.h" #include "biosemu.h" #include +#include "mem.h" // define a check for access to certain (virtual) memory regions (interrupt handlers, BIOS Data Area, ...) #ifdef DEBUG diff --git a/roms/SLOF/clients/net-snk/app/biosemu/vbe.c b/roms/SLOF/clients/net-snk/app/biosemu/vbe.c index f9618b4..957a1f2 100644 --- a/roms/SLOF/clients/net-snk/app/biosemu/vbe.c +++ b/roms/SLOF/clients/net-snk/app/biosemu/vbe.c @@ -28,6 +28,7 @@ #include "mem.h" #include "interrupt.h" #include "device.h" +#include "vbe.h" static X86EMU_memFuncs my_mem_funcs = { my_rdb, my_rdw, my_rdl, @@ -97,7 +98,7 @@ typedef struct { } vbe_ddc_info_t; static inline uint8_t -vbe_prepare() +vbe_prepare(void) { vbe_info_buffer = biosmem + (VBE_SEGMENT << 4); // segment:offset off VBE Data Area //clear buffer @@ -115,7 +116,7 @@ vbe_prepare() } // VBE Function 00h -uint8_t +static uint8_t vbe_info(vbe_info_t * info) { vbe_prepare(); @@ -184,7 +185,7 @@ vbe_info(vbe_info_t * info) } // VBE Function 01h -uint8_t +static uint8_t vbe_get_mode_info(vbe_mode_info_t * mode_info) { vbe_prepare(); @@ -252,7 +253,7 @@ vbe_get_mode_info(vbe_mode_info_t * mode_info) } // VBE Function 02h -uint8_t +static uint8_t vbe_set_mode(vbe_mode_info_t * mode_info) { vbe_prepare(); @@ -289,7 +290,7 @@ vbe_set_mode(vbe_mode_info_t * mode_info) } //VBE Function 08h -uint8_t +static uint8_t vbe_set_palette_format(uint8_t format) { vbe_prepare(); @@ -325,7 +326,7 @@ vbe_set_palette_format(uint8_t format) } // VBE Function 09h -uint8_t +static uint8_t vbe_set_color(uint16_t color_number, uint32_t color_value) { vbe_prepare(); @@ -367,7 +368,8 @@ vbe_set_color(uint16_t color_number, uint32_t color_value) return 0; } -uint8_t +#if 0 +static uint8_t vbe_get_color(uint16_t color_number, uint32_t * color_value) { vbe_prepare(); @@ -408,9 +410,10 @@ vbe_get_color(uint16_t color_number, uint32_t * color_value) return 0; } +#endif // VBE Function 15h -uint8_t +static uint8_t vbe_get_ddc_info(vbe_ddc_info_t * ddc_info) { vbe_prepare(); diff --git a/roms/SLOF/clients/net-snk/app/biosemu/vbe.h b/roms/SLOF/clients/net-snk/app/biosemu/vbe.h index 07daedb..17e9826 100644 --- a/roms/SLOF/clients/net-snk/app/biosemu/vbe.h +++ b/roms/SLOF/clients/net-snk/app/biosemu/vbe.h @@ -13,4 +13,6 @@ #ifndef _BIOSEMU_VBE_H_ #define _BIOSEMU_VBE_H_ +uint32_t vbe_get_info(uint8_t argc, char ** argv); + #endif diff --git a/roms/SLOF/clients/net-snk/app/main.c b/roms/SLOF/clients/net-snk/app/main.c index d894aa2..90e14b3 100644 --- a/roms/SLOF/clients/net-snk/app/main.c +++ b/roms/SLOF/clients/net-snk/app/main.c @@ -17,8 +17,8 @@ #include #ifdef SNK_BIOSEMU_APPS -extern int biosemu(char argc, char**argv); -extern int vbe_get_info(char argc, char**argv); +#include "biosemu/biosemu.h" +#include "biosemu/vbe.h" #endif extern void _callback_entry(void); diff --git a/roms/SLOF/clients/net-snk/app/netapps/netboot.c b/roms/SLOF/clients/net-snk/app/netapps/netboot.c index aa009bf..cf20b59 100644 --- a/roms/SLOF/clients/net-snk/app/netapps/netboot.c +++ b/roms/SLOF/clients/net-snk/app/netapps/netboot.c @@ -350,7 +350,7 @@ int dhcp(char *ret_buffer, filename_ip_t * fn_ip, unsigned int retries, int flag } if ((!flags && (rc == -1)) || (flags == F_IPV6)) { ip_version = 6; - set_ipv6_address(0); + set_ipv6_address(fn_ip->fd, 0); rc = dhcpv6(ret_buffer, fn_ip); if (rc == 0) { printf("\n"); @@ -428,6 +428,8 @@ netboot(int argc, char *argv[]) return -101; } + fn_ip.fd = fd_device; + printf(" Reading MAC address from device: " "%02x:%02x:%02x:%02x:%02x:%02x\n", own_mac[0], own_mac[1], own_mac[2], @@ -506,7 +508,7 @@ netboot(int argc, char *argv[]) obp_tftp_args.bootp_retries, F_IPV6); break; case IP_INIT_IPV6_MANUAL: - set_ipv6_address(&obp_tftp_args.ci6addr); + set_ipv6_address(fn_ip.fd, &obp_tftp_args.ci6addr); break; case IP_INIT_DEFAULT: printf(" Requesting IP address via DHCP: "); @@ -605,7 +607,7 @@ netboot(int argc, char *argv[]) &tftp_err, huge_load, block_size, ip_version); if(obp_tftp_args.ip_init == IP_INIT_DHCP) - dhcp_send_release(); + dhcp_send_release(fn_ip.fd); if (rc > 0) { printf(" TFTP: Received %s (%d KBytes)\n", fn_ip.filename, @@ -732,10 +734,12 @@ netboot(int argc, char *argv[]) * @param buffer string with arguments, * @param server_ip server ip as result * @param filename default filename - * @param len len of the buffer, + * @param fd Socket descriptor + * @param len len of the buffer, * @return 0 on SUCCESS and -1 on failure */ -int parse_tftp_args(char buffer[], char *server_ip, char filename[], int len) +int parse_tftp_args(char buffer[], char *server_ip, char filename[], int fd, + int len) { char *raw; char *tmp, *tmp1; @@ -814,7 +818,7 @@ int parse_tftp_args(char buffer[], char *server_ip, char filename[], int len) tmp = raw + 7; tmp[j] = '\0'; strcpy(domainname, tmp); - if (dns_get_ip((int8_t *)domainname, server_ip6, 6) == 0) { + if (dns_get_ip(fd, (int8_t *)domainname, server_ip6, 6) == 0) { printf("\n DNS failed for IPV6\n"); return -1; } diff --git a/roms/SLOF/clients/net-snk/app/netapps/ping.c b/roms/SLOF/clients/net-snk/app/netapps/ping.c index 8376f48..2c7dadb 100644 --- a/roms/SLOF/clients/net-snk/app/netapps/ping.c +++ b/roms/SLOF/clients/net-snk/app/netapps/ping.c @@ -138,6 +138,8 @@ ping(int argc, char *argv[]) return -101; } + fn_ip.fd = fd_device; + printf("%02x:%02x:%02x:%02x:%02x:%02x\n", own_mac[0], own_mac[1], own_mac[2], own_mac[3], own_mac[4], own_mac[5]); @@ -178,11 +180,11 @@ ping(int argc, char *argv[]) ((fn_ip.server_ip >> 8) & 0xFF), (fn_ip.server_ip & 0xFF)); - ping_ipv4(fn_ip.server_ip); + ping_ipv4(fd_device, fn_ip.server_ip); set_timer(TICKS_SEC / 10 * ping_args.timeout); while(get_timer() > 0) { - receive_ether(); + receive_ether(fd_device); if(pong_ipv4() == 0) { printf("success\n"); return 0; diff --git a/roms/SLOF/clients/net-snk/app/netlib/bootp.c b/roms/SLOF/clients/net-snk/app/netlib/bootp.c index 7adf3f4..1bc6efe 100644 --- a/roms/SLOF/clients/net-snk/app/netlib/bootp.c +++ b/roms/SLOF/clients/net-snk/app/netlib/bootp.c @@ -85,7 +85,7 @@ send_bootp(filename_ip_t * fn_ip) printf(".\n"); #endif - send_ipv4(packet, iph->ip_len); + send_ipv4(fn_ip->fd, packet, iph->ip_len); #if DEBUG printf("%d bytes transmitted over socket.\n", i); #endif @@ -121,7 +121,7 @@ receive_bootp(filename_ip_t * fn_ip) do { /* let's receive a packet */ - len = recv(0, packet, packetsize, 0); + len = recv(fn_ip->fd, packet, packetsize, 0); #if DEBUG int j; diff --git a/roms/SLOF/clients/net-snk/app/netlib/dhcp.c b/roms/SLOF/clients/net-snk/app/netlib/dhcp.c index 7a92e28..5f26f3a 100644 --- a/roms/SLOF/clients/net-snk/app/netlib/dhcp.c +++ b/roms/SLOF/clients/net-snk/app/netlib/dhcp.c @@ -135,7 +135,7 @@ static uint8_t dhcp_state; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>> PROTOTYPES <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ static int32_t -dhcp_attempt(void); +dhcp_attempt(int fd); static int32_t dhcp_encode_options(uint8_t * opt_field, dhcp_options_t * opt_struct); @@ -161,10 +161,10 @@ dhcp_combine_option(uint8_t dst_options[], uint32_t * dst_len, uint32_t dst_offset, uint8_t * new_option); static void -dhcp_send_discover(void); +dhcp_send_discover(int fd); static void -dhcp_send_request(void); +dhcp_send_request(int fd); static uint8_t strtoip(int8_t * str, uint32_t * ip); @@ -187,12 +187,14 @@ int32_t dhcpv4(char *ret_buffer, filename_ip_t * fn_ip) { uint32_t dhcp_tftp_ip = 0; + int fd = fn_ip->fd; + strcpy((char *) dhcp_filename, ""); strcpy((char *) dhcp_tftp_name, ""); response_buffer = ret_buffer; - if (dhcp_attempt() == 0) + if (dhcp_attempt(fd) == 0) return -1; if (fn_ip->own_ip) { @@ -218,7 +220,7 @@ dhcpv4(char *ret_buffer, filename_ip_t * fn_ip) { else { // TFTP server defined by its name if (!strtoip(dhcp_tftp_name, &(dhcp_tftp_ip))) { - if (!dns_get_ip(dhcp_tftp_name, (uint8_t *)&(dhcp_tftp_ip), 4)) { + if (!dns_get_ip(fd, dhcp_tftp_name, (uint8_t *)&(dhcp_tftp_ip), 4)) { // DNS error - can't obtain TFTP-server name // Use TFTP-ip from siaddr field, if presented if (dhcp_siaddr_ip) { @@ -244,11 +246,11 @@ dhcpv4(char *ret_buffer, filename_ip_t * fn_ip) { * DHCP: Tries o obtain DHCP parameters, refer to state-transition diagram */ static int32_t -dhcp_attempt(void) { +dhcp_attempt(int fd) { int sec; // Send DISCOVER message and switch DHCP-client to SELECT state - dhcp_send_discover(); + dhcp_send_discover(fd); dhcp_state = DHCP_STATE_SELECT; @@ -256,7 +258,7 @@ dhcp_attempt(void) { for (sec = 0; sec < 2; sec++) { set_timer(TICKS_SEC); do { - receive_ether(); + receive_ether(fd); // Wait until client will switch to Final state or Timeout occurs switch (dhcp_state) { @@ -611,7 +613,7 @@ dhcp_combine_option(uint8_t dst_options[], uint32_t * dst_len, * DHCP: Sends DHCP-Discover message. Looks for DHCP servers. */ static void -dhcp_send_discover(void) { +dhcp_send_discover(int fd) { uint32_t packetsize = sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct btphdr); struct btphdr *btph; @@ -647,14 +649,14 @@ dhcp_send_discover(void) { sizeof(struct udphdr) + sizeof(struct iphdr), IPTYPE_UDP, dhcp_own_ip, 0xFFFFFFFF); - send_ipv4(ether_packet, packetsize); + send_ipv4(fd, ether_packet, packetsize); } /** * DHCP: Sends DHCP-Request message. Asks for acknowledgment to occupy IP. */ static void -dhcp_send_request(void) { +dhcp_send_request(int fd) { uint32_t packetsize = sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct btphdr); struct btphdr *btph; @@ -695,14 +697,14 @@ dhcp_send_request(void) { sizeof(struct udphdr) + sizeof(struct iphdr), IPTYPE_UDP, 0, 0xFFFFFFFF); - send_ipv4(ether_packet, packetsize); + send_ipv4(fd, ether_packet, packetsize); } /** * DHCP: Sends DHCP-Release message. Releases occupied IP. */ -void dhcp_send_release(void) { +void dhcp_send_release(int fd) { uint32_t packetsize = sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct btphdr); struct btphdr *btph; @@ -735,13 +737,14 @@ void dhcp_send_release(void) { sizeof(struct udphdr) + sizeof(struct iphdr), IPTYPE_UDP, dhcp_own_ip, dhcp_server_ip); - send_ipv4(ether_packet, packetsize); + send_ipv4(fd, ether_packet, packetsize); } /** * DHCP: Handles DHCP-messages according to Receive-handle diagram. * Changes the state of DHCP-client. * + * @param fd socket descriptor * @param packet BootP/DHCP-packet to be handled * @param packetsize length of the packet * @return ZERO - packet handled successfully; @@ -751,7 +754,7 @@ void dhcp_send_release(void) { */ int8_t -handle_dhcp(uint8_t * packet, int32_t packetsize) { +handle_dhcp(int fd, uint8_t * packet, int32_t packetsize) { struct btphdr * btph; struct iphdr * iph; dhcp_options_t opt; @@ -863,7 +866,7 @@ handle_dhcp(uint8_t * packet, int32_t packetsize) { if (opt.msg_type == DHCPOFFER) { dhcp_own_ip = htonl(btph -> yiaddr); dhcp_server_ip = opt.server_ID; - dhcp_send_request(); + dhcp_send_request(fd); dhcp_state = DHCP_STATE_REQUEST; } return 0; diff --git a/roms/SLOF/clients/net-snk/app/netlib/dhcp.h b/roms/SLOF/clients/net-snk/app/netlib/dhcp.h index 3aab4ef..69dd49d 100644 --- a/roms/SLOF/clients/net-snk/app/netlib/dhcp.h +++ b/roms/SLOF/clients/net-snk/app/netlib/dhcp.h @@ -45,9 +45,9 @@ struct btphdr { int bootp(char *ret_buffer, filename_ip_t *, unsigned int); int dhcpv4(char *ret_buffer, filename_ip_t *); -void dhcp_send_release(void); +void dhcp_send_release(int fd); /* Handles DHCP-packets, which are detected by receive_ether. */ -extern int8_t handle_dhcp(uint8_t * packet, int32_t packetsize); +extern int8_t handle_dhcp(int fd, uint8_t * packet, int32_t packetsize); #endif diff --git a/roms/SLOF/clients/net-snk/app/netlib/dhcpv6.c b/roms/SLOF/clients/net-snk/app/netlib/dhcpv6.c index 53cc7d3..4deef30 100644 --- a/roms/SLOF/clients/net-snk/app/netlib/dhcpv6.c +++ b/roms/SLOF/clients/net-snk/app/netlib/dhcpv6.c @@ -37,7 +37,7 @@ generate_transaction_id(void) } static void -send_info_request(void) +send_info_request(int fd) { uint8_t ether_packet[ETH_MTU_SIZE]; uint32_t payload_length; @@ -78,19 +78,19 @@ send_info_request(void) dhcph->option.option_request_option.option_code[2] = DHCPV6_OPTION_BOOT_URL; - send_ipv6( ether_packet + sizeof(struct ethhdr), + send_ipv6(fd, ether_packet + sizeof(struct ethhdr), sizeof(struct ethhdr)+ sizeof(struct ip6hdr) + sizeof(struct udphdr) + sizeof( struct dhcp_message_header) ); } static int32_t -dhcpv6_attempt(void) +dhcpv6_attempt(int fd) { int sec; // Send information request - send_info_request(); + send_info_request(fd); dhcpv6_state = DHCPV6_STATE_SELECT; @@ -98,7 +98,7 @@ dhcpv6_attempt(void) for (sec = 0; sec < 2; sec++) { set_timer(TICKS_SEC); do { - receive_ether(); + receive_ether(fd); // Wait until client will switch to Final state or Timeout occurs switch (dhcpv6_state) { @@ -117,8 +117,12 @@ dhcpv6_attempt(void) int32_t dhcpv6 ( char *ret_buffer, void *fn_ip) { + int fd; + my_fn_ip = (filename_ip_t *) fn_ip; - if( !dhcpv6_attempt()) { + fd = my_fn_ip->fd; + + if( !dhcpv6_attempt(fd)) { return -1; } @@ -178,6 +182,7 @@ dhcp6_process_options (uint8_t *option, int32_t option_length) if (parse_tftp_args(buffer, (char *)my_fn_ip->server_ip6.addr, (char *)my_fn_ip->filename, + (int)my_fn_ip->fd, option_boot_url->length) == -1) return NULL; break; diff --git a/roms/SLOF/clients/net-snk/app/netlib/dns.c b/roms/SLOF/clients/net-snk/app/netlib/dns.c index 831d22d..0ab1346 100644 --- a/roms/SLOF/clients/net-snk/app/netlib/dns.c +++ b/roms/SLOF/clients/net-snk/app/netlib/dns.c @@ -66,7 +66,7 @@ struct dnshdr { /***************************** PROTOTYPES ********************************/ static void -dns_send_query(int8_t * domain_name, uint8_t ip_version); +dns_send_query(int fd, int8_t * domain_name, uint8_t ip_version); static void fill_dnshdr(uint8_t * packet, int8_t * domain_name, uint8_t ip_version); @@ -125,6 +125,7 @@ dns_init(uint32_t _dns_server_ip, uint8_t _dns_server_ipv6[16], uint8_t ip_versi *
    (e.g. "www.host.org"); * * + * @param fd socket descriptor * @param url the URL to be resolved * @param domain_ip In case of SUCCESS stores extracted IP. * In case of FAULT stores zeros (0.0.0.0). @@ -132,7 +133,7 @@ dns_init(uint32_t _dns_server_ip, uint8_t _dns_server_ipv6[16], uint8_t ip_versi * FALSE - error condition occurs. */ int8_t -dns_get_ip(int8_t * url, uint8_t * domain_ip, uint8_t ip_version) +dns_get_ip(int fd, int8_t * url, uint8_t * domain_ip, uint8_t ip_version) { /* this counter is used so that we abort after 30 DNS request */ int32_t i; @@ -171,14 +172,14 @@ dns_get_ip(int8_t * url, uint8_t * domain_ip, uint8_t ip_version) for(i = 0; i < 30; ++i) { // Use canonical name in case we obtained it if (strlen((char *) dns_domain_cname)) - dns_send_query(dns_domain_cname, ip_version); + dns_send_query(fd, dns_domain_cname, ip_version); else - dns_send_query(dns_domain_name, ip_version); + dns_send_query(fd, dns_domain_name, ip_version); // setting up a timer with a timeout of one seconds set_timer(TICKS_SEC); do { - receive_ether(); + receive_ether(fd); if (dns_error) return 0; // FALSE - error if ((dns_result_ip != 0) && (ip_version == 4)) { @@ -295,13 +296,14 @@ handle_dns(uint8_t * packet, int32_t packetsize) * DNS-server respones with host IP or signals some error condition. * Responses from the server are handled by handle_dns function. * + * @param fd socket descriptor * @param domain_name the domain name given as series of labels preceded * with length(label) and terminated with 0 *
    (e.g. "\3,w,w,w,\4,h,o,s,t,\3,o,r,g,\0") * @see handle_dns */ static void -dns_send_query(int8_t * domain_name, uint8_t ip_version) +dns_send_query(int fd, int8_t * domain_name, uint8_t ip_version) { int qry_len = strlen((char *) domain_name) + 5; int iphdr_len = (ip_version == 4) ? sizeof(struct iphdr) : sizeof(struct ip6hdr); @@ -333,7 +335,7 @@ dns_send_query(int8_t * domain_name, uint8_t ip_version) &server_ipv6); } - send_ip(ether_packet, packetsize); + send_ip(fd, ether_packet, packetsize); } /** diff --git a/roms/SLOF/clients/net-snk/app/netlib/dns.h b/roms/SLOF/clients/net-snk/app/netlib/dns.h index 6277418..82eea4e 100644 --- a/roms/SLOF/clients/net-snk/app/netlib/dns.h +++ b/roms/SLOF/clients/net-snk/app/netlib/dns.h @@ -20,7 +20,7 @@ extern int8_t dns_init(uint32_t _dns_server_ip, uint8_t _dns_server_ipv6[16], uint8_t ip_version); /* For given URL retrieves IPv4 from DNS-server. */ -extern int8_t dns_get_ip(int8_t * url, uint8_t * domain_ip, uint8_t ip_version); +extern int8_t dns_get_ip(int fd, int8_t * url, uint8_t * domain_ip, uint8_t ip_version); /* Handles DNS-packets, which are detected by receive_ether. */ extern int32_t handle_dns(uint8_t * packet, int32_t packetsize); diff --git a/roms/SLOF/clients/net-snk/app/netlib/ethernet.c b/roms/SLOF/clients/net-snk/app/netlib/ethernet.c index c52583c..bbfd6d1 100644 --- a/roms/SLOF/clients/net-snk/app/netlib/ethernet.c +++ b/roms/SLOF/clients/net-snk/app/netlib/ethernet.c @@ -103,16 +103,17 @@ is_multicast_mac(uint8_t * mac) { * Ethernet: Receives an ethernet-packet and handles it according to * Receive-handle diagram. * + * @param fd socket fd * @return ZERO - packet was handled or no packets received; * NON ZERO - error condition occurs. */ int32_t -receive_ether(void) { +receive_ether(int fd) { int32_t bytes_received; struct ethhdr * ethh; memset(ether_packet, 0, ETH_MTU_SIZE); - bytes_received = recv(0, ether_packet, ETH_MTU_SIZE, 0); + bytes_received = recv(fd, ether_packet, ETH_MTU_SIZE, 0); if (!bytes_received) // No messages return 0; @@ -130,15 +131,15 @@ receive_ether(void) { switch (htons(ethh -> type)) { case ETHERTYPE_IP: - return handle_ipv4((uint8_t*) (ethh + 1), + return handle_ipv4(fd, (uint8_t*) (ethh + 1), bytes_received - sizeof(struct ethhdr)); case ETHERTYPE_IPv6: - return handle_ipv6(ether_packet + sizeof(struct ethhdr), + return handle_ipv6(fd, ether_packet + sizeof(struct ethhdr), bytes_received - sizeof(struct ethhdr)); case ETHERTYPE_ARP: - return handle_arp((uint8_t*) (ethh + 1), + return handle_arp(fd, (uint8_t*) (ethh + 1), bytes_received - sizeof(struct ethhdr)); default: break; @@ -152,9 +153,9 @@ receive_ether(void) { * @return number of transmitted bytes */ int -send_ether(void* buffer, int len) +send_ether(int fd, void* buffer, int len) { - return send(0, buffer, len, 0); + return send(fd, buffer, len, 0); } /** diff --git a/roms/SLOF/clients/net-snk/app/netlib/ethernet.h b/roms/SLOF/clients/net-snk/app/netlib/ethernet.h index ce44906..e541c8f 100644 --- a/roms/SLOF/clients/net-snk/app/netlib/ethernet.h +++ b/roms/SLOF/clients/net-snk/app/netlib/ethernet.h @@ -35,10 +35,10 @@ extern void set_mac_address(const uint8_t * own_mac); extern const uint8_t * get_mac_address(void); /* Receives and handles packets, according to Receive-handle diagram */ -extern int32_t receive_ether(void); +extern int32_t receive_ether(int fd); /* Sends an ethernet frame. */ -extern int send_ether(void* buffer, int len); +extern int send_ether(int fd, void* buffer, int len); /* fills ethernet header */ extern void fill_ethhdr(uint8_t * packet, uint16_t eth_type, diff --git a/roms/SLOF/clients/net-snk/app/netlib/icmpv6.c b/roms/SLOF/clients/net-snk/app/netlib/icmpv6.c index 8cf0e2d..be6cc11 100644 --- a/roms/SLOF/clients/net-snk/app/netlib/icmpv6.c +++ b/roms/SLOF/clients/net-snk/app/netlib/icmpv6.c @@ -25,10 +25,10 @@ static int ra_received = 0; /** * NET: - * + * @param fd socket fd */ void -send_router_solicitation () +send_router_solicitation (int fd) { ip6_addr_t dest_addr; uint8_t ether_packet[ETH_MTU_SIZE]; @@ -58,7 +58,7 @@ send_router_solicitation () memcpy( &(headers.icmp6h->icmp6body.router_solicit.lladdr.mac), get_mac_address(), 6); - send_ip (headers.ip6h, sizeof(struct ip6hdr) + + send_ip (fd, headers.ip6h, sizeof(struct ip6hdr) + ICMPv6_HEADER_SIZE + sizeof(struct router_solicitation)); } @@ -194,10 +194,11 @@ int is_ra_received(void) /** * NET: * + * @param fd socket fd * @param ip6_addr_t *dest_ip6 */ void -send_neighbour_solicitation (ip6_addr_t *dest_ip6) +send_neighbour_solicitation (int fd, ip6_addr_t *dest_ip6) { ip6_addr_t snma; @@ -233,7 +234,7 @@ send_neighbour_solicitation (ip6_addr_t *dest_ip6) memcpy( &(headers.icmp6h->icmp6body.nghb_solicit.lladdr.mac), get_mac_address(), 6); - send_ip (ether_packet + sizeof(struct ethhdr), + send_ip (fd, ether_packet + sizeof(struct ethhdr), sizeof(struct ip6hdr) + ICMPv6_HEADER_SIZE + sizeof(struct neighbour_solicitation)); } @@ -241,12 +242,13 @@ send_neighbour_solicitation (ip6_addr_t *dest_ip6) /** * NET: * + * @param fd socket fd * @param ip6_packet pointer to an IPv6 packet * @param icmp6hdr pointer to the icmp6 header in ip6_packet * @param na_flags Neighbour advertisment flags */ static void -send_neighbour_advertisement (struct neighbor *target) +send_neighbour_advertisement (int fd, struct neighbor *target) { struct na_flags na_adv_flags; uint8_t ether_packet[ETH_MTU_SIZE]; @@ -297,7 +299,7 @@ send_neighbour_advertisement (struct neighbor *target) memcpy( &(headers.icmp6h->icmp6body.nghb_adv.lladdr.mac), get_mac_address(), 6); - send_ip (ether_packet + sizeof(struct ethhdr), + send_ip (fd, ether_packet + sizeof(struct ethhdr), sizeof(struct ip6hdr) + ICMPv6_HEADER_SIZE + sizeof(struct neighbour_advertisement)); } @@ -305,10 +307,11 @@ send_neighbour_advertisement (struct neighbor *target) /** * NET: * + * @param fd socket fd * @param ip6_packet pointer to an IPv6 packet */ static int8_t -handle_na (uint8_t *packet) +handle_na (int fd, uint8_t *packet) { struct neighbor *n = NULL; struct packeth headers; @@ -338,7 +341,7 @@ handle_na (uint8_t *packet) if (n->eth_len > 0) { struct ethhdr * ethh = (struct ethhdr *) &(n->eth_frame); memcpy(ethh->dest_mac, &(n->mac), 6); - send_ether (&(n->eth_frame), n->eth_len + sizeof(struct ethhdr)); + send_ether (fd, &(n->eth_frame), n->eth_len + sizeof(struct ethhdr)); n->eth_len = 0; } } @@ -349,11 +352,12 @@ handle_na (uint8_t *packet) /** * NET: Handles ICMPv6 messages * + * @param fd socket fd * @param ip6_packet pointer to an IPv6 packet * @param packetsize size of ipv6_packet */ int8_t -handle_icmpv6 (struct ethhdr *etherhdr, +handle_icmpv6 (int fd, struct ethhdr *etherhdr, uint8_t *ip6_packet) { @@ -371,10 +375,10 @@ handle_icmpv6 (struct ethhdr *etherhdr, /* process ICMPv6 types */ switch(received_icmp6->type) { case ICMPV6_NEIGHBOUR_SOLICITATION: - send_neighbour_advertisement( &target ); + send_neighbour_advertisement(fd, &target); break; case ICMPV6_NEIGHBOUR_ADVERTISEMENT: - handle_na((uint8_t *) ip6_packet - sizeof(struct ethhdr)); + handle_na(fd, (uint8_t *) ip6_packet - sizeof(struct ethhdr)); break; case ICMPV6_ROUTER_ADVERTISEMENT: handle_ra(received_icmp6, (uint8_t *) received_ip6); diff --git a/roms/SLOF/clients/net-snk/app/netlib/icmpv6.h b/roms/SLOF/clients/net-snk/app/netlib/icmpv6.h index 280df1f..3279797 100644 --- a/roms/SLOF/clients/net-snk/app/netlib/icmpv6.h +++ b/roms/SLOF/clients/net-snk/app/netlib/icmpv6.h @@ -47,9 +47,9 @@ #define ICMPV6_REDIRECT_MSG 137 /* Redirect message */ /******** Functions *******************/ -int8_t handle_icmpv6 (struct ethhdr *etherhdr, uint8_t *ip6_packet); -void send_neighbour_solicitation( ip6_addr_t *target_ip6); -void send_router_solicitation(void); +int8_t handle_icmpv6 (int fd, struct ethhdr *etherhdr, uint8_t *ip6_packet); +void send_neighbour_solicitation(int fd, ip6_addr_t *target_ip6); +void send_router_solicitation(int fd); int is_ra_received(void); /* Prefix information */ diff --git a/roms/SLOF/clients/net-snk/app/netlib/ipv4.c b/roms/SLOF/clients/net-snk/app/netlib/ipv4.c index 83293e8..8185de5 100644 --- a/roms/SLOF/clients/net-snk/app/netlib/ipv4.c +++ b/roms/SLOF/clients/net-snk/app/netlib/ipv4.c @@ -87,10 +87,10 @@ static unsigned short checksum(unsigned short *packet, int words); static void -arp_send_request(uint32_t dest_ip); +arp_send_request(int fd, uint32_t dest_ip); static void -arp_send_reply(uint32_t src_ip, uint8_t * src_mac); +arp_send_reply(int fd, uint32_t src_ip, uint8_t * src_mac); static void fill_arphdr(uint8_t * packet, uint8_t opcode, @@ -104,7 +104,7 @@ static void fill_udp_checksum(struct iphdr *ipv4_hdr); static int8_t -handle_icmp(struct iphdr * iph, uint8_t * packet, int32_t packetsize); +handle_icmp(int fd, struct iphdr * iph, uint8_t * packet, int32_t packetsize); /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>> LOCAL VARIABLES <<<<<<<<<<<<<<<<<<<<<<<<<*/ @@ -129,7 +129,7 @@ static arp_entry_t arp_table[ARP_ENTRIES]; static arp_entry_t pending_pkt; /* Function pointer send_ip. Points either to send_ipv4() or send_ipv6() */ -int (*send_ip) (void *, int); +int (*send_ip) (int fd, void *, int); /*>>>>>>>>>>>>>>>>>>>>>>>>>>>> IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<<*/ @@ -300,6 +300,7 @@ fill_iphdr(uint8_t * packet, uint16_t packetsize, /** * IPv4: Handles IPv4-packets according to Receive-handle diagram. * + * @param fd socket fd * @param ip_packet IP-packet to be handled * @param packetsize Length of the packet * @return ZERO - packet handled successfully; @@ -308,7 +309,7 @@ fill_iphdr(uint8_t * packet, uint16_t packetsize, * @see iphdr */ int8_t -handle_ipv4(uint8_t * ip_packet, int32_t packetsize) +handle_ipv4(int fd, uint8_t * ip_packet, int32_t packetsize) { struct iphdr * iph; int32_t old_sum; @@ -377,10 +378,10 @@ handle_ipv4(uint8_t * ip_packet, int32_t packetsize) switch (iph -> ip_p) { case IPTYPE_ICMP: - return handle_icmp(iph, ip_packet + sizeof(struct iphdr), + return handle_icmp(fd, iph, ip_packet + sizeof(struct iphdr), iph -> ip_len - sizeof(struct iphdr)); case IPTYPE_UDP: - return handle_udp(ip_packet + sizeof(struct iphdr), + return handle_udp(fd, ip_packet + sizeof(struct iphdr), iph -> ip_len - sizeof(struct iphdr)); case IPTYPE_TCP: return handle_tcp(ip_packet + sizeof(struct iphdr), @@ -409,6 +410,7 @@ handle_ipv4(uint8_t * ip_packet, int32_t packetsize) * If there is already an ARP request pending, then we drop this packet * and send again an ARP request. * + * @param fd socket fd * @param ip_packet IP-packet to be handled * @param packetsize Length of the packet * @return -2 - packet dropped (MAC address not resolved - ARP request pending) @@ -421,11 +423,12 @@ handle_ipv4(uint8_t * ip_packet, int32_t packetsize) * @see iphdr */ int -send_ipv4(void* buffer, int len) +send_ipv4(int fd, void* buffer, int len) { arp_entry_t *arp_entry = 0; struct iphdr *ip; const uint8_t *mac_addr = 0; + uint32_t ip_dst = 0; if(len + sizeof(struct ethhdr) > ETH_MTU_SIZE) return -1; @@ -457,6 +460,7 @@ send_ipv4(void* buffer, int len) fill_udp_checksum(ip); } + ip_dst = ip->ip_dst; // Check if the MAC address is already cached if(~ip->ip_dst == 0 || ( ((~subnet_mask) & ip->ip_dst) == ~subnet_mask && @@ -473,8 +477,10 @@ send_ipv4(void* buffer, int len) if((subnet_mask & own_ip) == (subnet_mask & ip->ip_dst)) arp_entry = lookup_mac_addr(ip->ip_dst); // if not then we need to know the router's IP address - else + else { + ip_dst = router_ip; arp_entry = lookup_mac_addr(router_ip); + } if(arp_entry && memcmp(arp_entry->mac_addr, null_mac_addr, 6) != 0) mac_addr = arp_entry->mac_addr; } @@ -482,7 +488,7 @@ send_ipv4(void* buffer, int len) // If we could not resolv the MAC address by our own... if(!mac_addr) { // send the ARP request - arp_send_request(ip->ip_dst); + arp_send_request(fd, ip_dst); // drop the current packet if there is already a ARP request pending if(arp_entry) @@ -498,9 +504,9 @@ send_ipv4(void* buffer, int len) // store the packet to be send if the ARP reply is received arp_entry->pkt_pending = 1; - arp_entry->ipv4_addr = ip->ip_dst; + arp_entry->ipv4_addr = ip_dst; memset(arp_entry->mac_addr, 0, 6); - pending_pkt.ipv4_addr = ip->ip_dst; + pending_pkt.ipv4_addr = ip_dst; memset(pending_pkt.mac_addr, 0, 6); fill_ethhdr (pending_pkt.eth_frame, htons(ETHERTYPE_IP), get_mac_address(), null_mac_addr); @@ -510,7 +516,7 @@ send_ipv4(void* buffer, int len) set_timer(TICKS_SEC); do { - receive_ether(); + receive_ether(fd); if (!arp_entry->eth_len) break; } while (get_timer() > 0); @@ -522,7 +528,7 @@ send_ipv4(void* buffer, int len) fill_ethhdr(arp_entry->eth_frame, htons(ETHERTYPE_IP), get_mac_address(), mac_addr); memcpy(&arp_entry->eth_frame[sizeof(struct ethhdr)], buffer, len); - return send_ether(arp_entry->eth_frame, len + sizeof(struct ethhdr)); + return send_ether(fd, arp_entry->eth_frame, len + sizeof(struct ethhdr)); } /** @@ -609,10 +615,11 @@ lookup_mac_addr(uint32_t ipv4_addr) * ARP: Sends an ARP-request package. * For given IPv4 retrieves MAC via ARP (makes several attempts) * + * @param fd socket fd * @param dest_ip IP of the host which MAC should be obtained */ static void -arp_send_request(uint32_t dest_ip) +arp_send_request(int fd, uint32_t dest_ip) { arp_entry_t *arp_entry = &arp_table[arp_producer]; @@ -622,7 +629,7 @@ arp_send_request(uint32_t dest_ip) fill_ethhdr(arp_entry->eth_frame, ETHERTYPE_ARP, get_mac_address(), broadcast_mac); - send_ether(arp_entry->eth_frame, + send_ether(fd, arp_entry->eth_frame, sizeof(struct ethhdr) + sizeof(struct arphdr)); } @@ -631,11 +638,12 @@ arp_send_request(uint32_t dest_ip) * This package is used to serve foreign requests (in case IP in * foreign request matches our host IP). * + * @param fd socket fd * @param src_ip requester IP address (foreign IP) * @param src_mac requester MAC address (foreign MAC) */ static void -arp_send_reply(uint32_t src_ip, uint8_t * src_mac) +arp_send_reply(int fd, uint32_t src_ip, uint8_t * src_mac) { arp_entry_t *arp_entry = &arp_table[arp_producer]; @@ -645,7 +653,7 @@ arp_send_reply(uint32_t src_ip, uint8_t * src_mac) fill_arphdr(&arp_entry->eth_frame[sizeof(struct ethhdr)], ARP_REPLY, get_mac_address(), own_ip, src_mac, src_ip); - send_ether(arp_entry->eth_frame, + send_ether(fd, arp_entry->eth_frame, sizeof(struct ethhdr) + sizeof(struct arphdr)); } @@ -689,6 +697,7 @@ fill_arphdr(uint8_t * packet, uint8_t opcode, * ARP: Handles ARP-messages according to Receive-handle diagram. * Updates arp_table for outstanding ARP requests (see arp_getmac). * + * @param fd socket fd * @param packet ARP-packet to be handled * @param packetsize length of the packet * @return ZERO - packet handled successfully; @@ -698,7 +707,7 @@ fill_arphdr(uint8_t * packet, uint8_t opcode, * @see arphdr */ int8_t -handle_arp(uint8_t * packet, int32_t packetsize) +handle_arp(int fd, uint8_t * packet, int32_t packetsize) { struct arphdr * arph = (struct arphdr *) packet; @@ -715,7 +724,7 @@ handle_arp(uint8_t * packet, int32_t packetsize) case ARP_REQUEST: // foreign request if(own_ip != 0) - arp_send_reply(htonl(arph->src_ip), arph -> src_mac); + arp_send_reply(fd, htonl(arph->src_ip), arph -> src_mac); return 0; // no error case ARP_REPLY: { unsigned int i; @@ -748,7 +757,7 @@ handle_arp(uint8_t * packet, int32_t packetsize) struct ethhdr * ethh = (struct ethhdr *) pending_pkt.eth_frame; memcpy(ethh -> dest_mac, arp_table[i].mac_addr, 6); - send_ether(pending_pkt.eth_frame, pending_pkt.eth_len); + send_ether(fd, pending_pkt.eth_frame, pending_pkt.eth_len); pending_pkt.pkt_pending = 0; arp_table[i].eth_len = 0; } @@ -768,10 +777,11 @@ handle_arp(uint8_t * packet, int32_t packetsize) * In other words, reading a value of 0 form this variable * means that an answer to the request has been arrived. * + * @param fd socket descriptor * @param _ping_dst_ip destination IPv4 address */ void -ping_ipv4(uint32_t _ping_dst_ip) +ping_ipv4(int fd, uint32_t _ping_dst_ip) { unsigned char packet[sizeof(struct iphdr) + sizeof(struct icmphdr)]; struct icmphdr *icmp; @@ -794,7 +804,7 @@ ping_ipv4(uint32_t _ping_dst_ip) icmp->checksum = checksum((unsigned short *) icmp, sizeof(struct icmphdr) >> 1); - send_ipv4(packet, sizeof(struct iphdr) + sizeof(struct icmphdr)); + send_ipv4(fd, packet, sizeof(struct iphdr) + sizeof(struct icmphdr)); } /** @@ -813,6 +823,7 @@ pong_ipv4(void) /** * ICMP: Handles ICMP-packets according to Receive-handle diagram. * + * @param fd socket fd * @param icmp_packet ICMP-packet to be handled * @param packetsize Length of the packet * @return ZERO - packet handled successfully; @@ -820,7 +831,7 @@ pong_ipv4(void) * @see handle_ipv4 */ static int8_t -handle_icmp(struct iphdr * iph, uint8_t * packet, int32_t packetsize) +handle_icmp(int fd, struct iphdr * iph, uint8_t * packet, int32_t packetsize) { struct icmphdr *icmp = (struct icmphdr *) packet; @@ -873,7 +884,7 @@ handle_icmp(struct iphdr * iph, uint8_t * packet, int32_t packetsize) reply_icmph->checksum = checksum((unsigned short *) reply_icmph, sizeof(struct icmphdr) >> 1); - send_ipv4(reply_packet, sizeof(struct iphdr) + packetsize); + send_ipv4(fd, reply_packet, sizeof(struct iphdr) + packetsize); break; } case ICMP_TIME_EXCEEDED: diff --git a/roms/SLOF/clients/net-snk/app/netlib/ipv4.h b/roms/SLOF/clients/net-snk/app/netlib/ipv4.h index fd104cf..eb719f8 100644 --- a/roms/SLOF/clients/net-snk/app/netlib/ipv4.h +++ b/roms/SLOF/clients/net-snk/app/netlib/ipv4.h @@ -70,7 +70,7 @@ extern uint32_t get_ipv4_router(void); extern void set_ipv4_netmask(uint32_t subnet_mask); extern uint32_t get_ipv4_netmask(void); -extern int (*send_ip) (void *, int); +extern int (*send_ip) (int fd, void *, int); /* fills ip header */ extern void fill_iphdr(uint8_t * packet, uint16_t packetsize, @@ -79,18 +79,18 @@ extern void fill_iphdr(uint8_t * packet, uint16_t packetsize, /* Send a IPv4 packet. Adding the Ethernet-Header and resolving the * MAC address is done transparent in the background if necessary. */ -extern int send_ipv4(void* buffer, int len); +extern int send_ipv4(int fd, void* buffer, int len); /* Sends an ICMP Echo request to destination IPv4 address */ -extern void ping_ipv4(uint32_t _ping_dst_ip); +extern void ping_ipv4(int fd, uint32_t _ping_dst_ip); /* Returns host IPv4 address that we are waiting for a response */ extern uint32_t pong_ipv4(void); /* Handles IPv4-packets that are detected by receive_ether. */ -extern int8_t handle_ipv4(uint8_t * packet, int32_t packetsize); +extern int8_t handle_ipv4(int fd, uint8_t * packet, int32_t packetsize); /* Handles ARP-packets that are detected by receive_ether. */ -extern int8_t handle_arp(uint8_t * packet, int32_t packetsize); +extern int8_t handle_arp(int fd, uint8_t * packet, int32_t packetsize); #endif diff --git a/roms/SLOF/clients/net-snk/app/netlib/ipv6.c b/roms/SLOF/clients/net-snk/app/netlib/ipv6.c index 109598b..0cb0a2e 100644 --- a/roms/SLOF/clients/net-snk/app/netlib/ipv6.c +++ b/roms/SLOF/clients/net-snk/app/netlib/ipv6.c @@ -32,7 +32,7 @@ /****************************** PROTOTYPES *******************************/ int8_t ip6addr_add (struct ip6addr_list_entry *new_address); -static void ipv6_init(void); +static void ipv6_init(int fd); static int ip6_is_multicast (ip6_addr_t * ip); /****************************** LOCAL VARIABLES **************************/ @@ -52,12 +52,12 @@ static uint8_t null_mac[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; /** * IPv6: Set the own IPv6 address. * + * @param fd Socket descriptor * @param _own_ip client IPv6 address (e.g. ::1) */ void -set_ipv6_address (ip6_addr_t *_own_ip6) +set_ipv6_address (int fd, ip6_addr_t *_own_ip6) { - own_ip6 = malloc (sizeof(struct ip6addr_list_entry)); /* If no address was passed as a parameter generate a link-local @@ -72,7 +72,7 @@ set_ipv6_address (ip6_addr_t *_own_ip6) /* Add to our list of IPv6 addresses */ ip6addr_add (own_ip6); - ipv6_init(); + ipv6_init(fd); } /** @@ -110,6 +110,7 @@ find_ip6addr (ip6_addr_t *ip) /** * NET: Handles IPv6-packets * + * @param fd - Socket descriptor * @param ip6_packet - Pointer to IPv6 header * @param packetsize - Size of Ipv6 packet * @return ERROR - -1 if packet is too small or unknown protocol @@ -119,7 +120,7 @@ find_ip6addr (ip6_addr_t *ip) * @see ip6hdr */ int8_t -handle_ipv6 (uint8_t * ip6_packet, int32_t packetsize) +handle_ipv6 (int fd, uint8_t * ip6_packet, int32_t packetsize) { struct ip6hdr *ip6 = NULL; @@ -134,10 +135,10 @@ handle_ipv6 (uint8_t * ip6_packet, int32_t packetsize) switch (ip6->nh) { case IPTYPE_UDP: - return handle_udp (ip6_packet + sizeof (struct ip6hdr), + return handle_udp (fd, ip6_packet + sizeof (struct ip6hdr), ip6->pl); case IPTYPE_ICMPV6: - return handle_icmpv6 ((struct ethhdr *) ip6_packet - sizeof(struct ethhdr), + return handle_icmpv6 (fd, (struct ethhdr *) ip6_packet - sizeof(struct ethhdr), ip6_packet); } @@ -329,9 +330,10 @@ ip6addr_add (struct ip6addr_list_entry *new_address) /** * NET: Initialize IPv6 * + * @param fd socket fd */ static void -ipv6_init () +ipv6_init (int fd) { int i = 0; @@ -363,11 +365,11 @@ ipv6_init () first_neighbor = NULL; last_neighbor = first_neighbor; - send_router_solicitation (); + send_router_solicitation (fd); for(i=0; i < 4 && !is_ra_received(); i++) { set_timer(TICKS_SEC); do { - receive_ether(); + receive_ether(fd); if (is_ra_received()) break; } while (get_timer() > 0); @@ -467,6 +469,7 @@ ip6_checksum (struct ip6hdr *ip6h, unsigned short *packet, int words) /** * NET: Handles IPv6-packets * + * @param fd socket fd * @param ip6_packet Pointer to IPv6 header in packet * @param packetsize Size of IPv6 packet * @return -1 == ERRROR @@ -476,7 +479,7 @@ ip6_checksum (struct ip6hdr *ip6h, unsigned short *packet, int words) * @see ip6hdr */ int -send_ipv6 (void* buffer, int len) +send_ipv6 (int fd, void* buffer, int len) { struct neighbor *n; struct ip6hdr *ip6h; @@ -544,7 +547,7 @@ send_ipv6 (void* buffer, int len) if (! memcmp (mac_addr, &null_mac, 6)) { if (n->eth_len == 0) { - send_neighbour_solicitation (&ip_dst); + send_neighbour_solicitation (fd, &ip_dst); // Store the packet until we know the MAC address memset(n->eth_frame, 0, 1500); @@ -557,7 +560,7 @@ send_ipv6 (void* buffer, int len) n->eth_len = len; set_timer(TICKS_SEC); do { - receive_ether(); + receive_ether(fd); } while (get_timer() > 0); } } @@ -566,7 +569,7 @@ send_ipv6 (void* buffer, int len) fill_ethhdr (n->eth_frame, htons(ETHERTYPE_IPv6), get_mac_address(), mac_addr); memcpy (&(n->eth_frame[sizeof(struct ethhdr)]), buffer, len); - return send_ether (n->eth_frame, len + sizeof(struct ethhdr)); + return send_ether (fd, n->eth_frame, len + sizeof(struct ethhdr)); } static int diff --git a/roms/SLOF/clients/net-snk/app/netlib/ipv6.h b/roms/SLOF/clients/net-snk/app/netlib/ipv6.h index 82f2cb0..b496364 100644 --- a/roms/SLOF/clients/net-snk/app/netlib/ipv6.h +++ b/roms/SLOF/clients/net-snk/app/netlib/ipv6.h @@ -130,7 +130,7 @@ struct ip6_config { /******************** VARIABLES **********************************************/ /* Function pointer send_ip. Points either to send_ipv4() or send_ipv6() */ -extern int (*send_ip) (void *, int); +extern int (*send_ip) (int fd, void *, int); /* IPv6 link-local multicast addresses */ struct ip6addr_list_entry all_routers_ll; // Routers @@ -151,14 +151,14 @@ struct router *last_router; /******************** FUNCTIONS *********************************************/ /* Handles IPv6-packets that are detected by receive_ether. */ -int8_t handle_ipv6(uint8_t * ip6_packet, int32_t packetsize); +int8_t handle_ipv6(int fd, uint8_t * ip6_packet, int32_t packetsize); /* Fill IPv6 header */ void fill_ip6hdr(uint8_t * packet, uint16_t packetsize, uint8_t ip_proto, ip6_addr_t *ip6_src, ip6_addr_t *ip6_dst); /* Set own IPv6 address */ -void set_ipv6_address(ip6_addr_t *own_ip6); +void set_ipv6_address(int fd, ip6_addr_t *own_ip6); /* Get own IPv6 address */ ip6_addr_t *get_ipv6_address(void); @@ -183,7 +183,7 @@ int8_t ip6_cmp( ip6_addr_t *ip_1, ip6_addr_t *ip_2 ); int8_t unknown_prefix (ip6_addr_t *ip); /* Send IPv6 packet */ -int send_ipv6 (void* buffer, int len); +int send_ipv6 (int fd, void* buffer, int len); /* Add IPv6 address to list */ int8_t ip6addr_add (struct ip6addr_list_entry *new_address); diff --git a/roms/SLOF/clients/net-snk/app/netlib/tftp.c b/roms/SLOF/clients/net-snk/app/netlib/tftp.c index 8a39a15..0a7c0ec 100644 --- a/roms/SLOF/clients/net-snk/app/netlib/tftp.c +++ b/roms/SLOF/clients/net-snk/app/netlib/tftp.c @@ -86,9 +86,11 @@ dump_package(unsigned char *buffer, unsigned int len) /** * send_rrq - Sends a read request package. + * + * @fd: Socket Descriptor */ static void -send_rrq(void) +send_rrq(int fd) { int ip_len = 0; int ip6_payload_len = 0; @@ -142,7 +144,7 @@ send_rrq(void) ptr += strlen("blksize") + 1; memcpy(ptr, blocksize_str, strlen(blocksize_str) + 1); - send_ip (packet, ip_len); + send_ip (fd, packet, ip_len); #ifdef __DEBUG__ printf("tftp RRQ with %d bytes transmitted.\n", ip_len); @@ -157,7 +159,7 @@ send_rrq(void) * @dport: UDP destination port */ static void -send_ack(int blckno, unsigned short dport) +send_ack(int fd, int blckno, unsigned short dport) { int ip_len = 0; int ip6_payload_len = 0; @@ -192,7 +194,7 @@ send_ack(int blckno, unsigned short dport) tftp->th_opcode = htons(ACK); tftp->th_data = htons(blckno); - send_ip(packet, ip_len); + send_ip(fd, packet, ip_len); #ifdef __DEBUG__ printf("tftp ACK %d bytes transmitted.\n", ip_len); @@ -204,11 +206,12 @@ send_ack(int blckno, unsigned short dport) /** * send_error - Sends an error package. * + * @fd: Socket Descriptor * @error_code: Used sub code for error packet * @dport: UDP destination port */ static void -send_error(int error_code, unsigned short dport) +send_error(int fd, int error_code, unsigned short dport) { int ip_len = 0; int ip6_payload_len = 0; @@ -244,7 +247,7 @@ send_error(int error_code, unsigned short dport) tftp->th_data = htons(error_code); ((char *) &tftp->th_data)[2] = 0; - send_ip(packet, ip_len); + send_ip(fd, packet, ip_len); #ifdef __DEBUG__ printf("tftp ERROR %d bytes transmitted.\n", ip_len); @@ -330,13 +333,14 @@ get_blksize(unsigned char *buffer, unsigned int len) * I for an ICMP packet * #+* for different unexpected TFTP packets (not very good) * + * @param fd socket descriptor * @param packet points to the UDP header of the packet * @param len the length of the network packet * @return ZERO if packet was handled successfully * ERRORCODE if error occurred */ int32_t -handle_tftp(uint8_t *pkt, int32_t packetsize) +handle_tftp(int fd, uint8_t *pkt, int32_t packetsize) { struct udphdr *udph; struct tftphdr *tftp; @@ -361,17 +365,17 @@ handle_tftp(uint8_t *pkt, int32_t packetsize) /* an OACK means that the server answers our blocksize request */ blocksize = get_blksize(pkt, packetsize); if (!blocksize || blocksize > MAX_BLOCKSIZE) { - send_error(8, port_number); + send_error(fd, 8, port_number); tftp_errno = -8; goto error; } - send_ack(0, port_number); + send_ack(fd, 0, port_number); } else if (tftp->th_opcode == htons(ACK)) { /* an ACK means that the server did not answers * our blocksize request, therefore we will set the blocksize * to the default value of 512 */ blocksize = 512; - send_ack(0, port_number); + send_ack(fd, 0, port_number); } else if ((unsigned char) tftp->th_opcode == ERROR) { #ifdef __DEBUG__ printf("tftp->th_opcode : %x\n", tftp->th_opcode); @@ -413,7 +417,7 @@ handle_tftp(uint8_t *pkt, int32_t packetsize) tftp->th_data, block + 1); printf("\b+ "); #endif - send_ack(tftp->th_data, port_number); + send_ack(fd, tftp->th_data, port_number); lost_packets++; tftp_err->bad_tftp_packets++; return 0; @@ -444,7 +448,7 @@ handle_tftp(uint8_t *pkt, int32_t packetsize) } memcpy(buffer + received_len, &tftp->th_data + 1, udph->uh_ulen - 12); - send_ack(tftp->th_data, port_number); + send_ack(fd, tftp->th_data, port_number); received_len += udph->uh_ulen - 12; /* Last packet reached if the payload of the UDP packet * is smaller than blocksize + 12 @@ -539,7 +543,7 @@ tftp(filename_ip_t * _fn_ip, unsigned char *_buffer, int _len, buffer = _buffer; set_timer(TICKS_SEC); - send_rrq(); + send_rrq(fn_ip->fd); while (! tftp_finished) { /* if timeout (no packet received) */ @@ -547,19 +551,19 @@ tftp(filename_ip_t * _fn_ip, unsigned char *_buffer, int _len, /* the server doesn't seem to retry let's help out a bit */ if (tftp_err->no_packets > 4 && port_number != -1 && block > 1) { - send_ack(block, port_number); + send_ack(fn_ip->fd, block, port_number); } else if (port_number == -1 && block == 0 && (tftp_err->no_packets&3) == 3) { printf("\nRepeating TFTP read request...\n"); - send_rrq(); + send_rrq(fn_ip->fd); } tftp_err->no_packets++; set_timer(TICKS_SEC); } /* handle received packets */ - receive_ether(); + receive_ether(fn_ip->fd); /* bad_tftp_packets are counted whenever we receive a TFTP packet * which was not expected; if this gets larger than 'retries' diff --git a/roms/SLOF/clients/net-snk/app/netlib/tftp.h b/roms/SLOF/clients/net-snk/app/netlib/tftp.h index a5ae948..1cf1266 100644 --- a/roms/SLOF/clients/net-snk/app/netlib/tftp.h +++ b/roms/SLOF/clients/net-snk/app/netlib/tftp.h @@ -29,6 +29,7 @@ typedef struct { ip6_addr_t server_ip6; ip6_addr_t dns_ip6; int8_t filename[256]; + int fd; } __attribute__ ((packed)) filename_ip_t ; typedef struct { @@ -43,8 +44,8 @@ int tftp(filename_ip_t *, unsigned char *, int, unsigned int, int tftp_netsave(filename_ip_t *, uint8_t * buffer, int len, int use_ci, unsigned int retries, tftp_err_t * tftp_err); -int32_t handle_tftp(uint8_t *, int32_t); +int32_t handle_tftp(int fd, uint8_t *, int32_t); void handle_tftp_dun(uint8_t err_code); -int parse_tftp_args(char buffer[], char *server_ip, char filename[], int len); +int parse_tftp_args(char buffer[], char *server_ip, char filename[], int fd, int len); #endif diff --git a/roms/SLOF/clients/net-snk/app/netlib/udp.c b/roms/SLOF/clients/net-snk/app/netlib/udp.c index 134a29e..db29bc9 100644 --- a/roms/SLOF/clients/net-snk/app/netlib/udp.c +++ b/roms/SLOF/clients/net-snk/app/netlib/udp.c @@ -57,7 +57,7 @@ void net_set_mtftp_port(uint16_t tftp_port) { * @see udphdr */ int8_t -handle_udp(uint8_t * udp_packet, int32_t packetsize) { +handle_udp(int fd, uint8_t * udp_packet, int32_t packetsize) { struct udphdr * udph = (struct udphdr *) udp_packet; if (packetsize < sizeof(struct udphdr)) @@ -66,7 +66,7 @@ handle_udp(uint8_t * udp_packet, int32_t packetsize) { switch (htons(udph -> uh_dport)) { case UDPPORT_BOOTPC: if (udph -> uh_sport == htons(UDPPORT_BOOTPS)) - return handle_dhcp(udp_packet + sizeof(struct udphdr), + return handle_dhcp(fd, udp_packet + sizeof(struct udphdr), packetsize - sizeof(struct udphdr)); else return -1; @@ -81,18 +81,18 @@ handle_udp(uint8_t * udp_packet, int32_t packetsize) { packetsize - sizeof(struct udphdr)); case UDPPORT_TFTPC: #ifdef USE_MTFTP - return handle_tftp(udp_packet + sizeof(struct udphdr), + return handle_tftp(fd, udp_packet + sizeof(struct udphdr), packetsize - sizeof(struct udphdr)); #else - return handle_tftp(udp_packet, packetsize); + return handle_tftp(fd, udp_packet, packetsize); #endif default: #ifdef USE_MTFTP if (htons(udph -> uh_dport) == net_tftp_uport) - return handle_tftp(udp_packet + sizeof(struct udphdr), + return handle_tftp(fd, udp_packet + sizeof(struct udphdr), packetsize - sizeof(struct udphdr)); else if (htons(udph -> uh_dport) == net_mtftp_uport) - return handle_tftp(udp_packet + sizeof(struct udphdr), + return handle_tftp(fd, udp_packet + sizeof(struct udphdr), packetsize - sizeof(struct udphdr)); #endif return -1; diff --git a/roms/SLOF/clients/net-snk/app/netlib/udp.h b/roms/SLOF/clients/net-snk/app/netlib/udp.h index 8acc0bc..1ba9332 100644 --- a/roms/SLOF/clients/net-snk/app/netlib/udp.h +++ b/roms/SLOF/clients/net-snk/app/netlib/udp.h @@ -40,7 +40,7 @@ typedef int32_t *(*handle_upper_udp_t)(uint8_t *, int32_t); typedef void *(*handle_upper_udp_dun_t)(uint8_t); /* Handles UDP-packets that are detected by any network layer. */ -extern int8_t handle_udp(uint8_t * udp_packet, int32_t packetsize); +extern int8_t handle_udp(int fd, uint8_t * udp_packet, int32_t packetsize); /* Handles UDP related ICMP-Dest.Unreachable packets that are detected by * the network layers. */ diff --git a/roms/SLOF/clients/net-snk/client.lds b/roms/SLOF/clients/net-snk/client.lds index 2ebf9d1..39d0459 100644 --- a/roms/SLOF/clients/net-snk/client.lds +++ b/roms/SLOF/clients/net-snk/client.lds @@ -15,37 +15,71 @@ OUTPUT_ARCH(powerpc:common64) ENTRY(_entry) SECTIONS { - .client 0xF000100: - { - __client_start = .; - *(.text .stub .text.* .gnu.linkonce.t.*) + . = 0xF000100; + .text : + { + __client_start = .; + *(.text* .stub .gnu.linkonce.t.*) *(.sfpr .glink) - *(.rodata .rodata.* .gnu.linkonce.r.*) - KEEP (*(.opd)) - . = ALIGN(256); - *(.data .data.* .gnu.linkonce.d.*) - . = ALIGN(256); - } =0x60000000 - - .lowmem : + } + + . = ALIGN(0x100); + .rodata : + { + *(.rodata* .gnu.linkonce.r.*) + } + + . = ALIGN(0x10); + .data : + { + *(.data* .gnu.linkonce.d.*) + *(.force.data) + *(.toc1) + *(.branch_lt) + } + + . = ALIGN(0x10); + .opd : { - _lowmem_start = .; - *(.lowmem) - _lowmem_end = .; + *(.opd) } + . = ALIGN(0x10); .got : { - . = ALIGN(8); _got = .; - *(.got .toc) + *(.got) + *(.toc) _got_end = .; - } - .comment : { *(.comment) } - .branch_lt : { *(.branch_lt) } + } + + . = ALIGN(0x1000); .bss : { - *(*COM* .bss .gnu.linkonce.b.*) + *(*COM* .bss* .gnu.linkonce.b.*) + } __client_end = .; + + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + . = ALIGN(0x10); + .dynamic : { + *(.dynamic) + } + . = ALIGN(0x10); + .rela.dyn : { + *(.rela*) + } + .hash : { *(.hash) } + + .comment : { + /* + * Discarding this section caused errors on binutils 2.23, + * this is fixed in 2.24. + */ + *(.comment) + } + /DISCARD/ : { + *(.interp) } } diff --git a/roms/SLOF/clients/net-snk/include/fileio.h b/roms/SLOF/clients/net-snk/include/fileio.h index 7c9a2b5..50f9650 100644 --- a/roms/SLOF/clients/net-snk/include/fileio.h +++ b/roms/SLOF/clients/net-snk/include/fileio.h @@ -12,40 +12,18 @@ #ifndef FILEIO_H #define FILEIO_H -#include -struct snk_fileio_type; -typedef struct snk_fileio_type snk_fileio_t; - -#define FILEIO_TYPE_EMPTY 0 -#define FILEIO_TYPE_USED 1 +#include -typedef long (*fileio_read_t) - (snk_fileio_t *fileio, char *buf, long len); -typedef long (*fileio_write_t) - (snk_fileio_t *fileio, char *buf, long len); -typedef int (*fileio_ioctl_t) - (snk_fileio_t *fileio, int request, void *data); -typedef int (*fileio_bind_t) - (snk_fileio_t *fileio, const struct sockaddr *, long len); -typedef int (*fileio_connect_t) - (snk_fileio_t *fileio, const struct sockaddr *, long len); -typedef int (*fileio_close_t) - (snk_fileio_t *fileio); +#define FILEIO_TYPE_EMPTY 0 +#define FILEIO_TYPE_FILE 1 +#define FILEIO_TYPE_SOCKET 2 struct snk_fileio_type { - int type; - int idx; - - fileio_read_t read; - fileio_write_t write; - fileio_ioctl_t ioctl; - fileio_bind_t bind; - fileio_connect_t connect; - fileio_close_t close; - - void *data; + int type; + ihandle_t ih; }; +typedef struct snk_fileio_type snk_fileio_t; #define FILEIO_MAX 32 extern snk_fileio_t fd_array[FILEIO_MAX]; diff --git a/roms/SLOF/clients/net-snk/include/ioctl.h b/roms/SLOF/clients/net-snk/include/ioctl.h deleted file mode 100644 index c993798..0000000 --- a/roms/SLOF/clients/net-snk/include/ioctl.h +++ /dev/null @@ -1,19 +0,0 @@ -/****************************************************************************** - * Copyright (c) 2004, 2008 IBM Corporation - * All rights reserved. - * This program and the accompanying materials - * are made available under the terms of the BSD License - * which accompanies this distribution, and is available at - * http://www.opensource.org/licenses/bsd-license.php - * - * Contributors: - * IBM Corporation - initial implementation - *****************************************************************************/ - - -#ifndef _IOCTL_H -#define _IOCTL_H - -extern int ioctl(int fd, int request, void *data); - -#endif diff --git a/roms/SLOF/clients/net-snk/include/kernel.h b/roms/SLOF/clients/net-snk/include/kernel.h index 3fd4ea8..4d6be2d 100644 --- a/roms/SLOF/clients/net-snk/include/kernel.h +++ b/roms/SLOF/clients/net-snk/include/kernel.h @@ -15,21 +15,18 @@ #include #include - -int printk(const char *, ...); -void *memcpy(void *, const void *, size_t); -void *memset(void *, int, size_t); +#include +#include +#include +#include uint64_t get_time(void); -void udelay(unsigned int); -void mdelay(unsigned int); int getchar(void); -int strcmp(const char *, const char *); -char *strcpy(char *, const char *); -int printf(const char *, ...); void *malloc_aligned(size_t size, int align); +int pre_open_ih(int fd, ihandle_t ih); + void exception_forward(void); void undo_exception(void); diff --git a/roms/SLOF/clients/net-snk/include/netdriver_int.h b/roms/SLOF/clients/net-snk/include/netdriver_int.h deleted file mode 100644 index 7612f59..0000000 --- a/roms/SLOF/clients/net-snk/include/netdriver_int.h +++ /dev/null @@ -1,191 +0,0 @@ -/****************************************************************************** - * Copyright (c) 2004, 2008 IBM Corporation - * All rights reserved. - * This program and the accompanying materials - * are made available under the terms of the BSD License - * which accompanies this distribution, and is available at - * http://www.opensource.org/licenses/bsd-license.php - * - * Contributors: - * IBM Corporation - initial implementation - *****************************************************************************/ - -#ifndef _NETDRIVER_INT_H -#define _NETDRIVER_INT_H -#include -#include /* ssize_t */ -#include - -#if defined(__GNUC__) && !defined(UNUSED) -# define UNUSED __attribute__((unused)) -#else -# define UNUSED -#endif - -typedef struct { - unsigned int addr; - unsigned int size; - int type; -} bar_t; - -typedef enum { - CONFIG_TYPE_PCI, - CONFIG_TYPE_VIO -} mod_config_type; - -typedef struct { - mod_config_type config_type; - unsigned long long puid; - unsigned int bus; - unsigned int devfn; - unsigned int vendor_id; - unsigned int device_id; - unsigned int revision_id; - unsigned int class_code; - bar_t bars[6]; - unsigned int interrupt_line; -} pci_config_t; - -typedef struct { - mod_config_type config_type; - unsigned int reg_len; - unsigned int reg[12]; - char compat[64]; -} vio_config_t; - -#define MOD_TYPE_NETWORK 0 -#define MOD_TYPE_OTHER 1 - -typedef int (*mod_init_t) (void); -typedef int (*mod_term_t) (void); -typedef int (*mod_socket_t)(snk_fileio_t *, int dom, int type, int proto); -typedef int (*mod_open_t) (snk_fileio_t *, const char *, int); -typedef int (*mod_read_t) (char *, int); -typedef int (*mod_write_t) (char *, int); -typedef int (*mod_ioctl_t) (int, void *); - -typedef struct { - int version; - int type; - int running; - void *link_addr; - mod_init_t init; - mod_term_t term; - mod_socket_t socket; - mod_open_t open; - mod_read_t read; - mod_write_t write; - mod_ioctl_t ioctl; - - char mac_addr[6]; -} snk_module_t; - -#define MODULES_MAX 10 -extern snk_module_t *snk_modules[MODULES_MAX]; - -typedef int (*print_t) (const char *, ...); -typedef void (*us_delay_t) (unsigned int); -typedef void (*ms_delay_t) (unsigned int); -typedef int (*pci_config_read_t) (long long puid, int size, - int bus, int devfn, int offset); -typedef int (*pci_config_write_t) (long long puid, int size, - int bus, int devfn, int offset, int value); -typedef void *(*malloc_aligned_t) (size_t, int); -typedef void *(*malloc_t) (size_t); -typedef void (*free_t) (void *); -typedef int (*strcmp_t) (const char *, const char *); -typedef int (*snk_call_t) (int, char **); -typedef unsigned int (*io_read_t) (void *, size_t); -typedef int (*io_write_t) (void *, unsigned int, size_t); -typedef unsigned int (*romfs_lookup_t) (const char *name, void **addr); -typedef void (*translate_addr_t) (unsigned long *); - -typedef int (*k_open_t) (const char *, int); -typedef int (*k_close_t) (int); -typedef ssize_t (*k_read_t) (int, void *, size_t); -typedef ssize_t (*k_write_t) (int, const void *, size_t); -typedef int (*k_ioctl_t) (int, int, void *); - -typedef void (*modules_remove_t) (int); -typedef snk_module_t *(*modules_load_t) (int); - -typedef long (*dma_map_in_t)(void *address, long size, int cachable); -typedef void (*dma_map_out_t)(void *address, long devaddr, long size); - -typedef struct { - int version; - print_t print; - us_delay_t us_delay; - ms_delay_t ms_delay; - pci_config_read_t pci_config_read; - pci_config_write_t pci_config_write; - malloc_t k_malloc; - malloc_aligned_t k_malloc_aligned; - free_t k_free; - strcmp_t strcmp; - snk_call_t snk_call; - io_read_t io_read; - io_write_t io_write; - romfs_lookup_t k_romfs_lookup; - translate_addr_t translate_addr; - union { - pci_config_t pci_conf; - vio_config_t vio_conf; - }; - k_open_t k_open; - k_close_t k_close; - k_read_t k_read; - k_write_t k_write; - k_ioctl_t k_ioctl; - modules_remove_t modules_remove; - modules_load_t modules_load; - dma_map_in_t dma_map_in; - dma_map_out_t dma_map_out; -} snk_kernel_t; - -/* Entry of module */ -snk_module_t *module_init(snk_kernel_t * snk_kernel_int, - pci_config_t * pciconf); - - -/* - * Constants for different kinds of IOCTL requests - */ - -#define SIOCETHTOOL 0x1000 - -/* - * special structure and constants for IOCTL requests of type ETHTOOL - */ - -#define ETHTOOL_GMAC 0x03 -#define ETHTOOL_SMAC 0x04 -#define ETHTOOL_VERSION 0x05 - -typedef struct { - int idx; - char address[6]; -} ioctl_ethtool_mac_t; - -typedef struct { - unsigned int length; - char *text; -} ioctl_ethtool_version_t; - - -/* - * default structure and constants for IOCTL requests - */ - -#define IF_NAME_SIZE 0xFF - -typedef struct { - char if_name[IF_NAME_SIZE]; - int subcmd; - union { - ioctl_ethtool_mac_t mac; - ioctl_ethtool_version_t version; - } data; -} ioctl_net_data_t; - -#endif /* _NETDRIVER_INT_H */ diff --git a/roms/SLOF/clients/net-snk/include/of.h b/roms/SLOF/clients/net-snk/include/of.h index b615297..22411ff 100644 --- a/roms/SLOF/clients/net-snk/include/of.h +++ b/roms/SLOF/clients/net-snk/include/of.h @@ -14,6 +14,7 @@ #define OF_H #include +#include #define p32 int #define p32cast (int) (unsigned long) (void*) @@ -34,6 +35,7 @@ phandle_t of_finddevice (const char *); phandle_t of_peer (phandle_t); phandle_t of_child (phandle_t); phandle_t of_parent (phandle_t); +phandle_t of_instance_to_package(ihandle_t ihandle); int of_getprop (phandle_t, const char *, void *, int); void * of_call_method_3 (const char *, ihandle_t, int); int of_test(const char *name); @@ -56,9 +58,13 @@ int vpd_read(unsigned int , unsigned int , char *); int vpd_write(unsigned int , unsigned int , char *); int write_mm_log(char *, unsigned int , unsigned short ); -void get_mac(char *mac); +int of_get_mac(phandle_t device, char *mac); uint64_t get_puid(phandle_t node); void translate_address_dev(uint64_t *, phandle_t); void translate_address(unsigned long *addr); +int of_glue_init(unsigned int * timebase, + size_t _client_start, size_t _client_size); +void of_glue_release(void); + #endif diff --git a/roms/SLOF/clients/net-snk/include/pci.h b/roms/SLOF/clients/net-snk/include/pci.h index b42619e..cf584c3 100644 --- a/roms/SLOF/clients/net-snk/include/pci.h +++ b/roms/SLOF/clients/net-snk/include/pci.h @@ -13,15 +13,6 @@ #ifndef _PCI_H #define _PCI_H -#include -#include - -int pci_calc_bar_size (long long puid, int bus, int devfn, int bar); -int pci_get_bar_start (long long puid, int bus, int devfn, int bar); -void pci_set_bar_start (long long puid, int bus, int devfn, int bar, int value); -int pci_bus_scan_puid(long long puid, int class_to_check, - pci_config_t *pci_devices, int max_devs); -long long get_next_phb (phandle_t *phb); unsigned int read_io(void *addr, size_t sz); int write_io(void *addr, unsigned int value, size_t sz); diff --git a/roms/SLOF/clients/net-snk/kernel/Makefile b/roms/SLOF/clients/net-snk/kernel/Makefile index 6846c83..c4b8164 100644 --- a/roms/SLOF/clients/net-snk/kernel/Makefile +++ b/roms/SLOF/clients/net-snk/kernel/Makefile @@ -17,7 +17,7 @@ ifndef TOP endif include $(TOP)/make.rules -OBJS = init.o systemcall.o crt0.o timer.o modules.o +OBJS = init.o systemcall.o crt0.o timer.o OBJS2 = entry.o all: kernel.o diff --git a/roms/SLOF/clients/net-snk/kernel/init.c b/roms/SLOF/clients/net-snk/kernel/init.c index 9e6176c..1376b64 100644 --- a/roms/SLOF/clients/net-snk/kernel/init.c +++ b/roms/SLOF/clients/net-snk/kernel/init.c @@ -18,8 +18,6 @@ #include #include #include -#include /* ioctl */ -#include "modules.h" /* Application entry point .*/ extern int _start(unsigned char *arg_string, long len); @@ -33,33 +31,9 @@ snk_fileio_t fd_array[FILEIO_MAX]; extern uint64_t tb_freq; -int glue_init(snk_kernel_t *, unsigned int *, size_t, size_t); -void glue_release(void); - -extern char _lowmem_start; -extern char _lowmem_end; extern char __client_start; extern char __client_end; -snk_kernel_t snk_kernel_interface = { - .version = 1, - .print = printk, - .us_delay = udelay, - .ms_delay = mdelay, - .k_malloc = malloc, - .k_malloc_aligned = malloc_aligned, - .k_free = free, - .strcmp = strcmp, - .snk_call = main, - .k_open = open, - .k_close = close, - .k_read = read, - .k_write = write, - .k_ioctl = ioctl, - .modules_remove = rmmod_by_type, - .modules_load = insmod_by_type, -}; - void * malloc_aligned(size_t size, int align) { unsigned long p = (unsigned long) malloc(size + align - 1); @@ -75,23 +49,19 @@ int _start_kernel(unsigned long p0, unsigned long p1) unsigned int timebase; /* initialize all file descriptor by marking them as empty */ - for(rc=0; rc -#include -#include -#include -#include -#include /* flush_cache */ -#include /* open, close, read, write */ -#include -#include -#include "modules.h" - -snk_module_t * cimod_check_and_install(void); - -extern snk_module_t of_module, ci_module; - -extern char __client_start[]; - - -typedef snk_module_t *(*module_init_t) (snk_kernel_t *, pci_config_t *); - -snk_module_t *snk_modules[MODULES_MAX]; - -extern snk_kernel_t snk_kernel_interface; - -/* Load module and call init code. - Init code will check, if module is responsible for device. - Returns -1, if not responsible for device, 0 otherwise. -*/ - -void -modules_init(void) -{ - int i; - - snk_kernel_interface.io_read = read_io; - snk_kernel_interface.io_write = write_io; - - snk_modules[0] = &of_module; - - /* Setup Module List */ - for(i=1; irunning != 0) { - snk_modules[i]->term(); - } - snk_modules[i] = 0; - } -} - -snk_module_t * -get_module_by_type(int type) { - int i; - - for(i=0; itype == type) { - return snk_modules[i]; - } - } - return 0; -} - -/** - * insmod_by_type - Load first module of given type - * - * @param type Type of module that we want to load - * @return module descriptor on success - * NULL if not successful - */ -snk_module_t * -insmod_by_type(int type) { - return 0; -} - -/** - * rmmod_by_type - Remove all module of given type - * - * @param type Type of module that we want to load - */ -void -rmmod_by_type(int type) { - int i; - - for (i = 0; i < MODULES_MAX; ++i) { - if (snk_modules[i] && snk_modules[i]->type == type) { - if (snk_modules[i]->running) - snk_modules[i]->term(); - snk_modules[i] = 0; - } - } -} diff --git a/roms/SLOF/clients/net-snk/kernel/modules.h b/roms/SLOF/clients/net-snk/kernel/modules.h deleted file mode 100644 index fde53af..0000000 --- a/roms/SLOF/clients/net-snk/kernel/modules.h +++ /dev/null @@ -1,17 +0,0 @@ -/****************************************************************************** - * Copyright (c) 2011 IBM Corporation - * All rights reserved. - * This program and the accompanying materials - * are made available under the terms of the BSD License - * which accompanies this distribution, and is available at - * http://www.opensource.org/licenses/bsd-license.php - * - * Contributors: - * IBM Corporation - initial implementation - *****************************************************************************/ - -extern void modules_init(void); -extern void modules_term(void); -extern snk_module_t *insmod_by_type(int); -extern void rmmod_by_type(int); -extern snk_module_t *get_module_by_type(int type); diff --git a/roms/SLOF/clients/net-snk/kernel/systemcall.c b/roms/SLOF/clients/net-snk/kernel/systemcall.c index e0f6bbc..52c45ca 100644 --- a/roms/SLOF/clients/net-snk/kernel/systemcall.c +++ b/roms/SLOF/clients/net-snk/kernel/systemcall.c @@ -13,122 +13,132 @@ #include #include #include -#include -#include #include -#include -#include "modules.h" +#include +#include +#include extern int vsprintf(char *, const char *, va_list); extern void _exit(int status); void exit(int status); -int printk(const char*, ...); int open(const char* name, int flags) { - int fd, i; + int fd; /* search free file descriptor */ - for(fd=0; fdopen) { - continue; - } + fd_array[fd].ih = of_open(name); + if (fd_array[fd].ih == 0) + return -1; - if(snk_modules[i]->running == 0) { - snk_modules[i]->init(); - } + fd_array[fd].type = FILEIO_TYPE_FILE; - if(snk_modules[i]->open(&fd_array[fd], name, flags) == 0) - break; - } + return fd; +} - if(i==MODULES_MAX) { - /* file not found */ - return -1; - } +int pre_open_ih(int fd, ihandle_t ih) +{ + if (fd_array[fd].type != FILEIO_TYPE_EMPTY) + return -2; + fd_array[fd].ih = ih; + fd_array[fd].type = FILEIO_TYPE_FILE; return fd; } int socket(int domain, int type, int proto, char *mac_addr) { - snk_module_t *net_module; + uint8_t tmpbuf[8]; + int fd; + phandle_t ph; - net_module = get_module_by_type(MOD_TYPE_NETWORK); - if( !net_module || !net_module->init) { - printk("No net_init function available"); - return -1; + /* search free file descriptor */ + for (fd=0; fdrunning == 0) { - net_module->init(); + if (fd == FILEIO_MAX) { + printf("Can not open socket, file descriptor list is full\n"); + /* there is no free file descriptor available */ + return -2; } - if(net_module->running == 0) - return -2; + fd_array[fd].ih = of_interpret_1("my-parent", tmpbuf); + if (fd_array[fd].ih == 0) { + printf("Can not open socket, no parent instance\n"); + return -1; + } + ph = of_instance_to_package(fd_array[fd].ih); + if (ph == -1) { + printf("Can not open socket, no parent package\n"); + return -1; + } + if (of_get_mac(ph, mac_addr) < 0) { + printf("Can not open socket, no MAC address\n"); + return -1; + } + fd_array[fd].type = FILEIO_TYPE_SOCKET; - memcpy(mac_addr, &net_module->mac_addr[0], 6); - return 0; + return fd; } int close(int fd) { - if(fd < 0 || fd >= FILEIO_MAX - || fd_array[fd].type == FILEIO_TYPE_EMPTY - || fd_array[fd].close == 0) + if (fd < 0 || fd >= FILEIO_MAX || + fd_array[fd].type == FILEIO_TYPE_EMPTY) return -1; - - return fd_array[fd].close(&fd_array[fd]); + if (fd_array[fd].type == FILEIO_TYPE_FILE) + of_close(fd_array[fd].ih); + fd_array[fd].type = FILEIO_TYPE_EMPTY; + return 0; } ssize_t read(int fd, void *buf, size_t len) { - if(fd < 0 || fd >= FILEIO_MAX - || fd_array[fd].type == FILEIO_TYPE_EMPTY - || fd_array[fd].read == 0) + if (fd < 0 || fd >= FILEIO_MAX || + fd_array[fd].type == FILEIO_TYPE_EMPTY) return -1; - return fd_array[fd].read(&fd_array[fd], buf, len); + return of_read(fd_array[fd].ih, buf, len); } ssize_t write (int fd, const void *buf, size_t len) { - char dest_buf[512]; - char *dest_buf_ptr; - const char *dbuf = buf; - int i; - - if (fd == 1 || fd == 2) { - dest_buf_ptr = &dest_buf[0]; - for (i = 0; i < len && i < 256; i++) - { - *dest_buf_ptr++ = *dbuf++; - if (dbuf[-1] == '\n') - *dest_buf_ptr++ = '\r'; + char dest_buf[512]; + char *dest_buf_ptr; + const char *dbuf = buf; + int i; + + if (fd == 1 || fd == 2) { + dest_buf_ptr = &dest_buf[0]; + for (i = 0; i < len && i < 256; i++) + { + *dest_buf_ptr++ = *dbuf++; + if (dbuf[-1] == '\n') + *dest_buf_ptr++ = '\r'; + } + len = dest_buf_ptr - &dest_buf[0]; + buf = &dest_buf[0]; } - len = dest_buf_ptr - &dest_buf[0]; - buf = &dest_buf[0]; - } - if(fd < 0 || fd >= FILEIO_MAX - || fd_array[fd].type == FILEIO_TYPE_EMPTY - || fd_array[fd].write == 0) + if(fd < 0 || fd >= FILEIO_MAX || + fd_array[fd].type == FILEIO_TYPE_EMPTY) return -1; - return fd_array[fd].write(&fd_array[fd], (void *)buf, len); + return of_write(fd_array[fd].ih, (void *)buf, len); } ssize_t lseek (int fd, long offset, int whence) @@ -144,51 +154,14 @@ ssize_t lseek (int fd, long offset, int whence) #endif } -int ioctl (int fd, int request, void* data) -{ - if (fd < 0 - || fd >= FILEIO_MAX - || fd_array[fd].type == FILEIO_TYPE_EMPTY) - return -1; - if (!fd_array[fd].ioctl) { /* for backwards compatibility with network modules */ - snk_module_t *net_module; - - net_module = get_module_by_type(MOD_TYPE_NETWORK); - if ( !net_module || !net_module->ioctl ) { - printk("No net_ioctl function available"); - return -1; - } - - return net_module->ioctl(request, data); - } - - return fd_array[fd].ioctl(&fd_array[fd], request, data); -} - int recv(int fd, void *packet, int packet_len, int flags) { - snk_module_t *net_module; - - net_module = get_module_by_type(MOD_TYPE_NETWORK); - if( !net_module || !net_module->read ) { - printk("No net_receive function available"); - return -1; - } - - return net_module->read(packet, packet_len); + return read(fd, packet, packet_len); } int send(int fd, const void *packet, int packet_len, int flags) { - snk_module_t *net_module; - - net_module = get_module_by_type(MOD_TYPE_NETWORK); - if( !net_module || !net_module->write ) { - printk("No net_xmit function available"); - return -1; - } - - return net_module->write((void *)packet, packet_len); + return write(fd, packet, packet_len); } int sendto(int fd, const void *packet, int packet_len, int flags, @@ -201,15 +174,3 @@ void exit(int status) { _exit(status); } - -int printk(const char* fmt, ...) -{ - int count; - va_list ap; - char buffer[256]; - va_start (ap, fmt); - count=vsprintf(buffer, fmt, ap); - write (1, buffer, count); - va_end (ap); - return count; -} diff --git a/roms/SLOF/clients/net-snk/kernel/timer.c b/roms/SLOF/clients/net-snk/kernel/timer.c index 1d5ae16..2d87058 100644 --- a/roms/SLOF/clients/net-snk/kernel/timer.c +++ b/roms/SLOF/clients/net-snk/kernel/timer.c @@ -37,33 +37,3 @@ uint64_t get_time(void) : "r0", "r4"); return act; } - -//------------------------------------------------------------------- -// wait for ticks/scale timebase ticks -static void wait_ticks(uint64_t ticks) -{ - uint64_t timeout = get_time() + ticks; - while (get_time() < timeout) { - unsigned int i; - for (i = 1000; i > 0; i--) - __asm__ __volatile__ ("" : : : "memory"); - } -} - -//------------------------------------------------------------------- -// wait for (at least) usecs microseconds -void udelay(unsigned int usecs) -{ - // first multiply the usec with timebase and then divide - // because 1.000.000 is relatively huge compared to usecs - wait_ticks((usecs*tb_freq)/1000000); -} - -//------------------------------------------------------------------- -// wait for (at least) msecs milliseconds -void mdelay(unsigned int msecs) -{ - // first multiply the msec and timebase and then divide - // because 1.000 is relatively huge compared to msecs - wait_ticks((msecs*tb_freq)/1000); -} diff --git a/roms/SLOF/clients/net-snk/oflib/Makefile b/roms/SLOF/clients/net-snk/oflib/Makefile index 58c2fd8..aad3e89 100644 --- a/roms/SLOF/clients/net-snk/oflib/Makefile +++ b/roms/SLOF/clients/net-snk/oflib/Makefile @@ -17,7 +17,7 @@ ifndef TOP endif include $(TOP)/make.rules -OBJS = rtas.o of.o pci.o ci_device.o +OBJS = rtas.o of.o pci.o OBJS2 = entry.o all: oflib.o diff --git a/roms/SLOF/clients/net-snk/oflib/ci_device.c b/roms/SLOF/clients/net-snk/oflib/ci_device.c deleted file mode 100644 index e490884..0000000 --- a/roms/SLOF/clients/net-snk/oflib/ci_device.c +++ /dev/null @@ -1,126 +0,0 @@ -/****************************************************************************** - * Copyright (c) 2011 IBM Corporation - * All rights reserved. - * This program and the accompanying materials - * are made available under the terms of the BSD License - * which accompanies this distribution, and is available at - * http://www.opensource.org/licenses/bsd-license.php - * - * Contributors: - * IBM Corporation - initial implementation - *****************************************************************************/ -/* - * A pseudo-module that uses the the "write" and "read" functions via the - * client interface to handle the given device. - * Normally the net-snk uses the various net_xxx modules from the romfs to - * drive the network cards. However, in case we do not have a net-snk driver - * for the given card and it has been initialized via FCODE instead, we've - * got to use the Open Firmware Client Interface "write" and "read" functions - * to talk to the NIC. This is achieved via this pseudo-module here. - */ - -#include -#include -#include -#include -#include -#include - -#define DEBUG 0 -#if DEBUG -#define dprintf(str, ...) snk_kernel_interface.print(str, ## __VA_ARGS__) -#else -#define dprintf(str, ...) do{}while(0) -#endif - -extern snk_kernel_t snk_kernel_interface; - -snk_module_t * cimod_check_and_install(void); -static int cimod_init(void); -static int cimod_term(void); -static int cimod_read(char *buffer, int len); -static int cimod_write(char *buffer, int len); -static int cimod_ioctl(int request, void *data); - -snk_module_t ci_module = { - .version = 1, - .type = MOD_TYPE_NETWORK, - .running = 0, - .link_addr = (char*) 1, - .init = cimod_init, - .term = cimod_term, - .write = cimod_write, - .read = cimod_read, - .ioctl = cimod_ioctl -}; - -static ihandle_t myself; - - -snk_module_t * -cimod_check_and_install(void) -{ - uint8_t tmpbuf[8]; - - dprintf("entered cimod_check_and_install!\n"); - - myself = of_interpret_1("my-parent", tmpbuf); - dprintf("cimod: myself=%lx\n", myself); - - /* Check whether "read" and "write" functions are provided by the - * device tree node: */ - if (of_read(myself, tmpbuf, 0) == -1 - || of_write(myself, tmpbuf, 0) == -1) { - dprintf("cimod: missing read or write!\n"); - return NULL; - } - - return &ci_module; -} - -static int -cimod_init(void) -{ - get_mac(&ci_module.mac_addr[0]); - ci_module.running = 1; - dprintf("client-interface module initialized!\n"); - return 0; -} - -static int -cimod_term(void) -{ - ci_module.running = 0; - dprintf("cimod term called!\n"); - return 0; -} - -static int -cimod_read(char *buffer, int len) -{ - int ret; - - ret = of_read(myself, buffer, len); - dprintf("cimod read returned: %i!\n", ret); - - return ret; -} - -static int -cimod_write(char *buffer, int len) -{ - int ret; - - ret = of_write(myself, buffer, len); - dprintf("cimod write returned: %i!\n", ret); - - return ret; -} - -static int -cimod_ioctl(int request, void *data) -{ - dprintf("cimod ioctl called!\n"); - - return 0; -} diff --git a/roms/SLOF/clients/net-snk/oflib/entry.S b/roms/SLOF/clients/net-snk/oflib/entry.S index f778920..f080267 100644 --- a/roms/SLOF/clients/net-snk/oflib/entry.S +++ b/roms/SLOF/clients/net-snk/oflib/entry.S @@ -20,14 +20,16 @@ C_ENTRY(call_client_interface) ld r4, .prom_entry_toc@toc(r2) # Load prom entry point mflr r0 + std r0, 16(r1) ld r4, 0(r4) - stdu r1, -16(r1) + stdu r1, -128(r1) + std r2,40(r1) mtctr r4 - std r0, 8(r1) bctrl - ld r0, 8(r1) + ld r2,40(r1) + addi r1, r1, 128 + ld r0, 16(r1) mtlr r0 - addi r1, r1, 16 blr diff --git a/roms/SLOF/clients/net-snk/oflib/of.c b/roms/SLOF/clients/net-snk/oflib/of.c index 1a836ca..5c502ac 100644 --- a/roms/SLOF/clients/net-snk/oflib/of.c +++ b/roms/SLOF/clients/net-snk/oflib/of.c @@ -14,36 +14,11 @@ #include #include #include -#include -#include #include +#include extern void call_client_interface(of_arg_t *); -static int ofmod_init(void); -static int ofmod_term(void); -static int ofmod_open(snk_fileio_t*, const char* name, int flags); -static int ofmod_read(char *buffer, int len); -static int ofmod_write(char *buffer, int len); -static int ofmod_ioctl(int request, void *data); - -int glue_init(snk_kernel_t *, unsigned int *, size_t, size_t); -void glue_release(void); - -snk_module_t of_module = { - .version = 1, - .type = MOD_TYPE_OTHER, - .running = 1, - .link_addr = (char*) 1, - .init = ofmod_init, - .term = ofmod_term, - .open = ofmod_open, - .write = ofmod_write, - .read = ofmod_read, - .ioctl = ofmod_ioctl -}; - -static ihandle_t fd_ihandle_array[FILEIO_MAX]; static int claim_rc = 0; static void* client_start; static size_t client_size; @@ -284,6 +259,13 @@ of_parent(phandle_t phandle) } phandle_t +of_instance_to_package(ihandle_t ihandle) +{ + return (phandle_t) of_1_1("instance-to-package", ihandle); +} + + +phandle_t of_finddevice(const char *name) { return (phandle_t) of_1_1("finddevice", p32cast name); @@ -307,16 +289,6 @@ of_release(void *start, unsigned int size) (void) of_2_0("release", p32cast start, size); } -unsigned int -romfs_lookup(const char *name, void **addr) -{ - unsigned int high, low; - unsigned int i = of_2_3("ibm,romfs-lookup", p32cast name, strlen(name), - (int *) &high, (int *) &low); - *addr = (void*)(((unsigned long) high << 32) | (unsigned long) low); - return i; -} - void * of_call_method_3(const char *name, ihandle_t ihandle, int arg0) { @@ -396,62 +368,6 @@ bootmsg_cp(short id) } */ -static long -of_fileio_read(snk_fileio_t *fileio, char *buf, long len) -{ - if(!fileio) - return -1; - return of_read( * (ihandle_t*) fileio->data, buf, len ); -} - -static long -of_fileio_write(snk_fileio_t *fileio, char *buf, long len) -{ - if(!fileio) - return -1; - return of_write( * (ihandle_t*) fileio->data, buf, len ); -} - -static int -of_fileio_close(snk_fileio_t *fileio) -{ - if(!fileio) - return -1; - - fileio->type = FILEIO_TYPE_EMPTY; - of_close( * (ihandle_t*) fileio->data ); - return 0; -} - -static long -dma_map_in(void *address, long size, int cachable) -{ - unsigned int ret; - - /* Is dma-map-in available? */ - if (of_test("dma-map-in") != 0) { - /* No dma-map-in available ==> Assume we can use 1:1 addresses */ - return (long)address; - } - - ret = of_3_1("dma-map-in", p32cast address, (int)size, cachable); - - return ret; -} - -static void -dma_map_out(void *address, long devaddr, long size) -{ - /* Is dma-map-out available? */ - if (of_test("dma-map-out") != 0) { - /* No dma-map-out available */ - return; - } - - of_3_0("dma-map-out", p32cast address, (int)devaddr, (int)size); -} - - #define CONFIG_SPACE 0 #define IO_SPACE 1 #define MEM_SPACE 2 @@ -461,7 +377,7 @@ dma_map_out(void *address, long devaddr, long size) #define DEBUG_TRANSLATE_ADDRESS 0 #if DEBUG_TRANSLATE_ADDRESS != 0 -#define DEBUG_TR(str...) printk(str) +#define DEBUG_TR(str...) printf(str) #else #define DEBUG_TR(str...) #endif @@ -729,86 +645,14 @@ get_puid(phandle_t node) return 0; } -static int set_vio_config(vio_config_t * vio_config, phandle_t net) -{ - vio_config->config_type = CONFIG_TYPE_VIO; - vio_config->reg_len = of_getprop(net, "reg", vio_config->reg, - sizeof(vio_config->reg)); - of_getprop(net, "compatible", &vio_config->compat, 64); - - return 0; -} - -/* Fill in the pci config structure from the device tree */ -static int set_pci_config(pci_config_t * pci_config, phandle_t net) -{ - unsigned char buf[400]; - int len, bar_nr; - unsigned int *assigned_ptr; - - pci_config->config_type = CONFIG_TYPE_PCI; - - of_getprop(net, "vendor-id", &pci_config->vendor_id, 4); - of_getprop(net, "device-id", &pci_config->device_id, 4); - of_getprop(net, "revision-id", &pci_config->revision_id, 4); - of_getprop(net, "class-code", &pci_config->class_code, 4); - of_getprop(net, "interrupts", &pci_config->interrupt_line, 4); - - len = of_getprop(net, "assigned-addresses", buf, 400); - if (len <= 0) - return -1; - - assigned_ptr = (unsigned int *) &buf[0]; - pci_config->bus = (assigned_ptr[0] & 0x00ff0000) >> 16; - pci_config->devfn = (assigned_ptr[0] & 0x0000ff00) >> 8; - - while (len > 0) { - /* Fixme 64 bit bars */ - bar_nr = ((assigned_ptr[0] & 0xff) - 0x10) / 4; - pci_config->bars[bar_nr].type = - (assigned_ptr[0] & 0x0f000000) >> 24; - pci_config->bars[bar_nr].addr = assigned_ptr[2]; - pci_config->bars[bar_nr].size = assigned_ptr[4]; - assigned_ptr += 5; - len -= 5 * sizeof(int); - } - - pci_config->puid = get_puid(net); - - return 0; -} - -static int set_config(snk_kernel_t * snk_kernel_interface) -{ - phandle_t parent, net = get_boot_device(); - char compat[64]; - - if (net == -1) - return -1; - - parent = of_parent(net); - of_getprop(parent, "compatible", compat, 64); - - if (strcmp(compat, "IBM,vdevice") == 0 - || strncmp(compat, "ibm,virtio", 10) == 0) - return set_vio_config(&snk_kernel_interface->vio_conf, net); - - return set_pci_config(&snk_kernel_interface->pci_conf, net); -} - -void -get_mac(char *mac) +int of_get_mac(phandle_t device, char *mac) { uint8_t localmac[8]; int len; - phandle_t net = get_boot_device(); - if (net == -1) - return; - - len = of_getprop(net, "local-mac-address", localmac, 8); + len = of_getprop(device, "local-mac-address", localmac, 8); if (len <= 0) - return; + return -1; if (len == 8) { /* Some bad FDT nodes like veth use a 8-byte wide @@ -818,6 +662,7 @@ get_mac(char *mac) else { memcpy(mac, localmac, 6); } + return 0; } static void @@ -837,12 +682,11 @@ get_timebase(unsigned int *timebase) of_getprop(cpu, "timebase-frequency", timebase, 4); } - -int -glue_init(snk_kernel_t * snk_kernel_interface, unsigned int * timebase, - size_t _client_start, size_t _client_size) +int of_glue_init(unsigned int * timebase, + size_t _client_start, size_t _client_size) { phandle_t chosen = of_finddevice("/chosen"); + ihandle_t stdin_ih, stdout_ih; client_start = (void *) (long) _client_start; client_size = _client_size; @@ -850,99 +694,22 @@ glue_init(snk_kernel_t * snk_kernel_interface, unsigned int * timebase, if (chosen == -1) return -1; - fd_array[0].type = FILEIO_TYPE_USED; - fd_array[0].read = of_fileio_read; - fd_array[0].write = of_fileio_write; - fd_array[0].ioctl = 0; - fd_array[0].close = of_fileio_close; - fd_array[0].data = &fd_ihandle_array[0]; - of_getprop(chosen, "stdin", fd_array[0].data, sizeof(ihandle_t)); - - fd_array[1].type = FILEIO_TYPE_USED; - fd_array[1].read = of_fileio_read; - fd_array[1].write = of_fileio_write; - fd_array[1].ioctl = 0; - fd_array[1].close = of_fileio_close; - fd_array[1].data = &fd_ihandle_array[1]; - of_getprop(chosen, "stdout", fd_array[1].data, sizeof(ihandle_t)); - - if (of_write(fd_ihandle_array[1], " ", 1) < 0) - return -2; - - /* Setup Kernel Struct */ - if (set_config(snk_kernel_interface) == -1) { - snk_kernel_interface->print(" No net device found \n"); - } - + of_getprop(chosen, "stdin", &stdin_ih, sizeof(ihandle_t)); + of_getprop(chosen, "stdout", &stdout_ih, sizeof(ihandle_t)); + pre_open_ih(0, stdin_ih); + pre_open_ih(1, stdout_ih); + pre_open_ih(2, stdout_ih); get_timebase(timebase); rtas_init(); - snk_kernel_interface->k_romfs_lookup = romfs_lookup; - snk_kernel_interface->translate_addr = translate_address; - snk_kernel_interface->pci_config_read = rtas_pci_config_read; - snk_kernel_interface->pci_config_write = rtas_pci_config_write; - snk_kernel_interface->dma_map_in = dma_map_in; - snk_kernel_interface->dma_map_out = dma_map_out; - claim_rc=(int)(long)of_claim(client_start, client_size, 0); return 0; } -void -glue_release(void) +void of_glue_release(void) { if (claim_rc >= 0) { of_release(client_start, client_size); } } - -static int -ofmod_init(void) -{ - of_module.running = 1; - return 0; -} - -static int -ofmod_term(void) -{ - of_module.running = 0; - return 0; -} - -static int -ofmod_open(snk_fileio_t *fileio, const char* name, int flags) -{ - if ((fd_ihandle_array[fileio->idx] = of_open (name)) == 0) - { - /* this module can not open this file */ - return -1; - } - - fileio->type = FILEIO_TYPE_USED; - fileio->read = of_fileio_read; - fileio->write = of_fileio_write; - fileio->close = of_fileio_close; - fileio->data = &fd_ihandle_array[fileio->idx]; - return 0; -} - -static int -ofmod_read(char *buffer, int len) -{ - return len; -} - -static int -ofmod_write(char *buffer, int len) -{ - return len; -} - -static int -ofmod_ioctl(int request, void *data) -{ - return 0; -} - diff --git a/roms/SLOF/clients/net-snk/oflib/pci.c b/roms/SLOF/clients/net-snk/oflib/pci.c index 40ff780..89003ae 100644 --- a/roms/SLOF/clients/net-snk/oflib/pci.c +++ b/roms/SLOF/clients/net-snk/oflib/pci.c @@ -19,33 +19,7 @@ #include #include -int -pci_calc_bar_size(long long puid, int bus, int devfn, int bar) -{ - int size; - int old; - bar = bar * 4 + 0x10; - old = rtas_pci_config_read(puid, 4, bus, devfn, bar); - rtas_pci_config_write(puid, 4, bus, devfn, bar, 0xffffffff); - size = (rtas_pci_config_read(puid, 4, bus, devfn, bar) & (-4)) * -1; - rtas_pci_config_write(puid, 4, bus, devfn, bar, old); - return size; -} - -int -pci_get_bar_start(long long puid, int bus, int devfn, int bar) -{ - return rtas_pci_config_read(puid, 4, bus, devfn, bar * 4 + 0x10); -} - -void -pci_set_bar_start(long long puid, int bus, int devfn, int bar, int value) -{ - rtas_pci_config_write(puid, 4, bus, devfn, bar * 4 + 0x10, value); -} - -unsigned int -read_io(void *addr, size_t sz) +unsigned int read_io(void *addr, size_t sz) { unsigned int ret; @@ -66,8 +40,7 @@ read_io(void *addr, size_t sz) return ret; } -int -write_io(void *addr, unsigned int value, size_t sz) +int write_io(void *addr, unsigned int value, size_t sz) { switch (sz) { case 1: diff --git a/roms/SLOF/clients/net-snk/oflib/rtas.c b/roms/SLOF/clients/net-snk/oflib/rtas.c index d0ef12a..c514c70 100644 --- a/roms/SLOF/clients/net-snk/oflib/rtas.c +++ b/roms/SLOF/clients/net-snk/oflib/rtas.c @@ -15,10 +15,7 @@ #include #include #include -#include -#include "kernel.h" - -extern snk_kernel_t snk_kernel_interface; +#include typedef int rtas_arg_t; @@ -103,7 +100,7 @@ instantiate_rtas(void) _rtas.dev = of_finddevice("/rtas"); if ((long) _rtas.dev < 0) { - snk_kernel_interface.print("\nCould not open /rtas\n"); + printf("\nCould not open /rtas\n"); return -1; } @@ -111,7 +108,7 @@ instantiate_rtas(void) sizeof(_rtas.rtas_size)); if (_rtas.rtas_size <= 0) { - snk_kernel_interface.print("\nSize of rtas (%x) too small to make sense\n", + printf("\nSize of rtas (%x) too small to make sense\n", _rtas.rtas_size); return -1; } @@ -119,14 +116,14 @@ instantiate_rtas(void) rtas_mem_space = (long long *) malloc_aligned(_rtas.rtas_size, 0x100); if (!rtas_mem_space) { - snk_kernel_interface.print("\nFailed to allocated memory for RTAS\n"); + printf("\nFailed to allocated memory for RTAS\n"); return -1; } ihandle = of_open("/rtas"); if ((long) ihandle < 0) { - snk_kernel_interface.print("Could not open /rtas\n"); + printf("Could not open /rtas\n"); return -1; } @@ -136,11 +133,11 @@ instantiate_rtas(void) > 0) { _rtas.rtas_start = rtas_mem_space; } else { - snk_kernel_interface.print("instantiate-rtas failed\n"); + printf("instantiate-rtas failed\n"); return -1; } #if 0 - snk_kernel_interface.print("\ninstantiate-rtas at %x size %x entry %x\n", + printf("\ninstantiate-rtas at %x size %x entry %x\n", _rtas.rtas_start, _rtas.rtas_size, _rtas.rtas_entry); #endif return 0; diff --git a/roms/SLOF/clients/net-snk/sec-client.lds b/roms/SLOF/clients/net-snk/sec-client.lds index 0ca24ab..0eefdda 100644 --- a/roms/SLOF/clients/net-snk/sec-client.lds +++ b/roms/SLOF/clients/net-snk/sec-client.lds @@ -28,13 +28,6 @@ SECTIONS { /* *(*COM* .bss .gnu.linkonce.b.*) */ } =0x60000000 - .lowmem : - { - _lowmem_start = .; - *(.lowmem) - _lowmem_end = .; - } - .got : { . = ALIGN(8); diff --git a/roms/SLOF/include/helpers.h b/roms/SLOF/include/helpers.h index f6d4375..fb10534 100644 --- a/roms/SLOF/include/helpers.h +++ b/roms/SLOF/include/helpers.h @@ -24,7 +24,7 @@ extern void SLOF_usleep(uint32_t time); extern void *SLOF_dma_alloc(long size); extern void SLOF_dma_free(void *virt, long size); extern void *SLOF_alloc_mem(long size); -extern void *SLOF_alloc_mem_aligned(long align, long size); +extern void *SLOF_alloc_mem_aligned(long size, long align); extern void SLOF_free_mem(void *addr, long size); extern long SLOF_dma_map_in(void *virt, long size, int cacheable); extern void SLOF_dma_map_out(long phys, void *virt, long size); diff --git a/roms/SLOF/lib/libhvcall/hvcall.code b/roms/SLOF/lib/libhvcall/hvcall.code index 744469f..0ff50f2 100644 --- a/roms/SLOF/lib/libhvcall/hvcall.code +++ b/roms/SLOF/lib/libhvcall/hvcall.code @@ -111,6 +111,12 @@ PRIM(hv_X2d_cas) TOS.u = hv_cas(vec, buf, size); MIRP +PRIM(hv_X2d_rtas_X2d_update) + unsigned long rtas_entry = TOS.u; POP; + unsigned long rtas_base = TOS.u; + TOS.u = hv_generic(KVMPPC_H_RTAS_UPDATE, rtas_base, rtas_entry); +MIRP + PRIM(get_X2d_print_X2d_version) unsigned long addr = TOS.u; POP; get_print_banner(addr); diff --git a/roms/SLOF/lib/libhvcall/hvcall.in b/roms/SLOF/lib/libhvcall/hvcall.in index e99d6d1..4437b77 100644 --- a/roms/SLOF/lib/libhvcall/hvcall.in +++ b/roms/SLOF/lib/libhvcall/hvcall.in @@ -30,4 +30,5 @@ cod(RX!) cod(hv-logical-memop) cod(hv-cas) +cod(hv-rtas-update) cod(get-print-version) diff --git a/roms/SLOF/lib/libhvcall/libhvcall.h b/roms/SLOF/lib/libhvcall/libhvcall.h index 6356a62..193b738 100644 --- a/roms/SLOF/lib/libhvcall/libhvcall.h +++ b/roms/SLOF/lib/libhvcall/libhvcall.h @@ -24,7 +24,9 @@ #define KVMPPC_H_LOGICAL_MEMOP (KVMPPC_HCALL_BASE + 0x1) /* Client Architecture support */ #define KVMPPC_H_CAS (KVMPPC_HCALL_BASE + 0x2) -#define KVMPPC_HCALL_MAX KVMPPC_H_CAS +#define KVMPPC_H_RTAS_UPDATE (KVMPPC_HCALL_BASE + 0x3) +#define KVMPPC_H_REPORT_MC_ERR (KVMPPC_HCALL_BASE + 0x4) +#define KVMPPC_HCALL_MAX KVMPPC_H_NMI_MCE #ifndef __ASSEMBLY__ diff --git a/roms/SLOF/lib/libusb/usb-xhci.c b/roms/SLOF/lib/libusb/usb-xhci.c index 9ff3bd1..0c3d6e4 100644 --- a/roms/SLOF/lib/libusb/usb-xhci.c +++ b/roms/SLOF/lib/libusb/usb-xhci.c @@ -534,7 +534,7 @@ static bool xhci_alloc_dev(struct xhci_hcd *xhcd, uint32_t slot_id, uint32_t por /* Step 3 */ slot = xhci_get_slot_ctx(&xdev->in_ctx, ctx_size); - newport = port - XHCI_CONFIG_MAX_SLOT + 1; + newport = port + 1; val = LAST_CONTEXT(1) | SLOT_SPEED_SS | (newport << 16); /* FIXME speed, read from PS */ slot->field1 = cpu_to_le32(val); slot->field2 = cpu_to_le32(ROOT_HUB_PORT(newport)); /* FIXME how to get port no */ @@ -642,12 +642,37 @@ static int xhci_hub_check_ports(struct xhci_hcd *xhcd) uint32_t num_ports, portsc, i; struct xhci_op_regs *op; struct xhci_port_regs *prs; + struct xhci_cap_regs *cap; + uint32_t xecp_off; + uint32_t *xecp_addr, *base; + uint32_t port_off = 1, port_cnt; dprintf("enter\n"); op = xhcd->op_regs; - num_ports = read_reg32(&op->config); - for (i = 0; i < num_ports * 2; i++) { + cap = xhcd->cap_regs; + port_cnt = num_ports = read_reg32(&cap->hcsparams1) >> 24; + + /* Read the xHCI extented capability to find usb3 ports and offset*/ + xecp_off = XHCI_HCCPARAMS_XECP(read_reg32(&cap->hccparams)); + base = (uint32_t *)cap; + while (xecp_off > 0) { + xecp_addr = base + xecp_off; + dprintf(stderr, "xecp_off %d %p %p \n", xecp_off, base, xecp_addr); + + if (XHCI_XECP_CAP_ID(read_reg32(xecp_addr)) == XHCI_XECP_CAP_SP && + XHCI_XECP_CAP_SP_MJ(read_reg32(xecp_addr)) == 3 && + XHCI_XECP_CAP_SP_MN(read_reg32(xecp_addr)) == 0) { + port_cnt = XHCI_XECP_CAP_SP_PC(read_reg32(xecp_addr + 2)); + port_off = XHCI_XECP_CAP_SP_PO(read_reg32(xecp_addr + 2)); + dprintf(stderr, "PortCount %d Portoffset %d\n", port_cnt, port_off); + } + base = xecp_addr; + xecp_off = XHCI_XECP_NEXT_PTR(read_reg32(xecp_addr)); + } + if (port_off == 0) /* port_off should always start from 1 */ + return false; + for (i = (port_off - 1); i < (port_off + port_cnt - 1); i++) { prs = &op->prs[i]; portsc = read_reg32(&prs->portsc); if ((portsc & PORTSC_CCS) && @@ -667,14 +692,13 @@ static int xhci_hub_check_ports(struct xhci_hcd *xhcd) dprintf("Port reset complete %d\n", i); } print_port_status(prs); - /* FIXME need to check if this is usb3 device */ - if (!usb3_dev_init(xhcd, i)) { + if (!usb3_dev_init(xhcd, (i - (port_off - 1)))) { dprintf("USB device initialization failed\n"); } } } dprintf("exit\n"); - return 0; + return true; } static bool xhci_hcd_init(struct xhci_hcd *xhcd) @@ -780,7 +804,8 @@ static bool xhci_hcd_init(struct xhci_hcd *xhcd) if (!xhci_hcd_set_runstop(op, true)) goto fail_erst_entries; - xhci_hub_check_ports(xhcd); + if (!xhci_hub_check_ports(xhcd)) + goto fail_erst_entries; return true; fail_erst_entries: diff --git a/roms/SLOF/lib/libusb/usb-xhci.h b/roms/SLOF/lib/libusb/usb-xhci.h index 9ee2b80..faeb07e 100644 --- a/roms/SLOF/lib/libusb/usb-xhci.h +++ b/roms/SLOF/lib/libusb/usb-xhci.h @@ -34,10 +34,20 @@ struct xhci_cap_regs { uint32_t hcsparams3; uint32_t hccparams; #define XHCI_HCCPARAMS_CSZ BIT(2) +#define XHCI_HCCPARAMS_XECP(x) ((x & 0xFFFF0000) >> 16) uint32_t dboff; uint32_t rtsoff; } __attribute__ ((packed)); +/* USB 3.0: Section 7 and 7.2 */ +#define XHCI_XECP_CAP_ID(x) ((x & 0xF)) +#define XHCI_XECP_CAP_SP 2 +#define XHCI_XECP_CAP_SP_MN(x) ((x & 0xFF0000) >> 16) +#define XHCI_XECP_CAP_SP_MJ(x) ((x & 0xFF000000) >> 24) +#define XHCI_XECP_CAP_SP_PC(x) ((x & 0xFF00) >> 8) +#define XHCI_XECP_CAP_SP_PO(x) (x & 0xFF) +#define XHCI_XECP_NEXT_PTR(x) ((x & 0xFF00) >> 8) + /* Table 27: Host Controller USB Port Register Set */ struct xhci_port_regs { uint32_t portsc; diff --git a/roms/SLOF/lib/libvirtio/virtio.c b/roms/SLOF/lib/libvirtio/virtio.c index b010796..f9c00a6 100644 --- a/roms/SLOF/lib/libvirtio/virtio.c +++ b/roms/SLOF/lib/libvirtio/virtio.c @@ -32,8 +32,10 @@ */ unsigned long virtio_vring_size(unsigned int qsize) { - return VQ_ALIGN(sizeof(struct vring_desc) * qsize + 2 * (2 + qsize)) - + VQ_ALIGN(sizeof(struct vring_used_elem) * qsize); + return VQ_ALIGN(sizeof(struct vring_desc) * qsize + + sizeof(struct vring_avail) + sizeof(uint16_t) * qsize) + + VQ_ALIGN(sizeof(struct vring_used) + + sizeof(struct vring_used_elem) * qsize); } diff --git a/roms/SLOF/other-licence/x86emu/x86emu_changes.diff b/roms/SLOF/other-licence/x86emu/x86emu_changes.diff index aa1a359..52b971a 100644 --- a/roms/SLOF/other-licence/x86emu/x86emu_changes.diff +++ b/roms/SLOF/other-licence/x86emu/x86emu_changes.diff @@ -844,15 +844,6 @@ diff -u -u -r1.1 -r1.4 u16 saved_ip; u16 saved_cs; int enc_pos; -@@ -366,7 +370,7 @@ - - /* Function to log information at runtime */ - --//void printk(const char *fmt, ...); -+void printk(const char *fmt, ...); - - #ifdef __cplusplus - } /* End of "C" linkage for C++ */ Index: include/x86emu/x86emu.h =================================================================== RCS file: /cvs/osdf/cvs/host/other-licence/x86emu/include/x86emu/x86emu.h,v @@ -861,21 +852,14 @@ retrieving revision 1.3 diff -u -u -r1.1 -r1.3 --- include/x86emu/x86emu.h 7 Sep 2007 10:01:21 -0000 1.1 +++ include/x86emu/x86emu.h 19 Oct 2007 08:42:15 -0000 1.3 -@@ -42,14 +42,6 @@ - #ifndef __X86EMU_X86EMU_H - #define __X86EMU_X86EMU_H +@@ -47,6 +47,7 @@ + #include + #define printk(x...) printk(BIOS_DEBUG, x) + #else ++#include + #define printk printf + #endif --/* FIXME: undefine printk for the moment */ --#ifdef LINUXBIOS_VERSION --#include --#define printk(x...) printk(BIOS_DEBUG, x) --#else --#define printk printf --#endif -- - #ifdef SCITECH - #include "scitech.h" - #define X86API _ASMAPI @@ -189,6 +181,8 @@ #define DEBUG_TRACECALL_REGS_F 0x004000 #define DEBUG_DECODE_NOPRINT_F 0x008000 diff --git a/roms/SLOF/slof/fs/graphics.fs b/roms/SLOF/slof/fs/graphics.fs new file mode 100644 index 0000000..7d5d930 --- /dev/null +++ b/roms/SLOF/slof/fs/graphics.fs @@ -0,0 +1,87 @@ +\ ***************************************************************************** +\ * Copyright (c) 2015 IBM Corporation +\ * All rights reserved. +\ * This program and the accompanying materials +\ * are made available under the terms of the BSD License +\ * which accompanies this distribution, and is available at +\ * http://www.opensource.org/licenses/bsd-license.php +\ * +\ * Contributors: +\ * IBM Corporation - initial implementation +\ ****************************************************************************/ + +\ Provide some of the functions that are defined in the +\ "OF Recommended Practice: 8bit Graphics Extension" document + +: draw-rectangle ( adr x y w h -- ) + frame-buffer-adr 0= IF 4drop drop EXIT THEN + 0 ?DO + 4dup drop ( adr x y w adr x y ) + \ calculate offset into framebuffer: ((y + i) * width + x) * depth + i + screen-width * + screen-depth * ( adr x y w adr offs ) + frame-buffer-adr + ( adr x y w adr fb_adr ) + over 3 pick screen-depth * i * + ( adr x y w adr fb_adr src ) + swap 3 pick screen-depth * ( adr x y w adr src fb_adr len ) + rmove \ copy line ( adr x y w adr ) + drop ( adr x y w ) + LOOP + 4drop +; + +: fill-rectangle ( col x y w h -- ) + frame-buffer-adr 0= IF 4drop drop EXIT THEN + 0 ?DO + 4dup drop ( col x y w col x y ) + \ calculate offset into framebuffer: ((y + i) * width + x) * depth + i + screen-width * + screen-depth * ( col x y w col offs ) + frame-buffer-adr + ( col x y w col adr ) + 2 pick screen-depth * 2 pick ( col x y w col adr len col ) + rfill \ draw line ( col x y w col ) + drop ( col x y w ) + LOOP + 4drop +; + +: read-rectangle ( adr x y w h -- ) + frame-buffer-adr 0= IF 4drop drop EXIT THEN + 0 ?DO + 4dup drop ( adr x y w adr x y ) + \ calculate offset into framebuffer: ((y + i) * width + x) * depth + i + screen-width * + screen-depth * ( adr x y w adr offs ) + frame-buffer-adr + ( adr x y w adr fb_adr ) + over 3 pick screen-depth * i * + ( adr x y w adr fb_adr dst ) + 3 pick ( adr x y w adr fb_adr dst w ) + rmove \ copy line ( adr x y w adr ) + drop ( adr x y w ) + LOOP + 4drop +; + +: dimensions ( -- width height ) + screen-width screen-height +; + +\ Initialize a default palette (not a standard command, but useful anyway) +: init-default-palette + \ Grayscale ramp for upper colors + 100 10 DO + i i i i color! + LOOP + \ Standard colors from "16-color Text Extension" specification + 00 00 00 0 color! + 00 00 aa 1 color! + 00 aa 00 2 color! + 00 aa aa 3 color! + aa 00 00 4 color! + aa 00 aa 5 color! + aa 55 00 6 color! + aa aa aa 7 color! + 55 55 55 8 color! + 55 55 ff 9 color! + 55 ff 55 a color! + 55 ff ff b color! + ff 55 55 c color! + ff 55 ff d color! + ff ff 55 e color! + ff ff ff f color! +; diff --git a/roms/SLOF/slof/fs/pci-properties.fs b/roms/SLOF/slof/fs/pci-properties.fs index da1d4fc..9efa87e 100644 --- a/roms/SLOF/slof/fs/pci-properties.fs +++ b/roms/SLOF/slof/fs/pci-properties.fs @@ -39,7 +39,6 @@ FFFFFFFF and \ keep lower 32 bits ; - \ calc 64 bit MEM BAR size : pci-bar-size-mem64 ( bar-addr -- bar-size ) dup pci-bar-size \ fetch raw size lower 32 bits @@ -127,7 +126,11 @@ \ Setup a prefetchable 64bit BAR and return its size : assign-mem64-bar ( bar-addr -- 8 ) dup pci-bar-size-mem64 \ fetch size - pci-next-mem \ var to change + pci-next-mem64 @ 0 = IF \ Check if we have 64-bit memory range + pci-next-mem + ELSE + pci-next-mem64 + THEN assign-bar-value64 \ and set it all ; @@ -141,7 +144,11 @@ \ Setup a non-prefetchable 64bit BAR and return its size : assign-mmio64-bar ( bar-addr -- 8 ) dup pci-bar-size-mem64 \ fetch size - pci-next-mmio \ var to change + pci-next-mem64 @ 0 = IF \ Check if we have 64-bit memory range + pci-next-mmio + ELSE + pci-next-mem64 \ for board-qemu we will use same range + THEN assign-bar-value64 \ and set it all ; @@ -558,7 +565,7 @@ \ *************************************************************************************** \ set up common properties for devices and bridges : pci-common-props ( addr -- ) - dup pci-class-name 2dup device-name device-type + dup pci-class-name device-name dup pci-vendor@ encode-int s" vendor-id" property dup pci-device@ encode-int s" device-id" property dup pci-revision@ encode-int s" revision-id" property diff --git a/roms/SLOF/slof/fs/pci-scan.fs b/roms/SLOF/slof/fs/pci-scan.fs index 7e860f4..15d0c8e 100644 --- a/roms/SLOF/slof/fs/pci-scan.fs +++ b/roms/SLOF/slof/fs/pci-scan.fs @@ -21,6 +21,8 @@ VARIABLE pci-next-mmio \ non-prefetchable memory VARIABLE pci-max-mmio VARIABLE pci-next-io \ I/O space VARIABLE pci-max-io +VARIABLE pci-next-mem64 \ prefetchable 64-bit memory mapped +VARIABLE pci-max-mem64 \ Counter of busses found 0 VALUE pci-bus-number @@ -231,11 +233,12 @@ DEFER func-pci-bridge-range-props dup set-space \ set the config addr for this device tree entry dup pci-set-slot \ set the slot bit dup pci-htype@ \ read HEADER-Type - 1 and IF \ IF BRIDGE - pci-bridge-setup \ | set up the bridge - ELSE \ ELSE - pci-device-setup \ | set up the device - THEN \ FI + 7f and \ Mask bit 7 - multifunction device + CASE + 0 OF pci-device-setup ENDOF \ | set up the device + 1 OF pci-bridge-setup ENDOF \ | set up the bridge + dup OF dup pci-htype@ pci-out ENDOF + ENDCASE finish-device \ and close the device-tree node ; diff --git a/roms/SLOF/slof/fs/root.fs b/roms/SLOF/slof/fs/root.fs index 1ae28fa..21c7109 100644 --- a/roms/SLOF/slof/fs/root.fs +++ b/roms/SLOF/slof/fs/root.fs @@ -67,7 +67,7 @@ finish-device \ Create /openprom new-device s" openprom" device-name - s" BootROM" device-type + 0 0 s" relative-addressing" property finish-device \ Create /packages diff --git a/roms/SLOF/slof/fs/term-io.fs b/roms/SLOF/slof/fs/term-io.fs index cdd754a..52ce12a 100644 --- a/roms/SLOF/slof/fs/term-io.fs +++ b/roms/SLOF/slof/fs/term-io.fs @@ -49,6 +49,8 @@ UNTIL (term-io-char-buf) c@ r> drop + ELSE + [ ' key behavior compile, ] THEN ; diff --git a/roms/SLOF/slof/fs/usb/dev-mouse.fs b/roms/SLOF/slof/fs/usb/dev-mouse.fs index 278a7c0..f6acd7e 100644 --- a/roms/SLOF/slof/fs/usb/dev-mouse.fs +++ b/roms/SLOF/slof/fs/usb/dev-mouse.fs @@ -6,7 +6,6 @@ sudev slof-dev>port l@ dup set-unit encode-phys " reg" property sudev slof-dev>udev @ VALUE udev s" usb-mouse" device-name -s" mouse" device-type \ .S cr \ dup slof-dev>udev dup . @ . cr diff --git a/roms/SLOF/slof/helpers.c b/roms/SLOF/slof/helpers.c index c582996..d7c1888 100644 --- a/roms/SLOF/slof/helpers.c +++ b/roms/SLOF/slof/helpers.c @@ -67,7 +67,7 @@ void *SLOF_alloc_mem(long size) return (void *)forth_pop(); } -void *SLOF_alloc_mem_aligned(long align, long size) +void *SLOF_alloc_mem_aligned(long size, long align) { unsigned long addr = (unsigned long)SLOF_alloc_mem(size + align - 1); addr = addr + align - 1; diff --git a/roms/config.seabios-128k b/roms/config.seabios-128k index e653c75..c719ba6 100644 --- a/roms/config.seabios-128k +++ b/roms/config.seabios-128k @@ -5,3 +5,4 @@ CONFIG_ROM_SIZE=128 CONFIG_XEN=n CONFIG_USB_XHCI=n CONFIG_USB_UAS=n +CONFIG_SDCARD=n diff --git a/roms/ipxe/src/Makefile b/roms/ipxe/src/Makefile index ea987b8..b742d12 100644 --- a/roms/ipxe/src/Makefile +++ b/roms/ipxe/src/Makefile @@ -46,6 +46,7 @@ ZBIN := ./util/zbin ELF2EFI32 := ./util/elf2efi32 ELF2EFI64 := ./util/elf2efi64 EFIROM := ./util/efirom +EFIFATBIN := ./util/efifatbin ICCFIX := ./util/iccfix EINFO := ./util/einfo GENKEYMAP := ./util/genkeymap.pl @@ -84,6 +85,7 @@ SRCDIRS += drivers/bitbash SRCDIRS += drivers/infiniband SRCDIRS += interface/pxe interface/efi interface/smbios SRCDIRS += interface/bofm +SRCDIRS += interface/xen SRCDIRS += tests SRCDIRS += crypto crypto/axtls crypto/matrixssl SRCDIRS += hci hci/commands hci/tui @@ -96,6 +98,7 @@ SRCDIRS += config # automatic build system. # NON_AUTO_SRCS := +NON_AUTO_SRCS += core/version.c NON_AUTO_SRCS += drivers/net/prism2.c # INCDIRS lists the include path @@ -145,6 +148,7 @@ all : $(ALL) # everything : $(Q)$(MAKE) --no-print-directory $(ALL) \ + bin/3c509.rom bin/intel.rom bin/intel.mrom \ bin-i386-efi/ipxe.efi bin-i386-efi/ipxe.efidrv \ bin-i386-efi/ipxe.efirom \ bin-x86_64-efi/ipxe.efi bin-x86_64-efi/ipxe.efidrv \ @@ -189,8 +193,8 @@ VERSION_PATCH = 0 EXTRAVERSION = + MM_VERSION = $(VERSION_MAJOR).$(VERSION_MINOR) VERSION = $(MM_VERSION).$(VERSION_PATCH)$(EXTRAVERSION) +ifneq ($(wildcard ../.git),) GITVERSION := $(shell git describe --always --abbrev=1 --match "" 2>/dev/null) -ifneq ($(GITVERSION),) VERSION += ($(GITVERSION)) endif version : diff --git a/roms/ipxe/src/Makefile.housekeeping b/roms/ipxe/src/Makefile.housekeeping index e240a73..1a75d39 100644 --- a/roms/ipxe/src/Makefile.housekeeping +++ b/roms/ipxe/src/Makefile.housekeeping @@ -229,7 +229,7 @@ endif # Determine how many different BIN directories are mentioned in the # make goals. # -BIN_GOALS := $(filter bin/% bin-%,$(MAKECMDGOALS)) +BIN_GOALS := $(filter bin bin/% bin-%,$(MAKECMDGOALS)) BIN_GOALS_BINS := $(sort $(foreach BG,$(BIN_GOALS),\ $(firstword $(subst /, ,$(BG))))) NUM_BINS := $(words $(BIN_GOALS_BINS)) @@ -689,6 +689,36 @@ privkey_DEPS += $(PRIVKEY_LIST) CFLAGS_privkey += $(if $(PRIVKEY),-DPRIVATE_KEY="\"$(PRIVKEY_INC)\"") +# (Single-element) list of named configurations +# +CONFIG_LIST := $(BIN)/.config.list +ifeq ($(wildcard $(CONFIG_LIST)),) +CONFIG_OLD := +else +CONFIG_OLD := $(shell cat $(CONFIG_LIST)) +endif +ifneq ($(CONFIG_OLD),$(CONFIG)) +$(shell $(ECHO) "$(CONFIG)" > $(CONFIG_LIST)) +endif + +$(CONFIG_LIST) : $(MAKEDEPS) + +VERYCLEANUP += $(CONFIG_LIST) + +# Named configurations +# +ifneq ($(CONFIG),) +ifneq ($(wildcard config/$(CONFIG)),) +CFLAGS += -DCONFIG=$(CONFIG) +endif +CFLAGS += -DLOCAL_CONFIG=$(CONFIG) +endif + +config/named.h : $(CONFIG_LIST) + $(Q)$(TOUCH) $@ + +.PRECIOUS : config/named.h + # These files use .incbin inline assembly to include a binary file. # Unfortunately ccache does not detect this dependency and caches # builds even when the binary file has changed. @@ -697,17 +727,31 @@ $(BIN)/embedded.% : override CC := env CCACHE_DISABLE=1 $(CC) $(BIN)/certstore.% : override CC := env CCACHE_DISABLE=1 $(CC) $(BIN)/privkey.% : override CC := env CCACHE_DISABLE=1 $(CC) -# Version number +# Debug message autocolourisation range # -CFLAGS_version += -DVERSION_MAJOR=$(VERSION_MAJOR) \ - -DVERSION_MINOR=$(VERSION_MINOR) \ - -DVERSION_PATCH=$(VERSION_PATCH) \ - -DVERSION="\"$(VERSION)\"" -# Make sure the version number gets updated on every git checkout -ifneq ($(GITVERSION),) -$(BIN)/version.% : ../.git/index +DBGCOL_LIST := $(BIN)/.dbgcol.list +ifeq ($(wildcard $(DBGCOL_LIST)),) +DBGCOL_OLD := +else +DBGCOL_OLD := $(shell cat $(DBGCOL_LIST)) +endif +ifneq ($(DBGCOL_OLD),$(DBGCOL)) +$(shell $(ECHO) "$(DBGCOL)" > $(DBGCOL_LIST)) endif +$(DBGCOL_LIST) : $(MAKEDEPS) + +VERYCLEANUP += $(DBGCOL_LIST) + +DBGCOL_COLOURS := $(subst -, ,$(DBGCOL)) +DBGCOL_MIN := $(word 1,$(DBGCOL_COLOURS)) +DBGCOL_MAX := $(word 2,$(DBGCOL_COLOURS)) + +debug_DEPS += $(DBGCOL_LIST) + +CFLAGS_debug += $(if $(DBGCOL_MIN),-DDBGCOL_MIN=$(DBGCOL_MIN)) +CFLAGS_debug += $(if $(DBGCOL_MAX),-DDBGCOL_MAX=$(DBGCOL_MAX)) + # We automatically generate rules for any file mentioned in AUTO_SRCS # using the following set of templates. We use $(eval ...) if # available, otherwise we generate separate Makefile fragments and @@ -856,28 +900,29 @@ $(BIN)/NIC : $(AUTO_DEPS) @perl -ne 'chomp; print "$$1\n" if /\# NIC\t(.*)$$/' $^ >> $@ CLEANUP += $(BIN)/NIC # Doesn't match the $(BIN)/*.* pattern -# Analyse a target name (e.g. "bin/dfe538--prism2_pci.zrom.tmp") and +# Analyse a target name (e.g. "bin/dfe538--prism2_pci.rom.tmp") and # derive the variables: # # TGT_ELEMENTS : the elements of the target (e.g. "dfe538 prism2_pci") -# TGT_PREFIX : the prefix type (e.g. "zrom") +# TGT_PREFIX : the prefix type (e.g. "pcirom") # TGT_DRIVERS : the driver for each element (e.g. "rtl8139 prism2_pci") # TGT_ROM_NAME : the ROM name (e.g. "dfe538") -# TGT_MEDIA : the media type (e.g. "rom") # DRIVERS_ipxe = $(DRIVERS) CARD_DRIVER = $(firstword $(DRIVER_$(1)) $(1)) TGT_ELEMENTS = $(subst --, ,$(firstword $(subst ., ,$(notdir $@)))) -TGT_PREFIX = $(word 2,$(subst ., ,$(notdir $@))) TGT_ROM_NAME = $(firstword $(TGT_ELEMENTS)) TGT_DRIVERS = $(strip $(if $(DRIVERS_$(TGT_ROM_NAME)), \ $(DRIVERS_$(TGT_ROM_NAME)), \ $(foreach TGT_ELEMENT,$(TGT_ELEMENTS), \ $(call CARD_DRIVER,$(TGT_ELEMENT))) )) -TGT_MEDIA = $(subst z,,$(TGT_PREFIX)) +TGT_PREFIX_NAME = $(word 2,$(subst ., ,$(notdir $@))) +TGT_PREFIX = $(strip $(if $(filter rom,$(TGT_PREFIX_NAME)), \ + $(ROM_TYPE_$(TGT_ROM_NAME))rom, \ + $(TGT_PREFIX_NAME))) # Look up ROM IDs for the current target -# (e.g. "bin/dfe538--prism2_pci.zrom.tmp") and derive the variables: +# (e.g. "bin/dfe538--prism2_pci.rom.tmp") and derive the variables: # # TGT_PCI_VENDOR : the PCI vendor ID (e.g. "0x1186") # TGT_PCI_DEVICE : the PCI device ID (e.g. "0x1300") @@ -886,7 +931,7 @@ TGT_PCI_VENDOR = $(PCI_VENDOR_$(TGT_ROM_NAME)) TGT_PCI_DEVICE = $(PCI_DEVICE_$(TGT_ROM_NAME)) # Calculate link-time options for the current target -# (e.g. "bin/dfe538--prism2_pci.zrom.tmp") and derive the variables: +# (e.g. "bin/dfe538--prism2_pci.rom.tmp") and derive the variables: # # TGT_LD_DRIVERS : symbols to require in order to drag in the relevant drivers # (e.g. "obj_rtl8139 obj_prism2_pci") @@ -899,7 +944,7 @@ TGT_LD_IDS = pci_vendor_id=$(firstword $(TGT_PCI_VENDOR) 0) \ TGT_LD_ENTRY = _$(TGT_PREFIX)_start # Calculate linker flags based on link-time options for the current -# target type (e.g. "bin/dfe538--prism2_pci.zrom.tmp") and derive the +# target type (e.g. "bin/dfe538--prism2_pci.rom.tmp") and derive the # variables: # # TGT_LD_FLAGS : target-specific flags to pass to linker (e.g. @@ -930,7 +975,6 @@ $(BIN)/%.info : @$(ECHO) 'Prefix : $(TGT_PREFIX)' @$(ECHO) 'Drivers : $(TGT_DRIVERS)' @$(ECHO) 'ROM name : $(TGT_ROM_NAME)' - @$(ECHO) 'Media : $(TGT_MEDIA)' @$(ECHO) @$(ECHO) 'PCI vendor : $(TGT_PCI_VENDOR)' @$(ECHO) 'PCI device : $(TGT_PCI_DEVICE)' @@ -977,13 +1021,31 @@ blib : $(BLIB) # BUILD_ID_CMD := perl -e 'printf "0x%08x", int ( rand ( 0xffffffff ) );' +# Build timestamp +# +BUILD_TIMESTAMP := $(shell date +%s) + +# Build version +# +GIT_INDEX := $(if $(GITVERSION),$(if $(wildcard ../.git/index),../.git/index)) +$(BIN)/version.%.o : core/version.c $(MAKEDEPS) $(GIT_INDEX) + $(QM)$(ECHO) " [VERSION] $@" + $(Q)$(COMPILE_c) -DBUILD_NAME="\"$*\"" \ + -DVERSION_MAJOR=$(VERSION_MAJOR) \ + -DVERSION_MINOR=$(VERSION_MINOR) \ + -DVERSION_PATCH=$(VERSION_PATCH) \ + -DVERSION="\"$(VERSION)\"" \ + -c $< -o $@ + # Build an intermediate object file from the objects required for the # specified target. # -$(BIN)/%.tmp : $(BLIB) $(MAKEDEPS) $(LDSCRIPT) +$(BIN)/%.tmp : $(BIN)/version.%.o $(BLIB) $(MAKEDEPS) $(LDSCRIPT) $(QM)$(ECHO) " [LD] $@" - $(Q)$(LD) $(LDFLAGS) -T $(LDSCRIPT) $(TGT_LD_FLAGS) $(BLIB) -o $@ \ - --defsym _build_id=`$(BUILD_ID_CMD)` -Map $(BIN)/$*.tmp.map + $(Q)$(LD) $(LDFLAGS) -T $(LDSCRIPT) $(TGT_LD_FLAGS) $< $(BLIB) -o $@ \ + --defsym _build_id=`$(BUILD_ID_CMD)` \ + --defsym _build_timestamp=$(BUILD_TIMESTAMP) \ + -Map $(BIN)/$*.tmp.map $(Q)$(OBJDUMP) -ht $@ | $(PERL) $(SORTOBJDUMP) >> $(BIN)/$*.tmp.map # Keep intermediate object file (useful for debugging) @@ -1206,6 +1268,11 @@ $(EFIROM) : util/efirom.c $(MAKEDEPS) $(Q)$(HOST_CC) $(HOST_CFLAGS) -idirafter include -o $@ $< CLEANUP += $(EFIROM) +$(EFIFATBIN) : util/efifatbin.c $(MAKEDEPS) + $(QM)$(ECHO) " [HOSTCC] $@" + $(Q)$(HOST_CC) $(HOST_CFLAGS) -idirafter include -o $@ $< +CLEANUP += $(EFIFATBIN) + ############################################################################### # # The ICC fixup utility @@ -1228,8 +1295,27 @@ CLEANUP += $(EINFO) # # Local configs # -config/local/%.h : - $(Q)touch $@ +CONFIG_HEADERS := $(patsubst config/%,%,$(wildcard config/*.h)) +CONFIG_LOCAL_HEADERS := $(foreach HEADER,$(CONFIG_HEADERS),\ + config/local/$(HEADER)) + +$(CONFIG_LOCAL_HEADERS) : + $(Q)$(TOUCH) $@ + +.PRECIOUS : $(CONFIG_LOCAL_HEADERS) + +ifneq ($(CONFIG),) + +CONFIG_LOCAL_NAMED_HEADERS := $(foreach HEADER,$(CONFIG_HEADERS),\ + config/local/$(CONFIG)/$(HEADER)) + +$(CONFIG_LOCAL_NAMED_HEADERS) : + $(Q)$(MKDIR) -p $(dir $@) + $(Q)$(TOUCH) $@ + +.PRECIOUS : $(CONFIG_LOCAL_NAMED_HEADERS) + +endif ############################################################################### # @@ -1358,6 +1444,13 @@ hci/keymap/keymap_%.c : # # Clean-up # + +ifeq ($(NUM_BINS),0) +ALLBINS := bin{,-*} +CLEANUP := $(patsubst $(BIN)/%,$(ALLBINS)/%,$(CLEANUP)) +VERYCLEANUP := $(patsubst $(BIN)/%,$(ALLBINS)/%,$(VERYCLEANUP)) +endif + clean : $(RM) $(CLEANUP) diff --git a/roms/ipxe/src/arch/i386/Makefile b/roms/ipxe/src/arch/i386/Makefile index d1b885c..4925cc4 100644 --- a/roms/ipxe/src/arch/i386/Makefile +++ b/roms/ipxe/src/arch/i386/Makefile @@ -82,7 +82,8 @@ ISOLINUX_BIN_LIST := \ /usr/share/syslinux/isolinux.bin \ /usr/share/syslinux/bios/isolinux.bin \ /usr/local/share/syslinux/isolinux.bin \ - /usr/local/share/syslinux/bios/isolinux.bin + /usr/local/share/syslinux/bios/isolinux.bin \ + /usr/lib/ISOLINUX/isolinux.bin ISOLINUX_BIN = $(firstword $(wildcard $(ISOLINUX_BIN_LIST))) # i386-specific directories containing source files diff --git a/roms/ipxe/src/arch/i386/Makefile.efi b/roms/ipxe/src/arch/i386/Makefile.efi index 8d651b0..aa809eb 100644 --- a/roms/ipxe/src/arch/i386/Makefile.efi +++ b/roms/ipxe/src/arch/i386/Makefile.efi @@ -4,6 +4,10 @@ # ELF2EFI = $(ELF2EFI32) +# Use EFI ABI +# +CFLAGS += -malign-double + # Include generic EFI Makefile # MAKEDEPS += arch/x86/Makefile.efi diff --git a/roms/ipxe/src/arch/i386/Makefile.pcbios b/roms/ipxe/src/arch/i386/Makefile.pcbios index d3fe8b9..ff82373 100644 --- a/roms/ipxe/src/arch/i386/Makefile.pcbios +++ b/roms/ipxe/src/arch/i386/Makefile.pcbios @@ -16,6 +16,8 @@ SRCDIRS += arch/i386/drivers/net # MEDIA += rom MEDIA += mrom +MEDIA += pcirom +MEDIA += isarom MEDIA += pxe MEDIA += kpxe MEDIA += kkpxe @@ -31,6 +33,8 @@ MEDIA += exe # PAD_rom = $(PERL) $(PADIMG) --blksize=512 --byte=0xff PAD_mrom = $(PAD_rom) +PAD_pcirom = $(PAD_rom) +PAD_isarom = $(PAD_rom) PAD_dsk = $(PERL) $(PADIMG) --blksize=512 PAD_hd = $(PERL) $(PADIMG) --blksize=32768 PAD_exe = $(PERL) $(PADIMG) --blksize=512 @@ -39,23 +43,27 @@ PAD_exe = $(PERL) $(PADIMG) --blksize=512 # FINALISE_rom = $(PERL) $(FIXROM) FINALISE_mrom = $(FINALISE_rom) +FINALISE_pcirom = $(FINALISE_rom) +FINALISE_isarom = $(FINALISE_rom) -# Use $(ROMS) rather than $(DRIVERS) for "allroms" and "allmroms" +# Use $(ROMS) rather than $(DRIVERS) for "allroms", "allmroms", etc. # LIST_NAME_rom := ROMS LIST_NAME_mrom := ROMS +LIST_NAME_pcirom := ROMS +LIST_NAME_isarom := ROMS # rule to make a non-emulation ISO boot image NON_AUTO_MEDIA += iso %iso: %lkrn util/geniso $(QM)$(ECHO) " [GENISO] $@" - $(Q)ISOLINUX_BIN=$(ISOLINUX_BIN) bash util/geniso $@ $< + $(Q)ISOLINUX_BIN=$(ISOLINUX_BIN) VERSION="$(VERSION)" bash util/geniso -o $@ $< # rule to make a floppy emulation ISO boot image NON_AUTO_MEDIA += liso -%liso: %lkrn util/genliso - $(QM)$(ECHO) " [GENLISO] $@" - $(Q)bash util/genliso $@ $< +%liso: %lkrn util/geniso + $(QM)$(ECHO) " [GENISO] $@" + $(Q)VERSION="$(VERSION)" bash util/geniso -l -o $@ $< # rule to make a syslinux floppy image (mountable, bootable) NON_AUTO_MEDIA += sdsk diff --git a/roms/ipxe/src/arch/i386/core/pci_autoboot.c b/roms/ipxe/src/arch/i386/core/pci_autoboot.c index 5e6197e..a3eb1f9 100644 --- a/roms/ipxe/src/arch/i386/core/pci_autoboot.c +++ b/roms/ipxe/src/arch/i386/core/pci_autoboot.c @@ -34,10 +34,8 @@ uint16_t __bss16 ( autoboot_busdevfn ); */ static void pci_autoboot_init ( void ) { - if ( autoboot_busdevfn ) { - autoboot_device.bus_type = BUS_TYPE_PCI; - autoboot_device.location = autoboot_busdevfn; - } + if ( autoboot_busdevfn ) + set_autoboot_busloc ( BUS_TYPE_PCI, autoboot_busdevfn ); } /** PCI autoboot device initialisation function */ diff --git a/roms/ipxe/src/arch/i386/drivers/net/undinet.c b/roms/ipxe/src/arch/i386/drivers/net/undinet.c index 82dd8d2..6450665 100644 --- a/roms/ipxe/src/arch/i386/drivers/net/undinet.c +++ b/roms/ipxe/src/arch/i386/drivers/net/undinet.c @@ -72,8 +72,8 @@ struct undi_nic { /** Delay between retries of PXENV_UNDI_INITIALIZE */ #define UNDI_INITIALIZE_RETRY_DELAY_MS 200 -/** Maximum number of calls to PXENV_UNDI_ISR per poll */ -#define UNDI_POLL_QUOTA 4 +/** Maximum number of received packets per poll */ +#define UNDI_RX_QUOTA 4 /** Alignment of received frame payload */ #define UNDI_RX_ALIGN 16 @@ -331,7 +331,7 @@ static void undinet_poll ( struct net_device *netdev ) { struct undi_nic *undinic = netdev->priv; struct s_PXENV_UNDI_ISR undi_isr; struct io_buffer *iobuf = NULL; - unsigned int quota = UNDI_POLL_QUOTA; + unsigned int quota = UNDI_RX_QUOTA; size_t len; size_t reserve_len; size_t frag_len; @@ -370,7 +370,7 @@ static void undinet_poll ( struct net_device *netdev ) { } /* Run through the ISR loop */ - while ( quota-- ) { + while ( quota ) { profile_start ( &undinet_isr_call_profiler ); if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_ISR, &undi_isr, @@ -424,6 +424,7 @@ static void undinet_poll ( struct net_device *netdev ) { if ( iob_len ( iobuf ) == len ) { /* Whole packet received; deliver it */ netdev_rx ( netdev, iob_disown ( iobuf ) ); + quota--; /* Etherboot 5.4 fails to return all packets * under mild load; pretend it retriggered. */ diff --git a/roms/ipxe/src/arch/i386/firmware/pcbios/bios_console.c b/roms/ipxe/src/arch/i386/firmware/pcbios/bios_console.c index b2b7bf2..bd73838 100644 --- a/roms/ipxe/src/arch/i386/firmware/pcbios/bios_console.c +++ b/roms/ipxe/src/arch/i386/firmware/pcbios/bios_console.c @@ -21,6 +21,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include +#include #include #include #include @@ -148,11 +149,53 @@ static void bios_handle_sgr ( struct ansiesc_context *ctx __unused, } } +/** + * Handle ANSI DECTCEM set (show cursor) + * + * @v ctx ANSI escape sequence context + * @v count Parameter count + * @v params List of graphic rendition aspects + */ +static void bios_handle_dectcem_set ( struct ansiesc_context *ctx __unused, + unsigned int count __unused, + int params[] __unused ) { + uint8_t height; + + /* Get character height */ + get_real ( height, BDA_SEG, BDA_CHAR_HEIGHT ); + + __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" + "int $0x10\n\t" + "cli\n\t" ) + : : "a" ( 0x0100 ), + "c" ( ( ( height - 2 ) << 8 ) | + ( height - 1 ) ) ); +} + +/** + * Handle ANSI DECTCEM reset (hide cursor) + * + * @v ctx ANSI escape sequence context + * @v count Parameter count + * @v params List of graphic rendition aspects + */ +static void bios_handle_dectcem_reset ( struct ansiesc_context *ctx __unused, + unsigned int count __unused, + int params[] __unused ) { + + __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" + "int $0x10\n\t" + "cli\n\t" ) + : : "a" ( 0x0100 ), "c" ( 0x2000 ) ); +} + /** BIOS console ANSI escape sequence handlers */ static struct ansiesc_handler bios_ansiesc_handlers[] = { { ANSIESC_CUP, bios_handle_cup }, { ANSIESC_ED, bios_handle_ed }, { ANSIESC_SGR, bios_handle_sgr }, + { ANSIESC_DECTCEM_SET, bios_handle_dectcem_set }, + { ANSIESC_DECTCEM_RESET, bios_handle_dectcem_reset }, { 0, NULL } }; diff --git a/roms/ipxe/src/arch/i386/include/bios.h b/roms/ipxe/src/arch/i386/include/bios.h index 3e6a845..0754b11 100644 --- a/roms/ipxe/src/arch/i386/include/bios.h +++ b/roms/ipxe/src/arch/i386/include/bios.h @@ -9,5 +9,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define BDA_REBOOT 0x0072 #define BDA_REBOOT_WARM 0x1234 #define BDA_NUM_DRIVES 0x0075 +#define BDA_CHAR_HEIGHT 0x0085 #endif /* BIOS_H */ diff --git a/roms/ipxe/src/arch/i386/include/ipxe/msr.h b/roms/ipxe/src/arch/i386/include/ipxe/msr.h new file mode 100644 index 0000000..c88e26a --- /dev/null +++ b/roms/ipxe/src/arch/i386/include/ipxe/msr.h @@ -0,0 +1,38 @@ +#ifndef _IPXE_MSR_H +#define _IPXE_MSR_H + +/** @file + * + * Model-specific registers + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** + * Read model-specific register + * + * @v msr Model-specific register + * @ret value Value + */ +static inline __attribute__ (( always_inline )) uint64_t +rdmsr ( unsigned int msr ) { + uint64_t value; + + __asm__ __volatile__ ( "rdmsr" : "=A" ( value ) : "c" ( msr ) ); + return value; +} + +/** + * Write model-specific register + * + * @v msr Model-specific register + * @v value Value + */ +static inline __attribute__ (( always_inline )) void +wrmsr ( unsigned int msr, uint64_t value ) { + + __asm__ __volatile__ ( "wrmsr" : : "c" ( msr ), "A" ( value ) ); +} + +#endif /* _IPXE_MSR_H */ diff --git a/roms/ipxe/src/arch/i386/interface/pxeparent/pxeparent.c b/roms/ipxe/src/arch/i386/interface/pxeparent/pxeparent.c index 744c7b6..0b6be9a 100644 --- a/roms/ipxe/src/arch/i386/interface/pxeparent/pxeparent.c +++ b/roms/ipxe/src/arch/i386/interface/pxeparent/pxeparent.c @@ -240,12 +240,12 @@ int pxeparent_call ( SEGOFF16_t entry, unsigned int function, "D" ( __from_data16 ( &pxeparent_params ) ) : "ecx", "esi" ); profile_stop ( &profiler->total ); - profile_start_at ( &profiler->p2r, profiler->total.started ); + profile_start_at ( &profiler->p2r, profile_started ( &profiler->total)); profile_stop_at ( &profiler->p2r, started ); profile_start_at ( &profiler->ext, started ); profile_stop_at ( &profiler->ext, stopped ); profile_start_at ( &profiler->r2p, stopped ); - profile_stop_at ( &profiler->r2p, profiler->total.stopped ); + profile_stop_at ( &profiler->r2p, profile_stopped ( &profiler->total )); /* Determine return status code based on PXENV_EXIT and * PXENV_STATUS diff --git a/roms/ipxe/src/arch/i386/prefix/isaromprefix.S b/roms/ipxe/src/arch/i386/prefix/isaromprefix.S new file mode 100644 index 0000000..e282080 --- /dev/null +++ b/roms/ipxe/src/arch/i386/prefix/isaromprefix.S @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2014 Michael Brown . + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ) + +#define BUSTYPE "ISAR" +#define _rom_start _isarom_start +#include "romprefix.S" diff --git a/roms/ipxe/src/arch/i386/prefix/libprefix.S b/roms/ipxe/src/arch/i386/prefix/libprefix.S index 3aee415..7c1ece7 100644 --- a/roms/ipxe/src/arch/i386/prefix/libprefix.S +++ b/roms/ipxe/src/arch/i386/prefix/libprefix.S @@ -95,6 +95,29 @@ print_character: .size print_character, . - print_character /***************************************************************************** + * Utility function: print space + * + * Parameters: + * %ds:di : output buffer (or %di=0 to print to console) + * Returns: + * %ds:di : next character in output buffer (if applicable) + ***************************************************************************** + */ + .section ".prefix.lib", "awx", @progbits + .code16 + .globl print_space +print_space: + /* Preserve registers */ + pushw %ax + /* Print space */ + movb $( ' ' ), %al + call print_character + /* Restore registers and return */ + popw %ax + ret + .size print_space, . - print_space + +/***************************************************************************** * Utility function: print a NUL-terminated string * * Parameters: @@ -231,12 +254,10 @@ print_kill_line: movb $( '\r' ), %al call print_character /* Print 79 spaces */ - movb $( ' ' ), %al movw $79, %cx -1: call print_character +1: call print_space loop 1b /* Print CR */ - movb $( '\r' ), %al call print_character /* Restore registers and return */ popw %cx @@ -725,9 +746,15 @@ a20_death_message: xorw %di, %di movl %esi, %eax call print_hex_dword + call print_space + movl %ecx, %eax + call print_hex_dword movw $payload_death_message, %si call print_message -2: jmp 2b +2: /* Halt system */ + cli + hlt + jmp 2b .section ".prefix.data", "aw", @progbits payload_death_message: .asciz "\nPayload inaccessible - cannot continue\n" diff --git a/roms/ipxe/src/arch/i386/prefix/lkrnprefix.S b/roms/ipxe/src/arch/i386/prefix/lkrnprefix.S index 624f9b0..259bc6b 100644 --- a/roms/ipxe/src/arch/i386/prefix/lkrnprefix.S +++ b/roms/ipxe/src/arch/i386/prefix/lkrnprefix.S @@ -1,123 +1,58 @@ -/* - Copyright (C) 2000, Entity Cyber, Inc. - - Authors: Gary Byers (gb@thinguin.org) - Marty Connor (mdc@thinguin.org) - - This software may be used and distributed according to the terms - of the GNU Public License (GPL), incorporated herein by reference. - - Description: - - This is just a little bit of code and data that can get prepended - to a ROM image in order to allow bootloaders to load the result - as if it were a Linux kernel image. - - A real Linux kernel image consists of a one-sector boot loader - (to load the image from a floppy disk), followed a few sectors - of setup code, followed by the kernel code itself. There's - a table in the first sector (starting at offset 497) that indicates - how many sectors of setup code follow the first sector and which - contains some other parameters that aren't interesting in this - case. - - When a bootloader loads the sectors that comprise a kernel image, - it doesn't execute the code in the first sector (since that code - would try to load the image from a floppy disk.) The code in the - first sector below doesn't expect to get executed (and prints an - error message if it ever -is- executed.) - - We don't require much in the way of setup code. Historically, the - Linux kernel required at least 4 sectors of setup code. - Therefore, at least 4 sectors must be present even though we don't - use them. - -*/ - FILE_LICENCE ( GPL_ANY ) -#define SETUPSECS 4 /* Minimal nr of setup-sectors */ -#define PREFIXSIZE ((SETUPSECS+1)*512) -#define PREFIXPGH (PREFIXSIZE / 16 ) -#define BOOTSEG 0x07C0 /* original address of boot-sector */ -#define INITSEG 0x9000 /* we move boot here - out of the way */ -#define SETUPSEG 0x9020 /* setup starts here */ -#define SYSSEG 0x1000 /* system loaded at 0x10000 (65536). */ +#define BZI_LOAD_HIGH_ADDR 0x100000 .text - .code16 .arch i386 - .org 0 + .code16 .section ".prefix", "ax", @progbits .globl _lkrn_start _lkrn_start: -/* - This is a minimal boot sector. If anyone tries to execute it (e.g., if - a .lilo file is dd'ed to a floppy), print an error message. -*/ - -bootsector: - jmp $BOOTSEG, $1f /* reload cs:ip to match relocation addr */ -1: - movw $0x2000, %di /* 0x2000 is arbitrary value >= length - of bootsect + room for stack */ - - movw $BOOTSEG, %ax - movw %ax,%ds - movw %ax,%es - - cli - movw %ax, %ss /* put stack at BOOTSEG:0x2000. */ - movw %di,%sp - sti - - movw $why_end-why, %cx - movw $why, %si - - movw $0x0007, %bx /* page 0, attribute 7 (normal) */ - movb $0x0e, %ah /* write char, tty mode */ -prloop: - lodsb - int $0x10 - loop prloop -freeze: jmp freeze - -why: .ascii "This image cannot be loaded from a floppy disk.\r\n" -why_end: +/***************************************************************************** + * + * Kernel header + * + * We place our prefix (i.e. our .prefix and .text16.early sections) + * within the bzImage real-mode portion which gets loaded at + * 1000:0000, and our payload (i.e. everything else) within the + * bzImage protected-mode portion which gets loaded at 0x100000 + * upwards. + * + */ -/* - The following header is documented in the Linux source code at - Documentation/x86/boot.txt -*/ - .org 497 -setup_sects: - .byte SETUPSECS -root_flags: + .org 0x1f1 +setup_sects: + .byte -1 /* Allow for initial "boot sector" */ + .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ + .ascii "ADHL" + .long setup_sects + .long 512 + .long 0 + .previous +root_flags: .word 0 -syssize: - .long -PREFIXPGH - +syssize: + .long 0 .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ - .ascii "ADDL" + .ascii "ADPL" .long syssize .long 16 .long 0 .previous - -ram_size: +ram_size: .word 0 -vid_mode: +vid_mode: .word 0 -root_dev: +root_dev: .word 0 -boot_flag: - .word 0xAA55 +boot_flag: + .word 0xaa55 jump: /* Manually specify a two-byte jmp instruction here rather - * than leaving it up to the assembler. */ - .byte 0xeb - .byte setup_code - header + * than leaving it up to the assembler. + */ + .byte 0xeb, ( setup - header ) header: .byte 'H', 'd', 'r', 'S' version: @@ -131,7 +66,7 @@ kernel_version: type_of_loader: .byte 0 loadflags: - .byte 0 + .byte 0x01 /* LOADED_HIGH */ setup_move_size: .word 0 code32_start: @@ -144,21 +79,22 @@ bootsect_kludge: .long 0 heap_end_ptr: .word 0 -pad1: - .word 0 +ext_loader_ver: + .byte 0 +ext_loader_type: + .byte 0 cmd_line_ptr: .long 0 initrd_addr_max: - /* We don't use an initrd but some bootloaders (e.g. SYSLINUX) have - * been known to require this field. Set the value to 2 GB. This - * value is also used by the Linux kernel. */ - .long 0x7fffffff + .long 0xffffffff kernel_alignment: .long 0 relocatable_kernel: .byte 0 -pad2: - .byte 0, 0, 0 +min_alignment: + .byte 0 +xloadflags: + .word 0 cmdline_size: .long 0x7ff hardware_subarch: @@ -169,28 +105,18 @@ hardware_subarch_data: version_string: .asciz VERSION -/* - We don't need to do too much setup. - - This code gets loaded at SETUPSEG:0. It wants to start - executing the image that's loaded at SYSSEG:0 and - whose entry point is SYSSEG:0. -*/ -setup_code: - /* We expect to be contiguous in memory once loaded. The Linux image - * boot process requires that setup code is loaded separately from - * "non-real code". Since we don't need any information that's left - * in the prefix, it doesn't matter: we just have to ensure that - * %cs:0000 is where the start of the image *would* be. - */ - ljmp $(SYSSEG-(PREFIXSIZE/16)), $run_ipxe - - - .org PREFIXSIZE -/* - We're now at the beginning of the kernel proper. +/***************************************************************************** + * + * Setup code + * */ -run_ipxe: + +setup: + /* Fix up code segment */ + pushw %ds + pushw $1f + lret +1: /* Set up stack just below 0x7c00 and clear direction flag */ xorw %ax, %ax movw %ax, %ss @@ -198,7 +124,7 @@ run_ipxe: cld /* Retrieve command-line pointer */ - movl %ds:cmd_line_ptr, %edx + movl cmd_line_ptr, %edx testl %edx, %edx jz no_cmd_line @@ -240,7 +166,6 @@ no_cmd_line: jnz 1f orl $0xffffffff, %ebp /* Allow arbitrary relocation if no initrd */ 1: - /* Install iPXE */ call alloc_basemem xorl %esi, %esi @@ -282,3 +207,30 @@ no_cmd_line: /* Boot next device */ int $0x18 + +/***************************************************************************** + * + * Open payload (called by libprefix) + * + * Parameters: + * %ds:0000 : Prefix + * %esi : Buffer for copy of image source (or zero if no buffer available) + * %ecx : Expected offset within buffer of first payload block + * Returns: + * %esi : Valid image source address (buffered or unbuffered) + * %ecx : Actual offset within buffer of first payload block + * CF set on error + */ + + .section ".text16.early", "awx", @progbits + .globl open_payload +open_payload: + + /* Our payload will always end up at BZI_LOAD_HIGH_ADDR */ + movl $BZI_LOAD_HIGH_ADDR, %esi + xorl %ecx, %ecx + lret + + /* Payload must be aligned to a whole number of setup sectors */ + .globl _payload_align + .equ _payload_align, 512 diff --git a/roms/ipxe/src/arch/i386/prefix/mromprefix.S b/roms/ipxe/src/arch/i386/prefix/mromprefix.S index 0f0847e..4c94457 100644 --- a/roms/ipxe/src/arch/i386/prefix/mromprefix.S +++ b/roms/ipxe/src/arch/i386/prefix/mromprefix.S @@ -30,10 +30,12 @@ FILE_LICENCE ( GPL2_OR_LATER ) #define PCI_BAR_5 0x24 #define PCI_BAR_EXPROM 0x30 +#define PCIR_SIGNATURE ( 'P' + ( 'C' << 8 ) + ( 'I' << 16 ) + ( 'R' << 24 ) ) + #define ROMPREFIX_EXCLUDE_PAYLOAD 1 #define ROMPREFIX_MORE_IMAGES 1 -#define _rom_start _mrom_start -#include "romprefix.S" +#define _pcirom_start _mrom_start +#include "pciromprefix.S" .text .arch i386 @@ -99,6 +101,7 @@ find_mem_bar: jle 1f stc movl $0xbabababa, %esi /* Report "No suitable BAR" */ + movl rom_bar_size, %ecx jmp 99f 1: movw $4, %bp @@ -157,17 +160,26 @@ find_mem_bar: call pci_write_config_dword /* Locate our ROM image */ -1: addr32 es cmpw $0xaa55, (%eax) - je 2f - stc - movl %eax, %esi /* Report failure address */ - jmp 99f -2: addr32 es cmpl $_build_id, build_id(%eax) +1: movl $0xaa55, %ecx /* 55aa signature */ + addr32 es cmpw %cx, (%eax) + jne 2f + movl $PCIR_SIGNATURE, %ecx /* PCIR signature */ + addr32 es movzwl 0x18(%eax), %edx + addr32 es cmpl %ecx, (%eax,%edx) + jne 2f + addr32 es cmpl $_build_id, build_id(%eax) /* iPXE build ID */ je 3f - addr32 es movzbl 2(%eax), %ecx + movl $0x80, %ecx /* Last image */ + addr32 es testb %cl, 0x15(%eax,%edx) + jnz 2f + addr32 es movzwl 0x10(%eax,%edx), %ecx /* PCIR image length */ shll $9, %ecx addl %ecx, %eax jmp 1b +2: /* Failure */ + stc + movl %eax, %esi /* Report failure address */ + jmp 99f 3: /* Copy payload to buffer, or set buffer address to BAR address */ @@ -184,7 +196,7 @@ find_mem_bar: movl %eax, %esi addr32 es movzbl 2(%esi), %ecx shll $7, %ecx - addr32 es movzbl 2(%esi,%ecx,4), %edx + addr32 es movzwl mpciheader_image_length(%esi,%ecx,4), %edx shll $7, %edx addl %edx, %ecx addr32 es rep movsl @@ -451,20 +463,12 @@ pci_set_mem_access: .org 0x00 mromheader: .word 0xaa55 /* BIOS extension signature */ -mromheader_size: .byte 0 /* Size in 512-byte blocks */ .org 0x18 .word mpciheader .org 0x1a .word 0 .size mromheader, . - mromheader - .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ - .ascii "APPB" - .long mromheader_size - .long 512 - .long 0 - .previous - mpciheader: .ascii "PCIR" /* Signature */ .word pci_vendor_id /* Vendor identification */ diff --git a/roms/ipxe/src/arch/i386/prefix/pciromprefix.S b/roms/ipxe/src/arch/i386/prefix/pciromprefix.S new file mode 100644 index 0000000..45ba31f --- /dev/null +++ b/roms/ipxe/src/arch/i386/prefix/pciromprefix.S @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2014 Michael Brown . + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ) + +#define BUSTYPE "PCIR" +#define _rom_start _pcirom_start +#include "romprefix.S" diff --git a/roms/ipxe/src/arch/i386/prefix/romprefix.S b/roms/ipxe/src/arch/i386/prefix/romprefix.S index ae18f05..7bc4fe8 100644 --- a/roms/ipxe/src/arch/i386/prefix/romprefix.S +++ b/roms/ipxe/src/arch/i386/prefix/romprefix.S @@ -46,6 +46,12 @@ FILE_LICENCE ( GPL2_OR_LATER ) #define INDICATOR 0x80 #endif +/* Default to building a PCI ROM if no bus type is specified + */ +#ifndef BUSTYPE +#define BUSTYPE "PCIR" +#endif + .text .code16 .arch i386 @@ -64,8 +70,10 @@ checksum: .word ipxeheader .org 0x16 .word undiheader +.ifeqs BUSTYPE, "PCIR" .org 0x18 .word pciheader +.endif .org 0x1a .word pnpheader .size romheader, . - romheader @@ -77,6 +85,7 @@ checksum: .long 0 .previous +.ifeqs BUSTYPE, "PCIR" pciheader: .ascii "PCIR" /* Signature */ .word pci_vendor_id /* Vendor identification */ @@ -107,6 +116,7 @@ pciheader_runtime_length: .long 512 .long 0 .previous +.endif /* PCIR */ /* PnP doesn't require any particular alignment, but IBM * BIOSes will scan on 16-byte boundaries rather than using @@ -148,11 +158,14 @@ mfgstr: */ prodstr: .ascii PRODUCT_SHORT_NAME +.ifeqs BUSTYPE, "PCIR" prodstr_separator: .byte 0 .ascii "(PCI " prodstr_pci_id: - .asciz "xx:xx.x)" /* Filled in by init code */ + .ascii "xx:xx.x)" /* Filled in by init code */ +.endif /* PCIR */ + .byte 0 .size prodstr, . - prodstr .globl undiheader @@ -167,7 +180,7 @@ undiheader: .word _data16_memsz /* Stack segment size */ .word _data16_memsz /* Data segment size */ .word _text16_memsz /* Code segment size */ - .ascii "PCIR" /* Bus type */ + .ascii BUSTYPE /* Bus type */ .equ undiheader_len, . - undiheader .size undiheader, . - undiheader @@ -208,38 +221,39 @@ init: pushw %cs popw %ds - /* Shuffle some registers around. We need %di available for - * the print_xxx functions, and in a register that's - * addressable from %es, so shuffle as follows: - * - * %di (pointer to PnP structure) => %bx - * %bx (runtime segment address, for PCI 3.0) => %gs - */ - movw %bx, %gs - movw %di, %bx - - /* Store PCI bus:dev.fn address */ - movw %ax, init_pci_busdevfn - /* Print message as early as possible */ movw $init_message, %si xorw %di, %di call print_message - call print_pci_busdevfn - /* Fill in product name string, if possible */ + /* Store PCI 3.0 runtime segment address for later use, if + * applicable. + */ +.ifeqs BUSTYPE, "PCIR" + movw %bx, %gs +.endif + + /* Store PCI bus:dev.fn address, print PCI bus:dev.fn, and add + * PCI bus:dev.fn to product name string, if applicable. + */ +.ifeqs BUSTYPE, "PCIR" + xorw %di, %di + call print_space + movw %ax, init_pci_busdevfn + call print_pci_busdevfn movw $prodstr_pci_id, %di call print_pci_busdevfn movb $( ' ' ), prodstr_separator +.endif /* Print segment address */ - movb $( ' ' ), %al xorw %di, %di - call print_character + call print_space movw %cs, %ax call print_hex_word - /* Check for PCI BIOS version */ + /* Check for PCI BIOS version, if applicable */ +.ifeqs BUSTYPE, "PCIR" pushl %ebx pushl %edx pushl %edi @@ -290,6 +304,7 @@ no_pci3: 1: popl %edi popl %edx popl %ebx +.endif /* PCIR */ /* Check for PnP BIOS. Although %es:di should point to the * PnP BIOS signature on entry, some BIOSes fail to do this. @@ -403,13 +418,13 @@ no_pmm: loop 1b subb %bl, checksum - /* Copy self to option ROM space. Required for PCI3.0, which - * loads us to a temporary location in low memory. Will be a - * no-op for lower PCI versions. + /* Copy self to option ROM space, if applicable. Required for + * PCI3.0, which loads us to a temporary location in low + * memory. Will be a no-op for lower PCI versions. */ - movb $( ' ' ), %al +.ifeqs BUSTYPE, "PCIR" xorw %di, %di - call print_character + call print_space movw %gs, %ax call print_hex_word movzbw romheader_size, %cx @@ -418,10 +433,13 @@ no_pmm: xorw %si, %si xorw %di, %di cs rep movsb +.endif - /* Skip prompt if this is not the first PCI function */ + /* Skip prompt if this is not the first PCI function, if applicable */ +.ifeqs BUSTYPE, "PCIR" testb $PCI_FUNC_MASK, init_pci_busdevfn jnz no_shell +.endif /* Prompt for POST-time shell */ movw $init_message_prompt, %si xorw %di, %di @@ -571,11 +589,13 @@ init_message: .ascii "\n" .ascii PRODUCT_NAME .ascii "\n" - .asciz "iPXE (http://ipxe.org) " + .asciz "iPXE (http://ipxe.org)" .size init_message, . - init_message +.ifeqs BUSTYPE, "PCIR" init_message_pci: .asciz " PCI" .size init_message_pci, . - init_message_pci +.endif /* PCIR */ init_message_pnp: .asciz " PnP" .size init_message_pnp, . - init_message_pnp @@ -598,9 +618,11 @@ init_message_done: /* PCI bus:dev.fn * */ +.ifeqs BUSTYPE, "PCIR" init_pci_busdevfn: .word 0 .size init_pci_busdevfn, . - init_pci_busdevfn +.endif /* PCIR */ /* Image source area * @@ -739,14 +761,18 @@ exec: /* Set %ds = %cs */ lret .section ".text16", "awx", @progbits 1: - /* Retrieve PCI bus:dev.fn */ + /* Retrieve PCI bus:dev.fn, if applicable */ +.ifeqs BUSTYPE, "PCIR" movw init_pci_busdevfn, %ax +.endif /* Set up %ds for access to .data16 */ movw %bx, %ds - /* Store PCI bus:dev.fn */ + /* Store PCI bus:dev.fn, if applicable */ +.ifeqs BUSTYPE, "PCIR" movw %ax, autoboot_busdevfn +.endif /* Call main() */ pushl $main diff --git a/roms/ipxe/src/arch/i386/transitions/librm_mgmt.c b/roms/ipxe/src/arch/i386/transitions/librm_mgmt.c index f90d49b..cc4765d 100644 --- a/roms/ipxe/src/arch/i386/transitions/librm_mgmt.c +++ b/roms/ipxe/src/arch/i386/transitions/librm_mgmt.c @@ -8,6 +8,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include +#include #include #include @@ -20,7 +21,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); extern char interrupt_wrapper[]; /** The interrupt vectors */ -static struct interrupt_vector intr_vec[ IRQ_MAX + 1 ]; +static struct interrupt_vector intr_vec[NUM_INT]; /** The interrupt descriptor table */ struct interrupt_descriptor idt[NUM_INT] __attribute__ (( aligned ( 16 ) )); @@ -30,6 +31,12 @@ struct idtr idtr = { .limit = ( sizeof ( idt ) - 1 ), }; +/** Timer interrupt profiler */ +static struct profiler timer_irq_profiler __profiler = { .name = "irq.timer" }; + +/** Other interrupt profiler */ +static struct profiler other_irq_profiler __profiler = { .name = "irq.other" }; + /** * Allocate space on the real-mode stack and copy data there from a * user buffer @@ -83,13 +90,11 @@ void set_interrupt_vector ( unsigned int intr, void *vector ) { */ void init_idt ( void ) { struct interrupt_vector *vec; - unsigned int irq; unsigned int intr; /* Initialise the interrupt descriptor table and interrupt vectors */ - for ( irq = 0 ; irq <= IRQ_MAX ; irq++ ) { - intr = IRQ_INT ( irq ); - vec = &intr_vec[irq]; + for ( intr = 0 ; intr < NUM_INT ; intr++ ) { + vec = &intr_vec[intr]; vec->pushal = PUSHAL_INSN; vec->movb = MOVB_INSN; vec->intr = intr; @@ -98,24 +103,47 @@ void init_idt ( void ) { ( uint32_t ) vec->next ); set_interrupt_vector ( intr, vec ); } + DBGC ( &intr_vec[0], "INTn vector at %p+%xn (phys %#lx+%xn)\n", + intr_vec, sizeof ( intr_vec[0] ), + virt_to_phys ( intr_vec ), sizeof ( intr_vec[0] ) ); /* Initialise the interrupt descriptor table register */ idtr.base = virt_to_phys ( idt ); } /** + * Determine interrupt profiler (for debugging) + * + * @v intr Interrupt number + * @ret profiler Profiler + */ +static struct profiler * interrupt_profiler ( int intr ) { + + switch ( intr ) { + case IRQ_INT ( 0 ) : + return &timer_irq_profiler; + default: + return &other_irq_profiler; + } +} + +/** * Interrupt handler * - * @v irq Interrupt number + * @v intr Interrupt number */ -void __attribute__ (( cdecl, regparm ( 1 ) )) interrupt ( int irq ) { +void __attribute__ (( cdecl, regparm ( 1 ) )) interrupt ( int intr ) { + struct profiler *profiler = interrupt_profiler ( intr ); uint32_t discard_eax; /* Reissue interrupt in real mode */ + profile_start ( profiler ); __asm__ __volatile__ ( REAL_CODE ( "movb %%al, %%cs:(1f + 1)\n\t" "\n1:\n\t" "int $0x00\n\t" ) - : "=a" ( discard_eax ) : "0" ( irq ) ); + : "=a" ( discard_eax ) : "0" ( intr ) ); + profile_stop ( profiler ); + profile_exclude ( profiler ); } PROVIDE_UACCESS_INLINE ( librm, phys_to_user ); diff --git a/roms/ipxe/src/arch/x86/Makefile b/roms/ipxe/src/arch/x86/Makefile index cdd397d..e555587 100644 --- a/roms/ipxe/src/arch/x86/Makefile +++ b/roms/ipxe/src/arch/x86/Makefile @@ -8,6 +8,7 @@ SRCDIRS += arch/x86/core SRCDIRS += arch/x86/interface/efi SRCDIRS += arch/x86/prefix SRCDIRS += arch/x86/hci/commands +SRCDIRS += arch/x86/drivers/xen # breaks building some of the linux-related objects CFLAGS += -Ulinux diff --git a/roms/ipxe/src/arch/x86/Makefile.efi b/roms/ipxe/src/arch/x86/Makefile.efi index 3e3fbe3..13a69d9 100644 --- a/roms/ipxe/src/arch/x86/Makefile.efi +++ b/roms/ipxe/src/arch/x86/Makefile.efi @@ -15,6 +15,10 @@ NON_AUTO_MEDIA += efidrv NON_AUTO_MEDIA += drv.efi NON_AUTO_MEDIA += efirom +# Include SNP driver in the all-drivers build +# +DRIVERS += snp + # Rules for building EFI files # $(BIN)/%.efi : $(BIN)/%.efi.tmp $(ELF2EFI) diff --git a/roms/ipxe/src/arch/x86/Makefile.linux b/roms/ipxe/src/arch/x86/Makefile.linux index e35b04f..1faf847 100644 --- a/roms/ipxe/src/arch/x86/Makefile.linux +++ b/roms/ipxe/src/arch/x86/Makefile.linux @@ -10,4 +10,4 @@ SRCDIRS += arch/x86/core/linux $(BIN)/%.linux : $(BIN)/%.linux.tmp $(QM)$(ECHO) " [FINISH] $@" - $(Q)cp -p $< $@ + $(Q)$(CP) $< $@ diff --git a/roms/ipxe/src/arch/x86/drivers/xen/hvm.c b/roms/ipxe/src/arch/x86/drivers/xen/hvm.c new file mode 100644 index 0000000..7406ca6 --- /dev/null +++ b/roms/ipxe/src/arch/x86/drivers/xen/hvm.c @@ -0,0 +1,496 @@ +/* + * Copyright (C) 2014 Michael Brown . + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hvm.h" + +/** @file + * + * Xen HVM driver + * + */ + +/** + * Get CPUID base + * + * @v hvm HVM device + * @ret rc Return status code + */ +static int hvm_cpuid_base ( struct hvm_device *hvm ) { + struct { + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + } __attribute__ (( packed )) signature; + uint32_t base; + uint32_t version; + uint32_t discard_eax; + uint32_t discard_ebx; + uint32_t discard_ecx; + uint32_t discard_edx; + + /* Scan for magic signature */ + for ( base = HVM_CPUID_MIN ; base <= HVM_CPUID_MAX ; + base += HVM_CPUID_STEP ) { + cpuid ( base, &discard_eax, &signature.ebx, &signature.ecx, + &signature.edx ); + if ( memcmp ( &signature, HVM_CPUID_MAGIC, + sizeof ( signature ) ) == 0 ) { + hvm->cpuid_base = base; + cpuid ( ( base + HVM_CPUID_VERSION ), &version, + &discard_ebx, &discard_ecx, &discard_edx ); + DBGC2 ( hvm, "HVM using CPUID base %#08x (v%d.%d)\n", + base, ( version >> 16 ), ( version & 0xffff ) ); + return 0; + } + } + + DBGC ( hvm, "HVM could not find hypervisor\n" ); + return -ENODEV; +} + +/** + * Map hypercall page(s) + * + * @v hvm HVM device + * @ret rc Return status code + */ +static int hvm_map_hypercall ( struct hvm_device *hvm ) { + uint32_t pages; + uint32_t msr; + uint32_t discard_ecx; + uint32_t discard_edx; + physaddr_t hypercall_phys; + uint32_t version; + static xen_extraversion_t extraversion; + int xenrc; + int rc; + + /* Get number of hypercall pages and MSR to use */ + cpuid ( ( hvm->cpuid_base + HVM_CPUID_PAGES ), &pages, &msr, + &discard_ecx, &discard_edx ); + + /* Allocate pages */ + hvm->hypercall_len = ( pages * PAGE_SIZE ); + hvm->xen.hypercall = malloc_dma ( hvm->hypercall_len, PAGE_SIZE ); + if ( ! hvm->xen.hypercall ) { + DBGC ( hvm, "HVM could not allocate %d hypercall page(s)\n", + pages ); + return -ENOMEM; + } + hypercall_phys = virt_to_phys ( hvm->xen.hypercall ); + DBGC2 ( hvm, "HVM hypercall page(s) at [%#08lx,%#08lx) via MSR %#08x\n", + hypercall_phys, ( hypercall_phys + hvm->hypercall_len ), msr ); + + /* Write to MSR */ + wrmsr ( msr, hypercall_phys ); + + /* Check that hypercall mechanism is working */ + version = xenver_version ( &hvm->xen ); + if ( ( xenrc = xenver_extraversion ( &hvm->xen, &extraversion ) ) != 0){ + rc = -EXEN ( xenrc ); + DBGC ( hvm, "HVM could not get extraversion: %s\n", + strerror ( rc ) ); + return rc; + } + DBGC2 ( hvm, "HVM found Xen version %d.%d%s\n", + ( version >> 16 ), ( version & 0xffff ) , extraversion ); + + return 0; +} + +/** + * Unmap hypercall page(s) + * + * @v hvm HVM device + */ +static void hvm_unmap_hypercall ( struct hvm_device *hvm ) { + + /* Free pages */ + free_dma ( hvm->xen.hypercall, hvm->hypercall_len ); +} + +/** + * Allocate and map MMIO space + * + * @v hvm HVM device + * @v space Source mapping space + * @v len Length (must be a multiple of PAGE_SIZE) + * @ret mmio MMIO space address, or NULL on error + */ +static void * hvm_ioremap ( struct hvm_device *hvm, unsigned int space, + size_t len ) { + struct xen_add_to_physmap add; + struct xen_remove_from_physmap remove; + unsigned int pages = ( len / PAGE_SIZE ); + physaddr_t mmio_phys; + unsigned int i; + void *mmio; + int xenrc; + int rc; + + /* Sanity check */ + assert ( ( len % PAGE_SIZE ) == 0 ); + + /* Check for available space */ + if ( ( hvm->mmio_offset + len ) > hvm->mmio_len ) { + DBGC ( hvm, "HVM could not allocate %zd bytes of MMIO space " + "(%zd of %zd remaining)\n", len, + ( hvm->mmio_len - hvm->mmio_offset ), hvm->mmio_len ); + goto err_no_space; + } + + /* Map this space */ + mmio = ioremap ( ( hvm->mmio + hvm->mmio_offset ), len ); + if ( ! mmio ) { + DBGC ( hvm, "HVM could not map MMIO space [%08lx,%08lx)\n", + ( hvm->mmio + hvm->mmio_offset ), + ( hvm->mmio + hvm->mmio_offset + len ) ); + goto err_ioremap; + } + mmio_phys = virt_to_phys ( mmio ); + + /* Add to physical address space */ + for ( i = 0 ; i < pages ; i++ ) { + add.domid = DOMID_SELF; + add.idx = i; + add.space = space; + add.gpfn = ( ( mmio_phys / PAGE_SIZE ) + i ); + if ( ( xenrc = xenmem_add_to_physmap ( &hvm->xen, &add ) ) !=0){ + rc = -EXEN ( xenrc ); + DBGC ( hvm, "HVM could not add space %d idx %d at " + "[%08lx,%08lx): %s\n", space, i, + ( mmio_phys + ( i * PAGE_SIZE ) ), + ( mmio_phys + ( ( i + 1 ) * PAGE_SIZE ) ), + strerror ( rc ) ); + goto err_add_to_physmap; + } + } + + /* Update offset */ + hvm->mmio_offset += len; + + return mmio; + + i = pages; + err_add_to_physmap: + for ( i-- ; ( signed int ) i >= 0 ; i-- ) { + remove.domid = DOMID_SELF; + add.gpfn = ( ( mmio_phys / PAGE_SIZE ) + i ); + xenmem_remove_from_physmap ( &hvm->xen, &remove ); + } + iounmap ( mmio ); + err_ioremap: + err_no_space: + return NULL; +} + +/** + * Unmap MMIO space + * + * @v hvm HVM device + * @v mmio MMIO space address + * @v len Length (must be a multiple of PAGE_SIZE) + */ +static void hvm_iounmap ( struct hvm_device *hvm, void *mmio, size_t len ) { + struct xen_remove_from_physmap remove; + physaddr_t mmio_phys = virt_to_phys ( mmio ); + unsigned int pages = ( len / PAGE_SIZE ); + unsigned int i; + int xenrc; + int rc; + + /* Unmap this space */ + iounmap ( mmio ); + + /* Remove from physical address space */ + for ( i = 0 ; i < pages ; i++ ) { + remove.domid = DOMID_SELF; + remove.gpfn = ( ( mmio_phys / PAGE_SIZE ) + i ); + if ( ( xenrc = xenmem_remove_from_physmap ( &hvm->xen, + &remove ) ) != 0 ) { + rc = -EXEN ( xenrc ); + DBGC ( hvm, "HVM could not remove space [%08lx,%08lx): " + "%s\n", ( mmio_phys + ( i * PAGE_SIZE ) ), + ( mmio_phys + ( ( i + 1 ) * PAGE_SIZE ) ), + strerror ( rc ) ); + /* Nothing we can do about this */ + } + } +} + +/** + * Map shared info page + * + * @v hvm HVM device + * @ret rc Return status code + */ +static int hvm_map_shared_info ( struct hvm_device *hvm ) { + physaddr_t shared_info_phys; + int rc; + + /* Map shared info page */ + hvm->xen.shared = hvm_ioremap ( hvm, XENMAPSPACE_shared_info, + PAGE_SIZE ); + if ( ! hvm->xen.shared ) { + rc = -ENOMEM; + goto err_alloc; + } + shared_info_phys = virt_to_phys ( hvm->xen.shared ); + DBGC2 ( hvm, "HVM shared info page at [%#08lx,%#08lx)\n", + shared_info_phys, ( shared_info_phys + PAGE_SIZE ) ); + + /* Sanity check */ + DBGC2 ( hvm, "HVM wallclock time is %d\n", + readl ( &hvm->xen.shared->wc_sec ) ); + + return 0; + + hvm_iounmap ( hvm, hvm->xen.shared, PAGE_SIZE ); + err_alloc: + return rc; +} + +/** + * Unmap shared info page + * + * @v hvm HVM device + */ +static void hvm_unmap_shared_info ( struct hvm_device *hvm ) { + + /* Unmap shared info page */ + hvm_iounmap ( hvm, hvm->xen.shared, PAGE_SIZE ); +} + +/** + * Map grant table + * + * @v hvm HVM device + * @ret rc Return status code + */ +static int hvm_map_grant ( struct hvm_device *hvm ) { + physaddr_t grant_phys; + int rc; + + /* Initialise grant table */ + if ( ( rc = xengrant_init ( &hvm->xen ) ) != 0 ) { + DBGC ( hvm, "HVM could not initialise grant table: %s\n", + strerror ( rc ) ); + return rc; + } + + /* Map grant table */ + hvm->xen.grant.table = hvm_ioremap ( hvm, XENMAPSPACE_grant_table, + hvm->xen.grant.len ); + if ( ! hvm->xen.grant.table ) + return -ENODEV; + + grant_phys = virt_to_phys ( hvm->xen.grant.table ); + DBGC2 ( hvm, "HVM mapped grant table at [%08lx,%08lx)\n", + grant_phys, ( grant_phys + hvm->xen.grant.len ) ); + return 0; +} + +/** + * Unmap grant table + * + * @v hvm HVM device + */ +static void hvm_unmap_grant ( struct hvm_device *hvm ) { + + /* Unmap grant table */ + hvm_iounmap ( hvm, hvm->xen.grant.table, hvm->xen.grant.len ); +} + +/** + * Map XenStore + * + * @v hvm HVM device + * @ret rc Return status code + */ +static int hvm_map_xenstore ( struct hvm_device *hvm ) { + uint64_t xenstore_evtchn; + uint64_t xenstore_pfn; + physaddr_t xenstore_phys; + char *name; + int xenrc; + int rc; + + /* Get XenStore event channel */ + if ( ( xenrc = xen_hvm_get_param ( &hvm->xen, HVM_PARAM_STORE_EVTCHN, + &xenstore_evtchn ) ) != 0 ) { + rc = -EXEN ( xenrc ); + DBGC ( hvm, "HVM could not get XenStore event channel: %s\n", + strerror ( rc ) ); + return rc; + } + hvm->xen.store.port = xenstore_evtchn; + + /* Get XenStore PFN */ + if ( ( xenrc = xen_hvm_get_param ( &hvm->xen, HVM_PARAM_STORE_PFN, + &xenstore_pfn ) ) != 0 ) { + rc = -EXEN ( xenrc ); + DBGC ( hvm, "HVM could not get XenStore PFN: %s\n", + strerror ( rc ) ); + return rc; + } + xenstore_phys = ( xenstore_pfn * PAGE_SIZE ); + + /* Map XenStore */ + hvm->xen.store.intf = ioremap ( xenstore_phys, PAGE_SIZE ); + if ( ! hvm->xen.store.intf ) { + DBGC ( hvm, "HVM could not map XenStore at [%08lx,%08lx)\n", + xenstore_phys, ( xenstore_phys + PAGE_SIZE ) ); + return -ENODEV; + } + DBGC2 ( hvm, "HVM mapped XenStore at [%08lx,%08lx) with event port " + "%d\n", xenstore_phys, ( xenstore_phys + PAGE_SIZE ), + hvm->xen.store.port ); + + /* Check that XenStore is working */ + if ( ( rc = xenstore_read ( &hvm->xen, &name, "name", NULL ) ) != 0 ) { + DBGC ( hvm, "HVM could not read domain name: %s\n", + strerror ( rc ) ); + return rc; + } + DBGC2 ( hvm, "HVM running in domain \"%s\"\n", name ); + free ( name ); + + return 0; +} + +/** + * Unmap XenStore + * + * @v hvm HVM device + */ +static void hvm_unmap_xenstore ( struct hvm_device *hvm ) { + + /* Unmap XenStore */ + iounmap ( hvm->xen.store.intf ); +} + +/** + * Probe PCI device + * + * @v pci PCI device + * @ret rc Return status code + */ +static int hvm_probe ( struct pci_device *pci ) { + struct hvm_device *hvm; + int rc; + + /* Allocate and initialise structure */ + hvm = zalloc ( sizeof ( *hvm ) ); + if ( ! hvm ) { + rc = -ENOMEM; + goto err_alloc; + } + hvm->mmio = pci_bar_start ( pci, HVM_MMIO_BAR ); + hvm->mmio_len = pci_bar_size ( pci, HVM_MMIO_BAR ); + DBGC2 ( hvm, "HVM has MMIO space [%08lx,%08lx)\n", + hvm->mmio, ( hvm->mmio + hvm->mmio_len ) ); + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Attach to hypervisor */ + if ( ( rc = hvm_cpuid_base ( hvm ) ) != 0 ) + goto err_cpuid_base; + if ( ( rc = hvm_map_hypercall ( hvm ) ) != 0 ) + goto err_map_hypercall; + if ( ( rc = hvm_map_shared_info ( hvm ) ) != 0 ) + goto err_map_shared_info; + if ( ( rc = hvm_map_grant ( hvm ) ) != 0 ) + goto err_map_grant; + if ( ( rc = hvm_map_xenstore ( hvm ) ) != 0 ) + goto err_map_xenstore; + + /* Probe Xen devices */ + if ( ( rc = xenbus_probe ( &hvm->xen, &pci->dev ) ) != 0 ) { + DBGC ( hvm, "HVM could not probe Xen bus: %s\n", + strerror ( rc ) ); + goto err_xenbus_probe; + } + + pci_set_drvdata ( pci, hvm ); + return 0; + + xenbus_remove ( &hvm->xen, &pci->dev ); + err_xenbus_probe: + hvm_unmap_xenstore ( hvm ); + err_map_xenstore: + hvm_unmap_grant ( hvm ); + err_map_grant: + hvm_unmap_shared_info ( hvm ); + err_map_shared_info: + hvm_unmap_hypercall ( hvm ); + err_map_hypercall: + err_cpuid_base: + free ( hvm ); + err_alloc: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void hvm_remove ( struct pci_device *pci ) { + struct hvm_device *hvm = pci_get_drvdata ( pci ); + + xenbus_remove ( &hvm->xen, &pci->dev ); + hvm_unmap_xenstore ( hvm ); + hvm_unmap_grant ( hvm ); + hvm_unmap_shared_info ( hvm ); + hvm_unmap_hypercall ( hvm ); + free ( hvm ); +} + +/** PCI device IDs */ +static struct pci_device_id hvm_ids[] = { + PCI_ROM ( 0x5853, 0x0001, "hvm", "hvm", 0 ), + PCI_ROM ( 0x5853, 0x0002, "hvm2", "hvm2", 0 ), +}; + +/** PCI driver */ +struct pci_driver hvm_driver __pci_driver = { + .ids = hvm_ids, + .id_count = ( sizeof ( hvm_ids ) / sizeof ( hvm_ids[0] ) ), + .probe = hvm_probe, + .remove = hvm_remove, +}; + +/* Drag in netfront driver */ +REQUIRE_OBJECT ( netfront ); diff --git a/roms/ipxe/src/arch/x86/drivers/xen/hvm.h b/roms/ipxe/src/arch/x86/drivers/xen/hvm.h new file mode 100644 index 0000000..325d20d --- /dev/null +++ b/roms/ipxe/src/arch/x86/drivers/xen/hvm.h @@ -0,0 +1,75 @@ +#ifndef _HVM_H +#define _HVM_H + +/** @file + * + * Xen HVM driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include + +/** Minimum CPUID base */ +#define HVM_CPUID_MIN 0x40000000UL + +/** Maximum CPUID base */ +#define HVM_CPUID_MAX 0x4000ff00UL + +/** Increment between CPUID bases */ +#define HVM_CPUID_STEP 0x00000100UL + +/** Magic signature */ +#define HVM_CPUID_MAGIC "XenVMMXenVMM" + +/** Get Xen version */ +#define HVM_CPUID_VERSION 1 + +/** Get number of hypercall pages */ +#define HVM_CPUID_PAGES 2 + +/** PCI MMIO BAR */ +#define HVM_MMIO_BAR PCI_BASE_ADDRESS_1 + +/** A Xen HVM device */ +struct hvm_device { + /** Xen hypervisor */ + struct xen_hypervisor xen; + /** CPUID base */ + uint32_t cpuid_base; + /** Length of hypercall table */ + size_t hypercall_len; + /** MMIO base address */ + unsigned long mmio; + /** Current offset within MMIO address space */ + size_t mmio_offset; + /** Length of MMIO address space */ + size_t mmio_len; +}; + +/** + * Get HVM parameter value + * + * @v xen Xen hypervisor + * @v index Parameter index + * @v value Value to fill in + * @ret xenrc Xen status code + */ +static inline int xen_hvm_get_param ( struct xen_hypervisor *xen, + unsigned int index, uint64_t *value ) { + struct xen_hvm_param param; + int xenrc; + + param.domid = DOMID_SELF; + param.index = index; + xenrc = xen_hypercall_2 ( xen, __HYPERVISOR_hvm_op, HVMOP_get_param, + virt_to_phys ( ¶m ) ); + *value = param.value; + return xenrc; +} + +#endif /* _HVM_H */ diff --git a/roms/ipxe/src/arch/x86/include/bits/errfile.h b/roms/ipxe/src/arch/x86/include/bits/errfile.h index acf8c3e..6245756 100644 --- a/roms/ipxe/src/arch/x86/include/bits/errfile.h +++ b/roms/ipxe/src/arch/x86/include/bits/errfile.h @@ -45,6 +45,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_timer_rdtsc ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00000000 ) #define ERRFILE_timer_bios ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00010000 ) +#define ERRFILE_hvm ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00020000 ) #define ERRFILE_cpuid_cmd ( ERRFILE_ARCH | ERRFILE_OTHER | 0x00000000 ) #define ERRFILE_cpuid_settings ( ERRFILE_ARCH | ERRFILE_OTHER | 0x00010000 ) diff --git a/roms/ipxe/src/arch/x86/include/bits/xen.h b/roms/ipxe/src/arch/x86/include/bits/xen.h new file mode 100644 index 0000000..dbccf1b --- /dev/null +++ b/roms/ipxe/src/arch/x86/include/bits/xen.h @@ -0,0 +1,164 @@ +#ifndef _BITS_XEN_H +#define _BITS_XEN_H + +/** @file + * + * Xen interface + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/* Hypercall registers */ +#ifdef __x86_64__ +#define XEN_REG1 "rdi" +#define XEN_REG2 "rsi" +#define XEN_REG3 "rdx" +#define XEN_REG4 "r10" +#define XEN_REG5 "r8" +#else +#define XEN_REG1 "ebx" +#define XEN_REG2 "ecx" +#define XEN_REG3 "edx" +#define XEN_REG4 "esi" +#define XEN_REG5 "edi" +#endif + +/** A hypercall entry point */ +struct xen_hypercall { + /** Code generated by hypervisor */ + uint8_t code[32]; +} __attribute__ (( packed )); + +/** + * Issue hypercall with one argument + * + * @v xen Xen hypervisor + * @v hypercall Hypercall number + * @v arg1 First argument + * @ret retval Return value + */ +static inline __attribute__ (( always_inline )) unsigned long +xen_hypercall_1 ( struct xen_hypervisor *xen, unsigned int hypercall, + unsigned long arg1 ) { + register unsigned long reg1 asm ( XEN_REG1 ) = arg1; + unsigned long retval; + + __asm__ __volatile__ ( "call *%2" + : "=a" ( retval ), "+r" ( reg1 ) + : "r" ( &xen->hypercall[hypercall] ) + : XEN_REG2, XEN_REG3, XEN_REG4, XEN_REG5, + "memory" ); + return retval; +} + +/** + * Issue hypercall with two arguments + * + * @v xen Xen hypervisor + * @v hypercall Hypercall number + * @v arg1 First argument + * @v arg2 Second argument + * @ret retval Return value + */ +static inline __attribute__ (( always_inline )) unsigned long +xen_hypercall_2 ( struct xen_hypervisor *xen, unsigned int hypercall, + unsigned long arg1, unsigned long arg2 ) { + register unsigned long reg1 asm ( XEN_REG1 ) = arg1; + register unsigned long reg2 asm ( XEN_REG2 ) = arg2; + unsigned long retval; + + __asm__ __volatile__ ( "call *%3" + : "=a" ( retval ), "+r" ( reg1 ), "+r" ( reg2 ) + : "r" ( &xen->hypercall[hypercall] ) + : XEN_REG3, XEN_REG4, XEN_REG5, "memory" ); + return retval; +} + +/** + * Issue hypercall with three arguments + * + * @v xen Xen hypervisor + * @v hypercall Hypercall number + * @v arg1 First argument + * @v arg2 Second argument + * @v arg3 Third argument + * @ret retval Return value + */ +static inline __attribute__ (( always_inline )) unsigned long +xen_hypercall_3 ( struct xen_hypervisor *xen, unsigned int hypercall, + unsigned long arg1, unsigned long arg2, unsigned long arg3 ) { + register unsigned long reg1 asm ( XEN_REG1 ) = arg1; + register unsigned long reg2 asm ( XEN_REG2 ) = arg2; + register unsigned long reg3 asm ( XEN_REG3 ) = arg3; + unsigned long retval; + + __asm__ __volatile__ ( "call *%4" + : "=a" ( retval ), "+r" ( reg1 ), "+r" ( reg2 ), + "+r" ( reg3 ) + : "r" ( &xen->hypercall[hypercall] ) + : XEN_REG4, XEN_REG5, "memory" ); + return retval; +} + +/** + * Issue hypercall with four arguments + * + * @v xen Xen hypervisor + * @v hypercall Hypercall number + * @v arg1 First argument + * @v arg2 Second argument + * @v arg3 Third argument + * @v arg4 Fourth argument + * @ret retval Return value + */ +static inline __attribute__ (( always_inline )) unsigned long +xen_hypercall_4 ( struct xen_hypervisor *xen, unsigned int hypercall, + unsigned long arg1, unsigned long arg2, unsigned long arg3, + unsigned long arg4 ) { + register unsigned long reg1 asm ( XEN_REG1 ) = arg1; + register unsigned long reg2 asm ( XEN_REG2 ) = arg2; + register unsigned long reg3 asm ( XEN_REG3 ) = arg3; + register unsigned long reg4 asm ( XEN_REG4 ) = arg4; + unsigned long retval; + + __asm__ __volatile__ ( "call *%5" + : "=a" ( retval ), "+r" ( reg1 ), "+r" ( reg2 ), + "+r" ( reg3 ), "+r" ( reg4 ) + : "r" ( &xen->hypercall[hypercall] ) + : XEN_REG5, "memory" ); + return retval; +} + +/** + * Issue hypercall with five arguments + * + * @v xen Xen hypervisor + * @v hypercall Hypercall number + * @v arg1 First argument + * @v arg2 Second argument + * @v arg3 Third argument + * @v arg4 Fourth argument + * @v arg5 Fifth argument + * @ret retval Return value + */ +static inline __attribute__ (( always_inline )) unsigned long +xen_hypercall_5 ( struct xen_hypervisor *xen, unsigned int hypercall, + unsigned long arg1, unsigned long arg2, unsigned long arg3, + unsigned long arg4, unsigned long arg5 ) { + register unsigned long reg1 asm ( XEN_REG1 ) = arg1; + register unsigned long reg2 asm ( XEN_REG2 ) = arg2; + register unsigned long reg3 asm ( XEN_REG3 ) = arg3; + register unsigned long reg4 asm ( XEN_REG4 ) = arg4; + register unsigned long reg5 asm ( XEN_REG5 ) = arg5; + unsigned long retval; + + __asm__ __volatile__ ( "call *%6" + : "=a" ( retval ), "+r" ( reg1 ), "+r" ( reg2 ), + "+r" ( reg3 ), "+r" ( reg4 ), "+r" ( reg5 ) + : "r" ( &xen->hypercall[hypercall] ) + : "memory" ); + return retval; +} + +#endif /* _BITS_XEN_H */ diff --git a/roms/ipxe/src/arch/x86/include/ipxe/x86_io.h b/roms/ipxe/src/arch/x86/include/ipxe/x86_io.h index adb00a6..9e68f4e 100644 --- a/roms/ipxe/src/arch/x86/include/ipxe/x86_io.h +++ b/roms/ipxe/src/arch/x86/include/ipxe/x86_io.h @@ -28,6 +28,9 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +/** Page shift */ +#define PAGE_SHIFT 12 + /* * Physical<->Bus and Bus<->I/O address mappings * @@ -45,7 +48,7 @@ IOAPI_INLINE ( x86, bus_to_phys ) ( unsigned long bus_addr ) { static inline __always_inline void * IOAPI_INLINE ( x86, ioremap ) ( unsigned long bus_addr, size_t len __unused ) { - return phys_to_virt ( bus_addr ); + return ( bus_addr ? phys_to_virt ( bus_addr ) : NULL ); } static inline __always_inline void diff --git a/roms/ipxe/src/arch/x86/prefix/efidrvprefix.c b/roms/ipxe/src/arch/x86/prefix/efidrvprefix.c index 280e335..3daefd0 100644 --- a/roms/ipxe/src/arch/x86/prefix/efidrvprefix.c +++ b/roms/ipxe/src/arch/x86/prefix/efidrvprefix.c @@ -22,7 +22,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include -#include /** * EFI entry point @@ -43,8 +42,5 @@ EFI_STATUS EFIAPI _efidrv_start ( EFI_HANDLE image_handle, initialise(); startup(); - /* Release network devices for use via SNP */ - efi_snp_release(); - return 0; } diff --git a/roms/ipxe/src/arch/x86/prefix/efiprefix.c b/roms/ipxe/src/arch/x86/prefix/efiprefix.c index eb8aa73..b0bf99c 100644 --- a/roms/ipxe/src/arch/x86/prefix/efiprefix.c +++ b/roms/ipxe/src/arch/x86/prefix/efiprefix.c @@ -21,7 +21,11 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include +#include #include +#include +#include +#include /** * EFI entry point @@ -39,6 +43,12 @@ EFI_STATUS EFIAPI _efi_start ( EFI_HANDLE image_handle, if ( ( efirc = efi_init ( image_handle, systab ) ) != 0 ) goto err_init; + /* Record autoboot device (if any) */ + efi_set_autoboot(); + + /* Claim SNP devices for use by iPXE */ + efi_snp_claim(); + /* Call to main() */ if ( ( rc = main() ) != 0 ) { efirc = EFIRC ( rc ); @@ -46,7 +56,41 @@ EFI_STATUS EFIAPI _efi_start ( EFI_HANDLE image_handle, } err_main: + efi_snp_release(); efi_loaded_image->Unload ( image_handle ); + efi_driver_reconnect_all(); err_init: return efirc; } + +/** + * Probe EFI root bus + * + * @v rootdev EFI root device + */ +static int efi_probe ( struct root_device *rootdev __unused ) { + + return efi_driver_connect_all(); +} + +/** + * Remove EFI root bus + * + * @v rootdev EFI root device + */ +static void efi_remove ( struct root_device *rootdev __unused ) { + + efi_driver_disconnect_all(); +} + +/** EFI root device driver */ +static struct root_driver efi_root_driver = { + .probe = efi_probe, + .remove = efi_remove, +}; + +/** EFI root device */ +struct root_device efi_root_device __root_device = { + .dev = { .name = "EFI" }, + .driver = &efi_root_driver, +}; diff --git a/roms/ipxe/src/arch/x86_64/include/ipxe/msr.h b/roms/ipxe/src/arch/x86_64/include/ipxe/msr.h new file mode 100644 index 0000000..a5816ac --- /dev/null +++ b/roms/ipxe/src/arch/x86_64/include/ipxe/msr.h @@ -0,0 +1,43 @@ +#ifndef _IPXE_MSR_H +#define _IPXE_MSR_H + +/** @file + * + * Model-specific registers + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** + * Read model-specific register + * + * @v msr Model-specific register + * @ret value Value + */ +static inline __attribute__ (( always_inline )) uint64_t +rdmsr ( unsigned int msr ) { + uint32_t high; + uint32_t low; + + __asm__ __volatile__ ( "rdmsr" : + "=d" ( high ), "=a" ( low ) : "c" ( msr ) ); + return ( ( ( ( uint64_t ) high ) << 32 ) | low ); +} + +/** + * Write model-specific register + * + * @v msr Model-specific register + * @v value Value + */ +static inline __attribute__ (( always_inline )) void +wrmsr ( unsigned int msr, uint64_t value ) { + uint32_t high = ( value >> 32 ); + uint32_t low = ( value >> 0 ); + + __asm__ __volatile__ ( "wrmsr" : : + "c" ( msr ), "d" ( high ), "a" ( low ) ); +} + +#endif /* _IPXE_MSR_H */ diff --git a/roms/ipxe/src/config/colour.h b/roms/ipxe/src/config/colour.h index c75f65e..57d20c1 100644 --- a/roms/ipxe/src/config/colour.h +++ b/roms/ipxe/src/config/colour.h @@ -30,6 +30,9 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define COLOR_PXE_FG COLOR_BLACK #define COLOR_PXE_BG COLOR_WHITE +#include +#include NAMED_CONFIG(colour.h) #include +#include LOCAL_NAMED_CONFIG(colour.h) #endif /* CONFIG_COLOUR_H */ diff --git a/roms/ipxe/src/config/console.h b/roms/ipxe/src/config/console.h index 5d2cc1d..908ec5a 100644 --- a/roms/ipxe/src/config/console.h +++ b/roms/ipxe/src/config/console.h @@ -28,6 +28,9 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define LOG_LEVEL LOG_NONE +#include +#include NAMED_CONFIG(console.h) #include +#include LOCAL_NAMED_CONFIG(console.h) #endif /* CONFIG_CONSOLE_H */ diff --git a/roms/ipxe/src/config/crypto.h b/roms/ipxe/src/config/crypto.h index 95c73d4..1e021b0 100644 --- a/roms/ipxe/src/config/crypto.h +++ b/roms/ipxe/src/config/crypto.h @@ -17,6 +17,9 @@ FILE_LICENCE ( GPL2_OR_LATER ); */ #define TIMESTAMP_ERROR_MARGIN ( ( 12 * 60 + 30 ) * 60 ) +#include +#include NAMED_CONFIG(crypto.h) #include +#include LOCAL_NAMED_CONFIG(crypto.h) #endif /* CONFIG_CRYPTO_H */ diff --git a/roms/ipxe/src/config/general.h b/roms/ipxe/src/config/general.h index 72cfc3b..5392034 100644 --- a/roms/ipxe/src/config/general.h +++ b/roms/ipxe/src/config/general.h @@ -182,6 +182,9 @@ FILE_LICENCE ( GPL2_OR_LATER ); #undef GDBUDP /* Remote GDB debugging over UDP * (both may be set) */ +#include +#include NAMED_CONFIG(general.h) #include +#include LOCAL_NAMED_CONFIG(general.h) #endif /* CONFIG_GENERAL_H */ diff --git a/roms/ipxe/src/config/named.h b/roms/ipxe/src/config/named.h new file mode 100644 index 0000000..36efdab --- /dev/null +++ b/roms/ipxe/src/config/named.h @@ -0,0 +1,26 @@ +#ifndef CONFIG_NAMED_H +#define CONFIG_NAMED_H + +/** @file + * + * Named configurations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/* config//
    .h */ +#ifdef CONFIG +#define NAMED_CONFIG(_header) +#else +#define NAMED_CONFIG(_header) +#endif + +/* config/local//
    .h */ +#ifdef LOCAL_CONFIG +#define LOCAL_NAMED_CONFIG(_header) +#else +#define LOCAL_NAMED_CONFIG(_header) +#endif + +#endif /* CONFIG_NAMED_H */ diff --git a/roms/ipxe/src/config/serial.h b/roms/ipxe/src/config/serial.h index 8bb9311..08368ef 100644 --- a/roms/ipxe/src/config/serial.h +++ b/roms/ipxe/src/config/serial.h @@ -32,6 +32,9 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define COMSTOP 1 /* Stop bits */ #endif +#include +#include NAMED_CONFIG(serial.h) #include +#include LOCAL_NAMED_CONFIG(serial.h) #endif /* CONFIG_SERIAL_H */ diff --git a/roms/ipxe/src/config/settings.h b/roms/ipxe/src/config/settings.h index b06f290..42fe9cc 100644 --- a/roms/ipxe/src/config/settings.h +++ b/roms/ipxe/src/config/settings.h @@ -14,6 +14,9 @@ FILE_LICENCE ( GPL2_OR_LATER ); //#define MEMMAP_SETTINGS /* Memory map settings */ //#define VMWARE_SETTINGS /* VMware GuestInfo settings */ +#include +#include NAMED_CONFIG(settings.h) #include +#include LOCAL_NAMED_CONFIG(settings.h) #endif /* CONFIG_SETTINGS_H */ diff --git a/roms/ipxe/src/config/sideband.h b/roms/ipxe/src/config/sideband.h index 2e2a8d4..039bb5d 100644 --- a/roms/ipxe/src/config/sideband.h +++ b/roms/ipxe/src/config/sideband.h @@ -11,6 +11,9 @@ FILE_LICENCE ( GPL2_OR_LATER ); //#define CONFIG_BOFM /* IBM's BladeCenter Open Fabric Manager */ +#include +#include NAMED_CONFIG(sideband.h) #include +#include LOCAL_NAMED_CONFIG(sideband.h) #endif /* CONFIG_SIDEBAND_H */ diff --git a/roms/ipxe/src/config/vbox/README b/roms/ipxe/src/config/vbox/README new file mode 100644 index 0000000..b6f2da9 --- /dev/null +++ b/roms/ipxe/src/config/vbox/README @@ -0,0 +1,18 @@ +Build using this command line: + +make CONFIG=vbox bin/intel--virtio-net--pcnet32.isarom + +Max size of a VirtualBox ROM is 56KB, 57344 bytes. There should be no need +to pad the image as long as the binary is smaller or equal to this size. + +To use the ROM in VirtualBox you need to enable it using this command: + +vboxmanage setextradata global \ + VBoxInternal/Devices/pcbios/0/Config/LanBootRom \ + /absolute/path/to/intel--virtio-net--pcnet32.isarom + +NB: If you build the ROM using the .rom prefix then it'll be built as a PCI +ROM, which won't work properly in VirtualBox. The error message you'll see +is "No more network devices", which is somewhat confusing. If you enter the +shell and use the "autoboot" command things will work as intended. Remember +to always build as a .isarom to avoid this issue. diff --git a/roms/ipxe/src/config/vbox/colour.h b/roms/ipxe/src/config/vbox/colour.h new file mode 100644 index 0000000..e69de29 diff --git a/roms/ipxe/src/config/vbox/console.h b/roms/ipxe/src/config/vbox/console.h new file mode 100644 index 0000000..e69de29 diff --git a/roms/ipxe/src/config/vbox/crypto.h b/roms/ipxe/src/config/vbox/crypto.h new file mode 100644 index 0000000..e69de29 diff --git a/roms/ipxe/src/config/vbox/general.h b/roms/ipxe/src/config/vbox/general.h new file mode 100644 index 0000000..27d15da --- /dev/null +++ b/roms/ipxe/src/config/vbox/general.h @@ -0,0 +1,27 @@ +/* Disabled from config/defaults/pcbios.h */ + +#undef IMAGE_ELF +#undef SANBOOT_PROTO_ISCSI +#undef SANBOOT_PROTO_AOE +#undef SANBOOT_PROTO_IB_SRP +#undef SANBOOT_PROTO_FCP +#undef REBOOT_CMD +#undef CPUID_CMD + +/* Disabled from config/general.h */ + +#undef DOWNLOAD_PROTO_HTTP +#undef CRYPTO_80211_WEP +#undef CRYPTO_80211_WPA +#undef CRYPTO_80211_WPA2 +#undef IWMGMT_CMD +#undef FCMGMT_CMD +#undef SANBOOT_CMD +#undef MENU_CMD +#undef LOGIN_CMD +#undef SYNC_CMD + +/* Ensure ROM banner is not displayed */ + +#undef ROM_BANNER_TIMEOUT +#define ROM_BANNER_TIMEOUT 0 diff --git a/roms/ipxe/src/config/vbox/serial.h b/roms/ipxe/src/config/vbox/serial.h new file mode 100644 index 0000000..e69de29 diff --git a/roms/ipxe/src/config/vbox/settings.h b/roms/ipxe/src/config/vbox/settings.h new file mode 100644 index 0000000..e69de29 diff --git a/roms/ipxe/src/config/vbox/sideband.h b/roms/ipxe/src/config/vbox/sideband.h new file mode 100644 index 0000000..e69de29 diff --git a/roms/ipxe/src/core/debug.c b/roms/ipxe/src/core/debug.c index 2161f97..7ded470 100644 --- a/roms/ipxe/src/core/debug.c +++ b/roms/ipxe/src/core/debug.c @@ -119,13 +119,24 @@ void dbg_hex_dump_da ( unsigned long dispaddr, const void *data, } /** + * Base message stream colour + * + * We default to using 31 (red foreground) as the base colour. + */ +#ifndef DBGCOL_MIN +#define DBGCOL_MIN 31 +#endif + +/** * Maximum number of separately coloured message streams * * Six is the realistic maximum; there are 8 basic ANSI colours, one * of which will be the terminal default and one of which will be * invisible on the terminal because it matches the background colour. */ -#define NUM_AUTO_COLOURS 6 +#ifndef DBGCOL_MAX +#define DBGCOL_MAX ( DBGCOL_MIN + 6 - 1 ) +#endif /** A colour assigned to an autocolourised debug message stream */ struct autocolour { @@ -142,7 +153,7 @@ struct autocolour { * @ret colour Colour ID */ static int dbg_autocolour ( unsigned long stream ) { - static struct autocolour acs[NUM_AUTO_COLOURS]; + static struct autocolour acs[ DBGCOL_MAX - DBGCOL_MIN + 1 ]; static unsigned long use; unsigned int i; unsigned int oldest; @@ -180,7 +191,7 @@ static int dbg_autocolour ( unsigned long stream ) { */ void dbg_autocolourise ( unsigned long stream ) { dbg_printf ( "\033[%dm", - ( stream ? ( 31 + dbg_autocolour ( stream ) ) : 0 ) ); + ( stream ? ( DBGCOL_MIN + dbg_autocolour ( stream ) ) :0)); } /** diff --git a/roms/ipxe/src/core/main.c b/roms/ipxe/src/core/main.c index c55ca26..db09e4c 100644 --- a/roms/ipxe/src/core/main.c +++ b/roms/ipxe/src/core/main.c @@ -17,8 +17,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include -#include /** * Main entry point @@ -31,7 +31,7 @@ __asmcall int main ( void ) { initialise(); /* Some devices take an unreasonably long time to initialise */ - printf ( PRODUCT_SHORT_NAME " initialising devices..." ); + printf ( "%s initialising devices...", product_short_name ); startup(); printf ( "ok\n" ); diff --git a/roms/ipxe/src/core/malloc.c b/roms/ipxe/src/core/malloc.c index 56ca7ed..d9c0749 100644 --- a/roms/ipxe/src/core/malloc.c +++ b/roms/ipxe/src/core/malloc.c @@ -187,6 +187,42 @@ static inline void valgrind_make_blocks_noaccess ( void ) { } /** + * Check integrity of the blocks in the free list + * + */ +static inline void check_blocks ( void ) { + struct memory_block *block; + struct memory_block *prev = NULL; + + if ( ! ASSERTING ) + return; + + list_for_each_entry ( block, &free_blocks, list ) { + + /* Check that list structure is intact */ + list_check ( &block->list ); + + /* Check that block size is not too small */ + assert ( block->size >= sizeof ( *block ) ); + assert ( block->size >= MIN_MEMBLOCK_SIZE ); + + /* Check that block does not wrap beyond end of address space */ + assert ( ( ( void * ) block + block->size ) > + ( ( void * ) block ) ); + + /* Check that blocks remain in ascending order, and + * that adjacent blocks have been merged. + */ + if ( prev ) { + assert ( ( ( void * ) block ) > ( ( void * ) prev ) ); + assert ( ( ( void * ) block ) > + ( ( ( void * ) prev ) + prev->size ) ); + } + prev = block; + } +} + +/** * Discard some cached data * * @ret discarded Number of cached items discarded @@ -237,7 +273,12 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) { struct memory_block *post; struct memory_block *ptr; + /* Sanity checks */ + assert ( size != 0 ); + assert ( ( align == 0 ) || ( ( align & ( align - 1 ) ) == 0 ) ); + valgrind_make_blocks_defined(); + check_blocks(); /* Round up size to multiple of MIN_MEMBLOCK_SIZE and * calculate alignment mask. @@ -245,7 +286,8 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) { size = ( size + MIN_MEMBLOCK_SIZE - 1 ) & ~( MIN_MEMBLOCK_SIZE - 1 ); align_mask = ( align - 1 ) | ( MIN_MEMBLOCK_SIZE - 1 ); - DBG ( "Allocating %#zx (aligned %#zx+%zx)\n", size, align, offset ); + DBGC2 ( &heap, "Allocating %#zx (aligned %#zx+%zx)\n", + size, align, offset ); while ( 1 ) { /* Search through blocks for the first one with enough space */ list_for_each_entry ( block, &free_blocks, list ) { @@ -261,10 +303,10 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) { pre = block; block = ( ( ( void * ) pre ) + pre_size ); post = ( ( ( void * ) block ) + size ); - DBG ( "[%p,%p) -> [%p,%p) + [%p,%p)\n", pre, - ( ( ( void * ) pre ) + pre->size ), - pre, block, post, - ( ( ( void * ) pre ) + pre->size ) ); + DBGC2 ( &heap, "[%p,%p) -> [%p,%p) + [%p,%p)\n", + pre, ( ( ( void * ) pre ) + pre->size ), + pre, block, post, + ( ( ( void * ) pre ) + pre->size ) ); /* If there is a "post" block, add it in to * the free list. Leak it if it is too small * (which can happen only at the very end of @@ -291,8 +333,8 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) { /* Update total free memory */ freemem -= size; /* Return allocated block */ - DBG ( "Allocated [%p,%p)\n", block, - ( ( ( void * ) block ) + size ) ); + DBGC2 ( &heap, "Allocated [%p,%p)\n", block, + ( ( ( void * ) block ) + size ) ); ptr = block; goto done; } @@ -301,14 +343,15 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) { /* Try discarding some cached data to free up memory */ if ( ! discard_cache() ) { /* Nothing available to discard */ - DBG ( "Failed to allocate %#zx (aligned %#zx)\n", - size, align ); + DBGC ( &heap, "Failed to allocate %#zx (aligned " + "%#zx)\n", size, align ); ptr = NULL; goto done; } } done: + check_blocks(); valgrind_make_blocks_noaccess(); return ptr; } @@ -333,17 +376,38 @@ void free_memblock ( void *ptr, size_t size ) { return; valgrind_make_blocks_defined(); + check_blocks(); /* Round up size to match actual size that alloc_memblock() * would have used. */ + assert ( size != 0 ); size = ( size + MIN_MEMBLOCK_SIZE - 1 ) & ~( MIN_MEMBLOCK_SIZE - 1 ); freeing = ptr; VALGRIND_MAKE_MEM_DEFINED ( freeing, sizeof ( *freeing ) ); - freeing->size = size; - DBG ( "Freeing [%p,%p)\n", freeing, ( ( ( void * ) freeing ) + size )); + DBGC2 ( &heap, "Freeing [%p,%p)\n", + freeing, ( ( ( void * ) freeing ) + size ) ); + + /* Check that this block does not overlap the free list */ + if ( ASSERTING ) { + list_for_each_entry ( block, &free_blocks, list ) { + if ( ( ( ( void * ) block ) < + ( ( void * ) freeing + size ) ) && + ( ( void * ) freeing < + ( ( void * ) block + block->size ) ) ) { + assert ( 0 ); + DBGC ( &heap, "Double free of [%p,%p) " + "overlapping [%p,%p) detected from %p\n", + freeing, + ( ( ( void * ) freeing ) + size ), block, + ( ( void * ) block + block->size ), + __builtin_return_address ( 0 ) ); + } + } + } /* Insert/merge into free list */ + freeing->size = size; list_for_each_entry_safe ( block, tmp, &free_blocks, list ) { /* Calculate gaps before and after the "freeing" block */ gap_before = ( ( ( void * ) freeing ) - @@ -352,10 +416,11 @@ void free_memblock ( void *ptr, size_t size ) { ( ( ( void * ) freeing ) + freeing->size ) ); /* Merge with immediately preceding block, if possible */ if ( gap_before == 0 ) { - DBG ( "[%p,%p) + [%p,%p) -> [%p,%p)\n", block, - ( ( ( void * ) block ) + block->size ), freeing, - ( ( ( void * ) freeing ) + freeing->size ),block, - ( ( ( void * ) freeing ) + freeing->size ) ); + DBGC2 ( &heap, "[%p,%p) + [%p,%p) -> [%p,%p)\n", block, + ( ( ( void * ) block ) + block->size ), freeing, + ( ( ( void * ) freeing ) + freeing->size ), + block, + ( ( ( void * ) freeing ) + freeing->size ) ); block->size += size; list_del ( &block->list ); freeing = block; @@ -369,13 +434,14 @@ void free_memblock ( void *ptr, size_t size ) { * possible, merge the following block into the "freeing" * block. */ - DBG ( "[%p,%p)\n", freeing, ( ( ( void * ) freeing ) + freeing->size)); + DBGC2 ( &heap, "[%p,%p)\n", + freeing, ( ( ( void * ) freeing ) + freeing->size ) ); list_add_tail ( &freeing->list, &block->list ); if ( gap_after == 0 ) { - DBG ( "[%p,%p) + [%p,%p) -> [%p,%p)\n", freeing, - ( ( ( void * ) freeing ) + freeing->size ), block, - ( ( ( void * ) block ) + block->size ), freeing, - ( ( ( void * ) block ) + block->size ) ); + DBGC2 ( &heap, "[%p,%p) + [%p,%p) -> [%p,%p)\n", freeing, + ( ( ( void * ) freeing ) + freeing->size ), block, + ( ( ( void * ) block ) + block->size ), freeing, + ( ( ( void * ) block ) + block->size ) ); freeing->size += block->size; list_del ( &block->list ); } @@ -383,6 +449,7 @@ void free_memblock ( void *ptr, size_t size ) { /* Update free memory counter */ freemem += size; + check_blocks(); valgrind_make_blocks_noaccess(); } @@ -440,6 +507,7 @@ void * realloc ( void *old_ptr, size_t new_size ) { data ); VALGRIND_MAKE_MEM_DEFINED ( old_block, offsetof ( struct autosized_block, data ) ); old_total_size = old_block->size; + assert ( old_total_size != 0 ); old_size = ( old_total_size - offsetof ( struct autosized_block, data ) ); memcpy ( new_ptr, old_ptr, @@ -449,6 +517,10 @@ void * realloc ( void *old_ptr, size_t new_size ) { VALGRIND_FREELIKE_BLOCK ( old_ptr, 0 ); } + if ( ASSERTED ) { + DBGC ( &heap, "Possible memory corruption detected from %p\n", + __builtin_return_address ( 0 ) ); + } return new_ptr; } @@ -462,7 +534,14 @@ void * realloc ( void *old_ptr, size_t new_size ) { * will be aligned to at least a multiple of sizeof(void*). */ void * malloc ( size_t size ) { - return realloc ( NULL, size ); + void *ptr; + + ptr = realloc ( NULL, size ); + if ( ASSERTED ) { + DBGC ( &heap, "Possible memory corruption detected from %p\n", + __builtin_return_address ( 0 ) ); + } + return ptr; } /** @@ -476,7 +555,12 @@ void * malloc ( size_t size ) { * If @c ptr is NULL, no action is taken. */ void free ( void *ptr ) { + realloc ( ptr, 0 ); + if ( ASSERTED ) { + DBGC ( &heap, "Possible memory corruption detected from %p\n", + __builtin_return_address ( 0 ) ); + } } /** @@ -496,6 +580,10 @@ void * zalloc ( size_t size ) { data = malloc ( size ); if ( data ) memset ( data, 0, size ); + if ( ASSERTED ) { + DBGC ( &heap, "Possible memory corruption detected from %p\n", + __builtin_return_address ( 0 ) ); + } return data; } diff --git a/roms/ipxe/src/core/pinger.c b/roms/ipxe/src/core/pinger.c index c912a64..31ea2ce 100644 --- a/roms/ipxe/src/core/pinger.c +++ b/roms/ipxe/src/core/pinger.c @@ -68,10 +68,16 @@ struct pinger { size_t len; /** Current sequence number */ uint16_t sequence; + /** Response for current sequence number is still pending */ + int pending; + /** Number of remaining expiry events (zero to continue indefinitely) */ + unsigned int remaining; + /** Return status */ + int rc; /** Callback function * - * @v src Source socket address + * @v src Source socket address, or NULL * @v sequence Sequence number * @v len Payload length * @v rc Status code @@ -159,6 +165,16 @@ static void pinger_expired ( struct retry_timer *timer, int over __unused ) { struct io_buffer *iobuf; int rc; + /* If no response has been received, notify the callback function */ + if ( pinger->pending && pinger->callback ) + pinger->callback ( NULL, pinger->sequence, 0, -ETIMEDOUT ); + + /* Check for termination */ + if ( pinger->remaining && ( --pinger->remaining == 0 ) ) { + pinger_close ( pinger, pinger->rc ); + return; + } + /* Increase sequence number */ pinger->sequence++; @@ -166,6 +182,7 @@ static void pinger_expired ( struct retry_timer *timer, int over __unused ) { * case the transmission attempt fails. */ start_timer_fixed ( &pinger->timer, pinger->timeout ); + pinger->pending = 1; /* Allocate I/O buffer */ iobuf = xfer_alloc_iob ( &pinger->xfer, pinger->len ); @@ -203,29 +220,56 @@ static int pinger_deliver ( struct pinger *pinger, struct io_buffer *iobuf, struct xfer_metadata *meta ) { size_t len = iob_len ( iobuf ); uint16_t sequence = meta->offset; + int terminate = 0; int rc; + /* Clear response pending flag, if applicable */ + if ( sequence == pinger->sequence ) + pinger->pending = 0; + /* Check for errors */ if ( len != pinger->len ) { + /* Incorrect length: terminate immediately if we are + * not pinging indefinitely. + */ DBGC ( pinger, "PINGER %p received incorrect length %zd " "(expected %zd)\n", pinger, len, pinger->len ); rc = -EPROTO_LEN; + terminate = ( pinger->remaining != 0 ); } else if ( ( rc = pinger_verify ( pinger, iobuf->data ) ) != 0 ) { + /* Incorrect data: terminate immediately if we are not + * pinging indefinitely. + */ DBGC ( pinger, "PINGER %p received incorrect data:\n", pinger ); DBGC_HDA ( pinger, 0, iobuf->data, iob_len ( iobuf ) ); + terminate = ( pinger->remaining != 0 ); } else if ( sequence != pinger->sequence ) { + /* Incorrect sequence number (probably a delayed response): + * report via callback but otherwise ignore. + */ DBGC ( pinger, "PINGER %p received sequence %d (expected %d)\n", pinger, sequence, pinger->sequence ); rc = -EPROTO_SEQ; + terminate = 0; } else { + /* Success: record that a packet was successfully received, + * and terminate if we expect to send no further packets. + */ rc = 0; + pinger->rc = 0; + terminate = ( pinger->remaining == 1 ); } /* Discard I/O buffer */ free_iob ( iobuf ); - /* Notify callback function */ - pinger->callback ( meta->src, sequence, len, rc ); + /* Notify callback function, if applicable */ + if ( pinger->callback ) + pinger->callback ( meta->src, sequence, len, rc ); + + /* Terminate if applicable */ + if ( terminate ) + pinger_close ( pinger, rc ); return rc; } @@ -257,10 +301,12 @@ static struct interface_descriptor pinger_job_desc = * @v hostname Hostname to ping * @v timeout Timeout (in ticks) * @v len Payload length + * @v count Number of packets to send (or zero for no limit) + * @v callback Callback function (or NULL) * @ret rc Return status code */ int create_pinger ( struct interface *job, const char *hostname, - unsigned long timeout, size_t len, + unsigned long timeout, size_t len, unsigned int count, void ( * callback ) ( struct sockaddr *src, unsigned int sequence, size_t len, int rc ) ) { @@ -281,7 +327,9 @@ int create_pinger ( struct interface *job, const char *hostname, timer_init ( &pinger->timer, pinger_expired, &pinger->refcnt ); pinger->timeout = timeout; pinger->len = len; + pinger->remaining = ( count ? ( count + 1 /* Initial packet */ ) : 0 ); pinger->callback = callback; + pinger->rc = -ETIMEDOUT; /* Open socket */ if ( ( rc = xfer_open_named_socket ( &pinger->xfer, SOCK_ECHO, NULL, diff --git a/roms/ipxe/src/core/profile.c b/roms/ipxe/src/core/profile.c index ceaadd6..150e6b2 100644 --- a/roms/ipxe/src/core/profile.c +++ b/roms/ipxe/src/core/profile.c @@ -40,6 +40,9 @@ FILE_LICENCE ( GPL2_OR_LATER ); * to avoid the use of floating-point instructions. */ +/** Accumulated time excluded from profiling */ +unsigned long profile_excluded; + /** * Format a hex fraction (for debugging) * diff --git a/roms/ipxe/src/core/string.c b/roms/ipxe/src/core/string.c index 190007a..e53c283 100644 --- a/roms/ipxe/src/core/string.c +++ b/roms/ipxe/src/core/string.c @@ -337,11 +337,9 @@ void * memchr(const void *s, int c, size_t n) char * strndup(const char *s, size_t n) { - size_t len = strlen(s); + size_t len = strnlen(s,n); char *new; - if (len>n) - len = n; new = malloc(len+1); if (new) { new[len] = '\0'; diff --git a/roms/ipxe/src/core/version.c b/roms/ipxe/src/core/version.c index 1aa22d8..1e1e9da 100644 --- a/roms/ipxe/src/core/version.c +++ b/roms/ipxe/src/core/version.c @@ -25,12 +25,35 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +#include #include #include +#include + +/** + * Create wide-character version of string + * + * @v string String + * @ret wstring Wide-character version of string + */ +#define WSTRING( string ) _WSTRING ( string ) +#define _WSTRING( string ) L ## string /** Version number feature */ FEATURE_VERSION ( VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH ); +/** Build timestamp (generated by linker) */ +extern char _build_timestamp[]; + +/** Build ID (generated by linker) */ +extern char _build_id[]; + +/** Build timestamp */ +unsigned long build_timestamp = ( ( unsigned long ) _build_timestamp ); + +/** Build ID */ +unsigned long build_id = ( ( unsigned long ) _build_id ); + /** Product major version */ const int product_major_version = VERSION_MAJOR; @@ -38,4 +61,29 @@ const int product_major_version = VERSION_MAJOR; const int product_minor_version = VERSION_MINOR; /** Product version string */ -const char *product_version = VERSION; +const char product_version[] = VERSION; + +/** Product name string */ +const char product_name[] = PRODUCT_NAME; + +/** Product short name string */ +const char product_short_name[] = PRODUCT_SHORT_NAME; + +/** Build name string */ +const char build_name[] = BUILD_NAME; + +/** Wide-character product version string */ +const wchar_t product_wversion[] = WSTRING ( VERSION ); + +/** Wide-character product name string */ +const wchar_t product_wname[] = WSTRING ( PRODUCT_NAME ); + +/** Wide-character product short name string */ +const wchar_t product_short_wname[] = WSTRING ( PRODUCT_SHORT_NAME ); + +/** Wide-character build name string */ +const wchar_t build_wname[] = WSTRING ( BUILD_NAME ); + +/** Copy of build name string within ".prefix" */ +const char build_name_prefix[] __attribute__ (( section ( ".prefix.name" ) )) + = BUILD_NAME; diff --git a/roms/ipxe/src/crypto/ocsp.c b/roms/ipxe/src/crypto/ocsp.c index d4815a1..66e47c5 100644 --- a/roms/ipxe/src/crypto/ocsp.c +++ b/roms/ipxe/src/crypto/ocsp.c @@ -405,12 +405,17 @@ static int ocsp_compare_responder_name ( struct ocsp_check *ocsp, static int ocsp_compare_responder_key_hash ( struct ocsp_check *ocsp, struct x509_certificate *cert ) { struct ocsp_responder *responder = &ocsp->response.responder; + struct asn1_cursor key_hash; uint8_t ctx[SHA1_CTX_SIZE]; uint8_t digest[SHA1_DIGEST_SIZE]; int difference; + /* Enter responder key hash */ + memcpy ( &key_hash, &responder->id, sizeof ( key_hash ) ); + asn1_enter ( &key_hash, ASN1_OCTET_STRING ); + /* Sanity check */ - difference = ( sizeof ( digest ) - responder->id.len ); + difference = ( sizeof ( digest ) - key_hash.len ); if ( difference ) return difference; @@ -421,8 +426,8 @@ static int ocsp_compare_responder_key_hash ( struct ocsp_check *ocsp, cert->subject.public_key.raw_bits.len ); digest_final ( &sha1_algorithm, ctx, digest ); - /* Compare responder ID with SHA1 hash of certificate's public key */ - return memcmp ( digest, responder->id.data, sizeof ( digest ) ); + /* Compare responder key hash with hash of certificate's public key */ + return memcmp ( digest, key_hash.data, sizeof ( digest ) ); } /** diff --git a/roms/ipxe/src/crypto/x509.c b/roms/ipxe/src/crypto/x509.c index 0502efa..4a02dad 100644 --- a/roms/ipxe/src/crypto/x509.c +++ b/roms/ipxe/src/crypto/x509.c @@ -33,6 +33,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include +#include #include #include @@ -457,7 +459,7 @@ static int x509_parse_basic_constraints ( struct x509_certificate *cert, return -EINVAL; } basic->path_len = path_len; - DBGC2 ( cert, "X509 %p path length constraint is %u\n", + DBGC2 ( cert, "X509 %p path length constraint is %d\n", cert, basic->path_len ); } @@ -1418,6 +1420,57 @@ static int x509_check_dnsname ( struct x509_certificate *cert, } /** + * Check X.509 certificate alternative iPAddress + * + * @v cert X.509 certificate + * @v raw ASN.1 cursor + * @v name Name + * @ret rc Return status code + */ +static int x509_check_ipaddress ( struct x509_certificate *cert, + const struct asn1_cursor *raw, + const char *name ) { + struct sockaddr sa; + sa_family_t family; + const void *address; + int rc; + + /* Determine address family */ + if ( raw->len == sizeof ( struct in_addr ) ) { + struct sockaddr_in *sin = ( ( struct sockaddr_in * ) &sa ); + family = AF_INET; + address = &sin->sin_addr; + } else if ( raw->len == sizeof ( struct in6_addr ) ) { + struct sockaddr_in6 *sin6 = ( ( struct sockaddr_in6 * ) &sa ); + family = AF_INET6; + address = &sin6->sin6_addr; + } else { + DBGC ( cert, "X509 %p \"%s\" has iPAddress with unexpected " + "length %zd\n", cert, x509_name ( cert ), raw->len ); + DBGC_HDA ( cert, 0, raw->data, raw->len ); + return -EINVAL; + } + + /* Attempt to convert name to a socket address */ + if ( ( rc = sock_aton ( name, &sa ) ) != 0 ) { + DBGC2 ( cert, "X509 %p \"%s\" cannot parse \"%s\" as " + "iPAddress: %s\n", cert, x509_name ( cert ), name, + strerror ( rc ) ); + return rc; + } + if ( sa.sa_family != family ) + return -ENOENT; + + /* Compare addresses */ + if ( memcmp ( address, raw->data, raw->len ) != 0 ) + return -ENOENT; + + DBGC2 ( cert, "X509 %p \"%s\" found iPAddress match for \"%s\"\n", + cert, x509_name ( cert ), sock_ntoa ( &sa ) ); + return 0; +} + +/** * Check X.509 certificate alternative name * * @v cert X.509 certificate @@ -1440,6 +1493,8 @@ static int x509_check_alt_name ( struct x509_certificate *cert, switch ( type ) { case X509_GENERAL_NAME_DNS : return x509_check_dnsname ( cert, &alt_name, name ); + case X509_GENERAL_NAME_IP : + return x509_check_ipaddress ( cert, &alt_name, name ); default: DBGC2 ( cert, "X509 %p \"%s\" unknown name of type %#02x:\n", cert, x509_name ( cert ), type ); diff --git a/roms/ipxe/src/drivers/block/ibft.c b/roms/ipxe/src/drivers/block/ibft.c index 0700f8c..6aabd76 100644 --- a/roms/ipxe/src/drivers/block/ibft.c +++ b/roms/ipxe/src/drivers/block/ibft.c @@ -102,17 +102,19 @@ static void ibft_set_ipaddr ( struct ibft_ipaddr *ipaddr, struct in_addr in ) { /** * Fill in an IP address within iBFT from configuration setting * + * @v settings Parent settings block, or NULL * @v ipaddr IP address field * @v setting Configuration setting * @v count Maximum number of IP addresses */ -static void ibft_set_ipaddr_setting ( struct ibft_ipaddr *ipaddr, +static void ibft_set_ipaddr_setting ( struct settings *settings, + struct ibft_ipaddr *ipaddr, const struct setting *setting, unsigned int count ) { struct in_addr in[count]; unsigned int i; - fetch_ipv4_array_setting ( NULL, setting, in, count ); + fetch_ipv4_array_setting ( settings, setting, in, count ); for ( i = 0 ; i < count ; i++ ) { ibft_set_ipaddr ( &ipaddr[i], in[i] ); } @@ -176,12 +178,14 @@ static int ibft_set_string ( struct ibft_strings *strings, /** * Fill in a string field within iBFT from configuration setting * + * @v settings Parent settings block, or NULL * @v strings iBFT string block descriptor * @v string String field * @v setting Configuration setting * @ret rc Return status code */ -static int ibft_set_string_setting ( struct ibft_strings *strings, +static int ibft_set_string_setting ( struct settings *settings, + struct ibft_strings *strings, struct ibft_string *string, const struct setting *setting ) { struct settings *origin; @@ -189,7 +193,7 @@ static int ibft_set_string_setting ( struct ibft_strings *strings, int len; char *dest; - len = fetch_setting ( NULL, setting, &origin, &fetched, NULL, 0 ); + len = fetch_setting ( settings, setting, &origin, &fetched, NULL, 0 ); if ( len < 0 ) { string->offset = 0; string->len = 0; @@ -231,6 +235,8 @@ static int ibft_fill_nic ( struct ibft_nic *nic, struct ll_protocol *ll_protocol = netdev->ll_protocol; struct in_addr netmask_addr = { 0 }; unsigned int netmask_count = 0; + struct settings *parent = netdev_settings ( netdev ); + struct settings *origin; int rc; /* Fill in common header */ @@ -240,24 +246,30 @@ static int ibft_fill_nic ( struct ibft_nic *nic, nic->header.flags = ( IBFT_FL_NIC_BLOCK_VALID | IBFT_FL_NIC_FIRMWARE_BOOT_SELECTED ); + /* Determine origin of IP address */ + fetch_setting ( parent, &ip_setting, &origin, NULL, NULL, 0 ); + nic->origin = ( ( origin == parent ) ? + IBFT_NIC_ORIGIN_MANUAL : IBFT_NIC_ORIGIN_DHCP ); + DBG ( "iBFT NIC origin = %d\n", nic->origin ); + /* Extract values from configuration settings */ - ibft_set_ipaddr_setting ( &nic->ip_address, &ip_setting, 1 ); + ibft_set_ipaddr_setting ( parent, &nic->ip_address, &ip_setting, 1 ); DBG ( "iBFT NIC IP = %s\n", ibft_ipaddr ( &nic->ip_address ) ); - ibft_set_ipaddr_setting ( &nic->gateway, &gateway_setting, 1 ); + ibft_set_ipaddr_setting ( parent, &nic->gateway, &gateway_setting, 1 ); DBG ( "iBFT NIC gateway = %s\n", ibft_ipaddr ( &nic->gateway ) ); - ibft_set_ipaddr_setting ( &nic->dns[0], &dns_setting, + ibft_set_ipaddr_setting ( NULL, &nic->dns[0], &dns_setting, ( sizeof ( nic->dns ) / sizeof ( nic->dns[0] ) ) ); DBG ( "iBFT NIC DNS = %s", ibft_ipaddr ( &nic->dns[0] ) ); DBG ( ", %s\n", ibft_ipaddr ( &nic->dns[1] ) ); - if ( ( rc = ibft_set_string_setting ( strings, &nic->hostname, + if ( ( rc = ibft_set_string_setting ( NULL, strings, &nic->hostname, &hostname_setting ) ) != 0 ) return rc; DBG ( "iBFT NIC hostname = %s\n", ibft_string ( strings, &nic->hostname ) ); /* Derive subnet mask prefix from subnet mask */ - fetch_ipv4_setting ( NULL, &netmask_setting, &netmask_addr ); + fetch_ipv4_setting ( parent, &netmask_setting, &netmask_addr ); while ( netmask_addr.s_addr ) { if ( netmask_addr.s_addr & 0x1 ) netmask_count++; diff --git a/roms/ipxe/src/drivers/block/scsi.c b/roms/ipxe/src/drivers/block/scsi.c index 4245f01..64d6929 100644 --- a/roms/ipxe/src/drivers/block/scsi.c +++ b/roms/ipxe/src/drivers/block/scsi.c @@ -132,6 +132,33 @@ int scsi_parse_lun ( const char *lun_string, struct scsi_lun *lun ) { return 0; } +/** + * Parse SCSI sense data + * + * @v data Raw sense data + * @v len Length of raw sense data + * @v sense Descriptor-format sense data to fill in + */ +void scsi_parse_sense ( const void *data, size_t len, + struct scsi_sns_descriptor *sense ) { + const union scsi_sns *sns = data; + + /* Avoid returning uninitialised data */ + memset ( sense, 0, sizeof ( *sense ) ); + + /* Copy, assuming descriptor-format data */ + if ( len < sizeof ( sns->desc ) ) + return; + memcpy ( sense, &sns->desc, sizeof ( *sense ) ); + + /* Convert fixed-format to descriptor-format, if applicable */ + if ( len < sizeof ( sns->fixed ) ) + return; + if ( ! SCSI_SENSE_FIXED ( sns->code ) ) + return; + sense->additional = sns->fixed.additional; +} + /****************************************************************************** * * Interface methods @@ -468,9 +495,10 @@ static void scsicmd_response ( struct scsi_command *scsicmd, underrun = -(response->overrun); DBGC ( scsidev, " underrun -%zd", underrun ); } - DBGC ( scsidev, " sense %02x:%02x:%08x\n", - response->sense.code, response->sense.key, - ntohl ( response->sense.info ) ); + DBGC ( scsidev, " sense %02x key %02x additional %04x\n", + ( response->sense.code & SCSI_SENSE_CODE_MASK ), + ( response->sense.key & SCSI_SENSE_KEY_MASK ), + ntohs ( response->sense.additional ) ); /* Construct error number from sense data */ rc = -EIO_SENSE ( response->sense.key & SCSI_SENSE_KEY_MASK ); diff --git a/roms/ipxe/src/drivers/block/srp.c b/roms/ipxe/src/drivers/block/srp.c index 70a97b2..7edf69a 100644 --- a/roms/ipxe/src/drivers/block/srp.c +++ b/roms/ipxe/src/drivers/block/srp.c @@ -476,12 +476,14 @@ static int srp_rsp ( struct srp_device *srpdev, const struct srp_rsp *rsp = data; struct srp_command *srpcmd; struct scsi_rsp response; - const void *sense; ssize_t data_out_residual_count; ssize_t data_in_residual_count; /* Sanity check */ - if ( len < sizeof ( *rsp ) ) { + if ( ( len < sizeof ( *rsp ) ) || + ( len < ( sizeof ( *rsp ) + + srp_rsp_response_data_len ( rsp ) + + srp_rsp_sense_data_len ( rsp ) ) ) ) { DBGC ( srpdev, "SRP %p RSP too short (%zd bytes)\n", srpdev, len ); return -EINVAL; @@ -523,9 +525,8 @@ static int srp_rsp ( struct srp_device *srpdev, } else if ( rsp->valid & SRP_RSP_VALID_DIUNDER ) { response.overrun = -(data_in_residual_count); } - sense = srp_rsp_sense_data ( rsp ); - if ( sense ) - memcpy ( &response.sense, sense, sizeof ( response.sense ) ); + scsi_parse_sense ( srp_rsp_sense_data ( rsp ), + srp_rsp_sense_data_len ( rsp ), &response.sense ); /* Report SCSI response */ scsi_response ( &srpcmd->scsi, &response ); diff --git a/roms/ipxe/src/drivers/net/efi/nii.c b/roms/ipxe/src/drivers/net/efi/nii.c new file mode 100644 index 0000000..d0d7da9 --- /dev/null +++ b/roms/ipxe/src/drivers/net/efi/nii.c @@ -0,0 +1,1101 @@ +/* + * Copyright (C) 2014 Michael Brown . + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nii.h" + +/** @file + * + * NII driver + * + */ + +/* Error numbers generated by NII */ +#define EIO_INVALID_CDB __einfo_error ( EINFO_EIO_INVALID_CDB ) +#define EINFO_EIO_INVALID_CDB \ + __einfo_uniqify ( EINFO_EIO, PXE_STATCODE_INVALID_CDB, \ + "Invalid CDB" ) +#define EIO_INVALID_CPB __einfo_error ( EINFO_EIO_INVALID_CPB ) +#define EINFO_EIO_INVALID_CPB \ + __einfo_uniqify ( EINFO_EIO, PXE_STATCODE_INVALID_CPB, \ + "Invalid CPB" ) +#define EIO_BUSY __einfo_error ( EINFO_EIO_BUSY ) +#define EINFO_EIO_BUSY \ + __einfo_uniqify ( EINFO_EIO, PXE_STATCODE_BUSY, \ + "Busy" ) +#define EIO_QUEUE_FULL __einfo_error ( EINFO_EIO_QUEUE_FULL ) +#define EINFO_EIO_QUEUE_FULL \ + __einfo_uniqify ( EINFO_EIO, PXE_STATCODE_QUEUE_FULL, \ + "Queue full" ) +#define EIO_ALREADY_STARTED __einfo_error ( EINFO_EIO_ALREADY_STARTED ) +#define EINFO_EIO_ALREADY_STARTED \ + __einfo_uniqify ( EINFO_EIO, PXE_STATCODE_ALREADY_STARTED, \ + "Already started" ) +#define EIO_NOT_STARTED __einfo_error ( EINFO_EIO_NOT_STARTED ) +#define EINFO_EIO_NOT_STARTED \ + __einfo_uniqify ( EINFO_EIO, PXE_STATCODE_NOT_STARTED, \ + "Not started" ) +#define EIO_NOT_SHUTDOWN __einfo_error ( EINFO_EIO_NOT_SHUTDOWN ) +#define EINFO_EIO_NOT_SHUTDOWN \ + __einfo_uniqify ( EINFO_EIO, PXE_STATCODE_NOT_SHUTDOWN, \ + "Not shutdown" ) +#define EIO_ALREADY_INITIALIZED __einfo_error ( EINFO_EIO_ALREADY_INITIALIZED ) +#define EINFO_EIO_ALREADY_INITIALIZED \ + __einfo_uniqify ( EINFO_EIO, PXE_STATCODE_ALREADY_INITIALIZED, \ + "Already initialized" ) +#define EIO_NOT_INITIALIZED __einfo_error ( EINFO_EIO_NOT_INITIALIZED ) +#define EINFO_EIO_NOT_INITIALIZED \ + __einfo_uniqify ( EINFO_EIO, PXE_STATCODE_NOT_INITIALIZED, \ + "Not initialized" ) +#define EIO_DEVICE_FAILURE __einfo_error ( EINFO_EIO_DEVICE_FAILURE ) +#define EINFO_EIO_DEVICE_FAILURE \ + __einfo_uniqify ( EINFO_EIO, PXE_STATCODE_DEVICE_FAILURE, \ + "Device failure" ) +#define EIO_NVDATA_FAILURE __einfo_error ( EINFO_EIO_NVDATA_FAILURE ) +#define EINFO_EIO_NVDATA_FAILURE \ + __einfo_uniqify ( EINFO_EIO, PXE_STATCODE_NVDATA_FAILURE, \ + "Non-volatile data failure" ) +#define EIO_UNSUPPORTED __einfo_error ( EINFO_EIO_UNSUPPORTED ) +#define EINFO_EIO_UNSUPPORTED \ + __einfo_uniqify ( EINFO_EIO, PXE_STATCODE_UNSUPPORTED, \ + "Unsupported" ) +#define EIO_BUFFER_FULL __einfo_error ( EINFO_EIO_BUFFER_FULL ) +#define EINFO_EIO_BUFFER_FULL \ + __einfo_uniqify ( EINFO_EIO, PXE_STATCODE_BUFFER_FULL, \ + "Buffer full" ) +#define EIO_INVALID_PARAMETER __einfo_error ( EINFO_EIO_INVALID_PARAMETER ) +#define EINFO_EIO_INVALID_PARAMETER \ + __einfo_uniqify ( EINFO_EIO, PXE_STATCODE_INVALID_PARAMETER, \ + "Invalid parameter" ) +#define EIO_INVALID_UNDI __einfo_error ( EINFO_EIO_INVALID_UNDI ) +#define EINFO_EIO_INVALID_UNDI \ + __einfo_uniqify ( EINFO_EIO, PXE_STATCODE_INVALID_UNDI, \ + "Invalid UNDI" ) +#define EIO_IPV4_NOT_SUPPORTED __einfo_error ( EINFO_EIO_IPV4_NOT_SUPPORTED ) +#define EINFO_EIO_IPV4_NOT_SUPPORTED \ + __einfo_uniqify ( EINFO_EIO, PXE_STATCODE_IPV4_NOT_SUPPORTED, \ + "IPv4 not supported" ) +#define EIO_IPV6_NOT_SUPPORTED __einfo_error ( EINFO_EIO_IPV6_NOT_SUPPORTED ) +#define EINFO_EIO_IPV6_NOT_SUPPORTED \ + __einfo_uniqify ( EINFO_EIO, PXE_STATCODE_IPV6_NOT_SUPPORTED, \ + "IPv6 not supported" ) +#define EIO_NOT_ENOUGH_MEMORY __einfo_error ( EINFO_EIO_NOT_ENOUGH_MEMORY ) +#define EINFO_EIO_NOT_ENOUGH_MEMORY \ + __einfo_uniqify ( EINFO_EIO, PXE_STATCODE_NOT_ENOUGH_MEMORY, \ + "Not enough memory" ) +#define EIO_NO_DATA __einfo_error ( EINFO_EIO_NO_DATA ) +#define EINFO_EIO_NO_DATA \ + __einfo_uniqify ( EINFO_EIO, PXE_STATCODE_NO_DATA, \ + "No data" ) +#define EIO_STAT( stat ) \ + EUNIQ ( EINFO_EIO, -(stat), EIO_INVALID_CDB, EIO_INVALID_CPB, \ + EIO_BUSY, EIO_QUEUE_FULL, EIO_ALREADY_STARTED, \ + EIO_NOT_STARTED, EIO_NOT_SHUTDOWN, EIO_ALREADY_INITIALIZED, \ + EIO_NOT_INITIALIZED, EIO_DEVICE_FAILURE, EIO_NVDATA_FAILURE, \ + EIO_UNSUPPORTED, EIO_BUFFER_FULL, EIO_INVALID_PARAMETER, \ + EIO_INVALID_UNDI, EIO_IPV4_NOT_SUPPORTED, \ + EIO_IPV6_NOT_SUPPORTED, EIO_NOT_ENOUGH_MEMORY, EIO_NO_DATA ) + +/** Maximum PCI BAR + * + * This is defined in , but we + * can't #include that since it collides with . + */ +#define PCI_MAX_BAR 6 + +/** An NII NIC */ +struct nii_nic { + /** EFI device */ + struct efi_device *efidev; + /** Network interface identifier protocol */ + EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *nii; + /** !PXE structure */ + PXE_SW_UNDI *undi; + /** Entry point */ + EFIAPI VOID ( * issue ) ( UINT64 cdb ); + /** Generic device */ + struct device dev; + + /** PCI device */ + EFI_HANDLE pci_device; + /** PCI I/O protocol */ + EFI_PCI_IO_PROTOCOL *pci_io; + /** Memory BAR */ + unsigned int mem_bar; + /** I/O BAR */ + unsigned int io_bar; + + /** Broadcast address */ + PXE_MAC_ADDR broadcast; + /** Maximum packet length */ + size_t mtu; + + /** Hardware transmit/receive buffer */ + userptr_t buffer; + /** Hardware transmit/receive buffer length */ + size_t buffer_len; + + /** Saved task priority level */ + EFI_TPL saved_tpl; + + /** Current transmit buffer */ + struct io_buffer *txbuf; + /** Current receive buffer */ + struct io_buffer *rxbuf; +}; + +/** Maximum number of received packets per poll */ +#define NII_RX_QUOTA 4 + +/** + * Open PCI I/O protocol and identify BARs + * + * @v nii NII NIC + * @ret rc Return status code + */ +static int nii_pci_open ( struct nii_nic *nii ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE device = nii->efidev->device; + EFI_HANDLE pci_device; + union { + EFI_PCI_IO_PROTOCOL *pci_io; + void *interface; + } pci_io; + union { + EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *acpi; + void *resource; + } desc; + unsigned int bar; + EFI_STATUS efirc; + int rc; + + /* Locate PCI I/O protocol */ + if ( ( rc = efi_locate_device ( device, &efi_pci_io_protocol_guid, + &pci_device ) ) != 0 ) { + DBGC ( nii, "NII %s could not locate PCI I/O protocol: %s\n", + nii->dev.name, strerror ( rc ) ); + goto err_locate; + } + nii->pci_device = pci_device; + + /* Open PCI I/O protocol */ + if ( ( efirc = bs->OpenProtocol ( pci_device, &efi_pci_io_protocol_guid, + &pci_io.interface, efi_image_handle, + device, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ + rc = -EEFI ( efirc ); + DBGC ( nii, "NII %s could not open PCI I/O protocol: %s\n", + nii->dev.name, strerror ( rc ) ); + goto err_open; + } + nii->pci_io = pci_io.pci_io; + + /* Identify memory and I/O BARs */ + nii->mem_bar = PCI_MAX_BAR; + nii->io_bar = PCI_MAX_BAR; + for ( bar = 0 ; bar < PCI_MAX_BAR ; bar++ ) { + efirc = nii->pci_io->GetBarAttributes ( nii->pci_io, bar, NULL, + &desc.resource ); + if ( efirc == EFI_UNSUPPORTED ) { + /* BAR not present; ignore */ + continue; + } + if ( efirc != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( nii, "NII %s could not get BAR %d attributes: " + "%s\n", nii->dev.name, bar, strerror ( rc ) ); + goto err_get_bar_attributes; + } + if ( desc.acpi->ResType == ACPI_ADDRESS_SPACE_TYPE_MEM ) { + nii->mem_bar = bar; + } else if ( desc.acpi->ResType == ACPI_ADDRESS_SPACE_TYPE_IO ) { + nii->io_bar = bar; + } + bs->FreePool ( desc.resource ); + } + DBGC ( nii, "NII %s has ", nii->dev.name ); + if ( nii->mem_bar < PCI_MAX_BAR ) { + DBGC ( nii, "memory BAR %d and ", nii->mem_bar ); + } else { + DBGC ( nii, "no memory BAR and " ); + } + if ( nii->io_bar < PCI_MAX_BAR ) { + DBGC ( nii, "I/O BAR %d\n", nii->io_bar ); + } else { + DBGC ( nii, "no I/O BAR\n" ); + } + + return 0; + + err_get_bar_attributes: + bs->CloseProtocol ( pci_device, &efi_pci_io_protocol_guid, + efi_image_handle, device ); + err_open: + err_locate: + return rc; +} + +/** + * Close PCI I/O protocol + * + * @v nii NII NIC + * @ret rc Return status code + */ +static void nii_pci_close ( struct nii_nic *nii ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + + bs->CloseProtocol ( nii->pci_device, &efi_pci_io_protocol_guid, + efi_image_handle, nii->efidev->device ); +} + +/** + * I/O callback + * + * @v unique_id NII NIC + * @v op Operations + * @v len Length of data + * @v addr Address + * @v data Data buffer + */ +static EFIAPI VOID nii_io ( UINT64 unique_id, UINT8 op, UINT8 len, UINT64 addr, + UINT64 data ) { + struct nii_nic *nii = ( ( void * ) ( intptr_t ) unique_id ); + EFI_PCI_IO_PROTOCOL_ACCESS *access; + EFI_PCI_IO_PROTOCOL_IO_MEM io; + EFI_PCI_IO_PROTOCOL_WIDTH width; + unsigned int bar; + EFI_STATUS efirc; + int rc; + + /* Determine accessor and BAR */ + if ( op & ( PXE_MEM_READ | PXE_MEM_WRITE ) ) { + access = &nii->pci_io->Mem; + bar = nii->mem_bar; + } else { + access = &nii->pci_io->Io; + bar = nii->io_bar; + } + + /* Determine operaton */ + io = ( ( op & ( PXE_IO_WRITE | PXE_MEM_WRITE ) ) ? + access->Write : access->Read ); + + /* Determine width */ + width = ( fls ( len ) - 1 ); + + /* Issue operation */ + if ( ( efirc = io ( nii->pci_io, width, bar, addr, 1, + ( ( void * ) ( intptr_t ) data ) ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( nii, "NII %s I/O operation %#x failed: %s\n", + nii->dev.name, op, strerror ( rc ) ); + /* No way to report failure */ + return; + } +} + +/** + * Delay callback + * + * @v unique_id NII NIC + * @v microseconds Delay in microseconds + */ +static EFIAPI VOID nii_delay ( UINT64 unique_id __unused, UINTN microseconds ) { + + udelay ( microseconds ); +} + +/** + * Block callback + * + * @v unique_id NII NIC + * @v acquire Acquire lock + */ +static EFIAPI VOID nii_block ( UINT64 unique_id, UINT32 acquire ) { + struct nii_nic *nii = ( ( void * ) ( intptr_t ) unique_id ); + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + + /* This functionality (which is copied verbatim from the + * SnpDxe implementation of this function) appears to be + * totally brain-dead, since it produces no actual blocking + * behaviour. + */ + if ( acquire ) { + nii->saved_tpl = bs->RaiseTPL ( TPL_NOTIFY ); + } else { + bs->RestoreTPL ( nii->saved_tpl ); + } +} + +/** + * Construct operation from opcode and flags + * + * @v opcode Opcode + * @v opflags Flags + * @ret op Operation + */ +#define NII_OP( opcode, opflags ) ( (opcode) | ( (opflags) << 16 ) ) + +/** + * Extract opcode from operation + * + * @v op Operation + * @ret opcode Opcode + */ +#define NII_OPCODE( op ) ( (op) & 0xffff ) + +/** + * Extract flags from operation + * + * @v op Operation + * @ret opflags Flags + */ +#define NII_OPFLAGS( op ) ( (op) >> 16 ) + +/** + * Issue command with parameter block and data block + * + * @v nii NII NIC + * @v op Operation + * @v cpb Command parameter block, or NULL + * @v cpb_len Command parameter block length + * @v db Data block, or NULL + * @v db_len Data block length + * @ret stat Status flags, or negative status code + */ +static int nii_issue_cpb_db ( struct nii_nic *nii, unsigned int op, void *cpb, + size_t cpb_len, void *db, size_t db_len ) { + PXE_CDB cdb; + + /* Prepare command descriptor block */ + memset ( &cdb, 0, sizeof ( cdb ) ); + cdb.OpCode = NII_OPCODE ( op ); + cdb.OpFlags = NII_OPFLAGS ( op ); + cdb.CPBaddr = ( ( intptr_t ) cpb ); + cdb.CPBsize = cpb_len; + cdb.DBaddr = ( ( intptr_t ) db ); + cdb.DBsize = db_len; + cdb.IFnum = nii->nii->IfNum; + + /* Issue command */ + nii->issue ( ( intptr_t ) &cdb ); + + /* Check completion status */ + if ( cdb.StatCode != PXE_STATCODE_SUCCESS ) + return -cdb.StatCode; + + /* Return command-specific status flags */ + return ( cdb.StatFlags & ~PXE_STATFLAGS_STATUS_MASK ); +} + +/** + * Issue command with parameter block + * + * @v nii NII NIC + * @v op Operation + * @v cpb Command parameter block, or NULL + * @v cpb_len Command parameter block length + * @ret stat Status flags, or negative status code + */ +static int nii_issue_cpb ( struct nii_nic *nii, unsigned int op, void *cpb, + size_t cpb_len ) { + + return nii_issue_cpb_db ( nii, op, cpb, cpb_len, NULL, 0 ); +} + +/** + * Issue command with data block + * + * @v nii NII NIC + * @v op Operation + * @v db Data block, or NULL + * @v db_len Data block length + * @ret stat Status flags, or negative status code + */ +static int nii_issue_db ( struct nii_nic *nii, unsigned int op, void *db, + size_t db_len ) { + + return nii_issue_cpb_db ( nii, op, NULL, 0, db, db_len ); +} + +/** + * Issue command + * + * + * @v nii NII NIC + * @v op Operation + * @ret stat Status flags, or negative status code + */ +static int nii_issue ( struct nii_nic *nii, unsigned int op ) { + + return nii_issue_cpb_db ( nii, op, NULL, 0, NULL, 0 ); +} + +/** + * Start UNDI + * + * @v nii NII NIC + * @ret rc Return status code + */ +static int nii_start_undi ( struct nii_nic *nii ) { + PXE_CPB_START_31 cpb; + int stat; + int rc; + + /* Construct parameter block */ + memset ( &cpb, 0, sizeof ( cpb ) ); + cpb.Delay = ( ( intptr_t ) nii_delay ); + cpb.Block = ( ( intptr_t ) nii_block ); + cpb.Mem_IO = ( ( intptr_t ) nii_io ); + cpb.Unique_ID = ( ( intptr_t ) nii ); + + /* Issue command */ + if ( ( stat = nii_issue_cpb ( nii, PXE_OPCODE_START, &cpb, + sizeof ( cpb ) ) ) < 0 ) { + rc = -EIO_STAT ( stat ); + DBGC ( nii, "NII %s could not start: %s\n", + nii->dev.name, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Stop UNDI + * + * @v nii NII NIC + */ +static void nii_stop_undi ( struct nii_nic *nii ) { + int stat; + int rc; + + /* Issue command */ + if ( ( stat = nii_issue ( nii, PXE_OPCODE_STOP ) ) < 0 ) { + rc = -EIO_STAT ( stat ); + DBGC ( nii, "NII %s could not stop: %s\n", + nii->dev.name, strerror ( rc ) ); + /* Nothing we can do about it */ + return; + } +} + +/** + * Get initialisation information + * + * @v nii NII NIC + * @v netdev Network device to fill in + * @ret rc Return status code + */ +static int nii_get_init_info ( struct nii_nic *nii, + struct net_device *netdev ) { + PXE_DB_GET_INIT_INFO db; + int stat; + int rc; + + /* Issue command */ + if ( ( stat = nii_issue_db ( nii, PXE_OPCODE_GET_INIT_INFO, &db, + sizeof ( db ) ) ) < 0 ) { + rc = -EIO_STAT ( stat ); + DBGC ( nii, "NII %s could not get initialisation info: %s\n", + nii->dev.name, strerror ( rc ) ); + return rc; + } + + /* Determine link layer protocol */ + switch ( db.IFtype ) { + case PXE_IFTYPE_ETHERNET : + netdev->ll_protocol = ðernet_protocol; + break; + default: + DBGC ( nii, "NII %s unknown interface type %#02x\n", + nii->dev.name, db.IFtype ); + return -ENOTSUP; + } + + /* Sanity checks */ + assert ( db.MediaHeaderLen == netdev->ll_protocol->ll_header_len ); + assert ( db.HWaddrLen == netdev->ll_protocol->hw_addr_len ); + assert ( db.HWaddrLen == netdev->ll_protocol->ll_addr_len ); + + /* Extract parameters */ + nii->buffer_len = db.MemoryRequired; + nii->mtu = ( db.FrameDataLen + db.MediaHeaderLen ); + netdev->max_pkt_len = nii->mtu; + + return 0; +} + +/** + * Initialise UNDI + * + * @v nii NII NIC + * @ret rc Return status code + */ +static int nii_initialise ( struct nii_nic *nii ) { + PXE_CPB_INITIALIZE cpb; + unsigned int op; + int stat; + int rc; + + /* Allocate memory buffer */ + nii->buffer = umalloc ( nii->buffer_len ); + if ( ! nii->buffer ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Construct parameter block */ + memset ( &cpb, 0, sizeof ( cpb ) ); + cpb.MemoryAddr = ( ( intptr_t ) nii->buffer ); + cpb.MemoryLength = nii->buffer_len; + + /* Issue command */ + op = NII_OP ( PXE_OPCODE_INITIALIZE, + PXE_OPFLAGS_INITIALIZE_DO_NOT_DETECT_CABLE ); + if ( ( stat = nii_issue_cpb ( nii, op, &cpb, sizeof ( cpb ) ) ) < 0 ) { + rc = -EIO_STAT ( stat ); + DBGC ( nii, "NII %s could not initialise: %s\n", + nii->dev.name, strerror ( rc ) ); + goto err_initialize; + } + + return 0; + + err_initialize: + ufree ( nii->buffer ); + err_alloc: + return rc; +} + +/** + * Shut down UNDI + * + * @v nii NII NIC + */ +static void nii_shutdown ( struct nii_nic *nii ) { + int stat; + int rc; + + /* Issue command */ + if ( ( stat = nii_issue ( nii, PXE_OPCODE_SHUTDOWN ) ) < 0 ) { + rc = -EIO_STAT ( stat ); + DBGC ( nii, "NII %s could not shut down: %s\n", + nii->dev.name, strerror ( rc ) ); + /* Leak memory to avoid corruption */ + return; + } + + /* Free buffer */ + ufree ( nii->buffer ); +} + +/** + * Get station addresses + * + * @v nii NII NIC + * @v netdev Network device to fill in + * @ret rc Return status code + */ +static int nii_get_station_address ( struct nii_nic *nii, + struct net_device *netdev ) { + PXE_DB_STATION_ADDRESS db; + int stat; + int rc; + + /* Initialise UNDI */ + if ( ( rc = nii_initialise ( nii ) ) != 0 ) + goto err_initialise; + + /* Issue command */ + if ( ( stat = nii_issue_db ( nii, PXE_OPCODE_STATION_ADDRESS, &db, + sizeof ( db ) ) ) < 0 ) { + rc = -EIO_STAT ( stat ); + DBGC ( nii, "NII %s could not get station address: %s\n", + nii->dev.name, strerror ( rc ) ); + goto err_station_address; + } + + /* Copy MAC addresses */ + memcpy ( netdev->ll_addr, db.StationAddr, + netdev->ll_protocol->ll_addr_len ); + memcpy ( netdev->hw_addr, db.PermanentAddr, + netdev->ll_protocol->hw_addr_len ); + memcpy ( nii->broadcast, db.BroadcastAddr, + sizeof ( nii->broadcast ) ); + + err_station_address: + nii_shutdown ( nii ); + err_initialise: + return rc; +} + +/** + * Set station address + * + * @v nii NII NIC + * @v netdev Network device + * @ret rc Return status code + */ +static int nii_set_station_address ( struct nii_nic *nii, + struct net_device *netdev ) { + PXE_CPB_STATION_ADDRESS cpb; + int stat; + int rc; + + /* Construct parameter block */ + memset ( &cpb, 0, sizeof ( cpb ) ); + memcpy ( cpb.StationAddr, netdev->ll_addr, + netdev->ll_protocol->ll_addr_len ); + + /* Issue command */ + if ( ( stat = nii_issue_cpb ( nii, PXE_OPCODE_STATION_ADDRESS, + &cpb, sizeof ( cpb ) ) ) < 0 ) { + rc = -EIO_STAT ( stat ); + DBGC ( nii, "NII %s could not set station address: %s\n", + nii->dev.name, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Set receive filters + * + * @v nii NII NIC + * @ret rc Return status code + */ +static int nii_set_rx_filters ( struct nii_nic *nii ) { + unsigned int op; + int stat; + int rc; + + /* Issue command */ + op = NII_OP ( PXE_OPCODE_RECEIVE_FILTERS, + ( PXE_OPFLAGS_RECEIVE_FILTER_ENABLE | + PXE_OPFLAGS_RECEIVE_FILTER_UNICAST | + PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST | + PXE_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS | + PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST ) ); + if ( ( stat = nii_issue ( nii, op ) ) < 0 ) { + rc = -EIO_STAT ( stat ); + DBGC ( nii, "NII %s could not set receive filters: %s\n", + nii->dev.name, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int nii_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct nii_nic *nii = netdev->priv; + PXE_CPB_TRANSMIT cpb; + int stat; + int rc; + + /* Defer the packet if there is already a transmission in progress */ + if ( nii->txbuf ) { + netdev_tx_defer ( netdev, iobuf ); + return 0; + } + + /* Construct parameter block */ + memset ( &cpb, 0, sizeof ( cpb ) ); + cpb.FrameAddr = virt_to_bus ( iobuf->data ); + cpb.DataLen = iob_len ( iobuf ); + cpb.MediaheaderLen = netdev->ll_protocol->ll_header_len; + + /* Transmit packet */ + if ( ( stat = nii_issue_cpb ( nii, PXE_OPCODE_TRANSMIT, &cpb, + sizeof ( cpb ) ) ) < 0 ) { + rc = -EIO_STAT ( stat ); + DBGC ( nii, "NII %s could not transmit: %s\n", + nii->dev.name, strerror ( rc ) ); + return rc; + } + nii->txbuf = iobuf; + + return 0; +} + +/** + * Poll for completed packets + * + * @v netdev Network device + * @v stat Status flags + */ +static void nii_poll_tx ( struct net_device *netdev, unsigned int stat ) { + struct nii_nic *nii = netdev->priv; + struct io_buffer *iobuf; + + /* Do nothing unless we have a completion */ + if ( stat & PXE_STATFLAGS_GET_STATUS_NO_TXBUFS_WRITTEN ) + return; + + /* Sanity check */ + if ( ! nii->txbuf ) { + DBGC ( nii, "NII %s reported spurious TX completion\n", + nii->dev.name ); + netdev_tx_err ( netdev, NULL, -EPIPE ); + return; + } + + /* Complete transmission */ + iobuf = nii->txbuf; + nii->txbuf = NULL; + netdev_tx_complete ( netdev, iobuf ); +} + +/** + * Poll for received packets + * + * @v netdev Network device + */ +static void nii_poll_rx ( struct net_device *netdev ) { + struct nii_nic *nii = netdev->priv; + PXE_CPB_RECEIVE cpb; + PXE_DB_RECEIVE db; + unsigned int quota; + int stat; + int rc; + + /* Retrieve up to NII_RX_QUOTA packets */ + for ( quota = NII_RX_QUOTA ; quota ; quota-- ) { + + /* Allocate buffer, if required */ + if ( ! nii->rxbuf ) { + nii->rxbuf = alloc_iob ( nii->mtu ); + if ( ! nii->rxbuf ) { + /* Leave for next poll */ + break; + } + } + + /* Construct parameter block */ + memset ( &cpb, 0, sizeof ( cpb ) ); + cpb.BufferAddr = virt_to_bus ( nii->rxbuf->data ); + cpb.BufferLen = iob_tailroom ( nii->rxbuf ); + + /* Issue command */ + if ( ( stat = nii_issue_cpb_db ( nii, PXE_OPCODE_RECEIVE, + &cpb, sizeof ( cpb ), + &db, sizeof ( db ) ) ) < 0 ) { + + /* PXE_STATCODE_NO_DATA is just the usual "no packet" + * status indicator; ignore it. + */ + if ( stat == -PXE_STATCODE_NO_DATA ) + break; + + /* Anything else is an error */ + rc = -EIO_STAT ( stat ); + DBGC ( nii, "NII %s could not receive: %s\n", + nii->dev.name, strerror ( rc ) ); + netdev_rx_err ( netdev, NULL, rc ); + break; + } + + /* Hand off to network stack */ + iob_put ( nii->rxbuf, db.FrameLen ); + netdev_rx ( netdev, nii->rxbuf ); + nii->rxbuf = NULL; + } +} + +/** + * Check for link state changes + * + * @v netdev Network device + * @v stat Status flags + */ +static void nii_poll_link ( struct net_device *netdev, unsigned int stat ) { + int no_media = ( stat & PXE_STATFLAGS_GET_STATUS_NO_MEDIA ); + + if ( no_media && netdev_link_ok ( netdev ) ) { + netdev_link_down ( netdev ); + } else if ( ( ! no_media ) && ( ! netdev_link_ok ( netdev ) ) ) { + netdev_link_up ( netdev ); + } +} + +/** + * Poll for completed packets + * + * @v netdev Network device + */ +static void nii_poll ( struct net_device *netdev ) { + struct nii_nic *nii = netdev->priv; + PXE_DB_GET_STATUS db; + unsigned int op; + int stat; + int rc; + + /* Get status */ + op = NII_OP ( PXE_OPCODE_GET_STATUS, + ( PXE_OPFLAGS_GET_INTERRUPT_STATUS | + PXE_OPFLAGS_GET_TRANSMITTED_BUFFERS | + PXE_OPFLAGS_GET_MEDIA_STATUS ) ); + if ( ( stat = nii_issue_db ( nii, op, &db, sizeof ( db ) ) ) < 0 ) { + rc = -EIO_STAT ( stat ); + DBGC ( nii, "NII %s could not get status: %s\n", + nii->dev.name, strerror ( rc ) ); + return; + } + + /* Process any TX completions */ + nii_poll_tx ( netdev, stat ); + + /* Process any RX completions */ + nii_poll_rx ( netdev ); + + /* Check for link state changes */ + nii_poll_link ( netdev, stat ); +} + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int nii_open ( struct net_device *netdev ) { + struct nii_nic *nii = netdev->priv; + int rc; + + /* Initialise NIC */ + if ( ( rc = nii_initialise ( nii ) ) != 0 ) + goto err_initialise; + + /* Attempt to set station address */ + if ( ( rc = nii_set_station_address ( nii, netdev ) ) != 0 ) { + DBGC ( nii, "NII %s could not set station address: %s\n", + nii->dev.name, strerror ( rc ) ); + /* Treat as non-fatal */ + } + + /* Set receive filters */ + if ( ( rc = nii_set_rx_filters ( nii ) ) != 0 ) + goto err_set_rx_filters; + + return 0; + + err_set_rx_filters: + nii_shutdown ( nii ); + err_initialise: + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void nii_close ( struct net_device *netdev ) { + struct nii_nic *nii = netdev->priv; + + /* Shut down NIC */ + nii_shutdown ( nii ); + + /* Discard transmit buffer, if applicable */ + if ( nii->txbuf ) { + netdev_tx_complete_err ( netdev, nii->txbuf, -ECANCELED ); + nii->txbuf = NULL; + } + + /* Discard receive buffer, if applicable */ + if ( nii->rxbuf ) { + free_iob ( nii->rxbuf ); + nii->rxbuf = NULL; + } +} + +/** NII network device operations */ +static struct net_device_operations nii_operations = { + .open = nii_open, + .close = nii_close, + .transmit = nii_transmit, + .poll = nii_poll, +}; + +/** + * Attach driver to device + * + * @v efidev EFI device + * @ret rc Return status code + */ +int nii_start ( struct efi_device *efidev ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE device = efidev->device; + struct net_device *netdev; + struct nii_nic *nii; + void *interface; + EFI_STATUS efirc; + int rc; + + /* Allocate and initialise structure */ + netdev = alloc_netdev ( sizeof ( *nii ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &nii_operations ); + nii = netdev->priv; + nii->efidev = efidev; + netdev->ll_broadcast = nii->broadcast; + efidev_set_drvdata ( efidev, netdev ); + + /* Populate underlying device information */ + efi_device_info ( device, "NII", &nii->dev ); + nii->dev.driver_name = "NII"; + nii->dev.parent = &efidev->dev; + list_add ( &nii->dev.siblings, &efidev->dev.children ); + INIT_LIST_HEAD ( &nii->dev.children ); + netdev->dev = &nii->dev; + + /* Open NII protocol */ + if ( ( efirc = bs->OpenProtocol ( device, &efi_nii31_protocol_guid, + &interface, efi_image_handle, device, + ( EFI_OPEN_PROTOCOL_BY_DRIVER | + EFI_OPEN_PROTOCOL_EXCLUSIVE )))!=0){ + rc = -EEFI ( efirc ); + DBGC ( nii, "NII %s cannot open NII protocol: %s\n", + nii->dev.name, strerror ( rc ) ); + DBGC_EFI_OPENERS ( device, device, &efi_nii31_protocol_guid ); + goto err_open_protocol; + } + nii->nii = interface; + + /* Locate UNDI and entry point */ + nii->undi = ( ( void * ) ( intptr_t ) nii->nii->Id ); + if ( ! nii->undi ) { + DBGC ( nii, "NII %s has no UNDI\n", nii->dev.name ); + rc = -ENODEV; + goto err_no_undi; + } + if ( nii->undi->Implementation & PXE_ROMID_IMP_HW_UNDI ) { + DBGC ( nii, "NII %s is a mythical hardware UNDI\n", + nii->dev.name ); + rc = -ENOTSUP; + goto err_hw_undi; + } + if ( nii->undi->Implementation & PXE_ROMID_IMP_SW_VIRT_ADDR ) { + nii->issue = ( ( void * ) ( intptr_t ) nii->undi->EntryPoint ); + } else { + nii->issue = ( ( ( void * ) nii->undi ) + + nii->undi->EntryPoint ); + } + DBGC ( nii, "NII %s using UNDI v%x.%x at %p entry %p\n", nii->dev.name, + nii->nii->MajorVer, nii->nii->MinorVer, nii->undi, nii->issue ); + + /* Open PCI I/O protocols and locate BARs */ + if ( ( rc = nii_pci_open ( nii ) ) != 0 ) + goto err_pci_open; + + /* Start UNDI */ + if ( ( rc = nii_start_undi ( nii ) ) != 0 ) + goto err_start_undi; + + /* Get initialisation information */ + if ( ( rc = nii_get_init_info ( nii, netdev ) ) != 0 ) + goto err_get_init_info; + + /* Get MAC addresses */ + if ( ( rc = nii_get_station_address ( nii, netdev ) ) != 0 ) + goto err_get_station_address; + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register_netdev; + DBGC ( nii, "NII %s registered as %s for %p %s\n", nii->dev.name, + netdev->name, device, efi_handle_name ( device ) ); + + return 0; + + unregister_netdev ( netdev ); + err_register_netdev: + err_get_station_address: + err_get_init_info: + nii_stop_undi ( nii ); + err_start_undi: + nii_pci_close ( nii ); + err_pci_open: + err_hw_undi: + err_no_undi: + bs->CloseProtocol ( device, &efi_nii31_protocol_guid, + efi_image_handle, device ); + err_open_protocol: + list_del ( &nii->dev.siblings ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Detach driver from device + * + * @v efidev EFI device + */ +void nii_stop ( struct efi_device *efidev ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + struct net_device *netdev = efidev_get_drvdata ( efidev ); + struct nii_nic *nii = netdev->priv; + EFI_HANDLE device = efidev->device; + + /* Unregister network device */ + unregister_netdev ( netdev ); + + /* Stop UNDI */ + nii_stop_undi ( nii ); + + /* Close PCI I/O protocols */ + nii_pci_close ( nii ); + + /* Close NII protocol */ + bs->CloseProtocol ( device, &efi_nii31_protocol_guid, + efi_image_handle, device ); + + /* Free network device */ + list_del ( &nii->dev.siblings ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} diff --git a/roms/ipxe/src/drivers/net/efi/nii.h b/roms/ipxe/src/drivers/net/efi/nii.h new file mode 100644 index 0000000..de0ac68 --- /dev/null +++ b/roms/ipxe/src/drivers/net/efi/nii.h @@ -0,0 +1,17 @@ +#ifndef _NII_H +#define _NII_H + +/** @file + * + * NII driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +struct efi_device; + +extern int nii_start ( struct efi_device *efidev ); +extern void nii_stop ( struct efi_device *efidev ); + +#endif /* _NII_H */ diff --git a/roms/ipxe/src/drivers/net/efi/snp.c b/roms/ipxe/src/drivers/net/efi/snp.c new file mode 100644 index 0000000..2b5fc86 --- /dev/null +++ b/roms/ipxe/src/drivers/net/efi/snp.c @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2014 Michael Brown . + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include "snpnet.h" +#include "nii.h" + +/** @file + * + * SNP driver + * + */ + +/** + * Check to see if driver supports a device + * + * @v device EFI device handle + * @ret rc Return status code + */ +static int snp_supported ( EFI_HANDLE device ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_STATUS efirc; + + /* Check that this is not a device we are providing ourselves */ + if ( find_snpdev ( device ) != NULL ) { + DBGCP ( device, "SNP %p %s is provided by this binary\n", + device, efi_handle_name ( device ) ); + return -ENOTTY; + } + + /* Test for presence of simple network protocol */ + if ( ( efirc = bs->OpenProtocol ( device, + &efi_simple_network_protocol_guid, + NULL, efi_image_handle, device, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL))!=0){ + DBGCP ( device, "SNP %p %s is not an SNP device\n", + device, efi_handle_name ( device ) ); + return -EEFI ( efirc ); + } + DBGC ( device, "SNP %p %s is an SNP device\n", + device, efi_handle_name ( device ) ); + + return 0; +} + +/** + * Check to see if driver supports a device + * + * @v device EFI device handle + * @ret rc Return status code + */ +static int nii_supported ( EFI_HANDLE device ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_STATUS efirc; + + /* Check that this is not a device we are providing ourselves */ + if ( find_snpdev ( device ) != NULL ) { + DBGCP ( device, "NII %p %s is provided by this binary\n", + device, efi_handle_name ( device ) ); + return -ENOTTY; + } + + /* Test for presence of NII protocol */ + if ( ( efirc = bs->OpenProtocol ( device, + &efi_nii31_protocol_guid, + NULL, efi_image_handle, device, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL))!=0){ + DBGCP ( device, "NII %p %s is not an NII device\n", + device, efi_handle_name ( device ) ); + return -EEFI ( efirc ); + } + DBGC ( device, "NII %p %s is an NII device\n", + device, efi_handle_name ( device ) ); + + return 0; +} + +/** EFI SNP driver */ +struct efi_driver snp_driver __efi_driver ( EFI_DRIVER_NORMAL ) = { + .name = "SNP", + .supported = snp_supported, + .start = snpnet_start, + .stop = snpnet_stop, +}; + +/** EFI NII driver */ +struct efi_driver nii_driver __efi_driver ( EFI_DRIVER_NORMAL ) = { + .name = "NII", + .supported = nii_supported, + .start = nii_start, + .stop = nii_stop, +}; diff --git a/roms/ipxe/src/drivers/net/efi/snp.h b/roms/ipxe/src/drivers/net/efi/snp.h deleted file mode 100644 index 4d6b101..0000000 --- a/roms/ipxe/src/drivers/net/efi/snp.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2010 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; either version 2 of the - * License, 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef _SNP_H -#define _SNP_H - -/** @file - * - * SNP driver - * - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - -#include -#include -#include - -/** A network device that consumes the EFI Simple Network Protocol */ -struct snp_device { - /** Underlying simple network protocol instance */ - EFI_SIMPLE_NETWORK_PROTOCOL *snp; - - /** Generic device */ - struct device dev; - - /** Network device */ - struct net_device *netdev; - - /** State to put the snp in when removing the device */ - uint32 removal_state; -}; - -#endif /* _SNP_H */ diff --git a/roms/ipxe/src/drivers/net/efi/snpnet.c b/roms/ipxe/src/drivers/net/efi/snpnet.c index cd9e7e3..96642c4 100644 --- a/roms/ipxe/src/drivers/net/efi/snpnet.c +++ b/roms/ipxe/src/drivers/net/efi/snpnet.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 VMware, Inc. All Rights Reserved. + * Copyright (C) 2014 Michael Brown . * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -13,38 +13,137 @@ * * 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. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. */ FILE_LICENCE ( GPL2_OR_LATER ); -#include +#include +#include #include -#include +#include #include #include -#include #include +#include #include #include -#include "snp.h" +#include +#include #include "snpnet.h" /** @file * - * SNP network device driver + * SNP NIC driver * */ -/** SNP net device structure */ -struct snpnet_device { - /** The underlying simple network protocol */ +/** An SNP NIC */ +struct snp_nic { + /** EFI device */ + struct efi_device *efidev; + /** Simple network protocol */ EFI_SIMPLE_NETWORK_PROTOCOL *snp; + /** Generic device */ + struct device dev; + + /** Maximum packet size + * + * This is calculated as the sum of MediaHeaderSize and + * MaxPacketSize, and may therefore be an overestimate. + */ + size_t mtu; - /** State that the SNP should be in after close */ - UINT32 close_state; + /** Current transmit buffer */ + struct io_buffer *txbuf; + /** Current receive buffer */ + struct io_buffer *rxbuf; }; +/** Maximum number of received packets per poll */ +#define SNP_RX_QUOTA 4 + +/** + * Format SNP MAC address (for debugging) + * + * @v mac MAC address + * @v len Length of MAC address + * @ret text MAC address as text + */ +static const char * snpnet_mac_text ( EFI_MAC_ADDRESS *mac, size_t len ) { + static char buf[ sizeof ( *mac ) * 3 /* "xx:" or "xx\0" */ ]; + size_t used = 0; + unsigned int i; + + for ( i = 0 ; i < len ; i++ ) { + used += ssnprintf ( &buf[used], ( sizeof ( buf ) - used ), + "%s%02x", ( used ? ":" : "" ), + mac->Addr[i] ); + } + return buf; +} + +/** + * Dump SNP mode information (for debugging) + * + * @v netdev Network device + */ +static void snpnet_dump_mode ( struct net_device *netdev ) { + struct snp_nic *snp = netdev_priv ( netdev ); + EFI_SIMPLE_NETWORK_MODE *mode = snp->snp->Mode; + size_t mac_len = mode->HwAddressSize; + unsigned int i; + + /* Do nothing unless debugging is enabled */ + if ( ! DBG_EXTRA ) + return; + + DBGC2 ( snp, "SNP %s st %d type %d hdr %d pkt %d rxflt %#x/%#x%s " + "nvram %d acc %d mcast %d/%d\n", netdev->name, mode->State, + mode->IfType, mode->MediaHeaderSize, mode->MaxPacketSize, + mode->ReceiveFilterSetting, mode->ReceiveFilterMask, + ( mode->MultipleTxSupported ? " multitx" : "" ), + mode->NvRamSize, mode->NvRamAccessSize, + mode->MCastFilterCount, mode->MaxMCastFilterCount ); + DBGC2 ( snp, "SNP %s hw %s", netdev->name, + snpnet_mac_text ( &mode->PermanentAddress, mac_len ) ); + DBGC2 ( snp, " addr %s%s", + snpnet_mac_text ( &mode->CurrentAddress, mac_len ), + ( mode->MacAddressChangeable ? "" : "(f)" ) ); + DBGC2 ( snp, " bcast %s\n", + snpnet_mac_text ( &mode->BroadcastAddress, mac_len ) ); + for ( i = 0 ; i < mode->MCastFilterCount ; i++ ) { + DBGC2 ( snp, "SNP %s mcast %s\n", netdev->name, + snpnet_mac_text ( &mode->MCastFilter[i], mac_len ) ); + } + DBGC2 ( snp, "SNP %s media %s\n", netdev->name, + ( mode->MediaPresentSupported ? + ( mode->MediaPresent ? "present" : "not present" ) : + "presence not supported" ) ); +} + +/** + * Check link state + * + * @v netdev Network device + */ +static void snpnet_check_link ( struct net_device *netdev ) { + struct snp_nic *snp = netdev_priv ( netdev ); + EFI_SIMPLE_NETWORK_MODE *mode = snp->snp->Mode; + + /* Do nothing unless media presence detection is supported */ + if ( ! mode->MediaPresentSupported ) + return; + + /* Report any link status change */ + if ( mode->MediaPresent && ( ! netdev_link_ok ( netdev ) ) ) { + netdev_link_up ( netdev ); + } else if ( ( ! mode->MediaPresent ) && netdev_link_ok ( netdev ) ) { + netdev_link_down ( netdev ); + } +} + /** * Transmit packet * @@ -54,297 +153,411 @@ struct snpnet_device { */ static int snpnet_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { - struct snpnet_device *snpnetdev = netdev->priv; - EFI_SIMPLE_NETWORK_PROTOCOL *snp = snpnetdev->snp; - void *txbuf=NULL; - size_t len = iob_len ( iobuf ); + struct snp_nic *snp = netdev_priv ( netdev ); EFI_STATUS efirc; int rc; - if ( ( efirc = snp->Transmit ( snp, 0, len, iobuf->data, NULL, NULL, - NULL ) ) != 0 ) { - return -EEFI ( efirc ); + /* Defer the packet if there is already a transmission in progress */ + if ( snp->txbuf ) { + netdev_tx_defer ( netdev, iobuf ); + return 0; } - /* since GetStatus is so inconsistent, don't try more than one outstanding transmit at a time */ - while ( txbuf == NULL ) { - if ( ( efirc = snp->GetStatus ( snp, NULL, &txbuf ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( snp, "SNP %p could not get status %s\n", snp, - strerror ( rc ) ); - break; - } + /* Transmit packet */ + if ( ( efirc = snp->snp->Transmit ( snp->snp, 0, iob_len ( iobuf ), + iobuf->data, NULL, NULL, + NULL ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( snp, "SNP %s could not transmit: %s\n", + netdev->name, strerror ( rc ) ); + return rc; } - netdev_tx_complete ( netdev, iobuf ); + snp->txbuf = iobuf; + return 0; } /** + * Poll for completed packets + * + * @v netdev Network device + */ +static void snpnet_poll_tx ( struct net_device *netdev ) { + struct snp_nic *snp = netdev->priv; + struct io_buffer *iobuf; + UINT32 irq; + VOID *txbuf; + EFI_STATUS efirc; + int rc; + + /* Get status */ + if ( ( efirc = snp->snp->GetStatus ( snp->snp, &irq, &txbuf ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( snp, "SNP %s could not get status: %s\n", + netdev->name, strerror ( rc ) ); + netdev_rx_err ( netdev, NULL, rc ); + return; + } + + /* Do nothing unless we have a completion */ + if ( ! txbuf ) + return; + + /* Sanity check */ + if ( ! snp->txbuf ) { + DBGC ( snp, "SNP %s reported spurious TX completion\n", + netdev->name ); + netdev_tx_err ( netdev, NULL, -EPIPE ); + return; + } + + /* Complete transmission */ + iobuf = snp->txbuf; + snp->txbuf = NULL; + netdev_tx_complete ( netdev, iobuf ); +} + +/** * Poll for received packets * * @v netdev Network device */ -static void snpnet_poll ( struct net_device *netdev ) { - struct snpnet_device *snpnetdev = netdev->priv; - EFI_SIMPLE_NETWORK_PROTOCOL *snp = snpnetdev->snp; - struct io_buffer *iobuf = NULL; +static void snpnet_poll_rx ( struct net_device *netdev ) { + struct snp_nic *snp = netdev->priv; UINTN len; + unsigned int quota; EFI_STATUS efirc; int rc; - /* Process received packets */ - while ( 1 ) { - /* The spec is not clear if the max packet size refers to the - * payload or the entire packet including headers. The Receive - * function needs a buffer large enough to contain the headers, - * and potentially a 4-byte CRC and 4-byte VLAN tag (?), so add - * some breathing room. - */ - len = snp->Mode->MaxPacketSize + ETH_HLEN + 8; - iobuf = alloc_iob ( len ); - if ( iobuf == NULL ) { - netdev_rx_err ( netdev, NULL, -ENOMEM ); - break; + /* Retrieve up to SNP_RX_QUOTA packets */ + for ( quota = SNP_RX_QUOTA ; quota ; quota-- ) { + + /* Allocate buffer, if required */ + if ( ! snp->rxbuf ) { + snp->rxbuf = alloc_iob ( snp->mtu ); + if ( ! snp->rxbuf ) { + /* Leave for next poll */ + break; + } } - efirc = snp->Receive ( snp, NULL, &len, iobuf->data, - NULL, NULL, NULL ); + /* Receive packet */ + len = iob_tailroom ( snp->rxbuf ); + if ( ( efirc = snp->snp->Receive ( snp->snp, NULL, &len, + snp->rxbuf->data, NULL, + NULL, NULL ) ) != 0 ) { - /* No packets left? */ - if ( efirc == EFI_NOT_READY ) { - free_iob ( iobuf ); - break; - } + /* EFI_NOT_READY is just the usual "no packet" + * status indication; ignore it. + */ + if ( efirc == EFI_NOT_READY ) + break; - /* Other error? */ - if ( efirc != 0 ) { + /* Anything else is an error */ rc = -EEFI ( efirc ); - DBGC ( snp, "SNP %p receive packet error: %s " - "(len was %zd, is now %zd)\n", - snp, strerror ( rc ), iob_len(iobuf), - (size_t)len ); - netdev_rx_err ( netdev, iobuf, rc ); + DBGC ( snp, "SNP %s could not receive: %s\n", + netdev->name, strerror ( rc ) ); + netdev_rx_err ( netdev, NULL, rc ); break; } - /* Packet is valid, deliver it */ - iob_put ( iobuf, len ); - netdev_rx ( netdev, iob_disown ( iobuf ) ); + /* Hand off to network stack */ + iob_put ( snp->rxbuf, len ); + netdev_rx ( netdev, snp->rxbuf ); + snp->rxbuf = NULL; } } /** - * Open NIC + * Poll for completed packets * - * @v netdev Net device + * @v netdev Network device + */ +static void snpnet_poll ( struct net_device *netdev ) { + + /* Process any TX completions */ + snpnet_poll_tx ( netdev ); + + /* Process any RX completions */ + snpnet_poll_rx ( netdev ); + + /* Check for link state changes */ + snpnet_check_link ( netdev ); +} + +/** + * Set receive filters + * + * @v netdev Network device * @ret rc Return status code */ -static int snpnet_open ( struct net_device *netdev ) { - struct snpnet_device *snpnetdev = netdev->priv; - EFI_SIMPLE_NETWORK_PROTOCOL *snp = snpnetdev->snp; - EFI_MAC_ADDRESS *mac; - UINT32 enableFlags, disableFlags; +static int snpnet_rx_filters ( struct net_device *netdev ) { + struct snp_nic *snp = netdev->priv; + UINT32 filters[] = { + snp->snp->Mode->ReceiveFilterMask, + ( EFI_SIMPLE_NETWORK_RECEIVE_UNICAST | + EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST | + EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST ), + ( EFI_SIMPLE_NETWORK_RECEIVE_UNICAST | + EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST ), + ( EFI_SIMPLE_NETWORK_RECEIVE_UNICAST ), + }; + unsigned int i; EFI_STATUS efirc; int rc; - snpnetdev->close_state = snp->Mode->State; - if ( snp->Mode->State != EfiSimpleNetworkInitialized ) { - if ( ( efirc = snp->Initialize ( snp, 0, 0 ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( snp, "SNP %p could not initialize: %s\n", - snp, strerror ( rc ) ); - return rc; - } + /* Try possible receive filters in turn */ + for ( i = 0; i < ( sizeof ( filters ) / sizeof ( filters[0] ) ); i++ ) { + efirc = snp->snp->ReceiveFilters ( snp->snp, filters[i], + 0, TRUE, 0, NULL ); + if ( efirc == 0 ) + return 0; + rc = -EEFI ( efirc ); + DBGC ( snp, "SNP %s could not set receive filters %#02x (have " + "%#02x): %s\n", netdev->name, filters[i], + snp->snp->Mode->ReceiveFilterSetting, strerror ( rc ) ); } - /* Use the default MAC address */ - mac = ( ( void * ) netdev->ll_addr ); - if ( ( efirc = snp->StationAddress ( snp, FALSE, mac ) ) != 0 ) { + return rc; +} + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int snpnet_open ( struct net_device *netdev ) { + struct snp_nic *snp = netdev->priv; + EFI_MAC_ADDRESS *mac = ( ( void * ) netdev->ll_addr ); + EFI_STATUS efirc; + int rc; + + /* Try setting MAC address (before initialising) */ + if ( ( efirc = snp->snp->StationAddress ( snp->snp, FALSE, mac ) ) !=0){ rc = -EEFI ( efirc ); - DBGC ( snp, "SNP %p could not reset station address: %s\n", - snp, strerror ( rc ) ); + DBGC ( snp, "SNP %s could not set station address before " + "initialising: %s\n", netdev->name, strerror ( rc ) ); + /* Ignore error */ } - /* Set up receive filters to receive unicast and broadcast packets - * always. Also, enable either promiscuous multicast (if possible) or - * promiscuous operation, in order to catch all multicast packets. - */ - enableFlags = snp->Mode->ReceiveFilterMask & - ( EFI_SIMPLE_NETWORK_RECEIVE_UNICAST | - EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST ); - disableFlags = snp->Mode->ReceiveFilterMask & - ( EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST | - EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS | - EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST ); - if ( snp->Mode->ReceiveFilterMask & - EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST ) { - enableFlags |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST; - } else if ( snp->Mode->ReceiveFilterMask & - EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS ) { - enableFlags |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS; + /* Initialise NIC */ + if ( ( efirc = snp->snp->Initialize ( snp->snp, 0, 0 ) ) != 0 ) { + rc = -EEFI ( efirc ); + snpnet_dump_mode ( netdev ); + DBGC ( snp, "SNP %s could not initialise: %s\n", + netdev->name, strerror ( rc ) ); + return rc; } - disableFlags &= ~enableFlags; - if ( ( efirc = snp->ReceiveFilters ( snp, enableFlags, disableFlags, - FALSE, 0, NULL ) ) != 0 ) { + + /* Try setting MAC address (after initialising) */ + if ( ( efirc = snp->snp->StationAddress ( snp->snp, FALSE, mac ) ) !=0){ rc = -EEFI ( efirc ); - DBGC ( snp, "SNP %p could not set receive filters: %s\n", - snp, strerror ( rc ) ); + DBGC ( snp, "SNP %s could not set station address after " + "initialising: %s\n", netdev->name, strerror ( rc ) ); + /* Ignore error */ + } + + /* Set receive filters */ + if ( ( rc = snpnet_rx_filters ( netdev ) ) != 0 ) { + /* Ignore error */ } - DBGC ( snp, "SNP %p opened\n", snp ); + /* Dump mode information (for debugging) */ + snpnet_dump_mode ( netdev ); + return 0; } /** - * Close NIC + * Close network device * - * @v netdev Net device + * @v netdev Network device */ static void snpnet_close ( struct net_device *netdev ) { - struct snpnet_device *snpnetdev = netdev->priv; - EFI_SIMPLE_NETWORK_PROTOCOL *snp = snpnetdev->snp; + struct snp_nic *snp = netdev->priv; EFI_STATUS efirc; int rc; - if ( snpnetdev->close_state != EfiSimpleNetworkInitialized ) { - if ( ( efirc = snp->Shutdown ( snp ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( snp, "SNP %p could not shut down: %s\n", - snp, strerror ( rc ) ); - } + /* Shut down NIC */ + if ( ( efirc = snp->snp->Shutdown ( snp->snp ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( snp, "SNP %s could not shut down: %s\n", + netdev->name, strerror ( rc ) ); + /* Nothing we can do about this */ } -} -/** - * Enable/disable interrupts - * - * @v netdev Net device - * @v enable Interrupts should be enabled - */ -static void snpnet_irq ( struct net_device *netdev, int enable ) { - struct snpnet_device *snpnetdev = netdev->priv; - EFI_SIMPLE_NETWORK_PROTOCOL *snp = snpnetdev->snp; + /* Discard transmit buffer, if applicable */ + if ( snp->txbuf ) { + netdev_tx_complete_err ( netdev, snp->txbuf, -ECANCELED ); + snp->txbuf = NULL; + } - /* On EFI, interrupts are never necessary. (This function is only - * required for BIOS PXE.) If interrupts were required, they could be - * simulated using a fast timer. - */ - DBGC ( snp, "SNP %p cannot %s interrupts\n", - snp, ( enable ? "enable" : "disable" ) ); + /* Discard receive buffer, if applicable */ + if ( snp->rxbuf ) { + free_iob ( snp->rxbuf ); + snp->rxbuf = NULL; + } } /** SNP network device operations */ static struct net_device_operations snpnet_operations = { - .open = snpnet_open, - .close = snpnet_close, - .transmit = snpnet_transmit, - .poll = snpnet_poll, - .irq = snpnet_irq, + .open = snpnet_open, + .close = snpnet_close, + .transmit = snpnet_transmit, + .poll = snpnet_poll, }; /** - * Probe SNP device + * Attach driver to device * - * @v snpdev SNP device + * @v efidev EFI device * @ret rc Return status code */ -int snpnet_probe ( struct snp_device *snpdev ) { - EFI_SIMPLE_NETWORK_PROTOCOL *snp = snpdev->snp; - EFI_STATUS efirc; +int snpnet_start ( struct efi_device *efidev ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE device = efidev->device; + EFI_SIMPLE_NETWORK_MODE *mode; struct net_device *netdev; - struct snpnet_device *snpnetdev; + struct snp_nic *snp; + void *interface; + EFI_STATUS efirc; int rc; - DBGC ( snp, "SNP %p probing...\n", snp ); + /* Open SNP protocol */ + if ( ( efirc = bs->OpenProtocol ( device, + &efi_simple_network_protocol_guid, + &interface, efi_image_handle, device, + ( EFI_OPEN_PROTOCOL_BY_DRIVER | + EFI_OPEN_PROTOCOL_EXCLUSIVE )))!=0){ + rc = -EEFI ( efirc ); + DBGC ( device, "SNP %p %s cannot open SNP protocol: %s\n", + device, efi_handle_name ( device ), strerror ( rc ) ); + DBGC_EFI_OPENERS ( device, device, + &efi_simple_network_protocol_guid ); + goto err_open_protocol; + } - /* Allocate net device */ - netdev = alloc_etherdev ( sizeof ( struct snpnet_device ) ); - if ( ! netdev ) - return -ENOMEM; + /* Allocate and initialise structure */ + netdev = alloc_etherdev ( sizeof ( *snp ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } netdev_init ( netdev, &snpnet_operations ); - netdev->dev = &snpdev->dev; - snpdev->netdev = netdev; - snpnetdev = netdev->priv; - snpnetdev->snp = snp; - snpdev->removal_state = snp->Mode->State; - - /* Start the interface */ - if ( snp->Mode->State == EfiSimpleNetworkStopped ) { - if ( ( efirc = snp->Start ( snp ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( snp, "SNP %p could not start: %s\n", snp, - strerror ( rc ) ); - goto err_start; - } + snp = netdev->priv; + snp->efidev = efidev; + snp->snp = interface; + mode = snp->snp->Mode; + efidev_set_drvdata ( efidev, netdev ); + + /* Populate underlying device information */ + efi_device_info ( device, "SNP", &snp->dev ); + snp->dev.driver_name = "SNP"; + snp->dev.parent = &efidev->dev; + list_add ( &snp->dev.siblings, &efidev->dev.children ); + INIT_LIST_HEAD ( &snp->dev.children ); + netdev->dev = &snp->dev; + + /* Bring to the Started state */ + if ( ( mode->State == EfiSimpleNetworkStopped ) && + ( ( efirc = snp->snp->Start ( snp->snp ) ) != 0 ) ) { + rc = -EEFI ( efirc ); + DBGC ( device, "SNP %p %s could not start: %s\n", device, + efi_handle_name ( device ), strerror ( rc ) ); + goto err_start; + } + if ( ( mode->State == EfiSimpleNetworkInitialized ) && + ( ( efirc = snp->snp->Shutdown ( snp->snp ) ) != 0 ) ) { + rc = -EEFI ( efirc ); + DBGC ( device, "SNP %p %s could not shut down: %s\n", device, + efi_handle_name ( device ), strerror ( rc ) ); + goto err_shutdown; } - if ( snp->Mode->HwAddressSize > sizeof ( netdev->hw_addr ) ) { - DBGC ( snp, "SNP %p hardware address is too large\n", snp ); - rc = -EINVAL; - goto err_hwaddr; + /* Populate network device parameters */ + if ( mode->HwAddressSize != netdev->ll_protocol->hw_addr_len ) { + DBGC ( device, "SNP %p %s has invalid hardware address " + "length %d\n", device, efi_handle_name ( device ), + mode->HwAddressSize ); + rc = -ENOTSUP; + goto err_hw_addr_len; } - memcpy ( netdev->hw_addr, snp->Mode->PermanentAddress.Addr, - snp->Mode->HwAddressSize ); + memcpy ( netdev->hw_addr, &mode->PermanentAddress, + netdev->ll_protocol->hw_addr_len ); + if ( mode->HwAddressSize != netdev->ll_protocol->ll_addr_len ) { + DBGC ( device, "SNP %p %s has invalid link-layer address " + "length %d\n", device, efi_handle_name ( device ), + mode->HwAddressSize ); + rc = -ENOTSUP; + goto err_ll_addr_len; + } + memcpy ( netdev->ll_addr, &mode->CurrentAddress, + netdev->ll_protocol->ll_addr_len ); + snp->mtu = ( snp->snp->Mode->MaxPacketSize + + snp->snp->Mode->MediaHeaderSize ); /* Register network device */ if ( ( rc = register_netdev ( netdev ) ) != 0 ) - goto err_register; - - /* Mark as link up; we don't handle link state */ - netdev_link_up ( netdev ); + goto err_register_netdev; + DBGC ( device, "SNP %p %s registered as %s\n", + device, efi_handle_name ( device ), netdev->name ); + + /* Set initial link state */ + if ( snp->snp->Mode->MediaPresentSupported ) { + snpnet_check_link ( netdev ); + } else { + netdev_link_up ( netdev ); + } - DBGC ( snp, "SNP %p added\n", snp ); return 0; -err_register: -err_hwaddr: - if ( snpdev->removal_state == EfiSimpleNetworkStopped ) - snp->Stop ( snp ); - -err_start: + unregister_netdev ( netdev ); + err_register_netdev: + err_ll_addr_len: + err_hw_addr_len: + err_shutdown: + err_start: + list_del ( &snp->dev.siblings ); netdev_nullify ( netdev ); netdev_put ( netdev ); - snpdev->netdev = NULL; + err_alloc: + bs->CloseProtocol ( device, &efi_simple_network_protocol_guid, + efi_image_handle, device ); + err_open_protocol: return rc; } /** - * Remove SNP device + * Detach driver from device * - * @v snpdev SNP device - */ -void snpnet_remove ( struct snp_device *snpdev ) { - EFI_SIMPLE_NETWORK_PROTOCOL *snp = snpdev->snp; - struct net_device *netdev = snpdev->netdev; + * @v efidev EFI device + */ +void snpnet_stop ( struct efi_device *efidev ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + struct net_device *netdev = efidev_get_drvdata ( efidev ); + struct snp_nic *snp = netdev->priv; + EFI_HANDLE device = efidev->device; EFI_STATUS efirc; int rc; - if ( snp->Mode->State == EfiSimpleNetworkInitialized && - snpdev->removal_state != EfiSimpleNetworkInitialized ) { - DBGC ( snp, "SNP %p shutting down\n", snp ); - if ( ( efirc = snp->Shutdown ( snp ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( snp, "SNP %p could not shut down: %s\n", - snp, strerror ( rc ) ); - } - } + /* Unregister network device */ + unregister_netdev ( netdev ); - if ( snp->Mode->State == EfiSimpleNetworkStarted && - snpdev->removal_state == EfiSimpleNetworkStopped ) { - DBGC ( snp, "SNP %p stopping\n", snp ); - if ( ( efirc = snp->Stop ( snp ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( snp, "SNP %p could not be stopped: %s\n", - snp, strerror ( rc ) ); - } + /* Stop SNP protocol */ + if ( ( efirc = snp->snp->Stop ( snp->snp ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( device, "SNP %p %s could not stop: %s\n", device, + efi_handle_name ( device ), strerror ( rc ) ); + /* Nothing we can do about this */ } - /* Unregister net device */ - unregister_netdev ( netdev ); - /* Free network device */ + list_del ( &snp->dev.siblings ); netdev_nullify ( netdev ); netdev_put ( netdev ); - DBGC ( snp, "SNP %p removed\n", snp ); + /* Close SNP protocol */ + bs->CloseProtocol ( device, &efi_simple_network_protocol_guid, + efi_image_handle, device ); } diff --git a/roms/ipxe/src/drivers/net/efi/snpnet.h b/roms/ipxe/src/drivers/net/efi/snpnet.h index 72b4a7d..e6d31d5 100644 --- a/roms/ipxe/src/drivers/net/efi/snpnet.h +++ b/roms/ipxe/src/drivers/net/efi/snpnet.h @@ -1,35 +1,17 @@ -/* - * Copyright (C) 2010 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; either version 2 of the - * License, 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - */ - #ifndef _SNPNET_H #define _SNPNET_H /** @file * - * EFI Simple Network Protocol network device driver + * SNP NIC driver * */ FILE_LICENCE ( GPL2_OR_LATER ); -struct snp_device; +struct efi_device; -extern int snpnet_probe ( struct snp_device *snpdev ); -extern void snpnet_remove ( struct snp_device *snpdev ); +extern int snpnet_start ( struct efi_device *efidev ); +extern void snpnet_stop ( struct efi_device *efidev ); #endif /* _SNPNET_H */ diff --git a/roms/ipxe/src/drivers/net/efi/snponly.c b/roms/ipxe/src/drivers/net/efi/snponly.c index 6fcc54a..99f264b 100644 --- a/roms/ipxe/src/drivers/net/efi/snponly.c +++ b/roms/ipxe/src/drivers/net/efi/snponly.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 VMware, Inc. All Rights Reserved. + * Copyright (C) 2014 Michael Brown . * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -13,117 +13,196 @@ * * 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. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. */ FILE_LICENCE ( GPL2_OR_LATER ); #include #include -#include #include #include +#include +#include #include -#include "snp.h" +#include #include "snpnet.h" +#include "nii.h" /** @file * - * Chain-loading Simple Network Protocol Bus Driver + * EFI chainloaded-device-only driver * - * This bus driver allows iPXE to use the EFI Simple Network Protocol provided - * by the platform to transmit and receive packets. It attaches to only the - * device handle that iPXE was loaded from, that is, it will only use the - * Simple Network Protocol on the current loaded image's device handle. - * - * Eseentially, this driver provides the EFI equivalent of the "undionly" - * driver. */ -/** The one and only SNP network device */ -static struct snp_device snponly_dev; +/** A chainloaded protocol */ +struct chained_protocol { + /** Protocol GUID */ + EFI_GUID *protocol; + /** + * Protocol instance installed on the loaded image's device handle + * + * We match against the protocol instance (rather than simply + * matching against the device handle itself) because some + * systems load us via a child of the underlying device, with + * a duplicate protocol installed on the child handle. + */ + void *interface; +}; + +/** Chainloaded SNP protocol */ +static struct chained_protocol chained_snp = { + .protocol = &efi_simple_network_protocol_guid, +}; -/** EFI simple network protocol GUID */ -static EFI_GUID efi_simple_network_protocol_guid - = EFI_SIMPLE_NETWORK_PROTOCOL_GUID; +/** Chainloaded NII protocol */ +static struct chained_protocol chained_nii = { + .protocol = &efi_nii31_protocol_guid, +}; /** - * Probe SNP root bus + * Locate chainloaded protocol instance * - * @v rootdev SNP bus root device - * - * Look at the loaded image's device handle and see if the simple network - * protocol exists. If so, register a driver for it. + * @v chained Chainloaded protocol + * @ret rc Return status code */ -static int snpbus_probe ( struct root_device *rootdev ) { +static int chained_locate ( struct chained_protocol *chained ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE device = efi_loaded_image->DeviceHandle; + EFI_HANDLE parent; EFI_STATUS efirc; int rc; - void *snp; - - efirc = bs->OpenProtocol ( efi_loaded_image->DeviceHandle, - &efi_simple_network_protocol_guid, - &snp, efi_image_handle, NULL, - EFI_OPEN_PROTOCOL_GET_PROTOCOL ); - if ( efirc ) { - DBG ( "Could not find Simple Network Protocol!\n" ); - return -ENODEV; + + /* Locate handle supporting this protocol */ + if ( ( rc = efi_locate_device ( device, chained->protocol, + &parent ) ) != 0 ) { + DBGC ( device, "CHAINED %p %s does not support %s: %s\n", + device, efi_handle_name ( device ), + efi_guid_ntoa ( chained->protocol ), strerror ( rc ) ); + goto err_locate_device; } - snponly_dev.snp = snp; + DBGC ( device, "CHAINED %p %s found %s on ", device, + efi_handle_name ( device ), efi_guid_ntoa ( chained->protocol )); + DBGC ( device, "%p %s\n", parent, efi_handle_name ( parent ) ); + + /* Get protocol instance */ + if ( ( efirc = bs->OpenProtocol ( parent, chained->protocol, + &chained->interface, efi_image_handle, + device, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ + rc = -EEFI ( efirc ); + DBGC ( device, "CHAINED %p %s could not open %s on ", + device, efi_handle_name ( device ), + efi_guid_ntoa ( chained->protocol ) ); + DBGC ( device, "%p %s: %s\n", + parent, efi_handle_name ( parent ), strerror ( rc ) ); + goto err_open_protocol; + } + + err_locate_device: + bs->CloseProtocol ( parent, chained->protocol, efi_image_handle, + device ); + err_open_protocol: + return rc; +} + +/** + * Check to see if driver supports a device + * + * @v device EFI device handle + * @v chained Chainloaded protocol + * @ret rc Return status code + */ +static int chained_supported ( EFI_HANDLE device, + struct chained_protocol *chained ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_STATUS efirc; + void *interface; + int rc; - /* Add to device hierarchy */ - strncpy ( snponly_dev.dev.name, "EFI SNP", - ( sizeof ( snponly_dev.dev.name ) - 1 ) ); - snponly_dev.dev.parent = &rootdev->dev; - list_add ( &snponly_dev.dev.siblings, &rootdev->dev.children); - INIT_LIST_HEAD ( &snponly_dev.dev.children ); + /* Get protocol */ + if ( ( efirc = bs->OpenProtocol ( device, chained->protocol, &interface, + efi_image_handle, device, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ + rc = -EEFI ( efirc ); + DBGCP ( device, "CHAINED %p %s is not a %s device\n", + device, efi_handle_name ( device ), + efi_guid_ntoa ( chained->protocol ) ); + goto err_open_protocol; + } - /* Create network device */ - if ( ( rc = snpnet_probe ( &snponly_dev ) ) != 0 ) - goto err; + /* Test for a match against the chainloading device */ + if ( interface != chained->interface ) { + DBGC ( device, "CHAINED %p %s %p is not the chainloaded " + "%s\n", device, efi_handle_name ( device ), + interface, efi_guid_ntoa ( chained->protocol ) ); + rc = -ENOTTY; + goto err_no_match; + } - return 0; + /* Success */ + rc = 0; + DBGC ( device, "CHAINED %p %s %p is the chainloaded %s\n", + device, efi_handle_name ( device ), interface, + efi_guid_ntoa ( chained->protocol ) ); -err: - list_del ( &snponly_dev.dev.siblings ); + err_no_match: + bs->CloseProtocol ( device, chained->protocol, efi_image_handle, + device ); + err_open_protocol: return rc; } /** - * Remove SNP root bus + * Check to see if driver supports a device * - * @v rootdev SNP bus root device + * @v device EFI device handle + * @ret rc Return status code */ -static void snpbus_remove ( struct root_device *rootdev __unused ) { - snpnet_remove ( &snponly_dev ); - list_del ( &snponly_dev.dev.siblings ); +static int snponly_supported ( EFI_HANDLE device ) { + + return chained_supported ( device, &chained_snp ); } -/** SNP bus root device driver */ -static struct root_driver snp_root_driver = { - .probe = snpbus_probe, - .remove = snpbus_remove, +/** + * Check to see if driver supports a device + * + * @v device EFI device handle + * @ret rc Return status code + */ +static int niionly_supported ( EFI_HANDLE device ) { + + return chained_supported ( device, &chained_nii ); +} + +/** EFI SNP chainloading-device-only driver */ +struct efi_driver snponly_driver __efi_driver ( EFI_DRIVER_NORMAL ) = { + .name = "SNPONLY", + .supported = snponly_supported, + .start = snpnet_start, + .stop = snpnet_stop, }; -/** SNP bus root device */ -struct root_device snp_root_device __root_device = { - .dev = { .name = "EFI SNP" }, - .driver = &snp_root_driver, +/** EFI NII chainloading-device-only driver */ +struct efi_driver niionly_driver __efi_driver ( EFI_DRIVER_NORMAL ) = { + .name = "NIIONLY", + .supported = niionly_supported, + .start = nii_start, + .stop = nii_stop, }; /** - * Prepare for exit + * Initialise EFI chainloaded-device-only driver * - * @v booting System is shutting down for OS boot */ -static void snponly_shutdown ( int booting ) { - /* If we are shutting down to boot an OS, make sure the SNP does not - * stay active. - */ - if ( booting ) - snponly_dev.removal_state = EfiSimpleNetworkStopped; +static void chained_init ( void ) { + + chained_locate ( &chained_snp ); + chained_locate ( &chained_nii ); } -struct startup_fn startup_snponly __startup_fn ( STARTUP_LATE ) = { - .shutdown = snponly_shutdown, +/** EFI chainloaded-device-only initialisation function */ +struct init_fn chained_init_fn __init_fn ( INIT_LATE ) = { + .initialise = chained_init, }; diff --git a/roms/ipxe/src/drivers/net/igbvf/igbvf_main.c b/roms/ipxe/src/drivers/net/igbvf/igbvf_main.c index cd189ec..aace5ad 100644 --- a/roms/ipxe/src/drivers/net/igbvf/igbvf_main.c +++ b/roms/ipxe/src/drivers/net/igbvf/igbvf_main.c @@ -617,6 +617,10 @@ static int igbvf_open ( struct net_device *netdev ) DBG ("igbvf_open\n"); + /* Update MAC address */ + memcpy ( adapter->hw.mac.addr, netdev->ll_addr, ETH_ALEN ); + igbvf_reset( adapter ); + /* allocate transmit descriptors */ err = igbvf_setup_tx_resources ( adapter ); if (err) { @@ -871,20 +875,14 @@ int igbvf_probe ( struct pci_device *pdev ) DBG ("Error reading MAC address\n"); goto err_hw_init; } + if ( ! is_valid_ether_addr(adapter->hw.mac.addr) ) { + /* Assign random MAC address */ + eth_random_addr(adapter->hw.mac.addr); + } } memcpy ( netdev->hw_addr, adapter->hw.mac.addr, ETH_ALEN ); - if ( ! is_valid_ether_addr( netdev->hw_addr ) ) { - DBG ("Invalid MAC Address: " - "%02x:%02x:%02x:%02x:%02x:%02x\n", - netdev->hw_addr[0], netdev->hw_addr[1], - netdev->hw_addr[2], netdev->hw_addr[3], - netdev->hw_addr[4], netdev->hw_addr[5]); - err = -EIO; - goto err_hw_init; - } - /* reset the hardware with the new settings */ igbvf_reset ( adapter ); diff --git a/roms/ipxe/src/drivers/net/intel.c b/roms/ipxe/src/drivers/net/intel.c index 6684bdb..a89f947 100644 --- a/roms/ipxe/src/drivers/net/intel.c +++ b/roms/ipxe/src/drivers/net/intel.c @@ -232,16 +232,16 @@ static int intel_fetch_mac ( struct intel_nic *intel, uint8_t *hw_addr ) { DBGC ( intel, "INTEL %p has autoloaded MAC address %s\n", intel, eth_ntoa ( mac.raw ) ); - /* Try to read address from EEPROM */ - if ( ( rc = intel_fetch_mac_eeprom ( intel, hw_addr ) ) == 0 ) - return 0; - /* Use current address if valid */ if ( is_valid_ether_addr ( mac.raw ) ) { memcpy ( hw_addr, mac.raw, ETH_ALEN ); return 0; } + /* Otherwise, try to read address from EEPROM */ + if ( ( rc = intel_fetch_mac_eeprom ( intel, hw_addr ) ) == 0 ) + return 0; + DBGC ( intel, "INTEL %p has no MAC address to use\n", intel ); return -ENOENT; } @@ -287,18 +287,23 @@ static void __attribute__ (( unused )) intel_diag ( struct intel_nic *intel ) { */ static int intel_reset ( struct intel_nic *intel ) { uint32_t pbs; + uint32_t pba; uint32_t ctrl; uint32_t status; /* Force RX and TX packet buffer allocation, to work around an * errata in ICH devices. */ - pbs = readl ( intel->regs + INTEL_PBS ); - if ( ( pbs == 0x14 ) || ( pbs == 0x18 ) ) { + if ( intel->flags & INTEL_PBS_ERRATA ) { DBGC ( intel, "INTEL %p WARNING: applying ICH PBS/PBA errata\n", intel ); + pbs = readl ( intel->regs + INTEL_PBS ); + pba = readl ( intel->regs + INTEL_PBA ); writel ( 0x08, intel->regs + INTEL_PBA ); writel ( 0x10, intel->regs + INTEL_PBS ); + DBGC ( intel, "INTEL %p PBS %#08x->%#08x PBA %#08x->%#08x\n", + intel, pbs, readl ( intel->regs + INTEL_PBS ), + pba, readl ( intel->regs + INTEL_PBA ) ); } /* Always reset MAC. Required to reset the TX and RX rings. */ @@ -496,6 +501,7 @@ void intel_refill_rx ( struct intel_nic *intel ) { profile_start ( &intel_vm_refill_profiler ); writel ( rx_tail, intel->regs + intel->rx.reg + INTEL_xDT ); profile_stop ( &intel_vm_refill_profiler ); + profile_exclude ( &intel_vm_refill_profiler ); } } @@ -634,6 +640,7 @@ int intel_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { profile_start ( &intel_vm_tx_profiler ); writel ( tx_tail, intel->regs + intel->tx.reg + INTEL_xDT ); profile_stop ( &intel_vm_tx_profiler ); + profile_exclude ( &intel_vm_tx_profiler ); DBGC2 ( intel, "INTEL %p TX %d is [%llx,%llx)\n", intel, tx_idx, ( ( unsigned long long ) address ), @@ -728,6 +735,7 @@ static void intel_poll ( struct net_device *netdev ) { profile_start ( &intel_vm_poll_profiler ); icr = readl ( intel->regs + INTEL_ICR ); profile_stop ( &intel_vm_poll_profiler ); + profile_exclude ( &intel_vm_poll_profiler ); if ( ! icr ) return; @@ -808,6 +816,7 @@ static int intel_probe ( struct pci_device *pci ) { netdev->dev = &pci->dev; memset ( intel, 0, sizeof ( *intel ) ); intel->port = PCI_FUNC ( pci->busdevfn ); + intel->flags = pci->id->driver_data; intel_init_ring ( &intel->tx, INTEL_NUM_TX_DESC, INTEL_TD ); intel_init_ring ( &intel->rx, INTEL_NUM_RX_DESC, INTEL_RD ); @@ -816,6 +825,10 @@ static int intel_probe ( struct pci_device *pci ) { /* Map registers */ intel->regs = ioremap ( pci->membase, INTEL_BAR_SIZE ); + if ( ! intel->regs ) { + rc = -ENODEV; + goto err_ioremap; + } /* Reset the NIC */ if ( ( rc = intel_reset ( intel ) ) != 0 ) @@ -840,6 +853,7 @@ static int intel_probe ( struct pci_device *pci ) { intel_reset ( intel ); err_reset: iounmap ( intel->regs ); + err_ioremap: netdev_nullify ( netdev ); netdev_put ( netdev ); err_alloc: @@ -898,11 +912,11 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x1026, "82545gm", "82545GM", 0 ), PCI_ROM ( 0x8086, 0x1027, "82545gm-1", "82545GM", 0 ), PCI_ROM ( 0x8086, 0x1028, "82545gm-2", "82545GM", 0 ), - PCI_ROM ( 0x8086, 0x1049, "82566mm", "82566MM", 0 ), - PCI_ROM ( 0x8086, 0x104a, "82566dm", "82566DM", 0 ), - PCI_ROM ( 0x8086, 0x104b, "82566dc", "82566DC", 0 ), - PCI_ROM ( 0x8086, 0x104c, "82562v", "82562V 10/100", 0 ), - PCI_ROM ( 0x8086, 0x104d, "82566mc", "82566MC", 0 ), + PCI_ROM ( 0x8086, 0x1049, "82566mm", "82566MM", INTEL_PBS_ERRATA ), + PCI_ROM ( 0x8086, 0x104a, "82566dm", "82566DM", INTEL_PBS_ERRATA ), + PCI_ROM ( 0x8086, 0x104b, "82566dc", "82566DC", INTEL_PBS_ERRATA ), + PCI_ROM ( 0x8086, 0x104c, "82562v", "82562V", INTEL_PBS_ERRATA ), + PCI_ROM ( 0x8086, 0x104d, "82566mc", "82566MC", INTEL_PBS_ERRATA ), PCI_ROM ( 0x8086, 0x105e, "82571eb", "82571EB", 0 ), PCI_ROM ( 0x8086, 0x105f, "82571eb-1", "82571EB", 0 ), PCI_ROM ( 0x8086, 0x1060, "82571eb-2", "82571EB", 0 ), @@ -935,11 +949,11 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x10bc, "82571eb", "82571EB (Copper)", 0 ), PCI_ROM ( 0x8086, 0x10bd, "82566dm-2", "82566DM-2", 0 ), PCI_ROM ( 0x8086, 0x10bf, "82567lf", "82567LF", 0 ), - PCI_ROM ( 0x8086, 0x10c0, "82562v-2", "82562V-2 10/100", 0 ), - PCI_ROM ( 0x8086, 0x10c2, "82562g-2", "82562G-2 10/100", 0 ), - PCI_ROM ( 0x8086, 0x10c3, "82562gt-2", "82562GT-2 10/100", 0 ), - PCI_ROM ( 0x8086, 0x10c4, "82562gt", "82562GT 10/100", 0 ), - PCI_ROM ( 0x8086, 0x10c5, "82562g", "82562G 10/100", 0 ), + PCI_ROM ( 0x8086, 0x10c0, "82562v-2", "82562V-2", 0 ), + PCI_ROM ( 0x8086, 0x10c2, "82562g-2", "82562G-2", 0 ), + PCI_ROM ( 0x8086, 0x10c3, "82562gt-2", "82562GT-2", 0 ), + PCI_ROM ( 0x8086, 0x10c4, "82562gt", "82562GT", INTEL_PBS_ERRATA ), + PCI_ROM ( 0x8086, 0x10c5, "82562g", "82562G", INTEL_PBS_ERRATA ), PCI_ROM ( 0x8086, 0x10c9, "82576", "82576", 0 ), PCI_ROM ( 0x8086, 0x10cb, "82567v", "82567V", 0 ), PCI_ROM ( 0x8086, 0x10cc, "82567lm-2", "82567LM-2", 0 ), @@ -962,7 +976,7 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x10f0, "82578dc", "82578DC", 0 ), PCI_ROM ( 0x8086, 0x10f5, "82567lm", "82567LM", 0 ), PCI_ROM ( 0x8086, 0x10f6, "82574l", "82574L", 0 ), - PCI_ROM ( 0x8086, 0x1501, "82567v-3", "82567V-3", 0 ), + PCI_ROM ( 0x8086, 0x1501, "82567v-3", "82567V-3", INTEL_PBS_ERRATA ), PCI_ROM ( 0x8086, 0x1502, "82579lm", "82579LM", 0 ), PCI_ROM ( 0x8086, 0x1503, "82579v", "82579V", 0 ), PCI_ROM ( 0x8086, 0x150a, "82576ns", "82576NS", 0 ), @@ -982,7 +996,8 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x1526, "82576-5", "82576", 0 ), PCI_ROM ( 0x8086, 0x1527, "82580-f2", "82580 Fiber", 0 ), PCI_ROM ( 0x8086, 0x1533, "i210", "I210", 0 ), - PCI_ROM ( 0x8086, 0x153b, "i217", "I217", 0 ), + PCI_ROM ( 0x8086, 0x153a, "i217lm", "I217-LM", 0 ), + PCI_ROM ( 0x8086, 0x153b, "i217v", "I217-V", 0 ), PCI_ROM ( 0x8086, 0x294c, "82566dc-2", "82566DC-2", 0 ), PCI_ROM ( 0x8086, 0x2e6e, "cemedia", "CE Media Processor", 0 ), }; diff --git a/roms/ipxe/src/drivers/net/intel.h b/roms/ipxe/src/drivers/net/intel.h index 8ce9ea9..8c4479b 100644 --- a/roms/ipxe/src/drivers/net/intel.h +++ b/roms/ipxe/src/drivers/net/intel.h @@ -138,10 +138,10 @@ enum intel_descriptor_status { * Minimum value is 8, since the descriptor ring length must be a * multiple of 128. */ -#define INTEL_NUM_RX_DESC 8 +#define INTEL_NUM_RX_DESC 16 /** Receive descriptor ring fill level */ -#define INTEL_RX_FILL 4 +#define INTEL_RX_FILL 8 /** Receive buffer length */ #define INTEL_RX_MAX_LEN 2048 @@ -229,6 +229,8 @@ struct intel_nic { void *regs; /** Port number (for multi-port devices) */ unsigned int port; + /** Flags */ + unsigned int flags; /** EEPROM */ struct nvs_device eeprom; @@ -245,6 +247,12 @@ struct intel_nic { struct io_buffer *rx_iobuf[INTEL_NUM_RX_DESC]; }; +/** Driver flags */ +enum intel_flags { + /** PBS/PBA errata workaround required */ + INTEL_PBS_ERRATA = 0x0001, +}; + extern int intel_create_ring ( struct intel_nic *intel, struct intel_ring *ring ); extern void intel_destroy_ring ( struct intel_nic *intel, diff --git a/roms/ipxe/src/drivers/net/intelx.c b/roms/ipxe/src/drivers/net/intelx.c index eb8b2a6..d69900e 100644 --- a/roms/ipxe/src/drivers/net/intelx.c +++ b/roms/ipxe/src/drivers/net/intelx.c @@ -400,6 +400,10 @@ static int intelx_probe ( struct pci_device *pci ) { /* Map registers */ intel->regs = ioremap ( pci->membase, INTEL_BAR_SIZE ); + if ( ! intel->regs ) { + rc = -ENODEV; + goto err_ioremap; + } /* Reset the NIC */ if ( ( rc = intelx_reset ( intel ) ) != 0 ) @@ -424,6 +428,7 @@ static int intelx_probe ( struct pci_device *pci ) { intelx_reset ( intel ); err_reset: iounmap ( intel->regs ); + err_ioremap: netdev_nullify ( netdev ); netdev_put ( netdev ); err_alloc: @@ -456,6 +461,7 @@ static struct pci_device_id intelx_nics[] = { PCI_ROM ( 0x8086, 0x10fb, "82599", "82599", 0 ), PCI_ROM ( 0x8086, 0x1528, "x540at2", "X540-AT2", 0 ), PCI_ROM ( 0x8086, 0x154d, "x520", "X520", 0 ), + PCI_ROM ( 0x8086, 0x1557, "82599", "82599", 0 ), }; /** PCI driver */ diff --git a/roms/ipxe/src/drivers/net/myson.c b/roms/ipxe/src/drivers/net/myson.c index 237f872..6abb556 100644 --- a/roms/ipxe/src/drivers/net/myson.c +++ b/roms/ipxe/src/drivers/net/myson.c @@ -603,6 +603,10 @@ static int myson_probe ( struct pci_device *pci ) { /* Map registers */ myson->regs = ioremap ( pci->membase, MYSON_BAR_SIZE ); + if ( ! myson->regs ) { + rc = -ENODEV; + goto err_ioremap; + } /* Reset the NIC */ if ( ( rc = myson_reset ( myson ) ) != 0 ) @@ -627,6 +631,7 @@ static int myson_probe ( struct pci_device *pci ) { myson_reset ( myson ); err_reset: iounmap ( myson->regs ); + err_ioremap: netdev_nullify ( netdev ); netdev_put ( netdev ); err_alloc: diff --git a/roms/ipxe/src/drivers/net/natsemi.c b/roms/ipxe/src/drivers/net/natsemi.c index 669fb87..9f2c302 100644 --- a/roms/ipxe/src/drivers/net/natsemi.c +++ b/roms/ipxe/src/drivers/net/natsemi.c @@ -854,6 +854,10 @@ static int natsemi_probe ( struct pci_device *pci ) { /* Map registers */ natsemi->regs = ioremap ( pci->membase, NATSEMI_BAR_SIZE ); + if ( ! natsemi->regs ) { + rc = -ENODEV; + goto err_ioremap; + } /* Reset the NIC */ if ( ( rc = natsemi_reset ( natsemi ) ) != 0 ) @@ -881,6 +885,7 @@ static int natsemi_probe ( struct pci_device *pci ) { natsemi_reset ( natsemi ); err_reset: iounmap ( natsemi->regs ); + err_ioremap: netdev_nullify ( netdev ); netdev_put ( netdev ); err_alloc: diff --git a/roms/ipxe/src/drivers/net/netfront.c b/roms/ipxe/src/drivers/net/netfront.c new file mode 100644 index 0000000..4b81632 --- /dev/null +++ b/roms/ipxe/src/drivers/net/netfront.c @@ -0,0 +1,940 @@ +/* + * Copyright (C) 2014 Michael Brown . + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "netfront.h" + +/** @file + * + * Xen netfront driver + * + */ + +/* Disambiguate the various error causes */ +#define EIO_NETIF_RSP_ERROR \ + __einfo_error ( EINFO_EIO_NETIF_RSP_ERROR ) +#define EINFO_EIO_NETIF_RSP_ERROR \ + __einfo_uniqify ( EINFO_EIO, -NETIF_RSP_ERROR, \ + "Unspecified network error" ) +#define EIO_NETIF_RSP_DROPPED \ + __einfo_error ( EINFO_EIO_NETIF_RSP_DROPPED ) +#define EINFO_EIO_NETIF_RSP_DROPPED \ + __einfo_uniqify ( EINFO_EIO, -NETIF_RSP_DROPPED, \ + "Packet dropped" ) +#define EIO_NETIF_RSP( status ) \ + EUNIQ ( EINFO_EIO, -(status), \ + EIO_NETIF_RSP_ERROR, EIO_NETIF_RSP_DROPPED ) + +/****************************************************************************** + * + * XenStore interface + * + ****************************************************************************** + */ + +/** + * Reset device + * + * @v netfront Netfront device + * @ret rc Return status code + */ +static int netfront_reset ( struct netfront_nic *netfront ) { + struct xen_device *xendev = netfront->xendev; + int state; + int rc; + + /* Get current backend state */ + if ( ( state = xenbus_backend_state ( xendev ) ) < 0 ) { + rc = state; + DBGC ( netfront, "NETFRONT %s could not read backend state: " + "%s\n", xendev->key, strerror ( rc ) ); + return rc; + } + + /* If the backend is not already in InitWait, then mark + * frontend as Closed to shut down the backend. + */ + if ( state != XenbusStateInitWait ) { + + /* Set state to Closed */ + xenbus_set_state ( xendev, XenbusStateClosed ); + + /* Wait for backend to reach Closed */ + if ( ( rc = xenbus_backend_wait ( xendev, + XenbusStateClosed ) ) != 0 ) { + DBGC ( netfront, "NETFRONT %s backend did not reach " + "Closed: %s\n", xendev->key, strerror ( rc ) ); + return rc; + } + } + + /* Reset state to Initialising */ + xenbus_set_state ( xendev, XenbusStateInitialising ); + + /* Wait for backend to reach InitWait */ + if ( ( rc = xenbus_backend_wait ( xendev, XenbusStateInitWait ) ) != 0){ + DBGC ( netfront, "NETFRONT %s backend did not reach InitWait: " + "%s\n", xendev->key, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Fetch MAC address + * + * @v netfront Netfront device + * @v hw_addr Hardware address to fill in + * @ret rc Return status code + */ +static int netfront_read_mac ( struct netfront_nic *netfront, void *hw_addr ) { + struct xen_device *xendev = netfront->xendev; + struct xen_hypervisor *xen = xendev->xen; + char *mac; + int len; + int rc; + + /* Fetch MAC address */ + if ( ( rc = xenstore_read ( xen, &mac, xendev->key, "mac", NULL ) )!=0){ + DBGC ( netfront, "NETFRONT %s could not read MAC address: %s\n", + xendev->key, strerror ( rc ) ); + goto err_xenstore_read; + } + DBGC2 ( netfront, "NETFRONT %s has MAC address \"%s\"\n", + xendev->key, mac ); + + /* Decode MAC address */ + len = hex_decode ( mac, ':', hw_addr, ETH_ALEN ); + if ( len < 0 ) { + rc = len; + DBGC ( netfront, "NETFRONT %s could not decode MAC address " + "\"%s\": %s\n", xendev->key, mac, strerror ( rc ) ); + goto err_decode; + } + + /* Success */ + rc = 0; + + err_decode: + free ( mac ); + err_xenstore_read: + return rc; +} + +/** + * Write XenStore numeric value + * + * @v netfront Netfront device + * @v subkey Subkey + * @v num Numeric value + * @ret rc Return status code + */ +static int netfront_write_num ( struct netfront_nic *netfront, + const char *subkey, unsigned long num ) { + struct xen_device *xendev = netfront->xendev; + struct xen_hypervisor *xen = xendev->xen; + int rc; + + /* Write value */ + if ( ( rc = xenstore_write_num ( xen, num, xendev->key, subkey, + NULL ) ) != 0 ) { + DBGC ( netfront, "NETFRONT %s could not set %s=\"%ld\": %s\n", + xendev->key, subkey, num, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Write XenStore flag value + * + * @v netfront Netfront device + * @v subkey Subkey + * @v num Numeric value + * @ret rc Return status code + */ +static int netfront_write_flag ( struct netfront_nic *netfront, + const char *subkey ) { + + return netfront_write_num ( netfront, subkey, 1 ); +} + +/** + * Delete XenStore value + * + * @v netfront Netfront device + * @v subkey Subkey + * @ret rc Return status code + */ +static int netfront_rm ( struct netfront_nic *netfront, const char *subkey ) { + struct xen_device *xendev = netfront->xendev; + struct xen_hypervisor *xen = xendev->xen; + int rc; + + /* Remove value */ + if ( ( rc = xenstore_rm ( xen, xendev->key, subkey, NULL ) ) != 0 ) { + DBGC ( netfront, "NETFRONT %s could not delete %s: %s\n", + xendev->key, subkey, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/****************************************************************************** + * + * Events + * + ****************************************************************************** + */ + +/** + * Create event channel + * + * @v netfront Netfront device + * @ret rc Return status code + */ +static int netfront_create_event ( struct netfront_nic *netfront ) { + struct xen_device *xendev = netfront->xendev; + struct xen_hypervisor *xen = xendev->xen; + struct evtchn_alloc_unbound alloc_unbound; + struct evtchn_close close; + int xenrc; + int rc; + + /* Allocate event */ + alloc_unbound.dom = DOMID_SELF; + alloc_unbound.remote_dom = xendev->backend_id; + if ( ( xenrc = xenevent_alloc_unbound ( xen, &alloc_unbound ) ) != 0 ) { + rc = -EXEN ( xenrc ); + DBGC ( netfront, "NETFRONT %s could not allocate event: %s\n", + xendev->key, strerror ( rc ) ); + goto err_alloc_unbound; + } + netfront->event.port = alloc_unbound.port; + + /* Publish event channel */ + if ( ( rc = netfront_write_num ( netfront, "event-channel", + netfront->event.port ) ) != 0 ) + goto err_write_num; + + DBGC ( netfront, "NETFRONT %s event-channel=\"%d\"\n", + xendev->key, netfront->event.port ); + return 0; + + netfront_rm ( netfront, "event-channel" ); + err_write_num: + close.port = netfront->event.port; + xenevent_close ( xen, &close ); + err_alloc_unbound: + return rc; +} + +/** + * Send event + * + * @v netfront Netfront device + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +netfront_send_event ( struct netfront_nic *netfront ) { + struct xen_device *xendev = netfront->xendev; + struct xen_hypervisor *xen = xendev->xen; + int xenrc; + int rc; + + /* Send event */ + if ( ( xenrc = xenevent_send ( xen, &netfront->event ) ) != 0 ) { + rc = -EXEN ( xenrc ); + DBGC ( netfront, "NETFRONT %s could not send event: %s\n", + xendev->key, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Destroy event channel + * + * @v netfront Netfront device + */ +static void netfront_destroy_event ( struct netfront_nic *netfront ) { + struct xen_device *xendev = netfront->xendev; + struct xen_hypervisor *xen = xendev->xen; + struct evtchn_close close; + + /* Unpublish event channel */ + netfront_rm ( netfront, "event-channel" ); + + /* Close event channel */ + close.port = netfront->event.port; + xenevent_close ( xen, &close ); +} + +/****************************************************************************** + * + * Descriptor rings + * + ****************************************************************************** + */ + +/** + * Create descriptor ring + * + * @v netfront Netfront device + * @v ring Descriptor ring + * @ret rc Return status code + */ +static int netfront_create_ring ( struct netfront_nic *netfront, + struct netfront_ring *ring ) { + struct xen_device *xendev = netfront->xendev; + struct xen_hypervisor *xen = xendev->xen; + unsigned int i; + int rc; + + /* Initialise buffer ID ring */ + for ( i = 0 ; i < ring->count ; i++ ) { + ring->ids[i] = i; + assert ( ring->iobufs[i] == NULL ); + } + ring->id_prod = 0; + ring->id_cons = 0; + + /* Allocate and initialise shared ring */ + ring->sring.raw = malloc_dma ( PAGE_SIZE, PAGE_SIZE ); + if ( ! ring->sring.raw ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Grant access to shared ring */ + if ( ( rc = xengrant_permit_access ( xen, ring->ref, xendev->backend_id, + 0, ring->sring.raw ) ) != 0 ) { + DBGC ( netfront, "NETFRONT %s could not permit access to " + "%#08lx: %s\n", xendev->key, + virt_to_phys ( ring->sring.raw ), strerror ( rc ) ); + goto err_permit_access; + } + + /* Publish shared ring reference */ + if ( ( rc = netfront_write_num ( netfront, ring->ref_key, + ring->ref ) ) != 0 ) + goto err_write_num; + + DBGC ( netfront, "NETFRONT %s %s=\"%d\" [%08lx,%08lx)\n", + xendev->key, ring->ref_key, ring->ref, + virt_to_phys ( ring->sring.raw ), + ( virt_to_phys ( ring->sring.raw ) + PAGE_SIZE ) ); + return 0; + + netfront_rm ( netfront, ring->ref_key ); + err_write_num: + xengrant_invalidate ( xen, ring->ref ); + err_permit_access: + free_dma ( ring->sring.raw, PAGE_SIZE ); + err_alloc: + return rc; +} + +/** + * Add buffer to descriptor ring + * + * @v netfront Netfront device + * @v ring Descriptor ring + * @v iobuf I/O buffer + * @v id Buffer ID to fill in + * @v ref Grant reference to fill in + * @ret rc Return status code + * + * The caller is responsible for ensuring that there is space in the + * ring. + */ +static int netfront_push ( struct netfront_nic *netfront, + struct netfront_ring *ring, struct io_buffer *iobuf, + uint16_t *id, grant_ref_t *ref ) { + struct xen_device *xendev = netfront->xendev; + struct xen_hypervisor *xen = xendev->xen; + unsigned int next_id; + unsigned int next_ref; + int rc; + + /* Sanity check */ + assert ( ! netfront_ring_is_full ( ring ) ); + + /* Allocate buffer ID */ + next_id = ring->ids[ ring->id_prod & ( ring->count - 1 ) ]; + next_ref = ring->refs[next_id]; + + /* Grant access to I/O buffer page. I/O buffers are naturally + * aligned, so we never need to worry about crossing a page + * boundary. + */ + if ( ( rc = xengrant_permit_access ( xen, next_ref, xendev->backend_id, + 0, iobuf->data ) ) != 0 ) { + DBGC ( netfront, "NETFRONT %s could not permit access to " + "%#08lx: %s\n", xendev->key, + virt_to_phys ( iobuf->data ), strerror ( rc ) ); + return rc; + } + + /* Store I/O buffer */ + assert ( ring->iobufs[next_id] == NULL ); + ring->iobufs[next_id] = iobuf; + + /* Consume buffer ID */ + ring->id_prod++; + + /* Return buffer ID and grant reference */ + *id = next_id; + *ref = next_ref; + + return 0; +} + +/** + * Remove buffer from descriptor ring + * + * @v netfront Netfront device + * @v ring Descriptor ring + * @v id Buffer ID + * @ret iobuf I/O buffer + */ +static struct io_buffer * netfront_pull ( struct netfront_nic *netfront, + struct netfront_ring *ring, + unsigned int id ) { + struct xen_device *xendev = netfront->xendev; + struct xen_hypervisor *xen = xendev->xen; + struct io_buffer *iobuf; + + /* Sanity check */ + assert ( id < ring->count ); + + /* Revoke access from I/O buffer page */ + xengrant_invalidate ( xen, ring->refs[id] ); + + /* Retrieve I/O buffer */ + iobuf = ring->iobufs[id]; + assert ( iobuf != NULL ); + ring->iobufs[id] = NULL; + + /* Free buffer ID */ + ring->ids[ ( ring->id_cons++ ) & ( ring->count - 1 ) ] = id; + + return iobuf; +} + +/** + * Destroy descriptor ring + * + * @v netfront Netfront device + * @v ring Descriptor ring + * @v discard Method used to discard outstanding buffer, or NULL + */ +static void netfront_destroy_ring ( struct netfront_nic *netfront, + struct netfront_ring *ring, + void ( * discard ) ( struct io_buffer * ) ){ + struct xen_device *xendev = netfront->xendev; + struct xen_hypervisor *xen = xendev->xen; + struct io_buffer *iobuf; + unsigned int id; + + /* Flush any outstanding buffers */ + while ( ! netfront_ring_is_empty ( ring ) ) { + id = ring->ids[ ring->id_cons & ( ring->count - 1 ) ]; + iobuf = netfront_pull ( netfront, ring, id ); + if ( discard ) + discard ( iobuf ); + } + + /* Unpublish shared ring reference */ + netfront_rm ( netfront, ring->ref_key ); + + /* Revoke access from shared ring */ + xengrant_invalidate ( xen, ring->ref ); + + /* Free page */ + free_dma ( ring->sring.raw, PAGE_SIZE ); + ring->sring.raw = NULL; +} + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Refill receive descriptor ring + * + * @v netdev Network device + */ +static void netfront_refill_rx ( struct net_device *netdev ) { + struct netfront_nic *netfront = netdev->priv; + struct xen_device *xendev = netfront->xendev; + struct io_buffer *iobuf; + struct netif_rx_request *request; + int notify; + int rc; + + /* Do nothing if ring is already full */ + if ( netfront_ring_is_full ( &netfront->rx ) ) + return; + + /* Refill ring */ + do { + + /* Allocate I/O buffer */ + iobuf = alloc_iob ( PAGE_SIZE ); + if ( ! iobuf ) { + /* Wait for next refill */ + break; + } + + /* Add to descriptor ring */ + request = RING_GET_REQUEST ( &netfront->rx_fring, + netfront->rx_fring.req_prod_pvt ); + if ( ( rc = netfront_push ( netfront, &netfront->rx, + iobuf, &request->id, + &request->gref ) ) != 0 ) { + netdev_rx_err ( netdev, iobuf, rc ); + break; + } + DBGC2 ( netfront, "NETFRONT %s RX id %d ref %d is %#08lx+%zx\n", + xendev->key, request->id, request->gref, + virt_to_phys ( iobuf->data ), iob_tailroom ( iobuf ) ); + + /* Move to next descriptor */ + netfront->rx_fring.req_prod_pvt++; + + } while ( ! netfront_ring_is_full ( &netfront->rx ) ); + + /* Push new descriptors and notify backend if applicable */ + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY ( &netfront->rx_fring, notify ); + if ( notify ) + netfront_send_event ( netfront ); +} + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int netfront_open ( struct net_device *netdev ) { + struct netfront_nic *netfront = netdev->priv; + struct xen_device *xendev = netfront->xendev; + int rc; + + /* Ensure device is in a suitable initial state */ + if ( ( rc = netfront_reset ( netfront ) ) != 0 ) + goto err_reset; + + /* Create transmit descriptor ring */ + if ( ( rc = netfront_create_ring ( netfront, &netfront->tx ) ) != 0 ) + goto err_create_tx; + SHARED_RING_INIT ( netfront->tx_sring ); + FRONT_RING_INIT ( &netfront->tx_fring, netfront->tx_sring, PAGE_SIZE ); + assert ( RING_SIZE ( &netfront->tx_fring ) >= netfront->tx.count ); + + /* Create receive descriptor ring */ + if ( ( rc = netfront_create_ring ( netfront, &netfront->rx ) ) != 0 ) + goto err_create_rx; + SHARED_RING_INIT ( netfront->rx_sring ); + FRONT_RING_INIT ( &netfront->rx_fring, netfront->rx_sring, PAGE_SIZE ); + assert ( RING_SIZE ( &netfront->rx_fring ) >= netfront->rx.count ); + + /* Create event channel */ + if ( ( rc = netfront_create_event ( netfront ) ) != 0 ) + goto err_create_event; + + /* "Request" the rx-copy feature. Current versions of + * xen_netback.ko will fail silently if this parameter is not + * present. + */ + if ( ( rc = netfront_write_flag ( netfront, "request-rx-copy" ) ) != 0 ) + goto err_request_rx_copy; + + /* Disable checksum offload, since we will always do the work anyway */ + if ( ( rc = netfront_write_flag ( netfront, + "feature-no-csum-offload" ) ) != 0 ) + goto err_feature_no_csum_offload; + + /* Set state to Connected */ + if ( ( rc = xenbus_set_state ( xendev, XenbusStateConnected ) ) != 0 ) { + DBGC ( netfront, "NETFRONT %s could not set state=\"%d\": %s\n", + xendev->key, XenbusStateConnected, strerror ( rc ) ); + goto err_set_state; + } + + /* Wait for backend to connect */ + if ( ( rc = xenbus_backend_wait ( xendev, XenbusStateConnected ) ) !=0){ + DBGC ( netfront, "NETFRONT %s could not connect to backend: " + "%s\n", xendev->key, strerror ( rc ) ); + goto err_backend_wait; + } + + /* Refill receive descriptor ring */ + netfront_refill_rx ( netdev ); + + /* Set link up */ + netdev_link_up ( netdev ); + + return 0; + + err_backend_wait: + netfront_reset ( netfront ); + err_set_state: + netfront_rm ( netfront, "feature-no-csum-offload" ); + err_feature_no_csum_offload: + netfront_rm ( netfront, "request-rx-copy" ); + err_request_rx_copy: + netfront_destroy_event ( netfront ); + err_create_event: + netfront_destroy_ring ( netfront, &netfront->rx, NULL ); + err_create_rx: + netfront_destroy_ring ( netfront, &netfront->tx, NULL ); + err_create_tx: + err_reset: + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void netfront_close ( struct net_device *netdev ) { + struct netfront_nic *netfront = netdev->priv; + struct xen_device *xendev = netfront->xendev; + int rc; + + /* Reset devic, thereby ensuring that grant references are no + * longer in use, etc. + */ + if ( ( rc = netfront_reset ( netfront ) ) != 0 ) { + DBGC ( netfront, "NETFRONT %s could not disconnect from " + "backend: %s\n", xendev->key, strerror ( rc ) ); + /* Things will probably go _very_ badly wrong if this + * happens, since it means the backend may still write + * to the outstanding RX buffers that we are about to + * free. The best we can do is report the error via + * the link status, but there's a good chance the + * machine will crash soon. + */ + netdev_link_err ( netdev, rc ); + } else { + netdev_link_down ( netdev ); + } + + /* Delete flags */ + netfront_rm ( netfront, "feature-no-csum-offload" ); + netfront_rm ( netfront, "request-rx-copy" ); + + /* Destroy event channel */ + netfront_destroy_event ( netfront ); + + /* Destroy receive descriptor ring, freeing any outstanding + * I/O buffers. + */ + netfront_destroy_ring ( netfront, &netfront->rx, free_iob ); + + /* Destroy transmit descriptor ring. Leave any outstanding + * I/O buffers to be freed by netdev_tx_flush(). + */ + netfront_destroy_ring ( netfront, &netfront->tx, NULL ); +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int netfront_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct netfront_nic *netfront = netdev->priv; + struct xen_device *xendev = netfront->xendev; + struct netif_tx_request *request; + int notify; + int rc; + + /* Check that we have space in the ring */ + if ( netfront_ring_is_full ( &netfront->tx ) ) { + DBGC ( netfront, "NETFRONT %s out of transmit descriptors\n", + xendev->key ); + return -ENOBUFS; + } + + /* Add to descriptor ring */ + request = RING_GET_REQUEST ( &netfront->tx_fring, + netfront->tx_fring.req_prod_pvt ); + if ( ( rc = netfront_push ( netfront, &netfront->tx, iobuf, + &request->id, &request->gref ) ) != 0 ) { + return rc; + } + request->offset = ( virt_to_phys ( iobuf->data ) & ( PAGE_SIZE - 1 ) ); + request->flags = NETTXF_data_validated; + request->size = iob_len ( iobuf ); + DBGC2 ( netfront, "NETFRONT %s TX id %d ref %d is %#08lx+%zx\n", + xendev->key, request->id, request->gref, + virt_to_phys ( iobuf->data ), iob_len ( iobuf ) ); + + /* Consume descriptor */ + netfront->tx_fring.req_prod_pvt++; + + /* Push new descriptor and notify backend if applicable */ + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY ( &netfront->tx_fring, notify ); + if ( notify ) + netfront_send_event ( netfront ); + + return 0; +} + +/** + * Poll for completed packets + * + * @v netdev Network device + */ +static void netfront_poll_tx ( struct net_device *netdev ) { + struct netfront_nic *netfront = netdev->priv; + struct xen_device *xendev = netfront->xendev; + struct netif_tx_response *response; + struct io_buffer *iobuf; + unsigned int status; + int rc; + + /* Consume any unconsumed responses */ + while ( RING_HAS_UNCONSUMED_RESPONSES ( &netfront->tx_fring ) ) { + + /* Get next response */ + response = RING_GET_RESPONSE ( &netfront->tx_fring, + netfront->tx_fring.rsp_cons++ ); + + /* Retrieve from descriptor ring */ + iobuf = netfront_pull ( netfront, &netfront->tx, response->id ); + status = response->status; + if ( status == NETIF_RSP_OKAY ) { + DBGC2 ( netfront, "NETFRONT %s TX id %d complete\n", + xendev->key, response->id ); + netdev_tx_complete ( netdev, iobuf ); + } else { + rc = -EIO_NETIF_RSP ( status ); + DBGC2 ( netfront, "NETFRONT %s TX id %d error %d: %s\n", + xendev->key, response->id, status, + strerror ( rc ) ); + netdev_tx_complete_err ( netdev, iobuf, rc ); + } + } +} + +/** + * Poll for received packets + * + * @v netdev Network device + */ +static void netfront_poll_rx ( struct net_device *netdev ) { + struct netfront_nic *netfront = netdev->priv; + struct xen_device *xendev = netfront->xendev; + struct netif_rx_response *response; + struct io_buffer *iobuf; + int status; + size_t len; + int rc; + + /* Consume any unconsumed responses */ + while ( RING_HAS_UNCONSUMED_RESPONSES ( &netfront->rx_fring ) ) { + + /* Get next response */ + response = RING_GET_RESPONSE ( &netfront->rx_fring, + netfront->rx_fring.rsp_cons++ ); + + /* Retrieve from descriptor ring */ + iobuf = netfront_pull ( netfront, &netfront->rx, response->id ); + status = response->status; + if ( status >= 0 ) { + len = status; + iob_reserve ( iobuf, response->offset ); + iob_put ( iobuf, len ); + DBGC2 ( netfront, "NETFRONT %s RX id %d complete " + "%#08lx+%zx\n", xendev->key, response->id, + virt_to_phys ( iobuf->data ), len ); + netdev_rx ( netdev, iobuf ); + } else { + rc = -EIO_NETIF_RSP ( status ); + DBGC2 ( netfront, "NETFRONT %s RX id %d error %d: %s\n", + xendev->key, response->id, status, + strerror ( rc ) ); + netdev_rx_err ( netdev, iobuf, rc ); + } + } +} + +/** + * Poll for completed and received packets + * + * @v netdev Network device + */ +static void netfront_poll ( struct net_device *netdev ) { + + /* Poll for TX completions */ + netfront_poll_tx ( netdev ); + + /* Poll for RX completions */ + netfront_poll_rx ( netdev ); + + /* Refill RX descriptor ring */ + netfront_refill_rx ( netdev ); +} + +/** Network device operations */ +static struct net_device_operations netfront_operations = { + .open = netfront_open, + .close = netfront_close, + .transmit = netfront_transmit, + .poll = netfront_poll, +}; + +/****************************************************************************** + * + * Xen device bus interface + * + ****************************************************************************** + */ + +/** + * Probe Xen device + * + * @v xendev Xen device + * @ret rc Return status code + */ +static int netfront_probe ( struct xen_device *xendev ) { + struct xen_hypervisor *xen = xendev->xen; + struct net_device *netdev; + struct netfront_nic *netfront; + int rc; + + /* Allocate and initialise structure */ + netdev = alloc_etherdev ( sizeof ( *netfront ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &netfront_operations ); + netdev->dev = &xendev->dev; + netfront = netdev->priv; + netfront->xendev = xendev; + DBGC ( netfront, "NETFRONT %s backend=\"%s\" in domain %ld\n", + xendev->key, xendev->backend, xendev->backend_id ); + + /* Allocate grant references and initialise descriptor rings */ + if ( ( rc = xengrant_alloc ( xen, netfront->refs, + NETFRONT_REF_COUNT ) ) != 0 ) { + DBGC ( netfront, "NETFRONT %s could not allocate grant " + "references: %s\n", xendev->key, strerror ( rc ) ); + goto err_grant_alloc; + } + netfront_init_ring ( &netfront->tx, "tx-ring-ref", + netfront->refs[NETFRONT_REF_TX_RING], + NETFRONT_NUM_TX_DESC, netfront->tx_iobufs, + &netfront->refs[NETFRONT_REF_TX_BASE], + netfront->tx_ids ); + netfront_init_ring ( &netfront->rx, "rx-ring-ref", + netfront->refs[NETFRONT_REF_RX_RING], + NETFRONT_NUM_RX_DESC, netfront->rx_iobufs, + &netfront->refs[NETFRONT_REF_RX_BASE], + netfront->rx_ids ); + + /* Fetch MAC address */ + if ( ( rc = netfront_read_mac ( netfront, netdev->hw_addr ) ) != 0 ) + goto err_read_mac; + + /* Reset device. Ignore failures; allow the device to be + * registered so that reset errors can be observed by the user + * when attempting to open the device. + */ + netfront_reset ( netfront ); + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register_netdev; + + /* Set initial link state */ + netdev_link_down ( netdev ); + + xen_set_drvdata ( xendev, netdev ); + return 0; + + unregister_netdev ( netdev ); + err_register_netdev: + err_read_mac: + xengrant_free ( xen, netfront->refs, NETFRONT_REF_COUNT ); + err_grant_alloc: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Remove Xen device + * + * @v xendev Xen device + */ +static void netfront_remove ( struct xen_device *xendev ) { + struct net_device *netdev = xen_get_drvdata ( xendev ); + struct netfront_nic *netfront = netdev->priv; + struct xen_hypervisor *xen = xendev->xen; + + /* Unregister network device */ + unregister_netdev ( netdev ); + + /* Free resources */ + xengrant_free ( xen, netfront->refs, NETFRONT_REF_COUNT ); + + /* Free network device */ + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** Xen netfront driver */ +struct xen_driver netfront_driver __xen_driver = { + .name = "netfront", + .type = "vif", + .probe = netfront_probe, + .remove = netfront_remove, +}; diff --git a/roms/ipxe/src/drivers/net/netfront.h b/roms/ipxe/src/drivers/net/netfront.h new file mode 100644 index 0000000..b3f899f --- /dev/null +++ b/roms/ipxe/src/drivers/net/netfront.h @@ -0,0 +1,153 @@ +#ifndef _NETFRONT_H +#define _NETFRONT_H + +/** @file + * + * Xen netfront driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include + +/** Number of transmit ring entries */ +#define NETFRONT_NUM_TX_DESC 16 + +/** Number of receive ring entries */ +#define NETFRONT_NUM_RX_DESC 8 + +/** Grant reference indices */ +enum netfront_ref_index { + /** Transmit ring grant reference index */ + NETFRONT_REF_TX_RING = 0, + /** Transmit descriptor grant reference base index */ + NETFRONT_REF_TX_BASE, + /** Receive ring grant reference index */ + NETFRONT_REF_RX_RING = ( NETFRONT_REF_TX_BASE + NETFRONT_NUM_TX_DESC ), + /** Receive descriptor grant reference base index */ + NETFRONT_REF_RX_BASE, + /** Total number of grant references required */ + NETFRONT_REF_COUNT = ( NETFRONT_REF_RX_BASE + NETFRONT_NUM_RX_DESC ) +}; + +/** A netfront descriptor ring */ +struct netfront_ring { + /** Shared ring */ + union { + /** Transmit shared ring */ + netif_tx_sring_t *tx; + /** Receive shared ring */ + netif_rx_sring_t *rx; + /** Raw pointer */ + void *raw; + } sring; + /** Shared ring grant reference key */ + const char *ref_key; + /** Shared ring grant reference */ + grant_ref_t ref; + + /** Maximum number of used descriptors */ + size_t count; + /** I/O buffers, indexed by buffer ID */ + struct io_buffer **iobufs; + /** I/O buffer grant references, indexed by buffer ID */ + grant_ref_t *refs; + + /** Buffer ID ring */ + uint8_t *ids; + /** Buffer ID ring producer counter */ + unsigned int id_prod; + /** Buffer ID ring consumer counter */ + unsigned int id_cons; +}; + +/** + * Initialise descriptor ring + * + * @v ring Descriptor ring + * @v ref_key Shared ring grant reference key + * @v ref Shared ring grant reference + * @v count Maxium number of used descriptors + * @v iobufs I/O buffers + * @v refs I/O buffer grant references + * @v ids Buffer IDs + */ +static inline __attribute__ (( always_inline )) void +netfront_init_ring ( struct netfront_ring *ring, const char *ref_key, + grant_ref_t ref, unsigned int count, + struct io_buffer **iobufs, grant_ref_t *refs, + uint8_t *ids ) { + + ring->ref_key = ref_key; + ring->ref = ref; + ring->count = count; + ring->iobufs = iobufs; + ring->refs = refs; + ring->ids = ids; +} + +/** + * Check whether or not descriptor ring is full + * + * @v ring Descriptor ring + * @v is_full Ring is full + */ +static inline __attribute__ (( always_inline )) int +netfront_ring_is_full ( struct netfront_ring *ring ) { + unsigned int fill_level; + + fill_level = ( ring->id_prod - ring->id_cons ); + assert ( fill_level <= ring->count ); + return ( fill_level >= ring->count ); +} + +/** + * Check whether or not descriptor ring is empty + * + * @v ring Descriptor ring + * @v is_empty Ring is empty + */ +static inline __attribute__ (( always_inline )) int +netfront_ring_is_empty ( struct netfront_ring *ring ) { + + return ( ring->id_prod == ring->id_cons ); +} + +/** A netfront NIC */ +struct netfront_nic { + /** Xen device */ + struct xen_device *xendev; + /** Grant references */ + grant_ref_t refs[NETFRONT_REF_COUNT]; + + /** Transmit ring */ + struct netfront_ring tx; + /** Transmit front ring */ + netif_tx_front_ring_t tx_fring; + /** Transmit I/O buffers */ + struct io_buffer *tx_iobufs[NETFRONT_NUM_TX_DESC]; + /** Transmit I/O buffer IDs */ + uint8_t tx_ids[NETFRONT_NUM_TX_DESC]; + + /** Receive ring */ + struct netfront_ring rx; + /** Receive front ring */ + netif_rx_front_ring_t rx_fring; + /** Receive I/O buffers */ + struct io_buffer *rx_iobufs[NETFRONT_NUM_RX_DESC]; + /** Receive I/O buffer IDs */ + uint8_t rx_ids[NETFRONT_NUM_RX_DESC]; + + /** Event channel */ + struct evtchn_send event; +}; + +/** Transmit shared ring field */ +#define tx_sring tx.sring.tx + +/** Receive shared ring field */ +#define rx_sring rx.sring.rx + +#endif /* _NETFRONT_H */ diff --git a/roms/ipxe/src/drivers/net/realtek.c b/roms/ipxe/src/drivers/net/realtek.c index 7964475..0aca8c7 100644 --- a/roms/ipxe/src/drivers/net/realtek.c +++ b/roms/ipxe/src/drivers/net/realtek.c @@ -1119,6 +1119,10 @@ static int realtek_probe ( struct pci_device *pci ) { /* Map registers */ rtl->regs = ioremap ( pci->membase, RTL_BAR_SIZE ); + if ( ! rtl->regs ) { + rc = -ENODEV; + goto err_ioremap; + } /* Reset the NIC */ if ( ( rc = realtek_reset ( rtl ) ) != 0 ) @@ -1177,6 +1181,7 @@ static int realtek_probe ( struct pci_device *pci ) { realtek_reset ( rtl ); err_reset: iounmap ( rtl->regs ); + err_ioremap: netdev_nullify ( netdev ); netdev_put ( netdev ); err_alloc: diff --git a/roms/ipxe/src/drivers/net/skeleton.c b/roms/ipxe/src/drivers/net/skeleton.c index e50b778..365111b 100644 --- a/roms/ipxe/src/drivers/net/skeleton.c +++ b/roms/ipxe/src/drivers/net/skeleton.c @@ -241,6 +241,10 @@ static int skeleton_probe ( struct pci_device *pci ) { /* Map registers */ skel->regs = ioremap ( pci->membase, SKELETON_BAR_SIZE ); + if ( ! skel->regs ) { + rc = -ENODEV; + goto err_ioremap; + } /* Reset the NIC */ if ( ( rc = skeleton_reset ( skel ) ) != 0 ) @@ -269,6 +273,7 @@ static int skeleton_probe ( struct pci_device *pci ) { skeleton_reset ( skel ); err_reset: iounmap ( skel->regs ); + err_ioremap: netdev_nullify ( netdev ); netdev_put ( netdev ); err_alloc: diff --git a/roms/ipxe/src/drivers/net/smc9000.c b/roms/ipxe/src/drivers/net/smc9000.c index 31e418a..c9762d5 100644 --- a/roms/ipxe/src/drivers/net/smc9000.c +++ b/roms/ipxe/src/drivers/net/smc9000.c @@ -882,7 +882,7 @@ static int smc9000_probe ( struct nic *nic, struct isa_device *isa ) { /* is it using AUI or 10BaseT ? */ SMC_SELECT_BANK(nic->ioaddr, 1); - if (inw(nic->ioaddr + CONFIG) & CFG_AUI_SELECT) + if (inw(nic->ioaddr + CFG) & CFG_AUI_SELECT) media = 2; else media = 1; @@ -911,12 +911,12 @@ static int smc9000_probe ( struct nic *nic, struct isa_device *isa ) { /* Select which interface to use */ SMC_SELECT_BANK(nic->ioaddr, 1); if ( media == 1 ) { - _outw( inw( nic->ioaddr + CONFIG ) & ~CFG_AUI_SELECT, - nic->ioaddr + CONFIG ); + _outw( inw( nic->ioaddr + CFG ) & ~CFG_AUI_SELECT, + nic->ioaddr + CFG ); } else if ( media == 2 ) { - _outw( inw( nic->ioaddr + CONFIG ) | CFG_AUI_SELECT, - nic->ioaddr + CONFIG ); + _outw( inw( nic->ioaddr + CFG ) | CFG_AUI_SELECT, + nic->ioaddr + CFG ); } smc_phy_configure(nic->ioaddr); diff --git a/roms/ipxe/src/drivers/net/smc9000.h b/roms/ipxe/src/drivers/net/smc9000.h index 02b1c83..8e655f0 100644 --- a/roms/ipxe/src/drivers/net/smc9000.h +++ b/roms/ipxe/src/drivers/net/smc9000.h @@ -131,7 +131,7 @@ typedef unsigned long int dword; #define RPC_DEFAULT (RPC_ANEG | (RPC_LED_100 << RPC_LSXA_SHFT) | (RPC_LED_FD << RPC_LSXB_SHFT) | RPC_SPEED | RPC_DPLX) /* BANK 1 */ -#define CONFIG 0 +#define CFG 0 #define CFG_AUI_SELECT 0x100 #define BASE 2 #define ADDR0 4 diff --git a/roms/ipxe/src/drivers/net/vmxnet3.c b/roms/ipxe/src/drivers/net/vmxnet3.c index 9401c11..31082bf 100644 --- a/roms/ipxe/src/drivers/net/vmxnet3.c +++ b/roms/ipxe/src/drivers/net/vmxnet3.c @@ -26,6 +26,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include #include #include @@ -39,6 +40,22 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +/** VM command profiler */ +static struct profiler vmxnet3_vm_command_profiler __profiler = + { .name = "vmxnet3.vm_command" }; + +/** VM transmit profiler */ +static struct profiler vmxnet3_vm_tx_profiler __profiler = + { .name = "vmxnet3.vm_tx" }; + +/** VM receive refill profiler */ +static struct profiler vmxnet3_vm_refill_profiler __profiler = + { .name = "vmxnet3.vm_refill" }; + +/** VM event profiler */ +static struct profiler vmxnet3_vm_event_profiler __profiler = + { .name = "vmxnet3.vm_event" }; + /** * Issue command * @@ -48,10 +65,16 @@ FILE_LICENCE ( GPL2_OR_LATER ); */ static inline uint32_t vmxnet3_command ( struct vmxnet3_nic *vmxnet, uint32_t command ) { + uint32_t result; /* Issue command */ + profile_start ( &vmxnet3_vm_command_profiler ); writel ( command, ( vmxnet->vd + VMXNET3_VD_CMD ) ); - return readl ( vmxnet->vd + VMXNET3_VD_CMD ); + result = readl ( vmxnet->vd + VMXNET3_VD_CMD ); + profile_stop ( &vmxnet3_vm_command_profiler ); + profile_exclude ( &vmxnet3_vm_command_profiler ); + + return result; } /** @@ -92,8 +115,11 @@ static int vmxnet3_transmit ( struct net_device *netdev, /* Hand over descriptor to NIC */ wmb(); + profile_start ( &vmxnet3_vm_tx_profiler ); writel ( ( vmxnet->count.tx_prod % VMXNET3_NUM_TX_DESC ), ( vmxnet->pt + VMXNET3_PT_TXPROD ) ); + profile_stop ( &vmxnet3_vm_tx_profiler ); + profile_exclude ( &vmxnet3_vm_tx_profiler ); return 0; } @@ -212,8 +238,11 @@ static void vmxnet3_refill_rx ( struct net_device *netdev ) { /* Hand over any new descriptors to NIC */ if ( vmxnet->count.rx_prod != orig_rx_prod ) { wmb(); + profile_start ( &vmxnet3_vm_refill_profiler ); writel ( ( vmxnet->count.rx_prod % VMXNET3_NUM_RX_DESC ), ( vmxnet->pt + VMXNET3_PT_RXPROD ) ); + profile_stop ( &vmxnet3_vm_refill_profiler ); + profile_exclude ( &vmxnet3_vm_refill_profiler ); } } @@ -331,7 +360,10 @@ static void vmxnet3_poll_events ( struct net_device *netdev ) { events = le32_to_cpu ( vmxnet->dma->shared.ecr ); /* Acknowledge these events */ + profile_start ( &vmxnet3_vm_event_profiler ); writel ( events, ( vmxnet->vd + VMXNET3_VD_ECR ) ); + profile_stop ( &vmxnet3_vm_event_profiler ); + profile_exclude ( &vmxnet3_vm_event_profiler ); /* Check for link state change */ if ( events & VMXNET3_ECR_LINK ) { @@ -602,8 +634,16 @@ static int vmxnet3_probe ( struct pci_device *pci ) { /* Map PCI BARs */ vmxnet->pt = ioremap ( pci_bar_start ( pci, VMXNET3_PT_BAR ), VMXNET3_PT_LEN ); + if ( ! vmxnet->pt ) { + rc = -ENODEV; + goto err_ioremap_pt; + } vmxnet->vd = ioremap ( pci_bar_start ( pci, VMXNET3_VD_BAR ), VMXNET3_VD_LEN ); + if ( ! vmxnet->vd ) { + rc = -ENODEV; + goto err_ioremap_vd; + } /* Version check */ if ( ( rc = vmxnet3_check_version ( vmxnet ) ) != 0 ) @@ -633,7 +673,9 @@ static int vmxnet3_probe ( struct pci_device *pci ) { err_reset: err_check_version: iounmap ( vmxnet->vd ); + err_ioremap_vd: iounmap ( vmxnet->pt ); + err_ioremap_pt: netdev_nullify ( netdev ); netdev_put ( netdev ); err_alloc_etherdev: diff --git a/roms/ipxe/src/hci/commands/ping_cmd.c b/roms/ipxe/src/hci/commands/ping_cmd.c index d514a2a..3480769 100644 --- a/roms/ipxe/src/hci/commands/ping_cmd.c +++ b/roms/ipxe/src/hci/commands/ping_cmd.c @@ -48,6 +48,10 @@ struct ping_options { unsigned int size; /** Timeout (in ms) */ unsigned long timeout; + /** Number of packets to send (or zero for no limit) */ + unsigned int count; + /** Inhibit output */ + int quiet; }; /** "ping" option list */ @@ -56,6 +60,10 @@ static struct option_descriptor ping_opts[] = { struct ping_options, size, parse_integer ), OPTION_DESC ( "timeout", 't', required_argument, struct ping_options, timeout, parse_timeout ), + OPTION_DESC ( "count", 'c', required_argument, + struct ping_options, count, parse_integer ), + OPTION_DESC ( "quiet", 'q', no_argument, + struct ping_options, quiet, parse_flag ), }; /** "ping" command descriptor */ @@ -87,7 +95,8 @@ static int ping_exec ( int argc, char **argv ) { hostname = argv[optind]; /* Ping */ - if ( ( rc = ping ( hostname, opts.timeout, opts.size ) ) != 0 ) + if ( ( rc = ping ( hostname, opts.timeout, opts.size, + opts.count, opts.quiet ) ) != 0 ) return rc; return 0; diff --git a/roms/ipxe/src/hci/editstring.c b/roms/ipxe/src/hci/editstring.c index 8c56d23..5f6f04d 100644 --- a/roms/ipxe/src/hci/editstring.c +++ b/roms/ipxe/src/hci/editstring.c @@ -21,6 +21,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include +#include #include #include @@ -37,6 +38,8 @@ static void insert_character ( struct edit_string *string, unsigned int character ) __nonnull; static void delete_character ( struct edit_string *string ) __nonnull; static void backspace ( struct edit_string *string ) __nonnull; +static void previous_word ( struct edit_string *string ) __nonnull; +static void kill_word ( struct edit_string *string ) __nonnull; static void kill_sol ( struct edit_string *string ) __nonnull; static void kill_eol ( struct edit_string *string ) __nonnull; @@ -111,9 +114,36 @@ static void backspace ( struct edit_string *string ) { } /** + * Move to start of previous word + * + * @v string Editable string + */ +static void previous_word ( struct edit_string *string ) { + while ( string->cursor && + isspace ( string->buf[ string->cursor - 1 ] ) ) { + string->cursor--; + } + while ( string->cursor && + ( ! isspace ( string->buf[ string->cursor - 1 ] ) ) ) { + string->cursor--; + } +} + +/** + * Delete to end of previous word + * + * @v string Editable string + */ +static void kill_word ( struct edit_string *string ) { + size_t old_cursor = string->cursor; + previous_word ( string ); + insert_delete ( string, ( old_cursor - string->cursor ), NULL ); +} + +/** * Delete to start of line * - * @v string Editable string + * @v string Editable string */ static void kill_sol ( struct edit_string *string ) { size_t old_cursor = string->cursor; @@ -181,6 +211,10 @@ int edit_string ( struct edit_string *string, int key ) { /* Delete character */ delete_character ( string ); break; + case CTRL_W: + /* Delete word */ + kill_word ( string ); + break; case CTRL_U: /* Delete to start of line */ kill_sol ( string ); diff --git a/roms/ipxe/src/hci/readline.c b/roms/ipxe/src/hci/readline.c index d67980b..40aa597 100644 --- a/roms/ipxe/src/hci/readline.c +++ b/roms/ipxe/src/hci/readline.c @@ -266,6 +266,9 @@ int readline_history ( const char *prompt, const char *prefill, if ( prompt ) printf ( "%s", prompt ); + /* Ensure cursor is visible */ + printf ( "\033[?25h" ); + /* Initialise editable string */ memset ( &string, 0, sizeof ( string ) ); init_editstring ( &string, buf, sizeof ( buf ) ); diff --git a/roms/ipxe/src/image/efi_image.c b/roms/ipxe/src/image/efi_image.c index 5de915b..b7d8f9c 100644 --- a/roms/ipxe/src/image/efi_image.c +++ b/roms/ipxe/src/image/efi_image.c @@ -26,8 +26,9 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include -#include +#include #include +#include #include #include #include @@ -51,10 +52,6 @@ FEATURE ( FEATURE_IMAGE, "EFI", DHCP_EB_FEATURE_EFI, 1 ); "Could not start image" ) #define EEFI_START( efirc ) EPLATFORM ( EINFO_EEFI_START, efirc ) -/** EFI loaded image protocol GUID */ -static EFI_GUID efi_loaded_image_protocol_guid = - EFI_LOADED_IMAGE_PROTOCOL_GUID; - /** * Create device path for image * @@ -156,21 +153,21 @@ static int efi_image_exec ( struct image *image ) { } /* Install file I/O protocols */ - if ( ( rc = efi_file_install ( &snpdev->handle ) ) != 0 ) { + if ( ( rc = efi_file_install ( snpdev->handle ) ) != 0 ) { DBGC ( image, "EFIIMAGE %p could not install file protocol: " "%s\n", image, strerror ( rc ) ); goto err_file_install; } /* Install iPXE download protocol */ - if ( ( rc = efi_download_install ( &snpdev->handle ) ) != 0 ) { + if ( ( rc = efi_download_install ( snpdev->handle ) ) != 0 ) { DBGC ( image, "EFIIMAGE %p could not install iPXE download " "protocol: %s\n", image, strerror ( rc ) ); goto err_download_install; } /* Create device path for image */ - path = efi_image_path ( image, &snpdev->path ); + path = efi_image_path ( image, snpdev->path ); if ( ! path ) { DBGC ( image, "EFIIMAGE %p could not create device path\n", image ); @@ -208,6 +205,13 @@ static int efi_image_exec ( struct image *image ) { goto err_open_protocol; } + /* Some EFI 1.10 implementations seem not to fill in DeviceHandle */ + if ( loaded.image->DeviceHandle == NULL ) { + DBGC ( image, "EFIIMAGE %p filling in missing DeviceHandle\n", + image ); + loaded.image->DeviceHandle = snpdev->handle; + } + /* Sanity checks */ assert ( loaded.image->ParentHandle == efi_image_handle ); assert ( loaded.image->DeviceHandle == snpdev->handle ); @@ -222,11 +226,14 @@ static int efi_image_exec ( struct image *image ) { /* Release network devices for use via SNP */ efi_snp_release(); + /* Wrap calls made by the loaded image (for debugging) */ + efi_wrap ( handle ); + /* Start the image */ if ( ( efirc = bs->StartImage ( handle, NULL, NULL ) ) != 0 ) { rc = -EEFI_START ( efirc ); - DBGC ( image, "EFIIMAGE %p returned with status %s\n", - image, strerror ( rc ) ); + DBGC ( image, "EFIIMAGE %p could not start (or returned with " + "error): %s\n", image, strerror ( rc ) ); goto err_start_image; } @@ -236,14 +243,22 @@ static int efi_image_exec ( struct image *image ) { err_start_image: efi_snp_claim(); err_open_protocol: - /* Unload the image. We can't leave it loaded, because we - * have no "unload" operation. + /* If there was no error, then the image must have been + * started and returned successfully. It either unloaded + * itself, or it intended to remain loaded (e.g. it was a + * driver). We therefore do not unload successful images. + * + * If there was an error, attempt to unload the image. This + * may not work. In particular, there is no way to tell + * whether an error returned from StartImage() was due to + * being unable to start the image (in which case we probably + * should call UnloadImage()), or due to the image itself + * returning an error (in which case we probably should not + * call UnloadImage()). We therefore ignore any failures from + * the UnloadImage() call itself. */ - if ( ( efirc = bs->UnloadImage ( handle ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( image, "EFIIMAGE %p could not unload: %s\n", - image, strerror ( rc ) ); - } + if ( rc != 0 ) + bs->UnloadImage ( handle ); err_load_image: free ( cmdline ); err_cmdline: @@ -265,12 +280,17 @@ static int efi_image_exec ( struct image *image ) { */ static int efi_image_probe ( struct image *image ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + static EFI_DEVICE_PATH_PROTOCOL empty_path = { + .Type = END_DEVICE_PATH_TYPE, + .SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE, + .Length[0] = sizeof ( empty_path ), + }; EFI_HANDLE handle; EFI_STATUS efirc; int rc; /* Attempt loading image */ - if ( ( efirc = bs->LoadImage ( FALSE, efi_image_handle, NULL, + if ( ( efirc = bs->LoadImage ( FALSE, efi_image_handle, &empty_path, user_to_virt ( image->data, 0 ), image->len, &handle ) ) != 0 ) { /* Not an EFI image */ diff --git a/roms/ipxe/src/include/assert.h b/roms/ipxe/src/include/assert.h index 655cbdc..a33f601 100644 --- a/roms/ipxe/src/include/assert.h +++ b/roms/ipxe/src/include/assert.h @@ -20,6 +20,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); extern unsigned int assertion_failures; +#define ASSERTED ( ASSERTING && ( assertion_failures != 0 ) ) + /** printf() for assertions * * This function exists so that the assert() macro can expand to diff --git a/roms/ipxe/src/include/ipxe/device.h b/roms/ipxe/src/include/ipxe/device.h index c59697c..7202a69 100644 --- a/roms/ipxe/src/include/ipxe/device.h +++ b/roms/ipxe/src/include/ipxe/device.h @@ -57,6 +57,12 @@ struct device_description { /** TAP bus type */ #define BUS_TYPE_TAP 6 +/** EFI bus type */ +#define BUS_TYPE_EFI 7 + +/** Xen bus type */ +#define BUS_TYPE_XEN 8 + /** A hardware device */ struct device { /** Name */ diff --git a/roms/ipxe/src/include/ipxe/efi/Base.h b/roms/ipxe/src/include/ipxe/efi/Base.h index 2fb4ec6..844f428 100644 --- a/roms/ipxe/src/include/ipxe/efi/Base.h +++ b/roms/ipxe/src/include/ipxe/efi/Base.h @@ -6,7 +6,7 @@ environment. There are a set of base libraries in the Mde Package that can be used to implement base modules. -Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.
    +Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.
    Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
    This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License @@ -81,21 +81,20 @@ VERIFY_SIZE_OF (CHAR16, 2); #endif // -// For symbol name in GNU assembly code, an extra "_" is necessary +// For symbol name in assembly code, an extra "_" is sometimes necessary // -#if defined(__GNUC__) - /// - /// Private worker functions for ASM_PFX() - /// - #define _CONCATENATE(a, b) __CONCATENATE(a, b) - #define __CONCATENATE(a, b) a ## b - /// - /// The __USER_LABEL_PREFIX__ macro predefined by GNUC represents the prefix - /// on symbols in assembly language. - /// - #define ASM_PFX(name) _CONCATENATE (__USER_LABEL_PREFIX__, name) -#endif +/// +/// Private worker functions for ASM_PFX() +/// +#define _CONCATENATE(a, b) __CONCATENATE(a, b) +#define __CONCATENATE(a, b) a ## b + +/// +/// The __USER_LABEL_PREFIX__ macro predefined by GNUC represents the prefix +/// on symbols in assembly language. +/// +#define ASM_PFX(name) _CONCATENATE (__USER_LABEL_PREFIX__, name) #if __APPLE__ // @@ -208,6 +207,17 @@ struct _LIST_ENTRY { /// #define NULL ((VOID *) 0) +/// +/// Maximum values for common UEFI Data Types +/// +#define MAX_INT8 ((INT8)0x7F) +#define MAX_UINT8 ((UINT8)0xFF) +#define MAX_INT16 ((INT16)0x7FFF) +#define MAX_UINT16 ((UINT16)0xFFFF) +#define MAX_INT32 ((INT32)0x7FFFFFFF) +#define MAX_UINT32 ((UINT32)0xFFFFFFFF) +#define MAX_INT64 ((INT64)0x7FFFFFFFFFFFFFFFULL) +#define MAX_UINT64 ((UINT64)0xFFFFFFFFFFFFFFFFULL) #define BIT0 0x00000001 #define BIT1 0x00000002 diff --git a/roms/ipxe/src/include/ipxe/efi/Ia32/ProcessorBind.h b/roms/ipxe/src/include/ipxe/efi/Ia32/ProcessorBind.h index 89bce6f..16e30b3 100644 --- a/roms/ipxe/src/include/ipxe/efi/Ia32/ProcessorBind.h +++ b/roms/ipxe/src/include/ipxe/efi/Ia32/ProcessorBind.h @@ -1,7 +1,7 @@ /** @file Processor or Compiler specific defines and types for IA-32 architecture. -Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.
    +Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.
    This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License that accompanies this distribution. The full text of the license may be found at @@ -229,6 +229,12 @@ typedef INT32 INTN; #define MAX_ADDRESS 0xFFFFFFFF /// +/// Maximum legal IA-32 INTN and UINTN values. +/// +#define MAX_INTN ((INTN)0x7FFFFFFF) +#define MAX_UINTN ((UINTN)0xFFFFFFFF) + +/// /// The stack alignment required for IA-32. /// #define CPU_STACK_ALIGNMENT sizeof(UINTN) @@ -280,5 +286,9 @@ typedef INT32 INTN; **/ #define FUNCTION_ENTRY_POINT(FunctionPointer) (VOID *)(UINTN)(FunctionPointer) +#ifndef __USER_LABEL_PREFIX__ +#define __USER_LABEL_PREFIX__ _ +#endif + #endif diff --git a/roms/ipxe/src/include/ipxe/efi/IndustryStandard/Acpi10.h b/roms/ipxe/src/include/ipxe/efi/IndustryStandard/Acpi10.h new file mode 100644 index 0000000..7857047 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/efi/IndustryStandard/Acpi10.h @@ -0,0 +1,663 @@ +/** @file + ACPI 1.0b definitions from the ACPI Specification, revision 1.0b + +Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
    +This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +**/ + +#ifndef _ACPI_1_0_H_ +#define _ACPI_1_0_H_ + +FILE_LICENCE ( BSD3 ); + +#include + +/// +/// Common table header, this prefaces all ACPI tables, including FACS, but +/// excluding the RSD PTR structure. +/// +typedef struct { + UINT32 Signature; + UINT32 Length; +} EFI_ACPI_COMMON_HEADER; + +#pragma pack(1) +/// +/// The common ACPI description table header. This structure prefaces most ACPI tables. +/// +typedef struct { + UINT32 Signature; + UINT32 Length; + UINT8 Revision; + UINT8 Checksum; + UINT8 OemId[6]; + UINT64 OemTableId; + UINT32 OemRevision; + UINT32 CreatorId; + UINT32 CreatorRevision; +} EFI_ACPI_DESCRIPTION_HEADER; +#pragma pack() + +// +// Define for Desriptor +// +#define ACPI_SMALL_ITEM_FLAG 0x00 +#define ACPI_LARGE_ITEM_FLAG 0x01 + +// +// Small Item Descriptor Name +// +#define ACPI_SMALL_IRQ_DESCRIPTOR_NAME 0x04 +#define ACPI_SMALL_DMA_DESCRIPTOR_NAME 0x05 +#define ACPI_SMALL_START_DEPENDENT_DESCRIPTOR_NAME 0x06 +#define ACPI_SMALL_END_DEPENDENT_DESCRIPTOR_NAME 0x07 +#define ACPI_SMALL_IO_PORT_DESCRIPTOR_NAME 0x08 +#define ACPI_SMALL_FIXED_IO_PORT_DESCRIPTOR_NAME 0x09 +#define ACPI_SMALL_VENDOR_DEFINED_DESCRIPTOR_NAME 0x0E +#define ACPI_SMALL_END_TAG_DESCRIPTOR_NAME 0x0F + +// +// Large Item Descriptor Name +// +#define ACPI_LARGE_24_BIT_MEMORY_RANGE_DESCRIPTOR_NAME 0x01 +#define ACPI_LARGE_VENDOR_DEFINED_DESCRIPTOR_NAME 0x04 +#define ACPI_LARGE_32_BIT_MEMORY_RANGE_DESCRIPTOR_NAME 0x05 +#define ACPI_LARGE_32_BIT_FIXED_MEMORY_RANGE_DESCRIPTOR_NAME 0x06 +#define ACPI_LARGE_DWORD_ADDRESS_SPACE_DESCRIPTOR_NAME 0x07 +#define ACPI_LARGE_WORD_ADDRESS_SPACE_DESCRIPTOR_NAME 0x08 +#define ACPI_LARGE_EXTENDED_IRQ_DESCRIPTOR_NAME 0x09 +#define ACPI_LARGE_QWORD_ADDRESS_SPACE_DESCRIPTOR_NAME 0x0A + +// +// Small Item Descriptor Value +// +#define ACPI_IRQ_NOFLAG_DESCRIPTOR 0x22 +#define ACPI_IRQ_DESCRIPTOR 0x23 +#define ACPI_DMA_DESCRIPTOR 0x2A +#define ACPI_START_DEPENDENT_DESCRIPTOR 0x30 +#define ACPI_START_DEPENDENT_EX_DESCRIPTOR 0x31 +#define ACPI_END_DEPENDENT_DESCRIPTOR 0x38 +#define ACPI_IO_PORT_DESCRIPTOR 0x47 +#define ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR 0x4B +#define ACPI_END_TAG_DESCRIPTOR 0x79 + +// +// Large Item Descriptor Value +// +#define ACPI_24_BIT_MEMORY_RANGE_DESCRIPTOR 0x81 +#define ACPI_32_BIT_MEMORY_RANGE_DESCRIPTOR 0x85 +#define ACPI_32_BIT_FIXED_MEMORY_RANGE_DESCRIPTOR 0x86 +#define ACPI_DWORD_ADDRESS_SPACE_DESCRIPTOR 0x87 +#define ACPI_WORD_ADDRESS_SPACE_DESCRIPTOR 0x88 +#define ACPI_EXTENDED_INTERRUPT_DESCRIPTOR 0x89 +#define ACPI_QWORD_ADDRESS_SPACE_DESCRIPTOR 0x8A +#define ACPI_ADDRESS_SPACE_DESCRIPTOR 0x8A + +// +// Resource Type +// +#define ACPI_ADDRESS_SPACE_TYPE_MEM 0x00 +#define ACPI_ADDRESS_SPACE_TYPE_IO 0x01 +#define ACPI_ADDRESS_SPACE_TYPE_BUS 0x02 + +/// +/// Power Management Timer frequency is fixed at 3.579545MHz. +/// +#define ACPI_TIMER_FREQUENCY 3579545 + +// +// Ensure proper structure formats +// +#pragma pack(1) + +/// +/// The commond definition of QWORD, DWORD, and WORD +/// Address Space Descriptors. +/// +typedef PACKED struct { + UINT8 Desc; + UINT16 Len; + UINT8 ResType; + UINT8 GenFlag; + UINT8 SpecificFlag; + UINT64 AddrSpaceGranularity; + UINT64 AddrRangeMin; + UINT64 AddrRangeMax; + UINT64 AddrTranslationOffset; + UINT64 AddrLen; +} EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR; + +typedef PACKED union { + UINT8 Byte; + PACKED struct { + UINT8 Length : 3; + UINT8 Name : 4; + UINT8 Type : 1; + } Bits; +} ACPI_SMALL_RESOURCE_HEADER; + +typedef PACKED struct { + PACKED union { + UINT8 Byte; + PACKED struct { + UINT8 Name : 7; + UINT8 Type : 1; + }Bits; + } Header; + UINT16 Length; +} ACPI_LARGE_RESOURCE_HEADER; + +/// +/// IRQ Descriptor. +/// +typedef PACKED struct { + ACPI_SMALL_RESOURCE_HEADER Header; + UINT16 Mask; +} EFI_ACPI_IRQ_NOFLAG_DESCRIPTOR; + +/// +/// IRQ Descriptor. +/// +typedef PACKED struct { + ACPI_SMALL_RESOURCE_HEADER Header; + UINT16 Mask; + UINT8 Information; +} EFI_ACPI_IRQ_DESCRIPTOR; + +/// +/// DMA Descriptor. +/// +typedef PACKED struct { + ACPI_SMALL_RESOURCE_HEADER Header; + UINT8 ChannelMask; + UINT8 Information; +} EFI_ACPI_DMA_DESCRIPTOR; + +/// +/// I/O Port Descriptor +/// +typedef PACKED struct { + ACPI_SMALL_RESOURCE_HEADER Header; + UINT8 Information; + UINT16 BaseAddressMin; + UINT16 BaseAddressMax; + UINT8 Alignment; + UINT8 Length; +} EFI_ACPI_IO_PORT_DESCRIPTOR; + +/// +/// Fixed Location I/O Port Descriptor. +/// +typedef PACKED struct { + ACPI_SMALL_RESOURCE_HEADER Header; + UINT16 BaseAddress; + UINT8 Length; +} EFI_ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR; + +/// +/// 24-Bit Memory Range Descriptor +/// +typedef PACKED struct { + ACPI_LARGE_RESOURCE_HEADER Header; + UINT8 Information; + UINT16 BaseAddressMin; + UINT16 BaseAddressMax; + UINT16 Alignment; + UINT16 Length; +} EFI_ACPI_24_BIT_MEMORY_RANGE_DESCRIPTOR; + +/// +/// 32-Bit Memory Range Descriptor +/// +typedef PACKED struct { + ACPI_LARGE_RESOURCE_HEADER Header; + UINT8 Information; + UINT32 BaseAddressMin; + UINT32 BaseAddressMax; + UINT32 Alignment; + UINT32 Length; +} EFI_ACPI_32_BIT_MEMORY_RANGE_DESCRIPTOR; + +/// +/// Fixed 32-Bit Fixed Memory Range Descriptor +/// +typedef PACKED struct { + ACPI_LARGE_RESOURCE_HEADER Header; + UINT8 Information; + UINT32 BaseAddress; + UINT32 Length; +} EFI_ACPI_32_BIT_FIXED_MEMORY_RANGE_DESCRIPTOR; + +/// +/// QWORD Address Space Descriptor +/// +typedef PACKED struct { + ACPI_LARGE_RESOURCE_HEADER Header; + UINT8 ResType; + UINT8 GenFlag; + UINT8 SpecificFlag; + UINT64 AddrSpaceGranularity; + UINT64 AddrRangeMin; + UINT64 AddrRangeMax; + UINT64 AddrTranslationOffset; + UINT64 AddrLen; +} EFI_ACPI_QWORD_ADDRESS_SPACE_DESCRIPTOR; + +/// +/// DWORD Address Space Descriptor +/// +typedef PACKED struct { + ACPI_LARGE_RESOURCE_HEADER Header; + UINT8 ResType; + UINT8 GenFlag; + UINT8 SpecificFlag; + UINT32 AddrSpaceGranularity; + UINT32 AddrRangeMin; + UINT32 AddrRangeMax; + UINT32 AddrTranslationOffset; + UINT32 AddrLen; +} EFI_ACPI_DWORD_ADDRESS_SPACE_DESCRIPTOR; + +/// +/// WORD Address Space Descriptor +/// +typedef PACKED struct { + ACPI_LARGE_RESOURCE_HEADER Header; + UINT8 ResType; + UINT8 GenFlag; + UINT8 SpecificFlag; + UINT16 AddrSpaceGranularity; + UINT16 AddrRangeMin; + UINT16 AddrRangeMax; + UINT16 AddrTranslationOffset; + UINT16 AddrLen; +} EFI_ACPI_WORD_ADDRESS_SPACE_DESCRIPTOR; + +/// +/// Extended Interrupt Descriptor +/// +typedef PACKED struct { + ACPI_LARGE_RESOURCE_HEADER Header; + UINT8 InterruptVectorFlags; + UINT8 InterruptTableLength; + UINT32 InterruptNumber[1]; +} EFI_ACPI_EXTENDED_INTERRUPT_DESCRIPTOR; + +#pragma pack() + +/// +/// The End tag identifies an end of resource data. +/// +typedef struct { + UINT8 Desc; + UINT8 Checksum; +} EFI_ACPI_END_TAG_DESCRIPTOR; + +// +// General use definitions +// +#define EFI_ACPI_RESERVED_BYTE 0x00 +#define EFI_ACPI_RESERVED_WORD 0x0000 +#define EFI_ACPI_RESERVED_DWORD 0x00000000 +#define EFI_ACPI_RESERVED_QWORD 0x0000000000000000 + +// +// Resource Type Specific Flags +// Ref ACPI specification 6.4.3.5.5 +// +// Bit [0] : Write Status, _RW +// +#define EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_READ_WRITE (1 << 0) +#define EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_READ_ONLY (0 << 0) +// +// Bit [2:1] : Memory Attributes, _MEM +// +#define EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_NON_CACHEABLE (0 << 1) +#define EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE (1 << 1) +#define EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_WRITE_COMBINING (2 << 1) +#define EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE (3 << 1) +// +// Bit [4:3] : Memory Attributes, _MTP +// +#define EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_ADDRESS_RANGE_MEMORY (0 << 3) +#define EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_ADDRESS_RANGE_RESERVED (1 << 3) +#define EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_ADDRESS_RANGE_ACPI (2 << 3) +#define EFI_APCI_MEMORY_RESOURCE_SPECIFIC_FLAG_ADDRESS_RANGE_NVS (3 << 3) +// +// Bit [5] : Memory to I/O Translation, _TTP +// +#define EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_TYPE_TRANSLATION (1 << 5) +#define EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_TYPE_STATIC (0 << 5) + +// +// IRQ Information +// Ref ACPI specification 6.4.2.1 +// +#define EFI_ACPI_IRQ_SHARABLE_MASK 0x10 +#define EFI_ACPI_IRQ_SHARABLE 0x10 + +#define EFI_ACPI_IRQ_POLARITY_MASK 0x08 +#define EFI_ACPI_IRQ_HIGH_TRUE 0x00 +#define EFI_ACPI_IRQ_LOW_FALSE 0x08 + +#define EFI_ACPI_IRQ_MODE 0x01 +#define EFI_ACPI_IRQ_LEVEL_TRIGGERED 0x00 +#define EFI_ACPI_IRQ_EDGE_TRIGGERED 0x01 + +// +// DMA Information +// Ref ACPI specification 6.4.2.2 +// +#define EFI_ACPI_DMA_SPEED_TYPE_MASK 0x60 +#define EFI_ACPI_DMA_SPEED_TYPE_COMPATIBILITY 0x00 +#define EFI_ACPI_DMA_SPEED_TYPE_A 0x20 +#define EFI_ACPI_DMA_SPEED_TYPE_B 0x40 +#define EFI_ACPI_DMA_SPEED_TYPE_F 0x60 + +#define EFI_ACPI_DMA_BUS_MASTER_MASK 0x04 +#define EFI_ACPI_DMA_BUS_MASTER 0x04 + +#define EFI_ACPI_DMA_TRANSFER_TYPE_MASK 0x03 +#define EFI_ACPI_DMA_TRANSFER_TYPE_8_BIT 0x00 +#define EFI_ACPI_DMA_TRANSFER_TYPE_8_BIT_AND_16_BIT 0x01 +#define EFI_ACPI_DMA_TRANSFER_TYPE_16_BIT 0x10 + +// +// IO Information +// Ref ACPI specification 6.4.2.5 +// +#define EFI_ACPI_IO_DECODE_MASK 0x01 +#define EFI_ACPI_IO_DECODE_16_BIT 0x01 +#define EFI_ACPI_IO_DECODE_10_BIT 0x00 + +// +// Memory Information +// Ref ACPI specification 6.4.3.4 +// +#define EFI_ACPI_MEMORY_WRITE_STATUS_MASK 0x01 +#define EFI_ACPI_MEMORY_WRITABLE 0x01 +#define EFI_ACPI_MEMORY_NON_WRITABLE 0x00 + +// +// Ensure proper structure formats +// +#pragma pack(1) +// +// ACPI 1.0b table structures +// + +/// +/// Root System Description Pointer Structure. +/// +typedef struct { + UINT64 Signature; + UINT8 Checksum; + UINT8 OemId[6]; + UINT8 Reserved; + UINT32 RsdtAddress; +} EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_POINTER; + +// +// Root System Description Table +// No definition needed as it is a common description table header, the same with +// EFI_ACPI_DESCRIPTION_HEADER, followed by a variable number of UINT32 table pointers. +// + +/// +/// RSDT Revision (as defined in ACPI 1.0b specification). +/// +#define EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_REVISION 0x01 + +/// +/// Fixed ACPI Description Table Structure (FADT). +/// +typedef struct { + EFI_ACPI_DESCRIPTION_HEADER Header; + UINT32 FirmwareCtrl; + UINT32 Dsdt; + UINT8 IntModel; + UINT8 Reserved1; + UINT16 SciInt; + UINT32 SmiCmd; + UINT8 AcpiEnable; + UINT8 AcpiDisable; + UINT8 S4BiosReq; + UINT8 Reserved2; + UINT32 Pm1aEvtBlk; + UINT32 Pm1bEvtBlk; + UINT32 Pm1aCntBlk; + UINT32 Pm1bCntBlk; + UINT32 Pm2CntBlk; + UINT32 PmTmrBlk; + UINT32 Gpe0Blk; + UINT32 Gpe1Blk; + UINT8 Pm1EvtLen; + UINT8 Pm1CntLen; + UINT8 Pm2CntLen; + UINT8 PmTmLen; + UINT8 Gpe0BlkLen; + UINT8 Gpe1BlkLen; + UINT8 Gpe1Base; + UINT8 Reserved3; + UINT16 PLvl2Lat; + UINT16 PLvl3Lat; + UINT16 FlushSize; + UINT16 FlushStride; + UINT8 DutyOffset; + UINT8 DutyWidth; + UINT8 DayAlrm; + UINT8 MonAlrm; + UINT8 Century; + UINT8 Reserved4; + UINT8 Reserved5; + UINT8 Reserved6; + UINT32 Flags; +} EFI_ACPI_1_0_FIXED_ACPI_DESCRIPTION_TABLE; + +/// +/// FADT Version (as defined in ACPI 1.0b specification). +/// +#define EFI_ACPI_1_0_FIXED_ACPI_DESCRIPTION_TABLE_REVISION 0x01 + +#define EFI_ACPI_1_0_INT_MODE_DUAL_PIC 0 +#define EFI_ACPI_1_0_INT_MODE_MULTIPLE_APIC 1 + +// +// Fixed ACPI Description Table Fixed Feature Flags +// All other bits are reserved and must be set to 0. +// +#define EFI_ACPI_1_0_WBINVD BIT0 +#define EFI_ACPI_1_0_WBINVD_FLUSH BIT1 +#define EFI_ACPI_1_0_PROC_C1 BIT2 +#define EFI_ACPI_1_0_P_LVL2_UP BIT3 +#define EFI_ACPI_1_0_PWR_BUTTON BIT4 +#define EFI_ACPI_1_0_SLP_BUTTON BIT5 +#define EFI_ACPI_1_0_FIX_RTC BIT6 +#define EFI_ACPI_1_0_RTC_S4 BIT7 +#define EFI_ACPI_1_0_TMR_VAL_EXT BIT8 +#define EFI_ACPI_1_0_DCK_CAP BIT9 + +/// +/// Firmware ACPI Control Structure. +/// +typedef struct { + UINT32 Signature; + UINT32 Length; + UINT32 HardwareSignature; + UINT32 FirmwareWakingVector; + UINT32 GlobalLock; + UINT32 Flags; + UINT8 Reserved[40]; +} EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE; + +/// +/// Firmware Control Structure Feature Flags. +/// All other bits are reserved and must be set to 0. +/// +#define EFI_ACPI_1_0_S4BIOS_F BIT0 + +/// +/// Multiple APIC Description Table header definition. The rest of the table +/// must be defined in a platform-specific manner. +/// +typedef struct { + EFI_ACPI_DESCRIPTION_HEADER Header; + UINT32 LocalApicAddress; + UINT32 Flags; +} EFI_ACPI_1_0_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER; + +/// +/// MADT Revision (as defined in ACPI 1.0b specification). +/// +#define EFI_ACPI_1_0_MULTIPLE_APIC_DESCRIPTION_TABLE_REVISION 0x01 + +/// +/// Multiple APIC Flags +/// All other bits are reserved and must be set to 0. +/// +#define EFI_ACPI_1_0_PCAT_COMPAT BIT0 + +// +// Multiple APIC Description Table APIC structure types +// All other values between 0x05 an 0xFF are reserved and +// will be ignored by OSPM. +// +#define EFI_ACPI_1_0_PROCESSOR_LOCAL_APIC 0x00 +#define EFI_ACPI_1_0_IO_APIC 0x01 +#define EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE 0x02 +#define EFI_ACPI_1_0_NON_MASKABLE_INTERRUPT_SOURCE 0x03 +#define EFI_ACPI_1_0_LOCAL_APIC_NMI 0x04 + +// +// APIC Structure Definitions +// + +/// +/// Processor Local APIC Structure Definition. +/// +typedef struct { + UINT8 Type; + UINT8 Length; + UINT8 AcpiProcessorId; + UINT8 ApicId; + UINT32 Flags; +} EFI_ACPI_1_0_PROCESSOR_LOCAL_APIC_STRUCTURE; + +/// +/// Local APIC Flags. All other bits are reserved and must be 0. +/// +#define EFI_ACPI_1_0_LOCAL_APIC_ENABLED BIT0 + +/// +/// IO APIC Structure. +/// +typedef struct { + UINT8 Type; + UINT8 Length; + UINT8 IoApicId; + UINT8 Reserved; + UINT32 IoApicAddress; + UINT32 SystemVectorBase; +} EFI_ACPI_1_0_IO_APIC_STRUCTURE; + +/// +/// Interrupt Source Override Structure. +/// +typedef struct { + UINT8 Type; + UINT8 Length; + UINT8 Bus; + UINT8 Source; + UINT32 GlobalSystemInterruptVector; + UINT16 Flags; +} EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE_STRUCTURE; + +/// +/// Non-Maskable Interrupt Source Structure. +/// +typedef struct { + UINT8 Type; + UINT8 Length; + UINT16 Flags; + UINT32 GlobalSystemInterruptVector; +} EFI_ACPI_1_0_NON_MASKABLE_INTERRUPT_SOURCE_STRUCTURE; + +/// +/// Local APIC NMI Structure. +/// +typedef struct { + UINT8 Type; + UINT8 Length; + UINT8 AcpiProcessorId; + UINT16 Flags; + UINT8 LocalApicInti; +} EFI_ACPI_1_0_LOCAL_APIC_NMI_STRUCTURE; + +/// +/// Smart Battery Description Table (SBST) +/// +typedef struct { + EFI_ACPI_DESCRIPTION_HEADER Header; + UINT32 WarningEnergyLevel; + UINT32 LowEnergyLevel; + UINT32 CriticalEnergyLevel; +} EFI_ACPI_1_0_SMART_BATTERY_DESCRIPTION_TABLE; + +// +// Known table signatures +// + +/// +/// "RSD PTR " Root System Description Pointer. +/// +#define EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_POINTER_SIGNATURE SIGNATURE_64('R', 'S', 'D', ' ', 'P', 'T', 'R', ' ') + +/// +/// "APIC" Multiple APIC Description Table. +/// +#define EFI_ACPI_1_0_APIC_SIGNATURE SIGNATURE_32('A', 'P', 'I', 'C') + +/// +/// "DSDT" Differentiated System Description Table. +/// +#define EFI_ACPI_1_0_DIFFERENTIATED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE SIGNATURE_32('D', 'S', 'D', 'T') + +/// +/// "FACS" Firmware ACPI Control Structure. +/// +#define EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE SIGNATURE_32('F', 'A', 'C', 'S') + +/// +/// "FACP" Fixed ACPI Description Table. +/// +#define EFI_ACPI_1_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE SIGNATURE_32('F', 'A', 'C', 'P') + +/// +/// "PSDT" Persistent System Description Table. +/// +#define EFI_ACPI_1_0_PERSISTENT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE SIGNATURE_32('P', 'S', 'D', 'T') + +/// +/// "RSDT" Root System Description Table. +/// +#define EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE SIGNATURE_32('R', 'S', 'D', 'T') + +/// +/// "SBST" Smart Battery Specification Table. +/// +#define EFI_ACPI_1_0_SMART_BATTERY_SPECIFICATION_TABLE_SIGNATURE SIGNATURE_32('S', 'B', 'S', 'T') + +/// +/// "SSDT" Secondary System Description Table. +/// +#define EFI_ACPI_1_0_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE SIGNATURE_32('S', 'S', 'D', 'T') + +#pragma pack() + +#endif diff --git a/roms/ipxe/src/include/ipxe/efi/IndustryStandard/AcpiAml.h b/roms/ipxe/src/include/ipxe/efi/IndustryStandard/AcpiAml.h new file mode 100644 index 0000000..a9186b4 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/efi/IndustryStandard/AcpiAml.h @@ -0,0 +1,177 @@ +/** @file + This file contains AML code definition in the latest ACPI spec. + + Copyright (c) 2011, Intel Corporation. All rights reserved.
    + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _ACPI_AML_H_ +#define _ACPI_AML_H_ + +FILE_LICENCE ( BSD3 ); + +// +// ACPI AML definition +// + +// +// Primary OpCode +// +#define AML_ZERO_OP 0x00 +#define AML_ONE_OP 0x01 +#define AML_ALIAS_OP 0x06 +#define AML_NAME_OP 0x08 +#define AML_BYTE_PREFIX 0x0a +#define AML_WORD_PREFIX 0x0b +#define AML_DWORD_PREFIX 0x0c +#define AML_STRING_PREFIX 0x0d +#define AML_QWORD_PREFIX 0x0e +#define AML_SCOPE_OP 0x10 +#define AML_BUFFER_OP 0x11 +#define AML_PACKAGE_OP 0x12 +#define AML_VAR_PACKAGE_OP 0x13 +#define AML_METHOD_OP 0x14 +#define AML_DUAL_NAME_PREFIX 0x2e +#define AML_MULTI_NAME_PREFIX 0x2f +#define AML_NAME_CHAR_A 0x41 +#define AML_NAME_CHAR_B 0x42 +#define AML_NAME_CHAR_C 0x43 +#define AML_NAME_CHAR_D 0x44 +#define AML_NAME_CHAR_E 0x45 +#define AML_NAME_CHAR_F 0x46 +#define AML_NAME_CHAR_G 0x47 +#define AML_NAME_CHAR_H 0x48 +#define AML_NAME_CHAR_I 0x49 +#define AML_NAME_CHAR_J 0x4a +#define AML_NAME_CHAR_K 0x4b +#define AML_NAME_CHAR_L 0x4c +#define AML_NAME_CHAR_M 0x4d +#define AML_NAME_CHAR_N 0x4e +#define AML_NAME_CHAR_O 0x4f +#define AML_NAME_CHAR_P 0x50 +#define AML_NAME_CHAR_Q 0x51 +#define AML_NAME_CHAR_R 0x52 +#define AML_NAME_CHAR_S 0x53 +#define AML_NAME_CHAR_T 0x54 +#define AML_NAME_CHAR_U 0x55 +#define AML_NAME_CHAR_V 0x56 +#define AML_NAME_CHAR_W 0x57 +#define AML_NAME_CHAR_X 0x58 +#define AML_NAME_CHAR_Y 0x59 +#define AML_NAME_CHAR_Z 0x5a +#define AML_ROOT_CHAR 0x5c +#define AML_PARENT_PREFIX_CHAR 0x5e +#define AML_NAME_CHAR__ 0x5f +#define AML_LOCAL0 0x60 +#define AML_LOCAL1 0x61 +#define AML_LOCAL2 0x62 +#define AML_LOCAL3 0x63 +#define AML_LOCAL4 0x64 +#define AML_LOCAL5 0x65 +#define AML_LOCAL6 0x66 +#define AML_LOCAL7 0x67 +#define AML_ARG0 0x68 +#define AML_ARG1 0x69 +#define AML_ARG2 0x6a +#define AML_ARG3 0x6b +#define AML_ARG4 0x6c +#define AML_ARG5 0x6d +#define AML_ARG6 0x6e +#define AML_STORE_OP 0x70 +#define AML_REF_OF_OP 0x71 +#define AML_ADD_OP 0x72 +#define AML_CONCAT_OP 0x73 +#define AML_SUBTRACT_OP 0x74 +#define AML_INCREMENT_OP 0x75 +#define AML_DECREMENT_OP 0x76 +#define AML_MULTIPLY_OP 0x77 +#define AML_DIVIDE_OP 0x78 +#define AML_SHIFT_LEFT_OP 0x79 +#define AML_SHIFT_RIGHT_OP 0x7a +#define AML_AND_OP 0x7b +#define AML_NAND_OP 0x7c +#define AML_OR_OP 0x7d +#define AML_NOR_OP 0x7e +#define AML_XOR_OP 0x7f +#define AML_NOT_OP 0x80 +#define AML_FIND_SET_LEFT_BIT_OP 0x81 +#define AML_FIND_SET_RIGHT_BIT_OP 0x82 +#define AML_DEREF_OF_OP 0x83 +#define AML_CONCAT_RES_OP 0x84 +#define AML_MOD_OP 0x85 +#define AML_NOTIFY_OP 0x86 +#define AML_SIZE_OF_OP 0x87 +#define AML_INDEX_OP 0x88 +#define AML_MATCH_OP 0x89 +#define AML_CREATE_DWORD_FIELD_OP 0x8a +#define AML_CREATE_WORD_FIELD_OP 0x8b +#define AML_CREATE_BYTE_FIELD_OP 0x8c +#define AML_CREATE_BIT_FIELD_OP 0x8d +#define AML_OBJECT_TYPE_OP 0x8e +#define AML_CREATE_QWORD_FIELD_OP 0x8f +#define AML_LAND_OP 0x90 +#define AML_LOR_OP 0x91 +#define AML_LNOT_OP 0x92 +#define AML_LEQUAL_OP 0x93 +#define AML_LGREATER_OP 0x94 +#define AML_LLESS_OP 0x95 +#define AML_TO_BUFFER_OP 0x96 +#define AML_TO_DEC_STRING_OP 0x97 +#define AML_TO_HEX_STRING_OP 0x98 +#define AML_TO_INTEGER_OP 0x99 +#define AML_TO_STRING_OP 0x9c +#define AML_COPY_OBJECT_OP 0x9d +#define AML_MID_OP 0x9e +#define AML_CONTINUE_OP 0x9f +#define AML_IF_OP 0xa0 +#define AML_ELSE_OP 0xa1 +#define AML_WHILE_OP 0xa2 +#define AML_NOOP_OP 0xa3 +#define AML_RETURN_OP 0xa4 +#define AML_BREAK_OP 0xa5 +#define AML_BREAK_POINT_OP 0xcc +#define AML_ONES_OP 0xff + +// +// Extended OpCode +// +#define AML_EXT_OP 0x5b + +#define AML_EXT_MUTEX_OP 0x01 +#define AML_EXT_EVENT_OP 0x02 +#define AML_EXT_COND_REF_OF_OP 0x12 +#define AML_EXT_CREATE_FIELD_OP 0x13 +#define AML_EXT_LOAD_TABLE_OP 0x1f +#define AML_EXT_LOAD_OP 0x20 +#define AML_EXT_STALL_OP 0x21 +#define AML_EXT_SLEEP_OP 0x22 +#define AML_EXT_ACQUIRE_OP 0x23 +#define AML_EXT_SIGNAL_OP 0x24 +#define AML_EXT_WAIT_OP 0x25 +#define AML_EXT_RESET_OP 0x26 +#define AML_EXT_RELEASE_OP 0x27 +#define AML_EXT_FROM_BCD_OP 0x28 +#define AML_EXT_TO_BCD_OP 0x29 +#define AML_EXT_UNLOAD_OP 0x2a +#define AML_EXT_REVISION_OP 0x30 +#define AML_EXT_DEBUG_OP 0x31 +#define AML_EXT_FATAL_OP 0x32 +#define AML_EXT_TIMER_OP 0x33 +#define AML_EXT_REGION_OP 0x80 +#define AML_EXT_FIELD_OP 0x81 +#define AML_EXT_DEVICE_OP 0x82 +#define AML_EXT_PROCESSOR_OP 0x83 +#define AML_EXT_POWER_RES_OP 0x84 +#define AML_EXT_THERMAL_ZONE_OP 0x85 +#define AML_EXT_INDEX_FIELD_OP 0x86 +#define AML_EXT_BANK_FIELD_OP 0x87 +#define AML_EXT_DATA_REGION_OP 0x88 + +#endif diff --git a/roms/ipxe/src/include/ipxe/efi/IndustryStandard/Pci22.h b/roms/ipxe/src/include/ipxe/efi/IndustryStandard/Pci22.h index 53654ee..a73820f 100644 --- a/roms/ipxe/src/include/ipxe/efi/IndustryStandard/Pci22.h +++ b/roms/ipxe/src/include/ipxe/efi/IndustryStandard/Pci22.h @@ -6,7 +6,10 @@ PCI-to-PCI Bridge Architecture Specification, Revision 1.2 PC Card Standard, 8.0 + + Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.
    + Copyright (c) 2014, Hewlett-Packard Development Company, L.P.
    This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -657,6 +660,42 @@ typedef struct { } EFI_PCI_CAPABILITY_PMI; /// +/// PMC - Power Management Capabilities +/// Section 3.2.3, PCI Power Management Interface Specifiction, Revision 1.2 +/// +typedef union { + struct { + UINT16 Version : 3; + UINT16 PmeClock : 1; + UINT16 : 1; + UINT16 DeviceSpecificInitialization : 1; + UINT16 AuxCurrent : 3; + UINT16 D1Support : 1; + UINT16 D2Support : 1; + UINT16 PmeSupport : 5; + } Bits; + UINT16 Data; +} EFI_PCI_PMC; + +#define EFI_PCI_PMC_D3_COLD_MASK (BIT15) + +/// +/// PMCSR - Power Management Control/Status +/// Section 3.2.4, PCI Power Management Interface Specifiction, Revision 1.2 +/// +typedef union { + struct { + UINT16 PowerState : 2; + UINT16 : 6; + UINT16 PmeEnable : 1; + UINT16 DataSelect : 4; + UINT16 DataScale : 2; + UINT16 PmeStatus : 1; + } Bits; + UINT16 Data; +} EFI_PCI_PMCSR; + +/// /// A.G.P Capability /// Section 6.1.4, Accelerated Graphics Port Interface Specification, Revision 1.0 /// diff --git a/roms/ipxe/src/include/ipxe/efi/IndustryStandard/PeImage.h b/roms/ipxe/src/include/ipxe/efi/IndustryStandard/PeImage.h index fb4c2aa..9499bb7 100644 --- a/roms/ipxe/src/include/ipxe/efi/IndustryStandard/PeImage.h +++ b/roms/ipxe/src/include/ipxe/efi/IndustryStandard/PeImage.h @@ -4,7 +4,7 @@ EFI_IMAGE_NT_HEADERS64 is for PE32+. This file is coded to the Visual Studio, Microsoft Portable Executable and - Common Object File Format Specification, Revision 8.0 - May 16, 2006. + Common Object File Format Specification, Revision 8.3 - February 6, 2013. This file also includes some definitions in PI Specification, Revision 1.0. Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
    @@ -41,6 +41,7 @@ FILE_LICENCE ( BSD3 ); #define IMAGE_FILE_MACHINE_EBC 0x0EBC #define IMAGE_FILE_MACHINE_X64 0x8664 #define IMAGE_FILE_MACHINE_ARMTHUMB_MIXED 0x01c2 +#define IMAGE_FILE_MACHINE_ARM64 0xAA64 // // EXE file formats diff --git a/roms/ipxe/src/include/ipxe/efi/IndustryStandard/Tpm12.h b/roms/ipxe/src/include/ipxe/efi/IndustryStandard/Tpm12.h new file mode 100644 index 0000000..509425c --- /dev/null +++ b/roms/ipxe/src/include/ipxe/efi/IndustryStandard/Tpm12.h @@ -0,0 +1,2175 @@ +/** @file + TPM Specification data structures (TCG TPM Specification Version 1.2 Revision 103) + See http://trustedcomputinggroup.org for latest specification updates + + Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
    + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +**/ + + +#ifndef _TPM12_H_ +#define _TPM12_H_ + +FILE_LICENCE ( BSD3 ); + +/// +/// The start of TPM return codes +/// +#define TPM_BASE 0 + +// +// All structures MUST be packed on a byte boundary. +// + +#pragma pack (1) + +// +// Part 2, section 2.2.3: Helper redefinitions +// +/// +/// Indicates the conditions where it is required that authorization be presented +/// +typedef UINT8 TPM_AUTH_DATA_USAGE; +/// +/// The information as to what the payload is in an encrypted structure +/// +typedef UINT8 TPM_PAYLOAD_TYPE; +/// +/// The version info breakdown +/// +typedef UINT8 TPM_VERSION_BYTE; +/// +/// The state of the dictionary attack mitigation logic +/// +typedef UINT8 TPM_DA_STATE; +/// +/// The request or response authorization type +/// +typedef UINT16 TPM_TAG; +/// +/// The protocol in use +/// +typedef UINT16 TPM_PROTOCOL_ID; +/// +/// Indicates the start state +/// +typedef UINT16 TPM_STARTUP_TYPE; +/// +/// The definition of the encryption scheme +/// +typedef UINT16 TPM_ENC_SCHEME; +/// +/// The definition of the signature scheme +/// +typedef UINT16 TPM_SIG_SCHEME; +/// +/// The definition of the migration scheme +/// +typedef UINT16 TPM_MIGRATE_SCHEME; +/// +/// Sets the state of the physical presence mechanism +/// +typedef UINT16 TPM_PHYSICAL_PRESENCE; +/// +/// Indicates the types of entity that are supported by the TPM +/// +typedef UINT16 TPM_ENTITY_TYPE; +/// +/// Indicates the permitted usage of the key +/// +typedef UINT16 TPM_KEY_USAGE; +/// +/// The type of asymmetric encrypted structure in use by the endorsement key +/// +typedef UINT16 TPM_EK_TYPE; +/// +/// The tag for the structure +/// +typedef UINT16 TPM_STRUCTURE_TAG; +/// +/// The platform specific spec to which the information relates to +/// +typedef UINT16 TPM_PLATFORM_SPECIFIC; +/// +/// The command ordinal +/// +typedef UINT32 TPM_COMMAND_CODE; +/// +/// Identifies a TPM capability area +/// +typedef UINT32 TPM_CAPABILITY_AREA; +/// +/// Indicates information regarding a key +/// +typedef UINT32 TPM_KEY_FLAGS; +/// +/// Indicates the type of algorithm +/// +typedef UINT32 TPM_ALGORITHM_ID; +/// +/// The locality modifier +/// +typedef UINT32 TPM_MODIFIER_INDICATOR; +/// +/// The actual number of a counter +/// +typedef UINT32 TPM_ACTUAL_COUNT; +/// +/// Attributes that define what options are in use for a transport session +/// +typedef UINT32 TPM_TRANSPORT_ATTRIBUTES; +/// +/// Handle to an authorization session +/// +typedef UINT32 TPM_AUTHHANDLE; +/// +/// Index to a DIR register +/// +typedef UINT32 TPM_DIRINDEX; +/// +/// The area where a key is held assigned by the TPM +/// +typedef UINT32 TPM_KEY_HANDLE; +/// +/// Index to a PCR register +/// +typedef UINT32 TPM_PCRINDEX; +/// +/// The return code from a function +/// +typedef UINT32 TPM_RESULT; +/// +/// The types of resources that a TPM may have using internal resources +/// +typedef UINT32 TPM_RESOURCE_TYPE; +/// +/// Allows for controlling of the key when loaded and how to handle TPM_Startup issues +/// +typedef UINT32 TPM_KEY_CONTROL; +/// +/// The index into the NV storage area +/// +typedef UINT32 TPM_NV_INDEX; +/// +/// The family ID. Family IDs are automatically assigned a sequence number by the TPM. +/// A trusted process can set the FamilyID value in an individual row to NULL, which +/// invalidates that row. The family ID resets to NULL on each change of TPM Owner. +/// +typedef UINT32 TPM_FAMILY_ID; +/// +/// IA value used as a label for the most recent verification of this family. Set to zero when not in use. +/// +typedef UINT32 TPM_FAMILY_VERIFICATION; +/// +/// How the TPM handles var +/// +typedef UINT32 TPM_STARTUP_EFFECTS; +/// +/// The mode of a symmetric encryption +/// +typedef UINT32 TPM_SYM_MODE; +/// +/// The family flags +/// +typedef UINT32 TPM_FAMILY_FLAGS; +/// +/// The index value for the delegate NV table +/// +typedef UINT32 TPM_DELEGATE_INDEX; +/// +/// The restrictions placed on delegation of CMK commands +/// +typedef UINT32 TPM_CMK_DELEGATE; +/// +/// The ID value of a monotonic counter +/// +typedef UINT32 TPM_COUNT_ID; +/// +/// A command to execute +/// +typedef UINT32 TPM_REDIT_COMMAND; +/// +/// A transport session handle +/// +typedef UINT32 TPM_TRANSHANDLE; +/// +/// A generic handle could be key, transport etc +/// +typedef UINT32 TPM_HANDLE; +/// +/// What operation is happening +/// +typedef UINT32 TPM_FAMILY_OPERATION; + +// +// Part 2, section 2.2.4: Vendor specific +// The following defines allow for the quick specification of a +// vendor specific item. +// +#define TPM_Vendor_Specific32 ((UINT32) 0x00000400) +#define TPM_Vendor_Specific8 ((UINT8) 0x80) + +// +// Part 2, section 3.1: TPM_STRUCTURE_TAG +// +#define TPM_TAG_CONTEXTBLOB ((TPM_STRUCTURE_TAG) 0x0001) +#define TPM_TAG_CONTEXT_SENSITIVE ((TPM_STRUCTURE_TAG) 0x0002) +#define TPM_TAG_CONTEXTPOINTER ((TPM_STRUCTURE_TAG) 0x0003) +#define TPM_TAG_CONTEXTLIST ((TPM_STRUCTURE_TAG) 0x0004) +#define TPM_TAG_SIGNINFO ((TPM_STRUCTURE_TAG) 0x0005) +#define TPM_TAG_PCR_INFO_LONG ((TPM_STRUCTURE_TAG) 0x0006) +#define TPM_TAG_PERSISTENT_FLAGS ((TPM_STRUCTURE_TAG) 0x0007) +#define TPM_TAG_VOLATILE_FLAGS ((TPM_STRUCTURE_TAG) 0x0008) +#define TPM_TAG_PERSISTENT_DATA ((TPM_STRUCTURE_TAG) 0x0009) +#define TPM_TAG_VOLATILE_DATA ((TPM_STRUCTURE_TAG) 0x000A) +#define TPM_TAG_SV_DATA ((TPM_STRUCTURE_TAG) 0x000B) +#define TPM_TAG_EK_BLOB ((TPM_STRUCTURE_TAG) 0x000C) +#define TPM_TAG_EK_BLOB_AUTH ((TPM_STRUCTURE_TAG) 0x000D) +#define TPM_TAG_COUNTER_VALUE ((TPM_STRUCTURE_TAG) 0x000E) +#define TPM_TAG_TRANSPORT_INTERNAL ((TPM_STRUCTURE_TAG) 0x000F) +#define TPM_TAG_TRANSPORT_LOG_IN ((TPM_STRUCTURE_TAG) 0x0010) +#define TPM_TAG_TRANSPORT_LOG_OUT ((TPM_STRUCTURE_TAG) 0x0011) +#define TPM_TAG_AUDIT_EVENT_IN ((TPM_STRUCTURE_TAG) 0x0012) +#define TPM_TAG_AUDIT_EVENT_OUT ((TPM_STRUCTURE_TAG) 0x0013) +#define TPM_TAG_CURRENT_TICKS ((TPM_STRUCTURE_TAG) 0x0014) +#define TPM_TAG_KEY ((TPM_STRUCTURE_TAG) 0x0015) +#define TPM_TAG_STORED_DATA12 ((TPM_STRUCTURE_TAG) 0x0016) +#define TPM_TAG_NV_ATTRIBUTES ((TPM_STRUCTURE_TAG) 0x0017) +#define TPM_TAG_NV_DATA_PUBLIC ((TPM_STRUCTURE_TAG) 0x0018) +#define TPM_TAG_NV_DATA_SENSITIVE ((TPM_STRUCTURE_TAG) 0x0019) +#define TPM_TAG_DELEGATIONS ((TPM_STRUCTURE_TAG) 0x001A) +#define TPM_TAG_DELEGATE_PUBLIC ((TPM_STRUCTURE_TAG) 0x001B) +#define TPM_TAG_DELEGATE_TABLE_ROW ((TPM_STRUCTURE_TAG) 0x001C) +#define TPM_TAG_TRANSPORT_AUTH ((TPM_STRUCTURE_TAG) 0x001D) +#define TPM_TAG_TRANSPORT_PUBLIC ((TPM_STRUCTURE_TAG) 0x001E) +#define TPM_TAG_PERMANENT_FLAGS ((TPM_STRUCTURE_TAG) 0x001F) +#define TPM_TAG_STCLEAR_FLAGS ((TPM_STRUCTURE_TAG) 0x0020) +#define TPM_TAG_STANY_FLAGS ((TPM_STRUCTURE_TAG) 0x0021) +#define TPM_TAG_PERMANENT_DATA ((TPM_STRUCTURE_TAG) 0x0022) +#define TPM_TAG_STCLEAR_DATA ((TPM_STRUCTURE_TAG) 0x0023) +#define TPM_TAG_STANY_DATA ((TPM_STRUCTURE_TAG) 0x0024) +#define TPM_TAG_FAMILY_TABLE_ENTRY ((TPM_STRUCTURE_TAG) 0x0025) +#define TPM_TAG_DELEGATE_SENSITIVE ((TPM_STRUCTURE_TAG) 0x0026) +#define TPM_TAG_DELG_KEY_BLOB ((TPM_STRUCTURE_TAG) 0x0027) +#define TPM_TAG_KEY12 ((TPM_STRUCTURE_TAG) 0x0028) +#define TPM_TAG_CERTIFY_INFO2 ((TPM_STRUCTURE_TAG) 0x0029) +#define TPM_TAG_DELEGATE_OWNER_BLOB ((TPM_STRUCTURE_TAG) 0x002A) +#define TPM_TAG_EK_BLOB_ACTIVATE ((TPM_STRUCTURE_TAG) 0x002B) +#define TPM_TAG_DAA_BLOB ((TPM_STRUCTURE_TAG) 0x002C) +#define TPM_TAG_DAA_CONTEXT ((TPM_STRUCTURE_TAG) 0x002D) +#define TPM_TAG_DAA_ENFORCE ((TPM_STRUCTURE_TAG) 0x002E) +#define TPM_TAG_DAA_ISSUER ((TPM_STRUCTURE_TAG) 0x002F) +#define TPM_TAG_CAP_VERSION_INFO ((TPM_STRUCTURE_TAG) 0x0030) +#define TPM_TAG_DAA_SENSITIVE ((TPM_STRUCTURE_TAG) 0x0031) +#define TPM_TAG_DAA_TPM ((TPM_STRUCTURE_TAG) 0x0032) +#define TPM_TAG_CMK_MIGAUTH ((TPM_STRUCTURE_TAG) 0x0033) +#define TPM_TAG_CMK_SIGTICKET ((TPM_STRUCTURE_TAG) 0x0034) +#define TPM_TAG_CMK_MA_APPROVAL ((TPM_STRUCTURE_TAG) 0x0035) +#define TPM_TAG_QUOTE_INFO2 ((TPM_STRUCTURE_TAG) 0x0036) +#define TPM_TAG_DA_INFO ((TPM_STRUCTURE_TAG) 0x0037) +#define TPM_TAG_DA_LIMITED ((TPM_STRUCTURE_TAG) 0x0038) +#define TPM_TAG_DA_ACTION_TYPE ((TPM_STRUCTURE_TAG) 0x0039) + +// +// Part 2, section 4: TPM Types +// + +// +// Part 2, section 4.1: TPM_RESOURCE_TYPE +// +#define TPM_RT_KEY ((TPM_RESOURCE_TYPE) 0x00000001) ///< The handle is a key handle and is the result of a LoadKey type operation +#define TPM_RT_AUTH ((TPM_RESOURCE_TYPE) 0x00000002) ///< The handle is an authorization handle. Auth handles come from TPM_OIAP, TPM_OSAP and TPM_DSAP +#define TPM_RT_HASH ((TPM_RESOURCE_TYPE) 0x00000003) ///< Reserved for hashes +#define TPM_RT_TRANS ((TPM_RESOURCE_TYPE) 0x00000004) ///< The handle is for a transport session. Transport handles come from TPM_EstablishTransport +#define TPM_RT_CONTEXT ((TPM_RESOURCE_TYPE) 0x00000005) ///< Resource wrapped and held outside the TPM using the context save/restore commands +#define TPM_RT_COUNTER ((TPM_RESOURCE_TYPE) 0x00000006) ///< Reserved for counters +#define TPM_RT_DELEGATE ((TPM_RESOURCE_TYPE) 0x00000007) ///< The handle is for a delegate row. These are the internal rows held in NV storage by the TPM +#define TPM_RT_DAA_TPM ((TPM_RESOURCE_TYPE) 0x00000008) ///< The value is a DAA TPM specific blob +#define TPM_RT_DAA_V0 ((TPM_RESOURCE_TYPE) 0x00000009) ///< The value is a DAA V0 parameter +#define TPM_RT_DAA_V1 ((TPM_RESOURCE_TYPE) 0x0000000A) ///< The value is a DAA V1 parameter + +// +// Part 2, section 4.2: TPM_PAYLOAD_TYPE +// +#define TPM_PT_ASYM ((TPM_PAYLOAD_TYPE) 0x01) ///< The entity is an asymmetric key +#define TPM_PT_BIND ((TPM_PAYLOAD_TYPE) 0x02) ///< The entity is bound data +#define TPM_PT_MIGRATE ((TPM_PAYLOAD_TYPE) 0x03) ///< The entity is a migration blob +#define TPM_PT_MAINT ((TPM_PAYLOAD_TYPE) 0x04) ///< The entity is a maintenance blob +#define TPM_PT_SEAL ((TPM_PAYLOAD_TYPE) 0x05) ///< The entity is sealed data +#define TPM_PT_MIGRATE_RESTRICTED ((TPM_PAYLOAD_TYPE) 0x06) ///< The entity is a restricted-migration asymmetric key +#define TPM_PT_MIGRATE_EXTERNAL ((TPM_PAYLOAD_TYPE) 0x07) ///< The entity is a external migratable key +#define TPM_PT_CMK_MIGRATE ((TPM_PAYLOAD_TYPE) 0x08) ///< The entity is a CMK migratable blob +#define TPM_PT_VENDOR_SPECIFIC ((TPM_PAYLOAD_TYPE) 0x80) ///< 0x80 - 0xFF Vendor specific payloads + +// +// Part 2, section 4.3: TPM_ENTITY_TYPE +// +#define TPM_ET_KEYHANDLE ((UINT16) 0x0001) ///< The entity is a keyHandle or key +#define TPM_ET_OWNER ((UINT16) 0x0002) ///< The entity is the TPM Owner +#define TPM_ET_DATA ((UINT16) 0x0003) ///< The entity is some data +#define TPM_ET_SRK ((UINT16) 0x0004) ///< The entity is the SRK +#define TPM_ET_KEY ((UINT16) 0x0005) ///< The entity is a key or keyHandle +#define TPM_ET_REVOKE ((UINT16) 0x0006) ///< The entity is the RevokeTrust value +#define TPM_ET_DEL_OWNER_BLOB ((UINT16) 0x0007) ///< The entity is a delegate owner blob +#define TPM_ET_DEL_ROW ((UINT16) 0x0008) ///< The entity is a delegate row +#define TPM_ET_DEL_KEY_BLOB ((UINT16) 0x0009) ///< The entity is a delegate key blob +#define TPM_ET_COUNTER ((UINT16) 0x000A) ///< The entity is a counter +#define TPM_ET_NV ((UINT16) 0x000B) ///< The entity is a NV index +#define TPM_ET_OPERATOR ((UINT16) 0x000C) ///< The entity is the operator +#define TPM_ET_RESERVED_HANDLE ((UINT16) 0x0040) ///< Reserved. This value avoids collisions with the handle MSB setting. +// +// TPM_ENTITY_TYPE MSB Values: The MSB is used to indicate the ADIP encryption sheme when applicable +// +#define TPM_ET_XOR ((UINT16) 0x0000) ///< ADIP encryption scheme: XOR +#define TPM_ET_AES128 ((UINT16) 0x0006) ///< ADIP encryption scheme: AES 128 bits + +// +// Part 2, section 4.4.1: Reserved Key Handles +// +#define TPM_KH_SRK ((TPM_KEY_HANDLE) 0x40000000) ///< The handle points to the SRK +#define TPM_KH_OWNER ((TPM_KEY_HANDLE) 0x40000001) ///< The handle points to the TPM Owner +#define TPM_KH_REVOKE ((TPM_KEY_HANDLE) 0x40000002) ///< The handle points to the RevokeTrust value +#define TPM_KH_TRANSPORT ((TPM_KEY_HANDLE) 0x40000003) ///< The handle points to the EstablishTransport static authorization +#define TPM_KH_OPERATOR ((TPM_KEY_HANDLE) 0x40000004) ///< The handle points to the Operator auth +#define TPM_KH_ADMIN ((TPM_KEY_HANDLE) 0x40000005) ///< The handle points to the delegation administration auth +#define TPM_KH_EK ((TPM_KEY_HANDLE) 0x40000006) ///< The handle points to the PUBEK, only usable with TPM_OwnerReadInternalPub + +// +// Part 2, section 4.5: TPM_STARTUP_TYPE +// +#define TPM_ST_CLEAR ((TPM_STARTUP_TYPE) 0x0001) ///< The TPM is starting up from a clean state +#define TPM_ST_STATE ((TPM_STARTUP_TYPE) 0x0002) ///< The TPM is starting up from a saved state +#define TPM_ST_DEACTIVATED ((TPM_STARTUP_TYPE) 0x0003) ///< The TPM is to startup and set the deactivated flag to TRUE + +// +// Part 2, section 4.6: TPM_STATUP_EFFECTS +// The table makeup is still an open issue. +// + +// +// Part 2, section 4.7: TPM_PROTOCOL_ID +// +#define TPM_PID_OIAP ((TPM_PROTOCOL_ID) 0x0001) ///< The OIAP protocol. +#define TPM_PID_OSAP ((TPM_PROTOCOL_ID) 0x0002) ///< The OSAP protocol. +#define TPM_PID_ADIP ((TPM_PROTOCOL_ID) 0x0003) ///< The ADIP protocol. +#define TPM_PID_ADCP ((TPM_PROTOCOL_ID) 0x0004) ///< The ADCP protocol. +#define TPM_PID_OWNER ((TPM_PROTOCOL_ID) 0x0005) ///< The protocol for taking ownership of a TPM. +#define TPM_PID_DSAP ((TPM_PROTOCOL_ID) 0x0006) ///< The DSAP protocol +#define TPM_PID_TRANSPORT ((TPM_PROTOCOL_ID) 0x0007) ///< The transport protocol + +// +// Part 2, section 4.8: TPM_ALGORITHM_ID +// The TPM MUST support the algorithms TPM_ALG_RSA, TPM_ALG_SHA, TPM_ALG_HMAC, +// TPM_ALG_MGF1 +// +#define TPM_ALG_RSA ((TPM_ALGORITHM_ID) 0x00000001) ///< The RSA algorithm. +#define TPM_ALG_DES ((TPM_ALGORITHM_ID) 0x00000002) ///< The DES algorithm +#define TPM_ALG_3DES ((TPM_ALGORITHM_ID) 0x00000003) ///< The 3DES algorithm in EDE mode +#define TPM_ALG_SHA ((TPM_ALGORITHM_ID) 0x00000004) ///< The SHA1 algorithm +#define TPM_ALG_HMAC ((TPM_ALGORITHM_ID) 0x00000005) ///< The RFC 2104 HMAC algorithm +#define TPM_ALG_AES128 ((TPM_ALGORITHM_ID) 0x00000006) ///< The AES algorithm, key size 128 +#define TPM_ALG_MGF1 ((TPM_ALGORITHM_ID) 0x00000007) ///< The XOR algorithm using MGF1 to create a string the size of the encrypted block +#define TPM_ALG_AES192 ((TPM_ALGORITHM_ID) 0x00000008) ///< AES, key size 192 +#define TPM_ALG_AES256 ((TPM_ALGORITHM_ID) 0x00000009) ///< AES, key size 256 +#define TPM_ALG_XOR ((TPM_ALGORITHM_ID) 0x0000000A) ///< XOR using the rolling nonces + +// +// Part 2, section 4.9: TPM_PHYSICAL_PRESENCE +// +#define TPM_PHYSICAL_PRESENCE_HW_DISABLE ((TPM_PHYSICAL_PRESENCE) 0x0200) ///< Sets the physicalPresenceHWEnable to FALSE +#define TPM_PHYSICAL_PRESENCE_CMD_DISABLE ((TPM_PHYSICAL_PRESENCE) 0x0100) ///< Sets the physicalPresenceCMDEnable to FALSE +#define TPM_PHYSICAL_PRESENCE_LIFETIME_LOCK ((TPM_PHYSICAL_PRESENCE) 0x0080) ///< Sets the physicalPresenceLifetimeLock to TRUE +#define TPM_PHYSICAL_PRESENCE_HW_ENABLE ((TPM_PHYSICAL_PRESENCE) 0x0040) ///< Sets the physicalPresenceHWEnable to TRUE +#define TPM_PHYSICAL_PRESENCE_CMD_ENABLE ((TPM_PHYSICAL_PRESENCE) 0x0020) ///< Sets the physicalPresenceCMDEnable to TRUE +#define TPM_PHYSICAL_PRESENCE_NOTPRESENT ((TPM_PHYSICAL_PRESENCE) 0x0010) ///< Sets PhysicalPresence = FALSE +#define TPM_PHYSICAL_PRESENCE_PRESENT ((TPM_PHYSICAL_PRESENCE) 0x0008) ///< Sets PhysicalPresence = TRUE +#define TPM_PHYSICAL_PRESENCE_LOCK ((TPM_PHYSICAL_PRESENCE) 0x0004) ///< Sets PhysicalPresenceLock = TRUE + +// +// Part 2, section 4.10: TPM_MIGRATE_SCHEME +// +#define TPM_MS_MIGRATE ((TPM_MIGRATE_SCHEME) 0x0001) ///< A public key that can be used with all TPM migration commands other than 'ReWrap' mode. +#define TPM_MS_REWRAP ((TPM_MIGRATE_SCHEME) 0x0002) ///< A public key that can be used for the ReWrap mode of TPM_CreateMigrationBlob. +#define TPM_MS_MAINT ((TPM_MIGRATE_SCHEME) 0x0003) ///< A public key that can be used for the Maintenance commands +#define TPM_MS_RESTRICT_MIGRATE ((TPM_MIGRATE_SCHEME) 0x0004) ///< The key is to be migrated to a Migration Authority. +#define TPM_MS_RESTRICT_APPROVE_DOUBLE ((TPM_MIGRATE_SCHEME) 0x0005) ///< The key is to be migrated to an entity approved by a Migration Authority using double wrapping + +// +// Part 2, section 4.11: TPM_EK_TYPE +// +#define TPM_EK_TYPE_ACTIVATE ((TPM_EK_TYPE) 0x0001) ///< The blob MUST be TPM_EK_BLOB_ACTIVATE +#define TPM_EK_TYPE_AUTH ((TPM_EK_TYPE) 0x0002) ///< The blob MUST be TPM_EK_BLOB_AUTH + +// +// Part 2, section 4.12: TPM_PLATFORM_SPECIFIC +// +#define TPM_PS_PC_11 ((TPM_PLATFORM_SPECIFIC) 0x0001) ///< PC Specific version 1.1 +#define TPM_PS_PC_12 ((TPM_PLATFORM_SPECIFIC) 0x0002) ///< PC Specific version 1.2 +#define TPM_PS_PDA_12 ((TPM_PLATFORM_SPECIFIC) 0x0003) ///< PDA Specific version 1.2 +#define TPM_PS_Server_12 ((TPM_PLATFORM_SPECIFIC) 0x0004) ///< Server Specific version 1.2 +#define TPM_PS_Mobile_12 ((TPM_PLATFORM_SPECIFIC) 0x0005) ///< Mobil Specific version 1.2 + +// +// Part 2, section 5: Basic Structures +// + +/// +/// Part 2, section 5.1: TPM_STRUCT_VER +/// +typedef struct tdTPM_STRUCT_VER { + UINT8 major; + UINT8 minor; + UINT8 revMajor; + UINT8 revMinor; +} TPM_STRUCT_VER; + +/// +/// Part 2, section 5.3: TPM_VERSION +/// +typedef struct tdTPM_VERSION { + TPM_VERSION_BYTE major; + TPM_VERSION_BYTE minor; + UINT8 revMajor; + UINT8 revMinor; +} TPM_VERSION; + + +#define TPM_SHA1_160_HASH_LEN 0x14 +#define TPM_SHA1BASED_NONCE_LEN TPM_SHA1_160_HASH_LEN + +/// +/// Part 2, section 5.4: TPM_DIGEST +/// +typedef struct tdTPM_DIGEST{ + UINT8 digest[TPM_SHA1_160_HASH_LEN]; +} TPM_DIGEST; + +/// +/// This SHALL be the digest of the chosen identityLabel and privacyCA for a new TPM identity +/// +typedef TPM_DIGEST TPM_CHOSENID_HASH; +/// +/// This SHALL be the hash of a list of PCR indexes and PCR values that a key or data is bound to +/// +typedef TPM_DIGEST TPM_COMPOSITE_HASH; +/// +/// This SHALL be the value of a DIR register +/// +typedef TPM_DIGEST TPM_DIRVALUE; + +typedef TPM_DIGEST TPM_HMAC; +/// +/// The value inside of the PCR +/// +typedef TPM_DIGEST TPM_PCRVALUE; +/// +/// This SHALL be the value of the current internal audit state +/// +typedef TPM_DIGEST TPM_AUDITDIGEST; + +/// +/// Part 2, section 5.5: TPM_NONCE +/// +typedef struct tdTPM_NONCE{ + UINT8 nonce[20]; +} TPM_NONCE; + +/// +/// This SHALL be a random value generated by a TPM immediately after the EK is installed +/// in that TPM, whenever an EK is installed in that TPM +/// +typedef TPM_NONCE TPM_DAA_TPM_SEED; +/// +/// This SHALL be a random value +/// +typedef TPM_NONCE TPM_DAA_CONTEXT_SEED; + +// +// Part 2, section 5.6: TPM_AUTHDATA +// +/// +/// The AuthData data is the information that is saved or passed to provide proof of ownership +/// 296 of an entity +/// +typedef UINT8 tdTPM_AUTHDATA[20]; + +typedef tdTPM_AUTHDATA TPM_AUTHDATA; +/// +/// A secret plaintext value used in the authorization process +/// +typedef TPM_AUTHDATA TPM_SECRET; +/// +/// A ciphertext (encrypted) version of AuthData data. The encryption mechanism depends on the context +/// +typedef TPM_AUTHDATA TPM_ENCAUTH; + +/// +/// Part 2, section 5.7: TPM_KEY_HANDLE_LIST +/// Size of handle is loaded * sizeof(TPM_KEY_HANDLE) +/// +typedef struct tdTPM_KEY_HANDLE_LIST { + UINT16 loaded; + TPM_KEY_HANDLE handle[1]; +} TPM_KEY_HANDLE_LIST; + +// +// Part 2, section 5.8: TPM_KEY_USAGE values +// +/// +/// TPM_KEY_SIGNING SHALL indicate a signing key. The [private] key SHALL be +/// used for signing operations, only. This means that it MUST be a leaf of the +/// Protected Storage key hierarchy. +/// +#define TPM_KEY_SIGNING ((UINT16) 0x0010) +/// +/// TPM_KEY_STORAGE SHALL indicate a storage key. The key SHALL be used to wrap +/// and unwrap other keys in the Protected Storage hierarchy +/// +#define TPM_KEY_STORAGE ((UINT16) 0x0011) +/// +/// TPM_KEY_IDENTITY SHALL indicate an identity key. The key SHALL be used for +/// operations that require a TPM identity, only. +/// +#define TPM_KEY_IDENTITY ((UINT16) 0x0012) +/// +/// TPM_KEY_AUTHCHANGE SHALL indicate an ephemeral key that is in use during +/// the ChangeAuthAsym process, only. +/// +#define TPM_KEY_AUTHCHANGE ((UINT16) 0x0013) +/// +/// TPM_KEY_BIND SHALL indicate a key that can be used for TPM_Bind and +/// TPM_Unbind operations only. +/// +#define TPM_KEY_BIND ((UINT16) 0x0014) +/// +/// TPM_KEY_LEGACY SHALL indicate a key that can perform signing and binding +/// operations. The key MAY be used for both signing and binding operations. +/// The TPM_KEY_LEGACY key type is to allow for use by applications where both +/// signing and encryption operations occur with the same key. The use of this +/// key type is not recommended TPM_KEY_MIGRATE 0x0016 This SHALL indicate a +/// key in use for TPM_MigrateKey +/// +#define TPM_KEY_LEGACY ((UINT16) 0x0015) +/// +/// TPM_KEY_MIGRAGE SHALL indicate a key in use for TPM_MigrateKey +/// +#define TPM_KEY_MIGRATE ((UINT16) 0x0016) + +// +// Part 2, section 5.8.1: Mandatory Key Usage Schemes +// + +#define TPM_ES_NONE ((TPM_ENC_SCHEME) 0x0001) +#define TPM_ES_RSAESPKCSv15 ((TPM_ENC_SCHEME) 0x0002) +#define TPM_ES_RSAESOAEP_SHA1_MGF1 ((TPM_ENC_SCHEME) 0x0003) +#define TPM_ES_SYM_CNT ((TPM_ENC_SCHEME) 0x0004) ///< rev94 defined +#define TPM_ES_SYM_CTR ((TPM_ENC_SCHEME) 0x0004) +#define TPM_ES_SYM_OFB ((TPM_ENC_SCHEME) 0x0005) + +#define TPM_SS_NONE ((TPM_SIG_SCHEME) 0x0001) +#define TPM_SS_RSASSAPKCS1v15_SHA1 ((TPM_SIG_SCHEME) 0x0002) +#define TPM_SS_RSASSAPKCS1v15_DER ((TPM_SIG_SCHEME) 0x0003) +#define TPM_SS_RSASSAPKCS1v15_INFO ((TPM_SIG_SCHEME) 0x0004) + +// +// Part 2, section 5.9: TPM_AUTH_DATA_USAGE values +// +#define TPM_AUTH_NEVER ((TPM_AUTH_DATA_USAGE) 0x00) +#define TPM_AUTH_ALWAYS ((TPM_AUTH_DATA_USAGE) 0x01) +#define TPM_AUTH_PRIV_USE_ONLY ((TPM_AUTH_DATA_USAGE) 0x03) + +/// +/// Part 2, section 5.10: TPM_KEY_FLAGS +/// +typedef enum tdTPM_KEY_FLAGS { + redirection = 0x00000001, + migratable = 0x00000002, + isVolatile = 0x00000004, + pcrIgnoredOnRead = 0x00000008, + migrateAuthority = 0x00000010 +} TPM_KEY_FLAGS_BITS; + +/// +/// Part 2, section 5.11: TPM_CHANGEAUTH_VALIDATE +/// +typedef struct tdTPM_CHANGEAUTH_VALIDATE { + TPM_SECRET newAuthSecret; + TPM_NONCE n1; +} TPM_CHANGEAUTH_VALIDATE; + +/// +/// Part 2, section 5.12: TPM_MIGRATIONKEYAUTH +/// decalared after section 10 to catch declaration of TPM_PUBKEY +/// +/// Part 2 section 10.1: TPM_KEY_PARMS +/// [size_is(parmSize)] BYTE* parms; +/// +typedef struct tdTPM_KEY_PARMS { + TPM_ALGORITHM_ID algorithmID; + TPM_ENC_SCHEME encScheme; + TPM_SIG_SCHEME sigScheme; + UINT32 parmSize; + UINT8 *parms; +} TPM_KEY_PARMS; + +/// +/// Part 2, section 10.4: TPM_STORE_PUBKEY +/// +typedef struct tdTPM_STORE_PUBKEY { + UINT32 keyLength; + UINT8 key[1]; +} TPM_STORE_PUBKEY; + +/// +/// Part 2, section 10.5: TPM_PUBKEY +/// +typedef struct tdTPM_PUBKEY{ + TPM_KEY_PARMS algorithmParms; + TPM_STORE_PUBKEY pubKey; +} TPM_PUBKEY; + +/// +/// Part 2, section 5.12: TPM_MIGRATIONKEYAUTH +/// +typedef struct tdTPM_MIGRATIONKEYAUTH{ + TPM_PUBKEY migrationKey; + TPM_MIGRATE_SCHEME migrationScheme; + TPM_DIGEST digest; +} TPM_MIGRATIONKEYAUTH; + +/// +/// Part 2, section 5.13: TPM_COUNTER_VALUE +/// +typedef struct tdTPM_COUNTER_VALUE{ + TPM_STRUCTURE_TAG tag; + UINT8 label[4]; + TPM_ACTUAL_COUNT counter; +} TPM_COUNTER_VALUE; + +/// +/// Part 2, section 5.14: TPM_SIGN_INFO +/// Size of data indicated by dataLen +/// +typedef struct tdTPM_SIGN_INFO { + TPM_STRUCTURE_TAG tag; + UINT8 fixed[4]; + TPM_NONCE replay; + UINT32 dataLen; + UINT8 *data; +} TPM_SIGN_INFO; + +/// +/// Part 2, section 5.15: TPM_MSA_COMPOSITE +/// Number of migAuthDigest indicated by MSAlist +/// +typedef struct tdTPM_MSA_COMPOSITE { + UINT32 MSAlist; + TPM_DIGEST migAuthDigest[1]; +} TPM_MSA_COMPOSITE; + +/// +/// Part 2, section 5.16: TPM_CMK_AUTH +/// +typedef struct tdTPM_CMK_AUTH{ + TPM_DIGEST migrationAuthorityDigest; + TPM_DIGEST destinationKeyDigest; + TPM_DIGEST sourceKeyDigest; +} TPM_CMK_AUTH; + +// +// Part 2, section 5.17: TPM_CMK_DELEGATE +// +#define TPM_CMK_DELEGATE_SIGNING ((TPM_CMK_DELEGATE) BIT31) +#define TPM_CMK_DELEGATE_STORAGE ((TPM_CMK_DELEGATE) BIT30) +#define TPM_CMK_DELEGATE_BIND ((TPM_CMK_DELEGATE) BIT29) +#define TPM_CMK_DELEGATE_LEGACY ((TPM_CMK_DELEGATE) BIT28) +#define TPM_CMK_DELEGATE_MIGRATE ((TPM_CMK_DELEGATE) BIT27) + +/// +/// Part 2, section 5.18: TPM_SELECT_SIZE +/// +typedef struct tdTPM_SELECT_SIZE { + UINT8 major; + UINT8 minor; + UINT16 reqSize; +} TPM_SELECT_SIZE; + +/// +/// Part 2, section 5,19: TPM_CMK_MIGAUTH +/// +typedef struct tdTPM_CMK_MIGAUTH{ + TPM_STRUCTURE_TAG tag; + TPM_DIGEST msaDigest; + TPM_DIGEST pubKeyDigest; +} TPM_CMK_MIGAUTH; + +/// +/// Part 2, section 5.20: TPM_CMK_SIGTICKET +/// +typedef struct tdTPM_CMK_SIGTICKET{ + TPM_STRUCTURE_TAG tag; + TPM_DIGEST verKeyDigest; + TPM_DIGEST signedData; +} TPM_CMK_SIGTICKET; + +/// +/// Part 2, section 5.21: TPM_CMK_MA_APPROVAL +/// +typedef struct tdTPM_CMK_MA_APPROVAL{ + TPM_STRUCTURE_TAG tag; + TPM_DIGEST migrationAuthorityDigest; +} TPM_CMK_MA_APPROVAL; + +// +// Part 2, section 6: Command Tags +// +#define TPM_TAG_RQU_COMMAND ((TPM_STRUCTURE_TAG) 0x00C1) +#define TPM_TAG_RQU_AUTH1_COMMAND ((TPM_STRUCTURE_TAG) 0x00C2) +#define TPM_TAG_RQU_AUTH2_COMMAND ((TPM_STRUCTURE_TAG) 0x00C3) +#define TPM_TAG_RSP_COMMAND ((TPM_STRUCTURE_TAG) 0x00C4) +#define TPM_TAG_RSP_AUTH1_COMMAND ((TPM_STRUCTURE_TAG) 0x00C5) +#define TPM_TAG_RSP_AUTH2_COMMAND ((TPM_STRUCTURE_TAG) 0x00C6) + +/// +/// Part 2, section 7.1: TPM_PERMANENT_FLAGS +/// +typedef struct tdTPM_PERMANENT_FLAGS{ + TPM_STRUCTURE_TAG tag; + BOOLEAN disable; + BOOLEAN ownership; + BOOLEAN deactivated; + BOOLEAN readPubek; + BOOLEAN disableOwnerClear; + BOOLEAN allowMaintenance; + BOOLEAN physicalPresenceLifetimeLock; + BOOLEAN physicalPresenceHWEnable; + BOOLEAN physicalPresenceCMDEnable; + BOOLEAN CEKPUsed; + BOOLEAN TPMpost; + BOOLEAN TPMpostLock; + BOOLEAN FIPS; + BOOLEAN operator; + BOOLEAN enableRevokeEK; + BOOLEAN nvLocked; + BOOLEAN readSRKPub; + BOOLEAN tpmEstablished; + BOOLEAN maintenanceDone; + BOOLEAN disableFullDALogicInfo; +} TPM_PERMANENT_FLAGS; + +// +// Part 2, section 7.1.1: Flag Restrictions (of TPM_PERMANENT_FLAGS) +// +#define TPM_PF_DISABLE ((TPM_CAPABILITY_AREA) 1) +#define TPM_PF_OWNERSHIP ((TPM_CAPABILITY_AREA) 2) +#define TPM_PF_DEACTIVATED ((TPM_CAPABILITY_AREA) 3) +#define TPM_PF_READPUBEK ((TPM_CAPABILITY_AREA) 4) +#define TPM_PF_DISABLEOWNERCLEAR ((TPM_CAPABILITY_AREA) 5) +#define TPM_PF_ALLOWMAINTENANCE ((TPM_CAPABILITY_AREA) 6) +#define TPM_PF_PHYSICALPRESENCELIFETIMELOCK ((TPM_CAPABILITY_AREA) 7) +#define TPM_PF_PHYSICALPRESENCEHWENABLE ((TPM_CAPABILITY_AREA) 8) +#define TPM_PF_PHYSICALPRESENCECMDENABLE ((TPM_CAPABILITY_AREA) 9) +#define TPM_PF_CEKPUSED ((TPM_CAPABILITY_AREA) 10) +#define TPM_PF_TPMPOST ((TPM_CAPABILITY_AREA) 11) +#define TPM_PF_TPMPOSTLOCK ((TPM_CAPABILITY_AREA) 12) +#define TPM_PF_FIPS ((TPM_CAPABILITY_AREA) 13) +#define TPM_PF_OPERATOR ((TPM_CAPABILITY_AREA) 14) +#define TPM_PF_ENABLEREVOKEEK ((TPM_CAPABILITY_AREA) 15) +#define TPM_PF_NV_LOCKED ((TPM_CAPABILITY_AREA) 16) +#define TPM_PF_READSRKPUB ((TPM_CAPABILITY_AREA) 17) +#define TPM_PF_TPMESTABLISHED ((TPM_CAPABILITY_AREA) 18) +#define TPM_PF_MAINTENANCEDONE ((TPM_CAPABILITY_AREA) 19) +#define TPM_PF_DISABLEFULLDALOGICINFO ((TPM_CAPABILITY_AREA) 20) + +/// +/// Part 2, section 7.2: TPM_STCLEAR_FLAGS +/// +typedef struct tdTPM_STCLEAR_FLAGS{ + TPM_STRUCTURE_TAG tag; + BOOLEAN deactivated; + BOOLEAN disableForceClear; + BOOLEAN physicalPresence; + BOOLEAN physicalPresenceLock; + BOOLEAN bGlobalLock; +} TPM_STCLEAR_FLAGS; + +// +// Part 2, section 7.2.1: Flag Restrictions (of TPM_STCLEAR_FLAGS) +// +#define TPM_SF_DEACTIVATED ((TPM_CAPABILITY_AREA) 1) +#define TPM_SF_DISABLEFORCECLEAR ((TPM_CAPABILITY_AREA) 2) +#define TPM_SF_PHYSICALPRESENCE ((TPM_CAPABILITY_AREA) 3) +#define TPM_SF_PHYSICALPRESENCELOCK ((TPM_CAPABILITY_AREA) 4) +#define TPM_SF_BGLOBALLOCK ((TPM_CAPABILITY_AREA) 5) + +/// +/// Part 2, section 7.3: TPM_STANY_FLAGS +/// +typedef struct tdTPM_STANY_FLAGS{ + TPM_STRUCTURE_TAG tag; + BOOLEAN postInitialise; + TPM_MODIFIER_INDICATOR localityModifier; + BOOLEAN transportExclusive; + BOOLEAN TOSPresent; +} TPM_STANY_FLAGS; + +// +// Part 2, section 7.3.1: Flag Restrictions (of TPM_STANY_FLAGS) +// +#define TPM_AF_POSTINITIALISE ((TPM_CAPABILITY_AREA) 1) +#define TPM_AF_LOCALITYMODIFIER ((TPM_CAPABILITY_AREA) 2) +#define TPM_AF_TRANSPORTEXCLUSIVE ((TPM_CAPABILITY_AREA) 3) +#define TPM_AF_TOSPRESENT ((TPM_CAPABILITY_AREA) 4) + +// +// All those structures defined in section 7.4, 7.5, 7.6 are not normative and +// thus no definitions here +// +// Part 2, section 7.4: TPM_PERMANENT_DATA +// +#define TPM_MIN_COUNTERS 4 ///< the minimum number of counters is 4 +#define TPM_DELEGATE_KEY TPM_KEY +#define TPM_NUM_PCR 16 +#define TPM_MAX_NV_WRITE_NOOWNER 64 + +// +// Part 2, section 7.4.1: PERMANENT_DATA Subcap for SetCapability +// +#define TPM_PD_REVMAJOR ((TPM_CAPABILITY_AREA) 1) +#define TPM_PD_REVMINOR ((TPM_CAPABILITY_AREA) 2) +#define TPM_PD_TPMPROOF ((TPM_CAPABILITY_AREA) 3) +#define TPM_PD_OWNERAUTH ((TPM_CAPABILITY_AREA) 4) +#define TPM_PD_OPERATORAUTH ((TPM_CAPABILITY_AREA) 5) +#define TPM_PD_MANUMAINTPUB ((TPM_CAPABILITY_AREA) 6) +#define TPM_PD_ENDORSEMENTKEY ((TPM_CAPABILITY_AREA) 7) +#define TPM_PD_SRK ((TPM_CAPABILITY_AREA) 8) +#define TPM_PD_DELEGATEKEY ((TPM_CAPABILITY_AREA) 9) +#define TPM_PD_CONTEXTKEY ((TPM_CAPABILITY_AREA) 10) +#define TPM_PD_AUDITMONOTONICCOUNTER ((TPM_CAPABILITY_AREA) 11) +#define TPM_PD_MONOTONICCOUNTER ((TPM_CAPABILITY_AREA) 12) +#define TPM_PD_PCRATTRIB ((TPM_CAPABILITY_AREA) 13) +#define TPM_PD_ORDINALAUDITSTATUS ((TPM_CAPABILITY_AREA) 14) +#define TPM_PD_AUTHDIR ((TPM_CAPABILITY_AREA) 15) +#define TPM_PD_RNGSTATE ((TPM_CAPABILITY_AREA) 16) +#define TPM_PD_FAMILYTABLE ((TPM_CAPABILITY_AREA) 17) +#define TPM_DELEGATETABLE ((TPM_CAPABILITY_AREA) 18) +#define TPM_PD_EKRESET ((TPM_CAPABILITY_AREA) 19) +#define TPM_PD_MAXNVBUFSIZE ((TPM_CAPABILITY_AREA) 20) +#define TPM_PD_LASTFAMILYID ((TPM_CAPABILITY_AREA) 21) +#define TPM_PD_NOOWNERNVWRITE ((TPM_CAPABILITY_AREA) 22) +#define TPM_PD_RESTRICTDELEGATE ((TPM_CAPABILITY_AREA) 23) +#define TPM_PD_TPMDAASEED ((TPM_CAPABILITY_AREA) 24) +#define TPM_PD_DAAPROOF ((TPM_CAPABILITY_AREA) 25) + +/// +/// Part 2, section 7.5: TPM_STCLEAR_DATA +/// available inside TPM only +/// + typedef struct tdTPM_STCLEAR_DATA{ + TPM_STRUCTURE_TAG tag; + TPM_NONCE contextNonceKey; + TPM_COUNT_ID countID; + UINT32 ownerReference; + BOOLEAN disableResetLock; + TPM_PCRVALUE PCR[TPM_NUM_PCR]; + UINT32 deferredPhysicalPresence; + }TPM_STCLEAR_DATA; + +// +// Part 2, section 7.5.1: STCLEAR_DATA Subcap for SetCapability +// +#define TPM_SD_CONTEXTNONCEKEY ((TPM_CAPABILITY_AREA)0x00000001) +#define TPM_SD_COUNTID ((TPM_CAPABILITY_AREA)0x00000002) +#define TPM_SD_OWNERREFERENCE ((TPM_CAPABILITY_AREA)0x00000003) +#define TPM_SD_DISABLERESETLOCK ((TPM_CAPABILITY_AREA)0x00000004) +#define TPM_SD_PCR ((TPM_CAPABILITY_AREA)0x00000005) +#define TPM_SD_DEFERREDPHYSICALPRESENCE ((TPM_CAPABILITY_AREA)0x00000006) + +// +// Part 2, section 7.6.1: STANY_DATA Subcap for SetCapability +// +#define TPM_AD_CONTEXTNONCESESSION ((TPM_CAPABILITY_AREA) 1) +#define TPM_AD_AUDITDIGEST ((TPM_CAPABILITY_AREA) 2) +#define TPM_AD_CURRENTTICKS ((TPM_CAPABILITY_AREA) 3) +#define TPM_AD_CONTEXTCOUNT ((TPM_CAPABILITY_AREA) 4) +#define TPM_AD_CONTEXTLIST ((TPM_CAPABILITY_AREA) 5) +#define TPM_AD_SESSIONS ((TPM_CAPABILITY_AREA) 6) + +// +// Part 2, section 8: PCR Structures +// + +/// +/// Part 2, section 8.1: TPM_PCR_SELECTION +/// Size of pcrSelect[] indicated by sizeOfSelect +/// +typedef struct tdTPM_PCR_SELECTION { + UINT16 sizeOfSelect; + UINT8 pcrSelect[1]; +} TPM_PCR_SELECTION; + +/// +/// Part 2, section 8.2: TPM_PCR_COMPOSITE +/// Size of pcrValue[] indicated by valueSize +/// +typedef struct tdTPM_PCR_COMPOSITE { + TPM_PCR_SELECTION select; + UINT32 valueSize; + TPM_PCRVALUE pcrValue[1]; +} TPM_PCR_COMPOSITE; + +/// +/// Part 2, section 8.3: TPM_PCR_INFO +/// +typedef struct tdTPM_PCR_INFO { + TPM_PCR_SELECTION pcrSelection; + TPM_COMPOSITE_HASH digestAtRelease; + TPM_COMPOSITE_HASH digestAtCreation; +} TPM_PCR_INFO; + +/// +/// Part 2, section 8.6: TPM_LOCALITY_SELECTION +/// +typedef UINT8 TPM_LOCALITY_SELECTION; + +#define TPM_LOC_FOUR ((UINT8) 0x10) +#define TPM_LOC_THREE ((UINT8) 0x08) +#define TPM_LOC_TWO ((UINT8) 0x04) +#define TPM_LOC_ONE ((UINT8) 0x02) +#define TPM_LOC_ZERO ((UINT8) 0x01) + +/// +/// Part 2, section 8.4: TPM_PCR_INFO_LONG +/// +typedef struct tdTPM_PCR_INFO_LONG { + TPM_STRUCTURE_TAG tag; + TPM_LOCALITY_SELECTION localityAtCreation; + TPM_LOCALITY_SELECTION localityAtRelease; + TPM_PCR_SELECTION creationPCRSelection; + TPM_PCR_SELECTION releasePCRSelection; + TPM_COMPOSITE_HASH digestAtCreation; + TPM_COMPOSITE_HASH digestAtRelease; +} TPM_PCR_INFO_LONG; + +/// +/// Part 2, section 8.5: TPM_PCR_INFO_SHORT +/// +typedef struct tdTPM_PCR_INFO_SHORT{ + TPM_PCR_SELECTION pcrSelection; + TPM_LOCALITY_SELECTION localityAtRelease; + TPM_COMPOSITE_HASH digestAtRelease; +} TPM_PCR_INFO_SHORT; + +/// +/// Part 2, section 8.8: TPM_PCR_ATTRIBUTES +/// +typedef struct tdTPM_PCR_ATTRIBUTES{ + BOOLEAN pcrReset; + TPM_LOCALITY_SELECTION pcrExtendLocal; + TPM_LOCALITY_SELECTION pcrResetLocal; +} TPM_PCR_ATTRIBUTES; + +// +// Part 2, section 9: Storage Structures +// + +/// +/// Part 2, section 9.1: TPM_STORED_DATA +/// [size_is(sealInfoSize)] BYTE* sealInfo; +/// [size_is(encDataSize)] BYTE* encData; +/// +typedef struct tdTPM_STORED_DATA { + TPM_STRUCT_VER ver; + UINT32 sealInfoSize; + UINT8 *sealInfo; + UINT32 encDataSize; + UINT8 *encData; +} TPM_STORED_DATA; + +/// +/// Part 2, section 9.2: TPM_STORED_DATA12 +/// [size_is(sealInfoSize)] BYTE* sealInfo; +/// [size_is(encDataSize)] BYTE* encData; +/// +typedef struct tdTPM_STORED_DATA12 { + TPM_STRUCTURE_TAG tag; + TPM_ENTITY_TYPE et; + UINT32 sealInfoSize; + UINT8 *sealInfo; + UINT32 encDataSize; + UINT8 *encData; +} TPM_STORED_DATA12; + +/// +/// Part 2, section 9.3: TPM_SEALED_DATA +/// [size_is(dataSize)] BYTE* data; +/// +typedef struct tdTPM_SEALED_DATA { + TPM_PAYLOAD_TYPE payload; + TPM_SECRET authData; + TPM_NONCE tpmProof; + TPM_DIGEST storedDigest; + UINT32 dataSize; + UINT8 *data; +} TPM_SEALED_DATA; + +/// +/// Part 2, section 9.4: TPM_SYMMETRIC_KEY +/// [size_is(size)] BYTE* data; +/// +typedef struct tdTPM_SYMMETRIC_KEY { + TPM_ALGORITHM_ID algId; + TPM_ENC_SCHEME encScheme; + UINT16 dataSize; + UINT8 *data; +} TPM_SYMMETRIC_KEY; + +/// +/// Part 2, section 9.5: TPM_BOUND_DATA +/// +typedef struct tdTPM_BOUND_DATA { + TPM_STRUCT_VER ver; + TPM_PAYLOAD_TYPE payload; + UINT8 payloadData[1]; +} TPM_BOUND_DATA; + +// +// Part 2 section 10: TPM_KEY complex +// + +// +// Section 10.1, 10.4, and 10.5 have been defined previously +// + +/// +/// Part 2, section 10.2: TPM_KEY +/// [size_is(encDataSize)] BYTE* encData; +/// +typedef struct tdTPM_KEY{ + TPM_STRUCT_VER ver; + TPM_KEY_USAGE keyUsage; + TPM_KEY_FLAGS keyFlags; + TPM_AUTH_DATA_USAGE authDataUsage; + TPM_KEY_PARMS algorithmParms; + UINT32 PCRInfoSize; + UINT8 *PCRInfo; + TPM_STORE_PUBKEY pubKey; + UINT32 encDataSize; + UINT8 *encData; +} TPM_KEY; + +/// +/// Part 2, section 10.3: TPM_KEY12 +/// [size_is(encDataSize)] BYTE* encData; +/// +typedef struct tdTPM_KEY12{ + TPM_STRUCTURE_TAG tag; + UINT16 fill; + TPM_KEY_USAGE keyUsage; + TPM_KEY_FLAGS keyFlags; + TPM_AUTH_DATA_USAGE authDataUsage; + TPM_KEY_PARMS algorithmParms; + UINT32 PCRInfoSize; + UINT8 *PCRInfo; + TPM_STORE_PUBKEY pubKey; + UINT32 encDataSize; + UINT8 *encData; +} TPM_KEY12; + +/// +/// Part 2, section 10.7: TPM_STORE_PRIVKEY +/// [size_is(keyLength)] BYTE* key; +/// +typedef struct tdTPM_STORE_PRIVKEY { + UINT32 keyLength; + UINT8 *key; +} TPM_STORE_PRIVKEY; + +/// +/// Part 2, section 10.6: TPM_STORE_ASYMKEY +/// +typedef struct tdTPM_STORE_ASYMKEY { // pos len total + TPM_PAYLOAD_TYPE payload; // 0 1 1 + TPM_SECRET usageAuth; // 1 20 21 + TPM_SECRET migrationAuth; // 21 20 41 + TPM_DIGEST pubDataDigest; // 41 20 61 + TPM_STORE_PRIVKEY privKey; // 61 132-151 193-214 +} TPM_STORE_ASYMKEY; + +/// +/// Part 2, section 10.8: TPM_MIGRATE_ASYMKEY +/// [size_is(partPrivKeyLen)] BYTE* partPrivKey; +/// +typedef struct tdTPM_MIGRATE_ASYMKEY { // pos len total + TPM_PAYLOAD_TYPE payload; // 0 1 1 + TPM_SECRET usageAuth; // 1 20 21 + TPM_DIGEST pubDataDigest; // 21 20 41 + UINT32 partPrivKeyLen; // 41 4 45 + UINT8 *partPrivKey; // 45 112-127 157-172 +} TPM_MIGRATE_ASYMKEY; + +/// +/// Part 2, section 10.9: TPM_KEY_CONTROL +/// +#define TPM_KEY_CONTROL_OWNER_EVICT ((UINT32) 0x00000001) + +// +// Part 2, section 11: Signed Structures +// + +/// +/// Part 2, section 11.1: TPM_CERTIFY_INFO Structure +/// +typedef struct tdTPM_CERTIFY_INFO { + TPM_STRUCT_VER version; + TPM_KEY_USAGE keyUsage; + TPM_KEY_FLAGS keyFlags; + TPM_AUTH_DATA_USAGE authDataUsage; + TPM_KEY_PARMS algorithmParms; + TPM_DIGEST pubkeyDigest; + TPM_NONCE data; + BOOLEAN parentPCRStatus; + UINT32 PCRInfoSize; + UINT8 *PCRInfo; +} TPM_CERTIFY_INFO; + +/// +/// Part 2, section 11.2: TPM_CERTIFY_INFO2 Structure +/// +typedef struct tdTPM_CERTIFY_INFO2 { + TPM_STRUCTURE_TAG tag; + UINT8 fill; + TPM_PAYLOAD_TYPE payloadType; + TPM_KEY_USAGE keyUsage; + TPM_KEY_FLAGS keyFlags; + TPM_AUTH_DATA_USAGE authDataUsage; + TPM_KEY_PARMS algorithmParms; + TPM_DIGEST pubkeyDigest; + TPM_NONCE data; + BOOLEAN parentPCRStatus; + UINT32 PCRInfoSize; + UINT8 *PCRInfo; + UINT32 migrationAuthoritySize; + UINT8 *migrationAuthority; +} TPM_CERTIFY_INFO2; + +/// +/// Part 2, section 11.3 TPM_QUOTE_INFO Structure +/// +typedef struct tdTPM_QUOTE_INFO { + TPM_STRUCT_VER version; + UINT8 fixed[4]; + TPM_COMPOSITE_HASH digestValue; + TPM_NONCE externalData; +} TPM_QUOTE_INFO; + +/// +/// Part 2, section 11.4 TPM_QUOTE_INFO2 Structure +/// +typedef struct tdTPM_QUOTE_INFO2 { + TPM_STRUCTURE_TAG tag; + UINT8 fixed[4]; + TPM_NONCE externalData; + TPM_PCR_INFO_SHORT infoShort; +} TPM_QUOTE_INFO2; + +// +// Part 2, section 12: Identity Structures +// + +/// +/// Part 2, section 12.1 TPM_EK_BLOB +/// +typedef struct tdTPM_EK_BLOB { + TPM_STRUCTURE_TAG tag; + TPM_EK_TYPE ekType; + UINT32 blobSize; + UINT8 *blob; +} TPM_EK_BLOB; + +/// +/// Part 2, section 12.2 TPM_EK_BLOB_ACTIVATE +/// +typedef struct tdTPM_EK_BLOB_ACTIVATE { + TPM_STRUCTURE_TAG tag; + TPM_SYMMETRIC_KEY sessionKey; + TPM_DIGEST idDigest; + TPM_PCR_INFO_SHORT pcrInfo; +} TPM_EK_BLOB_ACTIVATE; + +/// +/// Part 2, section 12.3 TPM_EK_BLOB_AUTH +/// +typedef struct tdTPM_EK_BLOB_AUTH { + TPM_STRUCTURE_TAG tag; + TPM_SECRET authValue; +} TPM_EK_BLOB_AUTH; + + +/// +/// Part 2, section 12.5 TPM_IDENTITY_CONTENTS +/// +typedef struct tdTPM_IDENTITY_CONTENTS { + TPM_STRUCT_VER ver; + UINT32 ordinal; + TPM_CHOSENID_HASH labelPrivCADigest; + TPM_PUBKEY identityPubKey; +} TPM_IDENTITY_CONTENTS; + +/// +/// Part 2, section 12.6 TPM_IDENTITY_REQ +/// +typedef struct tdTPM_IDENTITY_REQ { + UINT32 asymSize; + UINT32 symSize; + TPM_KEY_PARMS asymAlgorithm; + TPM_KEY_PARMS symAlgorithm; + UINT8 *asymBlob; + UINT8 *symBlob; +} TPM_IDENTITY_REQ; + +/// +/// Part 2, section 12.7 TPM_IDENTITY_PROOF +/// +typedef struct tdTPM_IDENTITY_PROOF { + TPM_STRUCT_VER ver; + UINT32 labelSize; + UINT32 identityBindingSize; + UINT32 endorsementSize; + UINT32 platformSize; + UINT32 conformanceSize; + TPM_PUBKEY identityKey; + UINT8 *labelArea; + UINT8 *identityBinding; + UINT8 *endorsementCredential; + UINT8 *platformCredential; + UINT8 *conformanceCredential; +} TPM_IDENTITY_PROOF; + +/// +/// Part 2, section 12.8 TPM_ASYM_CA_CONTENTS +/// +typedef struct tdTPM_ASYM_CA_CONTENTS { + TPM_SYMMETRIC_KEY sessionKey; + TPM_DIGEST idDigest; +} TPM_ASYM_CA_CONTENTS; + +/// +/// Part 2, section 12.9 TPM_SYM_CA_ATTESTATION +/// +typedef struct tdTPM_SYM_CA_ATTESTATION { + UINT32 credSize; + TPM_KEY_PARMS algorithm; + UINT8 *credential; +} TPM_SYM_CA_ATTESTATION; + +/// +/// Part 2, section 15: Tick Structures +/// Placed here out of order because definitions are used in section 13. +/// +typedef struct tdTPM_CURRENT_TICKS { + TPM_STRUCTURE_TAG tag; + UINT64 currentTicks; + UINT16 tickRate; + TPM_NONCE tickNonce; +} TPM_CURRENT_TICKS; + +/// +/// Part 2, section 13: Transport structures +/// + +/// +/// Part 2, section 13.1: TPM _TRANSPORT_PUBLIC +/// +typedef struct tdTPM_TRANSPORT_PUBLIC { + TPM_STRUCTURE_TAG tag; + TPM_TRANSPORT_ATTRIBUTES transAttributes; + TPM_ALGORITHM_ID algId; + TPM_ENC_SCHEME encScheme; +} TPM_TRANSPORT_PUBLIC; + +// +// Part 2, section 13.1.1 TPM_TRANSPORT_ATTRIBUTES Definitions +// +#define TPM_TRANSPORT_ENCRYPT ((UINT32)BIT0) +#define TPM_TRANSPORT_LOG ((UINT32)BIT1) +#define TPM_TRANSPORT_EXCLUSIVE ((UINT32)BIT2) + +/// +/// Part 2, section 13.2 TPM_TRANSPORT_INTERNAL +/// +typedef struct tdTPM_TRANSPORT_INTERNAL { + TPM_STRUCTURE_TAG tag; + TPM_AUTHDATA authData; + TPM_TRANSPORT_PUBLIC transPublic; + TPM_TRANSHANDLE transHandle; + TPM_NONCE transNonceEven; + TPM_DIGEST transDigest; +} TPM_TRANSPORT_INTERNAL; + +/// +/// Part 2, section 13.3 TPM_TRANSPORT_LOG_IN structure +/// +typedef struct tdTPM_TRANSPORT_LOG_IN { + TPM_STRUCTURE_TAG tag; + TPM_DIGEST parameters; + TPM_DIGEST pubKeyHash; +} TPM_TRANSPORT_LOG_IN; + +/// +/// Part 2, section 13.4 TPM_TRANSPORT_LOG_OUT structure +/// +typedef struct tdTPM_TRANSPORT_LOG_OUT { + TPM_STRUCTURE_TAG tag; + TPM_CURRENT_TICKS currentTicks; + TPM_DIGEST parameters; + TPM_MODIFIER_INDICATOR locality; +} TPM_TRANSPORT_LOG_OUT; + +/// +/// Part 2, section 13.5 TPM_TRANSPORT_AUTH structure +/// +typedef struct tdTPM_TRANSPORT_AUTH { + TPM_STRUCTURE_TAG tag; + TPM_AUTHDATA authData; +} TPM_TRANSPORT_AUTH; + +// +// Part 2, section 14: Audit Structures +// + +/// +/// Part 2, section 14.1 TPM_AUDIT_EVENT_IN structure +/// +typedef struct tdTPM_AUDIT_EVENT_IN { + TPM_STRUCTURE_TAG tag; + TPM_DIGEST inputParms; + TPM_COUNTER_VALUE auditCount; +} TPM_AUDIT_EVENT_IN; + +/// +/// Part 2, section 14.2 TPM_AUDIT_EVENT_OUT structure +/// +typedef struct tdTPM_AUDIT_EVENT_OUT { + TPM_STRUCTURE_TAG tag; + TPM_COMMAND_CODE ordinal; + TPM_DIGEST outputParms; + TPM_COUNTER_VALUE auditCount; + TPM_RESULT returnCode; +} TPM_AUDIT_EVENT_OUT; + +// +// Part 2, section 16: Return Codes +// + +#define TPM_VENDOR_ERROR TPM_Vendor_Specific32 +#define TPM_NON_FATAL 0x00000800 + +#define TPM_SUCCESS ((TPM_RESULT) TPM_BASE) +#define TPM_AUTHFAIL ((TPM_RESULT) (TPM_BASE + 1)) +#define TPM_BADINDEX ((TPM_RESULT) (TPM_BASE + 2)) +#define TPM_BAD_PARAMETER ((TPM_RESULT) (TPM_BASE + 3)) +#define TPM_AUDITFAILURE ((TPM_RESULT) (TPM_BASE + 4)) +#define TPM_CLEAR_DISABLED ((TPM_RESULT) (TPM_BASE + 5)) +#define TPM_DEACTIVATED ((TPM_RESULT) (TPM_BASE + 6)) +#define TPM_DISABLED ((TPM_RESULT) (TPM_BASE + 7)) +#define TPM_DISABLED_CMD ((TPM_RESULT) (TPM_BASE + 8)) +#define TPM_FAIL ((TPM_RESULT) (TPM_BASE + 9)) +#define TPM_BAD_ORDINAL ((TPM_RESULT) (TPM_BASE + 10)) +#define TPM_INSTALL_DISABLED ((TPM_RESULT) (TPM_BASE + 11)) +#define TPM_INVALID_KEYHANDLE ((TPM_RESULT) (TPM_BASE + 12)) +#define TPM_KEYNOTFOUND ((TPM_RESULT) (TPM_BASE + 13)) +#define TPM_INAPPROPRIATE_ENC ((TPM_RESULT) (TPM_BASE + 14)) +#define TPM_MIGRATEFAIL ((TPM_RESULT) (TPM_BASE + 15)) +#define TPM_INVALID_PCR_INFO ((TPM_RESULT) (TPM_BASE + 16)) +#define TPM_NOSPACE ((TPM_RESULT) (TPM_BASE + 17)) +#define TPM_NOSRK ((TPM_RESULT) (TPM_BASE + 18)) +#define TPM_NOTSEALED_BLOB ((TPM_RESULT) (TPM_BASE + 19)) +#define TPM_OWNER_SET ((TPM_RESULT) (TPM_BASE + 20)) +#define TPM_RESOURCES ((TPM_RESULT) (TPM_BASE + 21)) +#define TPM_SHORTRANDOM ((TPM_RESULT) (TPM_BASE + 22)) +#define TPM_SIZE ((TPM_RESULT) (TPM_BASE + 23)) +#define TPM_WRONGPCRVAL ((TPM_RESULT) (TPM_BASE + 24)) +#define TPM_BAD_PARAM_SIZE ((TPM_RESULT) (TPM_BASE + 25)) +#define TPM_SHA_THREAD ((TPM_RESULT) (TPM_BASE + 26)) +#define TPM_SHA_ERROR ((TPM_RESULT) (TPM_BASE + 27)) +#define TPM_FAILEDSELFTEST ((TPM_RESULT) (TPM_BASE + 28)) +#define TPM_AUTH2FAIL ((TPM_RESULT) (TPM_BASE + 29)) +#define TPM_BADTAG ((TPM_RESULT) (TPM_BASE + 30)) +#define TPM_IOERROR ((TPM_RESULT) (TPM_BASE + 31)) +#define TPM_ENCRYPT_ERROR ((TPM_RESULT) (TPM_BASE + 32)) +#define TPM_DECRYPT_ERROR ((TPM_RESULT) (TPM_BASE + 33)) +#define TPM_INVALID_AUTHHANDLE ((TPM_RESULT) (TPM_BASE + 34)) +#define TPM_NO_ENDORSEMENT ((TPM_RESULT) (TPM_BASE + 35)) +#define TPM_INVALID_KEYUSAGE ((TPM_RESULT) (TPM_BASE + 36)) +#define TPM_WRONG_ENTITYTYPE ((TPM_RESULT) (TPM_BASE + 37)) +#define TPM_INVALID_POSTINIT ((TPM_RESULT) (TPM_BASE + 38)) +#define TPM_INAPPROPRIATE_SIG ((TPM_RESULT) (TPM_BASE + 39)) +#define TPM_BAD_KEY_PROPERTY ((TPM_RESULT) (TPM_BASE + 40)) +#define TPM_BAD_MIGRATION ((TPM_RESULT) (TPM_BASE + 41)) +#define TPM_BAD_SCHEME ((TPM_RESULT) (TPM_BASE + 42)) +#define TPM_BAD_DATASIZE ((TPM_RESULT) (TPM_BASE + 43)) +#define TPM_BAD_MODE ((TPM_RESULT) (TPM_BASE + 44)) +#define TPM_BAD_PRESENCE ((TPM_RESULT) (TPM_BASE + 45)) +#define TPM_BAD_VERSION ((TPM_RESULT) (TPM_BASE + 46)) +#define TPM_NO_WRAP_TRANSPORT ((TPM_RESULT) (TPM_BASE + 47)) +#define TPM_AUDITFAIL_UNSUCCESSFUL ((TPM_RESULT) (TPM_BASE + 48)) +#define TPM_AUDITFAIL_SUCCESSFUL ((TPM_RESULT) (TPM_BASE + 49)) +#define TPM_NOTRESETABLE ((TPM_RESULT) (TPM_BASE + 50)) +#define TPM_NOTLOCAL ((TPM_RESULT) (TPM_BASE + 51)) +#define TPM_BAD_TYPE ((TPM_RESULT) (TPM_BASE + 52)) +#define TPM_INVALID_RESOURCE ((TPM_RESULT) (TPM_BASE + 53)) +#define TPM_NOTFIPS ((TPM_RESULT) (TPM_BASE + 54)) +#define TPM_INVALID_FAMILY ((TPM_RESULT) (TPM_BASE + 55)) +#define TPM_NO_NV_PERMISSION ((TPM_RESULT) (TPM_BASE + 56)) +#define TPM_REQUIRES_SIGN ((TPM_RESULT) (TPM_BASE + 57)) +#define TPM_KEY_NOTSUPPORTED ((TPM_RESULT) (TPM_BASE + 58)) +#define TPM_AUTH_CONFLICT ((TPM_RESULT) (TPM_BASE + 59)) +#define TPM_AREA_LOCKED ((TPM_RESULT) (TPM_BASE + 60)) +#define TPM_BAD_LOCALITY ((TPM_RESULT) (TPM_BASE + 61)) +#define TPM_READ_ONLY ((TPM_RESULT) (TPM_BASE + 62)) +#define TPM_PER_NOWRITE ((TPM_RESULT) (TPM_BASE + 63)) +#define TPM_FAMILYCOUNT ((TPM_RESULT) (TPM_BASE + 64)) +#define TPM_WRITE_LOCKED ((TPM_RESULT) (TPM_BASE + 65)) +#define TPM_BAD_ATTRIBUTES ((TPM_RESULT) (TPM_BASE + 66)) +#define TPM_INVALID_STRUCTURE ((TPM_RESULT) (TPM_BASE + 67)) +#define TPM_KEY_OWNER_CONTROL ((TPM_RESULT) (TPM_BASE + 68)) +#define TPM_BAD_COUNTER ((TPM_RESULT) (TPM_BASE + 69)) +#define TPM_NOT_FULLWRITE ((TPM_RESULT) (TPM_BASE + 70)) +#define TPM_CONTEXT_GAP ((TPM_RESULT) (TPM_BASE + 71)) +#define TPM_MAXNVWRITES ((TPM_RESULT) (TPM_BASE + 72)) +#define TPM_NOOPERATOR ((TPM_RESULT) (TPM_BASE + 73)) +#define TPM_RESOURCEMISSING ((TPM_RESULT) (TPM_BASE + 74)) +#define TPM_DELEGATE_LOCK ((TPM_RESULT) (TPM_BASE + 75)) +#define TPM_DELEGATE_FAMILY ((TPM_RESULT) (TPM_BASE + 76)) +#define TPM_DELEGATE_ADMIN ((TPM_RESULT) (TPM_BASE + 77)) +#define TPM_TRANSPORT_NOTEXCLUSIVE ((TPM_RESULT) (TPM_BASE + 78)) +#define TPM_OWNER_CONTROL ((TPM_RESULT) (TPM_BASE + 79)) +#define TPM_DAA_RESOURCES ((TPM_RESULT) (TPM_BASE + 80)) +#define TPM_DAA_INPUT_DATA0 ((TPM_RESULT) (TPM_BASE + 81)) +#define TPM_DAA_INPUT_DATA1 ((TPM_RESULT) (TPM_BASE + 82)) +#define TPM_DAA_ISSUER_SETTINGS ((TPM_RESULT) (TPM_BASE + 83)) +#define TPM_DAA_TPM_SETTINGS ((TPM_RESULT) (TPM_BASE + 84)) +#define TPM_DAA_STAGE ((TPM_RESULT) (TPM_BASE + 85)) +#define TPM_DAA_ISSUER_VALIDITY ((TPM_RESULT) (TPM_BASE + 86)) +#define TPM_DAA_WRONG_W ((TPM_RESULT) (TPM_BASE + 87)) +#define TPM_BAD_HANDLE ((TPM_RESULT) (TPM_BASE + 88)) +#define TPM_BAD_DELEGATE ((TPM_RESULT) (TPM_BASE + 89)) +#define TPM_BADCONTEXT ((TPM_RESULT) (TPM_BASE + 90)) +#define TPM_TOOMANYCONTEXTS ((TPM_RESULT) (TPM_BASE + 91)) +#define TPM_MA_TICKET_SIGNATURE ((TPM_RESULT) (TPM_BASE + 92)) +#define TPM_MA_DESTINATION ((TPM_RESULT) (TPM_BASE + 93)) +#define TPM_MA_SOURCE ((TPM_RESULT) (TPM_BASE + 94)) +#define TPM_MA_AUTHORITY ((TPM_RESULT) (TPM_BASE + 95)) +#define TPM_PERMANENTEK ((TPM_RESULT) (TPM_BASE + 97)) +#define TPM_BAD_SIGNATURE ((TPM_RESULT) (TPM_BASE + 98)) +#define TPM_NOCONTEXTSPACE ((TPM_RESULT) (TPM_BASE + 99)) + +#define TPM_RETRY ((TPM_RESULT) (TPM_BASE + TPM_NON_FATAL)) +#define TPM_NEEDS_SELFTEST ((TPM_RESULT) (TPM_BASE + TPM_NON_FATAL + 1)) +#define TPM_DOING_SELFTEST ((TPM_RESULT) (TPM_BASE + TPM_NON_FATAL + 2)) +#define TPM_DEFEND_LOCK_RUNNING ((TPM_RESULT) (TPM_BASE + TPM_NON_FATAL + 3)) + +// +// Part 2, section 17: Ordinals +// +// Ordinals are 32 bit values. The upper byte contains values that serve as +// flag indicators, the next byte contains values indicating what committee +// designated the ordinal, and the final two bytes contain the Command +// Ordinal Index. +// 3 2 1 +// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |P|C|V| Reserved| Purview | Command Ordinal Index | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// Where: +// +// * P is Protected/Unprotected command. When 0 the command is a Protected +// command, when 1 the command is an Unprotected command. +// +// * C is Non-Connection/Connection related command. When 0 this command +// passes through to either the protected (TPM) or unprotected (TSS) +// components. +// +// * V is TPM/Vendor command. When 0 the command is TPM defined, when 1 the +// command is vendor defined. +// +// * All reserved area bits are set to 0. +// + +#define TPM_ORD_ActivateIdentity ((TPM_COMMAND_CODE) 0x0000007A) +#define TPM_ORD_AuthorizeMigrationKey ((TPM_COMMAND_CODE) 0x0000002B) +#define TPM_ORD_CertifyKey ((TPM_COMMAND_CODE) 0x00000032) +#define TPM_ORD_CertifyKey2 ((TPM_COMMAND_CODE) 0x00000033) +#define TPM_ORD_CertifySelfTest ((TPM_COMMAND_CODE) 0x00000052) +#define TPM_ORD_ChangeAuth ((TPM_COMMAND_CODE) 0x0000000C) +#define TPM_ORD_ChangeAuthAsymFinish ((TPM_COMMAND_CODE) 0x0000000F) +#define TPM_ORD_ChangeAuthAsymStart ((TPM_COMMAND_CODE) 0x0000000E) +#define TPM_ORD_ChangeAuthOwner ((TPM_COMMAND_CODE) 0x00000010) +#define TPM_ORD_CMK_ApproveMA ((TPM_COMMAND_CODE) 0x0000001D) +#define TPM_ORD_CMK_ConvertMigration ((TPM_COMMAND_CODE) 0x00000024) +#define TPM_ORD_CMK_CreateBlob ((TPM_COMMAND_CODE) 0x0000001B) +#define TPM_ORD_CMK_CreateKey ((TPM_COMMAND_CODE) 0x00000013) +#define TPM_ORD_CMK_CreateTicket ((TPM_COMMAND_CODE) 0x00000012) +#define TPM_ORD_CMK_SetRestrictions ((TPM_COMMAND_CODE) 0x0000001C) +#define TPM_ORD_ContinueSelfTest ((TPM_COMMAND_CODE) 0x00000053) +#define TPM_ORD_ConvertMigrationBlob ((TPM_COMMAND_CODE) 0x0000002A) +#define TPM_ORD_CreateCounter ((TPM_COMMAND_CODE) 0x000000DC) +#define TPM_ORD_CreateEndorsementKeyPair ((TPM_COMMAND_CODE) 0x00000078) +#define TPM_ORD_CreateMaintenanceArchive ((TPM_COMMAND_CODE) 0x0000002C) +#define TPM_ORD_CreateMigrationBlob ((TPM_COMMAND_CODE) 0x00000028) +#define TPM_ORD_CreateRevocableEK ((TPM_COMMAND_CODE) 0x0000007F) +#define TPM_ORD_CreateWrapKey ((TPM_COMMAND_CODE) 0x0000001F) +#define TPM_ORD_DAA_JOIN ((TPM_COMMAND_CODE) 0x00000029) +#define TPM_ORD_DAA_SIGN ((TPM_COMMAND_CODE) 0x00000031) +#define TPM_ORD_Delegate_CreateKeyDelegation ((TPM_COMMAND_CODE) 0x000000D4) +#define TPM_ORD_Delegate_CreateOwnerDelegation ((TPM_COMMAND_CODE) 0x000000D5) +#define TPM_ORD_Delegate_LoadOwnerDelegation ((TPM_COMMAND_CODE) 0x000000D8) +#define TPM_ORD_Delegate_Manage ((TPM_COMMAND_CODE) 0x000000D2) +#define TPM_ORD_Delegate_ReadTable ((TPM_COMMAND_CODE) 0x000000DB) +#define TPM_ORD_Delegate_UpdateVerification ((TPM_COMMAND_CODE) 0x000000D1) +#define TPM_ORD_Delegate_VerifyDelegation ((TPM_COMMAND_CODE) 0x000000D6) +#define TPM_ORD_DirRead ((TPM_COMMAND_CODE) 0x0000001A) +#define TPM_ORD_DirWriteAuth ((TPM_COMMAND_CODE) 0x00000019) +#define TPM_ORD_DisableForceClear ((TPM_COMMAND_CODE) 0x0000005E) +#define TPM_ORD_DisableOwnerClear ((TPM_COMMAND_CODE) 0x0000005C) +#define TPM_ORD_DisablePubekRead ((TPM_COMMAND_CODE) 0x0000007E) +#define TPM_ORD_DSAP ((TPM_COMMAND_CODE) 0x00000011) +#define TPM_ORD_EstablishTransport ((TPM_COMMAND_CODE) 0x000000E6) +#define TPM_ORD_EvictKey ((TPM_COMMAND_CODE) 0x00000022) +#define TPM_ORD_ExecuteTransport ((TPM_COMMAND_CODE) 0x000000E7) +#define TPM_ORD_Extend ((TPM_COMMAND_CODE) 0x00000014) +#define TPM_ORD_FieldUpgrade ((TPM_COMMAND_CODE) 0x000000AA) +#define TPM_ORD_FlushSpecific ((TPM_COMMAND_CODE) 0x000000BA) +#define TPM_ORD_ForceClear ((TPM_COMMAND_CODE) 0x0000005D) +#define TPM_ORD_GetAuditDigest ((TPM_COMMAND_CODE) 0x00000085) +#define TPM_ORD_GetAuditDigestSigned ((TPM_COMMAND_CODE) 0x00000086) +#define TPM_ORD_GetAuditEvent ((TPM_COMMAND_CODE) 0x00000082) +#define TPM_ORD_GetAuditEventSigned ((TPM_COMMAND_CODE) 0x00000083) +#define TPM_ORD_GetCapability ((TPM_COMMAND_CODE) 0x00000065) +#define TPM_ORD_GetCapabilityOwner ((TPM_COMMAND_CODE) 0x00000066) +#define TPM_ORD_GetCapabilitySigned ((TPM_COMMAND_CODE) 0x00000064) +#define TPM_ORD_GetOrdinalAuditStatus ((TPM_COMMAND_CODE) 0x0000008C) +#define TPM_ORD_GetPubKey ((TPM_COMMAND_CODE) 0x00000021) +#define TPM_ORD_GetRandom ((TPM_COMMAND_CODE) 0x00000046) +#define TPM_ORD_GetTestResult ((TPM_COMMAND_CODE) 0x00000054) +#define TPM_ORD_GetTicks ((TPM_COMMAND_CODE) 0x000000F1) +#define TPM_ORD_IncrementCounter ((TPM_COMMAND_CODE) 0x000000DD) +#define TPM_ORD_Init ((TPM_COMMAND_CODE) 0x00000097) +#define TPM_ORD_KeyControlOwner ((TPM_COMMAND_CODE) 0x00000023) +#define TPM_ORD_KillMaintenanceFeature ((TPM_COMMAND_CODE) 0x0000002E) +#define TPM_ORD_LoadAuthContext ((TPM_COMMAND_CODE) 0x000000B7) +#define TPM_ORD_LoadContext ((TPM_COMMAND_CODE) 0x000000B9) +#define TPM_ORD_LoadKey ((TPM_COMMAND_CODE) 0x00000020) +#define TPM_ORD_LoadKey2 ((TPM_COMMAND_CODE) 0x00000041) +#define TPM_ORD_LoadKeyContext ((TPM_COMMAND_CODE) 0x000000B5) +#define TPM_ORD_LoadMaintenanceArchive ((TPM_COMMAND_CODE) 0x0000002D) +#define TPM_ORD_LoadManuMaintPub ((TPM_COMMAND_CODE) 0x0000002F) +#define TPM_ORD_MakeIdentity ((TPM_COMMAND_CODE) 0x00000079) +#define TPM_ORD_MigrateKey ((TPM_COMMAND_CODE) 0x00000025) +#define TPM_ORD_NV_DefineSpace ((TPM_COMMAND_CODE) 0x000000CC) +#define TPM_ORD_NV_ReadValue ((TPM_COMMAND_CODE) 0x000000CF) +#define TPM_ORD_NV_ReadValueAuth ((TPM_COMMAND_CODE) 0x000000D0) +#define TPM_ORD_NV_WriteValue ((TPM_COMMAND_CODE) 0x000000CD) +#define TPM_ORD_NV_WriteValueAuth ((TPM_COMMAND_CODE) 0x000000CE) +#define TPM_ORD_OIAP ((TPM_COMMAND_CODE) 0x0000000A) +#define TPM_ORD_OSAP ((TPM_COMMAND_CODE) 0x0000000B) +#define TPM_ORD_OwnerClear ((TPM_COMMAND_CODE) 0x0000005B) +#define TPM_ORD_OwnerReadInternalPub ((TPM_COMMAND_CODE) 0x00000081) +#define TPM_ORD_OwnerReadPubek ((TPM_COMMAND_CODE) 0x0000007D) +#define TPM_ORD_OwnerSetDisable ((TPM_COMMAND_CODE) 0x0000006E) +#define TPM_ORD_PCR_Reset ((TPM_COMMAND_CODE) 0x000000C8) +#define TPM_ORD_PcrRead ((TPM_COMMAND_CODE) 0x00000015) +#define TPM_ORD_PhysicalDisable ((TPM_COMMAND_CODE) 0x00000070) +#define TPM_ORD_PhysicalEnable ((TPM_COMMAND_CODE) 0x0000006F) +#define TPM_ORD_PhysicalSetDeactivated ((TPM_COMMAND_CODE) 0x00000072) +#define TPM_ORD_Quote ((TPM_COMMAND_CODE) 0x00000016) +#define TPM_ORD_Quote2 ((TPM_COMMAND_CODE) 0x0000003E) +#define TPM_ORD_ReadCounter ((TPM_COMMAND_CODE) 0x000000DE) +#define TPM_ORD_ReadManuMaintPub ((TPM_COMMAND_CODE) 0x00000030) +#define TPM_ORD_ReadPubek ((TPM_COMMAND_CODE) 0x0000007C) +#define TPM_ORD_ReleaseCounter ((TPM_COMMAND_CODE) 0x000000DF) +#define TPM_ORD_ReleaseCounterOwner ((TPM_COMMAND_CODE) 0x000000E0) +#define TPM_ORD_ReleaseTransportSigned ((TPM_COMMAND_CODE) 0x000000E8) +#define TPM_ORD_Reset ((TPM_COMMAND_CODE) 0x0000005A) +#define TPM_ORD_ResetLockValue ((TPM_COMMAND_CODE) 0x00000040) +#define TPM_ORD_RevokeTrust ((TPM_COMMAND_CODE) 0x00000080) +#define TPM_ORD_SaveAuthContext ((TPM_COMMAND_CODE) 0x000000B6) +#define TPM_ORD_SaveContext ((TPM_COMMAND_CODE) 0x000000B8) +#define TPM_ORD_SaveKeyContext ((TPM_COMMAND_CODE) 0x000000B4) +#define TPM_ORD_SaveState ((TPM_COMMAND_CODE) 0x00000098) +#define TPM_ORD_Seal ((TPM_COMMAND_CODE) 0x00000017) +#define TPM_ORD_Sealx ((TPM_COMMAND_CODE) 0x0000003D) +#define TPM_ORD_SelfTestFull ((TPM_COMMAND_CODE) 0x00000050) +#define TPM_ORD_SetCapability ((TPM_COMMAND_CODE) 0x0000003F) +#define TPM_ORD_SetOperatorAuth ((TPM_COMMAND_CODE) 0x00000074) +#define TPM_ORD_SetOrdinalAuditStatus ((TPM_COMMAND_CODE) 0x0000008D) +#define TPM_ORD_SetOwnerInstall ((TPM_COMMAND_CODE) 0x00000071) +#define TPM_ORD_SetOwnerPointer ((TPM_COMMAND_CODE) 0x00000075) +#define TPM_ORD_SetRedirection ((TPM_COMMAND_CODE) 0x0000009A) +#define TPM_ORD_SetTempDeactivated ((TPM_COMMAND_CODE) 0x00000073) +#define TPM_ORD_SHA1Complete ((TPM_COMMAND_CODE) 0x000000A2) +#define TPM_ORD_SHA1CompleteExtend ((TPM_COMMAND_CODE) 0x000000A3) +#define TPM_ORD_SHA1Start ((TPM_COMMAND_CODE) 0x000000A0) +#define TPM_ORD_SHA1Update ((TPM_COMMAND_CODE) 0x000000A1) +#define TPM_ORD_Sign ((TPM_COMMAND_CODE) 0x0000003C) +#define TPM_ORD_Startup ((TPM_COMMAND_CODE) 0x00000099) +#define TPM_ORD_StirRandom ((TPM_COMMAND_CODE) 0x00000047) +#define TPM_ORD_TakeOwnership ((TPM_COMMAND_CODE) 0x0000000D) +#define TPM_ORD_Terminate_Handle ((TPM_COMMAND_CODE) 0x00000096) +#define TPM_ORD_TickStampBlob ((TPM_COMMAND_CODE) 0x000000F2) +#define TPM_ORD_UnBind ((TPM_COMMAND_CODE) 0x0000001E) +#define TPM_ORD_Unseal ((TPM_COMMAND_CODE) 0x00000018) +#define TSC_ORD_PhysicalPresence ((TPM_COMMAND_CODE) 0x4000000A) +#define TSC_ORD_ResetEstablishmentBit ((TPM_COMMAND_CODE) 0x4000000B) + +// +// Part 2, section 18: Context structures +// + +/// +/// Part 2, section 18.1: TPM_CONTEXT_BLOB +/// +typedef struct tdTPM_CONTEXT_BLOB { + TPM_STRUCTURE_TAG tag; + TPM_RESOURCE_TYPE resourceType; + TPM_HANDLE handle; + UINT8 label[16]; + UINT32 contextCount; + TPM_DIGEST integrityDigest; + UINT32 additionalSize; + UINT8 *additionalData; + UINT32 sensitiveSize; + UINT8 *sensitiveData; +} TPM_CONTEXT_BLOB; + +/// +/// Part 2, section 18.2 TPM_CONTEXT_SENSITIVE +/// +typedef struct tdTPM_CONTEXT_SENSITIVE { + TPM_STRUCTURE_TAG tag; + TPM_NONCE contextNonce; + UINT32 internalSize; + UINT8 *internalData; +} TPM_CONTEXT_SENSITIVE; + +// +// Part 2, section 19: NV Structures +// + +// +// Part 2, section 19.1.1: Required TPM_NV_INDEX values +// +#define TPM_NV_INDEX_LOCK ((UINT32)0xffffffff) +#define TPM_NV_INDEX0 ((UINT32)0x00000000) +#define TPM_NV_INDEX_DIR ((UINT32)0x10000001) +#define TPM_NV_INDEX_EKCert ((UINT32)0x0000f000) +#define TPM_NV_INDEX_TPM_CC ((UINT32)0x0000f001) +#define TPM_NV_INDEX_PlatformCert ((UINT32)0x0000f002) +#define TPM_NV_INDEX_Platform_CC ((UINT32)0x0000f003) +// +// Part 2, section 19.1.2: Reserved Index values +// +#define TPM_NV_INDEX_TSS_BASE ((UINT32)0x00011100) +#define TPM_NV_INDEX_PC_BASE ((UINT32)0x00011200) +#define TPM_NV_INDEX_SERVER_BASE ((UINT32)0x00011300) +#define TPM_NV_INDEX_MOBILE_BASE ((UINT32)0x00011400) +#define TPM_NV_INDEX_PERIPHERAL_BASE ((UINT32)0x00011500) +#define TPM_NV_INDEX_GROUP_RESV_BASE ((UINT32)0x00010000) + +/// +/// Part 2, section 19.2: TPM_NV_ATTRIBUTES +/// +typedef struct tdTPM_NV_ATTRIBUTES { + TPM_STRUCTURE_TAG tag; + UINT32 attributes; +} TPM_NV_ATTRIBUTES; + +#define TPM_NV_PER_READ_STCLEAR (BIT31) +#define TPM_NV_PER_AUTHREAD (BIT18) +#define TPM_NV_PER_OWNERREAD (BIT17) +#define TPM_NV_PER_PPREAD (BIT16) +#define TPM_NV_PER_GLOBALLOCK (BIT15) +#define TPM_NV_PER_WRITE_STCLEAR (BIT14) +#define TPM_NV_PER_WRITEDEFINE (BIT13) +#define TPM_NV_PER_WRITEALL (BIT12) +#define TPM_NV_PER_AUTHWRITE (BIT2) +#define TPM_NV_PER_OWNERWRITE (BIT1) +#define TPM_NV_PER_PPWRITE (BIT0) + +/// +/// Part 2, section 19.3: TPM_NV_DATA_PUBLIC +/// +typedef struct tdTPM_NV_DATA_PUBLIC { + TPM_STRUCTURE_TAG tag; + TPM_NV_INDEX nvIndex; + TPM_PCR_INFO_SHORT pcrInfoRead; + TPM_PCR_INFO_SHORT pcrInfoWrite; + TPM_NV_ATTRIBUTES permission; + BOOLEAN bReadSTClear; + BOOLEAN bWriteSTClear; + BOOLEAN bWriteDefine; + UINT32 dataSize; +} TPM_NV_DATA_PUBLIC; + +// +// Part 2, section 20: Delegate Structures +// + +#define TPM_DEL_OWNER_BITS ((UINT32)0x00000001) +#define TPM_DEL_KEY_BITS ((UINT32)0x00000002) +/// +/// Part 2, section 20.2: Delegate Definitions +/// +typedef struct tdTPM_DELEGATIONS { + TPM_STRUCTURE_TAG tag; + UINT32 delegateType; + UINT32 per1; + UINT32 per2; +} TPM_DELEGATIONS; + +// +// Part 2, section 20.2.1: Owner Permission Settings +// +#define TPM_DELEGATE_SetOrdinalAuditStatus (BIT30) +#define TPM_DELEGATE_DirWriteAuth (BIT29) +#define TPM_DELEGATE_CMK_ApproveMA (BIT28) +#define TPM_DELEGATE_NV_WriteValue (BIT27) +#define TPM_DELEGATE_CMK_CreateTicket (BIT26) +#define TPM_DELEGATE_NV_ReadValue (BIT25) +#define TPM_DELEGATE_Delegate_LoadOwnerDelegation (BIT24) +#define TPM_DELEGATE_DAA_Join (BIT23) +#define TPM_DELEGATE_AuthorizeMigrationKey (BIT22) +#define TPM_DELEGATE_CreateMaintenanceArchive (BIT21) +#define TPM_DELEGATE_LoadMaintenanceArchive (BIT20) +#define TPM_DELEGATE_KillMaintenanceFeature (BIT19) +#define TPM_DELEGATE_OwnerReadInteralPub (BIT18) +#define TPM_DELEGATE_ResetLockValue (BIT17) +#define TPM_DELEGATE_OwnerClear (BIT16) +#define TPM_DELEGATE_DisableOwnerClear (BIT15) +#define TPM_DELEGATE_NV_DefineSpace (BIT14) +#define TPM_DELEGATE_OwnerSetDisable (BIT13) +#define TPM_DELEGATE_SetCapability (BIT12) +#define TPM_DELEGATE_MakeIdentity (BIT11) +#define TPM_DELEGATE_ActivateIdentity (BIT10) +#define TPM_DELEGATE_OwnerReadPubek (BIT9) +#define TPM_DELEGATE_DisablePubekRead (BIT8) +#define TPM_DELEGATE_SetRedirection (BIT7) +#define TPM_DELEGATE_FieldUpgrade (BIT6) +#define TPM_DELEGATE_Delegate_UpdateVerification (BIT5) +#define TPM_DELEGATE_CreateCounter (BIT4) +#define TPM_DELEGATE_ReleaseCounterOwner (BIT3) +#define TPM_DELEGATE_DelegateManage (BIT2) +#define TPM_DELEGATE_Delegate_CreateOwnerDelegation (BIT1) +#define TPM_DELEGATE_DAA_Sign (BIT0) + +// +// Part 2, section 20.2.3: Key Permission settings +// +#define TPM_KEY_DELEGATE_CMK_ConvertMigration (BIT28) +#define TPM_KEY_DELEGATE_TickStampBlob (BIT27) +#define TPM_KEY_DELEGATE_ChangeAuthAsymStart (BIT26) +#define TPM_KEY_DELEGATE_ChangeAuthAsymFinish (BIT25) +#define TPM_KEY_DELEGATE_CMK_CreateKey (BIT24) +#define TPM_KEY_DELEGATE_MigrateKey (BIT23) +#define TPM_KEY_DELEGATE_LoadKey2 (BIT22) +#define TPM_KEY_DELEGATE_EstablishTransport (BIT21) +#define TPM_KEY_DELEGATE_ReleaseTransportSigned (BIT20) +#define TPM_KEY_DELEGATE_Quote2 (BIT19) +#define TPM_KEY_DELEGATE_Sealx (BIT18) +#define TPM_KEY_DELEGATE_MakeIdentity (BIT17) +#define TPM_KEY_DELEGATE_ActivateIdentity (BIT16) +#define TPM_KEY_DELEGATE_GetAuditDigestSigned (BIT15) +#define TPM_KEY_DELEGATE_Sign (BIT14) +#define TPM_KEY_DELEGATE_CertifyKey2 (BIT13) +#define TPM_KEY_DELEGATE_CertifyKey (BIT12) +#define TPM_KEY_DELEGATE_CreateWrapKey (BIT11) +#define TPM_KEY_DELEGATE_CMK_CreateBlob (BIT10) +#define TPM_KEY_DELEGATE_CreateMigrationBlob (BIT9) +#define TPM_KEY_DELEGATE_ConvertMigrationBlob (BIT8) +#define TPM_KEY_DELEGATE_CreateKeyDelegation (BIT7) +#define TPM_KEY_DELEGATE_ChangeAuth (BIT6) +#define TPM_KEY_DELEGATE_GetPubKey (BIT5) +#define TPM_KEY_DELEGATE_UnBind (BIT4) +#define TPM_KEY_DELEGATE_Quote (BIT3) +#define TPM_KEY_DELEGATE_Unseal (BIT2) +#define TPM_KEY_DELEGATE_Seal (BIT1) +#define TPM_KEY_DELEGATE_LoadKey (BIT0) + +// +// Part 2, section 20.3: TPM_FAMILY_FLAGS +// +#define TPM_DELEGATE_ADMIN_LOCK (BIT1) +#define TPM_FAMFLAG_ENABLE (BIT0) + +/// +/// Part 2, section 20.4: TPM_FAMILY_LABEL +/// +typedef struct tdTPM_FAMILY_LABEL { + UINT8 label; +} TPM_FAMILY_LABEL; + +/// +/// Part 2, section 20.5: TPM_FAMILY_TABLE_ENTRY +/// +typedef struct tdTPM_FAMILY_TABLE_ENTRY { + TPM_STRUCTURE_TAG tag; + TPM_FAMILY_LABEL label; + TPM_FAMILY_ID familyID; + TPM_FAMILY_VERIFICATION verificationCount; + TPM_FAMILY_FLAGS flags; +} TPM_FAMILY_TABLE_ENTRY; + +// +// Part 2, section 20.6: TPM_FAMILY_TABLE +// +#define TPM_NUM_FAMILY_TABLE_ENTRY_MIN 8 + +typedef struct tdTPM_FAMILY_TABLE{ + TPM_FAMILY_TABLE_ENTRY famTableRow[TPM_NUM_FAMILY_TABLE_ENTRY_MIN]; +} TPM_FAMILY_TABLE; + +/// +/// Part 2, section 20.7: TPM_DELEGATE_LABEL +/// +typedef struct tdTPM_DELEGATE_LABEL { + UINT8 label; +} TPM_DELEGATE_LABEL; + +/// +/// Part 2, section 20.8: TPM_DELEGATE_PUBLIC +/// +typedef struct tdTPM_DELEGATE_PUBLIC { + TPM_STRUCTURE_TAG tag; + TPM_DELEGATE_LABEL label; + TPM_PCR_INFO_SHORT pcrInfo; + TPM_DELEGATIONS permissions; + TPM_FAMILY_ID familyID; + TPM_FAMILY_VERIFICATION verificationCount; +} TPM_DELEGATE_PUBLIC; + +/// +/// Part 2, section 20.9: TPM_DELEGATE_TABLE_ROW +/// +typedef struct tdTPM_DELEGATE_TABLE_ROW { + TPM_STRUCTURE_TAG tag; + TPM_DELEGATE_PUBLIC pub; + TPM_SECRET authValue; +} TPM_DELEGATE_TABLE_ROW; + +// +// Part 2, section 20.10: TPM_DELEGATE_TABLE +// +#define TPM_NUM_DELEGATE_TABLE_ENTRY_MIN 2 + +typedef struct tdTPM_DELEGATE_TABLE{ + TPM_DELEGATE_TABLE_ROW delRow[TPM_NUM_DELEGATE_TABLE_ENTRY_MIN]; +} TPM_DELEGATE_TABLE; + +/// +/// Part 2, section 20.11: TPM_DELEGATE_SENSITIVE +/// +typedef struct tdTPM_DELEGATE_SENSITIVE { + TPM_STRUCTURE_TAG tag; + TPM_SECRET authValue; +} TPM_DELEGATE_SENSITIVE; + +/// +/// Part 2, section 20.12: TPM_DELEGATE_OWNER_BLOB +/// +typedef struct tdTPM_DELEGATE_OWNER_BLOB { + TPM_STRUCTURE_TAG tag; + TPM_DELEGATE_PUBLIC pub; + TPM_DIGEST integrityDigest; + UINT32 additionalSize; + UINT8 *additionalArea; + UINT32 sensitiveSize; + UINT8 *sensitiveArea; +} TPM_DELEGATE_OWNER_BLOB; + +/// +/// Part 2, section 20.13: TTPM_DELEGATE_KEY_BLOB +/// +typedef struct tdTPM_DELEGATE_KEY_BLOB { + TPM_STRUCTURE_TAG tag; + TPM_DELEGATE_PUBLIC pub; + TPM_DIGEST integrityDigest; + TPM_DIGEST pubKeyDigest; + UINT32 additionalSize; + UINT8 *additionalArea; + UINT32 sensitiveSize; + UINT8 *sensitiveArea; +} TPM_DELEGATE_KEY_BLOB; + +// +// Part 2, section 20.14: TPM_FAMILY_OPERATION Values +// +#define TPM_FAMILY_CREATE ((UINT32)0x00000001) +#define TPM_FAMILY_ENABLE ((UINT32)0x00000002) +#define TPM_FAMILY_ADMIN ((UINT32)0x00000003) +#define TPM_FAMILY_INVALIDATE ((UINT32)0x00000004) + +// +// Part 2, section 21.1: TPM_CAPABILITY_AREA for GetCapability +// +#define TPM_CAP_ORD ((TPM_CAPABILITY_AREA) 0x00000001) +#define TPM_CAP_ALG ((TPM_CAPABILITY_AREA) 0x00000002) +#define TPM_CAP_PID ((TPM_CAPABILITY_AREA) 0x00000003) +#define TPM_CAP_FLAG ((TPM_CAPABILITY_AREA) 0x00000004) +#define TPM_CAP_PROPERTY ((TPM_CAPABILITY_AREA) 0x00000005) +#define TPM_CAP_VERSION ((TPM_CAPABILITY_AREA) 0x00000006) +#define TPM_CAP_KEY_HANDLE ((TPM_CAPABILITY_AREA) 0x00000007) +#define TPM_CAP_CHECK_LOADED ((TPM_CAPABILITY_AREA) 0x00000008) +#define TPM_CAP_SYM_MODE ((TPM_CAPABILITY_AREA) 0x00000009) +#define TPM_CAP_KEY_STATUS ((TPM_CAPABILITY_AREA) 0x0000000C) +#define TPM_CAP_NV_LIST ((TPM_CAPABILITY_AREA) 0x0000000D) +#define TPM_CAP_MFR ((TPM_CAPABILITY_AREA) 0x00000010) +#define TPM_CAP_NV_INDEX ((TPM_CAPABILITY_AREA) 0x00000011) +#define TPM_CAP_TRANS_ALG ((TPM_CAPABILITY_AREA) 0x00000012) +#define TPM_CAP_HANDLE ((TPM_CAPABILITY_AREA) 0x00000014) +#define TPM_CAP_TRANS_ES ((TPM_CAPABILITY_AREA) 0x00000015) +#define TPM_CAP_AUTH_ENCRYPT ((TPM_CAPABILITY_AREA) 0x00000017) +#define TPM_CAP_SELECT_SIZE ((TPM_CAPABILITY_AREA) 0x00000018) +#define TPM_CAP_VERSION_VAL ((TPM_CAPABILITY_AREA) 0x0000001A) + +#define TPM_CAP_FLAG_PERMANENT ((TPM_CAPABILITY_AREA) 0x00000108) +#define TPM_CAP_FLAG_VOLATILE ((TPM_CAPABILITY_AREA) 0x00000109) + +// +// Part 2, section 21.2: CAP_PROPERTY Subcap values for GetCapability +// +#define TPM_CAP_PROP_PCR ((TPM_CAPABILITY_AREA) 0x00000101) +#define TPM_CAP_PROP_DIR ((TPM_CAPABILITY_AREA) 0x00000102) +#define TPM_CAP_PROP_MANUFACTURER ((TPM_CAPABILITY_AREA) 0x00000103) +#define TPM_CAP_PROP_KEYS ((TPM_CAPABILITY_AREA) 0x00000104) +#define TPM_CAP_PROP_MIN_COUNTER ((TPM_CAPABILITY_AREA) 0x00000107) +#define TPM_CAP_PROP_AUTHSESS ((TPM_CAPABILITY_AREA) 0x0000010A) +#define TPM_CAP_PROP_TRANSESS ((TPM_CAPABILITY_AREA) 0x0000010B) +#define TPM_CAP_PROP_COUNTERS ((TPM_CAPABILITY_AREA) 0x0000010C) +#define TPM_CAP_PROP_MAX_AUTHSESS ((TPM_CAPABILITY_AREA) 0x0000010D) +#define TPM_CAP_PROP_MAX_TRANSESS ((TPM_CAPABILITY_AREA) 0x0000010E) +#define TPM_CAP_PROP_MAX_COUNTERS ((TPM_CAPABILITY_AREA) 0x0000010F) +#define TPM_CAP_PROP_MAX_KEYS ((TPM_CAPABILITY_AREA) 0x00000110) +#define TPM_CAP_PROP_OWNER ((TPM_CAPABILITY_AREA) 0x00000111) +#define TPM_CAP_PROP_CONTEXT ((TPM_CAPABILITY_AREA) 0x00000112) +#define TPM_CAP_PROP_MAX_CONTEXT ((TPM_CAPABILITY_AREA) 0x00000113) +#define TPM_CAP_PROP_FAMILYROWS ((TPM_CAPABILITY_AREA) 0x00000114) +#define TPM_CAP_PROP_TIS_TIMEOUT ((TPM_CAPABILITY_AREA) 0x00000115) +#define TPM_CAP_PROP_STARTUP_EFFECT ((TPM_CAPABILITY_AREA) 0x00000116) +#define TPM_CAP_PROP_DELEGATE_ROW ((TPM_CAPABILITY_AREA) 0x00000117) +#define TPM_CAP_PROP_DAA_MAX ((TPM_CAPABILITY_AREA) 0x00000119) +#define CAP_PROP_SESSION_DAA ((TPM_CAPABILITY_AREA) 0x0000011A) +#define TPM_CAP_PROP_CONTEXT_DIST ((TPM_CAPABILITY_AREA) 0x0000011B) +#define TPM_CAP_PROP_DAA_INTERRUPT ((TPM_CAPABILITY_AREA) 0x0000011C) +#define TPM_CAP_PROP_SESSIONS ((TPM_CAPABILITY_AREA) 0x0000011D) +#define TPM_CAP_PROP_MAX_SESSIONS ((TPM_CAPABILITY_AREA) 0x0000011E) +#define TPM_CAP_PROP_CMK_RESTRICTION ((TPM_CAPABILITY_AREA) 0x0000011F) +#define TPM_CAP_PROP_DURATION ((TPM_CAPABILITY_AREA) 0x00000120) +#define TPM_CAP_PROP_ACTIVE_COUNTER ((TPM_CAPABILITY_AREA) 0x00000122) +#define TPM_CAP_PROP_MAX_NV_AVAILABLE ((TPM_CAPABILITY_AREA) 0x00000123) +#define TPM_CAP_PROP_INPUT_BUFFER ((TPM_CAPABILITY_AREA) 0x00000124) + +// +// Part 2, section 21.4: TPM_CAPABILITY_AREA for SetCapability +// +#define TPM_SET_PERM_FLAGS ((TPM_CAPABILITY_AREA) 0x00000001) +#define TPM_SET_PERM_DATA ((TPM_CAPABILITY_AREA) 0x00000002) +#define TPM_SET_STCLEAR_FLAGS ((TPM_CAPABILITY_AREA) 0x00000003) +#define TPM_SET_STCLEAR_DATA ((TPM_CAPABILITY_AREA) 0x00000004) +#define TPM_SET_STANY_FLAGS ((TPM_CAPABILITY_AREA) 0x00000005) +#define TPM_SET_STANY_DATA ((TPM_CAPABILITY_AREA) 0x00000006) + +/// +/// Part 2, section 21.6: TPM_CAP_VERSION_INFO +/// [size_is(vendorSpecificSize)] BYTE* vendorSpecific; +/// +typedef struct tdTPM_CAP_VERSION_INFO { + TPM_STRUCTURE_TAG tag; + TPM_VERSION version; + UINT16 specLevel; + UINT8 errataRev; + UINT8 tpmVendorID[4]; + UINT16 vendorSpecificSize; + UINT8 *vendorSpecific; +} TPM_CAP_VERSION_INFO; + +/// +/// Part 2, section 21.10: TPM_DA_ACTION_TYPE +/// +typedef struct tdTPM_DA_ACTION_TYPE { + TPM_STRUCTURE_TAG tag; + UINT32 actions; +} TPM_DA_ACTION_TYPE; + +#define TPM_DA_ACTION_FAILURE_MODE (((UINT32)1)<<3) +#define TPM_DA_ACTION_DEACTIVATE (((UINT32)1)<<2) +#define TPM_DA_ACTION_DISABLE (((UINT32)1)<<1) +#define TPM_DA_ACTION_TIMEOUT (((UINT32)1)<<0) + +/// +/// Part 2, section 21.7: TPM_DA_INFO +/// +typedef struct tdTPM_DA_INFO { + TPM_STRUCTURE_TAG tag; + TPM_DA_STATE state; + UINT16 currentCount; + UINT16 thresholdCount; + TPM_DA_ACTION_TYPE actionAtThreshold; + UINT32 actionDependValue; + UINT32 vendorDataSize; + UINT8 *vendorData; +} TPM_DA_INFO; + +/// +/// Part 2, section 21.8: TPM_DA_INFO_LIMITED +/// +typedef struct tdTPM_DA_INFO_LIMITED { + TPM_STRUCTURE_TAG tag; + TPM_DA_STATE state; + TPM_DA_ACTION_TYPE actionAtThreshold; + UINT32 vendorDataSize; + UINT8 *vendorData; +} TPM_DA_INFO_LIMITED; + +// +// Part 2, section 21.9: CAP_PROPERTY Subcap values for GetCapability +// +#define TPM_DA_STATE_INACTIVE ((UINT8)0x00) +#define TPM_DA_STATE_ACTIVE ((UINT8)0x01) + +// +// Part 2, section 22: DAA Structures +// + +// +// Part 2, section 22.1: Size definitions +// +#define TPM_DAA_SIZE_r0 (43) +#define TPM_DAA_SIZE_r1 (43) +#define TPM_DAA_SIZE_r2 (128) +#define TPM_DAA_SIZE_r3 (168) +#define TPM_DAA_SIZE_r4 (219) +#define TPM_DAA_SIZE_NT (20) +#define TPM_DAA_SIZE_v0 (128) +#define TPM_DAA_SIZE_v1 (192) +#define TPM_DAA_SIZE_NE (256) +#define TPM_DAA_SIZE_w (256) +#define TPM_DAA_SIZE_issuerModulus (256) +// +// Part 2, section 22.2: Constant definitions +// +#define TPM_DAA_power0 (104) +#define TPM_DAA_power1 (1024) + +/// +/// Part 2, section 22.3: TPM_DAA_ISSUER +/// +typedef struct tdTPM_DAA_ISSUER { + TPM_STRUCTURE_TAG tag; + TPM_DIGEST DAA_digest_R0; + TPM_DIGEST DAA_digest_R1; + TPM_DIGEST DAA_digest_S0; + TPM_DIGEST DAA_digest_S1; + TPM_DIGEST DAA_digest_n; + TPM_DIGEST DAA_digest_gamma; + UINT8 DAA_generic_q[26]; +} TPM_DAA_ISSUER; + +/// +/// Part 2, section 22.4: TPM_DAA_TPM +/// +typedef struct tdTPM_DAA_TPM { + TPM_STRUCTURE_TAG tag; + TPM_DIGEST DAA_digestIssuer; + TPM_DIGEST DAA_digest_v0; + TPM_DIGEST DAA_digest_v1; + TPM_DIGEST DAA_rekey; + UINT32 DAA_count; +} TPM_DAA_TPM; + +/// +/// Part 2, section 22.5: TPM_DAA_CONTEXT +/// +typedef struct tdTPM_DAA_CONTEXT { + TPM_STRUCTURE_TAG tag; + TPM_DIGEST DAA_digestContext; + TPM_DIGEST DAA_digest; + TPM_DAA_CONTEXT_SEED DAA_contextSeed; + UINT8 DAA_scratch[256]; + UINT8 DAA_stage; +} TPM_DAA_CONTEXT; + +/// +/// Part 2, section 22.6: TPM_DAA_JOINDATA +/// +typedef struct tdTPM_DAA_JOINDATA { + UINT8 DAA_join_u0[128]; + UINT8 DAA_join_u1[138]; + TPM_DIGEST DAA_digest_n0; +} TPM_DAA_JOINDATA; + +/// +/// Part 2, section 22.8: TPM_DAA_BLOB +/// +typedef struct tdTPM_DAA_BLOB { + TPM_STRUCTURE_TAG tag; + TPM_RESOURCE_TYPE resourceType; + UINT8 label[16]; + TPM_DIGEST blobIntegrity; + UINT32 additionalSize; + UINT8 *additionalData; + UINT32 sensitiveSize; + UINT8 *sensitiveData; +} TPM_DAA_BLOB; + +/// +/// Part 2, section 22.9: TPM_DAA_SENSITIVE +/// +typedef struct tdTPM_DAA_SENSITIVE { + TPM_STRUCTURE_TAG tag; + UINT32 internalSize; + UINT8 *internalData; +} TPM_DAA_SENSITIVE; + + +// +// Part 2, section 23: Redirection +// + +/// +/// Part 2 section 23.1: TPM_REDIR_COMMAND +/// This section defines exactly one value but does not +/// give it a name. The definition of TPM_SetRedirection in Part3 +/// refers to exactly one name but does not give its value. We join +/// them here. +/// +#define TPM_REDIR_GPIO (0x00000001) + +/// +/// TPM Command Headers defined in Part 3 +/// +typedef struct tdTPM_RQU_COMMAND_HDR { + TPM_STRUCTURE_TAG tag; + UINT32 paramSize; + TPM_COMMAND_CODE ordinal; +} TPM_RQU_COMMAND_HDR; + +/// +/// TPM Response Headers defined in Part 3 +/// +typedef struct tdTPM_RSP_COMMAND_HDR { + TPM_STRUCTURE_TAG tag; + UINT32 paramSize; + TPM_RESULT returnCode; +} TPM_RSP_COMMAND_HDR; + +#pragma pack () + +#endif diff --git a/roms/ipxe/src/include/ipxe/efi/IndustryStandard/UefiTcgPlatform.h b/roms/ipxe/src/include/ipxe/efi/IndustryStandard/UefiTcgPlatform.h new file mode 100644 index 0000000..8bb7ea3 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/efi/IndustryStandard/UefiTcgPlatform.h @@ -0,0 +1,172 @@ +/** @file + TCG EFI Platform Definition in TCG_EFI_Platform_1_20_Final + + Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.
    + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __UEFI_TCG_PLATFORM_H__ +#define __UEFI_TCG_PLATFORM_H__ + +FILE_LICENCE ( BSD3 ); + +#include +#include + +// +// Standard event types +// +#define EV_POST_CODE ((TCG_EVENTTYPE) 0x00000001) +#define EV_SEPARATOR ((TCG_EVENTTYPE) 0x00000004) +#define EV_S_CRTM_CONTENTS ((TCG_EVENTTYPE) 0x00000007) +#define EV_S_CRTM_VERSION ((TCG_EVENTTYPE) 0x00000008) +#define EV_CPU_MICROCODE ((TCG_EVENTTYPE) 0x00000009) +#define EV_TABLE_OF_DEVICES ((TCG_EVENTTYPE) 0x0000000B) + +// +// EFI specific event types +// +#define EV_EFI_EVENT_BASE ((TCG_EVENTTYPE) 0x80000000) +#define EV_EFI_VARIABLE_DRIVER_CONFIG (EV_EFI_EVENT_BASE + 1) +#define EV_EFI_VARIABLE_BOOT (EV_EFI_EVENT_BASE + 2) +#define EV_EFI_BOOT_SERVICES_APPLICATION (EV_EFI_EVENT_BASE + 3) +#define EV_EFI_BOOT_SERVICES_DRIVER (EV_EFI_EVENT_BASE + 4) +#define EV_EFI_RUNTIME_SERVICES_DRIVER (EV_EFI_EVENT_BASE + 5) +#define EV_EFI_GPT_EVENT (EV_EFI_EVENT_BASE + 6) +#define EV_EFI_ACTION (EV_EFI_EVENT_BASE + 7) +#define EV_EFI_PLATFORM_FIRMWARE_BLOB (EV_EFI_EVENT_BASE + 8) +#define EV_EFI_HANDOFF_TABLES (EV_EFI_EVENT_BASE + 9) + +#define EFI_CALLING_EFI_APPLICATION \ + "Calling EFI Application from Boot Option" +#define EFI_RETURNING_FROM_EFI_APPLICATOIN \ + "Returning from EFI Application from Boot Option" +#define EFI_EXIT_BOOT_SERVICES_INVOCATION \ + "Exit Boot Services Invocation" +#define EFI_EXIT_BOOT_SERVICES_FAILED \ + "Exit Boot Services Returned with Failure" +#define EFI_EXIT_BOOT_SERVICES_SUCCEEDED \ + "Exit Boot Services Returned with Success" + + +#define EV_POSTCODE_INFO_POST_CODE "POST CODE" +#define POST_CODE_STR_LEN (sizeof(EV_POSTCODE_INFO_POST_CODE) - 1) + +#define EV_POSTCODE_INFO_SMM_CODE "SMM CODE" +#define SMM_CODE_STR_LEN (sizeof(EV_POSTCODE_INFO_SMM_CODE) - 1) + +#define EV_POSTCODE_INFO_ACPI_DATA "ACPI DATA" +#define ACPI_DATA_LEN (sizeof(EV_POSTCODE_INFO_ACPI_DATA) - 1) + +#define EV_POSTCODE_INFO_BIS_CODE "BIS CODE" +#define BIS_CODE_LEN (sizeof(EV_POSTCODE_INFO_BIS_CODE) - 1) + +#define EV_POSTCODE_INFO_UEFI_PI "UEFI PI" +#define UEFI_PI_LEN (sizeof(EV_POSTCODE_INFO_UEFI_PI) - 1) + +#define EV_POSTCODE_INFO_OPROM "Embedded Option ROM" +#define OPROM_LEN (sizeof(EV_POSTCODE_INFO_OPROM) - 1) + +// +// Set structure alignment to 1-byte +// +#pragma pack (1) + +typedef UINT32 TCG_EVENTTYPE; +typedef TPM_PCRINDEX TCG_PCRINDEX; +typedef TPM_DIGEST TCG_DIGEST; +/// +/// Event Log Entry Structure Definition +/// +typedef struct tdTCG_PCR_EVENT { + TCG_PCRINDEX PCRIndex; ///< PCRIndex event extended to + TCG_EVENTTYPE EventType; ///< TCG EFI event type + TCG_DIGEST Digest; ///< Value extended into PCRIndex + UINT32 EventSize; ///< Size of the event data + UINT8 Event[1]; ///< The event data +} TCG_PCR_EVENT; + +#define TSS_EVENT_DATA_MAX_SIZE 256 + +/// +/// TCG_PCR_EVENT_HDR +/// +typedef struct tdTCG_PCR_EVENT_HDR { + TCG_PCRINDEX PCRIndex; + TCG_EVENTTYPE EventType; + TCG_DIGEST Digest; + UINT32 EventSize; +} TCG_PCR_EVENT_HDR; + +/// +/// EFI_PLATFORM_FIRMWARE_BLOB +/// +/// BlobLength should be of type UINTN but we use UINT64 here +/// because PEI is 32-bit while DXE is 64-bit on x64 platforms +/// +typedef struct tdEFI_PLATFORM_FIRMWARE_BLOB { + EFI_PHYSICAL_ADDRESS BlobBase; + UINT64 BlobLength; +} EFI_PLATFORM_FIRMWARE_BLOB; + +/// +/// EFI_IMAGE_LOAD_EVENT +/// +/// This structure is used in EV_EFI_BOOT_SERVICES_APPLICATION, +/// EV_EFI_BOOT_SERVICES_DRIVER and EV_EFI_RUNTIME_SERVICES_DRIVER +/// +typedef struct tdEFI_IMAGE_LOAD_EVENT { + EFI_PHYSICAL_ADDRESS ImageLocationInMemory; + UINTN ImageLengthInMemory; + UINTN ImageLinkTimeAddress; + UINTN LengthOfDevicePath; + EFI_DEVICE_PATH_PROTOCOL DevicePath[1]; +} EFI_IMAGE_LOAD_EVENT; + +/// +/// EFI_HANDOFF_TABLE_POINTERS +/// +/// This structure is used in EV_EFI_HANDOFF_TABLES event to facilitate +/// the measurement of given configuration tables. +/// +typedef struct tdEFI_HANDOFF_TABLE_POINTERS { + UINTN NumberOfTables; + EFI_CONFIGURATION_TABLE TableEntry[1]; +} EFI_HANDOFF_TABLE_POINTERS; + +/// +/// EFI_VARIABLE_DATA +/// +/// This structure serves as the header for measuring variables. The name of the +/// variable (in Unicode format) should immediately follow, then the variable +/// data. +/// +typedef struct tdEFI_VARIABLE_DATA { + EFI_GUID VariableName; + UINTN UnicodeNameLength; + UINTN VariableDataLength; + CHAR16 UnicodeName[1]; + INT8 VariableData[1]; ///< Driver or platform-specific data +} EFI_VARIABLE_DATA; + +typedef struct tdEFI_GPT_DATA { + EFI_PARTITION_TABLE_HEADER EfiPartitionHeader; + UINTN NumberOfPartitions; + EFI_PARTITION_ENTRY Partitions[1]; +} EFI_GPT_DATA; + +// +// Restore original structure alignment +// +#pragma pack () + +#endif + + diff --git a/roms/ipxe/src/include/ipxe/efi/Library/BaseLib.h b/roms/ipxe/src/include/ipxe/efi/Library/BaseLib.h index be521f9..e9c31d1 100644 --- a/roms/ipxe/src/include/ipxe/efi/Library/BaseLib.h +++ b/roms/ipxe/src/include/ipxe/efi/Library/BaseLib.h @@ -2,7 +2,7 @@ Provides string functions, linked list functions, math functions, synchronization functions, and CPU architecture-specific functions. -Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.
    +Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.
    Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
    This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License @@ -148,6 +148,39 @@ typedef struct { #endif // defined (MDE_CPU_ARM) +#if defined (MDE_CPU_AARCH64) +typedef struct { + // GP regs + UINT64 X19; + UINT64 X20; + UINT64 X21; + UINT64 X22; + UINT64 X23; + UINT64 X24; + UINT64 X25; + UINT64 X26; + UINT64 X27; + UINT64 X28; + UINT64 FP; + UINT64 LR; + UINT64 IP0; + + // FP regs + UINT64 D8; + UINT64 D9; + UINT64 D10; + UINT64 D11; + UINT64 D12; + UINT64 D13; + UINT64 D14; + UINT64 D15; +} BASE_LIBRARY_JUMP_BUFFER; + +#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT 8 + +#endif // defined (MDE_CPU_AARCH64) + + // // String Services // @@ -1359,7 +1392,7 @@ InsertTailList ( @param List A pointer to the head node of a doubly linked list. @return The first node of a doubly linked list. - @retval NULL The list is empty. + @retval List The list is empty. **/ LIST_ENTRY * diff --git a/roms/ipxe/src/include/ipxe/efi/Pi/PiDxeCis.h b/roms/ipxe/src/include/ipxe/efi/Pi/PiDxeCis.h index e007cab..50d25f2 100644 --- a/roms/ipxe/src/include/ipxe/efi/Pi/PiDxeCis.h +++ b/roms/ipxe/src/include/ipxe/efi/Pi/PiDxeCis.h @@ -1,7 +1,7 @@ /** @file Include file matches things in PI. -Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
    +Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.
    This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License that accompanies this distribution. The full text of the license may be found at @@ -659,7 +659,7 @@ EFI_STATUS // #define DXE_SERVICES_SIGNATURE 0x565245535f455844ULL #define DXE_SPECIFICATION_MAJOR_REVISION 1 -#define DXE_SPECIFICATION_MINOR_REVISION 20 +#define DXE_SPECIFICATION_MINOR_REVISION 30 #define DXE_SERVICES_REVISION ((DXE_SPECIFICATION_MAJOR_REVISION<<16) | (DXE_SPECIFICATION_MINOR_REVISION)) typedef struct { diff --git a/roms/ipxe/src/include/ipxe/efi/Pi/PiFirmwareVolume.h b/roms/ipxe/src/include/ipxe/efi/Pi/PiFirmwareVolume.h index 4863e14..e818861 100644 --- a/roms/ipxe/src/include/ipxe/efi/Pi/PiFirmwareVolume.h +++ b/roms/ipxe/src/include/ipxe/efi/Pi/PiFirmwareVolume.h @@ -1,7 +1,7 @@ /** @file The firmware volume related definitions in PI. - Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
    + Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.
    This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. @par Revision Reference: - PI Version 1.2C + PI Version 1.3 **/ @@ -88,7 +88,7 @@ typedef UINT32 EFI_FVB_ATTRIBUTES_2; #define EFI_FVB2_ALIGNMENT_512M 0x001D0000 #define EFI_FVB2_ALIGNMENT_1G 0x001E0000 #define EFI_FVB2_ALIGNMENT_2G 0x001F0000 - +#define EFI_FVB2_WEAK_ALIGNMENT 0x80000000 typedef struct { /// diff --git a/roms/ipxe/src/include/ipxe/efi/Pi/PiMultiPhase.h b/roms/ipxe/src/include/ipxe/efi/Pi/PiMultiPhase.h index d5d7aaa..daf6591 100644 --- a/roms/ipxe/src/include/ipxe/efi/Pi/PiMultiPhase.h +++ b/roms/ipxe/src/include/ipxe/efi/Pi/PiMultiPhase.h @@ -1,7 +1,7 @@ /** @file Include file matches things in PI for multiple module types. -Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
    +Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.
    This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License that accompanies this distribution. The full text of the license may be found at @@ -135,4 +135,33 @@ typedef struct { UINT64 RegionState; } EFI_SMRAM_DESCRIPTOR; +typedef enum { + EFI_PCD_TYPE_8, + EFI_PCD_TYPE_16, + EFI_PCD_TYPE_32, + EFI_PCD_TYPE_64, + EFI_PCD_TYPE_BOOL, + EFI_PCD_TYPE_PTR +} EFI_PCD_TYPE; + +typedef struct { + /// + /// The returned information associated with the requested TokenNumber. If + /// TokenNumber is 0, then PcdType is set to EFI_PCD_TYPE_8. + /// + EFI_PCD_TYPE PcdType; + /// + /// The size of the data in bytes associated with the TokenNumber specified. If + /// TokenNumber is 0, then PcdSize is set 0. + /// + UINTN PcdSize; + /// + /// The null-terminated ASCII string associated with a given token. If the + /// TokenNumber specified was 0, then this field corresponds to the null-terminated + /// ASCII string associated with the token's namespace Guid. If NULL, there is no + /// name associated with this request. + /// + CHAR8 *PcdName; +} EFI_PCD_INFO; + #endif diff --git a/roms/ipxe/src/include/ipxe/efi/Pi/PiStatusCode.h b/roms/ipxe/src/include/ipxe/efi/Pi/PiStatusCode.h index 4c72052..5bef98f 100644 --- a/roms/ipxe/src/include/ipxe/efi/Pi/PiStatusCode.h +++ b/roms/ipxe/src/include/ipxe/efi/Pi/PiStatusCode.h @@ -1,7 +1,7 @@ /** @file StatusCode related definitions in PI. -Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.
    +Copyright (c) 2009 - 2013, Intel Corporation. All rights reserved.
    This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License that accompanies this distribution. The full text of the license may be found at @@ -598,7 +598,10 @@ typedef struct { // // IO Bus Class ATA/ATAPI Subclass Progress Code definitions. // - +#define EFI_IOB_ATA_BUS_SMART_ENABLE (EFI_SUBCLASS_SPECIFIC | 0x00000000) +#define EFI_IOB_ATA_BUS_SMART_DISABLE (EFI_SUBCLASS_SPECIFIC | 0x00000001) +#define EFI_IOB_ATA_BUS_SMART_OVERTHRESHOLD (EFI_SUBCLASS_SPECIFIC | 0x00000002) +#define EFI_IOB_ATA_BUS_SMART_UNDERTHRESHOLD (EFI_SUBCLASS_SPECIFIC | 0x00000003) // // IO Bus Class FC Subclass Progress Code definitions. // @@ -671,6 +674,8 @@ typedef struct { // // IO Bus Class ATA/ATAPI Subclass Error Code definitions. // +#define EFI_IOB_ATA_BUS_SMART_NOTSUPPORTED (EFI_SUBCLASS_SPECIFIC | 0x00000000) +#define EFI_IOB_ATA_BUS_SMART_DISABLED (EFI_SUBCLASS_SPECIFIC | 0x00000001) // // IO Bus Class FC Subclass Error Code definitions. diff --git a/roms/ipxe/src/include/ipxe/efi/Protocol/Arp.h b/roms/ipxe/src/include/ipxe/efi/Protocol/Arp.h new file mode 100644 index 0000000..80921f9 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/efi/Protocol/Arp.h @@ -0,0 +1,387 @@ +/** @file + EFI ARP Protocol Definition + + The EFI ARP Service Binding Protocol is used to locate EFI + ARP Protocol drivers to create and destroy child of the + driver to communicate with other host using ARP protocol. + The EFI ARP Protocol provides services to map IP network + address to hardware address used by a data link protocol. + +Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
    +This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + @par Revision Reference: + This Protocol was introduced in UEFI Specification 2.0. + +**/ + +#ifndef __EFI_ARP_PROTOCOL_H__ +#define __EFI_ARP_PROTOCOL_H__ + +FILE_LICENCE ( BSD3 ); + +#define EFI_ARP_SERVICE_BINDING_PROTOCOL_GUID \ + { \ + 0xf44c00ee, 0x1f2c, 0x4a00, {0xaa, 0x9, 0x1c, 0x9f, 0x3e, 0x8, 0x0, 0xa3 } \ + } + +#define EFI_ARP_PROTOCOL_GUID \ + { \ + 0xf4b427bb, 0xba21, 0x4f16, {0xbc, 0x4e, 0x43, 0xe4, 0x16, 0xab, 0x61, 0x9c } \ + } + +typedef struct _EFI_ARP_PROTOCOL EFI_ARP_PROTOCOL; + +typedef struct { + /// + /// Length in bytes of this entry. + /// + UINT32 Size; + + /// + /// Set to TRUE if this entry is a "deny" entry. + /// Set to FALSE if this entry is a "normal" entry. + /// + BOOLEAN DenyFlag; + + /// + /// Set to TRUE if this entry will not time out. + /// Set to FALSE if this entry will time out. + /// + BOOLEAN StaticFlag; + + /// + /// 16-bit ARP hardware identifier number. + /// + UINT16 HwAddressType; + + /// + /// 16-bit protocol type number. + /// + UINT16 SwAddressType; + + /// + /// The length of the hardware address. + /// + UINT8 HwAddressLength; + + /// + /// The length of the protocol address. + /// + UINT8 SwAddressLength; +} EFI_ARP_FIND_DATA; + +typedef struct { + /// + /// 16-bit protocol type number in host byte order. + /// + UINT16 SwAddressType; + + /// + /// The length in bytes of the station's protocol address to register. + /// + UINT8 SwAddressLength; + + /// + /// The pointer to the first byte of the protocol address to register. For + /// example, if SwAddressType is 0x0800 (IP), then + /// StationAddress points to the first byte of this station's IP + /// address stored in network byte order. + /// + VOID *StationAddress; + + /// + /// The timeout value in 100-ns units that is associated with each + /// new dynamic ARP cache entry. If it is set to zero, the value is + /// implementation-specific. + /// + UINT32 EntryTimeOut; + + /// + /// The number of retries before a MAC address is resolved. If it is + /// set to zero, the value is implementation-specific. + /// + UINT32 RetryCount; + + /// + /// The timeout value in 100-ns units that is used to wait for the ARP + /// reply packet or the timeout value between two retries. Set to zero + /// to use implementation-specific value. + /// + UINT32 RetryTimeOut; +} EFI_ARP_CONFIG_DATA; + + +/** + This function is used to assign a station address to the ARP cache for this instance + of the ARP driver. + + Each ARP instance has one station address. The EFI_ARP_PROTOCOL driver will + respond to ARP requests that match this registered station address. A call to + this function with the ConfigData field set to NULL will reset this ARP instance. + + Once a protocol type and station address have been assigned to this ARP instance, + all the following ARP functions will use this information. Attempting to change + the protocol type or station address to a configured ARP instance will result in errors. + + @param This The pointer to the EFI_ARP_PROTOCOL instance. + @param ConfigData The pointer to the EFI_ARP_CONFIG_DATA structure. + + @retval EFI_SUCCESS The new station address was successfully + registered. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + * This is NULL. + * SwAddressLength is zero when ConfigData is not NULL. + * StationAddress is NULL when ConfigData is not NULL. + @retval EFI_ACCESS_DENIED The SwAddressType, SwAddressLength, or + StationAddress is different from the one that is + already registered. + @retval EFI_OUT_OF_RESOURCES Storage for the new StationAddress could not be + allocated. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_ARP_CONFIGURE)( + IN EFI_ARP_PROTOCOL *This, + IN EFI_ARP_CONFIG_DATA *ConfigData OPTIONAL + ); + +/** + This function is used to insert entries into the ARP cache. + + ARP cache entries are typically inserted and updated by network protocol drivers + as network traffic is processed. Most ARP cache entries will time out and be + deleted if the network traffic stops. ARP cache entries that were inserted + by the Add() function may be static (will not time out) or dynamic (will time out). + Default ARP cache timeout values are not covered in most network protocol + specifications (although RFC 1122 comes pretty close) and will only be + discussed in general terms in this specification. The timeout values that are + used in the EFI Sample Implementation should be used only as a guideline. + Final product implementations of the EFI network stack should be tuned for + their expected network environments. + + @param This Pointer to the EFI_ARP_PROTOCOL instance. + @param DenyFlag Set to TRUE if this entry is a deny entry. Set to + FALSE if this entry is a normal entry. + @param TargetSwAddress Pointer to a protocol address to add (or deny). + May be set to NULL if DenyFlag is TRUE. + @param TargetHwAddress Pointer to a hardware address to add (or deny). + May be set to NULL if DenyFlag is TRUE. + @param TimeoutValue Time in 100-ns units that this entry will remain + in the ARP cache. A value of zero means that the + entry is permanent. A nonzero value will override + the one given by Configure() if the entry to be + added is a dynamic entry. + @param Overwrite If TRUE, the matching cache entry will be + overwritten with the supplied parameters. If + FALSE, EFI_ACCESS_DENIED is returned if the + corresponding cache entry already exists. + + @retval EFI_SUCCESS The entry has been added or updated. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + * This is NULL. + * DenyFlag is FALSE and TargetHwAddress is NULL. + * DenyFlag is FALSE and TargetSwAddress is NULL. + * TargetHwAddress is NULL and TargetSwAddress is NULL. + * Neither TargetSwAddress nor TargetHwAddress are NULL when DenyFlag is + TRUE. + @retval EFI_OUT_OF_RESOURCES The new ARP cache entry could not be allocated. + @retval EFI_ACCESS_DENIED The ARP cache entry already exists and Overwrite + is not true. + @retval EFI_NOT_STARTED The ARP driver instance has not been configured. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_ARP_ADD)( + IN EFI_ARP_PROTOCOL *This, + IN BOOLEAN DenyFlag, + IN VOID *TargetSwAddress OPTIONAL, + IN VOID *TargetHwAddress OPTIONAL, + IN UINT32 TimeoutValue, + IN BOOLEAN Overwrite + ); + +/** + This function searches the ARP cache for matching entries and allocates a buffer into + which those entries are copied. + + The first part of the allocated buffer is EFI_ARP_FIND_DATA, following which + are protocol address pairs and hardware address pairs. + When finding a specific protocol address (BySwAddress is TRUE and AddressBuffer + is not NULL), the ARP cache timeout for the found entry is reset if Refresh is + set to TRUE. If the found ARP cache entry is a permanent entry, it is not + affected by Refresh. + + @param This The pointer to the EFI_ARP_PROTOCOL instance. + @param BySwAddress Set to TRUE to look for matching software protocol + addresses. Set to FALSE to look for matching + hardware protocol addresses. + @param AddressBuffer The pointer to the address buffer. Set to NULL + to match all addresses. + @param EntryLength The size of an entry in the entries buffer. + @param EntryCount The number of ARP cache entries that are found by + the specified criteria. + @param Entries The pointer to the buffer that will receive the ARP + cache entries. + @param Refresh Set to TRUE to refresh the timeout value of the + matching ARP cache entry. + + @retval EFI_SUCCESS The requested ARP cache entries were copied into + the buffer. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. Both EntryCount and EntryLength are + NULL, when Refresh is FALSE. + @retval EFI_NOT_FOUND No matching entries were found. + @retval EFI_NOT_STARTED The ARP driver instance has not been configured. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_ARP_FIND)( + IN EFI_ARP_PROTOCOL *This, + IN BOOLEAN BySwAddress, + IN VOID *AddressBuffer OPTIONAL, + OUT UINT32 *EntryLength OPTIONAL, + OUT UINT32 *EntryCount OPTIONAL, + OUT EFI_ARP_FIND_DATA **Entries OPTIONAL, + IN BOOLEAN Refresh + ); + + +/** + This function removes specified ARP cache entries. + + @param This The pointer to the EFI_ARP_PROTOCOL instance. + @param BySwAddress Set to TRUE to delete matching protocol addresses. + Set to FALSE to delete matching hardware + addresses. + @param AddressBuffer The pointer to the address buffer that is used as a + key to look for the cache entry. Set to NULL to + delete all entries. + + @retval EFI_SUCCESS The entry was removed from the ARP cache. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_FOUND The specified deletion key was not found. + @retval EFI_NOT_STARTED The ARP driver instance has not been configured. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_ARP_DELETE)( + IN EFI_ARP_PROTOCOL *This, + IN BOOLEAN BySwAddress, + IN VOID *AddressBuffer OPTIONAL + ); + +/** + This function delete all dynamic entries from the ARP cache that match the specified + software protocol type. + + @param This The pointer to the EFI_ARP_PROTOCOL instance. + + @retval EFI_SUCCESS The cache has been flushed. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_FOUND There are no matching dynamic cache entries. + @retval EFI_NOT_STARTED The ARP driver instance has not been configured. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_ARP_FLUSH)( + IN EFI_ARP_PROTOCOL *This + ); + +/** + This function tries to resolve the TargetSwAddress and optionally returns a + TargetHwAddress if it already exists in the ARP cache. + + @param This The pointer to the EFI_ARP_PROTOCOL instance. + @param TargetSwAddress The pointer to the protocol address to resolve. + @param ResolvedEvent The pointer to the event that will be signaled when + the address is resolved or some error occurs. + @param TargetHwAddress The pointer to the buffer for the resolved hardware + address in network byte order. + + @retval EFI_SUCCESS The data is copied from the ARP cache into the + TargetHwAddress buffer. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. TargetHwAddress is NULL. + @retval EFI_ACCESS_DENIED The requested address is not present in the normal + ARP cache but is present in the deny address list. + Outgoing traffic to that address is forbidden. + @retval EFI_NOT_STARTED The ARP driver instance has not been configured. + @retval EFI_NOT_READY The request has been started and is not finished. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_ARP_REQUEST)( + IN EFI_ARP_PROTOCOL *This, + IN VOID *TargetSwAddress OPTIONAL, + IN EFI_EVENT ResolvedEvent OPTIONAL, + OUT VOID *TargetHwAddress + ); + +/** + This function aborts the previous ARP request (identified by This, TargetSwAddress + and ResolvedEvent) that is issued by EFI_ARP_PROTOCOL.Request(). + + If the request is in the internal ARP request queue, the request is aborted + immediately and its ResolvedEvent is signaled. Only an asynchronous address + request needs to be canceled. If TargeSwAddress and ResolveEvent are both + NULL, all the pending asynchronous requests that have been issued by This + instance will be cancelled and their corresponding events will be signaled. + + @param This The pointer to the EFI_ARP_PROTOCOL instance. + @param TargetSwAddress The pointer to the protocol address in previous + request session. + @param ResolvedEvent Pointer to the event that is used as the + notification event in previous request session. + + @retval EFI_SUCCESS The pending request session(s) is/are aborted and + corresponding event(s) is/are signaled. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. TargetSwAddress is not NULL and + ResolvedEvent is NULL. TargetSwAddress is NULL and + ResolvedEvent is not NULL. + @retval EFI_NOT_STARTED The ARP driver instance has not been configured. + @retval EFI_NOT_FOUND The request is not issued by + EFI_ARP_PROTOCOL.Request(). + + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_ARP_CANCEL)( + IN EFI_ARP_PROTOCOL *This, + IN VOID *TargetSwAddress OPTIONAL, + IN EFI_EVENT ResolvedEvent OPTIONAL + ); + +/// +/// ARP is used to resolve local network protocol addresses into +/// network hardware addresses. +/// +struct _EFI_ARP_PROTOCOL { + EFI_ARP_CONFIGURE Configure; + EFI_ARP_ADD Add; + EFI_ARP_FIND Find; + EFI_ARP_DELETE Delete; + EFI_ARP_FLUSH Flush; + EFI_ARP_REQUEST Request; + EFI_ARP_CANCEL Cancel; +}; + + +extern EFI_GUID gEfiArpServiceBindingProtocolGuid; +extern EFI_GUID gEfiArpProtocolGuid; + +#endif diff --git a/roms/ipxe/src/include/ipxe/efi/Protocol/BusSpecificDriverOverride.h b/roms/ipxe/src/include/ipxe/efi/Protocol/BusSpecificDriverOverride.h new file mode 100644 index 0000000..be92323 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/efi/Protocol/BusSpecificDriverOverride.h @@ -0,0 +1,74 @@ +/** @file + Bus Specific Driver Override protocol as defined in the UEFI 2.0 specification. + + Bus drivers that have a bus specific algorithm for matching drivers to controllers are + required to produce this protocol for each controller. For example, a PCI Bus Driver will produce an + instance of this protocol for every PCI controller that has a PCI option ROM that contains one or + more UEFI drivers. The protocol instance is attached to the handle of the PCI controller. + + Copyright (c) 2006 - 2008, Intel Corporation. All rights reserved.
    + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _EFI_BUS_SPECIFIC_DRIVER_OVERRIDE_PROTOCOL_H_ +#define _EFI_BUS_SPECIFIC_DRIVER_OVERRIDE_PROTOCOL_H_ + +FILE_LICENCE ( BSD3 ); + +/// +/// Global ID for the Bus Specific Driver Override Protocol +/// +#define EFI_BUS_SPECIFIC_DRIVER_OVERRIDE_PROTOCOL_GUID \ + { \ + 0x3bc1b285, 0x8a15, 0x4a82, {0xaa, 0xbf, 0x4d, 0x7d, 0x13, 0xfb, 0x32, 0x65 } \ + } + +typedef struct _EFI_BUS_SPECIFIC_DRIVER_OVERRIDE_PROTOCOL EFI_BUS_SPECIFIC_DRIVER_OVERRIDE_PROTOCOL; + +// +// Prototypes for the Bus Specific Driver Override Protocol +// + +/** + Uses a bus specific algorithm to retrieve a driver image handle for a controller. + + @param This A pointer to the EFI_BUS_SPECIFIC_DRIVER_ + OVERRIDE_PROTOCOL instance. + @param DriverImageHandle On input, a pointer to the previous driver image handle returned + by GetDriver(). On output, a pointer to the next driver + image handle. Passing in a NULL, will return the first driver + image handle. + + @retval EFI_SUCCESS A bus specific override driver is returned in DriverImageHandle. + @retval EFI_NOT_FOUND The end of the list of override drivers was reached. + A bus specific override driver is not returned in DriverImageHandle. + @retval EFI_INVALID_PARAMETER DriverImageHandle is not a handle that was returned on a + previous call to GetDriver(). + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_BUS_SPECIFIC_DRIVER_OVERRIDE_GET_DRIVER)( + IN EFI_BUS_SPECIFIC_DRIVER_OVERRIDE_PROTOCOL *This, + IN OUT EFI_HANDLE *DriverImageHandle + ); + +/// +/// This protocol matches one or more drivers to a controller. This protocol is produced by a bus driver, +/// and it is installed on the child handles of buses that require a bus specific algorithm for matching +/// drivers to controllers. +/// +struct _EFI_BUS_SPECIFIC_DRIVER_OVERRIDE_PROTOCOL { + EFI_BUS_SPECIFIC_DRIVER_OVERRIDE_GET_DRIVER GetDriver; +}; + +extern EFI_GUID gEfiBusSpecificDriverOverrideProtocolGuid; + +#endif diff --git a/roms/ipxe/src/include/ipxe/efi/Protocol/ComponentName.h b/roms/ipxe/src/include/ipxe/efi/Protocol/ComponentName.h new file mode 100644 index 0000000..87b6d61 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/efi/Protocol/ComponentName.h @@ -0,0 +1,131 @@ +/** @file + EFI Component Name Protocol as defined in the EFI 1.1 specification. + This protocol is used to retrieve user readable names of EFI Drivers + and controllers managed by EFI Drivers. + +Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
    +This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_COMPONENT_NAME_H__ +#define __EFI_COMPONENT_NAME_H__ + +FILE_LICENCE ( BSD3 ); + +/// +/// The global ID for the Component Name Protocol. +/// +#define EFI_COMPONENT_NAME_PROTOCOL_GUID \ + { \ + 0x107a772c, 0xd5e1, 0x11d4, {0x9a, 0x46, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \ + } + +typedef struct _EFI_COMPONENT_NAME_PROTOCOL EFI_COMPONENT_NAME_PROTOCOL; + + +/** + Retrieves a Unicode string that is the user-readable name of the EFI Driver. + + @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + @param Language A pointer to a three-character ISO 639-2 language identifier. + This is the language of the driver name that that the caller + is requesting, and it must match one of the languages specified + in SupportedLanguages. The number of languages supported by a + driver is up to the driver writer. + @param DriverName A pointer to the Unicode string to return. This Unicode string + is the name of the driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by This + and the language specified by Language was returned + in DriverName. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER DriverName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This does not support the + language specified by Language. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_COMPONENT_NAME_GET_DRIVER_NAME)( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by an EFI Driver. + + @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + @param ControllerHandle The handle of a controller that the driver specified by + This is managing. This handle specifies the controller + whose name is to be returned. + @param ChildHandle The handle of the child controller to retrieve the name + of. This is an optional parameter that may be NULL. It + will be NULL for device drivers. It will also be NULL + for a bus drivers that wish to retrieve the name of the + bus controller. It will not be NULL for a bus driver + that wishes to retrieve the name of a child controller. + @param Language A pointer to a three character ISO 639-2 language + identifier. This is the language of the controller name + that the caller is requesting, and it must match one + of the languages specified in SupportedLanguages. The + number of languages supported by a driver is up to the + driver writer. + @param ControllerName A pointer to the Unicode string to return. This Unicode + string is the name of the controller specified by + ControllerHandle and ChildHandle in the language specified + by Language, from the point of view of the driver specified + by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in the + language specified by Language for the driver + specified by This was returned in DriverName. + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid EFI_HANDLE. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This is not currently managing + the controller specified by ControllerHandle and + ChildHandle. + @retval EFI_UNSUPPORTED The driver specified by This does not support the + language specified by Language. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_COMPONENT_NAME_GET_CONTROLLER_NAME)( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +/// +/// This protocol is used to retrieve user readable names of drivers +/// and controllers managed by UEFI Drivers. +/// +struct _EFI_COMPONENT_NAME_PROTOCOL { + EFI_COMPONENT_NAME_GET_DRIVER_NAME GetDriverName; + EFI_COMPONENT_NAME_GET_CONTROLLER_NAME GetControllerName; + /// + /// A Null-terminated ASCII string that contains one or more + /// ISO 639-2 language codes. This is the list of language codes + /// that this protocol supports. + /// + CHAR8 *SupportedLanguages; +}; + +extern EFI_GUID gEfiComponentNameProtocolGuid; + +#endif diff --git a/roms/ipxe/src/include/ipxe/efi/Protocol/ConsoleControl/ConsoleControl.h b/roms/ipxe/src/include/ipxe/efi/Protocol/ConsoleControl/ConsoleControl.h new file mode 100644 index 0000000..0bf5799 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/efi/Protocol/ConsoleControl/ConsoleControl.h @@ -0,0 +1,124 @@ +/*++ + +Copyright (c) 2004 - 2010, Intel Corporation. All rights reserved.
    +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + ConsoleControl.h + +Abstract: + + Abstraction of a Text mode or GOP/UGA screen + +--*/ + +#ifndef __CONSOLE_CONTROL_H__ +#define __CONSOLE_CONTROL_H__ + +FILE_LICENCE ( BSD3 ); + +#define EFI_CONSOLE_CONTROL_PROTOCOL_GUID \ + { 0xf42f7782, 0x12e, 0x4c12, {0x99, 0x56, 0x49, 0xf9, 0x43, 0x4, 0xf7, 0x21} } + +typedef struct _EFI_CONSOLE_CONTROL_PROTOCOL EFI_CONSOLE_CONTROL_PROTOCOL; + + +typedef enum { + EfiConsoleControlScreenText, + EfiConsoleControlScreenGraphics, + EfiConsoleControlScreenMaxValue +} EFI_CONSOLE_CONTROL_SCREEN_MODE; + + +typedef +EFI_STATUS +(EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_GET_MODE) ( + IN EFI_CONSOLE_CONTROL_PROTOCOL *This, + OUT EFI_CONSOLE_CONTROL_SCREEN_MODE *Mode, + OUT BOOLEAN *GopUgaExists, OPTIONAL + OUT BOOLEAN *StdInLocked OPTIONAL + ) +/*++ + + Routine Description: + Return the current video mode information. Also returns info about existence + of Graphics Output devices or UGA Draw devices in system, and if the Std In + device is locked. All the arguments are optional and only returned if a non + NULL pointer is passed in. + + Arguments: + This - Protocol instance pointer. + Mode - Are we in text of grahics mode. + GopUgaExists - TRUE if Console Spliter has found a GOP or UGA device + StdInLocked - TRUE if StdIn device is keyboard locked + + Returns: + EFI_SUCCESS - Mode information returned. + +--*/ +; + + +typedef +EFI_STATUS +(EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE) ( + IN EFI_CONSOLE_CONTROL_PROTOCOL *This, + IN EFI_CONSOLE_CONTROL_SCREEN_MODE Mode + ) +/*++ + + Routine Description: + Set the current mode to either text or graphics. Graphics is + for Quiet Boot. + + Arguments: + This - Protocol instance pointer. + Mode - Mode to set the + + Returns: + EFI_SUCCESS - Mode information returned. + +--*/ +; + + +typedef +EFI_STATUS +(EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_LOCK_STD_IN) ( + IN EFI_CONSOLE_CONTROL_PROTOCOL *This, + IN CHAR16 *Password + ) +/*++ + + Routine Description: + Lock Std In devices until Password is typed. + + Arguments: + This - Protocol instance pointer. + Password - Password needed to unlock screen. NULL means unlock keyboard + + Returns: + EFI_SUCCESS - Mode information returned. + EFI_DEVICE_ERROR - Std In not locked + +--*/ +; + + + +struct _EFI_CONSOLE_CONTROL_PROTOCOL { + EFI_CONSOLE_CONTROL_PROTOCOL_GET_MODE GetMode; + EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE SetMode; + EFI_CONSOLE_CONTROL_PROTOCOL_LOCK_STD_IN LockStdIn; +}; + +extern EFI_GUID gEfiConsoleControlProtocolGuid; + +#endif diff --git a/roms/ipxe/src/include/ipxe/efi/Protocol/DebugSupport.h b/roms/ipxe/src/include/ipxe/efi/Protocol/DebugSupport.h index ea5cec5..e2b4b20 100644 --- a/roms/ipxe/src/include/ipxe/efi/Protocol/DebugSupport.h +++ b/roms/ipxe/src/include/ipxe/efi/Protocol/DebugSupport.h @@ -1,11 +1,13 @@ /** @file - DebugSupport protocol and supporting definitions as defined in the UEFI2.0 + DebugSupport protocol and supporting definitions as defined in the UEFI2.4 specification. The DebugSupport protocol is used by source level debuggers to abstract the processor and handle context save and restore operations. Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
    +Portions copyright (c) 2011 - 2013, ARM Ltd. All rights reserved.
    + This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License that accompanies this distribution. The full text of the license may be found at @@ -519,6 +521,97 @@ typedef struct { UINT32 IFAR; } EFI_SYSTEM_CONTEXT_ARM; + +/// +/// AARCH64 processor exception types. +/// +#define EXCEPT_AARCH64_SYNCHRONOUS_EXCEPTIONS 0 +#define EXCEPT_AARCH64_IRQ 1 +#define EXCEPT_AARCH64_FIQ 2 +#define EXCEPT_AARCH64_SERROR 3 + +/// +/// For coding convenience, define the maximum valid ARM exception. +/// +#define MAX_AARCH64_EXCEPTION EXCEPT_AARCH64_SERROR + +typedef struct { + // General Purpose Registers + UINT64 X0; + UINT64 X1; + UINT64 X2; + UINT64 X3; + UINT64 X4; + UINT64 X5; + UINT64 X6; + UINT64 X7; + UINT64 X8; + UINT64 X9; + UINT64 X10; + UINT64 X11; + UINT64 X12; + UINT64 X13; + UINT64 X14; + UINT64 X15; + UINT64 X16; + UINT64 X17; + UINT64 X18; + UINT64 X19; + UINT64 X20; + UINT64 X21; + UINT64 X22; + UINT64 X23; + UINT64 X24; + UINT64 X25; + UINT64 X26; + UINT64 X27; + UINT64 X28; + UINT64 FP; // x29 - Frame pointer + UINT64 LR; // x30 - Link Register + UINT64 SP; // x31 - Stack pointer + + // FP/SIMD Registers + UINT64 V0[2]; + UINT64 V1[2]; + UINT64 V2[2]; + UINT64 V3[2]; + UINT64 V4[2]; + UINT64 V5[2]; + UINT64 V6[2]; + UINT64 V7[2]; + UINT64 V8[2]; + UINT64 V9[2]; + UINT64 V10[2]; + UINT64 V11[2]; + UINT64 V12[2]; + UINT64 V13[2]; + UINT64 V14[2]; + UINT64 V15[2]; + UINT64 V16[2]; + UINT64 V17[2]; + UINT64 V18[2]; + UINT64 V19[2]; + UINT64 V20[2]; + UINT64 V21[2]; + UINT64 V22[2]; + UINT64 V23[2]; + UINT64 V24[2]; + UINT64 V25[2]; + UINT64 V26[2]; + UINT64 V27[2]; + UINT64 V28[2]; + UINT64 V29[2]; + UINT64 V30[2]; + UINT64 V31[2]; + + UINT64 ELR; // Exception Link Register + UINT64 SPSR; // Saved Processor Status Register + UINT64 FPSR; // Floating Point Status Register + UINT64 ESR; // Exception syndrome register + UINT64 FAR; // Fault Address Register +} EFI_SYSTEM_CONTEXT_AARCH64; + + /// /// Universal EFI_SYSTEM_CONTEXT definition. /// @@ -528,6 +621,7 @@ typedef union { EFI_SYSTEM_CONTEXT_X64 *SystemContextX64; EFI_SYSTEM_CONTEXT_IPF *SystemContextIpf; EFI_SYSTEM_CONTEXT_ARM *SystemContextArm; + EFI_SYSTEM_CONTEXT_AARCH64 *SystemContextAArch64; } EFI_SYSTEM_CONTEXT; // @@ -568,7 +662,8 @@ typedef enum { IsaX64 = IMAGE_FILE_MACHINE_X64, ///< 0x8664 IsaIpf = IMAGE_FILE_MACHINE_IA64, ///< 0x0200 IsaEbc = IMAGE_FILE_MACHINE_EBC, ///< 0x0EBC - IsaArm = IMAGE_FILE_MACHINE_ARMTHUMB_MIXED ///< 0x01c2 + IsaArm = IMAGE_FILE_MACHINE_ARMTHUMB_MIXED, ///< 0x01c2 + IsaAArch64 = IMAGE_FILE_MACHINE_ARM64 ///< 0xAA64 } EFI_INSTRUCTION_SET_ARCHITECTURE; diff --git a/roms/ipxe/src/include/ipxe/efi/Protocol/DevicePath.h b/roms/ipxe/src/include/ipxe/efi/Protocol/DevicePath.h index cc4c927..a305df5 100644 --- a/roms/ipxe/src/include/ipxe/efi/Protocol/DevicePath.h +++ b/roms/ipxe/src/include/ipxe/efi/Protocol/DevicePath.h @@ -5,7 +5,7 @@ from a software point of view. The path must persist from boot to boot, so it can not contain things like PCI bus numbers that change from boot to boot. -Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
    +Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.
    This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License that accompanies this distribution. The full text of the license may be found at @@ -788,6 +788,16 @@ typedef struct { } SASEX_DEVICE_PATH; /// +/// NvmExpress Namespace Device Path SubType. +/// +#define MSG_NVME_NAMESPACE_DP 0x17 +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + UINT32 NamespaceId; + UINT64 NamespaceUuid; +} NVME_NAMESPACE_DEVICE_PATH; + +/// /// iSCSI Device Path SubType /// #define MSG_ISCSI_DP 0x13 @@ -1085,6 +1095,7 @@ typedef union { UART_FLOW_CONTROL_DEVICE_PATH UartFlowControl; SAS_DEVICE_PATH Sas; SASEX_DEVICE_PATH SasEx; + NVME_NAMESPACE_DEVICE_PATH NvmeNamespace; HARDDRIVE_DEVICE_PATH HardDrive; CDROM_DEVICE_PATH CD; @@ -1134,6 +1145,7 @@ typedef union { UART_FLOW_CONTROL_DEVICE_PATH *UartFlowControl; SAS_DEVICE_PATH *Sas; SASEX_DEVICE_PATH *SasEx; + NVME_NAMESPACE_DEVICE_PATH *NvmeNamespace; HARDDRIVE_DEVICE_PATH *HardDrive; CDROM_DEVICE_PATH *CD; diff --git a/roms/ipxe/src/include/ipxe/efi/Protocol/Dhcp4.h b/roms/ipxe/src/include/ipxe/efi/Protocol/Dhcp4.h new file mode 100644 index 0000000..560ee32 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/efi/Protocol/Dhcp4.h @@ -0,0 +1,782 @@ +/** @file + EFI_DHCP4_PROTOCOL as defined in UEFI 2.0. + EFI_DHCP4_SERVICE_BINDING_PROTOCOL as defined in UEFI 2.0. + These protocols are used to collect configuration information for the EFI IPv4 Protocol + drivers and to provide DHCPv4 server and PXE boot server discovery services. + +Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
    +This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + @par Revision Reference: + This Protocol was introduced in UEFI Specification 2.0. + +**/ + +#ifndef __EFI_DHCP4_PROTOCOL_H__ +#define __EFI_DHCP4_PROTOCOL_H__ + +FILE_LICENCE ( BSD3 ); + +#define EFI_DHCP4_PROTOCOL_GUID \ + { \ + 0x8a219718, 0x4ef5, 0x4761, {0x91, 0xc8, 0xc0, 0xf0, 0x4b, 0xda, 0x9e, 0x56 } \ + } + +#define EFI_DHCP4_SERVICE_BINDING_PROTOCOL_GUID \ + { \ + 0x9d9a39d8, 0xbd42, 0x4a73, {0xa4, 0xd5, 0x8e, 0xe9, 0x4b, 0xe1, 0x13, 0x80 } \ + } + +typedef struct _EFI_DHCP4_PROTOCOL EFI_DHCP4_PROTOCOL; + + +#pragma pack(1) +typedef struct { + /// + /// DHCP option code. + /// + UINT8 OpCode; + /// + /// Length of the DHCP option data. Not present if OpCode is 0 or 255. + /// + UINT8 Length; + /// + /// Start of the DHCP option data. Not present if OpCode is 0 or 255 or if Length is zero. + /// + UINT8 Data[1]; +} EFI_DHCP4_PACKET_OPTION; +#pragma pack() + + +#pragma pack(1) +/// +/// EFI_DHCP4_PACKET defines the format of DHCPv4 packets. See RFC 2131 for more information. +/// +typedef struct { + UINT8 OpCode; + UINT8 HwType; + UINT8 HwAddrLen; + UINT8 Hops; + UINT32 Xid; + UINT16 Seconds; + UINT16 Reserved; + EFI_IPv4_ADDRESS ClientAddr; ///< Client IP address from client. + EFI_IPv4_ADDRESS YourAddr; ///< Client IP address from server. + EFI_IPv4_ADDRESS ServerAddr; ///< IP address of next server in bootstrap. + EFI_IPv4_ADDRESS GatewayAddr; ///< Relay agent IP address. + UINT8 ClientHwAddr[16]; ///< Client hardware address. + CHAR8 ServerName[64]; + CHAR8 BootFileName[128]; +}EFI_DHCP4_HEADER; +#pragma pack() + + +#pragma pack(1) +typedef struct { + /// + /// Size of the EFI_DHCP4_PACKET buffer. + /// + UINT32 Size; + /// + /// Length of the EFI_DHCP4_PACKET from the first byte of the Header field + /// to the last byte of the Option[] field. + /// + UINT32 Length; + + struct { + /// + /// DHCP packet header. + /// + EFI_DHCP4_HEADER Header; + /// + /// DHCP magik cookie in network byte order. + /// + UINT32 Magik; + /// + /// Start of the DHCP packed option data. + /// + UINT8 Option[1]; + } Dhcp4; +} EFI_DHCP4_PACKET; +#pragma pack() + + +typedef enum { + /// + /// The EFI DHCPv4 Protocol driver is stopped. + /// + Dhcp4Stopped = 0x0, + /// + /// The EFI DHCPv4 Protocol driver is inactive. + /// + Dhcp4Init = 0x1, + /// + /// The EFI DHCPv4 Protocol driver is collecting DHCP offer packets from DHCP servers. + /// + Dhcp4Selecting = 0x2, + /// + /// The EFI DHCPv4 Protocol driver has sent the request to the DHCP server and is waiting for a response. + /// + Dhcp4Requesting = 0x3, + /// + /// The DHCP configuration has completed. + /// + Dhcp4Bound = 0x4, + /// + /// The DHCP configuration is being renewed and another request has + /// been sent out, but it has not received a response from the server yet. + /// + Dhcp4Renewing = 0x5, + /// + /// The DHCP configuration has timed out and the EFI DHCPv4 + /// Protocol driver is trying to extend the lease time. + /// + Dhcp4Rebinding = 0x6, + /// + /// The EFI DHCPv4 Protocol driver was initialized with a previously + /// allocated or known IP address. + /// + Dhcp4InitReboot = 0x7, + /// + /// The EFI DHCPv4 Protocol driver is seeking to reuse the previously + /// allocated IP address by sending a request to the DHCP server. + /// + Dhcp4Rebooting = 0x8 +} EFI_DHCP4_STATE; + + +typedef enum{ + /// + /// The packet to start the configuration sequence is about to be sent. + /// + Dhcp4SendDiscover = 0x01, + /// + /// A reply packet was just received. + /// + Dhcp4RcvdOffer = 0x02, + /// + /// It is time for Dhcp4Callback to select an offer. + /// + Dhcp4SelectOffer = 0x03, + /// + /// A request packet is about to be sent. + /// + Dhcp4SendRequest = 0x04, + /// + /// A DHCPACK packet was received and will be passed to Dhcp4Callback. + /// + Dhcp4RcvdAck = 0x05, + /// + /// A DHCPNAK packet was received and will be passed to Dhcp4Callback. + /// + Dhcp4RcvdNak = 0x06, + /// + /// A decline packet is about to be sent. + /// + Dhcp4SendDecline = 0x07, + /// + /// The DHCP configuration process has completed. No packet is associated with this event. + /// + Dhcp4BoundCompleted = 0x08, + /// + /// It is time to enter the Dhcp4Renewing state and to contact the server + /// that originally issued the network address. No packet is associated with this event. + /// + Dhcp4EnterRenewing = 0x09, + /// + /// It is time to enter the Dhcp4Rebinding state and to contact any server. + /// No packet is associated with this event. + /// + Dhcp4EnterRebinding = 0x0a, + /// + /// The configured IP address was lost either because the lease has expired, + /// the user released the configuration, or a DHCPNAK packet was received in + /// the Dhcp4Renewing or Dhcp4Rebinding state. No packet is associated with this event. + /// + Dhcp4AddressLost = 0x0b, + /// + /// The DHCP process failed because a DHCPNAK packet was received or the user + /// aborted the DHCP process at a time when the configuration was not available yet. + /// No packet is associated with this event. + /// + Dhcp4Fail = 0x0c +} EFI_DHCP4_EVENT; + +/** + Callback routine. + + EFI_DHCP4_CALLBACK is provided by the consumer of the EFI DHCPv4 Protocol driver + to intercept events that occurred in the configuration process. This structure + provides advanced control of each state transition of the DHCP process. The + returned status code determines the behavior of the EFI DHCPv4 Protocol driver. + There are three possible returned values, which are described in the following + table. + + @param This The pointer to the EFI DHCPv4 Protocol instance that is used to + configure this callback function. + @param Context The pointer to the context that is initialized by + EFI_DHCP4_PROTOCOL.Configure(). + @param CurrentState The current operational state of the EFI DHCPv4 Protocol + driver. + @param Dhcp4Event The event that occurs in the current state, which usually means a + state transition. + @param Packet The DHCP packet that is going to be sent or already received. + @param NewPacket The packet that is used to replace the above Packet. + + @retval EFI_SUCCESS Tells the EFI DHCPv4 Protocol driver to continue the DHCP process. + When it is in the Dhcp4Selecting state, it tells the EFI DHCPv4 Protocol + driver to stop collecting additional packets. The driver will exit + the Dhcp4Selecting state and enter the Dhcp4Requesting state. + @retval EFI_NOT_READY Only used in the Dhcp4Selecting state. The EFI DHCPv4 Protocol + driver will continue to wait for more packets until the retry + timeout expires. + @retval EFI_ABORTED Tells the EFI DHCPv4 Protocol driver to abort the current process and + return to the Dhcp4Init or Dhcp4InitReboot state. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_DHCP4_CALLBACK)( + IN EFI_DHCP4_PROTOCOL *This, + IN VOID *Context, + IN EFI_DHCP4_STATE CurrentState, + IN EFI_DHCP4_EVENT Dhcp4Event, + IN EFI_DHCP4_PACKET *Packet OPTIONAL, + OUT EFI_DHCP4_PACKET **NewPacket OPTIONAL + ); + +typedef struct { + /// + /// The number of times to try sending a packet during the Dhcp4SendDiscover + /// event and waiting for a response during the Dhcp4RcvdOffer event. + /// Set to zero to use the default try counts and timeout values. + /// + UINT32 DiscoverTryCount; + /// + /// The maximum amount of time (in seconds) to wait for returned packets in each + /// of the retries. Timeout values of zero will default to a timeout value + /// of one second. Set to NULL to use default timeout values. + /// + UINT32 *DiscoverTimeout; + /// + /// The number of times to try sending a packet during the Dhcp4SendRequest event + /// and waiting for a response during the Dhcp4RcvdAck event before accepting + /// failure. Set to zero to use the default try counts and timeout values. + /// + UINT32 RequestTryCount; + /// + /// The maximum amount of time (in seconds) to wait for return packets in each of the retries. + /// Timeout values of zero will default to a timeout value of one second. + /// Set to NULL to use default timeout values. + /// + UINT32 *RequestTimeout; + /// + /// For a DHCPDISCOVER, setting this parameter to the previously allocated IP + /// address will cause the EFI DHCPv4 Protocol driver to enter the Dhcp4InitReboot state. + /// And set this field to 0.0.0.0 to enter the Dhcp4Init state. + /// For a DHCPINFORM this parameter should be set to the client network address + /// which was assigned to the client during a DHCPDISCOVER. + /// + EFI_IPv4_ADDRESS ClientAddress; + /// + /// The callback function to intercept various events that occurred in + /// the DHCP configuration process. Set to NULL to ignore all those events. + /// + EFI_DHCP4_CALLBACK Dhcp4Callback; + /// + /// The pointer to the context that will be passed to Dhcp4Callback when it is called. + /// + VOID *CallbackContext; + /// + /// Number of DHCP options in the OptionList. + /// + UINT32 OptionCount; + /// + /// List of DHCP options to be included in every packet that is sent during the + /// Dhcp4SendDiscover event. Pad options are appended automatically by DHCP driver + /// in outgoing DHCP packets. If OptionList itself contains pad option, they are + /// ignored by the driver. OptionList can be freed after EFI_DHCP4_PROTOCOL.Configure() + /// returns. Ignored if OptionCount is zero. + /// + EFI_DHCP4_PACKET_OPTION **OptionList; +} EFI_DHCP4_CONFIG_DATA; + + +typedef struct { + /// + /// The EFI DHCPv4 Protocol driver operating state. + /// + EFI_DHCP4_STATE State; + /// + /// The configuration data of the current EFI DHCPv4 Protocol driver instance. + /// + EFI_DHCP4_CONFIG_DATA ConfigData; + /// + /// The client IP address that was acquired from the DHCP server. If it is zero, + /// the DHCP acquisition has not completed yet and the following fields in this structure are undefined. + /// + EFI_IPv4_ADDRESS ClientAddress; + /// + /// The local hardware address. + /// + EFI_MAC_ADDRESS ClientMacAddress; + /// + /// The server IP address that is providing the DHCP service to this client. + /// + EFI_IPv4_ADDRESS ServerAddress; + /// + /// The router IP address that was acquired from the DHCP server. + /// May be zero if the server does not offer this address. + /// + EFI_IPv4_ADDRESS RouterAddress; + /// + /// The subnet mask of the connected network that was acquired from the DHCP server. + /// + EFI_IPv4_ADDRESS SubnetMask; + /// + /// The lease time (in 1-second units) of the configured IP address. + /// The value 0xFFFFFFFF means that the lease time is infinite. + /// A default lease of 7 days is used if the DHCP server does not provide a value. + /// + UINT32 LeaseTime; + /// + /// The cached latest DHCPACK or DHCPNAK or BOOTP REPLY packet. May be NULL if no packet is cached. + /// + EFI_DHCP4_PACKET *ReplyPacket; +} EFI_DHCP4_MODE_DATA; + + +typedef struct { + /// + /// Alternate listening address. It can be a unicast, multicast, or broadcast address. + /// + EFI_IPv4_ADDRESS ListenAddress; + /// + /// The subnet mask of above listening unicast/broadcast IP address. + /// Ignored if ListenAddress is a multicast address. + /// + EFI_IPv4_ADDRESS SubnetMask; + /// + /// Alternate station source (or listening) port number. + /// If zero, then the default station port number (68) will be used. + /// + UINT16 ListenPort; +} EFI_DHCP4_LISTEN_POINT; + + +typedef struct { + /// + /// The completion status of transmitting and receiving. + /// + EFI_STATUS Status; + /// + /// If not NULL, the event that will be signaled when the collection process + /// completes. If NULL, this function will busy-wait until the collection process competes. + /// + EFI_EVENT CompletionEvent; + /// + /// The pointer to the server IP address. This address may be a unicast, multicast, or broadcast address. + /// + EFI_IPv4_ADDRESS RemoteAddress; + /// + /// The server listening port number. If zero, the default server listening port number (67) will be used. + /// + UINT16 RemotePort; + /// + /// The pointer to the gateway address to override the existing setting. + /// + EFI_IPv4_ADDRESS GatewayAddress; + /// + /// The number of entries in ListenPoints. If zero, the default station address and port number 68 are used. + /// + UINT32 ListenPointCount; + /// + /// An array of station address and port number pairs that are used as receiving filters. + /// The first entry is also used as the source address and source port of the outgoing packet. + /// + EFI_DHCP4_LISTEN_POINT *ListenPoints; + /// + /// The number of seconds to collect responses. Zero is invalid. + /// + UINT32 TimeoutValue; + /// + /// The pointer to the packet to be transmitted. + /// + EFI_DHCP4_PACKET *Packet; + /// + /// Number of received packets. + /// + UINT32 ResponseCount; + /// + /// The pointer to the allocated list of received packets. + /// + EFI_DHCP4_PACKET *ResponseList; +} EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN; + + +/** + Returns the current operating mode and cached data packet for the EFI DHCPv4 Protocol driver. + + The GetModeData() function returns the current operating mode and cached data + packet for the EFI DHCPv4 Protocol driver. + + @param This The pointer to the EFI_DHCP4_PROTOCOL instance. + @param Dhcp4ModeData The pointer to storage for the EFI_DHCP4_MODE_DATA structure. + + @retval EFI_SUCCESS The mode data was returned. + @retval EFI_INVALID_PARAMETER This is NULL. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_DHCP4_GET_MODE_DATA)( + IN EFI_DHCP4_PROTOCOL *This, + OUT EFI_DHCP4_MODE_DATA *Dhcp4ModeData + ); + +/** + Initializes, changes, or resets the operational settings for the EFI DHCPv4 Protocol driver. + + The Configure() function is used to initialize, change, or reset the operational + settings of the EFI DHCPv4 Protocol driver for the communication device on which + the EFI DHCPv4 Service Binding Protocol is installed. This function can be + successfully called only if both of the following are true: + * This instance of the EFI DHCPv4 Protocol driver is in the Dhcp4Stopped, Dhcp4Init, + Dhcp4InitReboot, or Dhcp4Bound states. + * No other EFI DHCPv4 Protocol driver instance that is controlled by this EFI + DHCPv4 Service Binding Protocol driver instance has configured this EFI DHCPv4 + Protocol driver. + When this driver is in the Dhcp4Stopped state, it can transfer into one of the + following two possible initial states: + * Dhcp4Init + * Dhcp4InitReboot. + The driver can transfer into these states by calling Configure() with a non-NULL + Dhcp4CfgData. The driver will transfer into the appropriate state based on the + supplied client network address in the ClientAddress parameter and DHCP options + in the OptionList parameter as described in RFC 2131. + When Configure() is called successfully while Dhcp4CfgData is set to NULL, the + default configuring data will be reset in the EFI DHCPv4 Protocol driver and + the state of the EFI DHCPv4 Protocol driver will not be changed. If one instance + wants to make it possible for another instance to configure the EFI DHCPv4 Protocol + driver, it must call this function with Dhcp4CfgData set to NULL. + + @param This The pointer to the EFI_DHCP4_PROTOCOL instance. + @param Dhcp4CfgData The pointer to the EFI_DHCP4_CONFIG_DATA. + + @retval EFI_SUCCESS The EFI DHCPv4 Protocol driver is now in the Dhcp4Init or + Dhcp4InitReboot state, if the original state of this driver + was Dhcp4Stopped, Dhcp4Init,Dhcp4InitReboot, or Dhcp4Bound + and the value of Dhcp4CfgData was not NULL. + Otherwise, the state was left unchanged. + @retval EFI_ACCESS_DENIED This instance of the EFI DHCPv4 Protocol driver was not in the + Dhcp4Stopped, Dhcp4Init, Dhcp4InitReboot, or Dhcp4Bound state; + Or onother instance of this EFI DHCPv4 Protocol driver is already + in a valid configured state. + @retval EFI_INVALID_PARAMETER One or more following conditions are TRUE: + This is NULL. + DiscoverTryCount > 0 and DiscoverTimeout is NULL + RequestTryCount > 0 and RequestTimeout is NULL. + OptionCount >0 and OptionList is NULL. + ClientAddress is not a valid unicast address. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_DHCP4_CONFIGURE)( + IN EFI_DHCP4_PROTOCOL *This, + IN EFI_DHCP4_CONFIG_DATA *Dhcp4CfgData OPTIONAL + ); + + +/** + Starts the DHCP configuration process. + + The Start() function starts the DHCP configuration process. This function can + be called only when the EFI DHCPv4 Protocol driver is in the Dhcp4Init or + Dhcp4InitReboot state. + If the DHCP process completes successfully, the state of the EFI DHCPv4 Protocol + driver will be transferred through Dhcp4Selecting and Dhcp4Requesting to the + Dhcp4Bound state. The CompletionEvent will then be signaled if it is not NULL. + If the process aborts, either by the user or by some unexpected network error, + the state is restored to the Dhcp4Init state. The Start() function can be called + again to restart the process. + Refer to RFC 2131 for precise state transitions during this process. At the + time when each event occurs in this process, the callback function that was set + by EFI_DHCP4_PROTOCOL.Configure() will be called and the user can take this + opportunity to control the process. + + @param This The pointer to the EFI_DHCP4_PROTOCOL instance. + @param CompletionEvent If not NULL, it indicates the event that will be signaled when the + EFI DHCPv4 Protocol driver is transferred into the + Dhcp4Bound state or when the DHCP process is aborted. + EFI_DHCP4_PROTOCOL.GetModeData() can be called to + check the completion status. If NULL, + EFI_DHCP4_PROTOCOL.Start() will wait until the driver + is transferred into the Dhcp4Bound state or the process fails. + + @retval EFI_SUCCESS The DHCP configuration process has started, or it has completed + when CompletionEvent is NULL. + @retval EFI_NOT_STARTED The EFI DHCPv4 Protocol driver is in the Dhcp4Stopped + state. EFI_DHCP4_PROTOCOL. Configure() needs to be called. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_TIMEOUT The DHCP configuration process failed because no response was + received from the server within the specified timeout value. + @retval EFI_ABORTED The user aborted the DHCP process. + @retval EFI_ALREADY_STARTED Some other EFI DHCPv4 Protocol instance already started the + DHCP process. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_NO_MEDIA There was a media error. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_DHCP4_START)( + IN EFI_DHCP4_PROTOCOL *This, + IN EFI_EVENT CompletionEvent OPTIONAL + ); + +/** + Extends the lease time by sending a request packet. + + The RenewRebind() function is used to manually extend the lease time when the + EFI DHCPv4 Protocol driver is in the Dhcp4Bound state, and the lease time has + not expired yet. This function will send a request packet to the previously + found server (or to any server when RebindRequest is TRUE) and transfer the + state into the Dhcp4Renewing state (or Dhcp4Rebinding when RebindingRequest is + TRUE). When a response is received, the state is returned to Dhcp4Bound. + If no response is received before the try count is exceeded (the RequestTryCount + field that is specified in EFI_DHCP4_CONFIG_DATA) but before the lease time that + was issued by the previous server expires, the driver will return to the Dhcp4Bound + state, and the previous configuration is restored. The outgoing and incoming packets + can be captured by the EFI_DHCP4_CALLBACK function. + + @param This The pointer to the EFI_DHCP4_PROTOCOL instance. + @param RebindRequest If TRUE, this function broadcasts the request packets and enters + the Dhcp4Rebinding state. Otherwise, it sends a unicast + request packet and enters the Dhcp4Renewing state. + @param CompletionEvent If not NULL, this event is signaled when the renew/rebind phase + completes or some error occurs. + EFI_DHCP4_PROTOCOL.GetModeData() can be called to + check the completion status. If NULL, + EFI_DHCP4_PROTOCOL.RenewRebind() will busy-wait + until the DHCP process finishes. + + @retval EFI_SUCCESS The EFI DHCPv4 Protocol driver is now in the + Dhcp4Renewing state or is back to the Dhcp4Bound state. + @retval EFI_NOT_STARTED The EFI DHCPv4 Protocol driver is in the Dhcp4Stopped + state. EFI_DHCP4_PROTOCOL.Configure() needs to + be called. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_TIMEOUT There was no response from the server when the try count was + exceeded. + @retval EFI_ACCESS_DENIED The driver is not in the Dhcp4Bound state. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_DHCP4_RENEW_REBIND)( + IN EFI_DHCP4_PROTOCOL *This, + IN BOOLEAN RebindRequest, + IN EFI_EVENT CompletionEvent OPTIONAL + ); + +/** + Releases the current address configuration. + + The Release() function releases the current configured IP address by doing either + of the following: + * Sending a DHCPRELEASE packet when the EFI DHCPv4 Protocol driver is in the + Dhcp4Bound state + * Setting the previously assigned IP address that was provided with the + EFI_DHCP4_PROTOCOL.Configure() function to 0.0.0.0 when the driver is in + Dhcp4InitReboot state + After a successful call to this function, the EFI DHCPv4 Protocol driver returns + to the Dhcp4Init state, and any subsequent incoming packets will be discarded silently. + + @param This The pointer to the EFI_DHCP4_PROTOCOL instance. + + @retval EFI_SUCCESS The EFI DHCPv4 Protocol driver is now in the Dhcp4Init phase. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_ACCESS_DENIED The EFI DHCPv4 Protocol driver is not Dhcp4InitReboot state. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_DHCP4_RELEASE)( + IN EFI_DHCP4_PROTOCOL *This + ); + +/** + Stops the current address configuration. + + The Stop() function is used to stop the DHCP configuration process. After this + function is called successfully, the EFI DHCPv4 Protocol driver is transferred + into the Dhcp4Stopped state. EFI_DHCP4_PROTOCOL.Configure() needs to be called + before DHCP configuration process can be started again. This function can be + called when the EFI DHCPv4 Protocol driver is in any state. + + @param This The pointer to the EFI_DHCP4_PROTOCOL instance. + + @retval EFI_SUCCESS The EFI DHCPv4 Protocol driver is now in the Dhcp4Stopped phase. + @retval EFI_INVALID_PARAMETER This is NULL. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_DHCP4_STOP)( + IN EFI_DHCP4_PROTOCOL *This + ); + +/** + Builds a DHCP packet, given the options to be appended or deleted or replaced. + + The Build() function is used to assemble a new packet from the original packet + by replacing or deleting existing options or appending new options. This function + does not change any state of the EFI DHCPv4 Protocol driver and can be used at + any time. + + @param This The pointer to the EFI_DHCP4_PROTOCOL instance. + @param SeedPacket Initial packet to be used as a base for building new packet. + @param DeleteCount Number of opcodes in the DeleteList. + @param DeleteList List of opcodes to be deleted from the seed packet. + Ignored if DeleteCount is zero. + @param AppendCount Number of entries in the OptionList. + @param AppendList The pointer to a DHCP option list to be appended to SeedPacket. + If SeedPacket also contains options in this list, they are + replaced by new options (except pad option). Ignored if + AppendCount is zero. Type EFI_DHCP4_PACKET_OPTION + @param NewPacket The pointer to storage for the pointer to the new allocated packet. + Use the EFI Boot Service FreePool() on the resulting pointer + when done with the packet. + + @retval EFI_SUCCESS The new packet was built. + @retval EFI_OUT_OF_RESOURCES Storage for the new packet could not be allocated. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + SeedPacket is NULL. + SeedPacket is not a well-formed DHCP packet. + AppendCount is not zero and AppendList is NULL. + DeleteCount is not zero and DeleteList is NULL. + NewPacket is NULL + Both DeleteCount and AppendCount are zero and + NewPacket is not NULL. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_DHCP4_BUILD)( + IN EFI_DHCP4_PROTOCOL *This, + IN EFI_DHCP4_PACKET *SeedPacket, + IN UINT32 DeleteCount, + IN UINT8 *DeleteList OPTIONAL, + IN UINT32 AppendCount, + IN EFI_DHCP4_PACKET_OPTION *AppendList[] OPTIONAL, + OUT EFI_DHCP4_PACKET **NewPacket + ); + + +/** + Transmits a DHCP formatted packet and optionally waits for responses. + + The TransmitReceive() function is used to transmit a DHCP packet and optionally + wait for the response from servers. This function does not change the state of + the EFI DHCPv4 Protocol driver. It can be used at any time because of this. + + @param This The pointer to the EFI_DHCP4_PROTOCOL instance. + @param Token The pointer to the EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN structure. + + @retval EFI_SUCCESS The packet was successfully queued for transmission. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + Token.RemoteAddress is zero. + Token.Packet is NULL. + Token.Packet is not a well-formed DHCP packet. + The transaction ID in Token.Packet is in use by another DHCP process. + @retval EFI_NOT_READY The previous call to this function has not finished yet. Try to call + this function after collection process completes. + @retval EFI_NO_MAPPING The default station address is not available yet. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_UNSUPPORTED The implementation doesn't support this function + @retval Others Some other unexpected error occurred. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_DHCP4_TRANSMIT_RECEIVE)( + IN EFI_DHCP4_PROTOCOL *This, + IN EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN *Token + ); + + +/** + Parses the packed DHCP option data. + + The Parse() function is used to retrieve the option list from a DHCP packet. + If *OptionCount isn't zero, and there is enough space for all the DHCP options + in the Packet, each element of PacketOptionList is set to point to somewhere in + the Packet->Dhcp4.Option where a new DHCP option begins. If RFC3396 is supported, + the caller should reassemble the parsed DHCP options to get the final result. + If *OptionCount is zero or there isn't enough space for all of them, the number + of DHCP options in the Packet is returned in OptionCount. + + @param This The pointer to the EFI_DHCP4_PROTOCOL instance. + @param Packet The pointer to packet to be parsed. + @param OptionCount On input, the number of entries in the PacketOptionList. + On output, the number of entries that were written into the + PacketOptionList. + @param PacketOptionList A list of packet option entries to be filled in. End option or pad + options are not included. + + @retval EFI_SUCCESS The packet was successfully parsed. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + The packet is NULL. + The packet is not a well-formed DHCP packet. + OptionCount is NULL. + @retval EFI_BUFFER_TOO_SMALL One or more of the following conditions is TRUE: + 1) *OptionCount is smaller than the number of options that + were found in the Packet. + 2) PacketOptionList is NULL. + @retval EFI_OUT_OF_RESOURCE The packet failed to parse because of a resource shortage. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_DHCP4_PARSE)( + IN EFI_DHCP4_PROTOCOL *This, + IN EFI_DHCP4_PACKET *Packet, + IN OUT UINT32 *OptionCount, + OUT EFI_DHCP4_PACKET_OPTION *PacketOptionList[] OPTIONAL + ); + +/// +/// This protocol is used to collect configuration information for the EFI IPv4 Protocol drivers +/// and to provide DHCPv4 server and PXE boot server discovery services. +/// +struct _EFI_DHCP4_PROTOCOL { + EFI_DHCP4_GET_MODE_DATA GetModeData; + EFI_DHCP4_CONFIGURE Configure; + EFI_DHCP4_START Start; + EFI_DHCP4_RENEW_REBIND RenewRebind; + EFI_DHCP4_RELEASE Release; + EFI_DHCP4_STOP Stop; + EFI_DHCP4_BUILD Build; + EFI_DHCP4_TRANSMIT_RECEIVE TransmitReceive; + EFI_DHCP4_PARSE Parse; +}; + +extern EFI_GUID gEfiDhcp4ProtocolGuid; +extern EFI_GUID gEfiDhcp4ServiceBindingProtocolGuid; + +#endif diff --git a/roms/ipxe/src/include/ipxe/efi/Protocol/DiskIo.h b/roms/ipxe/src/include/ipxe/efi/Protocol/DiskIo.h new file mode 100644 index 0000000..1b47ce5 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/efi/Protocol/DiskIo.h @@ -0,0 +1,119 @@ +/** @file + Disk IO protocol as defined in the UEFI 2.0 specification. + + The Disk IO protocol is used to convert block oriented devices into byte + oriented devices. The Disk IO protocol is intended to layer on top of the + Block IO protocol. + + Copyright (c) 2006 - 2008, Intel Corporation. All rights reserved.
    + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __DISK_IO_H__ +#define __DISK_IO_H__ + +FILE_LICENCE ( BSD3 ); + +#define EFI_DISK_IO_PROTOCOL_GUID \ + { \ + 0xce345171, 0xba0b, 0x11d2, {0x8e, 0x4f, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b } \ + } + +/// +/// Protocol GUID name defined in EFI1.1. +/// +#define DISK_IO_PROTOCOL EFI_DISK_IO_PROTOCOL_GUID + +typedef struct _EFI_DISK_IO_PROTOCOL EFI_DISK_IO_PROTOCOL; + +/// +/// Protocol defined in EFI1.1. +/// +typedef EFI_DISK_IO_PROTOCOL EFI_DISK_IO; + +/** + Read BufferSize bytes from Offset into Buffer. + + @param This Protocol instance pointer. + @param MediaId Id of the media, changes every time the media is replaced. + @param Offset The starting byte offset to read from + @param BufferSize Size of Buffer + @param Buffer Buffer containing read data + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while performing the read. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. + @retval EFI_INVALID_PARAMETER The read request contains device addresses that are not + valid for the device. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_DISK_READ)( + IN EFI_DISK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN UINT64 Offset, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + +/** + Writes a specified number of bytes to a device. + + @param This Indicates a pointer to the calling context. + @param MediaId ID of the medium to be written. + @param Offset The starting byte offset on the logical block I/O device to write. + @param BufferSize The size in bytes of Buffer. The number of bytes to write to the device. + @param Buffer A pointer to the buffer containing the data to be written. + + @retval EFI_SUCCESS The data was written correctly to the device. + @retval EFI_WRITE_PROTECTED The device can not be written to. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. + @retval EFI_INVALID_PARAMETER The write request contains device addresses that are not + valid for the device. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_DISK_WRITE)( + IN EFI_DISK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN UINT64 Offset, + IN UINTN BufferSize, + IN VOID *Buffer + ); + +#define EFI_DISK_IO_PROTOCOL_REVISION 0x00010000 + +/// +/// Revision defined in EFI1.1 +/// +#define EFI_DISK_IO_INTERFACE_REVISION EFI_DISK_IO_PROTOCOL_REVISION + +/// +/// This protocol is used to abstract Block I/O interfaces. +/// +struct _EFI_DISK_IO_PROTOCOL { + /// + /// The revision to which the disk I/O interface adheres. All future + /// revisions must be backwards compatible. If a future version is not + /// backwards compatible, it is not the same GUID. + /// + UINT64 Revision; + EFI_DISK_READ ReadDisk; + EFI_DISK_WRITE WriteDisk; +}; + +extern EFI_GUID gEfiDiskIoProtocolGuid; + +#endif diff --git a/roms/ipxe/src/include/ipxe/efi/Protocol/FormBrowser2.h b/roms/ipxe/src/include/ipxe/efi/Protocol/FormBrowser2.h index 12873c3..0c0f56d 100644 --- a/roms/ipxe/src/include/ipxe/efi/Protocol/FormBrowser2.h +++ b/roms/ipxe/src/include/ipxe/efi/Protocol/FormBrowser2.h @@ -4,7 +4,7 @@ The EFI_FORM_BROWSER2_PROTOCOL is the interface to call for drivers to leverage the EFI configuration driver interface. -Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
    +Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.
    This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License that accompanies this distribution. The full text of the license may be found at @@ -84,7 +84,7 @@ typedef UINTN EFI_BROWSER_ACTION_REQUEST; @param FormSetGuid This field points to the EFI_GUID which must match the Guid field or one of the elements of the ClassId field in the EFI_IFR_FORM_SET op-code. If - FormsetGuid is NULL, then this function will display the the form set class + FormsetGuid is NULL, then this function will display the form set class EFI_HII_PLATFORM_SETUP_FORMSET_GUID. @param FormId This field specifies the identifier of the form within the form set to render as the first diff --git a/roms/ipxe/src/include/ipxe/efi/Protocol/GraphicsOutput.h b/roms/ipxe/src/include/ipxe/efi/Protocol/GraphicsOutput.h new file mode 100644 index 0000000..98ca8c9 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/efi/Protocol/GraphicsOutput.h @@ -0,0 +1,278 @@ +/** @file + Graphics Output Protocol from the UEFI 2.0 specification. + + Abstraction of a very simple graphics device. + + Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.
    + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __GRAPHICS_OUTPUT_H__ +#define __GRAPHICS_OUTPUT_H__ + +FILE_LICENCE ( BSD3 ); + +#define EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID \ + { \ + 0x9042a9de, 0x23dc, 0x4a38, {0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a } \ + } + +typedef struct _EFI_GRAPHICS_OUTPUT_PROTOCOL EFI_GRAPHICS_OUTPUT_PROTOCOL; + +typedef struct { + UINT32 RedMask; + UINT32 GreenMask; + UINT32 BlueMask; + UINT32 ReservedMask; +} EFI_PIXEL_BITMASK; + +typedef enum { + /// + /// A pixel is 32-bits and byte zero represents red, byte one represents green, + /// byte two represents blue, and byte three is reserved. This is the definition + /// for the physical frame buffer. The byte values for the red, green, and blue + /// components represent the color intensity. This color intensity value range + /// from a minimum intensity of 0 to maximum intensity of 255. + /// + PixelRedGreenBlueReserved8BitPerColor, + /// + /// A pixel is 32-bits and byte zero represents blue, byte one represents green, + /// byte two represents red, and byte three is reserved. This is the definition + /// for the physical frame buffer. The byte values for the red, green, and blue + /// components represent the color intensity. This color intensity value range + /// from a minimum intensity of 0 to maximum intensity of 255. + /// + PixelBlueGreenRedReserved8BitPerColor, + /// + /// The Pixel definition of the physical frame buffer. + /// + PixelBitMask, + /// + /// This mode does not support a physical frame buffer. + /// + PixelBltOnly, + /// + /// Valid EFI_GRAPHICS_PIXEL_FORMAT enum values are less than this value. + /// + PixelFormatMax +} EFI_GRAPHICS_PIXEL_FORMAT; + +typedef struct { + /// + /// The version of this data structure. A value of zero represents the + /// EFI_GRAPHICS_OUTPUT_MODE_INFORMATION structure as defined in this specification. + /// + UINT32 Version; + /// + /// The size of video screen in pixels in the X dimension. + /// + UINT32 HorizontalResolution; + /// + /// The size of video screen in pixels in the Y dimension. + /// + UINT32 VerticalResolution; + /// + /// Enumeration that defines the physical format of the pixel. A value of PixelBltOnly + /// implies that a linear frame buffer is not available for this mode. + /// + EFI_GRAPHICS_PIXEL_FORMAT PixelFormat; + /// + /// This bit-mask is only valid if PixelFormat is set to PixelPixelBitMask. + /// A bit being set defines what bits are used for what purpose such as Red, Green, Blue, or Reserved. + /// + EFI_PIXEL_BITMASK PixelInformation; + /// + /// Defines the number of pixel elements per video memory line. + /// + UINT32 PixelsPerScanLine; +} EFI_GRAPHICS_OUTPUT_MODE_INFORMATION; + +/** + Returns information for an available graphics mode that the graphics device + and the set of active video output devices supports. + + @param This The EFI_GRAPHICS_OUTPUT_PROTOCOL instance. + @param ModeNumber The mode number to return information on. + @param SizeOfInfo A pointer to the size, in bytes, of the Info buffer. + @param Info A pointer to callee allocated buffer that returns information about ModeNumber. + + @retval EFI_SUCCESS Valid mode information was returned. + @retval EFI_DEVICE_ERROR A hardware error occurred trying to retrieve the video mode. + @retval EFI_INVALID_PARAMETER ModeNumber is not valid. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_GRAPHICS_OUTPUT_PROTOCOL_QUERY_MODE)( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + IN UINT32 ModeNumber, + OUT UINTN *SizeOfInfo, + OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info + ); + +/** + Set the video device into the specified mode and clears the visible portions of + the output display to black. + + @param This The EFI_GRAPHICS_OUTPUT_PROTOCOL instance. + @param ModeNumber Abstraction that defines the current video mode. + + @retval EFI_SUCCESS The graphics mode specified by ModeNumber was selected. + @retval EFI_DEVICE_ERROR The device had an error and could not complete the request. + @retval EFI_UNSUPPORTED ModeNumber is not supported by this device. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_GRAPHICS_OUTPUT_PROTOCOL_SET_MODE)( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + IN UINT32 ModeNumber + ); + +typedef struct { + UINT8 Blue; + UINT8 Green; + UINT8 Red; + UINT8 Reserved; +} EFI_GRAPHICS_OUTPUT_BLT_PIXEL; + +typedef union { + EFI_GRAPHICS_OUTPUT_BLT_PIXEL Pixel; + UINT32 Raw; +} EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION; + +/// +/// actions for BltOperations +/// +typedef enum { + /// + /// Write data from the BltBuffer pixel (0, 0) + /// directly to every pixel of the video display rectangle + /// (DestinationX, DestinationY) (DestinationX + Width, DestinationY + Height). + /// Only one pixel will be used from the BltBuffer. Delta is NOT used. + /// + EfiBltVideoFill, + + /// + /// Read data from the video display rectangle + /// (SourceX, SourceY) (SourceX + Width, SourceY + Height) and place it in + /// the BltBuffer rectangle (DestinationX, DestinationY ) + /// (DestinationX + Width, DestinationY + Height). If DestinationX or + /// DestinationY is not zero then Delta must be set to the length in bytes + /// of a row in the BltBuffer. + /// + EfiBltVideoToBltBuffer, + + /// + /// Write data from the BltBuffer rectangle + /// (SourceX, SourceY) (SourceX + Width, SourceY + Height) directly to the + /// video display rectangle (DestinationX, DestinationY) + /// (DestinationX + Width, DestinationY + Height). If SourceX or SourceY is + /// not zero then Delta must be set to the length in bytes of a row in the + /// BltBuffer. + /// + EfiBltBufferToVideo, + + /// + /// Copy from the video display rectangle (SourceX, SourceY) + /// (SourceX + Width, SourceY + Height) to the video display rectangle + /// (DestinationX, DestinationY) (DestinationX + Width, DestinationY + Height). + /// The BltBuffer and Delta are not used in this mode. + /// + EfiBltVideoToVideo, + + EfiGraphicsOutputBltOperationMax +} EFI_GRAPHICS_OUTPUT_BLT_OPERATION; + +/** + Blt a rectangle of pixels on the graphics screen. Blt stands for BLock Transfer. + + @param This Protocol instance pointer. + @param BltBuffer The data to transfer to the graphics screen. + Size is at least Width*Height*sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL). + @param BltOperation The operation to perform when copying BltBuffer on to the graphics screen. + @param SourceX The X coordinate of source for the BltOperation. + @param SourceY The Y coordinate of source for the BltOperation. + @param DestinationX The X coordinate of destination for the BltOperation. + @param DestinationY The Y coordinate of destination for the BltOperation. + @param Width The width of a rectangle in the blt rectangle in pixels. + @param Height The height of a rectangle in the blt rectangle in pixels. + @param Delta Not used for EfiBltVideoFill or the EfiBltVideoToVideo operation. + If a Delta of zero is used, the entire BltBuffer is being operated on. + If a subrectangle of the BltBuffer is being used then Delta + represents the number of bytes in a row of the BltBuffer. + + @retval EFI_SUCCESS BltBuffer was drawn to the graphics screen. + @retval EFI_INVALID_PARAMETER BltOperation is not valid. + @retval EFI_DEVICE_ERROR The device had an error and could not complete the request. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_GRAPHICS_OUTPUT_PROTOCOL_BLT)( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL + IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation, + IN UINTN SourceX, + IN UINTN SourceY, + IN UINTN DestinationX, + IN UINTN DestinationY, + IN UINTN Width, + IN UINTN Height, + IN UINTN Delta OPTIONAL + ); + +typedef struct { + /// + /// The number of modes supported by QueryMode() and SetMode(). + /// + UINT32 MaxMode; + /// + /// Current Mode of the graphics device. Valid mode numbers are 0 to MaxMode -1. + /// + UINT32 Mode; + /// + /// Pointer to read-only EFI_GRAPHICS_OUTPUT_MODE_INFORMATION data. + /// + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info; + /// + /// Size of Info structure in bytes. + /// + UINTN SizeOfInfo; + /// + /// Base address of graphics linear frame buffer. + /// Offset zero in FrameBufferBase represents the upper left pixel of the display. + /// + EFI_PHYSICAL_ADDRESS FrameBufferBase; + /// + /// Amount of frame buffer needed to support the active mode as defined by + /// PixelsPerScanLine xVerticalResolution x PixelElementSize. + /// + UINTN FrameBufferSize; +} EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE; + +/// +/// Provides a basic abstraction to set video modes and copy pixels to and from +/// the graphics controller's frame buffer. The linear address of the hardware +/// frame buffer is also exposed so software can write directly to the video hardware. +/// +struct _EFI_GRAPHICS_OUTPUT_PROTOCOL { + EFI_GRAPHICS_OUTPUT_PROTOCOL_QUERY_MODE QueryMode; + EFI_GRAPHICS_OUTPUT_PROTOCOL_SET_MODE SetMode; + EFI_GRAPHICS_OUTPUT_PROTOCOL_BLT Blt; + /// + /// Pointer to EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE data. + /// + EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *Mode; +}; + +extern EFI_GUID gEfiGraphicsOutputProtocolGuid; + +#endif diff --git a/roms/ipxe/src/include/ipxe/efi/Protocol/HiiConfigAccess.h b/roms/ipxe/src/include/ipxe/efi/Protocol/HiiConfigAccess.h index 929d9cd..17ce3fd 100644 --- a/roms/ipxe/src/include/ipxe/efi/Protocol/HiiConfigAccess.h +++ b/roms/ipxe/src/include/ipxe/efi/Protocol/HiiConfigAccess.h @@ -5,7 +5,7 @@ This protocol is published by drivers providing and requesting configuration data from HII. It may only be invoked by HII. -Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
    +Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.
    This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License that accompanies this distribution. The full text of the license may be found at @@ -96,13 +96,10 @@ typedef UINTN EFI_BROWSER_ACTION; stored awaiting possible future protocols. - @retval EFI_NOT_FOUND Routing data doesn't match any - known driver. Progress set to the - first character in the routing header. - Note: There is no requirement that the - driver validate the routing data. It - must skip the in order to - process the names. + @retval EFI_NOT_FOUND A configuration element matching + the routing data is not found. + Progress set to the first character + in the routing header. @retval EFI_INVALID_PARAMETER Illegal syntax. Progress set to most recent "&" before the diff --git a/roms/ipxe/src/include/ipxe/efi/Protocol/HiiDatabase.h b/roms/ipxe/src/include/ipxe/efi/Protocol/HiiDatabase.h index 411ca2c..cbc0108 100644 --- a/roms/ipxe/src/include/ipxe/efi/Protocol/HiiDatabase.h +++ b/roms/ipxe/src/include/ipxe/efi/Protocol/HiiDatabase.h @@ -225,7 +225,9 @@ EFI_STATUS updated with a value that will enable the data to fit. @retval EFI_NOT_FOUND No matching handle could be found in database. - @retval EFI_INVALID_PARAMETER Handle or HandleBufferLength was NULL. + @retval EFI_INVALID_PARAMETER HandleBufferLength was NULL. + @retval EFI_INVALID_PARAMETER The value referenced by HandleBufferLength was not + zero and Handle was NULL. @retval EFI_INVALID_PARAMETER PackageType is not a EFI_HII_PACKAGE_TYPE_GUID but PackageGuid is not NULL, PackageType is a EFI_HII_ PACKAGE_TYPE_GUID but PackageGuid is NULL. @@ -273,6 +275,13 @@ EFI_STATUS @retval EFI_OUT_OF_RESOURCES BufferSize is too small to hold the package. + @retval EFI_NOT_FOUND The specifiecd Handle could not be found in the + current database. + + @retval EFI_INVALID_PARAMETER BufferSize was NULL. + + @retval EFI_INVALID_PARAMETER The value referenced by BufferSize was not zero + and Buffer was NULL. **/ typedef EFI_STATUS @@ -395,6 +404,11 @@ EFI_STATUS KeyGuidBufferLength is updated with a value that will enable the data to fit. + @retval EFI_INVALID_PARAMETER The KeyGuidBufferLength is NULL. + @retval EFI_INVALID_PARAMETER The value referenced by + KeyGuidBufferLength is not + zero and KeyGuidBuffer is NULL. + @retval EFI_NOT_FOUND There was no keyboard layout. **/ typedef diff --git a/roms/ipxe/src/include/ipxe/efi/Protocol/Ip4.h b/roms/ipxe/src/include/ipxe/efi/Protocol/Ip4.h new file mode 100644 index 0000000..f174c0c --- /dev/null +++ b/roms/ipxe/src/include/ipxe/efi/Protocol/Ip4.h @@ -0,0 +1,614 @@ +/** @file + This file defines the EFI IPv4 (Internet Protocol version 4) + Protocol interface. It is split into the following three main + sections: + - EFI IPv4 Service Binding Protocol + - EFI IPv4 Variable (deprecated in UEFI 2.4B) + - EFI IPv4 Protocol. + The EFI IPv4 Protocol provides basic network IPv4 packet I/O services, + which includes support foR a subset of the Internet Control Message + Protocol (ICMP) and may include support for the Internet Group Management + Protocol (IGMP). + +Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.
    +This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + @par Revision Reference: + This Protocol is introduced in UEFI Specification 2.0. + +**/ + +#ifndef __EFI_IP4_PROTOCOL_H__ +#define __EFI_IP4_PROTOCOL_H__ + +FILE_LICENCE ( BSD3 ); + +#include + +#define EFI_IP4_SERVICE_BINDING_PROTOCOL_GUID \ + { \ + 0xc51711e7, 0xb4bf, 0x404a, {0xbf, 0xb8, 0x0a, 0x04, 0x8e, 0xf1, 0xff, 0xe4 } \ + } + +#define EFI_IP4_PROTOCOL_GUID \ + { \ + 0x41d94cd2, 0x35b6, 0x455a, {0x82, 0x58, 0xd4, 0xe5, 0x13, 0x34, 0xaa, 0xdd } \ + } + +typedef struct _EFI_IP4_PROTOCOL EFI_IP4_PROTOCOL; + +/// +/// EFI_IP4_ADDRESS_PAIR is deprecated in the UEFI 2.4B and should not be used any more. +/// The definition in here is only present to provide backwards compatability. +/// +typedef struct { + EFI_HANDLE InstanceHandle; + EFI_IPv4_ADDRESS Ip4Address; + EFI_IPv4_ADDRESS SubnetMask; +} EFI_IP4_ADDRESS_PAIR; + +/// +/// EFI_IP4_VARIABLE_DATA is deprecated in the UEFI 2.4B and should not be used any more. +/// The definition in here is only present to provide backwards compatability. +/// +typedef struct { + EFI_HANDLE DriverHandle; + UINT32 AddressCount; + EFI_IP4_ADDRESS_PAIR AddressPairs[1]; +} EFI_IP4_VARIABLE_DATA; + +typedef struct { + /// + /// The default IPv4 protocol packets to send and receive. Ignored + /// when AcceptPromiscuous is TRUE. + /// + UINT8 DefaultProtocol; + /// + /// Set to TRUE to receive all IPv4 packets that get through the receive filters. + /// Set to FALSE to receive only the DefaultProtocol IPv4 + /// packets that get through the receive filters. + /// + BOOLEAN AcceptAnyProtocol; + /// + /// Set to TRUE to receive ICMP error report packets. Ignored when + /// AcceptPromiscuous or AcceptAnyProtocol is TRUE. + /// + BOOLEAN AcceptIcmpErrors; + /// + /// Set to TRUE to receive broadcast IPv4 packets. Ignored when + /// AcceptPromiscuous is TRUE. + /// Set to FALSE to stop receiving broadcast IPv4 packets. + /// + BOOLEAN AcceptBroadcast; + /// + /// Set to TRUE to receive all IPv4 packets that are sent to any + /// hardware address or any protocol address. + /// Set to FALSE to stop receiving all promiscuous IPv4 packets + /// + BOOLEAN AcceptPromiscuous; + /// + /// Set to TRUE to use the default IPv4 address and default routing table. + /// + BOOLEAN UseDefaultAddress; + /// + /// The station IPv4 address that will be assigned to this EFI IPv4Protocol instance. + /// + EFI_IPv4_ADDRESS StationAddress; + /// + /// The subnet address mask that is associated with the station address. + /// + EFI_IPv4_ADDRESS SubnetMask; + /// + /// TypeOfService field in transmitted IPv4 packets. + /// + UINT8 TypeOfService; + /// + /// TimeToLive field in transmitted IPv4 packets. + /// + UINT8 TimeToLive; + /// + /// State of the DoNotFragment bit in transmitted IPv4 packets. + /// + BOOLEAN DoNotFragment; + /// + /// Set to TRUE to send and receive unformatted packets. The other + /// IPv4 receive filters are still applied. Fragmentation is disabled for RawData mode. + /// + BOOLEAN RawData; + /// + /// The timer timeout value (number of microseconds) for the + /// receive timeout event to be associated with each assembled + /// packet. Zero means do not drop assembled packets. + /// + UINT32 ReceiveTimeout; + /// + /// The timer timeout value (number of microseconds) for the + /// transmit timeout event to be associated with each outgoing + /// packet. Zero means do not drop outgoing packets. + /// + UINT32 TransmitTimeout; +} EFI_IP4_CONFIG_DATA; + + +typedef struct { + EFI_IPv4_ADDRESS SubnetAddress; + EFI_IPv4_ADDRESS SubnetMask; + EFI_IPv4_ADDRESS GatewayAddress; +} EFI_IP4_ROUTE_TABLE; + +typedef struct { + UINT8 Type; + UINT8 Code; +} EFI_IP4_ICMP_TYPE; + +typedef struct { + /// + /// Set to TRUE after this EFI IPv4 Protocol instance has been successfully configured. + /// + BOOLEAN IsStarted; + /// + /// The maximum packet size, in bytes, of the packet which the upper layer driver could feed. + /// + UINT32 MaxPacketSize; + /// + /// Current configuration settings. + /// + EFI_IP4_CONFIG_DATA ConfigData; + /// + /// Set to TRUE when the EFI IPv4 Protocol instance has a station address and subnet mask. + /// + BOOLEAN IsConfigured; + /// + /// Number of joined multicast groups. + /// + UINT32 GroupCount; + /// + /// List of joined multicast group addresses. + /// + EFI_IPv4_ADDRESS *GroupTable; + /// + /// Number of entries in the routing table. + /// + UINT32 RouteCount; + /// + /// Routing table entries. + /// + EFI_IP4_ROUTE_TABLE *RouteTable; + /// + /// Number of entries in the supported ICMP types list. + /// + UINT32 IcmpTypeCount; + /// + /// Array of ICMP types and codes that are supported by this EFI IPv4 Protocol driver + /// + EFI_IP4_ICMP_TYPE *IcmpTypeList; +} EFI_IP4_MODE_DATA; + +#pragma pack(1) + +typedef struct { + UINT8 HeaderLength:4; + UINT8 Version:4; + UINT8 TypeOfService; + UINT16 TotalLength; + UINT16 Identification; + UINT16 Fragmentation; + UINT8 TimeToLive; + UINT8 Protocol; + UINT16 Checksum; + EFI_IPv4_ADDRESS SourceAddress; + EFI_IPv4_ADDRESS DestinationAddress; +} EFI_IP4_HEADER; +#pragma pack() + + +typedef struct { + UINT32 FragmentLength; + VOID *FragmentBuffer; +} EFI_IP4_FRAGMENT_DATA; + + +typedef struct { + EFI_TIME TimeStamp; + EFI_EVENT RecycleSignal; + UINT32 HeaderLength; + EFI_IP4_HEADER *Header; + UINT32 OptionsLength; + VOID *Options; + UINT32 DataLength; + UINT32 FragmentCount; + EFI_IP4_FRAGMENT_DATA FragmentTable[1]; +} EFI_IP4_RECEIVE_DATA; + + +typedef struct { + EFI_IPv4_ADDRESS SourceAddress; + EFI_IPv4_ADDRESS GatewayAddress; + UINT8 Protocol; + UINT8 TypeOfService; + UINT8 TimeToLive; + BOOLEAN DoNotFragment; +} EFI_IP4_OVERRIDE_DATA; + +typedef struct { + EFI_IPv4_ADDRESS DestinationAddress; + EFI_IP4_OVERRIDE_DATA *OverrideData; //OPTIONAL + UINT32 OptionsLength; //OPTIONAL + VOID *OptionsBuffer; //OPTIONAL + UINT32 TotalDataLength; + UINT32 FragmentCount; + EFI_IP4_FRAGMENT_DATA FragmentTable[1]; +} EFI_IP4_TRANSMIT_DATA; + +typedef struct { + /// + /// This Event will be signaled after the Status field is updated + /// by the EFI IPv4 Protocol driver. The type of Event must be + /// EFI_NOTIFY_SIGNAL. The Task Priority Level (TPL) of + /// Event must be lower than or equal to TPL_CALLBACK. + /// + EFI_EVENT Event; + /// + /// The status that is returned to the caller at the end of the operation + /// to indicate whether this operation completed successfully. + /// + EFI_STATUS Status; + union { + /// + /// When this token is used for receiving, RxData is a pointer to the EFI_IP4_RECEIVE_DATA. + /// + EFI_IP4_RECEIVE_DATA *RxData; + /// + /// When this token is used for transmitting, TxData is a pointer to the EFI_IP4_TRANSMIT_DATA. + /// + EFI_IP4_TRANSMIT_DATA *TxData; + } Packet; +} EFI_IP4_COMPLETION_TOKEN; + +/** + Gets the current operational settings for this instance of the EFI IPv4 Protocol driver. + + The GetModeData() function returns the current operational mode data for this + driver instance. The data fields in EFI_IP4_MODE_DATA are read only. This + function is used optionally to retrieve the operational mode data of underlying + networks or drivers. + + @param This The pointer to the EFI_IP4_PROTOCOL instance. + @param Ip4ModeData The pointer to the EFI IPv4 Protocol mode data structure. + @param MnpConfigData The pointer to the managed network configuration data structure. + @param SnpModeData The pointer to the simple network mode data structure. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_OUT_OF_RESOURCES The required mode data could not be allocated. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_IP4_GET_MODE_DATA)( + IN CONST EFI_IP4_PROTOCOL *This, + OUT EFI_IP4_MODE_DATA *Ip4ModeData OPTIONAL, + OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL, + OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL + ); + +/** + Assigns an IPv4 address and subnet mask to this EFI IPv4 Protocol driver instance. + + The Configure() function is used to set, change, or reset the operational + parameters and filter settings for this EFI IPv4 Protocol instance. Until these + parameters have been set, no network traffic can be sent or received by this + instance. Once the parameters have been reset (by calling this function with + IpConfigData set to NULL), no more traffic can be sent or received until these + parameters have been set again. Each EFI IPv4 Protocol instance can be started + and stopped independently of each other by enabling or disabling their receive + filter settings with the Configure() function. + + When IpConfigData.UseDefaultAddress is set to FALSE, the new station address will + be appended as an alias address into the addresses list in the EFI IPv4 Protocol + driver. While set to TRUE, Configure() will trigger the EFI_IP4_CONFIG_PROTOCOL + to retrieve the default IPv4 address if it is not available yet. Clients could + frequently call GetModeData() to check the status to ensure that the default IPv4 + address is ready. + + If operational parameters are reset or changed, any pending transmit and receive + requests will be cancelled. Their completion token status will be set to EFI_ABORTED + and their events will be signaled. + + @param This The pointer to the EFI_IP4_PROTOCOL instance. + @param IpConfigData The pointer to the EFI IPv4 Protocol configuration data structure. + + @retval EFI_SUCCESS The driver instance was successfully opened. + @retval EFI_NO_MAPPING When using the default address, configuration (DHCP, BOOTP, + RARP, etc.) is not finished yet. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + IpConfigData.StationAddress is not a unicast IPv4 address. + IpConfigData.SubnetMask is not a valid IPv4 subnet + @retval EFI_UNSUPPORTED One or more of the following conditions is TRUE: + A configuration protocol (DHCP, BOOTP, RARP, etc.) could + not be located when clients choose to use the default IPv4 + address. This EFI IPv4 Protocol implementation does not + support this requested filter or timeout setting. + @retval EFI_OUT_OF_RESOURCES The EFI IPv4 Protocol driver instance data could not be allocated. + @retval EFI_ALREADY_STARTED The interface is already open and must be stopped before the + IPv4 address or subnet mask can be changed. The interface must + also be stopped when switching to/from raw packet mode. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The EFI IPv4 + Protocol driver instance is not opened. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_IP4_CONFIGURE)( + IN EFI_IP4_PROTOCOL *This, + IN EFI_IP4_CONFIG_DATA *IpConfigData OPTIONAL + ); + +/** + Joins and leaves multicast groups. + + The Groups() function is used to join and leave multicast group sessions. Joining + a group will enable reception of matching multicast packets. Leaving a group will + disable the multicast packet reception. + + If JoinFlag is FALSE and GroupAddress is NULL, all joined groups will be left. + + @param This The pointer to the EFI_IP4_PROTOCOL instance. + @param JoinFlag Set to TRUE to join the multicast group session and FALSE to leave. + @param GroupAddress The pointer to the IPv4 multicast address. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER One or more of the following is TRUE: + - This is NULL. + - JoinFlag is TRUE and GroupAddress is NULL. + - GroupAddress is not NULL and *GroupAddress is + not a multicast IPv4 address. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_NO_MAPPING When using the default address, configuration (DHCP, BOOTP, + RARP, etc.) is not finished yet. + @retval EFI_OUT_OF_RESOURCES System resources could not be allocated. + @retval EFI_UNSUPPORTED This EFI IPv4 Protocol implementation does not support multicast groups. + @retval EFI_ALREADY_STARTED The group address is already in the group table (when + JoinFlag is TRUE). + @retval EFI_NOT_FOUND The group address is not in the group table (when JoinFlag is FALSE). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_IP4_GROUPS)( + IN EFI_IP4_PROTOCOL *This, + IN BOOLEAN JoinFlag, + IN EFI_IPv4_ADDRESS *GroupAddress OPTIONAL + ); + +/** + Adds and deletes routing table entries. + + The Routes() function adds a route to or deletes a route from the routing table. + + Routes are determined by comparing the SubnetAddress with the destination IPv4 + address arithmetically AND-ed with the SubnetMask. The gateway address must be + on the same subnet as the configured station address. + + The default route is added with SubnetAddress and SubnetMask both set to 0.0.0.0. + The default route matches all destination IPv4 addresses that do not match any + other routes. + + A GatewayAddress that is zero is a nonroute. Packets are sent to the destination + IP address if it can be found in the ARP cache or on the local subnet. One automatic + nonroute entry will be inserted into the routing table for outgoing packets that + are addressed to a local subnet (gateway address of 0.0.0.0). + + Each EFI IPv4 Protocol instance has its own independent routing table. Those EFI + IPv4 Protocol instances that use the default IPv4 address will also have copies + of the routing table that was provided by the EFI_IP4_CONFIG_PROTOCOL, and these + copies will be updated whenever the EIF IPv4 Protocol driver reconfigures its + instances. As a result, client modification to the routing table will be lost. + + @param This The pointer to the EFI_IP4_PROTOCOL instance. + @param DeleteRoute Set to TRUE to delete this route from the routing table. Set to + FALSE to add this route to the routing table. SubnetAddress + and SubnetMask are used as the key to each route entry. + @param SubnetAddress The address of the subnet that needs to be routed. + @param SubnetMask The subnet mask of SubnetAddress. + @param GatewayAddress The unicast gateway IPv4 address for this route. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_STARTED The driver instance has not been started. + @retval EFI_NO_MAPPING When using the default address, configuration (DHCP, BOOTP, + RARP, etc.) is not finished yet. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - SubnetAddress is NULL. + - SubnetMask is NULL. + - GatewayAddress is NULL. + - *SubnetAddress is not a valid subnet address. + - *SubnetMask is not a valid subnet mask. + - *GatewayAddress is not a valid unicast IPv4 address. + @retval EFI_OUT_OF_RESOURCES Could not add the entry to the routing table. + @retval EFI_NOT_FOUND This route is not in the routing table (when DeleteRoute is TRUE). + @retval EFI_ACCESS_DENIED The route is already defined in the routing table (when + DeleteRoute is FALSE). + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_IP4_ROUTES)( + IN EFI_IP4_PROTOCOL *This, + IN BOOLEAN DeleteRoute, + IN EFI_IPv4_ADDRESS *SubnetAddress, + IN EFI_IPv4_ADDRESS *SubnetMask, + IN EFI_IPv4_ADDRESS *GatewayAddress + ); + +/** + Places outgoing data packets into the transmit queue. + + The Transmit() function places a sending request in the transmit queue of this + EFI IPv4 Protocol instance. Whenever the packet in the token is sent out or some + errors occur, the event in the token will be signaled and the status is updated. + + @param This The pointer to the EFI_IP4_PROTOCOL instance. + @param Token The pointer to the transmit token. + + @retval EFI_SUCCESS The data has been queued for transmission. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_NO_MAPPING When using the default address, configuration (DHCP, BOOTP, + RARP, etc.) is not finished yet. + @retval EFI_INVALID_PARAMETER One or more pameters are invalid. + @retval EFI_ACCESS_DENIED The transmit completion token with the same Token.Event + was already in the transmit queue. + @retval EFI_NOT_READY The completion token could not be queued because the transmit + queue is full. + @retval EFI_NOT_FOUND Not route is found to destination address. + @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data. + @retval EFI_BUFFER_TOO_SMALL Token.Packet.TxData.TotalDataLength is too + short to transmit. + @retval EFI_BAD_BUFFER_SIZE The length of the IPv4 header + option length + total data length is + greater than MTU (or greater than the maximum packet size if + Token.Packet.TxData.OverrideData. + DoNotFragment is TRUE.) + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_IP4_TRANSMIT)( + IN EFI_IP4_PROTOCOL *This, + IN EFI_IP4_COMPLETION_TOKEN *Token + ); + +/** + Places a receiving request into the receiving queue. + + The Receive() function places a completion token into the receive packet queue. + This function is always asynchronous. + + The Token.Event field in the completion token must be filled in by the caller + and cannot be NULL. When the receive operation completes, the EFI IPv4 Protocol + driver updates the Token.Status and Token.Packet.RxData fields and the Token.Event + is signaled. + + @param This The pointer to the EFI_IP4_PROTOCOL instance. + @param Token The pointer to a token that is associated with the receive data descriptor. + + @retval EFI_SUCCESS The receive completion token was cached. + @retval EFI_NOT_STARTED This EFI IPv4 Protocol instance has not been started. + @retval EFI_NO_MAPPING When using the default address, configuration (DHCP, BOOTP, RARP, etc.) + is not finished yet. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Token is NULL. + - Token.Event is NULL. + @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued due to a lack of system + resources (usually memory). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + The EFI IPv4 Protocol instance has been reset to startup defaults. + @retval EFI_ACCESS_DENIED The receive completion token with the same Token.Event was already + in the receive queue. + @retval EFI_NOT_READY The receive request could not be queued because the receive queue is full. + @retval EFI_ICMP_ERROR An ICMP error packet was received. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_IP4_RECEIVE)( + IN EFI_IP4_PROTOCOL *This, + IN EFI_IP4_COMPLETION_TOKEN *Token + ); + +/** + Abort an asynchronous transmit or receive request. + + The Cancel() function is used to abort a pending transmit or receive request. + If the token is in the transmit or receive request queues, after calling this + function, Token->Status will be set to EFI_ABORTED and then Token->Event will + be signaled. If the token is not in one of the queues, which usually means the + asynchronous operation has completed, this function will not signal the token + and EFI_NOT_FOUND is returned. + + @param This The pointer to the EFI_IP4_PROTOCOL instance. + @param Token The pointer to a token that has been issued by + EFI_IP4_PROTOCOL.Transmit() or + EFI_IP4_PROTOCOL.Receive(). If NULL, all pending + tokens are aborted. Type EFI_IP4_COMPLETION_TOKEN is + defined in EFI_IP4_PROTOCOL.Transmit(). + + @retval EFI_SUCCESS The asynchronous I/O request was aborted and + Token->Event was signaled. When Token is NULL, all + pending requests were aborted and their events were signaled. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_NO_MAPPING When using the default address, configuration (DHCP, BOOTP, + RARP, etc.) is not finished yet. + @retval EFI_NOT_FOUND When Token is not NULL, the asynchronous I/O request was + not found in the transmit or receive queue. It has either completed + or was not issued by Transmit() and Receive(). + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_IP4_CANCEL)( + IN EFI_IP4_PROTOCOL *This, + IN EFI_IP4_COMPLETION_TOKEN *Token OPTIONAL + ); + +/** + Polls for incoming data packets and processes outgoing data packets. + + The Poll() function polls for incoming data packets and processes outgoing data + packets. Network drivers and applications can call the EFI_IP4_PROTOCOL.Poll() + function to increase the rate that data packets are moved between the communications + device and the transmit and receive queues. + + In some systems the periodic timer event may not poll the underlying communications + device fast enough to transmit and/or receive all data packets without missing + incoming packets or dropping outgoing packets. Drivers and applications that are + experiencing packet loss should try calling the EFI_IP4_PROTOCOL.Poll() function + more often. + + @param This The pointer to the EFI_IP4_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_NOT_STARTED This EFI IPv4 Protocol instance has not been started. + @retval EFI_NO_MAPPING When using the default address, configuration (DHCP, BOOTP, + RARP, etc.) is not finished yet. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_NOT_READY No incoming or outgoing data is processed. + @retval EFI_TIMEOUT Data was dropped out of the transmit and/or receive queue. + Consider increasing the polling rate. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_IP4_POLL)( + IN EFI_IP4_PROTOCOL *This + ); + +/// +/// The EFI IPv4 Protocol implements a simple packet-oriented interface that can be +/// used by drivers, daemons, and applications to transmit and receive network packets. +/// +struct _EFI_IP4_PROTOCOL { + EFI_IP4_GET_MODE_DATA GetModeData; + EFI_IP4_CONFIGURE Configure; + EFI_IP4_GROUPS Groups; + EFI_IP4_ROUTES Routes; + EFI_IP4_TRANSMIT Transmit; + EFI_IP4_RECEIVE Receive; + EFI_IP4_CANCEL Cancel; + EFI_IP4_POLL Poll; +}; + +extern EFI_GUID gEfiIp4ServiceBindingProtocolGuid; +extern EFI_GUID gEfiIp4ProtocolGuid; + +#endif diff --git a/roms/ipxe/src/include/ipxe/efi/Protocol/Ip4Config.h b/roms/ipxe/src/include/ipxe/efi/Protocol/Ip4Config.h new file mode 100644 index 0000000..227ae03 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/efi/Protocol/Ip4Config.h @@ -0,0 +1,184 @@ +/** @file + This file provides a definition of the EFI IPv4 Configuration + Protocol. + +Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.
    +This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + @par Revision Reference: + This Protocol is introduced in UEFI Specification 2.0. + +**/ +#ifndef __EFI_IP4CONFIG_PROTOCOL_H__ +#define __EFI_IP4CONFIG_PROTOCOL_H__ + +FILE_LICENCE ( BSD3 ); + +#include + +#define EFI_IP4_CONFIG_PROTOCOL_GUID \ + { \ + 0x3b95aa31, 0x3793, 0x434b, {0x86, 0x67, 0xc8, 0x07, 0x08, 0x92, 0xe0, 0x5e } \ + } + +typedef struct _EFI_IP4_CONFIG_PROTOCOL EFI_IP4_CONFIG_PROTOCOL; + +#define IP4_CONFIG_VARIABLE_ATTRIBUTES \ + (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS) + +/// +/// EFI_IP4_IPCONFIG_DATA contains the minimum IPv4 configuration data +/// that is needed to start basic network communication. The StationAddress +/// and SubnetMask must be a valid unicast IP address and subnet mask. +/// If RouteTableSize is not zero, then RouteTable contains a properly +/// formatted routing table for the StationAddress/SubnetMask, with the +/// last entry in the table being the default route. +/// +typedef struct { + /// + /// Default station IP address, stored in network byte order. + /// + EFI_IPv4_ADDRESS StationAddress; + /// + /// Default subnet mask, stored in network byte order. + /// + EFI_IPv4_ADDRESS SubnetMask; + /// + /// Number of entries in the following RouteTable. May be zero. + /// + UINT32 RouteTableSize; + /// + /// Default routing table data (stored in network byte order). + /// Ignored if RouteTableSize is zero. + /// + EFI_IP4_ROUTE_TABLE *RouteTable; +} EFI_IP4_IPCONFIG_DATA; + + +/** + Starts running the configuration policy for the EFI IPv4 Protocol driver. + + The Start() function is called to determine and to begin the platform + configuration policy by the EFI IPv4 Protocol driver. This determination may + be as simple as returning EFI_UNSUPPORTED if there is no EFI IPv4 Protocol + driver configuration policy. It may be as involved as loading some defaults + from nonvolatile storage, downloading dynamic data from a DHCP server, and + checking permissions with a site policy server. + Starting the configuration policy is just the beginning. It may finish almost + instantly or it may take several minutes before it fails to retrieve configuration + information from one or more servers. Once the policy is started, drivers + should use the DoneEvent parameter to determine when the configuration policy + has completed. EFI_IP4_CONFIG_PROTOCOL.GetData() must then be called to + determine if the configuration succeeded or failed. + Until the configuration completes successfully, EFI IPv4 Protocol driver instances + that are attempting to use default configurations must return EFI_NO_MAPPING. + Once the configuration is complete, the EFI IPv4 Configuration Protocol driver + signals DoneEvent. The configuration may need to be updated in the future. + Note that in this case the EFI IPv4 Configuration Protocol driver must signal + ReconfigEvent, and all EFI IPv4 Protocol driver instances that are using default + configurations must return EFI_NO_MAPPING until the configuration policy has + been rerun. + + @param This The pointer to the EFI_IP4_CONFIG_PROTOCOL instance. + @param DoneEvent Event that will be signaled when the EFI IPv4 + Protocol driver configuration policy completes + execution. This event must be of type EVT_NOTIFY_SIGNAL. + @param ReconfigEvent Event that will be signaled when the EFI IPv4 + Protocol driver configuration needs to be updated. + This event must be of type EVT_NOTIFY_SIGNAL. + + @retval EFI_SUCCESS The configuration policy for the EFI IPv4 Protocol + driver is now running. + @retval EFI_INVALID_PARAMETER One or more of the following parameters is NULL: + This + DoneEvent + ReconfigEvent + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_ALREADY_STARTED The configuration policy for the EFI IPv4 Protocol + driver was already started. + @retval EFI_DEVICE_ERROR An unexpected system error or network error occurred. + @retval EFI_UNSUPPORTED This interface does not support the EFI IPv4 Protocol + driver configuration. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_IP4_CONFIG_START)( + IN EFI_IP4_CONFIG_PROTOCOL *This, + IN EFI_EVENT DoneEvent, + IN EFI_EVENT ReconfigEvent + ); + +/** + Stops running the configuration policy for the EFI IPv4 Protocol driver. + + The Stop() function stops the configuration policy for the EFI IPv4 Protocol driver. + All configuration data will be lost after calling Stop(). + + @param This The pointer to the EFI_IP4_CONFIG_PROTOCOL instance. + + @retval EFI_SUCCESS The configuration policy for the EFI IPv4 Protocol + driver has been stopped. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_STARTED The configuration policy for the EFI IPv4 Protocol + driver was not started. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_IP4_CONFIG_STOP)( + IN EFI_IP4_CONFIG_PROTOCOL *This + ); + +/** + Returns the default configuration data (if any) for the EFI IPv4 Protocol driver. + + The GetData() function returns the current configuration data for the EFI IPv4 + Protocol driver after the configuration policy has completed. + + @param This The pointer to the EFI_IP4_CONFIG_PROTOCOL instance. + @param IpConfigDataSize On input, the size of the IpConfigData buffer. + On output, the count of bytes that were written + into the IpConfigData buffer. + @param IpConfigData The pointer to the EFI IPv4 Configuration Protocol + driver configuration data structure. + Type EFI_IP4_IPCONFIG_DATA is defined in + "Related Definitions" below. + + @retval EFI_SUCCESS The EFI IPv4 Protocol driver configuration has been returned. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_STARTED The configuration policy for the EFI IPv4 Protocol + driver is not running. + @retval EFI_NOT_READY EFI IPv4 Protocol driver configuration is still running. + @retval EFI_ABORTED EFI IPv4 Protocol driver configuration could not complete. + @retval EFI_BUFFER_TOO_SMALL *IpConfigDataSize is smaller than the configuration + data buffer or IpConfigData is NULL. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_IP4_CONFIG_GET_DATA)( + IN EFI_IP4_CONFIG_PROTOCOL *This, + IN OUT UINTN *IpConfigDataSize, + OUT EFI_IP4_IPCONFIG_DATA *IpConfigData OPTIONAL + ); + +/// +/// The EFI_IP4_CONFIG_PROTOCOL driver performs platform-dependent and policy-dependent +/// configurations for the EFI IPv4 Protocol driver. +/// +struct _EFI_IP4_CONFIG_PROTOCOL { + EFI_IP4_CONFIG_START Start; + EFI_IP4_CONFIG_STOP Stop; + EFI_IP4_CONFIG_GET_DATA GetData; +}; + +extern EFI_GUID gEfiIp4ConfigProtocolGuid; + +#endif diff --git a/roms/ipxe/src/include/ipxe/efi/Protocol/LoadFile2.h b/roms/ipxe/src/include/ipxe/efi/Protocol/LoadFile2.h new file mode 100644 index 0000000..6cb26ff --- /dev/null +++ b/roms/ipxe/src/include/ipxe/efi/Protocol/LoadFile2.h @@ -0,0 +1,87 @@ +/** @file + Load File protocol as defined in the UEFI 2.0 specification. + + Load file protocol exists to supports the addition of new boot devices, + and to support booting from devices that do not map well to file system. + Network boot is done via a LoadFile protocol. + + UEFI 2.0 can boot from any device that produces a LoadFile protocol. + + Copyright (c) 2006 - 2008, Intel Corporation. All rights reserved.
    + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_LOAD_FILE2_PROTOCOL_H__ +#define __EFI_LOAD_FILE2_PROTOCOL_H__ + +FILE_LICENCE ( BSD3 ); + +#define EFI_LOAD_FILE2_PROTOCOL_GUID \ + { \ + 0x4006c0c1, 0xfcb3, 0x403e, {0x99, 0x6d, 0x4a, 0x6c, 0x87, 0x24, 0xe0, 0x6d } \ + } + +/// +/// Protocol Guid defined by UEFI2.1. +/// +#define LOAD_FILE2_PROTOCOL EFI_LOAD_FILE2_PROTOCOL_GUID + +typedef struct _EFI_LOAD_FILE2_PROTOCOL EFI_LOAD_FILE2_PROTOCOL; + + +/** + Causes the driver to load a specified file. + + @param This Protocol instance pointer. + @param FilePath The device specific path of the file to load. + @param BootPolicy Should always be FALSE. + @param BufferSize On input the size of Buffer in bytes. On output with a return + code of EFI_SUCCESS, the amount of data transferred to + Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL, + the size of Buffer required to retrieve the requested file. + @param Buffer The memory buffer to transfer the file to. IF Buffer is NULL, + then no the size of the requested file is returned in + BufferSize. + + @retval EFI_SUCCESS The file was loaded. + @retval EFI_UNSUPPORTED BootPolicy is TRUE. + @retval EFI_INVALID_PARAMETER FilePath is not a valid device path, or + BufferSize is NULL. + @retval EFI_NO_MEDIA No medium was present to load the file. + @retval EFI_DEVICE_ERROR The file was not loaded due to a device error. + @retval EFI_NO_RESPONSE The remote system did not respond. + @retval EFI_NOT_FOUND The file was not found + @retval EFI_ABORTED The file load process was manually canceled. + @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current + directory entry. BufferSize has been updated with + the size needed to complete the request. + + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_LOAD_FILE2)( + IN EFI_LOAD_FILE2_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN BOOLEAN BootPolicy, + IN OUT UINTN *BufferSize, + IN VOID *Buffer OPTIONAL + ); + +/// +/// The EFI_LOAD_FILE_PROTOCOL is a simple protocol used to obtain files from arbitrary devices. +/// +struct _EFI_LOAD_FILE2_PROTOCOL { + EFI_LOAD_FILE2 LoadFile; +}; + +extern EFI_GUID gEfiLoadFile2ProtocolGuid; + +#endif diff --git a/roms/ipxe/src/include/ipxe/efi/Protocol/ManagedNetwork.h b/roms/ipxe/src/include/ipxe/efi/Protocol/ManagedNetwork.h new file mode 100644 index 0000000..2bd0922 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/efi/Protocol/ManagedNetwork.h @@ -0,0 +1,374 @@ +/** @file + EFI_MANAGED_NETWORK_SERVICE_BINDING_PROTOCOL as defined in UEFI 2.0. + EFI_MANAGED_NETWORK_PROTOCOL as defined in UEFI 2.0. + +Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
    +This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + @par Revision Reference: + This Protocol is introduced in UEFI Specification 2.0 + +**/ + +#ifndef __EFI_MANAGED_NETWORK_PROTOCOL_H__ +#define __EFI_MANAGED_NETWORK_PROTOCOL_H__ + +FILE_LICENCE ( BSD3 ); + +#include + +#define EFI_MANAGED_NETWORK_SERVICE_BINDING_PROTOCOL_GUID \ + { \ + 0xf36ff770, 0xa7e1, 0x42cf, {0x9e, 0xd2, 0x56, 0xf0, 0xf2, 0x71, 0xf4, 0x4c } \ + } + +#define EFI_MANAGED_NETWORK_PROTOCOL_GUID \ + { \ + 0x7ab33a91, 0xace5, 0x4326, { 0xb5, 0x72, 0xe7, 0xee, 0x33, 0xd3, 0x9f, 0x16 } \ + } + +typedef struct _EFI_MANAGED_NETWORK_PROTOCOL EFI_MANAGED_NETWORK_PROTOCOL; + +typedef struct { + /// + /// Timeout value for a UEFI one-shot timer event. A packet that has not been removed + /// from the MNP receive queue will be dropped if its receive timeout expires. + /// + UINT32 ReceivedQueueTimeoutValue; + /// + /// Timeout value for a UEFI one-shot timer event. A packet that has not been removed + /// from the MNP transmit queue will be dropped if its receive timeout expires. + /// + UINT32 TransmitQueueTimeoutValue; + /// + /// Ethernet type II 16-bit protocol type in host byte order. Valid + /// values are zero and 1,500 to 65,535. + /// + UINT16 ProtocolTypeFilter; + /// + /// Set to TRUE to receive packets that are sent to the network + /// device MAC address. The startup default value is FALSE. + /// + BOOLEAN EnableUnicastReceive; + /// + /// Set to TRUE to receive packets that are sent to any of the + /// active multicast groups. The startup default value is FALSE. + /// + BOOLEAN EnableMulticastReceive; + /// + /// Set to TRUE to receive packets that are sent to the network + /// device broadcast address. The startup default value is FALSE. + /// + BOOLEAN EnableBroadcastReceive; + /// + /// Set to TRUE to receive packets that are sent to any MAC address. + /// The startup default value is FALSE. + /// + BOOLEAN EnablePromiscuousReceive; + /// + /// Set to TRUE to drop queued packets when the configuration + /// is changed. The startup default value is FALSE. + /// + BOOLEAN FlushQueuesOnReset; + /// + /// Set to TRUE to timestamp all packets when they are received + /// by the MNP. Note that timestamps may be unsupported in some + /// MNP implementations. The startup default value is FALSE. + /// + BOOLEAN EnableReceiveTimestamps; + /// + /// Set to TRUE to disable background polling in this MNP + /// instance. Note that background polling may not be supported in + /// all MNP implementations. The startup default value is FALSE, + /// unless background polling is not supported. + /// + BOOLEAN DisableBackgroundPolling; +} EFI_MANAGED_NETWORK_CONFIG_DATA; + +typedef struct { + EFI_TIME Timestamp; + EFI_EVENT RecycleEvent; + UINT32 PacketLength; + UINT32 HeaderLength; + UINT32 AddressLength; + UINT32 DataLength; + BOOLEAN BroadcastFlag; + BOOLEAN MulticastFlag; + BOOLEAN PromiscuousFlag; + UINT16 ProtocolType; + VOID *DestinationAddress; + VOID *SourceAddress; + VOID *MediaHeader; + VOID *PacketData; +} EFI_MANAGED_NETWORK_RECEIVE_DATA; + +typedef struct { + UINT32 FragmentLength; + VOID *FragmentBuffer; +} EFI_MANAGED_NETWORK_FRAGMENT_DATA; + +typedef struct { + EFI_MAC_ADDRESS *DestinationAddress; //OPTIONAL + EFI_MAC_ADDRESS *SourceAddress; //OPTIONAL + UINT16 ProtocolType; //OPTIONAL + UINT32 DataLength; + UINT16 HeaderLength; //OPTIONAL + UINT16 FragmentCount; + EFI_MANAGED_NETWORK_FRAGMENT_DATA FragmentTable[1]; +} EFI_MANAGED_NETWORK_TRANSMIT_DATA; + + +typedef struct { + /// + /// This Event will be signaled after the Status field is updated + /// by the MNP. The type of Event must be + /// EFI_NOTIFY_SIGNAL. The Task Priority Level (TPL) of + /// Event must be lower than or equal to TPL_CALLBACK. + /// + EFI_EVENT Event; + /// + /// The status that is returned to the caller at the end of the operation + /// to indicate whether this operation completed successfully. + /// + EFI_STATUS Status; + union { + /// + /// When this token is used for receiving, RxData is a pointer to the EFI_MANAGED_NETWORK_RECEIVE_DATA. + /// + EFI_MANAGED_NETWORK_RECEIVE_DATA *RxData; + /// + /// When this token is used for transmitting, TxData is a pointer to the EFI_MANAGED_NETWORK_TRANSMIT_DATA. + /// + EFI_MANAGED_NETWORK_TRANSMIT_DATA *TxData; + } Packet; +} EFI_MANAGED_NETWORK_COMPLETION_TOKEN; + +/** + Returns the operational parameters for the current MNP child driver. + + @param This The pointer to the EFI_MANAGED_NETWORK_PROTOCOL instance. + @param MnpConfigData The pointer to storage for MNP operational parameters. + @param SnpModeData The pointer to storage for SNP operational parameters. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_UNSUPPORTED The requested feature is unsupported in this MNP implementation. + @retval EFI_NOT_STARTED This MNP child driver instance has not been configured. The default + values are returned in MnpConfigData if it is not NULL. + @retval Other The mode data could not be read. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_MANAGED_NETWORK_GET_MODE_DATA)( + IN EFI_MANAGED_NETWORK_PROTOCOL *This, + OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL, + OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL + ); + +/** + Sets or clears the operational parameters for the MNP child driver. + + @param This The pointer to the EFI_MANAGED_NETWORK_PROTOCOL instance. + @param MnpConfigData The pointer to configuration data that will be assigned to the MNP + child driver instance. If NULL, the MNP child driver instance is + reset to startup defaults and all pending transmit and receive + requests are flushed. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES Required system resources (usually memory) could not be + allocated. + @retval EFI_UNSUPPORTED The requested feature is unsupported in this [MNP] + implementation. + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred. + @retval Other The MNP child driver instance has been reset to startup defaults. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_MANAGED_NETWORK_CONFIGURE)( + IN EFI_MANAGED_NETWORK_PROTOCOL *This, + IN EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL + ); + +/** + Translates an IP multicast address to a hardware (MAC) multicast address. + + @param This The pointer to the EFI_MANAGED_NETWORK_PROTOCOL instance. + @param Ipv6Flag Set to TRUE to if IpAddress is an IPv6 multicast address. + Set to FALSE if IpAddress is an IPv4 multicast address. + @param IpAddress The pointer to the multicast IP address (in network byte order) to convert. + @param MacAddress The pointer to the resulting multicast MAC address. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER One of the following conditions is TRUE: + - This is NULL. + - IpAddress is NULL. + - *IpAddress is not a valid multicast IP address. + - MacAddress is NULL. + @retval EFI_NOT_STARTED This MNP child driver instance has not been configured. + @retval EFI_UNSUPPORTED The requested feature is unsupported in this MNP implementation. + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred. + @retval Other The address could not be converted. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_MANAGED_NETWORK_MCAST_IP_TO_MAC)( + IN EFI_MANAGED_NETWORK_PROTOCOL *This, + IN BOOLEAN Ipv6Flag, + IN EFI_IP_ADDRESS *IpAddress, + OUT EFI_MAC_ADDRESS *MacAddress + ); + +/** + Enables and disables receive filters for multicast address. + + @param This The pointer to the EFI_MANAGED_NETWORK_PROTOCOL instance. + @param JoinFlag Set to TRUE to join this multicast group. + Set to FALSE to leave this multicast group. + @param MacAddress The pointer to the multicast MAC group (address) to join or leave. + + @retval EFI_SUCCESS The requested operation completed successfully. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - JoinFlag is TRUE and MacAddress is NULL. + - *MacAddress is not a valid multicast MAC address. + @retval EFI_NOT_STARTED This MNP child driver instance has not been configured. + @retval EFI_ALREADY_STARTED The supplied multicast group is already joined. + @retval EFI_NOT_FOUND The supplied multicast group is not joined. + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred. + @retval EFI_UNSUPPORTED The requested feature is unsupported in this MNP implementation. + @retval Other The requested operation could not be completed. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_MANAGED_NETWORK_GROUPS)( + IN EFI_MANAGED_NETWORK_PROTOCOL *This, + IN BOOLEAN JoinFlag, + IN EFI_MAC_ADDRESS *MacAddress OPTIONAL + ); + +/** + Places asynchronous outgoing data packets into the transmit queue. + + @param This The pointer to the EFI_MANAGED_NETWORK_PROTOCOL instance. + @param Token The pointer to a token associated with the transmit data descriptor. + + @retval EFI_SUCCESS The transmit completion token was cached. + @retval EFI_NOT_STARTED This MNP child driver instance has not been configured. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_ACCESS_DENIED The transmit completion token is already in the transmit queue. + @retval EFI_OUT_OF_RESOURCES The transmit data could not be queued due to a lack of system resources + (usually memory). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_NOT_READY The transmit request could not be queued because the transmit queue is full. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_MANAGED_NETWORK_TRANSMIT)( + IN EFI_MANAGED_NETWORK_PROTOCOL *This, + IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token + ); + +/** + Places an asynchronous receiving request into the receiving queue. + + @param This The pointer to the EFI_MANAGED_NETWORK_PROTOCOL instance. + @param Token The pointer to a token associated with the receive data descriptor. + + @retval EFI_SUCCESS The receive completion token was cached. + @retval EFI_NOT_STARTED This MNP child driver instance has not been configured. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Token is NULL. + - Token.Event is NULL. + @retval EFI_OUT_OF_RESOURCES The transmit data could not be queued due to a lack of system resources + (usually memory). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_ACCESS_DENIED The receive completion token was already in the receive queue. + @retval EFI_NOT_READY The receive request could not be queued because the receive queue is full. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_MANAGED_NETWORK_RECEIVE)( + IN EFI_MANAGED_NETWORK_PROTOCOL *This, + IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token + ); + + +/** + Aborts an asynchronous transmit or receive request. + + @param This The pointer to the EFI_MANAGED_NETWORK_PROTOCOL instance. + @param Token The pointer to a token that has been issued by + EFI_MANAGED_NETWORK_PROTOCOL.Transmit() or + EFI_MANAGED_NETWORK_PROTOCOL.Receive(). If + NULL, all pending tokens are aborted. + + @retval EFI_SUCCESS The asynchronous I/O request was aborted and Token.Event + was signaled. When Token is NULL, all pending requests were + aborted and their events were signaled. + @retval EFI_NOT_STARTED This MNP child driver instance has not been configured. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_FOUND When Token is not NULL, the asynchronous I/O request was + not found in the transmit or receive queue. It has either completed + or was not issued by Transmit() and Receive(). + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_MANAGED_NETWORK_CANCEL)( + IN EFI_MANAGED_NETWORK_PROTOCOL *This, + IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token OPTIONAL + ); + +/** + Polls for incoming data packets and processes outgoing data packets. + + @param This The pointer to the EFI_MANAGED_NETWORK_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_NOT_STARTED This MNP child driver instance has not been configured. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_NOT_READY No incoming or outgoing data was processed. Consider increasing + the polling rate. + @retval EFI_TIMEOUT Data was dropped out of the transmit and/or receive queue. + Consider increasing the polling rate. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_MANAGED_NETWORK_POLL)( + IN EFI_MANAGED_NETWORK_PROTOCOL *This + ); + +/// +/// The MNP is used by network applications (and drivers) to +/// perform raw (unformatted) asynchronous network packet I/O. +/// +struct _EFI_MANAGED_NETWORK_PROTOCOL { + EFI_MANAGED_NETWORK_GET_MODE_DATA GetModeData; + EFI_MANAGED_NETWORK_CONFIGURE Configure; + EFI_MANAGED_NETWORK_MCAST_IP_TO_MAC McastIpToMac; + EFI_MANAGED_NETWORK_GROUPS Groups; + EFI_MANAGED_NETWORK_TRANSMIT Transmit; + EFI_MANAGED_NETWORK_RECEIVE Receive; + EFI_MANAGED_NETWORK_CANCEL Cancel; + EFI_MANAGED_NETWORK_POLL Poll; +}; + +extern EFI_GUID gEfiManagedNetworkServiceBindingProtocolGuid; +extern EFI_GUID gEfiManagedNetworkProtocolGuid; + +#endif diff --git a/roms/ipxe/src/include/ipxe/efi/Protocol/Mtftp4.h b/roms/ipxe/src/include/ipxe/efi/Protocol/Mtftp4.h new file mode 100644 index 0000000..0e961cf --- /dev/null +++ b/roms/ipxe/src/include/ipxe/efi/Protocol/Mtftp4.h @@ -0,0 +1,595 @@ +/** @file + EFI Multicast Trivial File Tranfer Protocol Definition + +Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
    +This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + @par Revision Reference: + This Protocol is introduced in UEFI Specification 2.0 + +**/ + +#ifndef __EFI_MTFTP4_PROTOCOL_H__ +#define __EFI_MTFTP4_PROTOCOL_H__ + +FILE_LICENCE ( BSD3 ); + +#define EFI_MTFTP4_SERVICE_BINDING_PROTOCOL_GUID \ + { \ + 0x2FE800BE, 0x8F01, 0x4aa6, {0x94, 0x6B, 0xD7, 0x13, 0x88, 0xE1, 0x83, 0x3F } \ + } + +#define EFI_MTFTP4_PROTOCOL_GUID \ + { \ + 0x78247c57, 0x63db, 0x4708, {0x99, 0xc2, 0xa8, 0xb4, 0xa9, 0xa6, 0x1f, 0x6b } \ + } + +typedef struct _EFI_MTFTP4_PROTOCOL EFI_MTFTP4_PROTOCOL; +typedef struct _EFI_MTFTP4_TOKEN EFI_MTFTP4_TOKEN; + +// +//MTFTP4 packet opcode definition +// +#define EFI_MTFTP4_OPCODE_RRQ 1 +#define EFI_MTFTP4_OPCODE_WRQ 2 +#define EFI_MTFTP4_OPCODE_DATA 3 +#define EFI_MTFTP4_OPCODE_ACK 4 +#define EFI_MTFTP4_OPCODE_ERROR 5 +#define EFI_MTFTP4_OPCODE_OACK 6 +#define EFI_MTFTP4_OPCODE_DIR 7 +#define EFI_MTFTP4_OPCODE_DATA8 8 +#define EFI_MTFTP4_OPCODE_ACK8 9 + +// +// MTFTP4 error code definition +// +#define EFI_MTFTP4_ERRORCODE_NOT_DEFINED 0 +#define EFI_MTFTP4_ERRORCODE_FILE_NOT_FOUND 1 +#define EFI_MTFTP4_ERRORCODE_ACCESS_VIOLATION 2 +#define EFI_MTFTP4_ERRORCODE_DISK_FULL 3 +#define EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION 4 +#define EFI_MTFTP4_ERRORCODE_UNKNOWN_TRANSFER_ID 5 +#define EFI_MTFTP4_ERRORCODE_FILE_ALREADY_EXISTS 6 +#define EFI_MTFTP4_ERRORCODE_NO_SUCH_USER 7 +#define EFI_MTFTP4_ERRORCODE_REQUEST_DENIED 8 + +// +// MTFTP4 pacekt definitions +// +#pragma pack(1) + +typedef struct { + UINT16 OpCode; + UINT8 Filename[1]; +} EFI_MTFTP4_REQ_HEADER; + +typedef struct { + UINT16 OpCode; + UINT8 Data[1]; +} EFI_MTFTP4_OACK_HEADER; + +typedef struct { + UINT16 OpCode; + UINT16 Block; + UINT8 Data[1]; +} EFI_MTFTP4_DATA_HEADER; + +typedef struct { + UINT16 OpCode; + UINT16 Block[1]; +} EFI_MTFTP4_ACK_HEADER; + +typedef struct { + UINT16 OpCode; + UINT64 Block; + UINT8 Data[1]; +} EFI_MTFTP4_DATA8_HEADER; + +typedef struct { + UINT16 OpCode; + UINT64 Block[1]; +} EFI_MTFTP4_ACK8_HEADER; + +typedef struct { + UINT16 OpCode; + UINT16 ErrorCode; + UINT8 ErrorMessage[1]; +} EFI_MTFTP4_ERROR_HEADER; + +typedef union { + /// + /// Type of packets as defined by the MTFTPv4 packet opcodes. + /// + UINT16 OpCode; + /// + /// Read request packet header. + /// + EFI_MTFTP4_REQ_HEADER Rrq; + /// + /// Write request packet header. + /// + EFI_MTFTP4_REQ_HEADER Wrq; + /// + /// Option acknowledge packet header. + /// + EFI_MTFTP4_OACK_HEADER Oack; + /// + /// Data packet header. + /// + EFI_MTFTP4_DATA_HEADER Data; + /// + /// Acknowledgement packet header. + /// + EFI_MTFTP4_ACK_HEADER Ack; + /// + /// Data packet header with big block number. + /// + EFI_MTFTP4_DATA8_HEADER Data8; + /// + /// Acknowledgement header with big block num. + /// + EFI_MTFTP4_ACK8_HEADER Ack8; + /// + /// Error packet header. + /// + EFI_MTFTP4_ERROR_HEADER Error; +} EFI_MTFTP4_PACKET; + +#pragma pack() + +/// +/// MTFTP4 option definition. +/// +typedef struct { + UINT8 *OptionStr; + UINT8 *ValueStr; +} EFI_MTFTP4_OPTION; + + +typedef struct { + BOOLEAN UseDefaultSetting; + EFI_IPv4_ADDRESS StationIp; + EFI_IPv4_ADDRESS SubnetMask; + UINT16 LocalPort; + EFI_IPv4_ADDRESS GatewayIp; + EFI_IPv4_ADDRESS ServerIp; + UINT16 InitialServerPort; + UINT16 TryCount; + UINT16 TimeoutValue; +} EFI_MTFTP4_CONFIG_DATA; + + +typedef struct { + EFI_MTFTP4_CONFIG_DATA ConfigData; + UINT8 SupportedOptionCount; + UINT8 **SupportedOptoins; + UINT8 UnsupportedOptionCount; + UINT8 **UnsupportedOptoins; +} EFI_MTFTP4_MODE_DATA; + + +typedef struct { + EFI_IPv4_ADDRESS GatewayIp; + EFI_IPv4_ADDRESS ServerIp; + UINT16 ServerPort; + UINT16 TryCount; + UINT16 TimeoutValue; +} EFI_MTFTP4_OVERRIDE_DATA; + +// +// Protocol interfaces definition +// + +/** + A callback function that is provided by the caller to intercept + the EFI_MTFTP4_OPCODE_DATA or EFI_MTFTP4_OPCODE_DATA8 packets processed in the + EFI_MTFTP4_PROTOCOL.ReadFile() function, and alternatively to intercept + EFI_MTFTP4_OPCODE_OACK or EFI_MTFTP4_OPCODE_ERROR packets during a call to + EFI_MTFTP4_PROTOCOL.ReadFile(), WriteFile() or ReadDirectory(). + + @param This The pointer to the EFI_MTFTP4_PROTOCOL instance. + @param Token The token that the caller provided in the + EFI_MTFTP4_PROTOCOL.ReadFile(), WriteFile() + or ReadDirectory() function. + @param PacketLen Indicates the length of the packet. + @param Packet The pointer to an MTFTPv4 packet. + + @retval EFI_SUCCESS The operation was successful. + @retval Others Aborts the transfer process. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_MTFTP4_CHECK_PACKET)( + IN EFI_MTFTP4_PROTOCOL *This, + IN EFI_MTFTP4_TOKEN *Token, + IN UINT16 PacketLen, + IN EFI_MTFTP4_PACKET *Paket + ); + +/** + Timeout callback funtion. + + @param This The pointer to the EFI_MTFTP4_PROTOCOL instance. + @param Token The token that is provided in the + EFI_MTFTP4_PROTOCOL.ReadFile() or + EFI_MTFTP4_PROTOCOL.WriteFile() or + EFI_MTFTP4_PROTOCOL.ReadDirectory() functions + by the caller. + + @retval EFI_SUCCESS The operation was successful. + @retval Others Aborts download process. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_MTFTP4_TIMEOUT_CALLBACK)( + IN EFI_MTFTP4_PROTOCOL *This, + IN EFI_MTFTP4_TOKEN *Token + ); + +/** + A callback function that the caller provides to feed data to the + EFI_MTFTP4_PROTOCOL.WriteFile() function. + + @param This The pointer to the EFI_MTFTP4_PROTOCOL instance. + @param Token The token provided in the + EFI_MTFTP4_PROTOCOL.WriteFile() by the caller. + @param Length Indicates the length of the raw data wanted on input, and the + length the data available on output. + @param Buffer The pointer to the buffer where the data is stored. + + @retval EFI_SUCCESS The operation was successful. + @retval Others Aborts session. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_MTFTP4_PACKET_NEEDED)( + IN EFI_MTFTP4_PROTOCOL *This, + IN EFI_MTFTP4_TOKEN *Token, + IN OUT UINT16 *Length, + OUT VOID **Buffer + ); + + +/** + Submits an asynchronous interrupt transfer to an interrupt endpoint of a USB device. + + @param This The pointer to the EFI_MTFTP4_PROTOCOL instance. + @param ModeData The pointer to storage for the EFI MTFTPv4 Protocol driver mode data. + + @retval EFI_SUCCESS The configuration data was successfully returned. + @retval EFI_OUT_OF_RESOURCES The required mode data could not be allocated. + @retval EFI_INVALID_PARAMETER This is NULL or ModeData is NULL. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_MTFTP4_GET_MODE_DATA)( + IN EFI_MTFTP4_PROTOCOL *This, + OUT EFI_MTFTP4_MODE_DATA *ModeData + ); + + +/** + Initializes, changes, or resets the default operational setting for this + EFI MTFTPv4 Protocol driver instance. + + @param This The pointer to the EFI_MTFTP4_PROTOCOL instance. + @param MtftpConfigData The pointer to the configuration data structure. + + @retval EFI_SUCCESS The EFI MTFTPv4 Protocol driver was configured successfully. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_ACCESS_DENIED The EFI configuration could not be changed at this time because + there is one MTFTP background operation in progress. + @retval EFI_NO_MAPPING When using a default address, configuration (DHCP, BOOTP, + RARP, etc.) has not finished yet. + @retval EFI_UNSUPPORTED A configuration protocol (DHCP, BOOTP, RARP, etc.) could not + be located when clients choose to use the default address + settings. + @retval EFI_OUT_OF_RESOURCES The EFI MTFTPv4 Protocol driver instance data could not be + allocated. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The EFI + MTFTPv4 Protocol driver instance is not configured. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_MTFTP4_CONFIGURE)( + IN EFI_MTFTP4_PROTOCOL *This, + IN EFI_MTFTP4_CONFIG_DATA *MtftpConfigData OPTIONAL + ); + + +/** + Gets information about a file from an MTFTPv4 server. + + @param This The pointer to the EFI_MTFTP4_PROTOCOL instance. + @param OverrideData Data that is used to override the existing parameters. If NULL, + the default parameters that were set in the + EFI_MTFTP4_PROTOCOL.Configure() function are used. + @param Filename The pointer to null-terminated ASCII file name string. + @param ModeStr The pointer to null-terminated ASCII mode string. If NULL, "octet" will be used. + @param OptionCount Number of option/value string pairs in OptionList. + @param OptionList The pointer to array of option/value string pairs. Ignored if + OptionCount is zero. + @param PacketLength The number of bytes in the returned packet. + @param Packet The pointer to the received packet. This buffer must be freed by + the caller. + + @retval EFI_SUCCESS An MTFTPv4 OACK packet was received and is in the Packet. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Filename is NULL. + - OptionCount is not zero and OptionList is NULL. + - One or more options in OptionList have wrong format. + - PacketLength is NULL. + - One or more IPv4 addresses in OverrideData are not valid + unicast IPv4 addresses if OverrideData is not NULL. + @retval EFI_UNSUPPORTED One or more options in the OptionList are in the + unsupported list of structure EFI_MTFTP4_MODE_DATA. + @retval EFI_NOT_STARTED The EFI MTFTPv4 Protocol driver has not been started. + @retval EFI_NO_MAPPING When using a default address, configuration (DHCP, BOOTP, + RARP, etc.) has not finished yet. + @retval EFI_ACCESS_DENIED The previous operation has not completed yet. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_TFTP_ERROR An MTFTPv4 ERROR packet was received and is in the Packet. + @retval EFI_NETWORK_UNREACHABLE An ICMP network unreachable error packet was received and the Packet is set to NULL. + @retval EFI_HOST_UNREACHABLE An ICMP host unreachable error packet was received and the Packet is set to NULL. + @retval EFI_PROTOCOL_UNREACHABLE An ICMP protocol unreachable error packet was received and the Packet is set to NULL. + @retval EFI_PORT_UNREACHABLE An ICMP port unreachable error packet was received and the Packet is set to NULL. + @retval EFI_ICMP_ERROR Some other ICMP ERROR packet was received and is in the Buffer. + @retval EFI_PROTOCOL_ERROR An unexpected MTFTPv4 packet was received and is in the Packet. + @retval EFI_TIMEOUT No responses were received from the MTFTPv4 server. + @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred. + @retval EFI_NO_MEDIA There was a media error. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_MTFTP4_GET_INFO)( + IN EFI_MTFTP4_PROTOCOL *This, + IN EFI_MTFTP4_OVERRIDE_DATA *OverrideData OPTIONAL, + IN UINT8 *Filename, + IN UINT8 *ModeStr OPTIONAL, + IN UINT8 OptionCount, + IN EFI_MTFTP4_OPTION *OptionList, + OUT UINT32 *PacketLength, + OUT EFI_MTFTP4_PACKET **Packet OPTIONAL + ); + +/** + Parses the options in an MTFTPv4 OACK packet. + + @param This The pointer to the EFI_MTFTP4_PROTOCOL instance. + @param PacketLen Length of the OACK packet to be parsed. + @param Packet The pointer to the OACK packet to be parsed. + @param OptionCount The pointer to the number of options in following OptionList. + @param OptionList The pointer to EFI_MTFTP4_OPTION storage. Call the EFI Boot + Service FreePool() to release the OptionList if the options + in this OptionList are not needed any more. + + @retval EFI_SUCCESS The OACK packet was valid and the OptionCount and + OptionList parameters have been updated. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - PacketLen is 0. + - Packet is NULL or Packet is not a valid MTFTPv4 packet. + - OptionCount is NULL. + @retval EFI_NOT_FOUND No options were found in the OACK packet. + @retval EFI_OUT_OF_RESOURCES Storage for the OptionList array cannot be allocated. + @retval EFI_PROTOCOL_ERROR One or more of the option fields is invalid. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_MTFTP4_PARSE_OPTIONS)( + IN EFI_MTFTP4_PROTOCOL *This, + IN UINT32 PacketLen, + IN EFI_MTFTP4_PACKET *Packet, + OUT UINT32 *OptionCount, + OUT EFI_MTFTP4_OPTION **OptionList OPTIONAL + ); + + +/** + Downloads a file from an MTFTPv4 server. + + @param This The pointer to the EFI_MTFTP4_PROTOCOL instance. + @param Token The pointer to the token structure to provide the parameters that are + used in this operation. + + @retval EFI_SUCCESS The data file has been transferred successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_BUFFER_TOO_SMALL BufferSize is not zero but not large enough to hold the + downloaded data in downloading process. + @retval EFI_ABORTED Current operation is aborted by user. + @retval EFI_NETWORK_UNREACHABLE An ICMP network unreachable error packet was received. + @retval EFI_HOST_UNREACHABLE An ICMP host unreachable error packet was received. + @retval EFI_PROTOCOL_UNREACHABLE An ICMP protocol unreachable error packet was received. + @retval EFI_PORT_UNREACHABLE An ICMP port unreachable error packet was received. + @retval EFI_ICMP_ERROR Some other ICMP ERROR packet was received. + @retval EFI_TIMEOUT No responses were received from the MTFTPv4 server. + @retval EFI_TFTP_ERROR An MTFTPv4 ERROR packet was received. + @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred. + @retval EFI_NO_MEDIA There was a media error. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_MTFTP4_READ_FILE)( + IN EFI_MTFTP4_PROTOCOL *This, + IN EFI_MTFTP4_TOKEN *Token + ); + + + +/** + Sends a file to an MTFTPv4 server. + + @param This The pointer to the EFI_MTFTP4_PROTOCOL instance. + @param Token The pointer to the token structure to provide the parameters that are + used in this operation. + + @retval EFI_SUCCESS The upload session has started. + @retval EFI_UNSUPPORTED The operation is not supported by this implementation. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_UNSUPPORTED One or more options in the Token.OptionList are in + the unsupported list of structure EFI_MTFTP4_MODE_DATA. + @retval EFI_NOT_STARTED The EFI MTFTPv4 Protocol driver has not been started. + @retval EFI_NO_MAPPING When using a default address, configuration (DHCP, BOOTP, + RARP, etc.) is not finished yet. + @retval EFI_ALREADY_STARTED This Token is already being used in another MTFTPv4 session. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_ACCESS_DENIED The previous operation has not completed yet. + @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_MTFTP4_WRITE_FILE)( + IN EFI_MTFTP4_PROTOCOL *This, + IN EFI_MTFTP4_TOKEN *Token + ); + + +/** + Downloads a data file "directory" from an MTFTPv4 server. May be unsupported in some EFI + implementations. + + @param This The pointer to the EFI_MTFTP4_PROTOCOL instance. + @param Token The pointer to the token structure to provide the parameters that are + used in this operation. + + @retval EFI_SUCCESS The MTFTPv4 related file "directory" has been downloaded. + @retval EFI_UNSUPPORTED The operation is not supported by this implementation. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_UNSUPPORTED One or more options in the Token.OptionList are in + the unsupported list of structure EFI_MTFTP4_MODE_DATA. + @retval EFI_NOT_STARTED The EFI MTFTPv4 Protocol driver has not been started. + @retval EFI_NO_MAPPING When using a default address, configuration (DHCP, BOOTP, + RARP, etc.) is not finished yet. + @retval EFI_ALREADY_STARTED This Token is already being used in another MTFTPv4 session. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_ACCESS_DENIED The previous operation has not completed yet. + @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_MTFTP4_READ_DIRECTORY)( + IN EFI_MTFTP4_PROTOCOL *This, + IN EFI_MTFTP4_TOKEN *Token + ); + +/** + Polls for incoming data packets and processes outgoing data packets. + + @param This The pointer to the EFI_MTFTP4_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_NOT_STARTED This EFI MTFTPv4 Protocol instance has not been started. + @retval EFI_NO_MAPPING When using a default address, configuration (DHCP, BOOTP, + RARP, etc.) is not finished yet. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_TIMEOUT Data was dropped out of the transmit and/or receive queue. + Consider increasing the polling rate. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_MTFTP4_POLL)( + IN EFI_MTFTP4_PROTOCOL *This + ); + +/// +/// The EFI_MTFTP4_PROTOCOL is designed to be used by UEFI drivers and applications +/// to transmit and receive data files. The EFI MTFTPv4 Protocol driver uses +/// the underlying EFI UDPv4 Protocol driver and EFI IPv4 Protocol driver. +/// +struct _EFI_MTFTP4_PROTOCOL { + EFI_MTFTP4_GET_MODE_DATA GetModeData; + EFI_MTFTP4_CONFIGURE Configure; + EFI_MTFTP4_GET_INFO GetInfo; + EFI_MTFTP4_PARSE_OPTIONS ParseOptions; + EFI_MTFTP4_READ_FILE ReadFile; + EFI_MTFTP4_WRITE_FILE WriteFile; + EFI_MTFTP4_READ_DIRECTORY ReadDirectory; + EFI_MTFTP4_POLL Poll; +}; + +struct _EFI_MTFTP4_TOKEN { + /// + /// The status that is returned to the caller at the end of the operation + /// to indicate whether this operation completed successfully. + /// + EFI_STATUS Status; + /// + /// The event that will be signaled when the operation completes. If + /// set to NULL, the corresponding function will wait until the read or + /// write operation finishes. The type of Event must be + /// EVT_NOTIFY_SIGNAL. The Task Priority Level (TPL) of + /// Event must be lower than or equal to TPL_CALLBACK. + /// + EFI_EVENT Event; + /// + /// If not NULL, the data that will be used to override the existing configure data. + /// + EFI_MTFTP4_OVERRIDE_DATA *OverrideData; + /// + /// The pointer to the null-terminated ASCII file name string. + /// + UINT8 *Filename; + /// + /// The pointer to the null-terminated ASCII mode string. If NULL, "octet" is used. + /// + UINT8 *ModeStr; + /// + /// Number of option/value string pairs. + /// + UINT32 OptionCount; + /// + /// The pointer to an array of option/value string pairs. Ignored if OptionCount is zero. + /// + EFI_MTFTP4_OPTION *OptionList; + /// + /// The size of the data buffer. + /// + UINT64 BufferSize; + /// + /// The pointer to the data buffer. Data that is downloaded from the + /// MTFTPv4 server is stored here. Data that is uploaded to the + /// MTFTPv4 server is read from here. Ignored if BufferSize is zero. + /// + VOID *Buffer; + /// + /// The pointer to the context that will be used by CheckPacket, + /// TimeoutCallback and PacketNeeded. + /// + VOID *Context; + /// + /// The pointer to the callback function to check the contents of the received packet. + /// + EFI_MTFTP4_CHECK_PACKET CheckPacket; + /// + /// The pointer to the function to be called when a timeout occurs. + /// + EFI_MTFTP4_TIMEOUT_CALLBACK TimeoutCallback; + /// + /// The pointer to the function to provide the needed packet contents. + /// + EFI_MTFTP4_PACKET_NEEDED PacketNeeded; +}; + +extern EFI_GUID gEfiMtftp4ServiceBindingProtocolGuid; +extern EFI_GUID gEfiMtftp4ProtocolGuid; + +#endif + diff --git a/roms/ipxe/src/include/ipxe/efi/Protocol/NetworkInterfaceIdentifier.h b/roms/ipxe/src/include/ipxe/efi/Protocol/NetworkInterfaceIdentifier.h index 2931906..5adedd8 100644 --- a/roms/ipxe/src/include/ipxe/efi/Protocol/NetworkInterfaceIdentifier.h +++ b/roms/ipxe/src/include/ipxe/efi/Protocol/NetworkInterfaceIdentifier.h @@ -1,7 +1,7 @@ /** @file EFI Network Interface Identifier Protocol. -Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
    +Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.
    This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License that accompanies this distribution. The full text of the license may be found at @@ -36,7 +36,11 @@ FILE_LICENCE ( BSD3 ); 0x1ACED566, 0x76ED, 0x4218, {0xBC, 0x81, 0x76, 0x7F, 0x1F, 0x97, 0x7A, 0x89 } \ } -#define EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_REVISION 0x00010000 +// +// Revision defined in UEFI Specification 2.4 +// +#define EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_REVISION 0x00020000 + /// /// Revision defined in EFI1.1. @@ -72,9 +76,9 @@ struct _EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL { UINT8 MajorVer; ///< Major version number. UINT8 MinorVer; ///< Minor version number. BOOLEAN Ipv6Supported; ///< TRUE if the network interface supports IPv6; otherwise FALSE. - UINT8 IfNum; ///< The network interface number that is being identified by this Network - ///< Interface Identifier Protocol. This field must be less than or equal - ///< to the IFcnt field in the !PXE structure. + UINT16 IfNum; ///< The network interface number that is being identified by this Network + ///< Interface Identifier Protocol. This field must be less than or + ///< equal to the (IFcnt | IFcntExt <<8 ) fields in the !PXE structure. }; diff --git a/roms/ipxe/src/include/ipxe/efi/Protocol/PxeBaseCode.h b/roms/ipxe/src/include/ipxe/efi/Protocol/PxeBaseCode.h new file mode 100644 index 0000000..2644798 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/efi/Protocol/PxeBaseCode.h @@ -0,0 +1,936 @@ +/** @file + EFI PXE Base Code Protocol definitions, which is used to access PXE-compatible + devices for network access and network booting. + +Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
    +This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + @par Revision Reference: + This Protocol is introduced in EFI Specification 1.10. + +**/ +#ifndef __PXE_BASE_CODE_PROTOCOL_H__ +#define __PXE_BASE_CODE_PROTOCOL_H__ + +FILE_LICENCE ( BSD3 ); + +/// +/// PXE Base Code protocol. +/// +#define EFI_PXE_BASE_CODE_PROTOCOL_GUID \ + { \ + 0x03c4e603, 0xac28, 0x11d3, {0x9a, 0x2d, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \ + } + +typedef struct _EFI_PXE_BASE_CODE_PROTOCOL EFI_PXE_BASE_CODE_PROTOCOL; + +/// +/// Protocol defined in EFI1.1. +/// +typedef EFI_PXE_BASE_CODE_PROTOCOL EFI_PXE_BASE_CODE; + +/// +/// Default IP TTL and ToS. +/// +#define DEFAULT_TTL 16 +#define DEFAULT_ToS 0 + +/// +/// ICMP error format. +/// +typedef struct { + UINT8 Type; + UINT8 Code; + UINT16 Checksum; + union { + UINT32 reserved; + UINT32 Mtu; + UINT32 Pointer; + struct { + UINT16 Identifier; + UINT16 Sequence; + } Echo; + } u; + UINT8 Data[494]; +} EFI_PXE_BASE_CODE_ICMP_ERROR; + +/// +/// TFTP error format. +/// +typedef struct { + UINT8 ErrorCode; + CHAR8 ErrorString[127]; +} EFI_PXE_BASE_CODE_TFTP_ERROR; + +/// +/// IP Receive Filter definitions. +/// +#define EFI_PXE_BASE_CODE_MAX_IPCNT 8 + +/// +/// IP Receive Filter structure. +/// +typedef struct { + UINT8 Filters; + UINT8 IpCnt; + UINT16 reserved; + EFI_IP_ADDRESS IpList[EFI_PXE_BASE_CODE_MAX_IPCNT]; +} EFI_PXE_BASE_CODE_IP_FILTER; + +#define EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP 0x0001 +#define EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST 0x0002 +#define EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS 0x0004 +#define EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST 0x0008 + +/// +/// ARP cache entries. +/// +typedef struct { + EFI_IP_ADDRESS IpAddr; + EFI_MAC_ADDRESS MacAddr; +} EFI_PXE_BASE_CODE_ARP_ENTRY; + +/// +/// ARP route table entries. +/// +typedef struct { + EFI_IP_ADDRESS IpAddr; + EFI_IP_ADDRESS SubnetMask; + EFI_IP_ADDRESS GwAddr; +} EFI_PXE_BASE_CODE_ROUTE_ENTRY; + +// +// UDP definitions +// +typedef UINT16 EFI_PXE_BASE_CODE_UDP_PORT; + +#define EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP 0x0001 +#define EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT 0x0002 +#define EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP 0x0004 +#define EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT 0x0008 +#define EFI_PXE_BASE_CODE_UDP_OPFLAGS_USE_FILTER 0x0010 +#define EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT 0x0020 + +// +// Discover() definitions +// +#define EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP 0 +#define EFI_PXE_BASE_CODE_BOOT_TYPE_MS_WINNT_RIS 1 +#define EFI_PXE_BASE_CODE_BOOT_TYPE_INTEL_LCM 2 +#define EFI_PXE_BASE_CODE_BOOT_TYPE_DOSUNDI 3 +#define EFI_PXE_BASE_CODE_BOOT_TYPE_NEC_ESMPRO 4 +#define EFI_PXE_BASE_CODE_BOOT_TYPE_IBM_WSoD 5 +#define EFI_PXE_BASE_CODE_BOOT_TYPE_IBM_LCCM 6 +#define EFI_PXE_BASE_CODE_BOOT_TYPE_CA_UNICENTER_TNG 7 +#define EFI_PXE_BASE_CODE_BOOT_TYPE_HP_OPENVIEW 8 +#define EFI_PXE_BASE_CODE_BOOT_TYPE_ALTIRIS_9 9 +#define EFI_PXE_BASE_CODE_BOOT_TYPE_ALTIRIS_10 10 +#define EFI_PXE_BASE_CODE_BOOT_TYPE_ALTIRIS_11 11 +#define EFI_PXE_BASE_CODE_BOOT_TYPE_NOT_USED_12 12 +#define EFI_PXE_BASE_CODE_BOOT_TYPE_REDHAT_INSTALL 13 +#define EFI_PXE_BASE_CODE_BOOT_TYPE_REDHAT_BOOT 14 +#define EFI_PXE_BASE_CODE_BOOT_TYPE_REMBO 15 +#define EFI_PXE_BASE_CODE_BOOT_TYPE_BEOBOOT 16 +// +// 17 through 32767 are reserved +// 32768 through 65279 are for vendor use +// 65280 through 65534 are reserved +// +#define EFI_PXE_BASE_CODE_BOOT_TYPE_PXETEST 65535 + +#define EFI_PXE_BASE_CODE_BOOT_LAYER_MASK 0x7FFF +#define EFI_PXE_BASE_CODE_BOOT_LAYER_INITIAL 0x0000 + +// +// PXE Tag definition that identifies the processor +// and programming environment of the client system. +// These identifiers are defined by IETF: +// http://www.ietf.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xml +// +#if defined (MDE_CPU_IA32) +#define EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE 0x0006 +#elif defined (MDE_CPU_IPF) +#define EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE 0x0002 +#elif defined (MDE_CPU_X64) +#define EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE 0x0007 +#elif defined (MDE_CPU_ARM) +#define EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE 0x000A +#elif defined (MDE_CPU_AARCH64) +#define EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE 0x000B +#endif + + +/// +/// Discover() server list structure. +/// +typedef struct { + UINT16 Type; + BOOLEAN AcceptAnyResponse; + UINT8 Reserved; + EFI_IP_ADDRESS IpAddr; +} EFI_PXE_BASE_CODE_SRVLIST; + +/// +/// Discover() information override structure. +/// +typedef struct { + BOOLEAN UseMCast; + BOOLEAN UseBCast; + BOOLEAN UseUCast; + BOOLEAN MustUseList; + EFI_IP_ADDRESS ServerMCastIp; + UINT16 IpCnt; + EFI_PXE_BASE_CODE_SRVLIST SrvList[1]; +} EFI_PXE_BASE_CODE_DISCOVER_INFO; + +/// +/// TFTP opcode definitions. +/// +typedef enum { + EFI_PXE_BASE_CODE_TFTP_FIRST, + EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE, + EFI_PXE_BASE_CODE_TFTP_READ_FILE, + EFI_PXE_BASE_CODE_TFTP_WRITE_FILE, + EFI_PXE_BASE_CODE_TFTP_READ_DIRECTORY, + EFI_PXE_BASE_CODE_MTFTP_GET_FILE_SIZE, + EFI_PXE_BASE_CODE_MTFTP_READ_FILE, + EFI_PXE_BASE_CODE_MTFTP_READ_DIRECTORY, + EFI_PXE_BASE_CODE_MTFTP_LAST +} EFI_PXE_BASE_CODE_TFTP_OPCODE; + +/// +/// MTFTP information. This information is required +/// to start or join a multicast TFTP session. It is also required to +/// perform the "get file size" and "read directory" operations of MTFTP. +/// +typedef struct { + EFI_IP_ADDRESS MCastIp; + EFI_PXE_BASE_CODE_UDP_PORT CPort; + EFI_PXE_BASE_CODE_UDP_PORT SPort; + UINT16 ListenTimeout; + UINT16 TransmitTimeout; +} EFI_PXE_BASE_CODE_MTFTP_INFO; + +/// +/// DHCPV4 Packet structure. +/// +typedef struct { + UINT8 BootpOpcode; + UINT8 BootpHwType; + UINT8 BootpHwAddrLen; + UINT8 BootpGateHops; + UINT32 BootpIdent; + UINT16 BootpSeconds; + UINT16 BootpFlags; + UINT8 BootpCiAddr[4]; + UINT8 BootpYiAddr[4]; + UINT8 BootpSiAddr[4]; + UINT8 BootpGiAddr[4]; + UINT8 BootpHwAddr[16]; + UINT8 BootpSrvName[64]; + UINT8 BootpBootFile[128]; + UINT32 DhcpMagik; + UINT8 DhcpOptions[56]; +} EFI_PXE_BASE_CODE_DHCPV4_PACKET; + +/// +/// DHCPV6 Packet structure. +/// +typedef struct { + UINT32 MessageType:8; + UINT32 TransactionId:24; + UINT8 DhcpOptions[1024]; +} EFI_PXE_BASE_CODE_DHCPV6_PACKET; + +/// +/// Packet structure. +/// +typedef union { + UINT8 Raw[1472]; + EFI_PXE_BASE_CODE_DHCPV4_PACKET Dhcpv4; + EFI_PXE_BASE_CODE_DHCPV6_PACKET Dhcpv6; +} EFI_PXE_BASE_CODE_PACKET; + +// +// PXE Base Code Mode structure +// +#define EFI_PXE_BASE_CODE_MAX_ARP_ENTRIES 8 +#define EFI_PXE_BASE_CODE_MAX_ROUTE_ENTRIES 8 + +/// +/// EFI_PXE_BASE_CODE_MODE. +/// The data values in this structure are read-only and +/// are updated by the code that produces the +/// EFI_PXE_BASE_CODE_PROTOCOL functions. +/// +typedef struct { + BOOLEAN Started; + BOOLEAN Ipv6Available; + BOOLEAN Ipv6Supported; + BOOLEAN UsingIpv6; + BOOLEAN BisSupported; + BOOLEAN BisDetected; + BOOLEAN AutoArp; + BOOLEAN SendGUID; + BOOLEAN DhcpDiscoverValid; + BOOLEAN DhcpAckReceived; + BOOLEAN ProxyOfferReceived; + BOOLEAN PxeDiscoverValid; + BOOLEAN PxeReplyReceived; + BOOLEAN PxeBisReplyReceived; + BOOLEAN IcmpErrorReceived; + BOOLEAN TftpErrorReceived; + BOOLEAN MakeCallbacks; + UINT8 TTL; + UINT8 ToS; + EFI_IP_ADDRESS StationIp; + EFI_IP_ADDRESS SubnetMask; + EFI_PXE_BASE_CODE_PACKET DhcpDiscover; + EFI_PXE_BASE_CODE_PACKET DhcpAck; + EFI_PXE_BASE_CODE_PACKET ProxyOffer; + EFI_PXE_BASE_CODE_PACKET PxeDiscover; + EFI_PXE_BASE_CODE_PACKET PxeReply; + EFI_PXE_BASE_CODE_PACKET PxeBisReply; + EFI_PXE_BASE_CODE_IP_FILTER IpFilter; + UINT32 ArpCacheEntries; + EFI_PXE_BASE_CODE_ARP_ENTRY ArpCache[EFI_PXE_BASE_CODE_MAX_ARP_ENTRIES]; + UINT32 RouteTableEntries; + EFI_PXE_BASE_CODE_ROUTE_ENTRY RouteTable[EFI_PXE_BASE_CODE_MAX_ROUTE_ENTRIES]; + EFI_PXE_BASE_CODE_ICMP_ERROR IcmpError; + EFI_PXE_BASE_CODE_TFTP_ERROR TftpError; +} EFI_PXE_BASE_CODE_MODE; + +// +// PXE Base Code Interface Function definitions +// + +/** + Enables the use of the PXE Base Code Protocol functions. + + This function enables the use of the PXE Base Code Protocol functions. If the + Started field of the EFI_PXE_BASE_CODE_MODE structure is already TRUE, then + EFI_ALREADY_STARTED will be returned. If UseIpv6 is TRUE, then IPv6 formatted + addresses will be used in this session. If UseIpv6 is FALSE, then IPv4 formatted + addresses will be used in this session. If UseIpv6 is TRUE, and the Ipv6Supported + field of the EFI_PXE_BASE_CODE_MODE structure is FALSE, then EFI_UNSUPPORTED will + be returned. If there is not enough memory or other resources to start the PXE + Base Code Protocol, then EFI_OUT_OF_RESOURCES will be returned. Otherwise, the + PXE Base Code Protocol will be started, and all of the fields of the EFI_PXE_BASE_CODE_MODE + structure will be initialized as follows: + StartedSet to TRUE. + Ipv6SupportedUnchanged. + Ipv6AvailableUnchanged. + UsingIpv6Set to UseIpv6. + BisSupportedUnchanged. + BisDetectedUnchanged. + AutoArpSet to TRUE. + SendGUIDSet to FALSE. + TTLSet to DEFAULT_TTL. + ToSSet to DEFAULT_ToS. + DhcpCompletedSet to FALSE. + ProxyOfferReceivedSet to FALSE. + StationIpSet to an address of all zeros. + SubnetMaskSet to a subnet mask of all zeros. + DhcpDiscoverZero-filled. + DhcpAckZero-filled. + ProxyOfferZero-filled. + PxeDiscoverValidSet to FALSE. + PxeDiscoverZero-filled. + PxeReplyValidSet to FALSE. + PxeReplyZero-filled. + PxeBisReplyValidSet to FALSE. + PxeBisReplyZero-filled. + IpFilterSet the Filters field to 0 and the IpCnt field to 0. + ArpCacheEntriesSet to 0. + ArpCacheZero-filled. + RouteTableEntriesSet to 0. + RouteTableZero-filled. + IcmpErrorReceivedSet to FALSE. + IcmpErrorZero-filled. + TftpErroReceivedSet to FALSE. + TftpErrorZero-filled. + MakeCallbacksSet to TRUE if the PXE Base Code Callback Protocol is available. + Set to FALSE if the PXE Base Code Callback Protocol is not available. + + @param This The pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param UseIpv6 Specifies the type of IP addresses that are to be used during the session + that is being started. Set to TRUE for IPv6 addresses, and FALSE for + IPv4 addresses. + + @retval EFI_SUCCESS The PXE Base Code Protocol was started. + @retval EFI_DEVICE_ERROR The network device encountered an error during this oper + @retval EFI_UNSUPPORTED UseIpv6 is TRUE, but the Ipv6Supported field of the + EFI_PXE_BASE_CODE_MODE structure is FALSE. + @retval EFI_ALREADY_STARTED The PXE Base Code Protocol is already in the started state. + @retval EFI_INVALID_PARAMETER The This parameter is NULL or does not point to a valid + EFI_PXE_BASE_CODE_PROTOCOL structure. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough memory or other resources to start the + PXE Base Code Protocol. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PXE_BASE_CODE_START)( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN BOOLEAN UseIpv6 + ); + +/** + Disables the use of the PXE Base Code Protocol functions. + + This function stops all activity on the network device. All the resources allocated + in Start() are released, the Started field of the EFI_PXE_BASE_CODE_MODE structure is + set to FALSE and EFI_SUCCESS is returned. If the Started field of the EFI_PXE_BASE_CODE_MODE + structure is already FALSE, then EFI_NOT_STARTED will be returned. + + @param This The pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + + @retval EFI_SUCCESS The PXE Base Code Protocol was stopped. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is already in the stopped state. + @retval EFI_INVALID_PARAMETER The This parameter is NULL or does not point to a valid + EFI_PXE_BASE_CODE_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PXE_BASE_CODE_STOP)( + IN EFI_PXE_BASE_CODE_PROTOCOL *This + ); + +/** + Attempts to complete a DHCPv4 D.O.R.A. (discover / offer / request / acknowledge) or DHCPv6 + S.A.R.R (solicit / advertise / request / reply) sequence. + + This function attempts to complete the DHCP sequence. If this sequence is completed, + then EFI_SUCCESS is returned, and the DhcpCompleted, ProxyOfferReceived, StationIp, + SubnetMask, DhcpDiscover, DhcpAck, and ProxyOffer fields of the EFI_PXE_BASE_CODE_MODE + structure are filled in. + If SortOffers is TRUE, then the cached DHCP offer packets will be sorted before + they are tried. If SortOffers is FALSE, then the cached DHCP offer packets will + be tried in the order in which they are received. Please see the Preboot Execution + Environment (PXE) Specification for additional details on the implementation of DHCP. + This function can take at least 31 seconds to timeout and return control to the + caller. If the DHCP sequence does not complete, then EFI_TIMEOUT will be returned. + If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, + then the DHCP sequence will be stopped and EFI_ABORTED will be returned. + + @param This The pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param SortOffers TRUE if the offers received should be sorted. Set to FALSE to try the + offers in the order that they are received. + + @retval EFI_SUCCESS Valid DHCP has completed. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER The This parameter is NULL or does not point to a valid + EFI_PXE_BASE_CODE_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough memory to complete the DHCP Protocol. + @retval EFI_ABORTED The callback function aborted the DHCP Protocol. + @retval EFI_TIMEOUT The DHCP Protocol timed out. + @retval EFI_ICMP_ERROR An ICMP error packet was received during the DHCP session. + @retval EFI_NO_RESPONSE Valid PXE offer was not received. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PXE_BASE_CODE_DHCP)( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN BOOLEAN SortOffers + ); + +/** + Attempts to complete the PXE Boot Server and/or boot image discovery sequence. + + This function attempts to complete the PXE Boot Server and/or boot image discovery + sequence. If this sequence is completed, then EFI_SUCCESS is returned, and the + PxeDiscoverValid, PxeDiscover, PxeReplyReceived, and PxeReply fields of the + EFI_PXE_BASE_CODE_MODE structure are filled in. If UseBis is TRUE, then the + PxeBisReplyReceived and PxeBisReply fields of the EFI_PXE_BASE_CODE_MODE structure + will also be filled in. If UseBis is FALSE, then PxeBisReplyValid will be set to FALSE. + In the structure referenced by parameter Info, the PXE Boot Server list, SrvList[], + has two uses: It is the Boot Server IP address list used for unicast discovery + (if the UseUCast field is TRUE), and it is the list used for Boot Server verification + (if the MustUseList field is TRUE). Also, if the MustUseList field in that structure + is TRUE and the AcceptAnyResponse field in the SrvList[] array is TRUE, any Boot + Server reply of that type will be accepted. If the AcceptAnyResponse field is + FALSE, only responses from Boot Servers with matching IP addresses will be accepted. + This function can take at least 10 seconds to timeout and return control to the + caller. If the Discovery sequence does not complete, then EFI_TIMEOUT will be + returned. Please see the Preboot Execution Environment (PXE) Specification for + additional details on the implementation of the Discovery sequence. + If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, + then the Discovery sequence is stopped and EFI_ABORTED will be returned. + + @param This The pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param Type The type of bootstrap to perform. + @param Layer The pointer to the boot server layer number to discover, which must be + PXE_BOOT_LAYER_INITIAL when a new server type is being + discovered. + @param UseBis TRUE if Boot Integrity Services are to be used. FALSE otherwise. + @param Info The pointer to a data structure that contains additional information on the + type of discovery operation that is to be performed. + + @retval EFI_SUCCESS The Discovery sequence has been completed. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough memory to complete Discovery. + @retval EFI_ABORTED The callback function aborted the Discovery sequence. + @retval EFI_TIMEOUT The Discovery sequence timed out. + @retval EFI_ICMP_ERROR An ICMP error packet was received during the PXE discovery + session. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PXE_BASE_CODE_DISCOVER)( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN UINT16 Type, + IN UINT16 *Layer, + IN BOOLEAN UseBis, + IN EFI_PXE_BASE_CODE_DISCOVER_INFO *Info OPTIONAL + ); + +/** + Used to perform TFTP and MTFTP services. + + This function is used to perform TFTP and MTFTP services. This includes the + TFTP operations to get the size of a file, read a directory, read a file, and + write a file. It also includes the MTFTP operations to get the size of a file, + read a directory, and read a file. The type of operation is specified by Operation. + If the callback function that is invoked during the TFTP/MTFTP operation does + not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, then EFI_ABORTED will + be returned. + For read operations, the return data will be placed in the buffer specified by + BufferPtr. If BufferSize is too small to contain the entire downloaded file, + then EFI_BUFFER_TOO_SMALL will be returned and BufferSize will be set to zero + or the size of the requested file (the size of the requested file is only returned + if the TFTP server supports TFTP options). If BufferSize is large enough for the + read operation, then BufferSize will be set to the size of the downloaded file, + and EFI_SUCCESS will be returned. Applications using the PxeBc.Mtftp() services + should use the get-file-size operations to determine the size of the downloaded + file prior to using the read-file operations--especially when downloading large + (greater than 64 MB) files--instead of making two calls to the read-file operation. + Following this recommendation will save time if the file is larger than expected + and the TFTP server does not support TFTP option extensions. Without TFTP option + extension support, the client has to download the entire file, counting and discarding + the received packets, to determine the file size. + For write operations, the data to be sent is in the buffer specified by BufferPtr. + BufferSize specifies the number of bytes to send. If the write operation completes + successfully, then EFI_SUCCESS will be returned. + For TFTP "get file size" operations, the size of the requested file or directory + is returned in BufferSize, and EFI_SUCCESS will be returned. If the TFTP server + does not support options, the file will be downloaded into a bit bucket and the + length of the downloaded file will be returned. For MTFTP "get file size" operations, + if the MTFTP server does not support the "get file size" option, EFI_UNSUPPORTED + will be returned. + This function can take up to 10 seconds to timeout and return control to the caller. + If the TFTP sequence does not complete, EFI_TIMEOUT will be returned. + If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, + then the TFTP sequence is stopped and EFI_ABORTED will be returned. + The format of the data returned from a TFTP read directory operation is a null-terminated + filename followed by a null-terminated information string, of the form + "size year-month-day hour:minute:second" (i.e. %d %d-%d-%d %d:%d:%f - note that + the seconds field can be a decimal number), where the date and time are UTC. For + an MTFTP read directory command, there is additionally a null-terminated multicast + IP address preceding the filename of the form %d.%d.%d.%d for IP v4. The final + entry is itself null-terminated, so that the final information string is terminated + with two null octets. + + @param This The pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param Operation The type of operation to perform. + @param BufferPtr A pointer to the data buffer. + @param Overwrite Only used on write file operations. TRUE if a file on a remote server can + be overwritten. + @param BufferSize For get-file-size operations, *BufferSize returns the size of the + requested file. + @param BlockSize The requested block size to be used during a TFTP transfer. + @param ServerIp The TFTP / MTFTP server IP address. + @param Filename A Null-terminated ASCII string that specifies a directory name or a file + name. + @param Info The pointer to the MTFTP information. + @param DontUseBuffer Set to FALSE for normal TFTP and MTFTP read file operation. + + @retval EFI_SUCCESS The TFTP/MTFTP operation was completed. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval EFI_BUFFER_TOO_SMALL The buffer is not large enough to complete the read operation. + @retval EFI_ABORTED The callback function aborted the TFTP/MTFTP operation. + @retval EFI_TIMEOUT The TFTP/MTFTP operation timed out. + @retval EFI_ICMP_ERROR An ICMP error packet was received during the MTFTP session. + @retval EFI_TFTP_ERROR A TFTP error packet was received during the MTFTP session. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PXE_BASE_CODE_MTFTP)( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN EFI_PXE_BASE_CODE_TFTP_OPCODE Operation, + IN OUT VOID *BufferPtr OPTIONAL, + IN BOOLEAN Overwrite, + IN OUT UINT64 *BufferSize, + IN UINTN *BlockSize OPTIONAL, + IN EFI_IP_ADDRESS *ServerIp, + IN UINT8 *Filename OPTIONAL, + IN EFI_PXE_BASE_CODE_MTFTP_INFO *Info OPTIONAL, + IN BOOLEAN DontUseBuffer + ); + +/** + Writes a UDP packet to the network interface. + + This function writes a UDP packet specified by the (optional HeaderPtr and) + BufferPtr parameters to the network interface. The UDP header is automatically + built by this routine. It uses the parameters OpFlags, DestIp, DestPort, GatewayIp, + SrcIp, and SrcPort to build this header. If the packet is successfully built and + transmitted through the network interface, then EFI_SUCCESS will be returned. + If a timeout occurs during the transmission of the packet, then EFI_TIMEOUT will + be returned. If an ICMP error occurs during the transmission of the packet, then + the IcmpErrorReceived field is set to TRUE, the IcmpError field is filled in and + EFI_ICMP_ERROR will be returned. If the Callback Protocol does not return + EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, then EFI_ABORTED will be returned. + + @param This The pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param OpFlags The UDP operation flags. + @param DestIp The destination IP address. + @param DestPort The destination UDP port number. + @param GatewayIp The gateway IP address. + @param SrcIp The source IP address. + @param SrcPort The source UDP port number. + @param HeaderSize An optional field which may be set to the length of a header at + HeaderPtr to be prefixed to the data at BufferPtr. + @param HeaderPtr If HeaderSize is not NULL, a pointer to a header to be prefixed to the + data at BufferPtr. + @param BufferSize A pointer to the size of the data at BufferPtr. + @param BufferPtr A pointer to the data to be written. + + @retval EFI_SUCCESS The UDP Write operation was completed. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_BAD_BUFFER_SIZE The buffer is too long to be transmitted. + @retval EFI_ABORTED The callback function aborted the UDP Write operation. + @retval EFI_TIMEOUT The UDP Write operation timed out. + @retval EFI_ICMP_ERROR An ICMP error packet was received during the UDP write session. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PXE_BASE_CODE_UDP_WRITE)( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN UINT16 OpFlags, + IN EFI_IP_ADDRESS *DestIp, + IN EFI_PXE_BASE_CODE_UDP_PORT *DestPort, + IN EFI_IP_ADDRESS *GatewayIp, OPTIONAL + IN EFI_IP_ADDRESS *SrcIp, OPTIONAL + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort, OPTIONAL + IN UINTN *HeaderSize, OPTIONAL + IN VOID *HeaderPtr, OPTIONAL + IN UINTN *BufferSize, + IN VOID *BufferPtr + ); + +/** + Reads a UDP packet from the network interface. + + This function reads a UDP packet from a network interface. The data contents + are returned in (the optional HeaderPtr and) BufferPtr, and the size of the + buffer received is returned in BufferSize. If the input BufferSize is smaller + than the UDP packet received (less optional HeaderSize), it will be set to the + required size, and EFI_BUFFER_TOO_SMALL will be returned. In this case, the + contents of BufferPtr are undefined, and the packet is lost. If a UDP packet is + successfully received, then EFI_SUCCESS will be returned, and the information + from the UDP header will be returned in DestIp, DestPort, SrcIp, and SrcPort if + they are not NULL. + Depending on the values of OpFlags and the DestIp, DestPort, SrcIp, and SrcPort + input values, different types of UDP packet receive filtering will be performed. + The following tables summarize these receive filter operations. + + @param This The pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param OpFlags The UDP operation flags. + @param DestIp The destination IP address. + @param DestPort The destination UDP port number. + @param SrcIp The source IP address. + @param SrcPort The source UDP port number. + @param HeaderSize An optional field which may be set to the length of a header at + HeaderPtr to be prefixed to the data at BufferPtr. + @param HeaderPtr If HeaderSize is not NULL, a pointer to a header to be prefixed to the + data at BufferPtr. + @param BufferSize A pointer to the size of the data at BufferPtr. + @param BufferPtr A pointer to the data to be read. + + @retval EFI_SUCCESS The UDP Read operation was completed. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval EFI_BUFFER_TOO_SMALL The packet is larger than Buffer can hold. + @retval EFI_ABORTED The callback function aborted the UDP Read operation. + @retval EFI_TIMEOUT The UDP Read operation timed out. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PXE_BASE_CODE_UDP_READ)( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN UINT16 OpFlags, + IN OUT EFI_IP_ADDRESS *DestIp, OPTIONAL + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort, OPTIONAL + IN OUT EFI_IP_ADDRESS *SrcIp, OPTIONAL + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort, OPTIONAL + IN UINTN *HeaderSize, OPTIONAL + IN VOID *HeaderPtr, OPTIONAL + IN OUT UINTN *BufferSize, + IN VOID *BufferPtr + ); + +/** + Updates the IP receive filters of a network device and enables software filtering. + + The NewFilter field is used to modify the network device's current IP receive + filter settings and to enable a software filter. This function updates the IpFilter + field of the EFI_PXE_BASE_CODE_MODE structure with the contents of NewIpFilter. + The software filter is used when the USE_FILTER in OpFlags is set to UdpRead(). + The current hardware filter remains in effect no matter what the settings of OpFlags + are, so that the meaning of ANY_DEST_IP set in OpFlags to UdpRead() is from those + packets whose reception is enabled in hardware - physical NIC address (unicast), + broadcast address, logical address or addresses (multicast), or all (promiscuous). + UdpRead() does not modify the IP filter settings. + Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP receive + filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP. + If an application or driver wishes to preserve the IP receive filter settings, + it will have to preserve the IP receive filter settings before these calls, and + use SetIpFilter() to restore them after the calls. If incompatible filtering is + requested (for example, PROMISCUOUS with anything else), or if the device does not + support a requested filter setting and it cannot be accommodated in software + (for example, PROMISCUOUS not supported), EFI_INVALID_PARAMETER will be returned. + The IPlist field is used to enable IPs other than the StationIP. They may be + multicast or unicast. If IPcnt is set as well as EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP, + then both the StationIP and the IPs from the IPlist will be used. + + @param This The pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param NewFilter The pointer to the new set of IP receive filters. + + @retval EFI_SUCCESS The IP receive filter settings were updated. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PXE_BASE_CODE_SET_IP_FILTER)( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN EFI_PXE_BASE_CODE_IP_FILTER *NewFilter + ); + +/** + Uses the ARP protocol to resolve a MAC address. + + This function uses the ARP protocol to resolve a MAC address. The UsingIpv6 field + of the EFI_PXE_BASE_CODE_MODE structure is used to determine if IPv4 or IPv6 + addresses are being used. The IP address specified by IpAddr is used to resolve + a MAC address. If the ARP protocol succeeds in resolving the specified address, + then the ArpCacheEntries and ArpCache fields of the EFI_PXE_BASE_CODE_MODE structure + are updated, and EFI_SUCCESS is returned. If MacAddr is not NULL, the resolved + MAC address is placed there as well. + If the PXE Base Code protocol is in the stopped state, then EFI_NOT_STARTED is + returned. If the ARP protocol encounters a timeout condition while attempting + to resolve an address, then EFI_TIMEOUT is returned. If the Callback Protocol + does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, then EFI_ABORTED is + returned. + + @param This The pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param IpAddr The pointer to the IP address that is used to resolve a MAC address. + @param MacAddr If not NULL, a pointer to the MAC address that was resolved with the + ARP protocol. + + @retval EFI_SUCCESS The IP or MAC address was resolved. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval EFI_ABORTED The callback function aborted the ARP Protocol. + @retval EFI_TIMEOUT The ARP Protocol encountered a timeout condition. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PXE_BASE_CODE_ARP)( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN EFI_IP_ADDRESS *IpAddr, + IN EFI_MAC_ADDRESS *MacAddr OPTIONAL + ); + +/** + Updates the parameters that affect the operation of the PXE Base Code Protocol. + + This function sets parameters that affect the operation of the PXE Base Code Protocol. + The parameter specified by NewAutoArp is used to control the generation of ARP + protocol packets. If NewAutoArp is TRUE, then ARP Protocol packets will be generated + as required by the PXE Base Code Protocol. If NewAutoArp is FALSE, then no ARP + Protocol packets will be generated. In this case, the only mappings that are + available are those stored in the ArpCache of the EFI_PXE_BASE_CODE_MODE structure. + If there are not enough mappings in the ArpCache to perform a PXE Base Code Protocol + service, then the service will fail. This function updates the AutoArp field of + the EFI_PXE_BASE_CODE_MODE structure to NewAutoArp. + The SetParameters() call must be invoked after a Callback Protocol is installed + to enable the use of callbacks. + + @param This The pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param NewAutoArp If not NULL, a pointer to a value that specifies whether to replace the + current value of AutoARP. + @param NewSendGUID If not NULL, a pointer to a value that specifies whether to replace the + current value of SendGUID. + @param NewTTL If not NULL, a pointer to be used in place of the current value of TTL, + the "time to live" field of the IP header. + @param NewToS If not NULL, a pointer to be used in place of the current value of ToS, + the "type of service" field of the IP header. + @param NewMakeCallback If not NULL, a pointer to a value that specifies whether to replace the + current value of the MakeCallback field of the Mode structure. + + @retval EFI_SUCCESS The new parameters values were updated. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PXE_BASE_CODE_SET_PARAMETERS)( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN BOOLEAN *NewAutoArp, OPTIONAL + IN BOOLEAN *NewSendGUID, OPTIONAL + IN UINT8 *NewTTL, OPTIONAL + IN UINT8 *NewToS, OPTIONAL + IN BOOLEAN *NewMakeCallback OPTIONAL + ); + +/** + Updates the station IP address and/or subnet mask values of a network device. + + This function updates the station IP address and/or subnet mask values of a network + device. + The NewStationIp field is used to modify the network device's current IP address. + If NewStationIP is NULL, then the current IP address will not be modified. Otherwise, + this function updates the StationIp field of the EFI_PXE_BASE_CODE_MODE structure + with NewStationIp. + The NewSubnetMask field is used to modify the network device's current subnet + mask. If NewSubnetMask is NULL, then the current subnet mask will not be modified. + Otherwise, this function updates the SubnetMask field of the EFI_PXE_BASE_CODE_MODE + structure with NewSubnetMask. + + @param This The pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param NewStationIp The pointer to the new IP address to be used by the network device. + @param NewSubnetMask The pointer to the new subnet mask to be used by the network device. + + @retval EFI_SUCCESS The new station IP address and/or subnet mask were updated. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PXE_BASE_CODE_SET_STATION_IP)( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN EFI_IP_ADDRESS *NewStationIp, OPTIONAL + IN EFI_IP_ADDRESS *NewSubnetMask OPTIONAL + ); + +/** + Updates the contents of the cached DHCP and Discover packets. + + The pointers to the new packets are used to update the contents of the cached + packets in the EFI_PXE_BASE_CODE_MODE structure. + + @param This The pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param NewDhcpDiscoverValid The pointer to a value that will replace the current + DhcpDiscoverValid field. + @param NewDhcpAckReceived The pointer to a value that will replace the current + DhcpAckReceived field. + @param NewProxyOfferReceived The pointer to a value that will replace the current + ProxyOfferReceived field. + @param NewPxeDiscoverValid The pointer to a value that will replace the current + ProxyOfferReceived field. + @param NewPxeReplyReceived The pointer to a value that will replace the current + PxeReplyReceived field. + @param NewPxeBisReplyReceived The pointer to a value that will replace the current + PxeBisReplyReceived field. + @param NewDhcpDiscover The pointer to the new cached DHCP Discover packet contents. + @param NewDhcpAck The pointer to the new cached DHCP Ack packet contents. + @param NewProxyOffer The pointer to the new cached Proxy Offer packet contents. + @param NewPxeDiscover The pointer to the new cached PXE Discover packet contents. + @param NewPxeReply The pointer to the new cached PXE Reply packet contents. + @param NewPxeBisReply The pointer to the new cached PXE BIS Reply packet contents. + + @retval EFI_SUCCESS The cached packet contents were updated. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER This is NULL or not point to a valid EFI_PXE_BASE_CODE_PROTOCOL structure. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PXE_BASE_CODE_SET_PACKETS)( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + BOOLEAN *NewDhcpDiscoverValid, OPTIONAL + BOOLEAN *NewDhcpAckReceived, OPTIONAL + BOOLEAN *NewProxyOfferReceived, OPTIONAL + BOOLEAN *NewPxeDiscoverValid, OPTIONAL + BOOLEAN *NewPxeReplyReceived, OPTIONAL + BOOLEAN *NewPxeBisReplyReceived, OPTIONAL + IN EFI_PXE_BASE_CODE_PACKET *NewDhcpDiscover, OPTIONAL + IN EFI_PXE_BASE_CODE_PACKET *NewDhcpAck, OPTIONAL + IN EFI_PXE_BASE_CODE_PACKET *NewProxyOffer, OPTIONAL + IN EFI_PXE_BASE_CODE_PACKET *NewPxeDiscover, OPTIONAL + IN EFI_PXE_BASE_CODE_PACKET *NewPxeReply, OPTIONAL + IN EFI_PXE_BASE_CODE_PACKET *NewPxeBisReply OPTIONAL + ); + +// +// PXE Base Code Protocol structure +// +#define EFI_PXE_BASE_CODE_PROTOCOL_REVISION 0x00010000 + +// +// Revision defined in EFI1.1 +// +#define EFI_PXE_BASE_CODE_INTERFACE_REVISION EFI_PXE_BASE_CODE_PROTOCOL_REVISION + +/// +/// The EFI_PXE_BASE_CODE_PROTOCOL is used to control PXE-compatible devices. +/// An EFI_PXE_BASE_CODE_PROTOCOL will be layered on top of an +/// EFI_MANAGED_NETWORK_PROTOCOL protocol in order to perform packet level transactions. +/// The EFI_PXE_BASE_CODE_PROTOCOL handle also supports the +/// EFI_LOAD_FILE_PROTOCOL protocol. This provides a clean way to obtain control from the +/// boot manager if the boot path is from the remote device. +/// +struct _EFI_PXE_BASE_CODE_PROTOCOL { + /// + /// The revision of the EFI_PXE_BASE_CODE_PROTOCOL. All future revisions must + /// be backwards compatible. If a future version is not backwards compatible + /// it is not the same GUID. + /// + UINT64 Revision; + EFI_PXE_BASE_CODE_START Start; + EFI_PXE_BASE_CODE_STOP Stop; + EFI_PXE_BASE_CODE_DHCP Dhcp; + EFI_PXE_BASE_CODE_DISCOVER Discover; + EFI_PXE_BASE_CODE_MTFTP Mtftp; + EFI_PXE_BASE_CODE_UDP_WRITE UdpWrite; + EFI_PXE_BASE_CODE_UDP_READ UdpRead; + EFI_PXE_BASE_CODE_SET_IP_FILTER SetIpFilter; + EFI_PXE_BASE_CODE_ARP Arp; + EFI_PXE_BASE_CODE_SET_PARAMETERS SetParameters; + EFI_PXE_BASE_CODE_SET_STATION_IP SetStationIp; + EFI_PXE_BASE_CODE_SET_PACKETS SetPackets; + /// + /// The pointer to the EFI_PXE_BASE_CODE_MODE data for this device. + /// + EFI_PXE_BASE_CODE_MODE *Mode; +}; + +extern EFI_GUID gEfiPxeBaseCodeProtocolGuid; + +#endif diff --git a/roms/ipxe/src/include/ipxe/efi/Protocol/SimpleFileSystem.h b/roms/ipxe/src/include/ipxe/efi/Protocol/SimpleFileSystem.h index 1981808..b6bacfd 100644 --- a/roms/ipxe/src/include/ipxe/efi/Protocol/SimpleFileSystem.h +++ b/roms/ipxe/src/include/ipxe/efi/Protocol/SimpleFileSystem.h @@ -7,7 +7,7 @@ UEFI 2.0 can boot from any valid EFI image contained in a SimpleFileSystem. -Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
    +Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.
    This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License that accompanies this distribution. The full text of the license may be found at @@ -192,7 +192,7 @@ EFI_STATUS @retval EFI_DEVICE_ERROR An attempt was made to read from a deleted file. @retval EFI_DEVICE_ERROR On entry, the current file position is beyond the end of the file. @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. - @retval EFI_BUFFER_TO_SMALL The BufferSize is too small to read the current directory + @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry. BufferSize has been updated with the size needed to complete the request. @@ -365,7 +365,164 @@ EFI_STATUS IN EFI_FILE_PROTOCOL *This ); -#define EFI_FILE_PROTOCOL_REVISION 0x00010000 +typedef struct { + // + // If Event is NULL, then blocking I/O is performed. + // If Event is not NULL and non-blocking I/O is supported, then non-blocking I/O is performed, + // and Event will be signaled when the read request is completed. + // The caller must be prepared to handle the case where the callback associated with Event + // occurs before the original asynchronous I/O request call returns. + // + EFI_EVENT Event; + + // + // Defines whether or not the signaled event encountered an error. + // + EFI_STATUS Status; + + // + // For OpenEx(): Not Used, ignored. + // For ReadEx(): On input, the size of the Buffer. On output, the amount of data returned in Buffer. + // In both cases, the size is measured in bytes. + // For WriteEx(): On input, the size of the Buffer. On output, the amount of data actually written. + // In both cases, the size is measured in bytes. + // For FlushEx(): Not used, ignored. + // + UINTN BufferSize; + + // + // For OpenEx(): Not Used, ignored. + // For ReadEx(): The buffer into which the data is read. + // For WriteEx(): The buffer of data to write. + // For FlushEx(): Not Used, ignored. + // + VOID *Buffer; +} EFI_FILE_IO_TOKEN; + +/** + Opens a new file relative to the source directory's location. + + @param This A pointer to the EFI_FILE_PROTOCOL instance that is the file + handle to the source location. + @param NewHandle A pointer to the location to return the opened handle for the new + file. + @param FileName The Null-terminated string of the name of the file to be opened. + The file name may contain the following path modifiers: "\", ".", + and "..". + @param OpenMode The mode to open the file. The only valid combinations that the + file may be opened with are: Read, Read/Write, or Create/Read/Write. + @param Attributes Only valid for EFI_FILE_MODE_CREATE, in which case these are the + attribute bits for the newly created file. + @param Token A pointer to the token associated with the transaction. + + @retval EFI_SUCCESS If Event is NULL (blocking I/O): The data was read successfully. + If Event is not NULL (asynchronous I/O): The request was successfully + queued for processing. + @retval EFI_NOT_FOUND The specified file could not be found on the device. + @retval EFI_NO_MEDIA The device has no medium. + @retval EFI_MEDIA_CHANGED The device has a different medium in it or the medium is no + longer supported. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED An attempt was made to create a file, or open a file for write + when the media is write-protected. + @retval EFI_ACCESS_DENIED The service denied access to the file. + @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file. + @retval EFI_VOLUME_FULL The volume is full. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_FILE_OPEN_EX)( + IN EFI_FILE_PROTOCOL *This, + OUT EFI_FILE_PROTOCOL **NewHandle, + IN CHAR16 *FileName, + IN UINT64 OpenMode, + IN UINT64 Attributes, + IN OUT EFI_FILE_IO_TOKEN *Token + ); + + +/** + Reads data from a file. + + @param This A pointer to the EFI_FILE_PROTOCOL instance that is the file handle to read data from. + @param Token A pointer to the token associated with the transaction. + + @retval EFI_SUCCESS If Event is NULL (blocking I/O): The data was read successfully. + If Event is not NULL (asynchronous I/O): The request was successfully + queued for processing. + @retval EFI_NO_MEDIA The device has no medium. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_DEVICE_ERROR An attempt was made to read from a deleted file. + @retval EFI_DEVICE_ERROR On entry, the current file position is beyond the end of the file. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_OUT_OF_RESOURCES Unable to queue the request due to lack of resources. +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_FILE_READ_EX) ( + IN EFI_FILE_PROTOCOL *This, + IN OUT EFI_FILE_IO_TOKEN *Token +); + + +/** + Writes data to a file. + + @param This A pointer to the EFI_FILE_PROTOCOL instance that is the file handle to write data to. + @param Token A pointer to the token associated with the transaction. + + @retval EFI_SUCCESS If Event is NULL (blocking I/O): The data was read successfully. + If Event is not NULL (asynchronous I/O): The request was successfully + queued for processing. + @retval EFI_UNSUPPORTED Writes to open directory files are not supported. + @retval EFI_NO_MEDIA The device has no medium. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_DEVICE_ERROR An attempt was made to write to a deleted file. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED The file or medium is write-protected. + @retval EFI_ACCESS_DENIED The file was opened read only. + @retval EFI_VOLUME_FULL The volume is full. + @retval EFI_OUT_OF_RESOURCES Unable to queue the request due to lack of resources. +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_FILE_WRITE_EX) ( + IN EFI_FILE_PROTOCOL *This, + IN OUT EFI_FILE_IO_TOKEN *Token +); + +/** + Flushes all modified data associated with a file to a device. + + @param This A pointer to the EFI_FILE_PROTOCOL instance that is the file + handle to flush. + @param Token A pointer to the token associated with the transaction. + + @retval EFI_SUCCESS If Event is NULL (blocking I/O): The data was read successfully. + If Event is not NULL (asynchronous I/O): The request was successfully + queued for processing. + @retval EFI_NO_MEDIA The device has no medium. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED The file or medium is write-protected. + @retval EFI_ACCESS_DENIED The file was opened read-only. + @retval EFI_VOLUME_FULL The volume is full. + @retval EFI_OUT_OF_RESOURCES Unable to queue the request due to lack of resources. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_FILE_FLUSH_EX) ( + IN EFI_FILE_PROTOCOL *This, + IN OUT EFI_FILE_IO_TOKEN *Token + ); + +#define EFI_FILE_PROTOCOL_REVISION 0x00010000 +#define EFI_FILE_PROTOCOL_REVISION2 0x00020000 +#define EFI_FILE_PROTOCOL_LATEST_REVISION EFI_FILE_PROTOCOL_REVISION2 + // // Revision defined in EFI1.1. // @@ -381,8 +538,8 @@ EFI_STATUS struct _EFI_FILE_PROTOCOL { /// /// The version of the EFI_FILE_PROTOCOL interface. The version specified - /// by this specification is 0x00010000. Future versions are required - /// to be backward compatible to version 1.0. + /// by this specification is EFI_FILE_PROTOCOL_LATEST_REVISION. + /// Future versions are required to be backward compatible to version 1.0. /// UINT64 Revision; EFI_FILE_OPEN Open; @@ -395,6 +552,10 @@ struct _EFI_FILE_PROTOCOL { EFI_FILE_GET_INFO GetInfo; EFI_FILE_SET_INFO SetInfo; EFI_FILE_FLUSH Flush; + EFI_FILE_OPEN_EX OpenEx; + EFI_FILE_READ_EX ReadEx; + EFI_FILE_WRITE_EX WriteEx; + EFI_FILE_FLUSH_EX FlushEx; }; diff --git a/roms/ipxe/src/include/ipxe/efi/Protocol/TcgService.h b/roms/ipxe/src/include/ipxe/efi/Protocol/TcgService.h new file mode 100644 index 0000000..1068448 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/efi/Protocol/TcgService.h @@ -0,0 +1,209 @@ +/** @file + TCG Service Protocol as defined in TCG_EFI_Protocol_1_20_Final + See http://trustedcomputinggroup.org for the latest specification + +Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.
    +This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _TCG_SERVICE_PROTOCOL_H_ +#define _TCG_SERVICE_PROTOCOL_H_ + +FILE_LICENCE ( BSD3 ); + +#include + +#define EFI_TCG_PROTOCOL_GUID \ + {0xf541796d, 0xa62e, 0x4954, { 0xa7, 0x75, 0x95, 0x84, 0xf6, 0x1b, 0x9c, 0xdd } } + +typedef struct _EFI_TCG_PROTOCOL EFI_TCG_PROTOCOL; + +typedef struct { + UINT8 Major; + UINT8 Minor; + UINT8 RevMajor; + UINT8 RevMinor; +} TCG_VERSION; + +typedef struct _TCG_EFI_BOOT_SERVICE_CAPABILITY { + UINT8 Size; /// Size of this structure. + TCG_VERSION StructureVersion; + TCG_VERSION ProtocolSpecVersion; + UINT8 HashAlgorithmBitmap; /// Hash algorithms . + /// This protocol is capable of : 01=SHA-1. + BOOLEAN TPMPresentFlag; /// 00h = TPM not present. + BOOLEAN TPMDeactivatedFlag; /// 01h = TPM currently deactivated. +} TCG_EFI_BOOT_SERVICE_CAPABILITY; + +typedef UINT32 TCG_ALGORITHM_ID; + +/// +/// Note: +/// Status codes returned for functions of EFI_TCG_PROTOCOL do not exactly match +/// those defined in the TCG EFI Protocol 1.20 Final Specification. +/// + +/** + This service provides EFI protocol capability information, state information + about the TPM, and Event Log state information. + + @param This Indicates the calling context + @param ProtocolCapability The callee allocates memory for a TCG_BOOT_SERVICE_CAPABILITY + structure and fills in the fields with the EFI protocol + capability information and the current TPM state information. + @param TCGFeatureFlags This is a pointer to the feature flags. No feature + flags are currently defined so this parameter + MUST be set to 0. However, in the future, + feature flags may be defined that, for example, + enable hash algorithm agility. + @param EventLogLocation This is a pointer to the address of the event log in memory. + @param EventLogLastEntry If the Event Log contains more than one entry, + this is a pointer to the address of the start of + the last entry in the event log in memory. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER ProtocolCapability does not match TCG capability. +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TCG_STATUS_CHECK)( + IN EFI_TCG_PROTOCOL *This, + OUT TCG_EFI_BOOT_SERVICE_CAPABILITY + *ProtocolCapability, + OUT UINT32 *TCGFeatureFlags, + OUT EFI_PHYSICAL_ADDRESS *EventLogLocation, + OUT EFI_PHYSICAL_ADDRESS *EventLogLastEntry + ); + +/** + This service abstracts the capability to do a hash operation on a data buffer. + + @param This Indicates the calling context. + @param HashData The pointer to the data buffer to be hashed. + @param HashDataLen The length of the data buffer to be hashed. + @param AlgorithmId Identification of the Algorithm to use for the hashing operation. + @param HashedDataLen Resultant length of the hashed data. + @param HashedDataResult Resultant buffer of the hashed data. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER HashDataLen is NULL. + @retval EFI_INVALID_PARAMETER HashDataLenResult is NULL. + @retval EFI_OUT_OF_RESOURCES Cannot allocate buffer of size *HashedDataLen. + @retval EFI_UNSUPPORTED AlgorithmId not supported. + @retval EFI_BUFFER_TOO_SMALL *HashedDataLen < sizeof (TCG_DIGEST). +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TCG_HASH_ALL)( + IN EFI_TCG_PROTOCOL *This, + IN UINT8 *HashData, + IN UINT64 HashDataLen, + IN TCG_ALGORITHM_ID AlgorithmId, + IN OUT UINT64 *HashedDataLen, + IN OUT UINT8 **HashedDataResult + ); + +/** + This service abstracts the capability to add an entry to the Event Log. + + @param This Indicates the calling context + @param TCGLogData The pointer to the start of the data buffer containing + the TCG_PCR_EVENT data structure. All fields in + this structure are properly filled by the caller. + @param EventNumber The event number of the event just logged. + @param Flags Indicates additional flags. Only one flag has been + defined at this time, which is 0x01 and means the + extend operation should not be performed. All + other bits are reserved. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Insufficient memory in the event log to complete this action. +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TCG_LOG_EVENT)( + IN EFI_TCG_PROTOCOL *This, + IN TCG_PCR_EVENT *TCGLogData, + IN OUT UINT32 *EventNumber, + IN UINT32 Flags + ); + +/** + This service is a proxy for commands to the TPM. + + @param This Indicates the calling context. + @param TpmInputParameterBlockSize Size of the TPM input parameter block. + @param TpmInputParameterBlock The pointer to the TPM input parameter block. + @param TpmOutputParameterBlockSize Size of the TPM output parameter block. + @param TpmOutputParameterBlock The pointer to the TPM output parameter block. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER Invalid ordinal. + @retval EFI_UNSUPPORTED Current Task Priority Level >= EFI_TPL_CALLBACK. + @retval EFI_TIMEOUT The TIS timed-out. +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TCG_PASS_THROUGH_TO_TPM)( + IN EFI_TCG_PROTOCOL *This, + IN UINT32 TpmInputParameterBlockSize, + IN UINT8 *TpmInputParameterBlock, + IN UINT32 TpmOutputParameterBlockSize, + IN UINT8 *TpmOutputParameterBlock + ); + +/** + This service abstracts the capability to do a hash operation on a data buffer, extend a specific TPM PCR with the hash result, and add an entry to the Event Log + + @param This Indicates the calling context + @param HashData The physical address of the start of the data buffer + to be hashed, extended, and logged. + @param HashDataLen The length, in bytes, of the buffer referenced by HashData + @param AlgorithmId Identification of the Algorithm to use for the hashing operation + @param TCGLogData The physical address of the start of the data + buffer containing the TCG_PCR_EVENT data structure. + @param EventNumber The event number of the event just logged. + @param EventLogLastEntry The physical address of the first byte of the entry + just placed in the Event Log. If the Event Log was + empty when this function was called then this physical + address will be the same as the physical address of + the start of the Event Log. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_UNSUPPORTED AlgorithmId != TPM_ALG_SHA. + @retval EFI_UNSUPPORTED Current TPL >= EFI_TPL_CALLBACK. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TCG_HASH_LOG_EXTEND_EVENT)( + IN EFI_TCG_PROTOCOL *This, + IN EFI_PHYSICAL_ADDRESS HashData, + IN UINT64 HashDataLen, + IN TCG_ALGORITHM_ID AlgorithmId, + IN OUT TCG_PCR_EVENT *TCGLogData, + IN OUT UINT32 *EventNumber, + OUT EFI_PHYSICAL_ADDRESS *EventLogLastEntry + ); + +/// +/// The EFI_TCG Protocol abstracts TCG activity. +/// +struct _EFI_TCG_PROTOCOL { + EFI_TCG_STATUS_CHECK StatusCheck; + EFI_TCG_HASH_ALL HashAll; + EFI_TCG_LOG_EVENT LogEvent; + EFI_TCG_PASS_THROUGH_TO_TPM PassThroughToTpm; + EFI_TCG_HASH_LOG_EXTEND_EVENT HashLogExtendEvent; +}; + +extern EFI_GUID gEfiTcgProtocolGuid; + +#endif diff --git a/roms/ipxe/src/include/ipxe/efi/Protocol/Tcp4.h b/roms/ipxe/src/include/ipxe/efi/Protocol/Tcp4.h new file mode 100644 index 0000000..1771bc5 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/efi/Protocol/Tcp4.h @@ -0,0 +1,579 @@ +/** @file + EFI TCPv4(Transmission Control Protocol version 4) Protocol Definition + The EFI TCPv4 Service Binding Protocol is used to locate EFI TCPv4 Protocol drivers to create + and destroy child of the driver to communicate with other host using TCP protocol. + The EFI TCPv4 Protocol provides services to send and receive data stream. + +Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.
    +This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + @par Revision Reference: + This Protocol is introduced in UEFI Specification 2.0. + +**/ + +#ifndef __EFI_TCP4_PROTOCOL_H__ +#define __EFI_TCP4_PROTOCOL_H__ + +FILE_LICENCE ( BSD3 ); + +#include + +#define EFI_TCP4_SERVICE_BINDING_PROTOCOL_GUID \ + { \ + 0x00720665, 0x67EB, 0x4a99, {0xBA, 0xF7, 0xD3, 0xC3, 0x3A, 0x1C, 0x7C, 0xC9 } \ + } + +#define EFI_TCP4_PROTOCOL_GUID \ + { \ + 0x65530BC7, 0xA359, 0x410f, {0xB0, 0x10, 0x5A, 0xAD, 0xC7, 0xEC, 0x2B, 0x62 } \ + } + +typedef struct _EFI_TCP4_PROTOCOL EFI_TCP4_PROTOCOL; + +/// +/// EFI_TCP4_SERVICE_POINT is deprecated in the UEFI 2.4B and should not be used any more. +/// The definition in here is only present to provide backwards compatability. +/// +typedef struct { + EFI_HANDLE InstanceHandle; + EFI_IPv4_ADDRESS LocalAddress; + UINT16 LocalPort; + EFI_IPv4_ADDRESS RemoteAddress; + UINT16 RemotePort; +} EFI_TCP4_SERVICE_POINT; + +/// +/// EFI_TCP4_VARIABLE_DATA is deprecated in the UEFI 2.4B and should not be used any more. +/// The definition in here is only present to provide backwards compatability. +/// +typedef struct { + EFI_HANDLE DriverHandle; + UINT32 ServiceCount; + EFI_TCP4_SERVICE_POINT Services[1]; +} EFI_TCP4_VARIABLE_DATA; + +typedef struct { + BOOLEAN UseDefaultAddress; + EFI_IPv4_ADDRESS StationAddress; + EFI_IPv4_ADDRESS SubnetMask; + UINT16 StationPort; + EFI_IPv4_ADDRESS RemoteAddress; + UINT16 RemotePort; + BOOLEAN ActiveFlag; +} EFI_TCP4_ACCESS_POINT; + +typedef struct { + UINT32 ReceiveBufferSize; + UINT32 SendBufferSize; + UINT32 MaxSynBackLog; + UINT32 ConnectionTimeout; + UINT32 DataRetries; + UINT32 FinTimeout; + UINT32 TimeWaitTimeout; + UINT32 KeepAliveProbes; + UINT32 KeepAliveTime; + UINT32 KeepAliveInterval; + BOOLEAN EnableNagle; + BOOLEAN EnableTimeStamp; + BOOLEAN EnableWindowScaling; + BOOLEAN EnableSelectiveAck; + BOOLEAN EnablePathMtuDiscovery; +} EFI_TCP4_OPTION; + +typedef struct { + // + // I/O parameters + // + UINT8 TypeOfService; + UINT8 TimeToLive; + + // + // Access Point + // + EFI_TCP4_ACCESS_POINT AccessPoint; + + // + // TCP Control Options + // + EFI_TCP4_OPTION *ControlOption; +} EFI_TCP4_CONFIG_DATA; + +/// +/// TCP4 connnection state +/// +typedef enum { + Tcp4StateClosed = 0, + Tcp4StateListen = 1, + Tcp4StateSynSent = 2, + Tcp4StateSynReceived = 3, + Tcp4StateEstablished = 4, + Tcp4StateFinWait1 = 5, + Tcp4StateFinWait2 = 6, + Tcp4StateClosing = 7, + Tcp4StateTimeWait = 8, + Tcp4StateCloseWait = 9, + Tcp4StateLastAck = 10 +} EFI_TCP4_CONNECTION_STATE; + +typedef struct { + EFI_EVENT Event; + EFI_STATUS Status; +} EFI_TCP4_COMPLETION_TOKEN; + +typedef struct { + /// + /// The Status in the CompletionToken will be set to one of + /// the following values if the active open succeeds or an unexpected + /// error happens: + /// EFI_SUCCESS: The active open succeeds and the instance's + /// state is Tcp4StateEstablished. + /// EFI_CONNECTION_RESET: The connect fails because the connection is reset + /// either by instance itself or the communication peer. + /// EFI_CONNECTION_REFUSED: The connect fails because this connection is initiated with + /// an active open and the connection is refused. + /// EFI_ABORTED: The active open is aborted. + /// EFI_TIMEOUT: The connection establishment timer expires and + /// no more specific information is available. + /// EFI_NETWORK_UNREACHABLE: The active open fails because + /// an ICMP network unreachable error is received. + /// EFI_HOST_UNREACHABLE: The active open fails because an + /// ICMP host unreachable error is received. + /// EFI_PROTOCOL_UNREACHABLE: The active open fails + /// because an ICMP protocol unreachable error is received. + /// EFI_PORT_UNREACHABLE: The connection establishment + /// timer times out and an ICMP port unreachable error is received. + /// EFI_ICMP_ERROR: The connection establishment timer timeout and some other ICMP + /// error is received. + /// EFI_DEVICE_ERROR: An unexpected system or network error occurred. + /// EFI_NO_MEDIA: There was a media error. + /// + EFI_TCP4_COMPLETION_TOKEN CompletionToken; +} EFI_TCP4_CONNECTION_TOKEN; + +typedef struct { + EFI_TCP4_COMPLETION_TOKEN CompletionToken; + EFI_HANDLE NewChildHandle; +} EFI_TCP4_LISTEN_TOKEN; + +typedef struct { + UINT32 FragmentLength; + VOID *FragmentBuffer; +} EFI_TCP4_FRAGMENT_DATA; + +typedef struct { + BOOLEAN UrgentFlag; + UINT32 DataLength; + UINT32 FragmentCount; + EFI_TCP4_FRAGMENT_DATA FragmentTable[1]; +} EFI_TCP4_RECEIVE_DATA; + +typedef struct { + BOOLEAN Push; + BOOLEAN Urgent; + UINT32 DataLength; + UINT32 FragmentCount; + EFI_TCP4_FRAGMENT_DATA FragmentTable[1]; +} EFI_TCP4_TRANSMIT_DATA; + +typedef struct { + /// + /// When transmission finishes or meets any unexpected error it will + /// be set to one of the following values: + /// EFI_SUCCESS: The receiving or transmission operation + /// completes successfully. + /// EFI_CONNECTION_FIN: The receiving operation fails because the communication peer + /// has closed the connection and there is no more data in the + /// receive buffer of the instance. + /// EFI_CONNECTION_RESET: The receiving or transmission operation fails + /// because this connection is reset either by instance + /// itself or the communication peer. + /// EFI_ABORTED: The receiving or transmission is aborted. + /// EFI_TIMEOUT: The transmission timer expires and no more + /// specific information is available. + /// EFI_NETWORK_UNREACHABLE: The transmission fails + /// because an ICMP network unreachable error is received. + /// EFI_HOST_UNREACHABLE: The transmission fails because an + /// ICMP host unreachable error is received. + /// EFI_PROTOCOL_UNREACHABLE: The transmission fails + /// because an ICMP protocol unreachable error is received. + /// EFI_PORT_UNREACHABLE: The transmission fails and an + /// ICMP port unreachable error is received. + /// EFI_ICMP_ERROR: The transmission fails and some other + /// ICMP error is received. + /// EFI_DEVICE_ERROR: An unexpected system or network error occurs. + /// EFI_NO_MEDIA: There was a media error. + /// + EFI_TCP4_COMPLETION_TOKEN CompletionToken; + union { + /// + /// When this token is used for receiving, RxData is a pointer to EFI_TCP4_RECEIVE_DATA. + /// + EFI_TCP4_RECEIVE_DATA *RxData; + /// + /// When this token is used for transmitting, TxData is a pointer to EFI_TCP4_TRANSMIT_DATA. + /// + EFI_TCP4_TRANSMIT_DATA *TxData; + } Packet; +} EFI_TCP4_IO_TOKEN; + +typedef struct { + EFI_TCP4_COMPLETION_TOKEN CompletionToken; + BOOLEAN AbortOnClose; +} EFI_TCP4_CLOSE_TOKEN; + +// +// Interface definition for TCP4 protocol +// + +/** + Get the current operational status. + + @param This The pointer to the EFI_TCP4_PROTOCOL instance. + @param Tcp4State The pointer to the buffer to receive the current TCP state. + @param Tcp4ConfigData The pointer to the buffer to receive the current TCP configuration. + @param Ip4ModeData The pointer to the buffer to receive the current IPv4 configuration + data used by the TCPv4 instance. + @param MnpConfigData The pointer to the buffer to receive the current MNP configuration + data used indirectly by the TCPv4 instance. + @param SnpModeData The pointer to the buffer to receive the current SNP configuration + data used indirectly by the TCPv4 instance. + + @retval EFI_SUCCESS The mode data was read. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_STARTED No configuration data is available because this instance hasn't + been started. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TCP4_GET_MODE_DATA)( + IN EFI_TCP4_PROTOCOL *This, + OUT EFI_TCP4_CONNECTION_STATE *Tcp4State OPTIONAL, + OUT EFI_TCP4_CONFIG_DATA *Tcp4ConfigData OPTIONAL, + OUT EFI_IP4_MODE_DATA *Ip4ModeData OPTIONAL, + OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL, + OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL + ); + +/** + Initialize or brutally reset the operational parameters for this EFI TCPv4 instance. + + @param This The pointer to the EFI_TCP4_PROTOCOL instance. + @param Tcp4ConfigData The pointer to the configure data to configure the instance. + + @retval EFI_SUCCESS The operational settings are set, changed, or reset + successfully. + @retval EFI_INVALID_PARAMETER Some parameter is invalid. + @retval EFI_NO_MAPPING When using a default address, configuration (through + DHCP, BOOTP, RARP, etc.) is not finished yet. + @retval EFI_ACCESS_DENIED Configuring TCP instance when it is configured without + calling Configure() with NULL to reset it. + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred. + @retval EFI_UNSUPPORTED One or more of the control options are not supported in + the implementation. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources when + executing Configure(). + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TCP4_CONFIGURE)( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_CONFIG_DATA *TcpConfigData OPTIONAL + ); + + +/** + Add or delete a route entry to the route table + + @param This The pointer to the EFI_TCP4_PROTOCOL instance. + @param DeleteRoute Set it to TRUE to delete this route from the routing table. Set it to + FALSE to add this route to the routing table. + DestinationAddress and SubnetMask are used as the + keywords to search route entry. + @param SubnetAddress The destination network. + @param SubnetMask The subnet mask of the destination network. + @param GatewayAddress The gateway address for this route. It must be on the same + subnet with the station address unless a direct route is specified. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_STARTED The EFI TCPv4 Protocol instance has not been configured. + @retval EFI_NO_MAPPING When using a default address, configuration (DHCP, BOOTP, + RARP, etc.) is not finished yet. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - SubnetAddress is NULL. + - SubnetMask is NULL. + - GatewayAddress is NULL. + - *SubnetAddress is not NULL a valid subnet address. + - *SubnetMask is not a valid subnet mask. + - *GatewayAddress is not a valid unicast IP address or it + is not in the same subnet. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to add the entry to the + routing table. + @retval EFI_NOT_FOUND This route is not in the routing table. + @retval EFI_ACCESS_DENIED The route is already defined in the routing table. + @retval EFI_UNSUPPORTED The TCP driver does not support this operation. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TCP4_ROUTES)( + IN EFI_TCP4_PROTOCOL *This, + IN BOOLEAN DeleteRoute, + IN EFI_IPv4_ADDRESS *SubnetAddress, + IN EFI_IPv4_ADDRESS *SubnetMask, + IN EFI_IPv4_ADDRESS *GatewayAddress + ); + +/** + Initiate a nonblocking TCP connection request for an active TCP instance. + + @param This The pointer to the EFI_TCP4_PROTOCOL instance. + @param ConnectionToken The pointer to the connection token to return when the TCP three + way handshake finishes. + + @retval EFI_SUCCESS The connection request is successfully initiated and the state + of this TCPv4 instance has been changed to Tcp4StateSynSent. + @retval EFI_NOT_STARTED This EFI TCPv4 Protocol instance has not been configured. + @retval EFI_ACCESS_DENIED One or more of the following conditions are TRUE: + - This instance is not configured as an active one. + - This instance is not in Tcp4StateClosed state. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + - ConnectionToken is NULL. + - ConnectionToken->CompletionToken.Event is NULL. + @retval EFI_OUT_OF_RESOURCES The driver can't allocate enough resource to initiate the activ eopen. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TCP4_CONNECT)( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_CONNECTION_TOKEN *ConnectionToken + ); + + +/** + Listen on the passive instance to accept an incoming connection request. This is a nonblocking operation. + + @param This The pointer to the EFI_TCP4_PROTOCOL instance. + @param ListenToken The pointer to the listen token to return when operation finishes. + + @retval EFI_SUCCESS The listen token has been queued successfully. + @retval EFI_NOT_STARTED This EFI TCPv4 Protocol instance has not been configured. + @retval EFI_ACCESS_DENIED One or more of the following are TRUE: + - This instance is not a passive instance. + - This instance is not in Tcp4StateListen state. + - The same listen token has already existed in the listen + token queue of this TCP instance. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + - ListenToken is NULL. + - ListentToken->CompletionToken.Event is NULL. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough resource to finish the operation. + @retval EFI_DEVICE_ERROR Any unexpected and not belonged to above category error. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TCP4_ACCEPT)( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_LISTEN_TOKEN *ListenToken + ); + +/** + Queues outgoing data into the transmit queue. + + @param This The pointer to the EFI_TCP4_PROTOCOL instance. + @param Token The pointer to the completion token to queue to the transmit queue. + + @retval EFI_SUCCESS The data has been queued for transmission. + @retval EFI_NOT_STARTED This EFI TCPv4 Protocol instance has not been configured. + @retval EFI_NO_MAPPING When using a default address, configuration (DHCP, BOOTP, + RARP, etc.) is not finished yet. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + - Token is NULL. + - Token->CompletionToken.Event is NULL. + - Token->Packet.TxData is NULL L. + - Token->Packet.FragmentCount is zero. + - Token->Packet.DataLength is not equal to the sum of fragment lengths. + @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE: + - A transmit completion token with the same Token->CompletionToken.Event + was already in the transmission queue. + - The current instance is in Tcp4StateClosed state. + - The current instance is a passive one and it is in + Tcp4StateListen state. + - User has called Close() to disconnect this connection. + @retval EFI_NOT_READY The completion token could not be queued because the + transmit queue is full. + @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data because of resource + shortage. + @retval EFI_NETWORK_UNREACHABLE There is no route to the destination network or address. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TCP4_TRANSMIT)( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_IO_TOKEN *Token + ); + + +/** + Places an asynchronous receive request into the receiving queue. + + @param This The pointer to the EFI_TCP4_PROTOCOL instance. + @param Token The pointer to a token that is associated with the receive data + descriptor. + + @retval EFI_SUCCESS The receive completion token was cached. + @retval EFI_NOT_STARTED This EFI TCPv4 Protocol instance has not been configured. + @retval EFI_NO_MAPPING When using a default address, configuration (DHCP, BOOTP, RARP, + etc.) is not finished yet. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Token is NULL. + - Token->CompletionToken.Event is NULL. + - Token->Packet.RxData is NULL. + - Token->Packet.RxData->DataLength is 0. + - The Token->Packet.RxData->DataLength is not + the sum of all FragmentBuffer length in FragmentTable. + @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued due to a lack of + system resources (usually memory). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE: + - A receive completion token with the same Token- + >CompletionToken.Event was already in the receive + queue. + - The current instance is in Tcp4StateClosed state. + - The current instance is a passive one and it is in + Tcp4StateListen state. + - User has called Close() to disconnect this connection. + @retval EFI_CONNECTION_FIN The communication peer has closed the connection and there is + no any buffered data in the receive buffer of this instance. + @retval EFI_NOT_READY The receive request could not be queued because the receive queue is full. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TCP4_RECEIVE)( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_IO_TOKEN *Token + ); + +/** + Disconnecting a TCP connection gracefully or reset a TCP connection. This function is a + nonblocking operation. + + @param This The pointer to the EFI_TCP4_PROTOCOL instance. + @param CloseToken The pointer to the close token to return when operation finishes. + + @retval EFI_SUCCESS The Close() is called successfully. + @retval EFI_NOT_STARTED This EFI TCPv4 Protocol instance has not been configured. + @retval EFI_ACCESS_DENIED One or more of the following are TRUE: + - Configure() has been called with + TcpConfigData set to NULL and this function has + not returned. + - Previous Close() call on this instance has not + finished. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + - CloseToken is NULL. + - CloseToken->CompletionToken.Event is NULL. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough resource to finish the operation. + @retval EFI_DEVICE_ERROR Any unexpected and not belonged to above category error. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TCP4_CLOSE)( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_CLOSE_TOKEN *CloseToken + ); + +/** + Abort an asynchronous connection, listen, transmission or receive request. + + @param This The pointer to the EFI_TCP4_PROTOCOL instance. + @param Token The pointer to a token that has been issued by + EFI_TCP4_PROTOCOL.Connect(), + EFI_TCP4_PROTOCOL.Accept(), + EFI_TCP4_PROTOCOL.Transmit() or + EFI_TCP4_PROTOCOL.Receive(). If NULL, all pending + tokens issued by above four functions will be aborted. Type + EFI_TCP4_COMPLETION_TOKEN is defined in + EFI_TCP4_PROTOCOL.Connect(). + + @retval EFI_SUCCESS The asynchronous I/O request is aborted and Token->Event + is signaled. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_STARTED This instance hasn't been configured. + @retval EFI_NO_MAPPING When using the default address, configuration + (DHCP, BOOTP,RARP, etc.) hasn't finished yet. + @retval EFI_NOT_FOUND The asynchronous I/O request isn't found in the + transmission or receive queue. It has either + completed or wasn't issued by Transmit() and Receive(). + @retval EFI_UNSUPPORTED The implementation does not support this function. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TCP4_CANCEL)( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_COMPLETION_TOKEN *Token OPTIONAL + ); + + +/** + Poll to receive incoming data and transmit outgoing segments. + + @param This The pointer to the EFI_TCP4_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_NOT_READY No incoming or outgoing data is processed. + @retval EFI_TIMEOUT Data was dropped out of the transmission or receive queue. + Consider increasing the polling rate. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TCP4_POLL)( + IN EFI_TCP4_PROTOCOL *This + ); + +/// +/// The EFI_TCP4_PROTOCOL defines the EFI TCPv4 Protocol child to be used by +/// any network drivers or applications to send or receive data stream. +/// It can either listen on a specified port as a service or actively connected +/// to remote peer as a client. Each instance has its own independent settings, +/// such as the routing table. +/// +struct _EFI_TCP4_PROTOCOL { + EFI_TCP4_GET_MODE_DATA GetModeData; + EFI_TCP4_CONFIGURE Configure; + EFI_TCP4_ROUTES Routes; + EFI_TCP4_CONNECT Connect; + EFI_TCP4_ACCEPT Accept; + EFI_TCP4_TRANSMIT Transmit; + EFI_TCP4_RECEIVE Receive; + EFI_TCP4_CLOSE Close; + EFI_TCP4_CANCEL Cancel; + EFI_TCP4_POLL Poll; +}; + +extern EFI_GUID gEfiTcp4ServiceBindingProtocolGuid; +extern EFI_GUID gEfiTcp4ProtocolGuid; + +#endif diff --git a/roms/ipxe/src/include/ipxe/efi/Protocol/Udp4.h b/roms/ipxe/src/include/ipxe/efi/Protocol/Udp4.h new file mode 100644 index 0000000..3c61db8 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/efi/Protocol/Udp4.h @@ -0,0 +1,447 @@ +/** @file + UDP4 Service Binding Protocol as defined in UEFI specification. + + The EFI UDPv4 Protocol provides simple packet-oriented services + to transmit and receive UDP packets. + +Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.
    +This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + @par Revision Reference: + This Protocol is introduced in UEFI Specification 2.0. + +**/ + +#ifndef __EFI_UDP4_PROTOCOL_H__ +#define __EFI_UDP4_PROTOCOL_H__ + +FILE_LICENCE ( BSD3 ); + +#include +// +//GUID definitions +// +#define EFI_UDP4_SERVICE_BINDING_PROTOCOL_GUID \ + { \ + 0x83f01464, 0x99bd, 0x45e5, {0xb3, 0x83, 0xaf, 0x63, 0x05, 0xd8, 0xe9, 0xe6 } \ + } + +#define EFI_UDP4_PROTOCOL_GUID \ + { \ + 0x3ad9df29, 0x4501, 0x478d, {0xb1, 0xf8, 0x7f, 0x7f, 0xe7, 0x0e, 0x50, 0xf3 } \ + } + +typedef struct _EFI_UDP4_PROTOCOL EFI_UDP4_PROTOCOL; + +/// +/// EFI_UDP4_SERVICE_POINT is deprecated in the UEFI 2.4B and should not be used any more. +/// The definition in here is only present to provide backwards compatability. +/// +typedef struct { + EFI_HANDLE InstanceHandle; + EFI_IPv4_ADDRESS LocalAddress; + UINT16 LocalPort; + EFI_IPv4_ADDRESS RemoteAddress; + UINT16 RemotePort; +} EFI_UDP4_SERVICE_POINT; + +/// +/// EFI_UDP4_VARIABLE_DATA is deprecated in the UEFI 2.4B and should not be used any more. +/// The definition in here is only present to provide backwards compatability. +/// +typedef struct { + EFI_HANDLE DriverHandle; + UINT32 ServiceCount; + EFI_UDP4_SERVICE_POINT Services[1]; +} EFI_UDP4_VARIABLE_DATA; + +typedef struct { + UINT32 FragmentLength; + VOID *FragmentBuffer; +} EFI_UDP4_FRAGMENT_DATA; + +typedef struct { + EFI_IPv4_ADDRESS SourceAddress; + UINT16 SourcePort; + EFI_IPv4_ADDRESS DestinationAddress; + UINT16 DestinationPort; +} EFI_UDP4_SESSION_DATA; +typedef struct { + // + // Receiving Filters + // + BOOLEAN AcceptBroadcast; + BOOLEAN AcceptPromiscuous; + BOOLEAN AcceptAnyPort; + BOOLEAN AllowDuplicatePort; + // + // I/O parameters + // + UINT8 TypeOfService; + UINT8 TimeToLive; + BOOLEAN DoNotFragment; + UINT32 ReceiveTimeout; + UINT32 TransmitTimeout; + // + // Access Point + // + BOOLEAN UseDefaultAddress; + EFI_IPv4_ADDRESS StationAddress; + EFI_IPv4_ADDRESS SubnetMask; + UINT16 StationPort; + EFI_IPv4_ADDRESS RemoteAddress; + UINT16 RemotePort; +} EFI_UDP4_CONFIG_DATA; + +typedef struct { + EFI_UDP4_SESSION_DATA *UdpSessionData; //OPTIONAL + EFI_IPv4_ADDRESS *GatewayAddress; //OPTIONAL + UINT32 DataLength; + UINT32 FragmentCount; + EFI_UDP4_FRAGMENT_DATA FragmentTable[1]; +} EFI_UDP4_TRANSMIT_DATA; + +typedef struct { + EFI_TIME TimeStamp; + EFI_EVENT RecycleSignal; + EFI_UDP4_SESSION_DATA UdpSession; + UINT32 DataLength; + UINT32 FragmentCount; + EFI_UDP4_FRAGMENT_DATA FragmentTable[1]; +} EFI_UDP4_RECEIVE_DATA; + + +typedef struct { + EFI_EVENT Event; + EFI_STATUS Status; + union { + EFI_UDP4_RECEIVE_DATA *RxData; + EFI_UDP4_TRANSMIT_DATA *TxData; + } Packet; +} EFI_UDP4_COMPLETION_TOKEN; + +/** + Reads the current operational settings. + + The GetModeData() function copies the current operational settings of this EFI + UDPv4 Protocol instance into user-supplied buffers. This function is used + optionally to retrieve the operational mode data of underlying networks or + drivers. + + @param This The pointer to the EFI_UDP4_PROTOCOL instance. + @param Udp4ConfigData The pointer to the buffer to receive the current configuration data. + @param Ip4ModeData The pointer to the EFI IPv4 Protocol mode data structure. + @param MnpConfigData The pointer to the managed network configuration data structure. + @param SnpModeData The pointer to the simple network mode data structure. + + @retval EFI_SUCCESS The mode data was read. + @retval EFI_NOT_STARTED When Udp4ConfigData is queried, no configuration data is + available because this instance has not been started. + @retval EFI_INVALID_PARAMETER This is NULL. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_UDP4_GET_MODE_DATA)( + IN EFI_UDP4_PROTOCOL *This, + OUT EFI_UDP4_CONFIG_DATA *Udp4ConfigData OPTIONAL, + OUT EFI_IP4_MODE_DATA *Ip4ModeData OPTIONAL, + OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL, + OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL + ); + + +/** + Initializes, changes, or resets the operational parameters for this instance of the EFI UDPv4 + Protocol. + + The Configure() function is used to do the following: + * Initialize and start this instance of the EFI UDPv4 Protocol. + * Change the filtering rules and operational parameters. + * Reset this instance of the EFI UDPv4 Protocol. + Until these parameters are initialized, no network traffic can be sent or + received by this instance. This instance can be also reset by calling Configure() + with UdpConfigData set to NULL. Once reset, the receiving queue and transmitting + queue are flushed and no traffic is allowed through this instance. + With different parameters in UdpConfigData, Configure() can be used to bind + this instance to specified port. + + @param This The pointer to the EFI_UDP4_PROTOCOL instance. + @param Udp4ConfigData The pointer to the buffer to receive the current configuration data. + + @retval EFI_SUCCESS The configuration settings were set, changed, or reset successfully. + @retval EFI_NO_MAPPING When using a default address, configuration (DHCP, BOOTP, + RARP, etc.) is not finished yet. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_INVALID_PARAMETER UdpConfigData.StationAddress is not a valid unicast IPv4 address. + @retval EFI_INVALID_PARAMETER UdpConfigData.SubnetMask is not a valid IPv4 address mask. The subnet + mask must be contiguous. + @retval EFI_INVALID_PARAMETER UdpConfigData.RemoteAddress is not a valid unicast IPv4 address if it + is not zero. + @retval EFI_ALREADY_STARTED The EFI UDPv4 Protocol instance is already started/configured + and must be stopped/reset before it can be reconfigured. + @retval EFI_ACCESS_DENIED UdpConfigData. AllowDuplicatePort is FALSE + and UdpConfigData.StationPort is already used by + other instance. + @retval EFI_OUT_OF_RESOURCES The EFI UDPv4 Protocol driver cannot allocate memory for this + EFI UDPv4 Protocol instance. + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred and this instance + was not opened. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_UDP4_CONFIGURE)( + IN EFI_UDP4_PROTOCOL *This, + IN EFI_UDP4_CONFIG_DATA *UdpConfigData OPTIONAL + ); + +/** + Joins and leaves multicast groups. + + The Groups() function is used to enable and disable the multicast group + filtering. If the JoinFlag is FALSE and the MulticastAddress is NULL, then all + currently joined groups are left. + + @param This The pointer to the EFI_UDP4_PROTOCOL instance. + @param JoinFlag Set to TRUE to join a multicast group. Set to FALSE to leave one + or all multicast groups. + @param MulticastAddress The pointer to multicast group address to join or leave. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_STARTED The EFI UDPv4 Protocol instance has not been started. + @retval EFI_NO_MAPPING When using a default address, configuration (DHCP, BOOTP, + RARP, etc.) is not finished yet. + @retval EFI_OUT_OF_RESOURCES Could not allocate resources to join the group. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - JoinFlag is TRUE and MulticastAddress is NULL. + - JoinFlag is TRUE and *MulticastAddress is not + a valid multicast address. + @retval EFI_ALREADY_STARTED The group address is already in the group table (when + JoinFlag is TRUE). + @retval EFI_NOT_FOUND The group address is not in the group table (when JoinFlag is + FALSE). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_UDP4_GROUPS)( + IN EFI_UDP4_PROTOCOL *This, + IN BOOLEAN JoinFlag, + IN EFI_IPv4_ADDRESS *MulticastAddress OPTIONAL + ); + +/** + Adds and deletes routing table entries. + + The Routes() function adds a route to or deletes a route from the routing table. + Routes are determined by comparing the SubnetAddress with the destination IP + address and arithmetically AND-ing it with the SubnetMask. The gateway address + must be on the same subnet as the configured station address. + The default route is added with SubnetAddress and SubnetMask both set to 0.0.0.0. + The default route matches all destination IP addresses that do not match any + other routes. + A zero GatewayAddress is a nonroute. Packets are sent to the destination IP + address if it can be found in the Address Resolution Protocol (ARP) cache or + on the local subnet. One automatic nonroute entry will be inserted into the + routing table for outgoing packets that are addressed to a local subnet + (gateway address of 0.0.0.0). + Each instance of the EFI UDPv4 Protocol has its own independent routing table. + Instances of the EFI UDPv4 Protocol that use the default IP address will also + have copies of the routing table provided by the EFI_IP4_CONFIG_PROTOCOL. These + copies will be updated automatically whenever the IP driver reconfigures its + instances; as a result, the previous modification to these copies will be lost. + + @param This The pointer to the EFI_UDP4_PROTOCOL instance. + @param DeleteRoute Set to TRUE to delete this route from the routing table. + Set to FALSE to add this route to the routing table. + @param SubnetAddress The destination network address that needs to be routed. + @param SubnetMask The subnet mask of SubnetAddress. + @param GatewayAddress The gateway IP address for this route. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_STARTED The EFI UDPv4 Protocol instance has not been started. + @retval EFI_NO_MAPPING When using a default address, configuration (DHCP, BOOTP, + - RARP, etc.) is not finished yet. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES Could not add the entry to the routing table. + @retval EFI_NOT_FOUND This route is not in the routing table. + @retval EFI_ACCESS_DENIED The route is already defined in the routing table. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_UDP4_ROUTES)( + IN EFI_UDP4_PROTOCOL *This, + IN BOOLEAN DeleteRoute, + IN EFI_IPv4_ADDRESS *SubnetAddress, + IN EFI_IPv4_ADDRESS *SubnetMask, + IN EFI_IPv4_ADDRESS *GatewayAddress + ); + +/** + Polls for incoming data packets and processes outgoing data packets. + + The Poll() function can be used by network drivers and applications to increase + the rate that data packets are moved between the communications device and the + transmit and receive queues. + In some systems, the periodic timer event in the managed network driver may not + poll the underlying communications device fast enough to transmit and/or receive + all data packets without missing incoming packets or dropping outgoing packets. + Drivers and applications that are experiencing packet loss should try calling + the Poll() function more often. + + @param This The pointer to the EFI_UDP4_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_TIMEOUT Data was dropped out of the transmit and/or receive queue. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_UDP4_POLL)( + IN EFI_UDP4_PROTOCOL *This + ); + +/** + Places an asynchronous receive request into the receiving queue. + + The Receive() function places a completion token into the receive packet queue. + This function is always asynchronous. + The caller must fill in the Token.Event field in the completion token, and this + field cannot be NULL. When the receive operation completes, the EFI UDPv4 Protocol + driver updates the Token.Status and Token.Packet.RxData fields and the Token.Event + is signaled. Providing a proper notification function and context for the event + will enable the user to receive the notification and receiving status. That + notification function is guaranteed to not be re-entered. + + @param This The pointer to the EFI_UDP4_PROTOCOL instance. + @param Token The pointer to a token that is associated with the receive data + descriptor. + + @retval EFI_SUCCESS The receive completion token was cached. + @retval EFI_NOT_STARTED This EFI UDPv4 Protocol instance has not been started. + @retval EFI_NO_MAPPING When using a default address, configuration (DHCP, BOOTP, RARP, etc.) + is not finished yet. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_INVALID_PARAMETER Token is NULL. + @retval EFI_INVALID_PARAMETER Token.Event is NULL. + @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued due to a lack of system + resources (usually memory). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_ACCESS_DENIED A receive completion token with the same Token.Event was already in + the receive queue. + @retval EFI_NOT_READY The receive request could not be queued because the receive queue is full. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_UDP4_RECEIVE)( + IN EFI_UDP4_PROTOCOL *This, + IN EFI_UDP4_COMPLETION_TOKEN *Token + ); + +/** + Queues outgoing data packets into the transmit queue. + + The Transmit() function places a sending request to this instance of the EFI + UDPv4 Protocol, alongside the transmit data that was filled by the user. Whenever + the packet in the token is sent out or some errors occur, the Token.Event will + be signaled and Token.Status is updated. Providing a proper notification function + and context for the event will enable the user to receive the notification and + transmitting status. + + @param This The pointer to the EFI_UDP4_PROTOCOL instance. + @param Token The pointer to the completion token that will be placed into the + transmit queue. + + @retval EFI_SUCCESS The data has been queued for transmission. + @retval EFI_NOT_STARTED This EFI UDPv4 Protocol instance has not been started. + @retval EFI_NO_MAPPING When using a default address, configuration (DHCP, BOOTP, + RARP, etc.) is not finished yet. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_ACCESS_DENIED The transmit completion token with the same + Token.Event was already in the transmit queue. + @retval EFI_NOT_READY The completion token could not be queued because the + transmit queue is full. + @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data. + @retval EFI_NOT_FOUND There is no route to the destination network or address. + @retval EFI_BAD_BUFFER_SIZE The data length is greater than the maximum UDP packet + size. Or the length of the IP header + UDP header + data + length is greater than MTU if DoNotFragment is TRUE. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_UDP4_TRANSMIT)( + IN EFI_UDP4_PROTOCOL *This, + IN EFI_UDP4_COMPLETION_TOKEN *Token + ); + +/** + Aborts an asynchronous transmit or receive request. + + The Cancel() function is used to abort a pending transmit or receive request. + If the token is in the transmit or receive request queues, after calling this + function, Token.Status will be set to EFI_ABORTED and then Token.Event will be + signaled. If the token is not in one of the queues, which usually means that + the asynchronous operation has completed, this function will not signal the + token and EFI_NOT_FOUND is returned. + + @param This The pointer to the EFI_UDP4_PROTOCOL instance. + @param Token The pointer to a token that has been issued by + EFI_UDP4_PROTOCOL.Transmit() or + EFI_UDP4_PROTOCOL.Receive().If NULL, all pending + tokens are aborted. + + @retval EFI_SUCCESS The asynchronous I/O request was aborted and Token.Event + was signaled. When Token is NULL, all pending requests are + aborted and their events are signaled. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_NO_MAPPING When using the default address, configuration (DHCP, BOOTP, + RARP, etc.) is not finished yet. + @retval EFI_NOT_FOUND When Token is not NULL, the asynchronous I/O request was + not found in the transmit or receive queue. It has either completed + or was not issued by Transmit() and Receive(). + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_UDP4_CANCEL)( + IN EFI_UDP4_PROTOCOL *This, + IN EFI_UDP4_COMPLETION_TOKEN *Token OPTIONAL + ); + +/// +/// The EFI_UDP4_PROTOCOL defines an EFI UDPv4 Protocol session that can be used +/// by any network drivers, applications, or daemons to transmit or receive UDP packets. +/// This protocol instance can either be bound to a specified port as a service or +/// connected to some remote peer as an active client. Each instance has its own settings, +/// such as the routing table and group table, which are independent from each other. +/// +struct _EFI_UDP4_PROTOCOL { + EFI_UDP4_GET_MODE_DATA GetModeData; + EFI_UDP4_CONFIGURE Configure; + EFI_UDP4_GROUPS Groups; + EFI_UDP4_ROUTES Routes; + EFI_UDP4_TRANSMIT Transmit; + EFI_UDP4_RECEIVE Receive; + EFI_UDP4_CANCEL Cancel; + EFI_UDP4_POLL Poll; +}; + +extern EFI_GUID gEfiUdp4ServiceBindingProtocolGuid; +extern EFI_GUID gEfiUdp4ProtocolGuid; + +#endif diff --git a/roms/ipxe/src/include/ipxe/efi/Protocol/VlanConfig.h b/roms/ipxe/src/include/ipxe/efi/Protocol/VlanConfig.h new file mode 100644 index 0000000..928fade --- /dev/null +++ b/roms/ipxe/src/include/ipxe/efi/Protocol/VlanConfig.h @@ -0,0 +1,145 @@ +/** @file + EFI VLAN Config protocol is to provide manageability interface for VLAN configuration. + + Copyright (c) 2009, Intel Corporation. All rights reserved.
    + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + @par Revision Reference: + This Protocol is introduced in UEFI Specification 2.2 + +**/ + +#ifndef __EFI_VLANCONFIG_PROTOCOL_H__ +#define __EFI_VLANCONFIG_PROTOCOL_H__ + +FILE_LICENCE ( BSD3 ); + + +#define EFI_VLAN_CONFIG_PROTOCOL_GUID \ + { \ + 0x9e23d768, 0xd2f3, 0x4366, {0x9f, 0xc3, 0x3a, 0x7a, 0xba, 0x86, 0x43, 0x74 } \ + } + +typedef struct _EFI_VLAN_CONFIG_PROTOCOL EFI_VLAN_CONFIG_PROTOCOL; + + +/// +/// EFI_VLAN_FIND_DATA +/// +typedef struct { + UINT16 VlanId; ///< Vlan Identifier. + UINT8 Priority; ///< Priority of this VLAN. +} EFI_VLAN_FIND_DATA; + + +/** + Create a VLAN device or modify the configuration parameter of an + already-configured VLAN. + + The Set() function is used to create a new VLAN device or change the VLAN + configuration parameters. If the VlanId hasn't been configured in the + physical Ethernet device, a new VLAN device will be created. If a VLAN with + this VlanId is already configured, then related configuration will be updated + as the input parameters. + + If VlanId is zero, the VLAN device will send and receive untagged frames. + Otherwise, the VLAN device will send and receive VLAN-tagged frames containing the VlanId. + If VlanId is out of scope of (0-4094), EFI_INVALID_PARAMETER is returned. + If Priority is out of the scope of (0-7), then EFI_INVALID_PARAMETER is returned. + If there is not enough system memory to perform the registration, then + EFI_OUT_OF_RESOURCES is returned. + + @param[in] This Points to the EFI_VLAN_CONFIG_PROTOCOL. + @param[in] VlanId A unique identifier (1-4094) of the VLAN which is being created + or modified, or zero (0). + @param[in] Priority 3 bit priority in VLAN header. Priority 0 is default value. If + VlanId is zero (0), Priority is ignored. + + @retval EFI_SUCCESS The VLAN is successfully configured. + @retval EFI_INVALID_PARAMETER One or more of following conditions is TRUE: + - This is NULL. + - VlanId is an invalid VLAN Identifier. + - Priority is invalid. + @retval EFI_OUT_OF_RESOURCES There is not enough system memory to perform the registration. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_VLAN_CONFIG_SET)( + IN EFI_VLAN_CONFIG_PROTOCOL *This, + IN UINT16 VlanId, + IN UINT8 Priority + ); + +/** + Find configuration information for specified VLAN or all configured VLANs. + + The Find() function is used to find the configuration information for matching + VLAN and allocate a buffer into which those entries are copied. + + @param[in] This Points to the EFI_VLAN_CONFIG_PROTOCOL. + @param[in] VlanId Pointer to VLAN identifier. Set to NULL to find all + configured VLANs. + @param[out] NumberOfVlan The number of VLANs which is found by the specified criteria. + @param[out] Entries The buffer which receive the VLAN configuration. + + @retval EFI_SUCCESS The VLAN is successfully found. + @retval EFI_INVALID_PARAMETER One or more of following conditions is TRUE: + - This is NULL. + - Specified VlanId is invalid. + @retval EFI_NOT_FOUND No matching VLAN is found. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_VLAN_CONFIG_FIND)( + IN EFI_VLAN_CONFIG_PROTOCOL *This, + IN UINT16 *VlanId OPTIONAL, + OUT UINT16 *NumberOfVlan, + OUT EFI_VLAN_FIND_DATA **Entries + ); + +/** + Remove the configured VLAN device. + + The Remove() function is used to remove the specified VLAN device. + If the VlanId is out of the scope of (0-4094), EFI_INVALID_PARAMETER is returned. + If specified VLAN hasn't been previously configured, EFI_NOT_FOUND is returned. + + @param[in] This Points to the EFI_VLAN_CONFIG_PROTOCOL. + @param[in] VlanId Identifier (0-4094) of the VLAN to be removed. + + @retval EFI_SUCCESS The VLAN is successfully removed. + @retval EFI_INVALID_PARAMETER One or more of following conditions is TRUE: + - This is NULL. + - VlanId is an invalid parameter. + @retval EFI_NOT_FOUND The to-be-removed VLAN does not exist. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_VLAN_CONFIG_REMOVE)( + IN EFI_VLAN_CONFIG_PROTOCOL *This, + IN UINT16 VlanId + ); + +/// +/// EFI_VLAN_CONFIG_PROTOCOL +/// provide manageability interface for VLAN setting. The intended +/// VLAN tagging implementation is IEEE802.1Q. +/// +struct _EFI_VLAN_CONFIG_PROTOCOL { + EFI_VLAN_CONFIG_SET Set; + EFI_VLAN_CONFIG_FIND Find; + EFI_VLAN_CONFIG_REMOVE Remove; +}; + +extern EFI_GUID gEfiVlanConfigProtocolGuid; + +#endif diff --git a/roms/ipxe/src/include/ipxe/efi/Uefi/UefiBaseType.h b/roms/ipxe/src/include/ipxe/efi/Uefi/UefiBaseType.h index c56f375..371dae6 100644 --- a/roms/ipxe/src/include/ipxe/efi/Uefi/UefiBaseType.h +++ b/roms/ipxe/src/include/ipxe/efi/Uefi/UefiBaseType.h @@ -2,6 +2,8 @@ Defines data types and constants introduced in UEFI. Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
    +Portions copyright (c) 2011 - 2013, ARM Ltd. All rights reserved.
    + This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License that accompanies this distribution. The full text of the license may be found at @@ -243,6 +245,11 @@ typedef union { /// #define EFI_IMAGE_MACHINE_ARMTHUMB_MIXED 0x01C2 +/// +/// PE32+ Machine type for AARCH64 A64 images. +/// +#define EFI_IMAGE_MACHINE_AARCH64 0xAA64 + #if defined (MDE_CPU_IA32) @@ -272,6 +279,13 @@ typedef union { #define EFI_IMAGE_MACHINE_CROSS_TYPE_SUPPORTED(Machine) ((Machine) == EFI_IMAGE_MACHINE_ARMTHUMB_MIXED) +#elif defined (MDE_CPU_AARCH64) + +#define EFI_IMAGE_MACHINE_TYPE_SUPPORTED(Machine) \ + (((Machine) == EFI_IMAGE_MACHINE_AARCH64) || ((Machine) == EFI_IMAGE_MACHINE_EBC)) + +#define EFI_IMAGE_MACHINE_CROSS_TYPE_SUPPORTED(Machine) (FALSE) + #elif defined (MDE_CPU_EBC) /// diff --git a/roms/ipxe/src/include/ipxe/efi/Uefi/UefiInternalFormRepresentation.h b/roms/ipxe/src/include/ipxe/efi/Uefi/UefiInternalFormRepresentation.h index e792473..19121de 100644 --- a/roms/ipxe/src/include/ipxe/efi/Uefi/UefiInternalFormRepresentation.h +++ b/roms/ipxe/src/include/ipxe/efi/Uefi/UefiInternalFormRepresentation.h @@ -3,7 +3,7 @@ IFR is primarily consumed by the EFI presentation engine, and produced by EFI internal application and drivers as well as all add-in card option-ROM drivers -Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.
    +Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.
    This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License that accompanies this distribution. The full text of the license may be found at @@ -781,6 +781,7 @@ typedef union { #define EFI_IFR_SECURITY_OP 0x60 #define EFI_IFR_MODAL_TAG_OP 0x61 #define EFI_IFR_REFRESH_ID_OP 0x62 +#define EFI_IFR_WARNING_IF_OP 0x63 // // Definitions of IFR Standard Headers @@ -1128,6 +1129,12 @@ typedef struct _EFI_IFR_NO_SUBMIT_IF { EFI_STRING_ID Error; } EFI_IFR_NO_SUBMIT_IF; +typedef struct _EFI_IFR_WARNING_IF { + EFI_IFR_OP_HEADER Header; + EFI_STRING_ID Warning; + UINT8 TimeOut; +} EFI_IFR_WARNING_IF; + typedef struct _EFI_IFR_REFRESH { EFI_IFR_OP_HEADER Header; UINT8 RefreshInterval; diff --git a/roms/ipxe/src/include/ipxe/efi/Uefi/UefiPxe.h b/roms/ipxe/src/include/ipxe/efi/Uefi/UefiPxe.h index 0b8da5f..5c0b203 100644 --- a/roms/ipxe/src/include/ipxe/efi/Uefi/UefiPxe.h +++ b/roms/ipxe/src/include/ipxe/efi/Uefi/UefiPxe.h @@ -3,7 +3,7 @@ structure prototypes, global variables and constants that are needed for porting PXE to EFI. -Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
    +Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.
    This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License that accompanies this distribution. The full text of the license may be found at @@ -721,10 +721,11 @@ typedef struct s_pxe_hw_undi { PXE_UINT8 Len; ///< sizeof(PXE_HW_UNDI). PXE_UINT8 Fudge; ///< makes 8-bit cksum equal zero. PXE_UINT8 Rev; ///< PXE_ROMID_REV. - PXE_UINT8 IFcnt; ///< physical connector count. + PXE_UINT8 IFcnt; ///< physical connector count lower byte. PXE_UINT8 MajorVer; ///< PXE_ROMID_MAJORVER. PXE_UINT8 MinorVer; ///< PXE_ROMID_MINORVER. - PXE_UINT16 reserved; ///< zero, not used. + PXE_UINT8 IFcntExt; ///< physical connector count upper byte. + PXE_UINT8 reserved; ///< zero, not used. PXE_UINT32 Implementation; ///< implementation flags. ///< reserved ///< vendor use. ///< UINT32 Status; ///< status port. @@ -817,10 +818,11 @@ typedef struct s_pxe_sw_undi { PXE_UINT8 Len; ///< sizeof(PXE_SW_UNDI). PXE_UINT8 Fudge; ///< makes 8-bit cksum zero. PXE_UINT8 Rev; ///< PXE_ROMID_REV. - PXE_UINT8 IFcnt; ///< physical connector count. + PXE_UINT8 IFcnt; ///< physical connector count lower byte. PXE_UINT8 MajorVer; ///< PXE_ROMID_MAJORVER. PXE_UINT8 MinorVer; ///< PXE_ROMID_MINORVER. - PXE_UINT16 reserved1; ///< zero, not used. + PXE_UINT8 IFcntExt; ///< physical connector count upper byte. + PXE_UINT8 reserved1; ///< zero, not used. PXE_UINT32 Implementation; ///< Implementation flags. PXE_UINT64 EntryPoint; ///< API entry point. PXE_UINT8 reserved2[3]; ///< zero, not used. diff --git a/roms/ipxe/src/include/ipxe/efi/Uefi/UefiSpec.h b/roms/ipxe/src/include/ipxe/efi/Uefi/UefiSpec.h index 141bccd..422b2f3 100644 --- a/roms/ipxe/src/include/ipxe/efi/Uefi/UefiSpec.h +++ b/roms/ipxe/src/include/ipxe/efi/Uefi/UefiSpec.h @@ -1,11 +1,11 @@ /** @file Include file that supports UEFI. - This include file must contain things defined in the UEFI 2.3 specification. - If a code construct is defined in the UEFI 2.3 specification it must be included + This include file must contain things defined in the UEFI 2.4 specification. + If a code construct is defined in the UEFI 2.4 specification it must be included by this include file. -Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.
    +Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.
    This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License that accompanies this distribution. The full text of the license may be found at @@ -459,11 +459,11 @@ typedef enum { /// TimerCancel, /// - /// An event is to be signalled periodically at a specified interval from the current time. + /// An event is to be signaled periodically at a specified interval from the current time. /// TimerPeriodic, /// - /// An event is to be signalled once at a specified interval from the current time. + /// An event is to be signaled once at a specified interval from the current time. /// TimerRelative } EFI_TIMER_DELAY; @@ -664,13 +664,20 @@ EFI_STATUS then EFI_INVALID_PARAMETER is returned. @param VendorGuid A unique identifier for the vendor. @param Attributes Attributes bitmask to set for the variable. - @param DataSize The size in bytes of the Data buffer. A size of zero causes the - variable to be deleted. + @param DataSize The size in bytes of the Data buffer. Unless the EFI_VARIABLE_APPEND_WRITE, + EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS, or + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute is set, a size of zero + causes the variable to be deleted. When the EFI_VARIABLE_APPEND_WRITE attribute is + set, then a SetVariable() call with a DataSize of zero will not cause any change to + the variable value (the timestamp associated with the variable may be updated however + even if no new data value is provided,see the description of the + EFI_VARIABLE_AUTHENTICATION_2 descriptor below. In this case the DataSize will not + be zero since the EFI_VARIABLE_AUTHENTICATION_2 descriptor will be populated). @param Data The contents for the variable. @retval EFI_SUCCESS The firmware has successfully stored the variable and its data as defined by the Attributes. - @retval EFI_INVALID_PARAMETER An invalid combination of attribute bits was supplied, or the + @retval EFI_INVALID_PARAMETER An invalid combination of attribute bits, name, and GUID was supplied, or the DataSize exceeds the maximum allowed. @retval EFI_INVALID_PARAMETER VariableName is an empty string. @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the variable and its data. @@ -678,8 +685,9 @@ EFI_STATUS @retval EFI_WRITE_PROTECTED The variable in question is read-only. @retval EFI_WRITE_PROTECTED The variable in question cannot be deleted. @retval EFI_SECURITY_VIOLATION The variable could not be written due to EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS - set but the AuthInfo does NOT pass the validation check carried out - by the firmware. + or EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACESS being set, but the AuthInfo + does NOT pass the validation check carried out by the firmware. + @retval EFI_NOT_FOUND The variable trying to be updated or deleted was not found. **/ @@ -989,7 +997,15 @@ typedef enum { /// state. If the system does not support this reset type, then when the system /// is rebooted, it should exhibit the EfiResetCold attributes. /// - EfiResetShutdown + EfiResetShutdown, + /// + /// Used to induce a system-wide reset. The exact type of the reset is defined by + /// the EFI_GUID that follows the Null-terminated Unicode string passed into + /// ResetData. If the platform does not recognize the EFI_GUID in ResetData the + /// platform must pick a supported reset type to perform. The platform may + /// optionally log the parameters from any non-normal reset that occurs. + /// + EfiResetPlatformSpecific } EFI_RESET_TYPE; /** @@ -1150,6 +1166,8 @@ EFI_STATUS @retval EFI_OUT_OF_RESOURCES There was not enough memory in pool to install all the protocols. @retval EFI_ALREADY_STARTED A Device Path Protocol instance was passed in that is already present in the handle database. + @retval EFI_INVALID_PARAMETER Handle is NULL. + @retval EFI_INVALID_PARAMETER Protocol is already installed on the handle specified by Handle. **/ typedef @@ -1655,7 +1673,11 @@ typedef struct { @retval EFI_INVALID_PARAMETER CapsuleCount is 0. @retval EFI_DEVICE_ERROR The capsule update was started, but failed due to a device error. @retval EFI_UNSUPPORTED The capsule type is not supported on this platform. - @retval EFI_OUT_OF_RESOURCES There were insufficient resources to process the capsule. + @retval EFI_OUT_OF_RESOURCES When ExitBootServices() has been previously called this error indicates the capsule + is compatible with this platform but is not capable of being submitted or processed + in runtime. The caller may resubmit the capsule prior to ExitBootServices(). + @retval EFI_OUT_OF_RESOURCES When ExitBootServices() has not been previously called then this error indicates + the capsule is compatible with this platform but there are insufficient resources to process. **/ typedef @@ -1682,7 +1704,11 @@ EFI_STATUS @retval EFI_UNSUPPORTED The capsule type is not supported on this platform, and MaximumCapsuleSize and ResetType are undefined. @retval EFI_INVALID_PARAMETER MaximumCapsuleSize is NULL. - @retval EFI_OUT_OF_RESOURCES There were insufficient resources to process the query request. + @retval EFI_OUT_OF_RESOURCES When ExitBootServices() has been previously called this error indicates the capsule + is compatible with this platform but is not capable of being submitted or processed + in runtime. The caller may resubmit the capsule prior to ExitBootServices(). + @retval EFI_OUT_OF_RESOURCES When ExitBootServices() has not been previously called then this error indicates + the capsule is compatible with this platform but there are insufficient resources to process. **/ typedef @@ -1728,12 +1754,17 @@ EFI_STATUS // // Firmware should stop at a firmware user interface on next boot // -#define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001 +#define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001 +#define EFI_OS_INDICATIONS_TIMESTAMP_REVOCATION 0x0000000000000002 +#define EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED 0x0000000000000004 +#define EFI_OS_INDICATIONS_FMP_CAPSULE_SUPPORTED 0x0000000000000008 +#define EFI_OS_INDICATIONS_CAPSULE_RESULT_VAR_SUPPORTED 0x0000000000000010 // // EFI Runtime Services Table // #define EFI_SYSTEM_TABLE_SIGNATURE SIGNATURE_64 ('I','B','I',' ','S','Y','S','T') +#define EFI_2_40_SYSTEM_TABLE_REVISION ((2 << 16) | (40)) #define EFI_2_31_SYSTEM_TABLE_REVISION ((2 << 16) | (31)) #define EFI_2_30_SYSTEM_TABLE_REVISION ((2 << 16) | (30)) #define EFI_2_20_SYSTEM_TABLE_REVISION ((2 << 16) | (20)) @@ -1741,10 +1772,11 @@ EFI_STATUS #define EFI_2_00_SYSTEM_TABLE_REVISION ((2 << 16) | (00)) #define EFI_1_10_SYSTEM_TABLE_REVISION ((1 << 16) | (10)) #define EFI_1_02_SYSTEM_TABLE_REVISION ((1 << 16) | (02)) -#define EFI_SYSTEM_TABLE_REVISION EFI_2_31_SYSTEM_TABLE_REVISION +#define EFI_SYSTEM_TABLE_REVISION EFI_2_40_SYSTEM_TABLE_REVISION +#define EFI_SPECIFICATION_VERSION EFI_SYSTEM_TABLE_REVISION #define EFI_RUNTIME_SERVICES_SIGNATURE SIGNATURE_64 ('R','U','N','T','S','E','R','V') -#define EFI_RUNTIME_SERVICES_REVISION EFI_2_31_SYSTEM_TABLE_REVISION +#define EFI_RUNTIME_SERVICES_REVISION EFI_SPECIFICATION_VERSION /// /// EFI Runtime Services Table. @@ -1796,7 +1828,7 @@ typedef struct { #define EFI_BOOT_SERVICES_SIGNATURE SIGNATURE_64 ('B','O','O','T','S','E','R','V') -#define EFI_BOOT_SERVICES_REVISION EFI_2_31_SYSTEM_TABLE_REVISION +#define EFI_BOOT_SERVICES_REVISION EFI_SPECIFICATION_VERSION /// /// EFI Boot Services Table. @@ -2013,41 +2045,46 @@ EFI_STATUS /// /// EFI Boot Key Data /// -typedef UINT32 EFI_BOOT_KEY_DATA; -/// -/// Indicates the revision of the EFI_KEY_OPTION structure. This revision level should be 0. -/// -#define EFI_KEY_OPTION_REVISION_MASK 0x000000FF -/// -/// Either the left or right Shift keys must be pressed (1) or must not be pressed (0). -/// -#define EFI_KEY_OPTION_SHIFT_PRESSED_MASK BIT8 -/// -/// Either the left or right Control keys must be pressed (1) or must not be pressed (0). -/// -#define EFI_KEY_OPTION_CONTROL_PRESSED_MASK BIT9 -/// -/// Either the left or right Alt keys must be pressed (1) or must not be pressed (0). -/// -#define EFI_KEY_OPTION_ALT_PRESSED_MASK BIT10 -/// -/// Either the left or right Logo keys must be pressed (1) or must not be pressed (0). -/// -#define EFI_KEY_OPTION_LOGO_PRESSED_MASK BIT11 -/// -/// The Menu key must be pressed (1) or must not be pressed (0). -/// -#define EFI_KEY_OPTION_MENU_PRESSED_MASK BIT12 -/// -/// The SysReq key must be pressed (1) or must not be pressed (0). -/// -#define EFI_KEY_OPTION_SYS_REQ_PRESSED_MASK BIT13 -/// -/// Specifies the actual number of entries in EFI_KEY_OPTION.Keys, from 0-3. If -/// zero, then only the shift state is considered. If more than one, then the boot option will -/// only be launched if all of the specified keys are pressed with the same shift state. -/// -#define EFI_KEY_OPTION_INPUT_KEY_COUNT_MASK (BIT30 | BIT31) +typedef union { + struct { + /// + /// Indicates the revision of the EFI_KEY_OPTION structure. This revision level should be 0. + /// + UINT32 Revision : 8; + /// + /// Either the left or right Shift keys must be pressed (1) or must not be pressed (0). + /// + UINT32 ShiftPressed : 1; + /// + /// Either the left or right Control keys must be pressed (1) or must not be pressed (0). + /// + UINT32 ControlPressed : 1; + /// + /// Either the left or right Alt keys must be pressed (1) or must not be pressed (0). + /// + UINT32 AltPressed : 1; + /// + /// Either the left or right Logo keys must be pressed (1) or must not be pressed (0). + /// + UINT32 LogoPressed : 1; + /// + /// The Menu key must be pressed (1) or must not be pressed (0). + /// + UINT32 MenuPressed : 1; + /// + /// The SysReq key must be pressed (1) or must not be pressed (0). + /// + UINT32 SysReqPressed : 1; + UINT32 Reserved : 16; + /// + /// Specifies the actual number of entries in EFI_KEY_OPTION.Keys, from 0-3. If + /// zero, then only the shift state is considered. If more than one, then the boot option will + /// only be launched if all of the specified keys are pressed with the same shift state. + /// + UINT32 InputKeyCount : 2; + } Options; + UINT32 PackedValue; +} EFI_BOOT_KEY_DATA; /// /// EFI Key Option. @@ -2085,6 +2122,7 @@ typedef struct { #define EFI_REMOVABLE_MEDIA_FILE_NAME_IA64 L"\\EFI\\BOOT\\BOOTIA64.EFI" #define EFI_REMOVABLE_MEDIA_FILE_NAME_X64 L"\\EFI\\BOOT\\BOOTX64.EFI" #define EFI_REMOVABLE_MEDIA_FILE_NAME_ARM L"\\EFI\\BOOT\\BOOTARM.EFI" +#define EFI_REMOVABLE_MEDIA_FILE_NAME_AARCH64 L"\\EFI\\BOOT\\BOOTAA64.EFI" #if defined (MDE_CPU_IA32) #define EFI_REMOVABLE_MEDIA_FILE_NAME EFI_REMOVABLE_MEDIA_FILE_NAME_IA32 @@ -2095,6 +2133,8 @@ typedef struct { #elif defined (MDE_CPU_EBC) #elif defined (MDE_CPU_ARM) #define EFI_REMOVABLE_MEDIA_FILE_NAME EFI_REMOVABLE_MEDIA_FILE_NAME_ARM +#elif defined (MDE_CPU_AARCH64) + #define EFI_REMOVABLE_MEDIA_FILE_NAME EFI_REMOVABLE_MEDIA_FILE_NAME_AARCH64 #else #error Unknown Processor Type #endif diff --git a/roms/ipxe/src/include/ipxe/efi/X64/ProcessorBind.h b/roms/ipxe/src/include/ipxe/efi/X64/ProcessorBind.h index e10e3b5..4f21bb8 100644 --- a/roms/ipxe/src/include/ipxe/efi/X64/ProcessorBind.h +++ b/roms/ipxe/src/include/ipxe/efi/X64/ProcessorBind.h @@ -1,7 +1,7 @@ /** @file Processor or Compiler specific defines and types x64 (Intel 64, AMD64). - Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.
    + Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.
    This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -230,6 +230,12 @@ typedef INT64 INTN; #define MAX_ADDRESS 0xFFFFFFFFFFFFFFFFULL /// +/// Maximum legal x64 INTN and UINTN values. +/// +#define MAX_INTN ((INTN)0x7FFFFFFFFFFFFFFFULL) +#define MAX_UINTN ((UINTN)0xFFFFFFFFFFFFFFFFULL) + +/// /// The stack alignment required for x64 /// #define CPU_STACK_ALIGNMENT 16 @@ -286,5 +292,9 @@ typedef INT64 INTN; **/ #define FUNCTION_ENTRY_POINT(FunctionPointer) (VOID *)(UINTN)(FunctionPointer) +#ifndef __USER_LABEL_PREFIX__ +#define __USER_LABEL_PREFIX__ +#endif + #endif diff --git a/roms/ipxe/src/include/ipxe/efi/efi.h b/roms/ipxe/src/include/ipxe/efi/efi.h index a98b558..ab52dd9 100644 --- a/roms/ipxe/src/include/ipxe/efi/efi.h +++ b/roms/ipxe/src/include/ipxe/efi/efi.h @@ -41,6 +41,16 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define EFIAPI __attribute__((cdecl,regparm(0))) #endif +/* EFI headers define EFI_HANDLE as a void pointer, which renders type + * checking somewhat useless. Work around this bizarre sabotage + * attempt by redefining EFI_HANDLE as a pointer to an anonymous + * structure. + */ +#define EFI_HANDLE STUPID_EFI_HANDLE +#include +#undef EFI_HANDLE +typedef struct {} *EFI_HANDLE; + /* Include the top-level EFI header files */ #include #include @@ -59,6 +69,8 @@ struct efi_protocol { EFI_GUID guid; /** Variable containing pointer to protocol structure */ void **protocol; + /** Protocol is required */ + int required; }; /** EFI protocol table */ @@ -78,6 +90,21 @@ struct efi_protocol { .protocol = ( ( void ** ) ( void * ) \ ( ( (_ptr) == ( ( _protocol ** ) (_ptr) ) ) ? \ (_ptr) : (_ptr) ) ), \ + .required = 1, \ + } + +/** Declare an EFI protocol to be requested by iPXE + * + * @v _protocol EFI protocol name + * @v _ptr Pointer to protocol instance + */ +#define EFI_REQUEST_PROTOCOL( _protocol, _ptr ) \ + struct efi_protocol __ ## _protocol __efi_protocol = { \ + .guid = _protocol ## _GUID, \ + .protocol = ( ( void ** ) ( void * ) \ + ( ( (_ptr) == ( ( _protocol ** ) (_ptr) ) ) ? \ + (_ptr) : (_ptr) ) ), \ + .required = 0, \ } /** An EFI configuration table used by iPXE */ @@ -126,45 +153,94 @@ struct efi_config_table { */ #define EEFI( efirc ) EPLATFORM ( EINFO_EPLATFORM, efirc ) +extern EFI_GUID efi_arp_protocol_guid; +extern EFI_GUID efi_arp_service_binding_protocol_guid; +extern EFI_GUID efi_block_io_protocol_guid; +extern EFI_GUID efi_bus_specific_driver_override_protocol_guid; +extern EFI_GUID efi_component_name_protocol_guid; +extern EFI_GUID efi_component_name2_protocol_guid; +extern EFI_GUID efi_device_path_protocol_guid; +extern EFI_GUID efi_dhcp4_protocol_guid; +extern EFI_GUID efi_dhcp4_service_binding_protocol_guid; +extern EFI_GUID efi_disk_io_protocol_guid; +extern EFI_GUID efi_driver_binding_protocol_guid; +extern EFI_GUID efi_graphics_output_protocol_guid; +extern EFI_GUID efi_hii_config_access_protocol_guid; +extern EFI_GUID efi_ip4_protocol_guid; +extern EFI_GUID efi_ip4_config_protocol_guid; +extern EFI_GUID efi_ip4_service_binding_protocol_guid; +extern EFI_GUID efi_load_file_protocol_guid; +extern EFI_GUID efi_load_file2_protocol_guid; +extern EFI_GUID efi_loaded_image_protocol_guid; +extern EFI_GUID efi_loaded_image_device_path_protocol_guid; +extern EFI_GUID efi_managed_network_protocol_guid; +extern EFI_GUID efi_managed_network_service_binding_protocol_guid; +extern EFI_GUID efi_mtftp4_protocol_guid; +extern EFI_GUID efi_mtftp4_service_binding_protocol_guid; +extern EFI_GUID efi_nii_protocol_guid; +extern EFI_GUID efi_nii31_protocol_guid; +extern EFI_GUID efi_pci_io_protocol_guid; +extern EFI_GUID efi_pci_root_bridge_io_protocol_guid; +extern EFI_GUID efi_pxe_base_code_protocol_guid; +extern EFI_GUID efi_simple_file_system_protocol_guid; +extern EFI_GUID efi_simple_network_protocol_guid; +extern EFI_GUID efi_tcg_protocol_guid; +extern EFI_GUID efi_tcp4_protocol_guid; +extern EFI_GUID efi_tcp4_service_binding_protocol_guid; +extern EFI_GUID efi_udp4_protocol_guid; +extern EFI_GUID efi_udp4_service_binding_protocol_guid; +extern EFI_GUID efi_vlan_config_protocol_guid; + extern EFI_HANDLE efi_image_handle; extern EFI_LOADED_IMAGE_PROTOCOL *efi_loaded_image; extern EFI_DEVICE_PATH_PROTOCOL *efi_loaded_image_path; extern EFI_SYSTEM_TABLE *efi_systab; extern const char * efi_guid_ntoa ( EFI_GUID *guid ); +extern const char * efi_devpath_text ( EFI_DEVICE_PATH_PROTOCOL *path ); +extern const char * efi_handle_name ( EFI_HANDLE handle ); +extern void dbg_efi_openers ( EFI_HANDLE handle, EFI_GUID *protocol ); extern void dbg_efi_protocols ( EFI_HANDLE handle ); -extern void dbg_efi_devpath ( EFI_DEVICE_PATH_PROTOCOL *path ); -#define DBG_EFI_PROTOCOLS_IF( level, handle ) do { \ +#define DBG_EFI_OPENERS_IF( level, handle, protocol ) do { \ if ( DBG_ ## level ) { \ - dbg_efi_protocols ( handle ); \ + dbg_efi_openers ( handle, protocol ); \ } \ } while ( 0 ) -#define DBG_EFI_DEVPATH_IF( level, path ) do { \ +#define DBG_EFI_PROTOCOLS_IF( level, handle ) do { \ if ( DBG_ ## level ) { \ - dbg_efi_devpath ( path ); \ + dbg_efi_protocols ( handle ); \ } \ } while ( 0 ) -#define DBGC_EFI_PROTOCOLS_IF( level, id, ... ) do { \ +#define DBGC_EFI_OPENERS_IF( level, id, ... ) do { \ DBG_AC_IF ( level, id ); \ - DBG_EFI_PROTOCOLS_IF ( level, __VA_ARGS__ ); \ + DBG_EFI_OPENERS_IF ( level, __VA_ARGS__ ); \ DBG_DC_IF ( level ); \ } while ( 0 ) -#define DBGC_EFI_DEVPATH_IF( level, id, ... ) do { \ +#define DBGC_EFI_PROTOCOLS_IF( level, id, ... ) do { \ DBG_AC_IF ( level, id ); \ - DBG_EFI_DEVPATH_IF ( level, __VA_ARGS__ ); \ + DBG_EFI_PROTOCOLS_IF ( level, __VA_ARGS__ ); \ DBG_DC_IF ( level ); \ } while ( 0 ) +#define DBGC_EFI_OPENERS( ... ) \ + DBGC_EFI_OPENERS_IF ( LOG, ##__VA_ARGS__ ) #define DBGC_EFI_PROTOCOLS( ... ) \ - DBGC_EFI_PROTOCOLS_IF( LOG, ##__VA_ARGS__ ) + DBGC_EFI_PROTOCOLS_IF ( LOG, ##__VA_ARGS__ ) + +#define DBGC2_EFI_OPENERS( ... ) \ + DBGC_EFI_OPENERS_IF ( EXTRA, ##__VA_ARGS__ ) +#define DBGC2_EFI_PROTOCOLS( ... ) \ + DBGC_EFI_PROTOCOLS_IF ( EXTRA, ##__VA_ARGS__ ) -#define DBGC_EFI_DEVPATH( ... ) \ - DBGC_EFI_DEVPATH_IF( LOG, ##__VA_ARGS__ ) +#define DBGCP_EFI_OPENERS( ... ) \ + DBGC_EFI_OPENERS_IF ( PROFILE, ##__VA_ARGS__ ) +#define DBGCP_EFI_PROTOCOLS( ... ) \ + DBGC_EFI_PROTOCOLS_IF ( PROFILE, ##__VA_ARGS__ ) extern EFI_STATUS efi_init ( EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *systab ); diff --git a/roms/ipxe/src/include/ipxe/efi/efi_autoboot.h b/roms/ipxe/src/include/ipxe/efi/efi_autoboot.h new file mode 100644 index 0000000..d4a2685 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/efi/efi_autoboot.h @@ -0,0 +1,14 @@ +#ifndef _IPXE_EFI_AUTOBOOT_H +#define _IPXE_EFI_AUTOBOOT_H + +/** @file + * + * EFI autoboot device + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +extern void efi_set_autoboot ( void ); + +#endif /* _IPXE_EFI_AUTOBOOT_H */ diff --git a/roms/ipxe/src/include/ipxe/efi/efi_download.h b/roms/ipxe/src/include/ipxe/efi/efi_download.h index 3ce4972..740fcad 100644 --- a/roms/ipxe/src/include/ipxe/efi/efi_download.h +++ b/roms/ipxe/src/include/ipxe/efi/efi_download.h @@ -151,7 +151,7 @@ struct _IPXE_DOWNLOAD_PROTOCOL { 0x3eaeaebd, 0xdecf, 0x493b, { 0x9b, 0xd1, 0xcd, 0xb2, 0xde, 0xca, 0xe7, 0x19 } \ } -extern int efi_download_install ( EFI_HANDLE *device ); -extern void efi_download_uninstall ( EFI_HANDLE device ); +extern int efi_download_install ( EFI_HANDLE handle ); +extern void efi_download_uninstall ( EFI_HANDLE handle ); #endif /* _IPXE_DOWNLOAD_H */ diff --git a/roms/ipxe/src/include/ipxe/efi/efi_driver.h b/roms/ipxe/src/include/ipxe/efi/efi_driver.h index afa574d..e16a24d 100644 --- a/roms/ipxe/src/include/ipxe/efi/efi_driver.h +++ b/roms/ipxe/src/include/ipxe/efi/efi_driver.h @@ -8,43 +8,85 @@ FILE_LICENCE ( GPL2_OR_LATER ); +#include +#include #include -#include -#include #include +/** An EFI device */ +struct efi_device { + /** Generic device */ + struct device dev; + /** EFI device handle */ + EFI_HANDLE device; + /** Driver for this device */ + struct efi_driver *driver; + /** Driver-private data */ + void *priv; +}; + /** An EFI driver */ struct efi_driver { /** Name */ const char *name; - /** EFI name */ - CHAR16 wname[32]; - /** EFI driver binding protocol */ - EFI_DRIVER_BINDING_PROTOCOL driver; - /** EFI component name protocol */ - EFI_COMPONENT_NAME2_PROTOCOL wtf; + /** + * Check if driver supports device + * + * @v device EFI device handle + * @ret rc Return status code + */ + int ( * supported ) ( EFI_HANDLE device ); + /** + * Attach driver to device + * + * @v efidev EFI device + * @ret rc Return status code + */ + int ( * start ) ( struct efi_device *efidev ); + /** + * Detach driver from device + * + * @v efidev EFI device + */ + void ( * stop ) ( struct efi_device *efidev ); }; -/** Initialise an EFI driver +/** EFI driver table */ +#define EFI_DRIVERS __table ( struct efi_driver, "efi_drivers" ) + +/** Declare an EFI driver */ +#define __efi_driver( order ) __table_entry ( EFI_DRIVERS, order ) + +#define EFI_DRIVER_EARLY 01 /**< Early drivers */ +#define EFI_DRIVER_NORMAL 02 /**< Normal drivers */ +#define EFI_DRIVER_LATE 03 /**< Late drivers */ + +/** + * Set EFI driver-private data * - * @v name Driver name - * @v supported Device supported method - * @v start Device start method - * @v stop Device stop method + * @v efidev EFI device + * @v priv Private data */ -#define EFI_DRIVER_INIT( _name, _supported, _start, _stop ) { \ - .name = _name, \ - .driver = { \ - .Supported = _supported, \ - .Start = _start, \ - .Stop = _stop, \ - .Version = 0x10, \ - } } - -extern EFI_DEVICE_PATH_PROTOCOL * -efi_devpath_end ( EFI_DEVICE_PATH_PROTOCOL *path ); - -extern int efi_driver_install ( struct efi_driver *efidrv ); -extern void efi_driver_uninstall ( struct efi_driver *efidrv ); +static inline void efidev_set_drvdata ( struct efi_device *efidev, + void *priv ) { + efidev->priv = priv; +} + +/** + * Get EFI driver-private data + * + * @v efidev EFI device + * @ret priv Private data + */ +static inline void * efidev_get_drvdata ( struct efi_device *efidev ) { + return efidev->priv; +} + +extern struct efi_device * efidev_parent ( struct device *dev ); +extern int efi_driver_install ( void ); +extern void efi_driver_uninstall ( void ); +extern int efi_driver_connect_all ( void ); +extern void efi_driver_disconnect_all ( void ); +extern void efi_driver_reconnect_all ( void ); #endif /* _IPXE_EFI_DRIVER_H */ diff --git a/roms/ipxe/src/include/ipxe/efi/efi_file.h b/roms/ipxe/src/include/ipxe/efi/efi_file.h index 0d75cf5..e4db030 100644 --- a/roms/ipxe/src/include/ipxe/efi/efi_file.h +++ b/roms/ipxe/src/include/ipxe/efi/efi_file.h @@ -7,7 +7,7 @@ * */ -extern int efi_file_install ( EFI_HANDLE *handle ); +extern int efi_file_install ( EFI_HANDLE handle ); extern void efi_file_uninstall ( EFI_HANDLE handle ); #endif /* _IPXE_EFI_FILE_H */ diff --git a/roms/ipxe/src/include/ipxe/efi/efi_pci.h b/roms/ipxe/src/include/ipxe/efi/efi_pci.h index e6b3197..af36613 100644 --- a/roms/ipxe/src/include/ipxe/efi/efi_pci.h +++ b/roms/ipxe/src/include/ipxe/efi/efi_pci.h @@ -8,44 +8,18 @@ FILE_LICENCE ( GPL2_OR_LATER ); +#include #include #include -#include /* PciRootBridgeIo.h uses LShiftU64(), which isn't defined anywhere else */ static inline EFIAPI uint64_t LShiftU64 ( UINT64 value, UINTN shift ) { return ( value << shift ); } -struct efi_driver; -struct device; - -/** An EFI PCI device */ -struct efi_pci_device { - /** List of EFI PCI devices */ - struct list_head list; - /** iPXE PCI device */ - struct pci_device pci; - /** Underlying EFI device */ - EFI_HANDLE device; - /** PCI I/O protocol */ - EFI_PCI_IO_PROTOCOL *pci_io; - /** Device path */ - EFI_DEVICE_PATH_PROTOCOL *path; - /** EFI driver */ - struct efi_driver *efidrv; -}; - -extern struct efi_pci_device * efipci_create ( struct efi_driver *efidrv, - EFI_HANDLE device ); -extern int efipci_enable ( struct efi_pci_device *efipci ); -extern struct efi_pci_device * efipci_find_efi ( EFI_HANDLE device ); -extern struct efi_pci_device * efipci_find ( struct device *dev ); -extern int efipci_child_add ( struct efi_pci_device *efipci, - EFI_HANDLE device ); -extern void efipci_child_del ( struct efi_pci_device *efipci, - EFI_HANDLE device ); -extern void efipci_destroy ( struct efi_driver *efidrv, - struct efi_pci_device *efipci ); +extern int efipci_open ( EFI_HANDLE device, UINT32 attributes, + struct pci_device *pci ); +extern void efipci_close ( EFI_HANDLE device ); +extern int efipci_info ( EFI_HANDLE device, struct pci_device *pci ); #endif /* _IPXE_EFI_PCI_H */ diff --git a/roms/ipxe/src/include/ipxe/efi/efi_pci_api.h b/roms/ipxe/src/include/ipxe/efi/efi_pci_api.h index 1bc43e3..498a038 100644 --- a/roms/ipxe/src/include/ipxe/efi/efi_pci_api.h +++ b/roms/ipxe/src/include/ipxe/efi/efi_pci_api.h @@ -55,6 +55,7 @@ static inline __always_inline int PCIAPI_INLINE ( efi, pci_read_config_byte ) ( struct pci_device *pci, unsigned int where, uint8_t *value ) { + *value = 0xff; return efipci_read ( pci, EFIPCI_LOCATION ( where, EFIPCI_WIDTH_BYTE ), value ); @@ -72,6 +73,7 @@ static inline __always_inline int PCIAPI_INLINE ( efi, pci_read_config_word ) ( struct pci_device *pci, unsigned int where, uint16_t *value ) { + *value = 0xffff; return efipci_read ( pci, EFIPCI_LOCATION ( where, EFIPCI_WIDTH_WORD ), value ); @@ -89,6 +91,7 @@ static inline __always_inline int PCIAPI_INLINE ( efi, pci_read_config_dword ) ( struct pci_device *pci, unsigned int where, uint32_t *value ) { + *value = 0xffffffffUL; return efipci_read ( pci, EFIPCI_LOCATION ( where, EFIPCI_WIDTH_DWORD ), value ); diff --git a/roms/ipxe/src/include/ipxe/efi/efi_snp.h b/roms/ipxe/src/include/ipxe/efi/efi_snp.h index e1b866e..a18bced 100644 --- a/roms/ipxe/src/include/ipxe/efi/efi_snp.h +++ b/roms/ipxe/src/include/ipxe/efi/efi_snp.h @@ -24,8 +24,8 @@ struct efi_snp_device { struct list_head list; /** The underlying iPXE network device */ struct net_device *netdev; - /** The underlying EFI PCI device */ - struct efi_pci_device *efipci; + /** The underlying EFI device */ + struct efi_device *efidev; /** EFI device handle */ EFI_HANDLE handle; /** The SNP structure itself */ @@ -65,19 +65,31 @@ struct efi_snp_device { /** Driver name */ wchar_t driver_name[16]; /** Controller name */ - wchar_t controller_name[32]; - /** The device path - * - * This field is variable in size and must appear at the end - * of the structure. - */ - EFI_DEVICE_PATH_PROTOCOL path; + wchar_t controller_name[64]; + /** The device path */ + EFI_DEVICE_PATH_PROTOCOL *path; }; extern int efi_snp_hii_install ( struct efi_snp_device *snpdev ); extern void efi_snp_hii_uninstall ( struct efi_snp_device *snpdev ); +extern struct efi_snp_device * find_snpdev ( EFI_HANDLE handle ); extern struct efi_snp_device * last_opened_snpdev ( void ); -extern void efi_snp_claim ( void ); -extern void efi_snp_release ( void ); +extern void efi_snp_set_claimed ( int claimed ); + +/** + * Claim network devices for use by iPXE + * + */ +static inline void efi_snp_claim ( void ) { + efi_snp_set_claimed ( 1 ); +} + +/** + * Release network devices for use via SNP + * + */ +static inline void efi_snp_release ( void ) { + efi_snp_set_claimed ( 0 ); +} #endif /* _IPXE_EFI_SNP_H */ diff --git a/roms/ipxe/src/include/ipxe/efi/efi_utils.h b/roms/ipxe/src/include/ipxe/efi/efi_utils.h new file mode 100644 index 0000000..9164be1 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/efi/efi_utils.h @@ -0,0 +1,25 @@ +#ifndef _IPXE_EFI_UTILS_H +#define _IPXE_EFI_UTILS_H + +/** @file + * + * EFI utilities + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include + +struct device; + +extern EFI_DEVICE_PATH_PROTOCOL * +efi_devpath_end ( EFI_DEVICE_PATH_PROTOCOL *path ); +extern int efi_locate_device ( EFI_HANDLE device, EFI_GUID *protocol, + EFI_HANDLE *parent ); +extern int efi_child_add ( EFI_HANDLE parent, EFI_HANDLE child ); +extern void efi_child_del ( EFI_HANDLE parent, EFI_HANDLE child ); +extern void efi_device_info ( EFI_HANDLE device, const char *prefix, + struct device *dev ); + +#endif /* _IPXE_EFI_UTILS_H */ diff --git a/roms/ipxe/src/include/ipxe/efi/efi_wrap.h b/roms/ipxe/src/include/ipxe/efi/efi_wrap.h new file mode 100644 index 0000000..7579e0f --- /dev/null +++ b/roms/ipxe/src/include/ipxe/efi/efi_wrap.h @@ -0,0 +1,15 @@ +#ifndef _IPXE_EFI_WRAP_H +#define _IPXE_EFI_WRAP_H + +/** @file + * + * EFI driver interface + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include + +extern void efi_wrap ( EFI_HANDLE handle ); + +#endif /* _IPXE_EFI_WRAP_H */ diff --git a/roms/ipxe/src/include/ipxe/efi/import.pl b/roms/ipxe/src/include/ipxe/efi/import.pl index 9955146..f5a3f54 100755 --- a/roms/ipxe/src/include/ipxe/efi/import.pl +++ b/roms/ipxe/src/include/ipxe/efi/import.pl @@ -78,9 +78,8 @@ sub try_import_file { # Write out line print $outfh "$_\n"; # Apply FILE_LICENCE() immediately after include guard - if ( defined $maybe_guard ) { + if ( defined $maybe_guard && ! defined $guard ) { if ( /^\#define\s+_?_${maybe_guard}_?_$/ ) { - die "Duplicate header guard detected in $infile\n" if $guard; $guard = $maybe_guard; print $outfh "\nFILE_LICENCE ( $licence );\n" if $licence; } @@ -120,7 +119,7 @@ my $edktop = shift; # Identify edk import directories my $edkdirs = [ "MdePkg/Include", "IntelFrameworkPkg/Include", - "MdeModulePkg/Include" ]; + "MdeModulePkg/Include", "EdkCompatibilityPkg/Foundation" ]; foreach my $edkdir ( @$edkdirs ) { die "Directory \"$edktop\" does not appear to contain the EFI EDK2 " ."(missing \"$edkdir\")\n" unless -d catdir ( $edktop, $edkdir ); diff --git a/roms/ipxe/src/include/ipxe/errfile.h b/roms/ipxe/src/include/ipxe/errfile.h index bd12c7c..f809337 100644 --- a/roms/ipxe/src/include/ipxe/errfile.h +++ b/roms/ipxe/src/include/ipxe/errfile.h @@ -154,6 +154,9 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_intel ( ERRFILE_DRIVER | 0x00650000 ) #define ERRFILE_myson ( ERRFILE_DRIVER | 0x00660000 ) #define ERRFILE_intelx ( ERRFILE_DRIVER | 0x00670000 ) +#define ERRFILE_snp ( ERRFILE_DRIVER | 0x00680000 ) +#define ERRFILE_netfront ( ERRFILE_DRIVER | 0x00690000 ) +#define ERRFILE_nii ( ERRFILE_DRIVER | 0x006a0000 ) #define ERRFILE_scsi ( ERRFILE_DRIVER | 0x00700000 ) #define ERRFILE_arbel ( ERRFILE_DRIVER | 0x00710000 ) @@ -223,6 +226,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_icmp ( ERRFILE_NET | 0x00390000 ) #define ERRFILE_ping ( ERRFILE_NET | 0x003a0000 ) #define ERRFILE_dhcpv6 ( ERRFILE_NET | 0x003b0000 ) +#define ERRFILE_nfs_uri ( ERRFILE_NET | 0x003c0000 ) #define ERRFILE_image ( ERRFILE_IMAGE | 0x00000000 ) #define ERRFILE_elf ( ERRFILE_IMAGE | 0x00010000 ) @@ -299,6 +303,11 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_memmap_settings ( ERRFILE_OTHER | 0x003f0000 ) #define ERRFILE_param_cmd ( ERRFILE_OTHER | 0x00400000 ) #define ERRFILE_deflate ( ERRFILE_OTHER | 0x00410000 ) +#define ERRFILE_xenstore ( ERRFILE_OTHER | 0x00420000 ) +#define ERRFILE_xenbus ( ERRFILE_OTHER | 0x00430000 ) +#define ERRFILE_xengrant ( ERRFILE_OTHER | 0x00440000 ) +#define ERRFILE_efi_utils ( ERRFILE_OTHER | 0x00450000 ) +#define ERRFILE_efi_wrap ( ERRFILE_OTHER | 0x00460000 ) /** @} */ diff --git a/roms/ipxe/src/include/ipxe/ethernet.h b/roms/ipxe/src/include/ipxe/ethernet.h index 5ffc45b..d1263d7 100644 --- a/roms/ipxe/src/include/ipxe/ethernet.h +++ b/roms/ipxe/src/include/ipxe/ethernet.h @@ -80,6 +80,8 @@ static inline int is_valid_ether_addr ( const void *addr ) { } extern uint8_t eth_broadcast[]; +extern struct ll_protocol ethernet_protocol __ll_protocol; + extern int eth_push ( struct net_device *netdev, struct io_buffer *iobuf, const void *ll_dest, const void *ll_source, uint16_t net_proto ); @@ -87,6 +89,7 @@ extern int eth_pull ( struct net_device *netdev, struct io_buffer *iobuf, const void **ll_dest, const void **ll_source, uint16_t *net_proto, unsigned int *flags ); extern void eth_init_addr ( const void *hw_addr, void *ll_addr ); +extern void eth_random_addr ( void *hw_addr ); extern const char * eth_ntoa ( const void *ll_addr ); extern int eth_mc_hash ( unsigned int af, const void *net_addr, void *ll_addr ); diff --git a/roms/ipxe/src/include/ipxe/ibft.h b/roms/ipxe/src/include/ipxe/ibft.h index 7337212..35f1510 100644 --- a/roms/ipxe/src/include/ipxe/ibft.h +++ b/roms/ipxe/src/include/ipxe/ibft.h @@ -197,6 +197,14 @@ struct ibft_nic { /** NIC global / link local */ #define IBFT_FL_NIC_GLOBAL 0x04 +/** NIC IP address origin */ +#define IBFT_NIC_ORIGIN_OTHER 0x00 +#define IBFT_NIC_ORIGIN_MANUAL 0x01 +#define IBFT_NIC_ORIGIN_WELLKNOWN 0x02 +#define IBFT_NIC_ORIGIN_DHCP 0x03 +#define IBFT_NIC_ORIGIN_RA 0x04 +#define IBFT_NIC_ORIGIN_UNCHANGED 0x0f + /** * iBFT Target structure * diff --git a/roms/ipxe/src/include/ipxe/in.h b/roms/ipxe/src/include/ipxe/in.h index 3603247..de96ca2 100644 --- a/roms/ipxe/src/include/ipxe/in.h +++ b/roms/ipxe/src/include/ipxe/in.h @@ -61,7 +61,7 @@ struct in6_addr { #define IN6_IS_ADDR_LINKLOCAL( addr ) \ ( ( *( ( const uint16_t * ) (addr) ) & htons ( 0xffc0 ) ) == \ - htonl ( 0xfe80 ) ) + htons ( 0xfe80 ) ) /** * IPv4 socket address diff --git a/roms/ipxe/src/include/ipxe/io.h b/roms/ipxe/src/include/ipxe/io.h index b8b8aa3..29ccfd1 100644 --- a/roms/ipxe/src/include/ipxe/io.h +++ b/roms/ipxe/src/include/ipxe/io.h @@ -23,6 +23,12 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include +/** Page size */ +#define PAGE_SIZE ( 1 << PAGE_SHIFT ) + +/** Page mask */ +#define PAGE_MASK ( PAGE_SIZE - 1 ) + /** * Calculate static inline I/O API function name * diff --git a/roms/ipxe/src/include/ipxe/list.h b/roms/ipxe/src/include/ipxe/list.h index 0d09730..581ec98 100644 --- a/roms/ipxe/src/include/ipxe/list.h +++ b/roms/ipxe/src/include/ipxe/list.h @@ -69,6 +69,8 @@ struct list_head { #define list_add( new, head ) do { \ list_check ( (head) ); \ extern_list_add ( (new), (head) ); \ + list_check ( (head) ); \ + list_check ( (new) ); \ } while ( 0 ) static inline void inline_list_add ( struct list_head *new, struct list_head *head ) { @@ -91,6 +93,8 @@ extern void extern_list_add ( struct list_head *new, #define list_add_tail( new, head ) do { \ list_check ( (head) ); \ extern_list_add_tail ( (new), (head) ); \ + list_check ( (head) ); \ + list_check ( (new) ); \ } while ( 0 ) static inline void inline_list_add_tail ( struct list_head *new, struct list_head *head ) { diff --git a/roms/ipxe/src/include/ipxe/netdevice.h b/roms/ipxe/src/include/ipxe/netdevice.h index 6ef9cb1..95ad1cf 100644 --- a/roms/ipxe/src/include/ipxe/netdevice.h +++ b/roms/ipxe/src/include/ipxe/netdevice.h @@ -685,6 +685,8 @@ extern struct net_device * find_netdev ( const char *name ); extern struct net_device * find_netdev_by_index ( unsigned int index ); extern struct net_device * find_netdev_by_location ( unsigned int bus_type, unsigned int location ); +extern struct net_device * +find_netdev_by_ll_addr ( struct ll_protocol *ll_protocol, const void *ll_addr ); extern struct net_device * last_opened_netdev ( void ); extern int net_tx ( struct io_buffer *iobuf, struct net_device *netdev, struct net_protocol *net_protocol, const void *ll_dest, diff --git a/roms/ipxe/src/include/ipxe/nfs_uri.h b/roms/ipxe/src/include/ipxe/nfs_uri.h new file mode 100644 index 0000000..d88bd6f --- /dev/null +++ b/roms/ipxe/src/include/ipxe/nfs_uri.h @@ -0,0 +1,29 @@ +#ifndef _IPXE_NFS_URI_H +#define _IPXE_NFS_URI_H + +/** @file + * + * Network File System protocol URI handling functions + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include + +struct nfs_uri { + char *mountpoint; + char *filename; + char *path; + char *lookup_pos; +}; + +int nfs_uri_init ( struct nfs_uri *nfs_uri, const struct uri *uri ); +int nfs_uri_next_mountpoint ( struct nfs_uri *uri ); +int nfs_uri_symlink ( struct nfs_uri *uri, const char *symlink_value ); +char *nfs_uri_mountpoint ( const struct nfs_uri *uri ); +char *nfs_uri_next_path_component ( struct nfs_uri *uri ); +void nfs_uri_free ( struct nfs_uri *uri ); + + +#endif /* _IPXE_NFS_URI_H */ diff --git a/roms/ipxe/src/include/ipxe/pinger.h b/roms/ipxe/src/include/ipxe/pinger.h index 2d84033..9932df6 100644 --- a/roms/ipxe/src/include/ipxe/pinger.h +++ b/roms/ipxe/src/include/ipxe/pinger.h @@ -15,6 +15,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); extern int create_pinger ( struct interface *job, const char *hostname, unsigned long timeout, size_t len, + unsigned int count, void ( * callback ) ( struct sockaddr *peer, unsigned int sequence, size_t len, diff --git a/roms/ipxe/src/include/ipxe/profile.h b/roms/ipxe/src/include/ipxe/profile.h index d4fc4f9..3a745fc 100644 --- a/roms/ipxe/src/include/ipxe/profile.h +++ b/roms/ipxe/src/include/ipxe/profile.h @@ -58,12 +58,66 @@ struct profiler { #define __profiler #endif +extern unsigned long profile_excluded; + extern void profile_update ( struct profiler *profiler, unsigned long sample ); extern unsigned long profile_mean ( struct profiler *profiler ); extern unsigned long profile_variance ( struct profiler *profiler ); extern unsigned long profile_stddev ( struct profiler *profiler ); /** + * Get start time + * + * @v profiler Profiler + * @ret started Start time + */ +static inline __attribute__ (( always_inline )) unsigned long +profile_started ( struct profiler *profiler ) { + + /* If profiling is active then return start time */ + if ( PROFILING ) { + return ( profiler->started + profile_excluded ); + } else { + return 0; + } +} + +/** + * Get stop time + * + * @v profiler Profiler + * @ret stopped Stop time + */ +static inline __attribute__ (( always_inline )) unsigned long +profile_stopped ( struct profiler *profiler ) { + + /* If profiling is active then return start time */ + if ( PROFILING ) { + return ( profiler->stopped + profile_excluded ); + } else { + return 0; + } +} + +/** + * Get elapsed time + * + * @v profiler Profiler + * @ret elapsed Elapsed time + */ +static inline __attribute__ (( always_inline )) unsigned long +profile_elapsed ( struct profiler *profiler ) { + + /* If profiling is active then return elapsed time */ + if ( PROFILING ) { + return ( profile_stopped ( profiler ) - + profile_started ( profiler ) ); + } else { + return 0; + } +} + +/** * Start profiling * * @v profiler Profiler @@ -74,7 +128,23 @@ profile_start_at ( struct profiler *profiler, unsigned long started ) { /* If profiling is active then record start timestamp */ if ( PROFILING ) - profiler->started = started; + profiler->started = ( started - profile_excluded ); +} + +/** + * Stop profiling + * + * @v profiler Profiler + * @v stopped Stop timestamp + */ +static inline __attribute__ (( always_inline )) void +profile_stop_at ( struct profiler *profiler, unsigned long stopped ) { + + /* If profiling is active then record end timestamp and update stats */ + if ( PROFILING ) { + profiler->stopped = ( stopped - profile_excluded ); + profile_update ( profiler, profile_elapsed ( profiler ) ); + } } /** @@ -91,32 +161,29 @@ profile_start ( struct profiler *profiler ) { } /** - * Record profiling result + * Stop profiling * * @v profiler Profiler - * @v stopped Stop timestamp */ static inline __attribute__ (( always_inline )) void -profile_stop_at ( struct profiler *profiler, unsigned long stopped ) { +profile_stop ( struct profiler *profiler ) { /* If profiling is active then record end timestamp and update stats */ - if ( PROFILING ) { - profiler->stopped = stopped; - profile_update ( profiler, ( stopped - profiler->started ) ); - } + if ( PROFILING ) + profile_stop_at ( profiler, profile_timestamp() ); } /** - * Record profiling result + * Exclude time from other ongoing profiling results * * @v profiler Profiler */ static inline __attribute__ (( always_inline )) void -profile_stop ( struct profiler *profiler ) { +profile_exclude ( struct profiler *profiler ) { - /* If profiling is active then record end timestamp and update stats */ + /* If profiling is active then update accumulated excluded time */ if ( PROFILING ) - profile_stop_at ( profiler, profile_timestamp() ); + profile_excluded += profile_elapsed ( profiler ); } #endif /* _IPXE_PROFILE_H */ diff --git a/roms/ipxe/src/include/ipxe/scsi.h b/roms/ipxe/src/include/ipxe/scsi.h index 6dfb7f1..4428daa 100644 --- a/roms/ipxe/src/include/ipxe/scsi.h +++ b/roms/ipxe/src/include/ipxe/scsi.h @@ -267,8 +267,8 @@ struct scsi_cmd { size_t data_in_len; }; -/** SCSI sense data */ -struct scsi_sns { +/** SCSI fixed-format sense data */ +struct scsi_sns_fixed { /** Response code */ uint8_t code; /** Reserved */ @@ -277,8 +277,44 @@ struct scsi_sns { uint8_t key; /** Information */ uint32_t info; + /** Additional sense length */ + uint8_t len; + /** Command-specific information */ + uint32_t cs_info; + /** Additional sense code and qualifier */ + uint16_t additional; +} __attribute__ (( packed )); + +/** SCSI descriptor-format sense data */ +struct scsi_sns_descriptor { + /** Response code */ + uint8_t code; + /** Sense key */ + uint8_t key; + /** Additional sense code and qualifier */ + uint16_t additional; +} __attribute__ (( packed )); + +/** SCSI sense data */ +union scsi_sns { + /** Response code */ + uint8_t code; + /** Fixed-format sense data */ + struct scsi_sns_fixed fixed; + /** Descriptor-format sense data */ + struct scsi_sns_descriptor desc; }; +/** SCSI sense response code mask */ +#define SCSI_SENSE_CODE_MASK 0x7f + +/** Test if SCSI sense data is in fixed format + * + * @v code Response code + * @ret is_fixed Sense data is in fixed format + */ +#define SCSI_SENSE_FIXED( code ) ( ( (code) & 0x7e ) == 0x70 ) + /** SCSI sense key mask */ #define SCSI_SENSE_KEY_MASK 0x0f @@ -288,11 +324,18 @@ struct scsi_rsp { uint8_t status; /** Data overrun (or negative underrun) */ ssize_t overrun; - /** Autosense data (if any) */ - struct scsi_sns sense; + /** Autosense data (if any) + * + * To minimise code size, this is stored as the first four + * bytes of a descriptor-format sense data block (even if the + * response code indicates fixed-format sense data). + */ + struct scsi_sns_descriptor sense; }; extern int scsi_parse_lun ( const char *lun_string, struct scsi_lun *lun ); +extern void scsi_parse_sense ( const void *data, size_t len, + struct scsi_sns_descriptor *sense ); extern int scsi_command ( struct interface *control, struct interface *data, struct scsi_cmd *command ); diff --git a/roms/ipxe/src/include/ipxe/smbios.h b/roms/ipxe/src/include/ipxe/smbios.h index 1a34ac0..ef5892a 100644 --- a/roms/ipxe/src/include/ipxe/smbios.h +++ b/roms/ipxe/src/include/ipxe/smbios.h @@ -116,6 +116,23 @@ struct smbios_system_information { /** SMBIOS system information structure type */ #define SMBIOS_TYPE_SYSTEM_INFORMATION 1 +/** SMBIOS base board information structure */ +struct smbios_base_board_information { + /** SMBIOS structure header */ + struct smbios_header header; + /** Manufacturer string */ + uint8_t manufacturer; + /** Product string */ + uint8_t product; + /** Version string */ + uint8_t version; + /** Serial number string */ + uint8_t serial; +} __attribute__ (( packed )); + +/** SMBIOS base board information structure type */ +#define SMBIOS_TYPE_BASE_BOARD_INFORMATION 2 + /** SMBIOS enclosure information structure */ struct smbios_enclosure_information { /** SMBIOS structure header */ diff --git a/roms/ipxe/src/include/ipxe/version.h b/roms/ipxe/src/include/ipxe/version.h index aa894d7..ae4275d 100644 --- a/roms/ipxe/src/include/ipxe/version.h +++ b/roms/ipxe/src/include/ipxe/version.h @@ -9,8 +9,19 @@ FILE_LICENCE ( GPL2_OR_LATER ); +#include + +extern unsigned long build_timestamp; +extern unsigned long build_id; extern const int product_major_version; extern const int product_minor_version; -extern const char *product_version; +extern const char product_version[]; +extern const char product_name[]; +extern const char product_short_name[]; +extern const char build_name[]; +extern const wchar_t product_wversion[]; +extern const wchar_t product_wname[]; +extern const wchar_t product_short_wname[]; +extern const wchar_t build_wname[]; #endif /* _IPXE_VERSION_H */ diff --git a/roms/ipxe/src/include/ipxe/virtio-ring.h b/roms/ipxe/src/include/ipxe/virtio-ring.h index 0afe8ba..c687aca 100644 --- a/roms/ipxe/src/include/ipxe/virtio-ring.h +++ b/roms/ipxe/src/include/ipxe/virtio-ring.h @@ -1,8 +1,5 @@ #ifndef _VIRTIO_RING_H_ # define _VIRTIO_RING_H_ -#define PAGE_SHIFT (12) -#define PAGE_SIZE (1< +#include +#include +#include + +/* Memory barrier macros used by ring.h */ +#define xen_mb() mb() +#define xen_rmb() rmb() +#define xen_wmb() wmb() + +struct xen_hypercall; + +/** A Xen grant table */ +struct xen_grant { + /** Grant table entries */ + struct grant_entry_v1 *table; + /** Total grant table length */ + size_t len; + /** Entry size shift (for later version tables) */ + unsigned int shift; + /** Number of grant table entries in use */ + unsigned int used; + /** Most recently used grant reference */ + unsigned int ref; +}; + +/** A XenStore */ +struct xen_store { + /** XenStore domain interface */ + struct xenstore_domain_interface *intf; + /** Event channel */ + evtchn_port_t port; +}; + +/** A Xen hypervisor */ +struct xen_hypervisor { + /** Hypercall table */ + struct xen_hypercall *hypercall; + /** Shared info page */ + struct shared_info *shared; + /** Grant table */ + struct xen_grant grant; + /** XenStore */ + struct xen_store store; +}; + +#include + +/** + * Convert a Xen status code to an iPXE status code + * + * @v xenrc Xen status code (negated) + * @ret rc iPXE status code (before negation) + * + * Xen status codes are defined in the file include/xen/errno.h in the + * Xen repository. They happen to match the Linux error codes, some + * of which can be found in our include/ipxe/errno/linux.h. + */ +#define EXEN( xenrc ) EPLATFORM ( EINFO_EPLATFORM, -(xenrc) ) + +#endif /* _IPXE_XEN_H */ diff --git a/roms/ipxe/src/include/ipxe/xenbus.h b/roms/ipxe/src/include/ipxe/xenbus.h new file mode 100644 index 0000000..ef2b549 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/xenbus.h @@ -0,0 +1,86 @@ +#ifndef _IPXE_XENBUS_H +#define _IPXE_XENBUS_H + +/** @file + * + * Xen device bus + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include + +/** A Xen device */ +struct xen_device { + /** Generic iPXE device */ + struct device dev; + /** Xen hypervisor */ + struct xen_hypervisor *xen; + /** XenStore key */ + char *key; + /** Backend XenStore key */ + char *backend; + /** Backend domain ID */ + unsigned long backend_id; + /** Driver */ + struct xen_driver *driver; + /** Driver-private data */ + void *priv; +}; + +/** A Xen device driver */ +struct xen_driver { + /** Name */ + const char *name; + /** Device type */ + const char *type; + /** Probe device + * + * @v xendev Xen device + * @ret rc Return status code + */ + int ( * probe ) ( struct xen_device *xendev ); + /** Remove device + * + * @v xendev Xen device + */ + void ( * remove ) ( struct xen_device *xendev ); +}; + +/** Xen device driver table */ +#define XEN_DRIVERS __table ( struct xen_driver, "xen_drivers" ) + +/** Declare a Xen device driver */ +#define __xen_driver __table_entry ( XEN_DRIVERS, 01 ) + +/** + * Set Xen device driver-private data + * + * @v xendev Xen device + * @v priv Private data + */ +static inline void xen_set_drvdata ( struct xen_device *xendev, void *priv ) { + xendev->priv = priv; +} + +/** + * Get Xen device driver-private data + * + * @v xendev Xen device + * @ret priv Private data + */ +static inline void * xen_get_drvdata ( struct xen_device *xendev ) { + return xendev->priv; +} + +extern int xenbus_set_state ( struct xen_device *xendev, int state ); +extern int xenbus_backend_state ( struct xen_device *xendev ); +extern int xenbus_backend_wait ( struct xen_device *xendev, int state ); +extern int xenbus_probe ( struct xen_hypervisor *xen, struct device *parent ); +extern void xenbus_remove ( struct xen_hypervisor *xen, struct device *parent ); + +#endif /* _IPXE_XENBUS_H */ diff --git a/roms/ipxe/src/include/ipxe/xenevent.h b/roms/ipxe/src/include/ipxe/xenevent.h new file mode 100644 index 0000000..1dd6a0c --- /dev/null +++ b/roms/ipxe/src/include/ipxe/xenevent.h @@ -0,0 +1,59 @@ +#ifndef _IPXE_XENEVENT_H +#define _IPXE_XENEVENT_H + +/** @file + * + * Xen events + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include + +/** + * Close event channel + * + * @v xen Xen hypervisor + * @v close Event descriptor + * @ret xenrc Xen status code + */ +static inline __attribute__ (( always_inline )) int +xenevent_close ( struct xen_hypervisor *xen, struct evtchn_close *close ) { + + return xen_hypercall_2 ( xen, __HYPERVISOR_event_channel_op, + EVTCHNOP_close, virt_to_phys ( close ) ); +} + +/** + * Send event + * + * @v xen Xen hypervisor + * @v send Event descriptor + * @ret xenrc Xen status code + */ +static inline __attribute__ (( always_inline )) int +xenevent_send ( struct xen_hypervisor *xen, struct evtchn_send *send ) { + + return xen_hypercall_2 ( xen, __HYPERVISOR_event_channel_op, + EVTCHNOP_send, virt_to_phys ( send ) ); +} + +/** + * Allocate an unbound event channel + * + * @v xen Xen hypervisor + * @v alloc_unbound Event descriptor + * @ret xenrc Xen status code + */ +static inline __attribute__ (( always_inline )) int +xenevent_alloc_unbound ( struct xen_hypervisor *xen, + struct evtchn_alloc_unbound *alloc_unbound ) { + + return xen_hypercall_2 ( xen, __HYPERVISOR_event_channel_op, + EVTCHNOP_alloc_unbound, + virt_to_phys ( alloc_unbound ) ); +} + +#endif /* _IPXE_XENEVENT_H */ diff --git a/roms/ipxe/src/include/ipxe/xengrant.h b/roms/ipxe/src/include/ipxe/xengrant.h new file mode 100644 index 0000000..f9b3beb --- /dev/null +++ b/roms/ipxe/src/include/ipxe/xengrant.h @@ -0,0 +1,232 @@ +#ifndef _IPXE_XENGRANT_H +#define _IPXE_XENGRANT_H + +/** @file + * + * Xen grant tables + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include + +/** Induced failure rate (for testing) */ +#define XENGRANT_FAIL_RATE 0 + +/** + * Query grant table size + * + * @v xen Xen hypervisor + * @v size Table size + * @ret xenrc Xen status code + */ +static inline __attribute__ (( always_inline )) int +xengrant_query_size ( struct xen_hypervisor *xen, + struct gnttab_query_size *size ) { + + return xen_hypercall_3 ( xen, __HYPERVISOR_grant_table_op, + GNTTABOP_query_size, + virt_to_phys ( size ), 1 ); +} + +/** + * Set grant table version + * + * @v xen Xen hypervisor + * @v version Version + * @ret xenrc Xen status code + */ +static inline __attribute__ (( always_inline )) int +xengrant_set_version ( struct xen_hypervisor *xen, + struct gnttab_set_version *version ) { + + return xen_hypercall_3 ( xen, __HYPERVISOR_grant_table_op, + GNTTABOP_set_version, + virt_to_phys ( version ), 1 ); +} + +/** + * Get grant table version + * + * @v xen Xen hypervisor + * @v version Version + * @ret xenrc Xen status code + */ +static inline __attribute__ (( always_inline )) int +xengrant_get_version ( struct xen_hypervisor *xen, + struct gnttab_get_version *version ) { + + return xen_hypercall_3 ( xen, __HYPERVISOR_grant_table_op, + GNTTABOP_get_version, + virt_to_phys ( version ), 1 ); +} + +/** + * Get number of grant table entries + * + * @v xen Xen hypervisor + * @ret entries Number of grant table entries + */ +static inline __attribute__ (( always_inline )) unsigned int +xengrant_entries ( struct xen_hypervisor *xen ) { + + return ( ( xen->grant.len / sizeof ( xen->grant.table[0] ) ) + >> xen->grant.shift ); +} + +/** + * Get grant table entry header + * + * @v xen Xen hypervisor + * @v ref Grant reference + * @ret hdr Grant table entry header + */ +static inline __attribute__ (( always_inline )) struct grant_entry_header * +xengrant_header ( struct xen_hypervisor *xen, grant_ref_t ref ) { + struct grant_entry_v1 *v1; + + v1 = &xen->grant.table[ ref << xen->grant.shift ]; + return ( container_of ( &v1->flags, struct grant_entry_header, flags )); +} + +/** + * Get version 1 grant table entry + * + * @v hdr Grant table entry header + * @ret v1 Version 1 grant table entry + */ +static inline __attribute__ (( always_inline )) struct grant_entry_v1 * +xengrant_v1 ( struct grant_entry_header *hdr ) { + + return ( container_of ( &hdr->flags, struct grant_entry_v1, flags ) ); +} + +/** + * Get version 2 grant table entry + * + * @v hdr Grant table entry header + * @ret v2 Version 2 grant table entry + */ +static inline __attribute__ (( always_inline )) union grant_entry_v2 * +xengrant_v2 ( struct grant_entry_header *hdr ) { + + return ( container_of ( &hdr->flags, union grant_entry_v2, hdr.flags )); +} + +/** + * Zero grant table entry + * + * @v xen Xen hypervisor + * @v hdr Grant table entry header + */ +static inline void xengrant_zero ( struct xen_hypervisor *xen, + struct grant_entry_header *hdr ) { + uint32_t *dword = ( ( uint32_t * ) hdr ); + unsigned int i = ( ( sizeof ( xen->grant.table[0] ) / sizeof ( *dword )) + << xen->grant.shift ); + + while ( i-- ) + writel ( 0, dword++ ); +} + +/** + * Invalidate access to a page + * + * @v xen Xen hypervisor + * @v ref Grant reference + */ +static inline __attribute__ (( always_inline )) void +xengrant_invalidate ( struct xen_hypervisor *xen, grant_ref_t ref ) { + struct grant_entry_header *hdr = xengrant_header ( xen, ref ); + + /* Sanity check */ + assert ( ( readw ( &hdr->flags ) & + ( GTF_reading | GTF_writing ) ) == 0 ); + + /* This should apparently be done using a cmpxchg instruction. + * We omit this: partly in the interests of simplicity, but + * mainly since our control flow generally does not permit + * failure paths to themselves fail. + */ + writew ( 0, &hdr->flags ); + + /* Leave reference marked as in-use (see xengrant_alloc()) */ + writew ( DOMID_SELF, &hdr->domid ); +} + +/** + * Permit access to a page + * + * @v xen Xen hypervisor + * @v ref Grant reference + * @v domid Domain ID + * @v subflags Additional flags + * @v page Page start + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +xengrant_permit_access ( struct xen_hypervisor *xen, grant_ref_t ref, + domid_t domid, unsigned int subflags, void *page ) { + struct grant_entry_header *hdr = xengrant_header ( xen, ref ); + struct grant_entry_v1 *v1 = xengrant_v1 ( hdr ); + union grant_entry_v2 *v2 = xengrant_v2 ( hdr ); + unsigned long frame = ( virt_to_phys ( page ) / PAGE_SIZE ); + + /* Fail (for test purposes) if applicable */ + if ( ( XENGRANT_FAIL_RATE > 0 ) && + ( random() % XENGRANT_FAIL_RATE ) == 0 ) { + return -EAGAIN; + } + + /* Record frame number. This may fail on a 64-bit system if + * we are using v1 grant tables. On a 32-bit system, there is + * no way for this code path to fail (with either v1 or v2 + * grant tables); we allow the compiler to optimise the + * failure paths away to save space. + */ + if ( sizeof ( physaddr_t ) == sizeof ( uint64_t ) ) { + + /* 64-bit system */ + if ( xen->grant.shift ) { + /* Version 2 table: no possible failure */ + writeq ( frame, &v2->full_page.frame ); + } else { + /* Version 1 table: may fail if address above 16TB */ + if ( frame > 0xffffffffUL ) + return -ERANGE; + writel ( frame, &v1->frame ); + } + + } else { + + /* 32-bit system */ + if ( xen->grant.shift ) { + /* Version 2 table: no possible failure */ + writel ( frame, &v2->full_page.frame ); + } else { + /* Version 1 table: no possible failure */ + writel ( frame, &v1->frame ); + } + } + + /* Record domain ID and flags */ + writew ( domid, &hdr->domid ); + wmb(); + writew ( ( GTF_permit_access | subflags ), &hdr->flags ); + wmb(); + + return 0; +} + +extern int xengrant_init ( struct xen_hypervisor *xen ); +extern int xengrant_alloc ( struct xen_hypervisor *xen, grant_ref_t *refs, + unsigned int count ); +extern void xengrant_free ( struct xen_hypervisor *xen, grant_ref_t *refs, + unsigned int count ); + +#endif /* _IPXE_XENGRANT_H */ diff --git a/roms/ipxe/src/include/ipxe/xenmem.h b/roms/ipxe/src/include/ipxe/xenmem.h new file mode 100644 index 0000000..9b9aeda --- /dev/null +++ b/roms/ipxe/src/include/ipxe/xenmem.h @@ -0,0 +1,46 @@ +#ifndef _IPXE_XENMEM_H +#define _IPXE_XENMEM_H + +/** @file + * + * Xen memory operations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include + +/** + * Add page to physical address space + * + * @v xen Xen hypervisor + * @v add Page mapping descriptor + * @ret xenrc Xen status code + */ +static inline __attribute__ (( always_inline )) int +xenmem_add_to_physmap ( struct xen_hypervisor *xen, + struct xen_add_to_physmap *add ) { + + return xen_hypercall_2 ( xen, __HYPERVISOR_memory_op, + XENMEM_add_to_physmap, virt_to_phys ( add ) ); +} + +/** + * Remove page from physical address space + * + * @v xen Xen hypervisor + * @v remove Page mapping descriptor + * @ret xenrc Xen status code + */ +static inline __attribute__ (( always_inline )) int +xenmem_remove_from_physmap ( struct xen_hypervisor *xen, + struct xen_remove_from_physmap *remove ) { + + return xen_hypercall_2 ( xen, __HYPERVISOR_memory_op, + XENMEM_remove_from_physmap, + virt_to_phys ( remove ) ); +} + +#endif /* _IPXE_XENMEM_H */ diff --git a/roms/ipxe/src/include/ipxe/xenstore.h b/roms/ipxe/src/include/ipxe/xenstore.h new file mode 100644 index 0000000..f25f157 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/xenstore.h @@ -0,0 +1,29 @@ +#ifndef _IPXE_XENSTORE_H +#define _IPXE_XENSTORE_H + +/** @file + * + * XenStore interface + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include + +extern __attribute__ (( sentinel )) int +xenstore_read ( struct xen_hypervisor *xen, char **value, ... ); +extern __attribute__ (( sentinel )) int +xenstore_read_num ( struct xen_hypervisor *xen, unsigned long *num, ... ); +extern __attribute__ (( sentinel )) int +xenstore_write ( struct xen_hypervisor *xen, const char *value, ... ); +extern __attribute__ (( sentinel )) int +xenstore_write_num ( struct xen_hypervisor *xen, unsigned long num, ... ); +extern __attribute__ (( sentinel )) int +xenstore_rm ( struct xen_hypervisor *xen, ... ); +extern __attribute__ (( sentinel )) int +xenstore_directory ( struct xen_hypervisor *xen, char **children, size_t *len, + ... ); +extern void xenstore_dump ( struct xen_hypervisor *xen, const char *key ); + +#endif /* _IPXE_XENSTORE_H */ diff --git a/roms/ipxe/src/include/ipxe/xenver.h b/roms/ipxe/src/include/ipxe/xenver.h new file mode 100644 index 0000000..5d678c5 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/xenver.h @@ -0,0 +1,44 @@ +#ifndef _IPXE_XENVER_H +#define _IPXE_VENVER_H + +/** @file + * + * Xen version + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include + +/** + * Get Xen version + * + * @v xen Xen hypervisor + * @ret version Version (major.minor: 16 bits each) + */ +static inline __attribute__ (( always_inline )) uint32 +xenver_version ( struct xen_hypervisor *xen ) { + + return xen_hypercall_2 ( xen, __HYPERVISOR_xen_version, + XENVER_version, 0 ); +} + +/** + * Get Xen extra version string + * + * @v xen Xen hypervisor + * @v extraversion Extra version string to fill in + * @ret xenrc Xen status code + */ +static inline __attribute__ (( always_inline )) int +xenver_extraversion ( struct xen_hypervisor *xen, + xen_extraversion_t *extraversion ) { + + return xen_hypercall_2 ( xen, __HYPERVISOR_xen_version, + XENVER_extraversion, + virt_to_phys ( extraversion ) ); +} + +#endif /* _IPXE_XENVER_H */ diff --git a/roms/ipxe/src/include/usr/autoboot.h b/roms/ipxe/src/include/usr/autoboot.h index f562b2b..bc51aae 100644 --- a/roms/ipxe/src/include/usr/autoboot.h +++ b/roms/ipxe/src/include/usr/autoboot.h @@ -9,8 +9,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); -#include #include + struct net_device; struct uri; struct settings; @@ -26,7 +26,9 @@ enum uriboot_flags { URIBOOT_NO_SAN_BOOT | \ URIBOOT_NO_SAN_UNHOOK ) -extern struct device_description autoboot_device; +extern void set_autoboot_busloc ( unsigned int bus_type, + unsigned int location ); +extern void set_autoboot_ll_addr ( const void *ll_addr, size_t len ); extern int uriboot ( struct uri *filename, struct uri *root_path, int drive, unsigned int flags ); diff --git a/roms/ipxe/src/include/usr/pingmgmt.h b/roms/ipxe/src/include/usr/pingmgmt.h index 45ad5d3..d4c2d6c 100644 --- a/roms/ipxe/src/include/usr/pingmgmt.h +++ b/roms/ipxe/src/include/usr/pingmgmt.h @@ -11,6 +11,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include -extern int ping ( const char *hostname, unsigned long timeout, size_t len ); +extern int ping ( const char *hostname, unsigned long timeout, size_t len, + unsigned int count, int quiet ); #endif /* _USR_PINGMGMT_H */ diff --git a/roms/ipxe/src/include/xen/arch-arm.h b/roms/ipxe/src/include/xen/arch-arm.h new file mode 100644 index 0000000..ebc3aa2 --- /dev/null +++ b/roms/ipxe/src/include/xen/arch-arm.h @@ -0,0 +1,422 @@ +/****************************************************************************** + * arch-arm.h + * + * Guest OS interface to ARM Xen. + * + * 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. + * + * Copyright 2011 (C) Citrix Systems + */ + +#ifndef __XEN_PUBLIC_ARCH_ARM_H__ +#define __XEN_PUBLIC_ARCH_ARM_H__ + +FILE_LICENCE ( MIT ); + +/* + * `incontents 50 arm_abi Hypercall Calling Convention + * + * A hypercall is issued using the ARM HVC instruction. + * + * A hypercall can take up to 5 arguments. These are passed in + * registers, the first argument in x0/r0 (for arm64/arm32 guests + * respectively irrespective of whether the underlying hypervisor is + * 32- or 64-bit), the second argument in x1/r1, the third in x2/r2, + * the forth in x3/r3 and the fifth in x4/r4. + * + * The hypercall number is passed in r12 (arm) or x16 (arm64). In both + * cases the relevant ARM procedure calling convention specifies this + * is an inter-procedure-call scratch register (e.g. for use in linker + * stubs). This use does not conflict with use during a hypercall. + * + * The HVC ISS must contain a Xen specific TAG: XEN_HYPERCALL_TAG. + * + * The return value is in x0/r0. + * + * The hypercall will clobber x16/r12 and the argument registers used + * by that hypercall (except r0 which is the return value) i.e. in + * addition to x16/r12 a 2 argument hypercall will clobber x1/r1 and a + * 4 argument hypercall will clobber x1/r1, x2/r2 and x3/r3. + * + * Parameter structs passed to hypercalls are laid out according to + * the Procedure Call Standard for the ARM Architecture (AAPCS, AKA + * EABI) and Procedure Call Standard for the ARM 64-bit Architecture + * (AAPCS64). Where there is a conflict the 64-bit standard should be + * used regardless of guest type. Structures which are passed as + * hypercall arguments are always little endian. + * + * All memory which is shared with other entities in the system + * (including the hypervisor and other guests) must reside in memory + * which is mapped as Normal Inner-cacheable. This applies to: + * - hypercall arguments passed via a pointer to guest memory. + * - memory shared via the grant table mechanism (including PV I/O + * rings etc). + * - memory shared with the hypervisor (struct shared_info, struct + * vcpu_info, the grant table, etc). + * + * Any Inner cache allocation strategy (Write-Back, Write-Through etc) + * is acceptable. There is no restriction on the Outer-cacheability. + */ + +/* + * `incontents 55 arm_hcall Supported Hypercalls + * + * Xen on ARM makes extensive use of hardware facilities and therefore + * only a subset of the potential hypercalls are required. + * + * Since ARM uses second stage paging any machine/physical addresses + * passed to hypercalls are Guest Physical Addresses (Intermediate + * Physical Addresses) unless otherwise noted. + * + * The following hypercalls (and sub operations) are supported on the + * ARM platform. Other hypercalls should be considered + * unavailable/unsupported. + * + * HYPERVISOR_memory_op + * All generic sub-operations. + * + * In addition the following arch specific sub-ops: + * * XENMEM_add_to_physmap + * * XENMEM_add_to_physmap_batch + * + * HYPERVISOR_domctl + * All generic sub-operations, with the exception of: + * * XEN_DOMCTL_iomem_permission (not yet implemented) + * * XEN_DOMCTL_irq_permission (not yet implemented) + * + * HYPERVISOR_sched_op + * All generic sub-operations, with the exception of: + * * SCHEDOP_block -- prefer wfi hardware instruction + * + * HYPERVISOR_console_io + * All generic sub-operations + * + * HYPERVISOR_xen_version + * All generic sub-operations + * + * HYPERVISOR_event_channel_op + * All generic sub-operations + * + * HYPERVISOR_physdev_op + * No sub-operations are currenty supported + * + * HYPERVISOR_sysctl + * All generic sub-operations, with the exception of: + * * XEN_SYSCTL_page_offline_op + * * XEN_SYSCTL_get_pmstat + * * XEN_SYSCTL_pm_op + * + * HYPERVISOR_hvm_op + * Exactly these sub-operations are supported: + * * HVMOP_set_param + * * HVMOP_get_param + * + * HYPERVISOR_grant_table_op + * All generic sub-operations + * + * HYPERVISOR_vcpu_op + * Exactly these sub-operations are supported: + * * VCPUOP_register_vcpu_info + * * VCPUOP_register_runstate_memory_area + * + * + * Other notes on the ARM ABI: + * + * - struct start_info is not exported to ARM guests. + * + * - struct shared_info is mapped by ARM guests using the + * HYPERVISOR_memory_op sub-op XENMEM_add_to_physmap, passing + * XENMAPSPACE_shared_info as space parameter. + * + * - All the per-cpu struct vcpu_info are mapped by ARM guests using the + * HYPERVISOR_vcpu_op sub-op VCPUOP_register_vcpu_info, including cpu0 + * struct vcpu_info. + * + * - The grant table is mapped using the HYPERVISOR_memory_op sub-op + * XENMEM_add_to_physmap, passing XENMAPSPACE_grant_table as space + * parameter. The memory range specified under the Xen compatible + * hypervisor node on device tree can be used as target gpfn for the + * mapping. + * + * - Xenstore is initialized by using the two hvm_params + * HVM_PARAM_STORE_PFN and HVM_PARAM_STORE_EVTCHN. They can be read + * with the HYPERVISOR_hvm_op sub-op HVMOP_get_param. + * + * - The paravirtualized console is initialized by using the two + * hvm_params HVM_PARAM_CONSOLE_PFN and HVM_PARAM_CONSOLE_EVTCHN. They + * can be read with the HYPERVISOR_hvm_op sub-op HVMOP_get_param. + * + * - Event channel notifications are delivered using the percpu GIC + * interrupt specified under the Xen compatible hypervisor node on + * device tree. + * + * - The device tree Xen compatible node is fully described under Linux + * at Documentation/devicetree/bindings/arm/xen.txt. + */ + +#define XEN_HYPERCALL_TAG 0XEA1 + +#define uint64_aligned_t uint64_t __attribute__((aligned(8))) + +#ifndef __ASSEMBLY__ +#define ___DEFINE_XEN_GUEST_HANDLE(name, type) \ + typedef union { type *p; unsigned long q; } \ + __guest_handle_ ## name; \ + typedef union { type *p; uint64_aligned_t q; } \ + __guest_handle_64_ ## name; + +/* + * XEN_GUEST_HANDLE represents a guest pointer, when passed as a field + * in a struct in memory. On ARM is always 8 bytes sizes and 8 bytes + * aligned. + * XEN_GUEST_HANDLE_PARAM represent a guest pointer, when passed as an + * hypercall argument. It is 4 bytes on aarch and 8 bytes on aarch64. + */ +#define __DEFINE_XEN_GUEST_HANDLE(name, type) \ + ___DEFINE_XEN_GUEST_HANDLE(name, type); \ + ___DEFINE_XEN_GUEST_HANDLE(const_##name, const type) +#define DEFINE_XEN_GUEST_HANDLE(name) __DEFINE_XEN_GUEST_HANDLE(name, name) +#define __XEN_GUEST_HANDLE(name) __guest_handle_64_ ## name +#define XEN_GUEST_HANDLE(name) __XEN_GUEST_HANDLE(name) +/* this is going to be changed on 64 bit */ +#define XEN_GUEST_HANDLE_PARAM(name) __guest_handle_ ## name +#define set_xen_guest_handle_raw(hnd, val) \ + do { \ + typeof(&(hnd)) _sxghr_tmp = &(hnd); \ + _sxghr_tmp->q = 0; \ + _sxghr_tmp->p = val; \ + } while ( 0 ) +#ifdef __XEN_TOOLS__ +#define get_xen_guest_handle(val, hnd) do { val = (hnd).p; } while (0) +#endif +#define set_xen_guest_handle(hnd, val) set_xen_guest_handle_raw(hnd, val) + +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) +/* Anonymous union includes both 32- and 64-bit names (e.g., r0/x0). */ +# define __DECL_REG(n64, n32) union { \ + uint64_t n64; \ + uint32_t n32; \ + } +#else +/* Non-gcc sources must always use the proper 64-bit name (e.g., x0). */ +#define __DECL_REG(n64, n32) uint64_t n64 +#endif + +struct vcpu_guest_core_regs +{ + /* Aarch64 Aarch32 */ + __DECL_REG(x0, r0_usr); + __DECL_REG(x1, r1_usr); + __DECL_REG(x2, r2_usr); + __DECL_REG(x3, r3_usr); + __DECL_REG(x4, r4_usr); + __DECL_REG(x5, r5_usr); + __DECL_REG(x6, r6_usr); + __DECL_REG(x7, r7_usr); + __DECL_REG(x8, r8_usr); + __DECL_REG(x9, r9_usr); + __DECL_REG(x10, r10_usr); + __DECL_REG(x11, r11_usr); + __DECL_REG(x12, r12_usr); + + __DECL_REG(x13, sp_usr); + __DECL_REG(x14, lr_usr); + + __DECL_REG(x15, __unused_sp_hyp); + + __DECL_REG(x16, lr_irq); + __DECL_REG(x17, sp_irq); + + __DECL_REG(x18, lr_svc); + __DECL_REG(x19, sp_svc); + + __DECL_REG(x20, lr_abt); + __DECL_REG(x21, sp_abt); + + __DECL_REG(x22, lr_und); + __DECL_REG(x23, sp_und); + + __DECL_REG(x24, r8_fiq); + __DECL_REG(x25, r9_fiq); + __DECL_REG(x26, r10_fiq); + __DECL_REG(x27, r11_fiq); + __DECL_REG(x28, r12_fiq); + + __DECL_REG(x29, sp_fiq); + __DECL_REG(x30, lr_fiq); + + /* Return address and mode */ + __DECL_REG(pc64, pc32); /* ELR_EL2 */ + uint32_t cpsr; /* SPSR_EL2 */ + + union { + uint32_t spsr_el1; /* AArch64 */ + uint32_t spsr_svc; /* AArch32 */ + }; + + /* AArch32 guests only */ + uint32_t spsr_fiq, spsr_irq, spsr_und, spsr_abt; + + /* AArch64 guests only */ + uint64_t sp_el0; + uint64_t sp_el1, elr_el1; +}; +typedef struct vcpu_guest_core_regs vcpu_guest_core_regs_t; +DEFINE_XEN_GUEST_HANDLE(vcpu_guest_core_regs_t); + +#undef __DECL_REG + +typedef uint64_t xen_pfn_t; +#define PRI_xen_pfn PRIx64 + +/* Maximum number of virtual CPUs in legacy multi-processor guests. */ +/* Only one. All other VCPUS must use VCPUOP_register_vcpu_info */ +#define XEN_LEGACY_MAX_VCPUS 1 + +typedef uint64_t xen_ulong_t; +#define PRI_xen_ulong PRIx64 + +#if defined(__XEN__) || defined(__XEN_TOOLS__) +struct vcpu_guest_context { +#define _VGCF_online 0 +#define VGCF_online (1<<_VGCF_online) + uint32_t flags; /* VGCF_* */ + + struct vcpu_guest_core_regs user_regs; /* Core CPU registers */ + + uint32_t sctlr; + uint64_t ttbcr, ttbr0, ttbr1; +}; +typedef struct vcpu_guest_context vcpu_guest_context_t; +DEFINE_XEN_GUEST_HANDLE(vcpu_guest_context_t); +#endif + +struct arch_vcpu_info { +}; +typedef struct arch_vcpu_info arch_vcpu_info_t; + +struct arch_shared_info { +}; +typedef struct arch_shared_info arch_shared_info_t; +typedef uint64_t xen_callback_t; + +#endif + +#if defined(__XEN__) || defined(__XEN_TOOLS__) + +/* PSR bits (CPSR, SPSR)*/ + +#define PSR_THUMB (1<<5) /* Thumb Mode enable */ +#define PSR_FIQ_MASK (1<<6) /* Fast Interrupt mask */ +#define PSR_IRQ_MASK (1<<7) /* Interrupt mask */ +#define PSR_ABT_MASK (1<<8) /* Asynchronous Abort mask */ +#define PSR_BIG_ENDIAN (1<<9) /* arm32: Big Endian Mode */ +#define PSR_DBG_MASK (1<<9) /* arm64: Debug Exception mask */ +#define PSR_IT_MASK (0x0600fc00) /* Thumb If-Then Mask */ +#define PSR_JAZELLE (1<<24) /* Jazelle Mode */ + +/* 32 bit modes */ +#define PSR_MODE_USR 0x10 +#define PSR_MODE_FIQ 0x11 +#define PSR_MODE_IRQ 0x12 +#define PSR_MODE_SVC 0x13 +#define PSR_MODE_MON 0x16 +#define PSR_MODE_ABT 0x17 +#define PSR_MODE_HYP 0x1a +#define PSR_MODE_UND 0x1b +#define PSR_MODE_SYS 0x1f + +/* 64 bit modes */ +#define PSR_MODE_BIT 0x10 /* Set iff AArch32 */ +#define PSR_MODE_EL3h 0x0d +#define PSR_MODE_EL3t 0x0c +#define PSR_MODE_EL2h 0x09 +#define PSR_MODE_EL2t 0x08 +#define PSR_MODE_EL1h 0x05 +#define PSR_MODE_EL1t 0x04 +#define PSR_MODE_EL0t 0x00 + +#define PSR_GUEST32_INIT (PSR_ABT_MASK|PSR_FIQ_MASK|PSR_IRQ_MASK|PSR_MODE_SVC) +#define PSR_GUEST64_INIT (PSR_ABT_MASK|PSR_FIQ_MASK|PSR_IRQ_MASK|PSR_MODE_EL1h) + +#define SCTLR_GUEST_INIT 0x00c50078 + +/* + * Virtual machine platform (memory layout, interrupts) + * + * These are defined for consistency between the tools and the + * hypervisor. Guests must not rely on these hardcoded values but + * should instead use the FDT. + */ + +/* Physical Address Space */ +#define GUEST_GICD_BASE 0x03001000ULL +#define GUEST_GICD_SIZE 0x00001000ULL +#define GUEST_GICC_BASE 0x03002000ULL +#define GUEST_GICC_SIZE 0x00000100ULL + +/* 16MB == 4096 pages reserved for guest to use as a region to map its + * grant table in. + */ +#define GUEST_GNTTAB_BASE 0x38000000ULL +#define GUEST_GNTTAB_SIZE 0x01000000ULL + +#define GUEST_MAGIC_BASE 0x39000000ULL +#define GUEST_MAGIC_SIZE 0x01000000ULL + +#define GUEST_RAM_BANKS 2 + +#define GUEST_RAM0_BASE 0x40000000ULL /* 3GB of low RAM @ 1GB */ +#define GUEST_RAM0_SIZE 0xc0000000ULL + +#define GUEST_RAM1_BASE 0x0200000000ULL /* 1016GB of RAM @ 8GB */ +#define GUEST_RAM1_SIZE 0xfe00000000ULL + +#define GUEST_RAM_BASE GUEST_RAM0_BASE /* Lowest RAM address */ +/* Largest amount of actual RAM, not including holes */ +#define GUEST_RAM_MAX (GUEST_RAM0_SIZE + GUEST_RAM1_SIZE) +/* Suitable for e.g. const uint64_t ramfoo[] = GUEST_RAM_BANK_FOOS; */ +#define GUEST_RAM_BANK_BASES { GUEST_RAM0_BASE, GUEST_RAM1_BASE } +#define GUEST_RAM_BANK_SIZES { GUEST_RAM0_SIZE, GUEST_RAM1_SIZE } + +/* Interrupts */ +#define GUEST_TIMER_VIRT_PPI 27 +#define GUEST_TIMER_PHYS_S_PPI 29 +#define GUEST_TIMER_PHYS_NS_PPI 30 +#define GUEST_EVTCHN_PPI 31 + +/* PSCI functions */ +#define PSCI_cpu_suspend 0 +#define PSCI_cpu_off 1 +#define PSCI_cpu_on 2 +#define PSCI_migrate 3 + +#endif + +#endif /* __XEN_PUBLIC_ARCH_ARM_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/roms/ipxe/src/include/xen/arch-x86/xen-x86_32.h b/roms/ipxe/src/include/xen/arch-x86/xen-x86_32.h new file mode 100644 index 0000000..96c8f48 --- /dev/null +++ b/roms/ipxe/src/include/xen/arch-x86/xen-x86_32.h @@ -0,0 +1,173 @@ +/****************************************************************************** + * xen-x86_32.h + * + * Guest OS interface to x86 32-bit Xen. + * + * 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. + * + * Copyright (c) 2004-2007, K A Fraser + */ + +#ifndef __XEN_PUBLIC_ARCH_X86_XEN_X86_32_H__ +#define __XEN_PUBLIC_ARCH_X86_XEN_X86_32_H__ + +FILE_LICENCE ( MIT ); + +/* + * Hypercall interface: + * Input: %ebx, %ecx, %edx, %esi, %edi, %ebp (arguments 1-6) + * Output: %eax + * Access is via hypercall page (set up by guest loader or via a Xen MSR): + * call hypercall_page + hypercall-number * 32 + * Clobbered: Argument registers (e.g., 2-arg hypercall clobbers %ebx,%ecx) + */ + +/* + * These flat segments are in the Xen-private section of every GDT. Since these + * are also present in the initial GDT, many OSes will be able to avoid + * installing their own GDT. + */ +#define FLAT_RING1_CS 0xe019 /* GDT index 259 */ +#define FLAT_RING1_DS 0xe021 /* GDT index 260 */ +#define FLAT_RING1_SS 0xe021 /* GDT index 260 */ +#define FLAT_RING3_CS 0xe02b /* GDT index 261 */ +#define FLAT_RING3_DS 0xe033 /* GDT index 262 */ +#define FLAT_RING3_SS 0xe033 /* GDT index 262 */ + +#define FLAT_KERNEL_CS FLAT_RING1_CS +#define FLAT_KERNEL_DS FLAT_RING1_DS +#define FLAT_KERNEL_SS FLAT_RING1_SS +#define FLAT_USER_CS FLAT_RING3_CS +#define FLAT_USER_DS FLAT_RING3_DS +#define FLAT_USER_SS FLAT_RING3_SS + +#define __HYPERVISOR_VIRT_START_PAE 0xF5800000 +#define __MACH2PHYS_VIRT_START_PAE 0xF5800000 +#define __MACH2PHYS_VIRT_END_PAE 0xF6800000 +#define HYPERVISOR_VIRT_START_PAE \ + mk_unsigned_long(__HYPERVISOR_VIRT_START_PAE) +#define MACH2PHYS_VIRT_START_PAE \ + mk_unsigned_long(__MACH2PHYS_VIRT_START_PAE) +#define MACH2PHYS_VIRT_END_PAE \ + mk_unsigned_long(__MACH2PHYS_VIRT_END_PAE) + +/* Non-PAE bounds are obsolete. */ +#define __HYPERVISOR_VIRT_START_NONPAE 0xFC000000 +#define __MACH2PHYS_VIRT_START_NONPAE 0xFC000000 +#define __MACH2PHYS_VIRT_END_NONPAE 0xFC400000 +#define HYPERVISOR_VIRT_START_NONPAE \ + mk_unsigned_long(__HYPERVISOR_VIRT_START_NONPAE) +#define MACH2PHYS_VIRT_START_NONPAE \ + mk_unsigned_long(__MACH2PHYS_VIRT_START_NONPAE) +#define MACH2PHYS_VIRT_END_NONPAE \ + mk_unsigned_long(__MACH2PHYS_VIRT_END_NONPAE) + +#define __HYPERVISOR_VIRT_START __HYPERVISOR_VIRT_START_PAE +#define __MACH2PHYS_VIRT_START __MACH2PHYS_VIRT_START_PAE +#define __MACH2PHYS_VIRT_END __MACH2PHYS_VIRT_END_PAE + +#ifndef HYPERVISOR_VIRT_START +#define HYPERVISOR_VIRT_START mk_unsigned_long(__HYPERVISOR_VIRT_START) +#endif + +#define MACH2PHYS_VIRT_START mk_unsigned_long(__MACH2PHYS_VIRT_START) +#define MACH2PHYS_VIRT_END mk_unsigned_long(__MACH2PHYS_VIRT_END) +#define MACH2PHYS_NR_ENTRIES ((MACH2PHYS_VIRT_END-MACH2PHYS_VIRT_START)>>2) +#ifndef machine_to_phys_mapping +#define machine_to_phys_mapping ((unsigned long *)MACH2PHYS_VIRT_START) +#endif + +/* 32-/64-bit invariability for control interfaces (domctl/sysctl). */ +#if defined(__XEN__) || defined(__XEN_TOOLS__) +#undef ___DEFINE_XEN_GUEST_HANDLE +#define ___DEFINE_XEN_GUEST_HANDLE(name, type) \ + typedef struct { type *p; } \ + __guest_handle_ ## name; \ + typedef struct { union { type *p; uint64_aligned_t q; }; } \ + __guest_handle_64_ ## name +#undef set_xen_guest_handle_raw +#define set_xen_guest_handle_raw(hnd, val) \ + do { if ( sizeof(hnd) == 8 ) *(uint64_t *)&(hnd) = 0; \ + (hnd).p = val; \ + } while ( 0 ) +#define uint64_aligned_t uint64_t __attribute__((aligned(8))) +#define __XEN_GUEST_HANDLE_64(name) __guest_handle_64_ ## name +#define XEN_GUEST_HANDLE_64(name) __XEN_GUEST_HANDLE_64(name) +#endif + +#ifndef __ASSEMBLY__ + +struct cpu_user_regs { + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + uint32_t esi; + uint32_t edi; + uint32_t ebp; + uint32_t eax; + uint16_t error_code; /* private */ + uint16_t entry_vector; /* private */ + uint32_t eip; + uint16_t cs; + uint8_t saved_upcall_mask; + uint8_t _pad0; + uint32_t eflags; /* eflags.IF == !saved_upcall_mask */ + uint32_t esp; + uint16_t ss, _pad1; + uint16_t es, _pad2; + uint16_t ds, _pad3; + uint16_t fs, _pad4; + uint16_t gs, _pad5; +}; +typedef struct cpu_user_regs cpu_user_regs_t; +DEFINE_XEN_GUEST_HANDLE(cpu_user_regs_t); + +/* + * Page-directory addresses above 4GB do not fit into architectural %cr3. + * When accessing %cr3, or equivalent field in vcpu_guest_context, guests + * must use the following accessor macros to pack/unpack valid MFNs. + */ +#define xen_pfn_to_cr3(pfn) (((unsigned)(pfn) << 12) | ((unsigned)(pfn) >> 20)) +#define xen_cr3_to_pfn(cr3) (((unsigned)(cr3) >> 12) | ((unsigned)(cr3) << 20)) + +struct arch_vcpu_info { + unsigned long cr2; + unsigned long pad[5]; /* sizeof(vcpu_info_t) == 64 */ +}; +typedef struct arch_vcpu_info arch_vcpu_info_t; + +struct xen_callback { + unsigned long cs; + unsigned long eip; +}; +typedef struct xen_callback xen_callback_t; + +#endif /* !__ASSEMBLY__ */ + +#endif /* __XEN_PUBLIC_ARCH_X86_XEN_X86_32_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/roms/ipxe/src/include/xen/arch-x86/xen-x86_64.h b/roms/ipxe/src/include/xen/arch-x86/xen-x86_64.h new file mode 100644 index 0000000..0e92702 --- /dev/null +++ b/roms/ipxe/src/include/xen/arch-x86/xen-x86_64.h @@ -0,0 +1,204 @@ +/****************************************************************************** + * xen-x86_64.h + * + * Guest OS interface to x86 64-bit Xen. + * + * 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. + * + * Copyright (c) 2004-2006, K A Fraser + */ + +#ifndef __XEN_PUBLIC_ARCH_X86_XEN_X86_64_H__ +#define __XEN_PUBLIC_ARCH_X86_XEN_X86_64_H__ + +FILE_LICENCE ( MIT ); + +/* + * Hypercall interface: + * Input: %rdi, %rsi, %rdx, %r10, %r8, %r9 (arguments 1-6) + * Output: %rax + * Access is via hypercall page (set up by guest loader or via a Xen MSR): + * call hypercall_page + hypercall-number * 32 + * Clobbered: argument registers (e.g., 2-arg hypercall clobbers %rdi,%rsi) + */ + +/* + * 64-bit segment selectors + * These flat segments are in the Xen-private section of every GDT. Since these + * are also present in the initial GDT, many OSes will be able to avoid + * installing their own GDT. + */ + +#define FLAT_RING3_CS32 0xe023 /* GDT index 260 */ +#define FLAT_RING3_CS64 0xe033 /* GDT index 261 */ +#define FLAT_RING3_DS32 0xe02b /* GDT index 262 */ +#define FLAT_RING3_DS64 0x0000 /* NULL selector */ +#define FLAT_RING3_SS32 0xe02b /* GDT index 262 */ +#define FLAT_RING3_SS64 0xe02b /* GDT index 262 */ + +#define FLAT_KERNEL_DS64 FLAT_RING3_DS64 +#define FLAT_KERNEL_DS32 FLAT_RING3_DS32 +#define FLAT_KERNEL_DS FLAT_KERNEL_DS64 +#define FLAT_KERNEL_CS64 FLAT_RING3_CS64 +#define FLAT_KERNEL_CS32 FLAT_RING3_CS32 +#define FLAT_KERNEL_CS FLAT_KERNEL_CS64 +#define FLAT_KERNEL_SS64 FLAT_RING3_SS64 +#define FLAT_KERNEL_SS32 FLAT_RING3_SS32 +#define FLAT_KERNEL_SS FLAT_KERNEL_SS64 + +#define FLAT_USER_DS64 FLAT_RING3_DS64 +#define FLAT_USER_DS32 FLAT_RING3_DS32 +#define FLAT_USER_DS FLAT_USER_DS64 +#define FLAT_USER_CS64 FLAT_RING3_CS64 +#define FLAT_USER_CS32 FLAT_RING3_CS32 +#define FLAT_USER_CS FLAT_USER_CS64 +#define FLAT_USER_SS64 FLAT_RING3_SS64 +#define FLAT_USER_SS32 FLAT_RING3_SS32 +#define FLAT_USER_SS FLAT_USER_SS64 + +#define __HYPERVISOR_VIRT_START 0xFFFF800000000000 +#define __HYPERVISOR_VIRT_END 0xFFFF880000000000 +#define __MACH2PHYS_VIRT_START 0xFFFF800000000000 +#define __MACH2PHYS_VIRT_END 0xFFFF804000000000 + +#ifndef HYPERVISOR_VIRT_START +#define HYPERVISOR_VIRT_START mk_unsigned_long(__HYPERVISOR_VIRT_START) +#define HYPERVISOR_VIRT_END mk_unsigned_long(__HYPERVISOR_VIRT_END) +#endif + +#define MACH2PHYS_VIRT_START mk_unsigned_long(__MACH2PHYS_VIRT_START) +#define MACH2PHYS_VIRT_END mk_unsigned_long(__MACH2PHYS_VIRT_END) +#define MACH2PHYS_NR_ENTRIES ((MACH2PHYS_VIRT_END-MACH2PHYS_VIRT_START)>>3) +#ifndef machine_to_phys_mapping +#define machine_to_phys_mapping ((unsigned long *)HYPERVISOR_VIRT_START) +#endif + +/* + * int HYPERVISOR_set_segment_base(unsigned int which, unsigned long base) + * @which == SEGBASE_* ; @base == 64-bit base address + * Returns 0 on success. + */ +#define SEGBASE_FS 0 +#define SEGBASE_GS_USER 1 +#define SEGBASE_GS_KERNEL 2 +#define SEGBASE_GS_USER_SEL 3 /* Set user %gs specified in base[15:0] */ + +/* + * int HYPERVISOR_iret(void) + * All arguments are on the kernel stack, in the following format. + * Never returns if successful. Current kernel context is lost. + * The saved CS is mapped as follows: + * RING0 -> RING3 kernel mode. + * RING1 -> RING3 kernel mode. + * RING2 -> RING3 kernel mode. + * RING3 -> RING3 user mode. + * However RING0 indicates that the guest kernel should return to iteself + * directly with + * orb $3,1*8(%rsp) + * iretq + * If flags contains VGCF_in_syscall: + * Restore RAX, RIP, RFLAGS, RSP. + * Discard R11, RCX, CS, SS. + * Otherwise: + * Restore RAX, R11, RCX, CS:RIP, RFLAGS, SS:RSP. + * All other registers are saved on hypercall entry and restored to user. + */ +/* Guest exited in SYSCALL context? Return to guest with SYSRET? */ +#define _VGCF_in_syscall 8 +#define VGCF_in_syscall (1<<_VGCF_in_syscall) +#define VGCF_IN_SYSCALL VGCF_in_syscall + +#ifndef __ASSEMBLY__ + +struct iret_context { + /* Top of stack (%rsp at point of hypercall). */ + uint64_t rax, r11, rcx, flags, rip, cs, rflags, rsp, ss; + /* Bottom of iret stack frame. */ +}; + +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) +/* Anonymous union includes both 32- and 64-bit names (e.g., eax/rax). */ +#define __DECL_REG(name) union { \ + uint64_t r ## name, e ## name; \ + uint32_t _e ## name; \ +} +#else +/* Non-gcc sources must always use the proper 64-bit name (e.g., rax). */ +#define __DECL_REG(name) uint64_t r ## name +#endif + +struct cpu_user_regs { + uint64_t r15; + uint64_t r14; + uint64_t r13; + uint64_t r12; + __DECL_REG(bp); + __DECL_REG(bx); + uint64_t r11; + uint64_t r10; + uint64_t r9; + uint64_t r8; + __DECL_REG(ax); + __DECL_REG(cx); + __DECL_REG(dx); + __DECL_REG(si); + __DECL_REG(di); + uint32_t error_code; /* private */ + uint32_t entry_vector; /* private */ + __DECL_REG(ip); + uint16_t cs, _pad0[1]; + uint8_t saved_upcall_mask; + uint8_t _pad1[3]; + __DECL_REG(flags); /* rflags.IF == !saved_upcall_mask */ + __DECL_REG(sp); + uint16_t ss, _pad2[3]; + uint16_t es, _pad3[3]; + uint16_t ds, _pad4[3]; + uint16_t fs, _pad5[3]; /* Non-zero => takes precedence over fs_base. */ + uint16_t gs, _pad6[3]; /* Non-zero => takes precedence over gs_base_usr. */ +}; +typedef struct cpu_user_regs cpu_user_regs_t; +DEFINE_XEN_GUEST_HANDLE(cpu_user_regs_t); + +#undef __DECL_REG + +#define xen_pfn_to_cr3(pfn) ((unsigned long)(pfn) << 12) +#define xen_cr3_to_pfn(cr3) ((unsigned long)(cr3) >> 12) + +struct arch_vcpu_info { + unsigned long cr2; + unsigned long pad; /* sizeof(vcpu_info_t) == 64 */ +}; +typedef struct arch_vcpu_info arch_vcpu_info_t; + +typedef unsigned long xen_callback_t; + +#endif /* !__ASSEMBLY__ */ + +#endif /* __XEN_PUBLIC_ARCH_X86_XEN_X86_64_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/roms/ipxe/src/include/xen/arch-x86/xen.h b/roms/ipxe/src/include/xen/arch-x86/xen.h new file mode 100644 index 0000000..d75528f --- /dev/null +++ b/roms/ipxe/src/include/xen/arch-x86/xen.h @@ -0,0 +1,275 @@ +/****************************************************************************** + * arch-x86/xen.h + * + * Guest OS interface to x86 Xen. + * + * 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. + * + * Copyright (c) 2004-2006, K A Fraser + */ + +#include "../xen.h" + +#ifndef __XEN_PUBLIC_ARCH_X86_XEN_H__ +#define __XEN_PUBLIC_ARCH_X86_XEN_H__ + +FILE_LICENCE ( MIT ); + +/* Structural guest handles introduced in 0x00030201. */ +#if __XEN_INTERFACE_VERSION__ >= 0x00030201 +#define ___DEFINE_XEN_GUEST_HANDLE(name, type) \ + typedef struct { type *p; } __guest_handle_ ## name +#else +#define ___DEFINE_XEN_GUEST_HANDLE(name, type) \ + typedef type * __guest_handle_ ## name +#endif + +/* + * XEN_GUEST_HANDLE represents a guest pointer, when passed as a field + * in a struct in memory. + * XEN_GUEST_HANDLE_PARAM represent a guest pointer, when passed as an + * hypercall argument. + * XEN_GUEST_HANDLE_PARAM and XEN_GUEST_HANDLE are the same on X86 but + * they might not be on other architectures. + */ +#define __DEFINE_XEN_GUEST_HANDLE(name, type) \ + ___DEFINE_XEN_GUEST_HANDLE(name, type); \ + ___DEFINE_XEN_GUEST_HANDLE(const_##name, const type) +#define DEFINE_XEN_GUEST_HANDLE(name) __DEFINE_XEN_GUEST_HANDLE(name, name) +#define __XEN_GUEST_HANDLE(name) __guest_handle_ ## name +#define XEN_GUEST_HANDLE(name) __XEN_GUEST_HANDLE(name) +#define XEN_GUEST_HANDLE_PARAM(name) XEN_GUEST_HANDLE(name) +#define set_xen_guest_handle_raw(hnd, val) do { (hnd).p = val; } while (0) +#ifdef __XEN_TOOLS__ +#define get_xen_guest_handle(val, hnd) do { val = (hnd).p; } while (0) +#endif +#define set_xen_guest_handle(hnd, val) set_xen_guest_handle_raw(hnd, val) + +#if defined(__i386__) +#include "xen-x86_32.h" +#elif defined(__x86_64__) +#include "xen-x86_64.h" +#endif + +#ifndef __ASSEMBLY__ +typedef unsigned long xen_pfn_t; +#define PRI_xen_pfn "lx" +#endif + +#define XEN_HAVE_PV_GUEST_ENTRY 1 + +#define XEN_HAVE_PV_UPCALL_MASK 1 + +/* + * `incontents 200 segdesc Segment Descriptor Tables + */ +/* + * ` enum neg_errnoval + * ` HYPERVISOR_set_gdt(const xen_pfn_t frames[], unsigned int entries); + * ` + */ +/* + * A number of GDT entries are reserved by Xen. These are not situated at the + * start of the GDT because some stupid OSes export hard-coded selector values + * in their ABI. These hard-coded values are always near the start of the GDT, + * so Xen places itself out of the way, at the far end of the GDT. + * + * NB The LDT is set using the MMUEXT_SET_LDT op of HYPERVISOR_mmuext_op + */ +#define FIRST_RESERVED_GDT_PAGE 14 +#define FIRST_RESERVED_GDT_BYTE (FIRST_RESERVED_GDT_PAGE * 4096) +#define FIRST_RESERVED_GDT_ENTRY (FIRST_RESERVED_GDT_BYTE / 8) + + +/* + * ` enum neg_errnoval + * ` HYPERVISOR_update_descriptor(u64 pa, u64 desc); + * ` + * ` @pa The machine physical address of the descriptor to + * ` update. Must be either a descriptor page or writable. + * ` @desc The descriptor value to update, in the same format as a + * ` native descriptor table entry. + */ + +/* Maximum number of virtual CPUs in legacy multi-processor guests. */ +#define XEN_LEGACY_MAX_VCPUS 32 + +#ifndef __ASSEMBLY__ + +typedef unsigned long xen_ulong_t; +#define PRI_xen_ulong "lx" + +/* + * ` enum neg_errnoval + * ` HYPERVISOR_stack_switch(unsigned long ss, unsigned long esp); + * ` + * Sets the stack segment and pointer for the current vcpu. + */ + +/* + * ` enum neg_errnoval + * ` HYPERVISOR_set_trap_table(const struct trap_info traps[]); + * ` + */ +/* + * Send an array of these to HYPERVISOR_set_trap_table(). + * Terminate the array with a sentinel entry, with traps[].address==0. + * The privilege level specifies which modes may enter a trap via a software + * interrupt. On x86/64, since rings 1 and 2 are unavailable, we allocate + * privilege levels as follows: + * Level == 0: Noone may enter + * Level == 1: Kernel may enter + * Level == 2: Kernel may enter + * Level == 3: Everyone may enter + */ +#define TI_GET_DPL(_ti) ((_ti)->flags & 3) +#define TI_GET_IF(_ti) ((_ti)->flags & 4) +#define TI_SET_DPL(_ti,_dpl) ((_ti)->flags |= (_dpl)) +#define TI_SET_IF(_ti,_if) ((_ti)->flags |= ((!!(_if))<<2)) +struct trap_info { + uint8_t vector; /* exception vector */ + uint8_t flags; /* 0-3: privilege level; 4: clear event enable? */ + uint16_t cs; /* code selector */ + unsigned long address; /* code offset */ +}; +typedef struct trap_info trap_info_t; +DEFINE_XEN_GUEST_HANDLE(trap_info_t); + +typedef uint64_t tsc_timestamp_t; /* RDTSC timestamp */ + +/* + * The following is all CPU context. Note that the fpu_ctxt block is filled + * in by FXSAVE if the CPU has feature FXSR; otherwise FSAVE is used. + * + * Also note that when calling DOMCTL_setvcpucontext and VCPU_initialise + * for HVM and PVH guests, not all information in this structure is updated: + * + * - For HVM guests, the structures read include: fpu_ctxt (if + * VGCT_I387_VALID is set), flags, user_regs, debugreg[*] + * + * - PVH guests are the same as HVM guests, but additionally use ctrlreg[3] to + * set cr3. All other fields not used should be set to 0. + */ +struct vcpu_guest_context { + /* FPU registers come first so they can be aligned for FXSAVE/FXRSTOR. */ + struct { char x[512]; } fpu_ctxt; /* User-level FPU registers */ +#define VGCF_I387_VALID (1<<0) +#define VGCF_IN_KERNEL (1<<2) +#define _VGCF_i387_valid 0 +#define VGCF_i387_valid (1<<_VGCF_i387_valid) +#define _VGCF_in_kernel 2 +#define VGCF_in_kernel (1<<_VGCF_in_kernel) +#define _VGCF_failsafe_disables_events 3 +#define VGCF_failsafe_disables_events (1<<_VGCF_failsafe_disables_events) +#define _VGCF_syscall_disables_events 4 +#define VGCF_syscall_disables_events (1<<_VGCF_syscall_disables_events) +#define _VGCF_online 5 +#define VGCF_online (1<<_VGCF_online) + unsigned long flags; /* VGCF_* flags */ + struct cpu_user_regs user_regs; /* User-level CPU registers */ + struct trap_info trap_ctxt[256]; /* Virtual IDT */ + unsigned long ldt_base, ldt_ents; /* LDT (linear address, # ents) */ + unsigned long gdt_frames[16], gdt_ents; /* GDT (machine frames, # ents) */ + unsigned long kernel_ss, kernel_sp; /* Virtual TSS (only SS1/SP1) */ + /* NB. User pagetable on x86/64 is placed in ctrlreg[1]. */ + unsigned long ctrlreg[8]; /* CR0-CR7 (control registers) */ + unsigned long debugreg[8]; /* DB0-DB7 (debug registers) */ +#ifdef __i386__ + unsigned long event_callback_cs; /* CS:EIP of event callback */ + unsigned long event_callback_eip; + unsigned long failsafe_callback_cs; /* CS:EIP of failsafe callback */ + unsigned long failsafe_callback_eip; +#else + unsigned long event_callback_eip; + unsigned long failsafe_callback_eip; +#ifdef __XEN__ + union { + unsigned long syscall_callback_eip; + struct { + unsigned int event_callback_cs; /* compat CS of event cb */ + unsigned int failsafe_callback_cs; /* compat CS of failsafe cb */ + }; + }; +#else + unsigned long syscall_callback_eip; +#endif +#endif + unsigned long vm_assist; /* VMASST_TYPE_* bitmap */ +#ifdef __x86_64__ + /* Segment base addresses. */ + uint64_t fs_base; + uint64_t gs_base_kernel; + uint64_t gs_base_user; +#endif +}; +typedef struct vcpu_guest_context vcpu_guest_context_t; +DEFINE_XEN_GUEST_HANDLE(vcpu_guest_context_t); + +struct arch_shared_info { + unsigned long max_pfn; /* max pfn that appears in table */ + /* Frame containing list of mfns containing list of mfns containing p2m. */ + xen_pfn_t pfn_to_mfn_frame_list_list; + unsigned long nmi_reason; + uint64_t pad[32]; +}; +typedef struct arch_shared_info arch_shared_info_t; + +#endif /* !__ASSEMBLY__ */ + +/* + * ` enum neg_errnoval + * ` HYPERVISOR_fpu_taskswitch(int set); + * ` + * Sets (if set!=0) or clears (if set==0) CR0.TS. + */ + +/* + * ` enum neg_errnoval + * ` HYPERVISOR_set_debugreg(int regno, unsigned long value); + * + * ` unsigned long + * ` HYPERVISOR_get_debugreg(int regno); + * For 0<=reg<=7, returns the debug register value. + * For other values of reg, returns ((unsigned long)-EINVAL). + * (Unfortunately, this interface is defective.) + */ + +/* + * Prefix forces emulation of some non-trapping instructions. + * Currently only CPUID. + */ +#ifdef __ASSEMBLY__ +#define XEN_EMULATE_PREFIX .byte 0x0f,0x0b,0x78,0x65,0x6e ; +#define XEN_CPUID XEN_EMULATE_PREFIX cpuid +#else +#define XEN_EMULATE_PREFIX ".byte 0x0f,0x0b,0x78,0x65,0x6e ; " +#define XEN_CPUID XEN_EMULATE_PREFIX "cpuid" +#endif + +#endif /* __XEN_PUBLIC_ARCH_X86_XEN_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/roms/ipxe/src/include/xen/event_channel.h b/roms/ipxe/src/include/xen/event_channel.h new file mode 100644 index 0000000..356e946 --- /dev/null +++ b/roms/ipxe/src/include/xen/event_channel.h @@ -0,0 +1,383 @@ +/****************************************************************************** + * event_channel.h + * + * Event channels between domains. + * + * 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. + * + * Copyright (c) 2003-2004, K A Fraser. + */ + +#ifndef __XEN_PUBLIC_EVENT_CHANNEL_H__ +#define __XEN_PUBLIC_EVENT_CHANNEL_H__ + +FILE_LICENCE ( MIT ); + +#include "xen.h" + +/* + * `incontents 150 evtchn Event Channels + * + * Event channels are the basic primitive provided by Xen for event + * notifications. An event is the Xen equivalent of a hardware + * interrupt. They essentially store one bit of information, the event + * of interest is signalled by transitioning this bit from 0 to 1. + * + * Notifications are received by a guest via an upcall from Xen, + * indicating when an event arrives (setting the bit). Further + * notifications are masked until the bit is cleared again (therefore, + * guests must check the value of the bit after re-enabling event + * delivery to ensure no missed notifications). + * + * Event notifications can be masked by setting a flag; this is + * equivalent to disabling interrupts and can be used to ensure + * atomicity of certain operations in the guest kernel. + * + * Event channels are represented by the evtchn_* fields in + * struct shared_info and struct vcpu_info. + */ + +/* + * ` enum neg_errnoval + * ` HYPERVISOR_event_channel_op(enum event_channel_op cmd, void *args) + * ` + * @cmd == EVTCHNOP_* (event-channel operation). + * @args == struct evtchn_* Operation-specific extra arguments (NULL if none). + */ + +/* ` enum event_channel_op { // EVTCHNOP_* => struct evtchn_* */ +#define EVTCHNOP_bind_interdomain 0 +#define EVTCHNOP_bind_virq 1 +#define EVTCHNOP_bind_pirq 2 +#define EVTCHNOP_close 3 +#define EVTCHNOP_send 4 +#define EVTCHNOP_status 5 +#define EVTCHNOP_alloc_unbound 6 +#define EVTCHNOP_bind_ipi 7 +#define EVTCHNOP_bind_vcpu 8 +#define EVTCHNOP_unmask 9 +#define EVTCHNOP_reset 10 +#define EVTCHNOP_init_control 11 +#define EVTCHNOP_expand_array 12 +#define EVTCHNOP_set_priority 13 +/* ` } */ + +typedef uint32_t evtchn_port_t; +DEFINE_XEN_GUEST_HANDLE(evtchn_port_t); + +/* + * EVTCHNOP_alloc_unbound: Allocate a port in domain and mark as + * accepting interdomain bindings from domain . A fresh port + * is allocated in and returned as . + * NOTES: + * 1. If the caller is unprivileged then must be DOMID_SELF. + * 2. may be DOMID_SELF, allowing loopback connections. + */ +struct evtchn_alloc_unbound { + /* IN parameters */ + domid_t dom, remote_dom; + /* OUT parameters */ + evtchn_port_t port; +}; +typedef struct evtchn_alloc_unbound evtchn_alloc_unbound_t; + +/* + * EVTCHNOP_bind_interdomain: Construct an interdomain event channel between + * the calling domain and . must identify + * a port that is unbound and marked as accepting bindings from the calling + * domain. A fresh port is allocated in the calling domain and returned as + * . + * + * In case the peer domain has already tried to set our event channel + * pending, before it was bound, EVTCHNOP_bind_interdomain always sets + * the local event channel pending. + * + * The usual pattern of use, in the guest's upcall (or subsequent + * handler) is as follows: (Re-enable the event channel for subsequent + * signalling and then) check for the existence of whatever condition + * is being waited for by other means, and take whatever action is + * needed (if any). + * + * NOTES: + * 1. may be DOMID_SELF, allowing loopback connections. + */ +struct evtchn_bind_interdomain { + /* IN parameters. */ + domid_t remote_dom; + evtchn_port_t remote_port; + /* OUT parameters. */ + evtchn_port_t local_port; +}; +typedef struct evtchn_bind_interdomain evtchn_bind_interdomain_t; + +/* + * EVTCHNOP_bind_virq: Bind a local event channel to VIRQ on specified + * vcpu. + * NOTES: + * 1. Virtual IRQs are classified as per-vcpu or global. See the VIRQ list + * in xen.h for the classification of each VIRQ. + * 2. Global VIRQs must be allocated on VCPU0 but can subsequently be + * re-bound via EVTCHNOP_bind_vcpu. + * 3. Per-vcpu VIRQs may be bound to at most one event channel per vcpu. + * The allocated event channel is bound to the specified vcpu and the + * binding cannot be changed. + */ +struct evtchn_bind_virq { + /* IN parameters. */ + uint32_t virq; /* enum virq */ + uint32_t vcpu; + /* OUT parameters. */ + evtchn_port_t port; +}; +typedef struct evtchn_bind_virq evtchn_bind_virq_t; + +/* + * EVTCHNOP_bind_pirq: Bind a local event channel to a real IRQ (PIRQ ). + * NOTES: + * 1. A physical IRQ may be bound to at most one event channel per domain. + * 2. Only a sufficiently-privileged domain may bind to a physical IRQ. + */ +struct evtchn_bind_pirq { + /* IN parameters. */ + uint32_t pirq; +#define BIND_PIRQ__WILL_SHARE 1 + uint32_t flags; /* BIND_PIRQ__* */ + /* OUT parameters. */ + evtchn_port_t port; +}; +typedef struct evtchn_bind_pirq evtchn_bind_pirq_t; + +/* + * EVTCHNOP_bind_ipi: Bind a local event channel to receive events. + * NOTES: + * 1. The allocated event channel is bound to the specified vcpu. The binding + * may not be changed. + */ +struct evtchn_bind_ipi { + uint32_t vcpu; + /* OUT parameters. */ + evtchn_port_t port; +}; +typedef struct evtchn_bind_ipi evtchn_bind_ipi_t; + +/* + * EVTCHNOP_close: Close a local event channel . If the channel is + * interdomain then the remote end is placed in the unbound state + * (EVTCHNSTAT_unbound), awaiting a new connection. + */ +struct evtchn_close { + /* IN parameters. */ + evtchn_port_t port; +}; +typedef struct evtchn_close evtchn_close_t; + +/* + * EVTCHNOP_send: Send an event to the remote end of the channel whose local + * endpoint is . + */ +struct evtchn_send { + /* IN parameters. */ + evtchn_port_t port; +}; +typedef struct evtchn_send evtchn_send_t; + +/* + * EVTCHNOP_status: Get the current status of the communication channel which + * has an endpoint at . + * NOTES: + * 1. may be specified as DOMID_SELF. + * 2. Only a sufficiently-privileged domain may obtain the status of an event + * channel for which is not DOMID_SELF. + */ +struct evtchn_status { + /* IN parameters */ + domid_t dom; + evtchn_port_t port; + /* OUT parameters */ +#define EVTCHNSTAT_closed 0 /* Channel is not in use. */ +#define EVTCHNSTAT_unbound 1 /* Channel is waiting interdom connection.*/ +#define EVTCHNSTAT_interdomain 2 /* Channel is connected to remote domain. */ +#define EVTCHNSTAT_pirq 3 /* Channel is bound to a phys IRQ line. */ +#define EVTCHNSTAT_virq 4 /* Channel is bound to a virtual IRQ line */ +#define EVTCHNSTAT_ipi 5 /* Channel is bound to a virtual IPI line */ + uint32_t status; + uint32_t vcpu; /* VCPU to which this channel is bound. */ + union { + struct { + domid_t dom; + } unbound; /* EVTCHNSTAT_unbound */ + struct { + domid_t dom; + evtchn_port_t port; + } interdomain; /* EVTCHNSTAT_interdomain */ + uint32_t pirq; /* EVTCHNSTAT_pirq */ + uint32_t virq; /* EVTCHNSTAT_virq */ + } u; +}; +typedef struct evtchn_status evtchn_status_t; + +/* + * EVTCHNOP_bind_vcpu: Specify which vcpu a channel should notify when an + * event is pending. + * NOTES: + * 1. IPI-bound channels always notify the vcpu specified at bind time. + * This binding cannot be changed. + * 2. Per-VCPU VIRQ channels always notify the vcpu specified at bind time. + * This binding cannot be changed. + * 3. All other channels notify vcpu0 by default. This default is set when + * the channel is allocated (a port that is freed and subsequently reused + * has its binding reset to vcpu0). + */ +struct evtchn_bind_vcpu { + /* IN parameters. */ + evtchn_port_t port; + uint32_t vcpu; +}; +typedef struct evtchn_bind_vcpu evtchn_bind_vcpu_t; + +/* + * EVTCHNOP_unmask: Unmask the specified local event-channel port and deliver + * a notification to the appropriate VCPU if an event is pending. + */ +struct evtchn_unmask { + /* IN parameters. */ + evtchn_port_t port; +}; +typedef struct evtchn_unmask evtchn_unmask_t; + +/* + * EVTCHNOP_reset: Close all event channels associated with specified domain. + * NOTES: + * 1. may be specified as DOMID_SELF. + * 2. Only a sufficiently-privileged domain may specify other than DOMID_SELF. + */ +struct evtchn_reset { + /* IN parameters. */ + domid_t dom; +}; +typedef struct evtchn_reset evtchn_reset_t; + +/* + * EVTCHNOP_init_control: initialize the control block for the FIFO ABI. + * + * Note: any events that are currently pending will not be resent and + * will be lost. Guests should call this before binding any event to + * avoid losing any events. + */ +struct evtchn_init_control { + /* IN parameters. */ + uint64_t control_gfn; + uint32_t offset; + uint32_t vcpu; + /* OUT parameters. */ + uint8_t link_bits; + uint8_t _pad[7]; +}; +typedef struct evtchn_init_control evtchn_init_control_t; + +/* + * EVTCHNOP_expand_array: add an additional page to the event array. + */ +struct evtchn_expand_array { + /* IN parameters. */ + uint64_t array_gfn; +}; +typedef struct evtchn_expand_array evtchn_expand_array_t; + +/* + * EVTCHNOP_set_priority: set the priority for an event channel. + */ +struct evtchn_set_priority { + /* IN parameters. */ + uint32_t port; + uint32_t priority; +}; +typedef struct evtchn_set_priority evtchn_set_priority_t; + +/* + * ` enum neg_errnoval + * ` HYPERVISOR_event_channel_op_compat(struct evtchn_op *op) + * ` + * Superceded by new event_channel_op() hypercall since 0x00030202. + */ +struct evtchn_op { + uint32_t cmd; /* enum event_channel_op */ + union { + struct evtchn_alloc_unbound alloc_unbound; + struct evtchn_bind_interdomain bind_interdomain; + struct evtchn_bind_virq bind_virq; + struct evtchn_bind_pirq bind_pirq; + struct evtchn_bind_ipi bind_ipi; + struct evtchn_close close; + struct evtchn_send send; + struct evtchn_status status; + struct evtchn_bind_vcpu bind_vcpu; + struct evtchn_unmask unmask; + } u; +}; +typedef struct evtchn_op evtchn_op_t; +DEFINE_XEN_GUEST_HANDLE(evtchn_op_t); + +/* + * 2-level ABI + */ + +#define EVTCHN_2L_NR_CHANNELS (sizeof(xen_ulong_t) * sizeof(xen_ulong_t) * 64) + +/* + * FIFO ABI + */ + +/* Events may have priorities from 0 (highest) to 15 (lowest). */ +#define EVTCHN_FIFO_PRIORITY_MAX 0 +#define EVTCHN_FIFO_PRIORITY_DEFAULT 7 +#define EVTCHN_FIFO_PRIORITY_MIN 15 + +#define EVTCHN_FIFO_MAX_QUEUES (EVTCHN_FIFO_PRIORITY_MIN + 1) + +typedef uint32_t event_word_t; + +#define EVTCHN_FIFO_PENDING 31 +#define EVTCHN_FIFO_MASKED 30 +#define EVTCHN_FIFO_LINKED 29 +#define EVTCHN_FIFO_BUSY 28 + +#define EVTCHN_FIFO_LINK_BITS 17 +#define EVTCHN_FIFO_LINK_MASK ((1 << EVTCHN_FIFO_LINK_BITS) - 1) + +#define EVTCHN_FIFO_NR_CHANNELS (1 << EVTCHN_FIFO_LINK_BITS) + +struct evtchn_fifo_control_block { + uint32_t ready; + uint32_t _rsvd; + uint32_t head[EVTCHN_FIFO_MAX_QUEUES]; +}; +typedef struct evtchn_fifo_control_block evtchn_fifo_control_block_t; + +#endif /* __XEN_PUBLIC_EVENT_CHANNEL_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/roms/ipxe/src/include/xen/features.h b/roms/ipxe/src/include/xen/features.h new file mode 100644 index 0000000..1302658 --- /dev/null +++ b/roms/ipxe/src/include/xen/features.h @@ -0,0 +1,111 @@ +/****************************************************************************** + * features.h + * + * Feature flags, reported by XENVER_get_features. + * + * 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. + * + * Copyright (c) 2006, Keir Fraser + */ + +#ifndef __XEN_PUBLIC_FEATURES_H__ +#define __XEN_PUBLIC_FEATURES_H__ + +FILE_LICENCE ( MIT ); + +/* + * `incontents 200 elfnotes_features XEN_ELFNOTE_FEATURES + * + * The list of all the features the guest supports. They are set by + * parsing the XEN_ELFNOTE_FEATURES and XEN_ELFNOTE_SUPPORTED_FEATURES + * string. The format is the feature names (as given here without the + * "XENFEAT_" prefix) separated by '|' characters. + * If a feature is required for the kernel to function then the feature name + * must be preceded by a '!' character. + * + * Note that if XEN_ELFNOTE_SUPPORTED_FEATURES is used, then in the + * XENFEAT_dom0 MUST be set if the guest is to be booted as dom0, + */ + +/* + * If set, the guest does not need to write-protect its pagetables, and can + * update them via direct writes. + */ +#define XENFEAT_writable_page_tables 0 + +/* + * If set, the guest does not need to write-protect its segment descriptor + * tables, and can update them via direct writes. + */ +#define XENFEAT_writable_descriptor_tables 1 + +/* + * If set, translation between the guest's 'pseudo-physical' address space + * and the host's machine address space are handled by the hypervisor. In this + * mode the guest does not need to perform phys-to/from-machine translations + * when performing page table operations. + */ +#define XENFEAT_auto_translated_physmap 2 + +/* If set, the guest is running in supervisor mode (e.g., x86 ring 0). */ +#define XENFEAT_supervisor_mode_kernel 3 + +/* + * If set, the guest does not need to allocate x86 PAE page directories + * below 4GB. This flag is usually implied by auto_translated_physmap. + */ +#define XENFEAT_pae_pgdir_above_4gb 4 + +/* x86: Does this Xen host support the MMU_PT_UPDATE_PRESERVE_AD hypercall? */ +#define XENFEAT_mmu_pt_update_preserve_ad 5 + +/* x86: Does this Xen host support the MMU_{CLEAR,COPY}_PAGE hypercall? */ +#define XENFEAT_highmem_assist 6 + +/* + * If set, GNTTABOP_map_grant_ref honors flags to be placed into guest kernel + * available pte bits. + */ +#define XENFEAT_gnttab_map_avail_bits 7 + +/* x86: Does this Xen host support the HVM callback vector type? */ +#define XENFEAT_hvm_callback_vector 8 + +/* x86: pvclock algorithm is safe to use on HVM */ +#define XENFEAT_hvm_safe_pvclock 9 + +/* x86: pirq can be used by HVM guests */ +#define XENFEAT_hvm_pirqs 10 + +/* operation as Dom0 is supported */ +#define XENFEAT_dom0 11 + +#define XENFEAT_NR_SUBMAPS 1 + +#endif /* __XEN_PUBLIC_FEATURES_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/roms/ipxe/src/include/xen/grant_table.h b/roms/ipxe/src/include/xen/grant_table.h new file mode 100644 index 0000000..137939e --- /dev/null +++ b/roms/ipxe/src/include/xen/grant_table.h @@ -0,0 +1,664 @@ +/****************************************************************************** + * grant_table.h + * + * Interface for granting foreign access to page frames, and receiving + * page-ownership transfers. + * + * 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. + * + * Copyright (c) 2004, K A Fraser + */ + +#ifndef __XEN_PUBLIC_GRANT_TABLE_H__ +#define __XEN_PUBLIC_GRANT_TABLE_H__ + +FILE_LICENCE ( MIT ); + +#include "xen.h" + +/* + * `incontents 150 gnttab Grant Tables + * + * Xen's grant tables provide a generic mechanism to memory sharing + * between domains. This shared memory interface underpins the split + * device drivers for block and network IO. + * + * Each domain has its own grant table. This is a data structure that + * is shared with Xen; it allows the domain to tell Xen what kind of + * permissions other domains have on its pages. Entries in the grant + * table are identified by grant references. A grant reference is an + * integer, which indexes into the grant table. It acts as a + * capability which the grantee can use to perform operations on the + * granter’s memory. + * + * This capability-based system allows shared-memory communications + * between unprivileged domains. A grant reference also encapsulates + * the details of a shared page, removing the need for a domain to + * know the real machine address of a page it is sharing. This makes + * it possible to share memory correctly with domains running in + * fully virtualised memory. + */ + +/*********************************** + * GRANT TABLE REPRESENTATION + */ + +/* Some rough guidelines on accessing and updating grant-table entries + * in a concurrency-safe manner. For more information, Linux contains a + * reference implementation for guest OSes (drivers/xen/grant_table.c, see + * http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=blob;f=drivers/xen/grant-table.c;hb=HEAD + * + * NB. WMB is a no-op on current-generation x86 processors. However, a + * compiler barrier will still be required. + * + * Introducing a valid entry into the grant table: + * 1. Write ent->domid. + * 2. Write ent->frame: + * GTF_permit_access: Frame to which access is permitted. + * GTF_accept_transfer: Pseudo-phys frame slot being filled by new + * frame, or zero if none. + * 3. Write memory barrier (WMB). + * 4. Write ent->flags, inc. valid type. + * + * Invalidating an unused GTF_permit_access entry: + * 1. flags = ent->flags. + * 2. Observe that !(flags & (GTF_reading|GTF_writing)). + * 3. Check result of SMP-safe CMPXCHG(&ent->flags, flags, 0). + * NB. No need for WMB as reuse of entry is control-dependent on success of + * step 3, and all architectures guarantee ordering of ctrl-dep writes. + * + * Invalidating an in-use GTF_permit_access entry: + * This cannot be done directly. Request assistance from the domain controller + * which can set a timeout on the use of a grant entry and take necessary + * action. (NB. This is not yet implemented!). + * + * Invalidating an unused GTF_accept_transfer entry: + * 1. flags = ent->flags. + * 2. Observe that !(flags & GTF_transfer_committed). [*] + * 3. Check result of SMP-safe CMPXCHG(&ent->flags, flags, 0). + * NB. No need for WMB as reuse of entry is control-dependent on success of + * step 3, and all architectures guarantee ordering of ctrl-dep writes. + * [*] If GTF_transfer_committed is set then the grant entry is 'committed'. + * The guest must /not/ modify the grant entry until the address of the + * transferred frame is written. It is safe for the guest to spin waiting + * for this to occur (detect by observing GTF_transfer_completed in + * ent->flags). + * + * Invalidating a committed GTF_accept_transfer entry: + * 1. Wait for (ent->flags & GTF_transfer_completed). + * + * Changing a GTF_permit_access from writable to read-only: + * Use SMP-safe CMPXCHG to set GTF_readonly, while checking !GTF_writing. + * + * Changing a GTF_permit_access from read-only to writable: + * Use SMP-safe bit-setting instruction. + */ + +/* + * Reference to a grant entry in a specified domain's grant table. + */ +typedef uint32_t grant_ref_t; + +/* + * A grant table comprises a packed array of grant entries in one or more + * page frames shared between Xen and a guest. + * [XEN]: This field is written by Xen and read by the sharing guest. + * [GST]: This field is written by the guest and read by Xen. + */ + +/* + * Version 1 of the grant table entry structure is maintained purely + * for backwards compatibility. New guests should use version 2. + */ +#if __XEN_INTERFACE_VERSION__ < 0x0003020a +#define grant_entry_v1 grant_entry +#define grant_entry_v1_t grant_entry_t +#endif +struct grant_entry_v1 { + /* GTF_xxx: various type and flag information. [XEN,GST] */ + uint16_t flags; + /* The domain being granted foreign privileges. [GST] */ + domid_t domid; + /* + * GTF_permit_access: Frame that @domid is allowed to map and access. [GST] + * GTF_accept_transfer: Frame whose ownership transferred by @domid. [XEN] + */ + uint32_t frame; +}; +typedef struct grant_entry_v1 grant_entry_v1_t; + +/* The first few grant table entries will be preserved across grant table + * version changes and may be pre-populated at domain creation by tools. + */ +#define GNTTAB_NR_RESERVED_ENTRIES 8 +#define GNTTAB_RESERVED_CONSOLE 0 +#define GNTTAB_RESERVED_XENSTORE 1 + +/* + * Type of grant entry. + * GTF_invalid: This grant entry grants no privileges. + * GTF_permit_access: Allow @domid to map/access @frame. + * GTF_accept_transfer: Allow @domid to transfer ownership of one page frame + * to this guest. Xen writes the page number to @frame. + * GTF_transitive: Allow @domid to transitively access a subrange of + * @trans_grant in @trans_domid. No mappings are allowed. + */ +#define GTF_invalid (0U<<0) +#define GTF_permit_access (1U<<0) +#define GTF_accept_transfer (2U<<0) +#define GTF_transitive (3U<<0) +#define GTF_type_mask (3U<<0) + +/* + * Subflags for GTF_permit_access. + * GTF_readonly: Restrict @domid to read-only mappings and accesses. [GST] + * GTF_reading: Grant entry is currently mapped for reading by @domid. [XEN] + * GTF_writing: Grant entry is currently mapped for writing by @domid. [XEN] + * GTF_PAT, GTF_PWT, GTF_PCD: (x86) cache attribute flags for the grant [GST] + * GTF_sub_page: Grant access to only a subrange of the page. @domid + * will only be allowed to copy from the grant, and not + * map it. [GST] + */ +#define _GTF_readonly (2) +#define GTF_readonly (1U<<_GTF_readonly) +#define _GTF_reading (3) +#define GTF_reading (1U<<_GTF_reading) +#define _GTF_writing (4) +#define GTF_writing (1U<<_GTF_writing) +#define _GTF_PWT (5) +#define GTF_PWT (1U<<_GTF_PWT) +#define _GTF_PCD (6) +#define GTF_PCD (1U<<_GTF_PCD) +#define _GTF_PAT (7) +#define GTF_PAT (1U<<_GTF_PAT) +#define _GTF_sub_page (8) +#define GTF_sub_page (1U<<_GTF_sub_page) + +/* + * Subflags for GTF_accept_transfer: + * GTF_transfer_committed: Xen sets this flag to indicate that it is committed + * to transferring ownership of a page frame. When a guest sees this flag + * it must /not/ modify the grant entry until GTF_transfer_completed is + * set by Xen. + * GTF_transfer_completed: It is safe for the guest to spin-wait on this flag + * after reading GTF_transfer_committed. Xen will always write the frame + * address, followed by ORing this flag, in a timely manner. + */ +#define _GTF_transfer_committed (2) +#define GTF_transfer_committed (1U<<_GTF_transfer_committed) +#define _GTF_transfer_completed (3) +#define GTF_transfer_completed (1U<<_GTF_transfer_completed) + +/* + * Version 2 grant table entries. These fulfil the same role as + * version 1 entries, but can represent more complicated operations. + * Any given domain will have either a version 1 or a version 2 table, + * and every entry in the table will be the same version. + * + * The interface by which domains use grant references does not depend + * on the grant table version in use by the other domain. + */ +#if __XEN_INTERFACE_VERSION__ >= 0x0003020a +/* + * Version 1 and version 2 grant entries share a common prefix. The + * fields of the prefix are documented as part of struct + * grant_entry_v1. + */ +struct grant_entry_header { + uint16_t flags; + domid_t domid; +}; +typedef struct grant_entry_header grant_entry_header_t; + +/* + * Version 2 of the grant entry structure. + */ +union grant_entry_v2 { + grant_entry_header_t hdr; + + /* + * This member is used for V1-style full page grants, where either: + * + * -- hdr.type is GTF_accept_transfer, or + * -- hdr.type is GTF_permit_access and GTF_sub_page is not set. + * + * In that case, the frame field has the same semantics as the + * field of the same name in the V1 entry structure. + */ + struct { + grant_entry_header_t hdr; + uint32_t pad0; + uint64_t frame; + } full_page; + + /* + * If the grant type is GTF_grant_access and GTF_sub_page is set, + * @domid is allowed to access bytes [@page_off,@page_off+@length) + * in frame @frame. + */ + struct { + grant_entry_header_t hdr; + uint16_t page_off; + uint16_t length; + uint64_t frame; + } sub_page; + + /* + * If the grant is GTF_transitive, @domid is allowed to use the + * grant @gref in domain @trans_domid, as if it was the local + * domain. Obviously, the transitive access must be compatible + * with the original grant. + * + * The current version of Xen does not allow transitive grants + * to be mapped. + */ + struct { + grant_entry_header_t hdr; + domid_t trans_domid; + uint16_t pad0; + grant_ref_t gref; + } transitive; + + uint32_t __spacer[4]; /* Pad to a power of two */ +}; +typedef union grant_entry_v2 grant_entry_v2_t; + +typedef uint16_t grant_status_t; + +#endif /* __XEN_INTERFACE_VERSION__ */ + +/*********************************** + * GRANT TABLE QUERIES AND USES + */ + +/* ` enum neg_errnoval + * ` HYPERVISOR_grant_table_op(enum grant_table_op cmd, + * ` void *args, + * ` unsigned int count) + * ` + * + * @args points to an array of a per-command data structure. The array + * has @count members + */ + +/* ` enum grant_table_op { // GNTTABOP_* => struct gnttab_* */ +#define GNTTABOP_map_grant_ref 0 +#define GNTTABOP_unmap_grant_ref 1 +#define GNTTABOP_setup_table 2 +#define GNTTABOP_dump_table 3 +#define GNTTABOP_transfer 4 +#define GNTTABOP_copy 5 +#define GNTTABOP_query_size 6 +#define GNTTABOP_unmap_and_replace 7 +#if __XEN_INTERFACE_VERSION__ >= 0x0003020a +#define GNTTABOP_set_version 8 +#define GNTTABOP_get_status_frames 9 +#define GNTTABOP_get_version 10 +#define GNTTABOP_swap_grant_ref 11 +#endif /* __XEN_INTERFACE_VERSION__ */ +/* ` } */ + +/* + * Handle to track a mapping created via a grant reference. + */ +typedef uint32_t grant_handle_t; + +/* + * GNTTABOP_map_grant_ref: Map the grant entry (,) for access + * by devices and/or host CPUs. If successful, is a tracking number + * that must be presented later to destroy the mapping(s). On error, + * is a negative status code. + * NOTES: + * 1. If GNTMAP_device_map is specified then is the address + * via which I/O devices may access the granted frame. + * 2. If GNTMAP_host_map is specified then a mapping will be added at + * either a host virtual address in the current address space, or at + * a PTE at the specified machine address. The type of mapping to + * perform is selected through the GNTMAP_contains_pte flag, and the + * address is specified in . + * 3. Mappings should only be destroyed via GNTTABOP_unmap_grant_ref. If a + * host mapping is destroyed by other means then it is *NOT* guaranteed + * to be accounted to the correct grant reference! + */ +struct gnttab_map_grant_ref { + /* IN parameters. */ + uint64_t host_addr; + uint32_t flags; /* GNTMAP_* */ + grant_ref_t ref; + domid_t dom; + /* OUT parameters. */ + int16_t status; /* => enum grant_status */ + grant_handle_t handle; + uint64_t dev_bus_addr; +}; +typedef struct gnttab_map_grant_ref gnttab_map_grant_ref_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_map_grant_ref_t); + +/* + * GNTTABOP_unmap_grant_ref: Destroy one or more grant-reference mappings + * tracked by . If or is zero, that + * field is ignored. If non-zero, they must refer to a device/host mapping + * that is tracked by + * NOTES: + * 1. The call may fail in an undefined manner if either mapping is not + * tracked by . + * 3. After executing a batch of unmaps, it is guaranteed that no stale + * mappings will remain in the device or host TLBs. + */ +struct gnttab_unmap_grant_ref { + /* IN parameters. */ + uint64_t host_addr; + uint64_t dev_bus_addr; + grant_handle_t handle; + /* OUT parameters. */ + int16_t status; /* => enum grant_status */ +}; +typedef struct gnttab_unmap_grant_ref gnttab_unmap_grant_ref_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_unmap_grant_ref_t); + +/* + * GNTTABOP_setup_table: Set up a grant table for comprising at least + * pages. The frame addresses are written to the . + * Only addresses are written, even if the table is larger. + * NOTES: + * 1. may be specified as DOMID_SELF. + * 2. Only a sufficiently-privileged domain may specify != DOMID_SELF. + * 3. Xen may not support more than a single grant-table page per domain. + */ +struct gnttab_setup_table { + /* IN parameters. */ + domid_t dom; + uint32_t nr_frames; + /* OUT parameters. */ + int16_t status; /* => enum grant_status */ +#if __XEN_INTERFACE_VERSION__ < 0x00040300 + XEN_GUEST_HANDLE(ulong) frame_list; +#else + XEN_GUEST_HANDLE(xen_pfn_t) frame_list; +#endif +}; +typedef struct gnttab_setup_table gnttab_setup_table_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_setup_table_t); + +/* + * GNTTABOP_dump_table: Dump the contents of the grant table to the + * xen console. Debugging use only. + */ +struct gnttab_dump_table { + /* IN parameters. */ + domid_t dom; + /* OUT parameters. */ + int16_t status; /* => enum grant_status */ +}; +typedef struct gnttab_dump_table gnttab_dump_table_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_dump_table_t); + +/* + * GNTTABOP_transfer_grant_ref: Transfer to a foreign domain. The + * foreign domain has previously registered its interest in the transfer via + * . + * + * Note that, even if the transfer fails, the specified page no longer belongs + * to the calling domain *unless* the error is GNTST_bad_page. + */ +struct gnttab_transfer { + /* IN parameters. */ + xen_pfn_t mfn; + domid_t domid; + grant_ref_t ref; + /* OUT parameters. */ + int16_t status; +}; +typedef struct gnttab_transfer gnttab_transfer_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_transfer_t); + + +/* + * GNTTABOP_copy: Hypervisor based copy + * source and destinations can be eithers MFNs or, for foreign domains, + * grant references. the foreign domain has to grant read/write access + * in its grant table. + * + * The flags specify what type source and destinations are (either MFN + * or grant reference). + * + * Note that this can also be used to copy data between two domains + * via a third party if the source and destination domains had previously + * grant appropriate access to their pages to the third party. + * + * source_offset specifies an offset in the source frame, dest_offset + * the offset in the target frame and len specifies the number of + * bytes to be copied. + */ + +#define _GNTCOPY_source_gref (0) +#define GNTCOPY_source_gref (1<<_GNTCOPY_source_gref) +#define _GNTCOPY_dest_gref (1) +#define GNTCOPY_dest_gref (1<<_GNTCOPY_dest_gref) + +struct gnttab_copy { + /* IN parameters. */ + struct { + union { + grant_ref_t ref; + xen_pfn_t gmfn; + } u; + domid_t domid; + uint16_t offset; + } source, dest; + uint16_t len; + uint16_t flags; /* GNTCOPY_* */ + /* OUT parameters. */ + int16_t status; +}; +typedef struct gnttab_copy gnttab_copy_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_copy_t); + +/* + * GNTTABOP_query_size: Query the current and maximum sizes of the shared + * grant table. + * NOTES: + * 1. may be specified as DOMID_SELF. + * 2. Only a sufficiently-privileged domain may specify != DOMID_SELF. + */ +struct gnttab_query_size { + /* IN parameters. */ + domid_t dom; + /* OUT parameters. */ + uint32_t nr_frames; + uint32_t max_nr_frames; + int16_t status; /* => enum grant_status */ +}; +typedef struct gnttab_query_size gnttab_query_size_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_query_size_t); + +/* + * GNTTABOP_unmap_and_replace: Destroy one or more grant-reference mappings + * tracked by but atomically replace the page table entry with one + * pointing to the machine address under . will be + * redirected to the null entry. + * NOTES: + * 1. The call may fail in an undefined manner if either mapping is not + * tracked by . + * 2. After executing a batch of unmaps, it is guaranteed that no stale + * mappings will remain in the device or host TLBs. + */ +struct gnttab_unmap_and_replace { + /* IN parameters. */ + uint64_t host_addr; + uint64_t new_addr; + grant_handle_t handle; + /* OUT parameters. */ + int16_t status; /* => enum grant_status */ +}; +typedef struct gnttab_unmap_and_replace gnttab_unmap_and_replace_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_unmap_and_replace_t); + +#if __XEN_INTERFACE_VERSION__ >= 0x0003020a +/* + * GNTTABOP_set_version: Request a particular version of the grant + * table shared table structure. This operation can only be performed + * once in any given domain. It must be performed before any grants + * are activated; otherwise, the domain will be stuck with version 1. + * The only defined versions are 1 and 2. + */ +struct gnttab_set_version { + /* IN/OUT parameters */ + uint32_t version; +}; +typedef struct gnttab_set_version gnttab_set_version_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_set_version_t); + + +/* + * GNTTABOP_get_status_frames: Get the list of frames used to store grant + * status for . In grant format version 2, the status is separated + * from the other shared grant fields to allow more efficient synchronization + * using barriers instead of atomic cmpexch operations. + * specify the size of vector . + * The frame addresses are returned in the . + * Only addresses are returned, even if the table is larger. + * NOTES: + * 1. may be specified as DOMID_SELF. + * 2. Only a sufficiently-privileged domain may specify != DOMID_SELF. + */ +struct gnttab_get_status_frames { + /* IN parameters. */ + uint32_t nr_frames; + domid_t dom; + /* OUT parameters. */ + int16_t status; /* => enum grant_status */ + XEN_GUEST_HANDLE(uint64_t) frame_list; +}; +typedef struct gnttab_get_status_frames gnttab_get_status_frames_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_get_status_frames_t); + +/* + * GNTTABOP_get_version: Get the grant table version which is in + * effect for domain . + */ +struct gnttab_get_version { + /* IN parameters */ + domid_t dom; + uint16_t pad; + /* OUT parameters */ + uint32_t version; +}; +typedef struct gnttab_get_version gnttab_get_version_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_get_version_t); + +/* + * GNTTABOP_swap_grant_ref: Swap the contents of two grant entries. + */ +struct gnttab_swap_grant_ref { + /* IN parameters */ + grant_ref_t ref_a; + grant_ref_t ref_b; + /* OUT parameters */ + int16_t status; /* => enum grant_status */ +}; +typedef struct gnttab_swap_grant_ref gnttab_swap_grant_ref_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_swap_grant_ref_t); + +#endif /* __XEN_INTERFACE_VERSION__ */ + +/* + * Bitfield values for gnttab_map_grant_ref.flags. + */ + /* Map the grant entry for access by I/O devices. */ +#define _GNTMAP_device_map (0) +#define GNTMAP_device_map (1<<_GNTMAP_device_map) + /* Map the grant entry for access by host CPUs. */ +#define _GNTMAP_host_map (1) +#define GNTMAP_host_map (1<<_GNTMAP_host_map) + /* Accesses to the granted frame will be restricted to read-only access. */ +#define _GNTMAP_readonly (2) +#define GNTMAP_readonly (1<<_GNTMAP_readonly) + /* + * GNTMAP_host_map subflag: + * 0 => The host mapping is usable only by the guest OS. + * 1 => The host mapping is usable by guest OS + current application. + */ +#define _GNTMAP_application_map (3) +#define GNTMAP_application_map (1<<_GNTMAP_application_map) + + /* + * GNTMAP_contains_pte subflag: + * 0 => This map request contains a host virtual address. + * 1 => This map request contains the machine addess of the PTE to update. + */ +#define _GNTMAP_contains_pte (4) +#define GNTMAP_contains_pte (1<<_GNTMAP_contains_pte) + +#define _GNTMAP_can_fail (5) +#define GNTMAP_can_fail (1<<_GNTMAP_can_fail) + +/* + * Bits to be placed in guest kernel available PTE bits (architecture + * dependent; only supported when XENFEAT_gnttab_map_avail_bits is set). + */ +#define _GNTMAP_guest_avail0 (16) +#define GNTMAP_guest_avail_mask ((uint32_t)~0 << _GNTMAP_guest_avail0) + +/* + * Values for error status returns. All errors are -ve. + */ +/* ` enum grant_status { */ +#define GNTST_okay (0) /* Normal return. */ +#define GNTST_general_error (-1) /* General undefined error. */ +#define GNTST_bad_domain (-2) /* Unrecognsed domain id. */ +#define GNTST_bad_gntref (-3) /* Unrecognised or inappropriate gntref. */ +#define GNTST_bad_handle (-4) /* Unrecognised or inappropriate handle. */ +#define GNTST_bad_virt_addr (-5) /* Inappropriate virtual address to map. */ +#define GNTST_bad_dev_addr (-6) /* Inappropriate device address to unmap.*/ +#define GNTST_no_device_space (-7) /* Out of space in I/O MMU. */ +#define GNTST_permission_denied (-8) /* Not enough privilege for operation. */ +#define GNTST_bad_page (-9) /* Specified page was invalid for op. */ +#define GNTST_bad_copy_arg (-10) /* copy arguments cross page boundary. */ +#define GNTST_address_too_big (-11) /* transfer page address too large. */ +#define GNTST_eagain (-12) /* Operation not done; try again. */ +/* ` } */ + +#define GNTTABOP_error_msgs { \ + "okay", \ + "undefined error", \ + "unrecognised domain id", \ + "invalid grant reference", \ + "invalid mapping handle", \ + "invalid virtual address", \ + "invalid device address", \ + "no spare translation slot in the I/O MMU", \ + "permission denied", \ + "bad page", \ + "copy arguments cross page boundary", \ + "page address size too large", \ + "operation not done; try again" \ +} + +#endif /* __XEN_PUBLIC_GRANT_TABLE_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/roms/ipxe/src/include/xen/hvm/hvm_op.h b/roms/ipxe/src/include/xen/hvm/hvm_op.h new file mode 100644 index 0000000..469ad4f --- /dev/null +++ b/roms/ipxe/src/include/xen/hvm/hvm_op.h @@ -0,0 +1,384 @@ +/* + * 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 __XEN_PUBLIC_HVM_HVM_OP_H__ +#define __XEN_PUBLIC_HVM_HVM_OP_H__ + +FILE_LICENCE ( MIT ); + +#include "../xen.h" +#include "../trace.h" +#include "../event_channel.h" + +/* Get/set subcommands: extra argument == pointer to xen_hvm_param struct. */ +#define HVMOP_set_param 0 +#define HVMOP_get_param 1 +struct xen_hvm_param { + domid_t domid; /* IN */ + uint32_t index; /* IN */ + uint64_t value; /* IN/OUT */ +}; +typedef struct xen_hvm_param xen_hvm_param_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_param_t); + +/* Set the logical level of one of a domain's PCI INTx wires. */ +#define HVMOP_set_pci_intx_level 2 +struct xen_hvm_set_pci_intx_level { + /* Domain to be updated. */ + domid_t domid; + /* PCI INTx identification in PCI topology (domain:bus:device:intx). */ + uint8_t domain, bus, device, intx; + /* Assertion level (0 = unasserted, 1 = asserted). */ + uint8_t level; +}; +typedef struct xen_hvm_set_pci_intx_level xen_hvm_set_pci_intx_level_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_set_pci_intx_level_t); + +/* Set the logical level of one of a domain's ISA IRQ wires. */ +#define HVMOP_set_isa_irq_level 3 +struct xen_hvm_set_isa_irq_level { + /* Domain to be updated. */ + domid_t domid; + /* ISA device identification, by ISA IRQ (0-15). */ + uint8_t isa_irq; + /* Assertion level (0 = unasserted, 1 = asserted). */ + uint8_t level; +}; +typedef struct xen_hvm_set_isa_irq_level xen_hvm_set_isa_irq_level_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_set_isa_irq_level_t); + +#define HVMOP_set_pci_link_route 4 +struct xen_hvm_set_pci_link_route { + /* Domain to be updated. */ + domid_t domid; + /* PCI link identifier (0-3). */ + uint8_t link; + /* ISA IRQ (1-15), or 0 (disable link). */ + uint8_t isa_irq; +}; +typedef struct xen_hvm_set_pci_link_route xen_hvm_set_pci_link_route_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_set_pci_link_route_t); + +/* Flushes all VCPU TLBs: @arg must be NULL. */ +#define HVMOP_flush_tlbs 5 + +typedef enum { + HVMMEM_ram_rw, /* Normal read/write guest RAM */ + HVMMEM_ram_ro, /* Read-only; writes are discarded */ + HVMMEM_mmio_dm, /* Reads and write go to the device model */ +} hvmmem_type_t; + +/* Following tools-only interfaces may change in future. */ +#if defined(__XEN__) || defined(__XEN_TOOLS__) + +/* Track dirty VRAM. */ +#define HVMOP_track_dirty_vram 6 +struct xen_hvm_track_dirty_vram { + /* Domain to be tracked. */ + domid_t domid; + /* Number of pages to track. */ + uint32_t nr; + /* First pfn to track. */ + uint64_aligned_t first_pfn; + /* OUT variable. */ + /* Dirty bitmap buffer. */ + XEN_GUEST_HANDLE_64(uint8) dirty_bitmap; +}; +typedef struct xen_hvm_track_dirty_vram xen_hvm_track_dirty_vram_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_track_dirty_vram_t); + +/* Notify that some pages got modified by the Device Model. */ +#define HVMOP_modified_memory 7 +struct xen_hvm_modified_memory { + /* Domain to be updated. */ + domid_t domid; + /* Number of pages. */ + uint32_t nr; + /* First pfn. */ + uint64_aligned_t first_pfn; +}; +typedef struct xen_hvm_modified_memory xen_hvm_modified_memory_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_modified_memory_t); + +#define HVMOP_set_mem_type 8 +/* Notify that a region of memory is to be treated in a specific way. */ +struct xen_hvm_set_mem_type { + /* Domain to be updated. */ + domid_t domid; + /* Memory type */ + uint16_t hvmmem_type; + /* Number of pages. */ + uint32_t nr; + /* First pfn. */ + uint64_aligned_t first_pfn; +}; +typedef struct xen_hvm_set_mem_type xen_hvm_set_mem_type_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_set_mem_type_t); + +#endif /* defined(__XEN__) || defined(__XEN_TOOLS__) */ + +/* Hint from PV drivers for pagetable destruction. */ +#define HVMOP_pagetable_dying 9 +struct xen_hvm_pagetable_dying { + /* Domain with a pagetable about to be destroyed. */ + domid_t domid; + uint16_t pad[3]; /* align next field on 8-byte boundary */ + /* guest physical address of the toplevel pagetable dying */ + uint64_t gpa; +}; +typedef struct xen_hvm_pagetable_dying xen_hvm_pagetable_dying_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_pagetable_dying_t); + +/* Get the current Xen time, in nanoseconds since system boot. */ +#define HVMOP_get_time 10 +struct xen_hvm_get_time { + uint64_t now; /* OUT */ +}; +typedef struct xen_hvm_get_time xen_hvm_get_time_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_get_time_t); + +#define HVMOP_xentrace 11 +struct xen_hvm_xentrace { + uint16_t event, extra_bytes; + uint8_t extra[TRACE_EXTRA_MAX * sizeof(uint32_t)]; +}; +typedef struct xen_hvm_xentrace xen_hvm_xentrace_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_xentrace_t); + +/* Following tools-only interfaces may change in future. */ +#if defined(__XEN__) || defined(__XEN_TOOLS__) + +/* Deprecated by XENMEM_access_op_set_access */ +#define HVMOP_set_mem_access 12 + +/* Deprecated by XENMEM_access_op_get_access */ +#define HVMOP_get_mem_access 13 + +#define HVMOP_inject_trap 14 +/* Inject a trap into a VCPU, which will get taken up on the next + * scheduling of it. Note that the caller should know enough of the + * state of the CPU before injecting, to know what the effect of + * injecting the trap will be. + */ +struct xen_hvm_inject_trap { + /* Domain to be queried. */ + domid_t domid; + /* VCPU */ + uint32_t vcpuid; + /* Vector number */ + uint32_t vector; + /* Trap type (HVMOP_TRAP_*) */ + uint32_t type; +/* NB. This enumeration precisely matches hvm.h:X86_EVENTTYPE_* */ +# define HVMOP_TRAP_ext_int 0 /* external interrupt */ +# define HVMOP_TRAP_nmi 2 /* nmi */ +# define HVMOP_TRAP_hw_exc 3 /* hardware exception */ +# define HVMOP_TRAP_sw_int 4 /* software interrupt (CD nn) */ +# define HVMOP_TRAP_pri_sw_exc 5 /* ICEBP (F1) */ +# define HVMOP_TRAP_sw_exc 6 /* INT3 (CC), INTO (CE) */ + /* Error code, or ~0u to skip */ + uint32_t error_code; + /* Intruction length */ + uint32_t insn_len; + /* CR2 for page faults */ + uint64_aligned_t cr2; +}; +typedef struct xen_hvm_inject_trap xen_hvm_inject_trap_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_inject_trap_t); + +#endif /* defined(__XEN__) || defined(__XEN_TOOLS__) */ + +#define HVMOP_get_mem_type 15 +/* Return hvmmem_type_t for the specified pfn. */ +struct xen_hvm_get_mem_type { + /* Domain to be queried. */ + domid_t domid; + /* OUT variable. */ + uint16_t mem_type; + uint16_t pad[2]; /* align next field on 8-byte boundary */ + /* IN variable. */ + uint64_t pfn; +}; +typedef struct xen_hvm_get_mem_type xen_hvm_get_mem_type_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_get_mem_type_t); + +/* Following tools-only interfaces may change in future. */ +#if defined(__XEN__) || defined(__XEN_TOOLS__) + +/* MSI injection for emulated devices */ +#define HVMOP_inject_msi 16 +struct xen_hvm_inject_msi { + /* Domain to be injected */ + domid_t domid; + /* Data -- lower 32 bits */ + uint32_t data; + /* Address (0xfeexxxxx) */ + uint64_t addr; +}; +typedef struct xen_hvm_inject_msi xen_hvm_inject_msi_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_inject_msi_t); + +/* + * IOREQ Servers + * + * The interface between an I/O emulator an Xen is called an IOREQ Server. + * A domain supports a single 'legacy' IOREQ Server which is instantiated if + * parameter... + * + * HVM_PARAM_IOREQ_PFN is read (to get the gmfn containing the synchronous + * ioreq structures), or... + * HVM_PARAM_BUFIOREQ_PFN is read (to get the gmfn containing the buffered + * ioreq ring), or... + * HVM_PARAM_BUFIOREQ_EVTCHN is read (to get the event channel that Xen uses + * to request buffered I/O emulation). + * + * The following hypercalls facilitate the creation of IOREQ Servers for + * 'secondary' emulators which are invoked to implement port I/O, memory, or + * PCI config space ranges which they explicitly register. + */ + +typedef uint16_t ioservid_t; + +/* + * HVMOP_create_ioreq_server: Instantiate a new IOREQ Server for a secondary + * emulator servicing domain . + * + * The handed back is unique for . If is zero + * the buffered ioreq ring will not be allocated and hence all emulation + * requestes to this server will be synchronous. + */ +#define HVMOP_create_ioreq_server 17 +struct xen_hvm_create_ioreq_server { + domid_t domid; /* IN - domain to be serviced */ + uint8_t handle_bufioreq; /* IN - should server handle buffered ioreqs */ + ioservid_t id; /* OUT - server id */ +}; +typedef struct xen_hvm_create_ioreq_server xen_hvm_create_ioreq_server_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_create_ioreq_server_t); + +/* + * HVMOP_get_ioreq_server_info: Get all the information necessary to access + * IOREQ Server . + * + * The emulator needs to map the synchronous ioreq structures and buffered + * ioreq ring (if it exists) that Xen uses to request emulation. These are + * hosted in domain 's gmfns and + * respectively. In addition, if the IOREQ Server is handling buffered + * emulation requests, the emulator needs to bind to event channel + * to listen for them. (The event channels used for + * synchronous emulation requests are specified in the per-CPU ioreq + * structures in ). + * If the IOREQ Server is not handling buffered emulation requests then the + * values handed back in and will both be 0. + */ +#define HVMOP_get_ioreq_server_info 18 +struct xen_hvm_get_ioreq_server_info { + domid_t domid; /* IN - domain to be serviced */ + ioservid_t id; /* IN - server id */ + evtchn_port_t bufioreq_port; /* OUT - buffered ioreq port */ + uint64_aligned_t ioreq_pfn; /* OUT - sync ioreq pfn */ + uint64_aligned_t bufioreq_pfn; /* OUT - buffered ioreq pfn */ +}; +typedef struct xen_hvm_get_ioreq_server_info xen_hvm_get_ioreq_server_info_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_get_ioreq_server_info_t); + +/* + * HVM_map_io_range_to_ioreq_server: Register an I/O range of domain + * for emulation by the client of IOREQ + * Server + * HVM_unmap_io_range_from_ioreq_server: Deregister an I/O range of + * for emulation by the client of IOREQ + * Server + * + * There are three types of I/O that can be emulated: port I/O, memory accesses + * and PCI config space accesses. The field denotes which type of range + * the and (inclusive) fields are specifying. + * PCI config space ranges are specified by segment/bus/device/function values + * which should be encoded using the HVMOP_PCI_SBDF helper macro below. + * + * NOTE: unless an emulation request falls entirely within a range mapped + * by a secondary emulator, it will not be passed to that emulator. + */ +#define HVMOP_map_io_range_to_ioreq_server 19 +#define HVMOP_unmap_io_range_from_ioreq_server 20 +struct xen_hvm_io_range { + domid_t domid; /* IN - domain to be serviced */ + ioservid_t id; /* IN - server id */ + uint32_t type; /* IN - type of range */ +# define HVMOP_IO_RANGE_PORT 0 /* I/O port range */ +# define HVMOP_IO_RANGE_MEMORY 1 /* MMIO range */ +# define HVMOP_IO_RANGE_PCI 2 /* PCI segment/bus/dev/func range */ + uint64_aligned_t start, end; /* IN - inclusive start and end of range */ +}; +typedef struct xen_hvm_io_range xen_hvm_io_range_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_io_range_t); + +#define HVMOP_PCI_SBDF(s,b,d,f) \ + ((((s) & 0xffff) << 16) | \ + (((b) & 0xff) << 8) | \ + (((d) & 0x1f) << 3) | \ + ((f) & 0x07)) + +/* + * HVMOP_destroy_ioreq_server: Destroy the IOREQ Server servicing domain + * . + * + * Any registered I/O ranges will be automatically deregistered. + */ +#define HVMOP_destroy_ioreq_server 21 +struct xen_hvm_destroy_ioreq_server { + domid_t domid; /* IN - domain to be serviced */ + ioservid_t id; /* IN - server id */ +}; +typedef struct xen_hvm_destroy_ioreq_server xen_hvm_destroy_ioreq_server_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_destroy_ioreq_server_t); + +/* + * HVMOP_set_ioreq_server_state: Enable or disable the IOREQ Server servicing + * domain . + * + * The IOREQ Server will not be passed any emulation requests until it is in the + * enabled state. + * Note that the contents of the ioreq_pfn and bufioreq_fn (see + * HVMOP_get_ioreq_server_info) are not meaningful until the IOREQ Server is in + * the enabled state. + */ +#define HVMOP_set_ioreq_server_state 22 +struct xen_hvm_set_ioreq_server_state { + domid_t domid; /* IN - domain to be serviced */ + ioservid_t id; /* IN - server id */ + uint8_t enabled; /* IN - enabled? */ +}; +typedef struct xen_hvm_set_ioreq_server_state xen_hvm_set_ioreq_server_state_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_set_ioreq_server_state_t); + +#endif /* defined(__XEN__) || defined(__XEN_TOOLS__) */ + +#endif /* __XEN_PUBLIC_HVM_HVM_OP_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/roms/ipxe/src/include/xen/hvm/params.h b/roms/ipxe/src/include/xen/hvm/params.h new file mode 100644 index 0000000..49e0658 --- /dev/null +++ b/roms/ipxe/src/include/xen/hvm/params.h @@ -0,0 +1,158 @@ +/* + * 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 __XEN_PUBLIC_HVM_PARAMS_H__ +#define __XEN_PUBLIC_HVM_PARAMS_H__ + +FILE_LICENCE ( MIT ); + +#include "hvm_op.h" + +/* + * Parameter space for HVMOP_{set,get}_param. + */ + +/* + * How should CPU0 event-channel notifications be delivered? + * val[63:56] == 0: val[55:0] is a delivery GSI (Global System Interrupt). + * val[63:56] == 1: val[55:0] is a delivery PCI INTx line, as follows: + * Domain = val[47:32], Bus = val[31:16], + * DevFn = val[15: 8], IntX = val[ 1: 0] + * val[63:56] == 2: val[7:0] is a vector number, check for + * XENFEAT_hvm_callback_vector to know if this delivery + * method is available. + * If val == 0 then CPU0 event-channel notifications are not delivered. + */ +#define HVM_PARAM_CALLBACK_IRQ 0 + +/* + * These are not used by Xen. They are here for convenience of HVM-guest + * xenbus implementations. + */ +#define HVM_PARAM_STORE_PFN 1 +#define HVM_PARAM_STORE_EVTCHN 2 + +#define HVM_PARAM_PAE_ENABLED 4 + +#define HVM_PARAM_IOREQ_PFN 5 + +#define HVM_PARAM_BUFIOREQ_PFN 6 +#define HVM_PARAM_BUFIOREQ_EVTCHN 26 + +#if defined(__i386__) || defined(__x86_64__) + +/* Expose Viridian interfaces to this HVM guest? */ +#define HVM_PARAM_VIRIDIAN 9 + +#endif + +/* + * Set mode for virtual timers (currently x86 only): + * delay_for_missed_ticks (default): + * Do not advance a vcpu's time beyond the correct delivery time for + * interrupts that have been missed due to preemption. Deliver missed + * interrupts when the vcpu is rescheduled and advance the vcpu's virtual + * time stepwise for each one. + * no_delay_for_missed_ticks: + * As above, missed interrupts are delivered, but guest time always tracks + * wallclock (i.e., real) time while doing so. + * no_missed_ticks_pending: + * No missed interrupts are held pending. Instead, to ensure ticks are + * delivered at some non-zero rate, if we detect missed ticks then the + * internal tick alarm is not disabled if the VCPU is preempted during the + * next tick period. + * one_missed_tick_pending: + * Missed interrupts are collapsed together and delivered as one 'late tick'. + * Guest time always tracks wallclock (i.e., real) time. + */ +#define HVM_PARAM_TIMER_MODE 10 +#define HVMPTM_delay_for_missed_ticks 0 +#define HVMPTM_no_delay_for_missed_ticks 1 +#define HVMPTM_no_missed_ticks_pending 2 +#define HVMPTM_one_missed_tick_pending 3 + +/* Boolean: Enable virtual HPET (high-precision event timer)? (x86-only) */ +#define HVM_PARAM_HPET_ENABLED 11 + +/* Identity-map page directory used by Intel EPT when CR0.PG=0. */ +#define HVM_PARAM_IDENT_PT 12 + +/* Device Model domain, defaults to 0. */ +#define HVM_PARAM_DM_DOMAIN 13 + +/* ACPI S state: currently support S0 and S3 on x86. */ +#define HVM_PARAM_ACPI_S_STATE 14 + +/* TSS used on Intel when CR0.PE=0. */ +#define HVM_PARAM_VM86_TSS 15 + +/* Boolean: Enable aligning all periodic vpts to reduce interrupts */ +#define HVM_PARAM_VPT_ALIGN 16 + +/* Console debug shared memory ring and event channel */ +#define HVM_PARAM_CONSOLE_PFN 17 +#define HVM_PARAM_CONSOLE_EVTCHN 18 + +/* + * Select location of ACPI PM1a and TMR control blocks. Currently two locations + * are supported, specified by version 0 or 1 in this parameter: + * - 0: default, use the old addresses + * PM1A_EVT == 0x1f40; PM1A_CNT == 0x1f44; PM_TMR == 0x1f48 + * - 1: use the new default qemu addresses + * PM1A_EVT == 0xb000; PM1A_CNT == 0xb004; PM_TMR == 0xb008 + * You can find these address definitions in + */ +#define HVM_PARAM_ACPI_IOPORTS_LOCATION 19 + +/* Enable blocking memory events, async or sync (pause vcpu until response) + * onchangeonly indicates messages only on a change of value */ +#define HVM_PARAM_MEMORY_EVENT_CR0 20 +#define HVM_PARAM_MEMORY_EVENT_CR3 21 +#define HVM_PARAM_MEMORY_EVENT_CR4 22 +#define HVM_PARAM_MEMORY_EVENT_INT3 23 +#define HVM_PARAM_MEMORY_EVENT_SINGLE_STEP 25 +#define HVM_PARAM_MEMORY_EVENT_MSR 30 + +#define HVMPME_MODE_MASK (3 << 0) +#define HVMPME_mode_disabled 0 +#define HVMPME_mode_async 1 +#define HVMPME_mode_sync 2 +#define HVMPME_onchangeonly (1 << 2) + +/* Boolean: Enable nestedhvm (hvm only) */ +#define HVM_PARAM_NESTEDHVM 24 + +/* Params for the mem event rings */ +#define HVM_PARAM_PAGING_RING_PFN 27 +#define HVM_PARAM_ACCESS_RING_PFN 28 +#define HVM_PARAM_SHARING_RING_PFN 29 + +/* SHUTDOWN_* action in case of a triple fault */ +#define HVM_PARAM_TRIPLE_FAULT_REASON 31 + +#define HVM_PARAM_IOREQ_SERVER_PFN 32 +#define HVM_PARAM_NR_IOREQ_SERVER_PAGES 33 + +/* Location of the VM Generation ID in guest physical address space. */ +#define HVM_PARAM_VM_GENERATION_ID_ADDR 34 + +#define HVM_NR_PARAMS 35 + +#endif /* __XEN_PUBLIC_HVM_PARAMS_H__ */ diff --git a/roms/ipxe/src/include/xen/import.pl b/roms/ipxe/src/include/xen/import.pl new file mode 100755 index 0000000..9f09a77 --- /dev/null +++ b/roms/ipxe/src/include/xen/import.pl @@ -0,0 +1,116 @@ +#!/usr/bin/perl -w + +=head1 NAME + +import.pl + +=head1 SYNOPSIS + +import.pl [options] /path/to/xen + +Options: + + -h,--help Display brief help message + -v,--verbose Increase verbosity + -q,--quiet Decrease verbosity + +=cut + +use File::Spec::Functions qw ( :ALL ); +use File::Find; +use File::Path; +use Getopt::Long; +use Pod::Usage; +use FindBin; +use strict; +use warnings; + +my $verbosity = 0; + +sub try_import_file { + my $ipxedir = shift; + my $xendir = shift; + my $filename = shift; + + # Skip everything except headers + return unless $filename =~ /\.h$/; + + # Search for importable header + ( undef, my $subdir, undef ) = splitpath ( $filename ); + my $outfile = catfile ( $ipxedir, $filename ); + my $infile = catfile ( $xendir, "xen/include/public", $filename ); + die "$infile does not exist\n" unless -e $infile; + + # Import header file + print "$filename <- ".catfile ( $xendir, $filename )."\n" + if $verbosity >= 1; + open my $infh, "<", $infile or die "Could not open $infile: $!\n"; + mkpath ( catdir ( $xendir, $subdir ) ); + open my $outfh, ">", $outfile or die "Could not open $outfile: $!\n"; + my @dependencies = (); + my $maybe_guard; + my $guard; + while ( <$infh> ) { + # Strip CR and trailing whitespace + s/\r//g; + s/\s*$//g; + chomp; + # Update include lines, and record included files + if ( /^\#include\s+[<\"](\S+)[>\"]/ ) { + push @dependencies, catfile ( $subdir, $1 ); + } + # Write out line + print $outfh "$_\n"; + # Apply FILE_LICENCE() immediately after include guard + if ( defined $maybe_guard ) { + if ( /^\#define\s+_+${maybe_guard}_H_*$/ ) { + die "Duplicate header guard detected in $infile\n" if $guard; + $guard = $maybe_guard; + print $outfh "\nFILE_LICENCE ( MIT );\n"; + } + undef $maybe_guard; + } + if ( /^#ifndef\s+_+(\S+)_H_*$/ ) { + $maybe_guard = $1; + } + } + close $outfh; + close $infh; + # Warn if no header guard was detected + warn "Cannot detect header guard in $infile\n" unless $guard; + # Recurse to handle any included files that we don't already have + foreach my $dependency ( @dependencies ) { + if ( ! -e catfile ( $ipxedir, $dependency ) ) { + print "...following dependency on $dependency\n" if $verbosity >= 1; + try_import_file ( $ipxedir, $xendir, $dependency ); + } + } + return; +} + +# Parse command-line options +Getopt::Long::Configure ( 'bundling', 'auto_abbrev' ); +GetOptions ( + 'verbose|v+' => sub { $verbosity++; }, + 'quiet|q+' => sub { $verbosity--; }, + 'help|h' => sub { pod2usage ( 1 ); }, +) or die "Could not parse command-line options\n"; +pod2usage ( 1 ) unless @ARGV == 1; +my $xendir = shift; + +# Identify Xen import directory +die "Directory \"$xendir\" does not appear to contain the Xen source tree\n" + unless -e catfile ( $xendir, "xen/include/public/xen.h" ); + +# Identify iPXE Xen includes directory +my $ipxedir = $FindBin::Bin; +die "Directory \"$ipxedir\" does not appear to contain the iPXE Xen includes\n" + unless -e catfile ( $ipxedir, "../../include/ipxe" ); + +print "Importing Xen headers into $ipxedir\nfrom $xendir\n" + if $verbosity >= 1; + +# Import headers +find ( { wanted => sub { + try_import_file ( $ipxedir, $xendir, abs2rel ( $_, $ipxedir ) ); +}, no_chdir => 1 }, $ipxedir ); diff --git a/roms/ipxe/src/include/xen/io/netif.h b/roms/ipxe/src/include/xen/io/netif.h new file mode 100644 index 0000000..ae12eab --- /dev/null +++ b/roms/ipxe/src/include/xen/io/netif.h @@ -0,0 +1,307 @@ +/****************************************************************************** + * netif.h + * + * Unified network-device I/O interface for Xen guest OSes. + * + * 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. + * + * Copyright (c) 2003-2004, Keir Fraser + */ + +#ifndef __XEN_PUBLIC_IO_NETIF_H__ +#define __XEN_PUBLIC_IO_NETIF_H__ + +FILE_LICENCE ( MIT ); + +#include "ring.h" +#include "../grant_table.h" + +/* + * Older implementation of Xen network frontend / backend has an + * implicit dependency on the MAX_SKB_FRAGS as the maximum number of + * ring slots a skb can use. Netfront / netback may not work as + * expected when frontend and backend have different MAX_SKB_FRAGS. + * + * A better approach is to add mechanism for netfront / netback to + * negotiate this value. However we cannot fix all possible + * frontends, so we need to define a value which states the minimum + * slots backend must support. + * + * The minimum value derives from older Linux kernel's MAX_SKB_FRAGS + * (18), which is proved to work with most frontends. Any new backend + * which doesn't negotiate with frontend should expect frontend to + * send a valid packet using slots up to this value. + */ +#define XEN_NETIF_NR_SLOTS_MIN 18 + +/* + * Notifications after enqueuing any type of message should be conditional on + * the appropriate req_event or rsp_event field in the shared ring. + * If the client sends notification for rx requests then it should specify + * feature 'feature-rx-notify' via xenbus. Otherwise the backend will assume + * that it cannot safely queue packets (as it may not be kicked to send them). + */ + +/* + * "feature-split-event-channels" is introduced to separate guest TX + * and RX notification. Backend either doesn't support this feature or + * advertises it via xenstore as 0 (disabled) or 1 (enabled). + * + * To make use of this feature, frontend should allocate two event + * channels for TX and RX, advertise them to backend as + * "event-channel-tx" and "event-channel-rx" respectively. If frontend + * doesn't want to use this feature, it just writes "event-channel" + * node as before. + */ + +/* + * Multiple transmit and receive queues: + * If supported, the backend will write the key "multi-queue-max-queues" to + * the directory for that vif, and set its value to the maximum supported + * number of queues. + * Frontends that are aware of this feature and wish to use it can write the + * key "multi-queue-num-queues", set to the number they wish to use, which + * must be greater than zero, and no more than the value reported by the backend + * in "multi-queue-max-queues". + * + * Queues replicate the shared rings and event channels. + * "feature-split-event-channels" may optionally be used when using + * multiple queues, but is not mandatory. + * + * Each queue consists of one shared ring pair, i.e. there must be the same + * number of tx and rx rings. + * + * For frontends requesting just one queue, the usual event-channel and + * ring-ref keys are written as before, simplifying the backend processing + * to avoid distinguishing between a frontend that doesn't understand the + * multi-queue feature, and one that does, but requested only one queue. + * + * Frontends requesting two or more queues must not write the toplevel + * event-channel (or event-channel-{tx,rx}) and {tx,rx}-ring-ref keys, + * instead writing those keys under sub-keys having the name "queue-N" where + * N is the integer ID of the queue for which those keys belong. Queues + * are indexed from zero. For example, a frontend with two queues and split + * event channels must write the following set of queue-related keys: + * + * /local/domain/1/device/vif/0/multi-queue-num-queues = "2" + * /local/domain/1/device/vif/0/queue-0 = "" + * /local/domain/1/device/vif/0/queue-0/tx-ring-ref = "" + * /local/domain/1/device/vif/0/queue-0/rx-ring-ref = "" + * /local/domain/1/device/vif/0/queue-0/event-channel-tx = "" + * /local/domain/1/device/vif/0/queue-0/event-channel-rx = "" + * /local/domain/1/device/vif/0/queue-1 = "" + * /local/domain/1/device/vif/0/queue-1/tx-ring-ref = "" + * /local/domain/1/device/vif/0/queue-1/rx-ring-ref = ">2)<<2 : __RD2(_x)) +#define __RD8(_x) (((_x) & 0x000000f0) ? __RD4((_x)>>4)<<4 : __RD4(_x)) +#define __RD16(_x) (((_x) & 0x0000ff00) ? __RD8((_x)>>8)<<8 : __RD8(_x)) +#define __RD32(_x) (((_x) & 0xffff0000) ? __RD16((_x)>>16)<<16 : __RD16(_x)) + +/* + * Calculate size of a shared ring, given the total available space for the + * ring and indexes (_sz), and the name tag of the request/response structure. + * A ring contains as many entries as will fit, rounded down to the nearest + * power of two (so we can mask with (size-1) to loop around). + */ +#define __CONST_RING_SIZE(_s, _sz) \ + (__RD32(((_sz) - offsetof(struct _s##_sring, ring)) / \ + sizeof(((struct _s##_sring *)0)->ring[0]))) +/* + * The same for passing in an actual pointer instead of a name tag. + */ +#define __RING_SIZE(_s, _sz) \ + (__RD32(((_sz) - (long)(_s)->ring + (long)(_s)) / sizeof((_s)->ring[0]))) + +/* + * Macros to make the correct C datatypes for a new kind of ring. + * + * To make a new ring datatype, you need to have two message structures, + * let's say request_t, and response_t already defined. + * + * In a header where you want the ring datatype declared, you then do: + * + * DEFINE_RING_TYPES(mytag, request_t, response_t); + * + * These expand out to give you a set of types, as you can see below. + * The most important of these are: + * + * mytag_sring_t - The shared ring. + * mytag_front_ring_t - The 'front' half of the ring. + * mytag_back_ring_t - The 'back' half of the ring. + * + * To initialize a ring in your code you need to know the location and size + * of the shared memory area (PAGE_SIZE, for instance). To initialise + * the front half: + * + * mytag_front_ring_t front_ring; + * SHARED_RING_INIT((mytag_sring_t *)shared_page); + * FRONT_RING_INIT(&front_ring, (mytag_sring_t *)shared_page, PAGE_SIZE); + * + * Initializing the back follows similarly (note that only the front + * initializes the shared ring): + * + * mytag_back_ring_t back_ring; + * BACK_RING_INIT(&back_ring, (mytag_sring_t *)shared_page, PAGE_SIZE); + */ + +#define DEFINE_RING_TYPES(__name, __req_t, __rsp_t) \ + \ +/* Shared ring entry */ \ +union __name##_sring_entry { \ + __req_t req; \ + __rsp_t rsp; \ +}; \ + \ +/* Shared ring page */ \ +struct __name##_sring { \ + RING_IDX req_prod, req_event; \ + RING_IDX rsp_prod, rsp_event; \ + union { \ + struct { \ + uint8_t smartpoll_active; \ + } netif; \ + struct { \ + uint8_t msg; \ + } tapif_user; \ + uint8_t pvt_pad[4]; \ + } private; \ + uint8_t __pad[44]; \ + union __name##_sring_entry ring[1]; /* variable-length */ \ +}; \ + \ +/* "Front" end's private variables */ \ +struct __name##_front_ring { \ + RING_IDX req_prod_pvt; \ + RING_IDX rsp_cons; \ + unsigned int nr_ents; \ + struct __name##_sring *sring; \ +}; \ + \ +/* "Back" end's private variables */ \ +struct __name##_back_ring { \ + RING_IDX rsp_prod_pvt; \ + RING_IDX req_cons; \ + unsigned int nr_ents; \ + struct __name##_sring *sring; \ +}; \ + \ +/* Syntactic sugar */ \ +typedef struct __name##_sring __name##_sring_t; \ +typedef struct __name##_front_ring __name##_front_ring_t; \ +typedef struct __name##_back_ring __name##_back_ring_t + +/* + * Macros for manipulating rings. + * + * FRONT_RING_whatever works on the "front end" of a ring: here + * requests are pushed on to the ring and responses taken off it. + * + * BACK_RING_whatever works on the "back end" of a ring: here + * requests are taken off the ring and responses put on. + * + * N.B. these macros do NO INTERLOCKS OR FLOW CONTROL. + * This is OK in 1-for-1 request-response situations where the + * requestor (front end) never has more than RING_SIZE()-1 + * outstanding requests. + */ + +/* Initialising empty rings */ +#define SHARED_RING_INIT(_s) do { \ + (_s)->req_prod = (_s)->rsp_prod = 0; \ + (_s)->req_event = (_s)->rsp_event = 1; \ + (void)memset((_s)->private.pvt_pad, 0, sizeof((_s)->private.pvt_pad)); \ + (void)memset((_s)->__pad, 0, sizeof((_s)->__pad)); \ +} while(0) + +#define FRONT_RING_INIT(_r, _s, __size) do { \ + (_r)->req_prod_pvt = 0; \ + (_r)->rsp_cons = 0; \ + (_r)->nr_ents = __RING_SIZE(_s, __size); \ + (_r)->sring = (_s); \ +} while (0) + +#define BACK_RING_INIT(_r, _s, __size) do { \ + (_r)->rsp_prod_pvt = 0; \ + (_r)->req_cons = 0; \ + (_r)->nr_ents = __RING_SIZE(_s, __size); \ + (_r)->sring = (_s); \ +} while (0) + +/* How big is this ring? */ +#define RING_SIZE(_r) \ + ((_r)->nr_ents) + +/* Number of free requests (for use on front side only). */ +#define RING_FREE_REQUESTS(_r) \ + (RING_SIZE(_r) - ((_r)->req_prod_pvt - (_r)->rsp_cons)) + +/* Test if there is an empty slot available on the front ring. + * (This is only meaningful from the front. ) + */ +#define RING_FULL(_r) \ + (RING_FREE_REQUESTS(_r) == 0) + +/* Test if there are outstanding messages to be processed on a ring. */ +#define RING_HAS_UNCONSUMED_RESPONSES(_r) \ + ((_r)->sring->rsp_prod - (_r)->rsp_cons) + +#ifdef __GNUC__ +#define RING_HAS_UNCONSUMED_REQUESTS(_r) ({ \ + unsigned int req = (_r)->sring->req_prod - (_r)->req_cons; \ + unsigned int rsp = RING_SIZE(_r) - \ + ((_r)->req_cons - (_r)->rsp_prod_pvt); \ + req < rsp ? req : rsp; \ +}) +#else +/* Same as above, but without the nice GCC ({ ... }) syntax. */ +#define RING_HAS_UNCONSUMED_REQUESTS(_r) \ + ((((_r)->sring->req_prod - (_r)->req_cons) < \ + (RING_SIZE(_r) - ((_r)->req_cons - (_r)->rsp_prod_pvt))) ? \ + ((_r)->sring->req_prod - (_r)->req_cons) : \ + (RING_SIZE(_r) - ((_r)->req_cons - (_r)->rsp_prod_pvt))) +#endif + +/* Direct access to individual ring elements, by index. */ +#define RING_GET_REQUEST(_r, _idx) \ + (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].req)) + +#define RING_GET_RESPONSE(_r, _idx) \ + (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].rsp)) + +/* Loop termination condition: Would the specified index overflow the ring? */ +#define RING_REQUEST_CONS_OVERFLOW(_r, _cons) \ + (((_cons) - (_r)->rsp_prod_pvt) >= RING_SIZE(_r)) + +/* Ill-behaved frontend determination: Can there be this many requests? */ +#define RING_REQUEST_PROD_OVERFLOW(_r, _prod) \ + (((_prod) - (_r)->rsp_prod_pvt) > RING_SIZE(_r)) + +#define RING_PUSH_REQUESTS(_r) do { \ + xen_wmb(); /* back sees requests /before/ updated producer index */ \ + (_r)->sring->req_prod = (_r)->req_prod_pvt; \ +} while (0) + +#define RING_PUSH_RESPONSES(_r) do { \ + xen_wmb(); /* front sees resps /before/ updated producer index */ \ + (_r)->sring->rsp_prod = (_r)->rsp_prod_pvt; \ +} while (0) + +/* + * Notification hold-off (req_event and rsp_event): + * + * When queueing requests or responses on a shared ring, it may not always be + * necessary to notify the remote end. For example, if requests are in flight + * in a backend, the front may be able to queue further requests without + * notifying the back (if the back checks for new requests when it queues + * responses). + * + * When enqueuing requests or responses: + * + * Use RING_PUSH_{REQUESTS,RESPONSES}_AND_CHECK_NOTIFY(). The second argument + * is a boolean return value. True indicates that the receiver requires an + * asynchronous notification. + * + * After dequeuing requests or responses (before sleeping the connection): + * + * Use RING_FINAL_CHECK_FOR_REQUESTS() or RING_FINAL_CHECK_FOR_RESPONSES(). + * The second argument is a boolean return value. True indicates that there + * are pending messages on the ring (i.e., the connection should not be put + * to sleep). + * + * These macros will set the req_event/rsp_event field to trigger a + * notification on the very next message that is enqueued. If you want to + * create batches of work (i.e., only receive a notification after several + * messages have been enqueued) then you will need to create a customised + * version of the FINAL_CHECK macro in your own code, which sets the event + * field appropriately. + */ + +#define RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(_r, _notify) do { \ + RING_IDX __old = (_r)->sring->req_prod; \ + RING_IDX __new = (_r)->req_prod_pvt; \ + xen_wmb(); /* back sees requests /before/ updated producer index */ \ + (_r)->sring->req_prod = __new; \ + xen_mb(); /* back sees new requests /before/ we check req_event */ \ + (_notify) = ((RING_IDX)(__new - (_r)->sring->req_event) < \ + (RING_IDX)(__new - __old)); \ +} while (0) + +#define RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(_r, _notify) do { \ + RING_IDX __old = (_r)->sring->rsp_prod; \ + RING_IDX __new = (_r)->rsp_prod_pvt; \ + xen_wmb(); /* front sees resps /before/ updated producer index */ \ + (_r)->sring->rsp_prod = __new; \ + xen_mb(); /* front sees new resps /before/ we check rsp_event */ \ + (_notify) = ((RING_IDX)(__new - (_r)->sring->rsp_event) < \ + (RING_IDX)(__new - __old)); \ +} while (0) + +#define RING_FINAL_CHECK_FOR_REQUESTS(_r, _work_to_do) do { \ + (_work_to_do) = RING_HAS_UNCONSUMED_REQUESTS(_r); \ + if (_work_to_do) break; \ + (_r)->sring->req_event = (_r)->req_cons + 1; \ + xen_mb(); \ + (_work_to_do) = RING_HAS_UNCONSUMED_REQUESTS(_r); \ +} while (0) + +#define RING_FINAL_CHECK_FOR_RESPONSES(_r, _work_to_do) do { \ + (_work_to_do) = RING_HAS_UNCONSUMED_RESPONSES(_r); \ + if (_work_to_do) break; \ + (_r)->sring->rsp_event = (_r)->rsp_cons + 1; \ + xen_mb(); \ + (_work_to_do) = RING_HAS_UNCONSUMED_RESPONSES(_r); \ +} while (0) + +#endif /* __XEN_PUBLIC_IO_RING_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/roms/ipxe/src/include/xen/io/xenbus.h b/roms/ipxe/src/include/xen/io/xenbus.h new file mode 100644 index 0000000..182aeb9 --- /dev/null +++ b/roms/ipxe/src/include/xen/io/xenbus.h @@ -0,0 +1,82 @@ +/***************************************************************************** + * xenbus.h + * + * Xenbus protocol details. + * + * 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. + * + * Copyright (C) 2005 XenSource Ltd. + */ + +#ifndef _XEN_PUBLIC_IO_XENBUS_H +#define _XEN_PUBLIC_IO_XENBUS_H + +FILE_LICENCE ( MIT ); + +/* + * The state of either end of the Xenbus, i.e. the current communication + * status of initialisation across the bus. States here imply nothing about + * the state of the connection between the driver and the kernel's device + * layers. + */ +enum xenbus_state { + XenbusStateUnknown = 0, + + XenbusStateInitialising = 1, + + /* + * InitWait: Finished early initialisation but waiting for information + * from the peer or hotplug scripts. + */ + XenbusStateInitWait = 2, + + /* + * Initialised: Waiting for a connection from the peer. + */ + XenbusStateInitialised = 3, + + XenbusStateConnected = 4, + + /* + * Closing: The device is being closed due to an error or an unplug event. + */ + XenbusStateClosing = 5, + + XenbusStateClosed = 6, + + /* + * Reconfiguring: The device is being reconfigured. + */ + XenbusStateReconfiguring = 7, + + XenbusStateReconfigured = 8 +}; +typedef enum xenbus_state XenbusState; + +#endif /* _XEN_PUBLIC_IO_XENBUS_H */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/roms/ipxe/src/include/xen/io/xs_wire.h b/roms/ipxe/src/include/xen/io/xs_wire.h new file mode 100644 index 0000000..50415f0 --- /dev/null +++ b/roms/ipxe/src/include/xen/io/xs_wire.h @@ -0,0 +1,140 @@ +/* + * Details of the "wire" protocol between Xen Store Daemon and client + * library or guest kernel. + * + * 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. + * + * Copyright (C) 2005 Rusty Russell IBM Corporation + */ + +#ifndef _XS_WIRE_H +#define _XS_WIRE_H + +FILE_LICENCE ( MIT ); + +enum xsd_sockmsg_type +{ + XS_DEBUG, + XS_DIRECTORY, + XS_READ, + XS_GET_PERMS, + XS_WATCH, + XS_UNWATCH, + XS_TRANSACTION_START, + XS_TRANSACTION_END, + XS_INTRODUCE, + XS_RELEASE, + XS_GET_DOMAIN_PATH, + XS_WRITE, + XS_MKDIR, + XS_RM, + XS_SET_PERMS, + XS_WATCH_EVENT, + XS_ERROR, + XS_IS_DOMAIN_INTRODUCED, + XS_RESUME, + XS_SET_TARGET, + XS_RESTRICT, + XS_RESET_WATCHES +}; + +#define XS_WRITE_NONE "NONE" +#define XS_WRITE_CREATE "CREATE" +#define XS_WRITE_CREATE_EXCL "CREATE|EXCL" + +/* We hand errors as strings, for portability. */ +struct xsd_errors +{ + int errnum; + const char *errstring; +}; +#ifdef EINVAL +#define XSD_ERROR(x) { x, #x } +/* LINTED: static unused */ +static struct xsd_errors xsd_errors[] +#if defined(__GNUC__) +__attribute__((unused)) +#endif + = { + XSD_ERROR(EINVAL), + XSD_ERROR(EACCES), + XSD_ERROR(EEXIST), + XSD_ERROR(EISDIR), + XSD_ERROR(ENOENT), + XSD_ERROR(ENOMEM), + XSD_ERROR(ENOSPC), + XSD_ERROR(EIO), + XSD_ERROR(ENOTEMPTY), + XSD_ERROR(ENOSYS), + XSD_ERROR(EROFS), + XSD_ERROR(EBUSY), + XSD_ERROR(EAGAIN), + XSD_ERROR(EISCONN), + XSD_ERROR(E2BIG) +}; +#endif + +struct xsd_sockmsg +{ + uint32_t type; /* XS_??? */ + uint32_t req_id;/* Request identifier, echoed in daemon's response. */ + uint32_t tx_id; /* Transaction id (0 if not related to a transaction). */ + uint32_t len; /* Length of data following this. */ + + /* Generally followed by nul-terminated string(s). */ +}; + +enum xs_watch_type +{ + XS_WATCH_PATH = 0, + XS_WATCH_TOKEN +}; + +/* + * `incontents 150 xenstore_struct XenStore wire protocol. + * + * Inter-domain shared memory communications. */ +#define XENSTORE_RING_SIZE 1024 +typedef uint32_t XENSTORE_RING_IDX; +#define MASK_XENSTORE_IDX(idx) ((idx) & (XENSTORE_RING_SIZE-1)) +struct xenstore_domain_interface { + char req[XENSTORE_RING_SIZE]; /* Requests to xenstore daemon. */ + char rsp[XENSTORE_RING_SIZE]; /* Replies and async watch events. */ + XENSTORE_RING_IDX req_cons, req_prod; + XENSTORE_RING_IDX rsp_cons, rsp_prod; +}; + +/* Violating this is very bad. See docs/misc/xenstore.txt. */ +#define XENSTORE_PAYLOAD_MAX 4096 + +/* Violating these just gets you an error back */ +#define XENSTORE_ABS_PATH_MAX 3072 +#define XENSTORE_REL_PATH_MAX 2048 + +#endif /* _XS_WIRE_H */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/roms/ipxe/src/include/xen/memory.h b/roms/ipxe/src/include/xen/memory.h new file mode 100644 index 0000000..0c76c0d --- /dev/null +++ b/roms/ipxe/src/include/xen/memory.h @@ -0,0 +1,540 @@ +/****************************************************************************** + * memory.h + * + * Memory reservation and information. + * + * 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. + * + * Copyright (c) 2005, Keir Fraser + */ + +#ifndef __XEN_PUBLIC_MEMORY_H__ +#define __XEN_PUBLIC_MEMORY_H__ + +FILE_LICENCE ( MIT ); + +#include "xen.h" + +/* + * Increase or decrease the specified domain's memory reservation. Returns the + * number of extents successfully allocated or freed. + * arg == addr of struct xen_memory_reservation. + */ +#define XENMEM_increase_reservation 0 +#define XENMEM_decrease_reservation 1 +#define XENMEM_populate_physmap 6 + +#if __XEN_INTERFACE_VERSION__ >= 0x00030209 +/* + * Maximum # bits addressable by the user of the allocated region (e.g., I/O + * devices often have a 32-bit limitation even in 64-bit systems). If zero + * then the user has no addressing restriction. This field is not used by + * XENMEM_decrease_reservation. + */ +#define XENMEMF_address_bits(x) (x) +#define XENMEMF_get_address_bits(x) ((x) & 0xffu) +/* NUMA node to allocate from. */ +#define XENMEMF_node(x) (((x) + 1) << 8) +#define XENMEMF_get_node(x) ((((x) >> 8) - 1) & 0xffu) +/* Flag to populate physmap with populate-on-demand entries */ +#define XENMEMF_populate_on_demand (1<<16) +/* Flag to request allocation only from the node specified */ +#define XENMEMF_exact_node_request (1<<17) +#define XENMEMF_exact_node(n) (XENMEMF_node(n) | XENMEMF_exact_node_request) +#endif + +struct xen_memory_reservation { + + /* + * XENMEM_increase_reservation: + * OUT: MFN (*not* GMFN) bases of extents that were allocated + * XENMEM_decrease_reservation: + * IN: GMFN bases of extents to free + * XENMEM_populate_physmap: + * IN: GPFN bases of extents to populate with memory + * OUT: GMFN bases of extents that were allocated + * (NB. This command also updates the mach_to_phys translation table) + * XENMEM_claim_pages: + * IN: must be zero + */ + XEN_GUEST_HANDLE(xen_pfn_t) extent_start; + + /* Number of extents, and size/alignment of each (2^extent_order pages). */ + xen_ulong_t nr_extents; + unsigned int extent_order; + +#if __XEN_INTERFACE_VERSION__ >= 0x00030209 + /* XENMEMF flags. */ + unsigned int mem_flags; +#else + unsigned int address_bits; +#endif + + /* + * Domain whose reservation is being changed. + * Unprivileged domains can specify only DOMID_SELF. + */ + domid_t domid; +}; +typedef struct xen_memory_reservation xen_memory_reservation_t; +DEFINE_XEN_GUEST_HANDLE(xen_memory_reservation_t); + +/* + * An atomic exchange of memory pages. If return code is zero then + * @out.extent_list provides GMFNs of the newly-allocated memory. + * Returns zero on complete success, otherwise a negative error code. + * On complete success then always @nr_exchanged == @in.nr_extents. + * On partial success @nr_exchanged indicates how much work was done. + */ +#define XENMEM_exchange 11 +struct xen_memory_exchange { + /* + * [IN] Details of memory extents to be exchanged (GMFN bases). + * Note that @in.address_bits is ignored and unused. + */ + struct xen_memory_reservation in; + + /* + * [IN/OUT] Details of new memory extents. + * We require that: + * 1. @in.domid == @out.domid + * 2. @in.nr_extents << @in.extent_order == + * @out.nr_extents << @out.extent_order + * 3. @in.extent_start and @out.extent_start lists must not overlap + * 4. @out.extent_start lists GPFN bases to be populated + * 5. @out.extent_start is overwritten with allocated GMFN bases + */ + struct xen_memory_reservation out; + + /* + * [OUT] Number of input extents that were successfully exchanged: + * 1. The first @nr_exchanged input extents were successfully + * deallocated. + * 2. The corresponding first entries in the output extent list correctly + * indicate the GMFNs that were successfully exchanged. + * 3. All other input and output extents are untouched. + * 4. If not all input exents are exchanged then the return code of this + * command will be non-zero. + * 5. THIS FIELD MUST BE INITIALISED TO ZERO BY THE CALLER! + */ + xen_ulong_t nr_exchanged; +}; +typedef struct xen_memory_exchange xen_memory_exchange_t; +DEFINE_XEN_GUEST_HANDLE(xen_memory_exchange_t); + +/* + * Returns the maximum machine frame number of mapped RAM in this system. + * This command always succeeds (it never returns an error code). + * arg == NULL. + */ +#define XENMEM_maximum_ram_page 2 + +/* + * Returns the current or maximum memory reservation, in pages, of the + * specified domain (may be DOMID_SELF). Returns -ve errcode on failure. + * arg == addr of domid_t. + */ +#define XENMEM_current_reservation 3 +#define XENMEM_maximum_reservation 4 + +/* + * Returns the maximum GPFN in use by the guest, or -ve errcode on failure. + */ +#define XENMEM_maximum_gpfn 14 + +/* + * Returns a list of MFN bases of 2MB extents comprising the machine_to_phys + * mapping table. Architectures which do not have a m2p table do not implement + * this command. + * arg == addr of xen_machphys_mfn_list_t. + */ +#define XENMEM_machphys_mfn_list 5 +struct xen_machphys_mfn_list { + /* + * Size of the 'extent_start' array. Fewer entries will be filled if the + * machphys table is smaller than max_extents * 2MB. + */ + unsigned int max_extents; + + /* + * Pointer to buffer to fill with list of extent starts. If there are + * any large discontiguities in the machine address space, 2MB gaps in + * the machphys table will be represented by an MFN base of zero. + */ + XEN_GUEST_HANDLE(xen_pfn_t) extent_start; + + /* + * Number of extents written to the above array. This will be smaller + * than 'max_extents' if the machphys table is smaller than max_e * 2MB. + */ + unsigned int nr_extents; +}; +typedef struct xen_machphys_mfn_list xen_machphys_mfn_list_t; +DEFINE_XEN_GUEST_HANDLE(xen_machphys_mfn_list_t); + +/* + * For a compat caller, this is identical to XENMEM_machphys_mfn_list. + * + * For a non compat caller, this functions similarly to + * XENMEM_machphys_mfn_list, but returns the mfns making up the compatibility + * m2p table. + */ +#define XENMEM_machphys_compat_mfn_list 25 + +/* + * Returns the location in virtual address space of the machine_to_phys + * mapping table. Architectures which do not have a m2p table, or which do not + * map it by default into guest address space, do not implement this command. + * arg == addr of xen_machphys_mapping_t. + */ +#define XENMEM_machphys_mapping 12 +struct xen_machphys_mapping { + xen_ulong_t v_start, v_end; /* Start and end virtual addresses. */ + xen_ulong_t max_mfn; /* Maximum MFN that can be looked up. */ +}; +typedef struct xen_machphys_mapping xen_machphys_mapping_t; +DEFINE_XEN_GUEST_HANDLE(xen_machphys_mapping_t); + +/* Source mapping space. */ +/* ` enum phys_map_space { */ +#define XENMAPSPACE_shared_info 0 /* shared info page */ +#define XENMAPSPACE_grant_table 1 /* grant table page */ +#define XENMAPSPACE_gmfn 2 /* GMFN */ +#define XENMAPSPACE_gmfn_range 3 /* GMFN range, XENMEM_add_to_physmap only. */ +#define XENMAPSPACE_gmfn_foreign 4 /* GMFN from another dom, + * XENMEM_add_to_physmap_batch only. */ +/* ` } */ + +/* + * Sets the GPFN at which a particular page appears in the specified guest's + * pseudophysical address space. + * arg == addr of xen_add_to_physmap_t. + */ +#define XENMEM_add_to_physmap 7 +struct xen_add_to_physmap { + /* Which domain to change the mapping for. */ + domid_t domid; + + /* Number of pages to go through for gmfn_range */ + uint16_t size; + + unsigned int space; /* => enum phys_map_space */ + +#define XENMAPIDX_grant_table_status 0x80000000 + + /* Index into space being mapped. */ + xen_ulong_t idx; + + /* GPFN in domid where the source mapping page should appear. */ + xen_pfn_t gpfn; +}; +typedef struct xen_add_to_physmap xen_add_to_physmap_t; +DEFINE_XEN_GUEST_HANDLE(xen_add_to_physmap_t); + +/* A batched version of add_to_physmap. */ +#define XENMEM_add_to_physmap_batch 23 +struct xen_add_to_physmap_batch { + /* IN */ + /* Which domain to change the mapping for. */ + domid_t domid; + uint16_t space; /* => enum phys_map_space */ + + /* Number of pages to go through */ + uint16_t size; + domid_t foreign_domid; /* IFF gmfn_foreign */ + + /* Indexes into space being mapped. */ + XEN_GUEST_HANDLE(xen_ulong_t) idxs; + + /* GPFN in domid where the source mapping page should appear. */ + XEN_GUEST_HANDLE(xen_pfn_t) gpfns; + + /* OUT */ + + /* Per index error code. */ + XEN_GUEST_HANDLE(int) errs; +}; +typedef struct xen_add_to_physmap_batch xen_add_to_physmap_batch_t; +DEFINE_XEN_GUEST_HANDLE(xen_add_to_physmap_batch_t); + +#if __XEN_INTERFACE_VERSION__ < 0x00040400 +#define XENMEM_add_to_physmap_range XENMEM_add_to_physmap_batch +#define xen_add_to_physmap_range xen_add_to_physmap_batch +typedef struct xen_add_to_physmap_batch xen_add_to_physmap_range_t; +DEFINE_XEN_GUEST_HANDLE(xen_add_to_physmap_range_t); +#endif + +/* + * Unmaps the page appearing at a particular GPFN from the specified guest's + * pseudophysical address space. + * arg == addr of xen_remove_from_physmap_t. + */ +#define XENMEM_remove_from_physmap 15 +struct xen_remove_from_physmap { + /* Which domain to change the mapping for. */ + domid_t domid; + + /* GPFN of the current mapping of the page. */ + xen_pfn_t gpfn; +}; +typedef struct xen_remove_from_physmap xen_remove_from_physmap_t; +DEFINE_XEN_GUEST_HANDLE(xen_remove_from_physmap_t); + +/*** REMOVED ***/ +/*#define XENMEM_translate_gpfn_list 8*/ + +/* + * Returns the pseudo-physical memory map as it was when the domain + * was started (specified by XENMEM_set_memory_map). + * arg == addr of xen_memory_map_t. + */ +#define XENMEM_memory_map 9 +struct xen_memory_map { + /* + * On call the number of entries which can be stored in buffer. On + * return the number of entries which have been stored in + * buffer. + */ + unsigned int nr_entries; + + /* + * Entries in the buffer are in the same format as returned by the + * BIOS INT 0x15 EAX=0xE820 call. + */ + XEN_GUEST_HANDLE(void) buffer; +}; +typedef struct xen_memory_map xen_memory_map_t; +DEFINE_XEN_GUEST_HANDLE(xen_memory_map_t); + +/* + * Returns the real physical memory map. Passes the same structure as + * XENMEM_memory_map. + * arg == addr of xen_memory_map_t. + */ +#define XENMEM_machine_memory_map 10 + +/* + * Set the pseudo-physical memory map of a domain, as returned by + * XENMEM_memory_map. + * arg == addr of xen_foreign_memory_map_t. + */ +#define XENMEM_set_memory_map 13 +struct xen_foreign_memory_map { + domid_t domid; + struct xen_memory_map map; +}; +typedef struct xen_foreign_memory_map xen_foreign_memory_map_t; +DEFINE_XEN_GUEST_HANDLE(xen_foreign_memory_map_t); + +#define XENMEM_set_pod_target 16 +#define XENMEM_get_pod_target 17 +struct xen_pod_target { + /* IN */ + uint64_t target_pages; + /* OUT */ + uint64_t tot_pages; + uint64_t pod_cache_pages; + uint64_t pod_entries; + /* IN */ + domid_t domid; +}; +typedef struct xen_pod_target xen_pod_target_t; + +#if defined(__XEN__) || defined(__XEN_TOOLS__) + +#ifndef uint64_aligned_t +#define uint64_aligned_t uint64_t +#endif + +/* + * Get the number of MFNs saved through memory sharing. + * The call never fails. + */ +#define XENMEM_get_sharing_freed_pages 18 +#define XENMEM_get_sharing_shared_pages 19 + +#define XENMEM_paging_op 20 +#define XENMEM_paging_op_nominate 0 +#define XENMEM_paging_op_evict 1 +#define XENMEM_paging_op_prep 2 + +struct xen_mem_event_op { + uint8_t op; /* XENMEM_*_op_* */ + domid_t domain; + + + /* PAGING_PREP IN: buffer to immediately fill page in */ + uint64_aligned_t buffer; + /* Other OPs */ + uint64_aligned_t gfn; /* IN: gfn of page being operated on */ +}; +typedef struct xen_mem_event_op xen_mem_event_op_t; +DEFINE_XEN_GUEST_HANDLE(xen_mem_event_op_t); + +#define XENMEM_access_op 21 +#define XENMEM_access_op_resume 0 +#define XENMEM_access_op_set_access 1 +#define XENMEM_access_op_get_access 2 + +typedef enum { + XENMEM_access_n, + XENMEM_access_r, + XENMEM_access_w, + XENMEM_access_rw, + XENMEM_access_x, + XENMEM_access_rx, + XENMEM_access_wx, + XENMEM_access_rwx, + /* + * Page starts off as r-x, but automatically + * change to r-w on a write + */ + XENMEM_access_rx2rw, + /* + * Log access: starts off as n, automatically + * goes to rwx, generating an event without + * pausing the vcpu + */ + XENMEM_access_n2rwx, + /* Take the domain default */ + XENMEM_access_default +} xenmem_access_t; + +struct xen_mem_access_op { + /* XENMEM_access_op_* */ + uint8_t op; + /* xenmem_access_t */ + uint8_t access; + domid_t domid; + /* + * Number of pages for set op + * Ignored on setting default access and other ops + */ + uint32_t nr; + /* + * First pfn for set op + * pfn for get op + * ~0ull is used to set and get the default access for pages + */ + uint64_aligned_t pfn; +}; +typedef struct xen_mem_access_op xen_mem_access_op_t; +DEFINE_XEN_GUEST_HANDLE(xen_mem_access_op_t); + +#define XENMEM_sharing_op 22 +#define XENMEM_sharing_op_nominate_gfn 0 +#define XENMEM_sharing_op_nominate_gref 1 +#define XENMEM_sharing_op_share 2 +#define XENMEM_sharing_op_resume 3 +#define XENMEM_sharing_op_debug_gfn 4 +#define XENMEM_sharing_op_debug_mfn 5 +#define XENMEM_sharing_op_debug_gref 6 +#define XENMEM_sharing_op_add_physmap 7 +#define XENMEM_sharing_op_audit 8 + +#define XENMEM_SHARING_OP_S_HANDLE_INVALID (-10) +#define XENMEM_SHARING_OP_C_HANDLE_INVALID (-9) + +/* The following allows sharing of grant refs. This is useful + * for sharing utilities sitting as "filters" in IO backends + * (e.g. memshr + blktap(2)). The IO backend is only exposed + * to grant references, and this allows sharing of the grefs */ +#define XENMEM_SHARING_OP_FIELD_IS_GREF_FLAG (1ULL << 62) + +#define XENMEM_SHARING_OP_FIELD_MAKE_GREF(field, val) \ + (field) = (XENMEM_SHARING_OP_FIELD_IS_GREF_FLAG | val) +#define XENMEM_SHARING_OP_FIELD_IS_GREF(field) \ + ((field) & XENMEM_SHARING_OP_FIELD_IS_GREF_FLAG) +#define XENMEM_SHARING_OP_FIELD_GET_GREF(field) \ + ((field) & (~XENMEM_SHARING_OP_FIELD_IS_GREF_FLAG)) + +struct xen_mem_sharing_op { + uint8_t op; /* XENMEM_sharing_op_* */ + domid_t domain; + + union { + struct mem_sharing_op_nominate { /* OP_NOMINATE_xxx */ + union { + uint64_aligned_t gfn; /* IN: gfn to nominate */ + uint32_t grant_ref; /* IN: grant ref to nominate */ + } u; + uint64_aligned_t handle; /* OUT: the handle */ + } nominate; + struct mem_sharing_op_share { /* OP_SHARE/ADD_PHYSMAP */ + uint64_aligned_t source_gfn; /* IN: the gfn of the source page */ + uint64_aligned_t source_handle; /* IN: handle to the source page */ + uint64_aligned_t client_gfn; /* IN: the client gfn */ + uint64_aligned_t client_handle; /* IN: handle to the client page */ + domid_t client_domain; /* IN: the client domain id */ + } share; + struct mem_sharing_op_debug { /* OP_DEBUG_xxx */ + union { + uint64_aligned_t gfn; /* IN: gfn to debug */ + uint64_aligned_t mfn; /* IN: mfn to debug */ + uint32_t gref; /* IN: gref to debug */ + } u; + } debug; + } u; +}; +typedef struct xen_mem_sharing_op xen_mem_sharing_op_t; +DEFINE_XEN_GUEST_HANDLE(xen_mem_sharing_op_t); + +/* + * Attempt to stake a claim for a domain on a quantity of pages + * of system RAM, but _not_ assign specific pageframes. Only + * arithmetic is performed so the hypercall is very fast and need + * not be preemptible, thus sidestepping time-of-check-time-of-use + * races for memory allocation. Returns 0 if the hypervisor page + * allocator has atomically and successfully claimed the requested + * number of pages, else non-zero. + * + * Any domain may have only one active claim. When sufficient memory + * has been allocated to resolve the claim, the claim silently expires. + * Claiming zero pages effectively resets any outstanding claim and + * is always successful. + * + * Note that a valid claim may be staked even after memory has been + * allocated for a domain. In this case, the claim is not incremental, + * i.e. if the domain's tot_pages is 3, and a claim is staked for 10, + * only 7 additional pages are claimed. + * + * Caller must be privileged or the hypercall fails. + */ +#define XENMEM_claim_pages 24 + +/* + * XENMEM_claim_pages flags - the are no flags at this time. + * The zero value is appropiate. + */ + +#endif /* defined(__XEN__) || defined(__XEN_TOOLS__) */ + +/* Next available subop number is 26 */ + +#endif /* __XEN_PUBLIC_MEMORY_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/roms/ipxe/src/include/xen/trace.h b/roms/ipxe/src/include/xen/trace.h new file mode 100644 index 0000000..bf8bf65 --- /dev/null +++ b/roms/ipxe/src/include/xen/trace.h @@ -0,0 +1,332 @@ +/****************************************************************************** + * include/public/trace.h + * + * 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. + * + * Mark Williamson, (C) 2004 Intel Research Cambridge + * Copyright (C) 2005 Bin Ren + */ + +#ifndef __XEN_PUBLIC_TRACE_H__ +#define __XEN_PUBLIC_TRACE_H__ + +FILE_LICENCE ( MIT ); + +#define TRACE_EXTRA_MAX 7 +#define TRACE_EXTRA_SHIFT 28 + +/* Trace classes */ +#define TRC_CLS_SHIFT 16 +#define TRC_GEN 0x0001f000 /* General trace */ +#define TRC_SCHED 0x0002f000 /* Xen Scheduler trace */ +#define TRC_DOM0OP 0x0004f000 /* Xen DOM0 operation trace */ +#define TRC_HVM 0x0008f000 /* Xen HVM trace */ +#define TRC_MEM 0x0010f000 /* Xen memory trace */ +#define TRC_PV 0x0020f000 /* Xen PV traces */ +#define TRC_SHADOW 0x0040f000 /* Xen shadow tracing */ +#define TRC_HW 0x0080f000 /* Xen hardware-related traces */ +#define TRC_GUEST 0x0800f000 /* Guest-generated traces */ +#define TRC_ALL 0x0ffff000 +#define TRC_HD_TO_EVENT(x) ((x)&0x0fffffff) +#define TRC_HD_CYCLE_FLAG (1UL<<31) +#define TRC_HD_INCLUDES_CYCLE_COUNT(x) ( !!( (x) & TRC_HD_CYCLE_FLAG ) ) +#define TRC_HD_EXTRA(x) (((x)>>TRACE_EXTRA_SHIFT)&TRACE_EXTRA_MAX) + +/* Trace subclasses */ +#define TRC_SUBCLS_SHIFT 12 + +/* trace subclasses for SVM */ +#define TRC_HVM_ENTRYEXIT 0x00081000 /* VMENTRY and #VMEXIT */ +#define TRC_HVM_HANDLER 0x00082000 /* various HVM handlers */ +#define TRC_HVM_EMUL 0x00084000 /* emulated devices */ + +#define TRC_SCHED_MIN 0x00021000 /* Just runstate changes */ +#define TRC_SCHED_CLASS 0x00022000 /* Scheduler-specific */ +#define TRC_SCHED_VERBOSE 0x00028000 /* More inclusive scheduling */ + +/* + * The highest 3 bits of the last 12 bits of TRC_SCHED_CLASS above are + * reserved for encoding what scheduler produced the information. The + * actual event is encoded in the last 9 bits. + * + * This means we have 8 scheduling IDs available (which means at most 8 + * schedulers generating events) and, in each scheduler, up to 512 + * different events. + */ +#define TRC_SCHED_ID_BITS 3 +#define TRC_SCHED_ID_SHIFT (TRC_SUBCLS_SHIFT - TRC_SCHED_ID_BITS) +#define TRC_SCHED_ID_MASK (((1UL<cpu_offset[cpu]). + */ +struct t_info { + uint16_t tbuf_size; /* Size in pages of each trace buffer */ + uint16_t mfn_offset[]; /* Offset within t_info structure of the page list per cpu */ + /* MFN lists immediately after the header */ +}; + +#endif /* __XEN_PUBLIC_TRACE_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/roms/ipxe/src/include/xen/version.h b/roms/ipxe/src/include/xen/version.h new file mode 100644 index 0000000..4e81ca0 --- /dev/null +++ b/roms/ipxe/src/include/xen/version.h @@ -0,0 +1,98 @@ +/****************************************************************************** + * version.h + * + * Xen version, type, and compile information. + * + * 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. + * + * Copyright (c) 2005, Nguyen Anh Quynh + * Copyright (c) 2005, Keir Fraser + */ + +#ifndef __XEN_PUBLIC_VERSION_H__ +#define __XEN_PUBLIC_VERSION_H__ + +FILE_LICENCE ( MIT ); + +#include "xen.h" + +/* NB. All ops return zero on success, except XENVER_{version,pagesize} */ + +/* arg == NULL; returns major:minor (16:16). */ +#define XENVER_version 0 + +/* arg == xen_extraversion_t. */ +#define XENVER_extraversion 1 +typedef char xen_extraversion_t[16]; +#define XEN_EXTRAVERSION_LEN (sizeof(xen_extraversion_t)) + +/* arg == xen_compile_info_t. */ +#define XENVER_compile_info 2 +struct xen_compile_info { + char compiler[64]; + char compile_by[16]; + char compile_domain[32]; + char compile_date[32]; +}; +typedef struct xen_compile_info xen_compile_info_t; + +#define XENVER_capabilities 3 +typedef char xen_capabilities_info_t[1024]; +#define XEN_CAPABILITIES_INFO_LEN (sizeof(xen_capabilities_info_t)) + +#define XENVER_changeset 4 +typedef char xen_changeset_info_t[64]; +#define XEN_CHANGESET_INFO_LEN (sizeof(xen_changeset_info_t)) + +#define XENVER_platform_parameters 5 +struct xen_platform_parameters { + xen_ulong_t virt_start; +}; +typedef struct xen_platform_parameters xen_platform_parameters_t; + +#define XENVER_get_features 6 +struct xen_feature_info { + unsigned int submap_idx; /* IN: which 32-bit submap to return */ + uint32_t submap; /* OUT: 32-bit submap */ +}; +typedef struct xen_feature_info xen_feature_info_t; + +/* Declares the features reported by XENVER_get_features. */ +#include "features.h" + +/* arg == NULL; returns host memory page size. */ +#define XENVER_pagesize 7 + +/* arg == xen_domain_handle_t. */ +#define XENVER_guest_handle 8 + +#define XENVER_commandline 9 +typedef char xen_commandline_t[1024]; + +#endif /* __XEN_PUBLIC_VERSION_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/roms/ipxe/src/include/xen/xen-compat.h b/roms/ipxe/src/include/xen/xen-compat.h new file mode 100644 index 0000000..0ba6fca --- /dev/null +++ b/roms/ipxe/src/include/xen/xen-compat.h @@ -0,0 +1,46 @@ +/****************************************************************************** + * xen-compat.h + * + * Guest OS interface to Xen. Compatibility layer. + * + * 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. + * + * Copyright (c) 2006, Christian Limpach + */ + +#ifndef __XEN_PUBLIC_XEN_COMPAT_H__ +#define __XEN_PUBLIC_XEN_COMPAT_H__ + +FILE_LICENCE ( MIT ); + +#define __XEN_LATEST_INTERFACE_VERSION__ 0x00040400 + +#if defined(__XEN__) || defined(__XEN_TOOLS__) +/* Xen is built with matching headers and implements the latest interface. */ +#define __XEN_INTERFACE_VERSION__ __XEN_LATEST_INTERFACE_VERSION__ +#elif !defined(__XEN_INTERFACE_VERSION__) +/* Guests which do not specify a version get the legacy interface. */ +#define __XEN_INTERFACE_VERSION__ 0x00000000 +#endif + +#if __XEN_INTERFACE_VERSION__ > __XEN_LATEST_INTERFACE_VERSION__ +#error "These header files do not support the requested interface version." +#endif + +#endif /* __XEN_PUBLIC_XEN_COMPAT_H__ */ diff --git a/roms/ipxe/src/include/xen/xen.h b/roms/ipxe/src/include/xen/xen.h new file mode 100644 index 0000000..2da521d --- /dev/null +++ b/roms/ipxe/src/include/xen/xen.h @@ -0,0 +1,901 @@ +/****************************************************************************** + * xen.h + * + * Guest OS interface to Xen. + * + * 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. + * + * Copyright (c) 2004, K A Fraser + */ + +#ifndef __XEN_PUBLIC_XEN_H__ +#define __XEN_PUBLIC_XEN_H__ + +FILE_LICENCE ( MIT ); + +#include "xen-compat.h" + +#if defined(__i386__) || defined(__x86_64__) +#include "arch-x86/xen.h" +#elif defined(__arm__) || defined (__aarch64__) +#include "arch-arm.h" +#else +#error "Unsupported architecture" +#endif + +#ifndef __ASSEMBLY__ +/* Guest handles for primitive C types. */ +DEFINE_XEN_GUEST_HANDLE(char); +__DEFINE_XEN_GUEST_HANDLE(uchar, unsigned char); +DEFINE_XEN_GUEST_HANDLE(int); +__DEFINE_XEN_GUEST_HANDLE(uint, unsigned int); +#if __XEN_INTERFACE_VERSION__ < 0x00040300 +DEFINE_XEN_GUEST_HANDLE(long); +__DEFINE_XEN_GUEST_HANDLE(ulong, unsigned long); +#endif +DEFINE_XEN_GUEST_HANDLE(void); + +DEFINE_XEN_GUEST_HANDLE(uint64_t); +DEFINE_XEN_GUEST_HANDLE(xen_pfn_t); +DEFINE_XEN_GUEST_HANDLE(xen_ulong_t); +#endif + +/* + * HYPERCALLS + */ + +/* `incontents 100 hcalls List of hypercalls + * ` enum hypercall_num { // __HYPERVISOR_* => HYPERVISOR_*() + */ + +#define __HYPERVISOR_set_trap_table 0 +#define __HYPERVISOR_mmu_update 1 +#define __HYPERVISOR_set_gdt 2 +#define __HYPERVISOR_stack_switch 3 +#define __HYPERVISOR_set_callbacks 4 +#define __HYPERVISOR_fpu_taskswitch 5 +#define __HYPERVISOR_sched_op_compat 6 /* compat since 0x00030101 */ +#define __HYPERVISOR_platform_op 7 +#define __HYPERVISOR_set_debugreg 8 +#define __HYPERVISOR_get_debugreg 9 +#define __HYPERVISOR_update_descriptor 10 +#define __HYPERVISOR_memory_op 12 +#define __HYPERVISOR_multicall 13 +#define __HYPERVISOR_update_va_mapping 14 +#define __HYPERVISOR_set_timer_op 15 +#define __HYPERVISOR_event_channel_op_compat 16 /* compat since 0x00030202 */ +#define __HYPERVISOR_xen_version 17 +#define __HYPERVISOR_console_io 18 +#define __HYPERVISOR_physdev_op_compat 19 /* compat since 0x00030202 */ +#define __HYPERVISOR_grant_table_op 20 +#define __HYPERVISOR_vm_assist 21 +#define __HYPERVISOR_update_va_mapping_otherdomain 22 +#define __HYPERVISOR_iret 23 /* x86 only */ +#define __HYPERVISOR_vcpu_op 24 +#define __HYPERVISOR_set_segment_base 25 /* x86/64 only */ +#define __HYPERVISOR_mmuext_op 26 +#define __HYPERVISOR_xsm_op 27 +#define __HYPERVISOR_nmi_op 28 +#define __HYPERVISOR_sched_op 29 +#define __HYPERVISOR_callback_op 30 +#define __HYPERVISOR_xenoprof_op 31 +#define __HYPERVISOR_event_channel_op 32 +#define __HYPERVISOR_physdev_op 33 +#define __HYPERVISOR_hvm_op 34 +#define __HYPERVISOR_sysctl 35 +#define __HYPERVISOR_domctl 36 +#define __HYPERVISOR_kexec_op 37 +#define __HYPERVISOR_tmem_op 38 +#define __HYPERVISOR_xc_reserved_op 39 /* reserved for XenClient */ + +/* Architecture-specific hypercall definitions. */ +#define __HYPERVISOR_arch_0 48 +#define __HYPERVISOR_arch_1 49 +#define __HYPERVISOR_arch_2 50 +#define __HYPERVISOR_arch_3 51 +#define __HYPERVISOR_arch_4 52 +#define __HYPERVISOR_arch_5 53 +#define __HYPERVISOR_arch_6 54 +#define __HYPERVISOR_arch_7 55 + +/* ` } */ + +/* + * HYPERCALL COMPATIBILITY. + */ + +/* New sched_op hypercall introduced in 0x00030101. */ +#if __XEN_INTERFACE_VERSION__ < 0x00030101 +#undef __HYPERVISOR_sched_op +#define __HYPERVISOR_sched_op __HYPERVISOR_sched_op_compat +#endif + +/* New event-channel and physdev hypercalls introduced in 0x00030202. */ +#if __XEN_INTERFACE_VERSION__ < 0x00030202 +#undef __HYPERVISOR_event_channel_op +#define __HYPERVISOR_event_channel_op __HYPERVISOR_event_channel_op_compat +#undef __HYPERVISOR_physdev_op +#define __HYPERVISOR_physdev_op __HYPERVISOR_physdev_op_compat +#endif + +/* New platform_op hypercall introduced in 0x00030204. */ +#if __XEN_INTERFACE_VERSION__ < 0x00030204 +#define __HYPERVISOR_dom0_op __HYPERVISOR_platform_op +#endif + +/* + * VIRTUAL INTERRUPTS + * + * Virtual interrupts that a guest OS may receive from Xen. + * + * In the side comments, 'V.' denotes a per-VCPU VIRQ while 'G.' denotes a + * global VIRQ. The former can be bound once per VCPU and cannot be re-bound. + * The latter can be allocated only once per guest: they must initially be + * allocated to VCPU0 but can subsequently be re-bound. + */ +/* ` enum virq { */ +#define VIRQ_TIMER 0 /* V. Timebase update, and/or requested timeout. */ +#define VIRQ_DEBUG 1 /* V. Request guest to dump debug info. */ +#define VIRQ_CONSOLE 2 /* G. (DOM0) Bytes received on emergency console. */ +#define VIRQ_DOM_EXC 3 /* G. (DOM0) Exceptional event for some domain. */ +#define VIRQ_TBUF 4 /* G. (DOM0) Trace buffer has records available. */ +#define VIRQ_DEBUGGER 6 /* G. (DOM0) A domain has paused for debugging. */ +#define VIRQ_XENOPROF 7 /* V. XenOprofile interrupt: new sample available */ +#define VIRQ_CON_RING 8 /* G. (DOM0) Bytes received on console */ +#define VIRQ_PCPU_STATE 9 /* G. (DOM0) PCPU state changed */ +#define VIRQ_MEM_EVENT 10 /* G. (DOM0) A memory event has occured */ +#define VIRQ_XC_RESERVED 11 /* G. Reserved for XenClient */ +#define VIRQ_ENOMEM 12 /* G. (DOM0) Low on heap memory */ + +/* Architecture-specific VIRQ definitions. */ +#define VIRQ_ARCH_0 16 +#define VIRQ_ARCH_1 17 +#define VIRQ_ARCH_2 18 +#define VIRQ_ARCH_3 19 +#define VIRQ_ARCH_4 20 +#define VIRQ_ARCH_5 21 +#define VIRQ_ARCH_6 22 +#define VIRQ_ARCH_7 23 +/* ` } */ + +#define NR_VIRQS 24 + +/* + * ` enum neg_errnoval + * ` HYPERVISOR_mmu_update(const struct mmu_update reqs[], + * ` unsigned count, unsigned *done_out, + * ` unsigned foreigndom) + * ` + * @reqs is an array of mmu_update_t structures ((ptr, val) pairs). + * @count is the length of the above array. + * @pdone is an output parameter indicating number of completed operations + * @foreigndom[15:0]: FD, the expected owner of data pages referenced in this + * hypercall invocation. Can be DOMID_SELF. + * @foreigndom[31:16]: PFD, the expected owner of pagetable pages referenced + * in this hypercall invocation. The value of this field + * (x) encodes the PFD as follows: + * x == 0 => PFD == DOMID_SELF + * x != 0 => PFD == x - 1 + * + * Sub-commands: ptr[1:0] specifies the appropriate MMU_* command. + * ------------- + * ptr[1:0] == MMU_NORMAL_PT_UPDATE: + * Updates an entry in a page table belonging to PFD. If updating an L1 table, + * and the new table entry is valid/present, the mapped frame must belong to + * FD. If attempting to map an I/O page then the caller assumes the privilege + * of the FD. + * FD == DOMID_IO: Permit /only/ I/O mappings, at the priv level of the caller. + * FD == DOMID_XEN: Map restricted areas of Xen's heap space. + * ptr[:2] -- Machine address of the page-table entry to modify. + * val -- Value to write. + * + * There also certain implicit requirements when using this hypercall. The + * pages that make up a pagetable must be mapped read-only in the guest. + * This prevents uncontrolled guest updates to the pagetable. Xen strictly + * enforces this, and will disallow any pagetable update which will end up + * mapping pagetable page RW, and will disallow using any writable page as a + * pagetable. In practice it means that when constructing a page table for a + * process, thread, etc, we MUST be very dilligient in following these rules: + * 1). Start with top-level page (PGD or in Xen language: L4). Fill out + * the entries. + * 2). Keep on going, filling out the upper (PUD or L3), and middle (PMD + * or L2). + * 3). Start filling out the PTE table (L1) with the PTE entries. Once + * done, make sure to set each of those entries to RO (so writeable bit + * is unset). Once that has been completed, set the PMD (L2) for this + * PTE table as RO. + * 4). When completed with all of the PMD (L2) entries, and all of them have + * been set to RO, make sure to set RO the PUD (L3). Do the same + * operation on PGD (L4) pagetable entries that have a PUD (L3) entry. + * 5). Now before you can use those pages (so setting the cr3), you MUST also + * pin them so that the hypervisor can verify the entries. This is done + * via the HYPERVISOR_mmuext_op(MMUEXT_PIN_L4_TABLE, guest physical frame + * number of the PGD (L4)). And this point the HYPERVISOR_mmuext_op( + * MMUEXT_NEW_BASEPTR, guest physical frame number of the PGD (L4)) can be + * issued. + * For 32-bit guests, the L4 is not used (as there is less pagetables), so + * instead use L3. + * At this point the pagetables can be modified using the MMU_NORMAL_PT_UPDATE + * hypercall. Also if so desired the OS can also try to write to the PTE + * and be trapped by the hypervisor (as the PTE entry is RO). + * + * To deallocate the pages, the operations are the reverse of the steps + * mentioned above. The argument is MMUEXT_UNPIN_TABLE for all levels and the + * pagetable MUST not be in use (meaning that the cr3 is not set to it). + * + * ptr[1:0] == MMU_MACHPHYS_UPDATE: + * Updates an entry in the machine->pseudo-physical mapping table. + * ptr[:2] -- Machine address within the frame whose mapping to modify. + * The frame must belong to the FD, if one is specified. + * val -- Value to write into the mapping entry. + * + * ptr[1:0] == MMU_PT_UPDATE_PRESERVE_AD: + * As MMU_NORMAL_PT_UPDATE above, but A/D bits currently in the PTE are ORed + * with those in @val. + * + * @val is usually the machine frame number along with some attributes. + * The attributes by default follow the architecture defined bits. Meaning that + * if this is a X86_64 machine and four page table layout is used, the layout + * of val is: + * - 63 if set means No execute (NX) + * - 46-13 the machine frame number + * - 12 available for guest + * - 11 available for guest + * - 10 available for guest + * - 9 available for guest + * - 8 global + * - 7 PAT (PSE is disabled, must use hypercall to make 4MB or 2MB pages) + * - 6 dirty + * - 5 accessed + * - 4 page cached disabled + * - 3 page write through + * - 2 userspace accessible + * - 1 writeable + * - 0 present + * + * The one bits that does not fit with the default layout is the PAGE_PSE + * also called PAGE_PAT). The MMUEXT_[UN]MARK_SUPER arguments to the + * HYPERVISOR_mmuext_op serve as mechanism to set a pagetable to be 4MB + * (or 2MB) instead of using the PAGE_PSE bit. + * + * The reason that the PAGE_PSE (bit 7) is not being utilized is due to Xen + * using it as the Page Attribute Table (PAT) bit - for details on it please + * refer to Intel SDM 10.12. The PAT allows to set the caching attributes of + * pages instead of using MTRRs. + * + * The PAT MSR is as follows (it is a 64-bit value, each entry is 8 bits): + * PAT4 PAT0 + * +-----+-----+----+----+----+-----+----+----+ + * | UC | UC- | WC | WB | UC | UC- | WC | WB | <= Linux + * +-----+-----+----+----+----+-----+----+----+ + * | UC | UC- | WT | WB | UC | UC- | WT | WB | <= BIOS (default when machine boots) + * +-----+-----+----+----+----+-----+----+----+ + * | rsv | rsv | WP | WC | UC | UC- | WT | WB | <= Xen + * +-----+-----+----+----+----+-----+----+----+ + * + * The lookup of this index table translates to looking up + * Bit 7, Bit 4, and Bit 3 of val entry: + * + * PAT/PSE (bit 7) ... PCD (bit 4) .. PWT (bit 3). + * + * If all bits are off, then we are using PAT0. If bit 3 turned on, + * then we are using PAT1, if bit 3 and bit 4, then PAT2.. + * + * As you can see, the Linux PAT1 translates to PAT4 under Xen. Which means + * that if a guest that follows Linux's PAT setup and would like to set Write + * Combined on pages it MUST use PAT4 entry. Meaning that Bit 7 (PAGE_PAT) is + * set. For example, under Linux it only uses PAT0, PAT1, and PAT2 for the + * caching as: + * + * WB = none (so PAT0) + * WC = PWT (bit 3 on) + * UC = PWT | PCD (bit 3 and 4 are on). + * + * To make it work with Xen, it needs to translate the WC bit as so: + * + * PWT (so bit 3 on) --> PAT (so bit 7 is on) and clear bit 3 + * + * And to translate back it would: + * + * PAT (bit 7 on) --> PWT (bit 3 on) and clear bit 7. + */ +#define MMU_NORMAL_PT_UPDATE 0 /* checked '*ptr = val'. ptr is MA. */ +#define MMU_MACHPHYS_UPDATE 1 /* ptr = MA of frame to modify entry for */ +#define MMU_PT_UPDATE_PRESERVE_AD 2 /* atomically: *ptr = val | (*ptr&(A|D)) */ + +/* + * MMU EXTENDED OPERATIONS + * + * ` enum neg_errnoval + * ` HYPERVISOR_mmuext_op(mmuext_op_t uops[], + * ` unsigned int count, + * ` unsigned int *pdone, + * ` unsigned int foreigndom) + */ +/* HYPERVISOR_mmuext_op() accepts a list of mmuext_op structures. + * A foreigndom (FD) can be specified (or DOMID_SELF for none). + * Where the FD has some effect, it is described below. + * + * cmd: MMUEXT_(UN)PIN_*_TABLE + * mfn: Machine frame number to be (un)pinned as a p.t. page. + * The frame must belong to the FD, if one is specified. + * + * cmd: MMUEXT_NEW_BASEPTR + * mfn: Machine frame number of new page-table base to install in MMU. + * + * cmd: MMUEXT_NEW_USER_BASEPTR [x86/64 only] + * mfn: Machine frame number of new page-table base to install in MMU + * when in user space. + * + * cmd: MMUEXT_TLB_FLUSH_LOCAL + * No additional arguments. Flushes local TLB. + * + * cmd: MMUEXT_INVLPG_LOCAL + * linear_addr: Linear address to be flushed from the local TLB. + * + * cmd: MMUEXT_TLB_FLUSH_MULTI + * vcpumask: Pointer to bitmap of VCPUs to be flushed. + * + * cmd: MMUEXT_INVLPG_MULTI + * linear_addr: Linear address to be flushed. + * vcpumask: Pointer to bitmap of VCPUs to be flushed. + * + * cmd: MMUEXT_TLB_FLUSH_ALL + * No additional arguments. Flushes all VCPUs' TLBs. + * + * cmd: MMUEXT_INVLPG_ALL + * linear_addr: Linear address to be flushed from all VCPUs' TLBs. + * + * cmd: MMUEXT_FLUSH_CACHE + * No additional arguments. Writes back and flushes cache contents. + * + * cmd: MMUEXT_FLUSH_CACHE_GLOBAL + * No additional arguments. Writes back and flushes cache contents + * on all CPUs in the system. + * + * cmd: MMUEXT_SET_LDT + * linear_addr: Linear address of LDT base (NB. must be page-aligned). + * nr_ents: Number of entries in LDT. + * + * cmd: MMUEXT_CLEAR_PAGE + * mfn: Machine frame number to be cleared. + * + * cmd: MMUEXT_COPY_PAGE + * mfn: Machine frame number of the destination page. + * src_mfn: Machine frame number of the source page. + * + * cmd: MMUEXT_[UN]MARK_SUPER + * mfn: Machine frame number of head of superpage to be [un]marked. + */ +/* ` enum mmuext_cmd { */ +#define MMUEXT_PIN_L1_TABLE 0 +#define MMUEXT_PIN_L2_TABLE 1 +#define MMUEXT_PIN_L3_TABLE 2 +#define MMUEXT_PIN_L4_TABLE 3 +#define MMUEXT_UNPIN_TABLE 4 +#define MMUEXT_NEW_BASEPTR 5 +#define MMUEXT_TLB_FLUSH_LOCAL 6 +#define MMUEXT_INVLPG_LOCAL 7 +#define MMUEXT_TLB_FLUSH_MULTI 8 +#define MMUEXT_INVLPG_MULTI 9 +#define MMUEXT_TLB_FLUSH_ALL 10 +#define MMUEXT_INVLPG_ALL 11 +#define MMUEXT_FLUSH_CACHE 12 +#define MMUEXT_SET_LDT 13 +#define MMUEXT_NEW_USER_BASEPTR 15 +#define MMUEXT_CLEAR_PAGE 16 +#define MMUEXT_COPY_PAGE 17 +#define MMUEXT_FLUSH_CACHE_GLOBAL 18 +#define MMUEXT_MARK_SUPER 19 +#define MMUEXT_UNMARK_SUPER 20 +/* ` } */ + +#ifndef __ASSEMBLY__ +struct mmuext_op { + unsigned int cmd; /* => enum mmuext_cmd */ + union { + /* [UN]PIN_TABLE, NEW_BASEPTR, NEW_USER_BASEPTR + * CLEAR_PAGE, COPY_PAGE, [UN]MARK_SUPER */ + xen_pfn_t mfn; + /* INVLPG_LOCAL, INVLPG_ALL, SET_LDT */ + unsigned long linear_addr; + } arg1; + union { + /* SET_LDT */ + unsigned int nr_ents; + /* TLB_FLUSH_MULTI, INVLPG_MULTI */ +#if __XEN_INTERFACE_VERSION__ >= 0x00030205 + XEN_GUEST_HANDLE(const_void) vcpumask; +#else + const void *vcpumask; +#endif + /* COPY_PAGE */ + xen_pfn_t src_mfn; + } arg2; +}; +typedef struct mmuext_op mmuext_op_t; +DEFINE_XEN_GUEST_HANDLE(mmuext_op_t); +#endif + +/* + * ` enum neg_errnoval + * ` HYPERVISOR_update_va_mapping(unsigned long va, u64 val, + * ` enum uvm_flags flags) + * ` + * ` enum neg_errnoval + * ` HYPERVISOR_update_va_mapping_otherdomain(unsigned long va, u64 val, + * ` enum uvm_flags flags, + * ` domid_t domid) + * ` + * ` @va: The virtual address whose mapping we want to change + * ` @val: The new page table entry, must contain a machine address + * ` @flags: Control TLB flushes + */ +/* These are passed as 'flags' to update_va_mapping. They can be ORed. */ +/* When specifying UVMF_MULTI, also OR in a pointer to a CPU bitmap. */ +/* UVMF_LOCAL is merely UVMF_MULTI with a NULL bitmap pointer. */ +/* ` enum uvm_flags { */ +#define UVMF_NONE (0UL<<0) /* No flushing at all. */ +#define UVMF_TLB_FLUSH (1UL<<0) /* Flush entire TLB(s). */ +#define UVMF_INVLPG (2UL<<0) /* Flush only one entry. */ +#define UVMF_FLUSHTYPE_MASK (3UL<<0) +#define UVMF_MULTI (0UL<<2) /* Flush subset of TLBs. */ +#define UVMF_LOCAL (0UL<<2) /* Flush local TLB. */ +#define UVMF_ALL (1UL<<2) /* Flush all TLBs. */ +/* ` } */ + +/* + * Commands to HYPERVISOR_console_io(). + */ +#define CONSOLEIO_write 0 +#define CONSOLEIO_read 1 + +/* + * Commands to HYPERVISOR_vm_assist(). + */ +#define VMASST_CMD_enable 0 +#define VMASST_CMD_disable 1 + +/* x86/32 guests: simulate full 4GB segment limits. */ +#define VMASST_TYPE_4gb_segments 0 + +/* x86/32 guests: trap (vector 15) whenever above vmassist is used. */ +#define VMASST_TYPE_4gb_segments_notify 1 + +/* + * x86 guests: support writes to bottom-level PTEs. + * NB1. Page-directory entries cannot be written. + * NB2. Guest must continue to remove all writable mappings of PTEs. + */ +#define VMASST_TYPE_writable_pagetables 2 + +/* x86/PAE guests: support PDPTs above 4GB. */ +#define VMASST_TYPE_pae_extended_cr3 3 + +#define MAX_VMASST_TYPE 3 + +#ifndef __ASSEMBLY__ + +typedef uint16_t domid_t; + +/* Domain ids >= DOMID_FIRST_RESERVED cannot be used for ordinary domains. */ +#define DOMID_FIRST_RESERVED (0x7FF0U) + +/* DOMID_SELF is used in certain contexts to refer to oneself. */ +#define DOMID_SELF (0x7FF0U) + +/* + * DOMID_IO is used to restrict page-table updates to mapping I/O memory. + * Although no Foreign Domain need be specified to map I/O pages, DOMID_IO + * is useful to ensure that no mappings to the OS's own heap are accidentally + * installed. (e.g., in Linux this could cause havoc as reference counts + * aren't adjusted on the I/O-mapping code path). + * This only makes sense in MMUEXT_SET_FOREIGNDOM, but in that context can + * be specified by any calling domain. + */ +#define DOMID_IO (0x7FF1U) + +/* + * DOMID_XEN is used to allow privileged domains to map restricted parts of + * Xen's heap space (e.g., the machine_to_phys table). + * This only makes sense in MMUEXT_SET_FOREIGNDOM, and is only permitted if + * the caller is privileged. + */ +#define DOMID_XEN (0x7FF2U) + +/* + * DOMID_COW is used as the owner of sharable pages */ +#define DOMID_COW (0x7FF3U) + +/* DOMID_INVALID is used to identify pages with unknown owner. */ +#define DOMID_INVALID (0x7FF4U) + +/* Idle domain. */ +#define DOMID_IDLE (0x7FFFU) + +/* + * Send an array of these to HYPERVISOR_mmu_update(). + * NB. The fields are natural pointer/address size for this architecture. + */ +struct mmu_update { + uint64_t ptr; /* Machine address of PTE. */ + uint64_t val; /* New contents of PTE. */ +}; +typedef struct mmu_update mmu_update_t; +DEFINE_XEN_GUEST_HANDLE(mmu_update_t); + +/* + * ` enum neg_errnoval + * ` HYPERVISOR_multicall(multicall_entry_t call_list[], + * ` uint32_t nr_calls); + * + * NB. The fields are logically the natural register size for this + * architecture. In cases where xen_ulong_t is larger than this then + * any unused bits in the upper portion must be zero. + */ +struct multicall_entry { + xen_ulong_t op, result; + xen_ulong_t args[6]; +}; +typedef struct multicall_entry multicall_entry_t; +DEFINE_XEN_GUEST_HANDLE(multicall_entry_t); + +#if __XEN_INTERFACE_VERSION__ < 0x00040400 +/* + * Event channel endpoints per domain (when using the 2-level ABI): + * 1024 if a long is 32 bits; 4096 if a long is 64 bits. + */ +#define NR_EVENT_CHANNELS EVTCHN_2L_NR_CHANNELS +#endif + +struct vcpu_time_info { + /* + * Updates to the following values are preceded and followed by an + * increment of 'version'. The guest can therefore detect updates by + * looking for changes to 'version'. If the least-significant bit of + * the version number is set then an update is in progress and the guest + * must wait to read a consistent set of values. + * The correct way to interact with the version number is similar to + * Linux's seqlock: see the implementations of read_seqbegin/read_seqretry. + */ + uint32_t version; + uint32_t pad0; + uint64_t tsc_timestamp; /* TSC at last update of time vals. */ + uint64_t system_time; /* Time, in nanosecs, since boot. */ + /* + * Current system time: + * system_time + + * ((((tsc - tsc_timestamp) << tsc_shift) * tsc_to_system_mul) >> 32) + * CPU frequency (Hz): + * ((10^9 << 32) / tsc_to_system_mul) >> tsc_shift + */ + uint32_t tsc_to_system_mul; + int8_t tsc_shift; + int8_t pad1[3]; +}; /* 32 bytes */ +typedef struct vcpu_time_info vcpu_time_info_t; + +struct vcpu_info { + /* + * 'evtchn_upcall_pending' is written non-zero by Xen to indicate + * a pending notification for a particular VCPU. It is then cleared + * by the guest OS /before/ checking for pending work, thus avoiding + * a set-and-check race. Note that the mask is only accessed by Xen + * on the CPU that is currently hosting the VCPU. This means that the + * pending and mask flags can be updated by the guest without special + * synchronisation (i.e., no need for the x86 LOCK prefix). + * This may seem suboptimal because if the pending flag is set by + * a different CPU then an IPI may be scheduled even when the mask + * is set. However, note: + * 1. The task of 'interrupt holdoff' is covered by the per-event- + * channel mask bits. A 'noisy' event that is continually being + * triggered can be masked at source at this very precise + * granularity. + * 2. The main purpose of the per-VCPU mask is therefore to restrict + * reentrant execution: whether for concurrency control, or to + * prevent unbounded stack usage. Whatever the purpose, we expect + * that the mask will be asserted only for short periods at a time, + * and so the likelihood of a 'spurious' IPI is suitably small. + * The mask is read before making an event upcall to the guest: a + * non-zero mask therefore guarantees that the VCPU will not receive + * an upcall activation. The mask is cleared when the VCPU requests + * to block: this avoids wakeup-waiting races. + */ + uint8_t evtchn_upcall_pending; +#ifdef XEN_HAVE_PV_UPCALL_MASK + uint8_t evtchn_upcall_mask; +#else /* XEN_HAVE_PV_UPCALL_MASK */ + uint8_t pad0; +#endif /* XEN_HAVE_PV_UPCALL_MASK */ + xen_ulong_t evtchn_pending_sel; + struct arch_vcpu_info arch; + struct vcpu_time_info time; +}; /* 64 bytes (x86) */ +#ifndef __XEN__ +typedef struct vcpu_info vcpu_info_t; +#endif + +/* + * `incontents 200 startofday_shared Start-of-day shared data structure + * Xen/kernel shared data -- pointer provided in start_info. + * + * This structure is defined to be both smaller than a page, and the + * only data on the shared page, but may vary in actual size even within + * compatible Xen versions; guests should not rely on the size + * of this structure remaining constant. + */ +struct shared_info { + struct vcpu_info vcpu_info[XEN_LEGACY_MAX_VCPUS]; + + /* + * A domain can create "event channels" on which it can send and receive + * asynchronous event notifications. There are three classes of event that + * are delivered by this mechanism: + * 1. Bi-directional inter- and intra-domain connections. Domains must + * arrange out-of-band to set up a connection (usually by allocating + * an unbound 'listener' port and avertising that via a storage service + * such as xenstore). + * 2. Physical interrupts. A domain with suitable hardware-access + * privileges can bind an event-channel port to a physical interrupt + * source. + * 3. Virtual interrupts ('events'). A domain can bind an event-channel + * port to a virtual interrupt source, such as the virtual-timer + * device or the emergency console. + * + * Event channels are addressed by a "port index". Each channel is + * associated with two bits of information: + * 1. PENDING -- notifies the domain that there is a pending notification + * to be processed. This bit is cleared by the guest. + * 2. MASK -- if this bit is clear then a 0->1 transition of PENDING + * will cause an asynchronous upcall to be scheduled. This bit is only + * updated by the guest. It is read-only within Xen. If a channel + * becomes pending while the channel is masked then the 'edge' is lost + * (i.e., when the channel is unmasked, the guest must manually handle + * pending notifications as no upcall will be scheduled by Xen). + * + * To expedite scanning of pending notifications, any 0->1 pending + * transition on an unmasked channel causes a corresponding bit in a + * per-vcpu selector word to be set. Each bit in the selector covers a + * 'C long' in the PENDING bitfield array. + */ + xen_ulong_t evtchn_pending[sizeof(xen_ulong_t) * 8]; + xen_ulong_t evtchn_mask[sizeof(xen_ulong_t) * 8]; + + /* + * Wallclock time: updated only by control software. Guests should base + * their gettimeofday() syscall on this wallclock-base value. + */ + uint32_t wc_version; /* Version counter: see vcpu_time_info_t. */ + uint32_t wc_sec; /* Secs 00:00:00 UTC, Jan 1, 1970. */ + uint32_t wc_nsec; /* Nsecs 00:00:00 UTC, Jan 1, 1970. */ + + struct arch_shared_info arch; + +}; +#ifndef __XEN__ +typedef struct shared_info shared_info_t; +#endif + +/* + * `incontents 200 startofday Start-of-day memory layout + * + * 1. The domain is started within contiguous virtual-memory region. + * 2. The contiguous region ends on an aligned 4MB boundary. + * 3. This the order of bootstrap elements in the initial virtual region: + * a. relocated kernel image + * b. initial ram disk [mod_start, mod_len] + * c. list of allocated page frames [mfn_list, nr_pages] + * (unless relocated due to XEN_ELFNOTE_INIT_P2M) + * d. start_info_t structure [register ESI (x86)] + * e. bootstrap page tables [pt_base and CR3 (x86)] + * f. bootstrap stack [register ESP (x86)] + * 4. Bootstrap elements are packed together, but each is 4kB-aligned. + * 5. The initial ram disk may be omitted. + * 6. The list of page frames forms a contiguous 'pseudo-physical' memory + * layout for the domain. In particular, the bootstrap virtual-memory + * region is a 1:1 mapping to the first section of the pseudo-physical map. + * 7. All bootstrap elements are mapped read-writable for the guest OS. The + * only exception is the bootstrap page table, which is mapped read-only. + * 8. There is guaranteed to be at least 512kB padding after the final + * bootstrap element. If necessary, the bootstrap virtual region is + * extended by an extra 4MB to ensure this. + * + * Note: Prior to 25833:bb85bbccb1c9. ("x86/32-on-64 adjust Dom0 initial page + * table layout") a bug caused the pt_base (3.e above) and cr3 to not point + * to the start of the guest page tables (it was offset by two pages). + * This only manifested itself on 32-on-64 dom0 kernels and not 32-on-64 domU + * or 64-bit kernels of any colour. The page tables for a 32-on-64 dom0 got + * allocated in the order: 'first L1','first L2', 'first L3', so the offset + * to the page table base is by two pages back. The initial domain if it is + * 32-bit and runs under a 64-bit hypervisor should _NOT_ use two of the + * pages preceding pt_base and mark them as reserved/unused. + */ +#ifdef XEN_HAVE_PV_GUEST_ENTRY +struct start_info { + /* THE FOLLOWING ARE FILLED IN BOTH ON INITIAL BOOT AND ON RESUME. */ + char magic[32]; /* "xen--". */ + unsigned long nr_pages; /* Total pages allocated to this domain. */ + unsigned long shared_info; /* MACHINE address of shared info struct. */ + uint32_t flags; /* SIF_xxx flags. */ + xen_pfn_t store_mfn; /* MACHINE page number of shared page. */ + uint32_t store_evtchn; /* Event channel for store communication. */ + union { + struct { + xen_pfn_t mfn; /* MACHINE page number of console page. */ + uint32_t evtchn; /* Event channel for console page. */ + } domU; + struct { + uint32_t info_off; /* Offset of console_info struct. */ + uint32_t info_size; /* Size of console_info struct from start.*/ + } dom0; + } console; + /* THE FOLLOWING ARE ONLY FILLED IN ON INITIAL BOOT (NOT RESUME). */ + unsigned long pt_base; /* VIRTUAL address of page directory. */ + unsigned long nr_pt_frames; /* Number of bootstrap p.t. frames. */ + unsigned long mfn_list; /* VIRTUAL address of page-frame list. */ + unsigned long mod_start; /* VIRTUAL address of pre-loaded module */ + /* (PFN of pre-loaded module if */ + /* SIF_MOD_START_PFN set in flags). */ + unsigned long mod_len; /* Size (bytes) of pre-loaded module. */ +#define MAX_GUEST_CMDLINE 1024 + int8_t cmd_line[MAX_GUEST_CMDLINE]; + /* The pfn range here covers both page table and p->m table frames. */ + unsigned long first_p2m_pfn;/* 1st pfn forming initial P->M table. */ + unsigned long nr_p2m_frames;/* # of pfns forming initial P->M table. */ +}; +typedef struct start_info start_info_t; + +/* New console union for dom0 introduced in 0x00030203. */ +#if __XEN_INTERFACE_VERSION__ < 0x00030203 +#define console_mfn console.domU.mfn +#define console_evtchn console.domU.evtchn +#endif +#endif /* XEN_HAVE_PV_GUEST_ENTRY */ + +/* These flags are passed in the 'flags' field of start_info_t. */ +#define SIF_PRIVILEGED (1<<0) /* Is the domain privileged? */ +#define SIF_INITDOMAIN (1<<1) /* Is this the initial control domain? */ +#define SIF_MULTIBOOT_MOD (1<<2) /* Is mod_start a multiboot module? */ +#define SIF_MOD_START_PFN (1<<3) /* Is mod_start a PFN? */ +#define SIF_PM_MASK (0xFF<<8) /* reserve 1 byte for xen-pm options */ + +/* + * A multiboot module is a package containing modules very similar to a + * multiboot module array. The only differences are: + * - the array of module descriptors is by convention simply at the beginning + * of the multiboot module, + * - addresses in the module descriptors are based on the beginning of the + * multiboot module, + * - the number of modules is determined by a termination descriptor that has + * mod_start == 0. + * + * This permits to both build it statically and reference it in a configuration + * file, and let the PV guest easily rebase the addresses to virtual addresses + * and at the same time count the number of modules. + */ +struct xen_multiboot_mod_list +{ + /* Address of first byte of the module */ + uint32_t mod_start; + /* Address of last byte of the module (inclusive) */ + uint32_t mod_end; + /* Address of zero-terminated command line */ + uint32_t cmdline; + /* Unused, must be zero */ + uint32_t pad; +}; +/* + * `incontents 200 startofday_dom0_console Dom0_console + * + * The console structure in start_info.console.dom0 + * + * This structure includes a variety of information required to + * have a working VGA/VESA console. + */ +typedef struct dom0_vga_console_info { + uint8_t video_type; /* DOM0_VGA_CONSOLE_??? */ +#define XEN_VGATYPE_TEXT_MODE_3 0x03 +#define XEN_VGATYPE_VESA_LFB 0x23 +#define XEN_VGATYPE_EFI_LFB 0x70 + + union { + struct { + /* Font height, in pixels. */ + uint16_t font_height; + /* Cursor location (column, row). */ + uint16_t cursor_x, cursor_y; + /* Number of rows and columns (dimensions in characters). */ + uint16_t rows, columns; + } text_mode_3; + + struct { + /* Width and height, in pixels. */ + uint16_t width, height; + /* Bytes per scan line. */ + uint16_t bytes_per_line; + /* Bits per pixel. */ + uint16_t bits_per_pixel; + /* LFB physical address, and size (in units of 64kB). */ + uint32_t lfb_base; + uint32_t lfb_size; + /* RGB mask offsets and sizes, as defined by VBE 1.2+ */ + uint8_t red_pos, red_size; + uint8_t green_pos, green_size; + uint8_t blue_pos, blue_size; + uint8_t rsvd_pos, rsvd_size; +#if __XEN_INTERFACE_VERSION__ >= 0x00030206 + /* VESA capabilities (offset 0xa, VESA command 0x4f00). */ + uint32_t gbl_caps; + /* Mode attributes (offset 0x0, VESA command 0x4f01). */ + uint16_t mode_attrs; +#endif + } vesa_lfb; + } u; +} dom0_vga_console_info_t; +#define xen_vga_console_info dom0_vga_console_info +#define xen_vga_console_info_t dom0_vga_console_info_t + +typedef uint8_t xen_domain_handle_t[16]; + +/* Turn a plain number into a C unsigned long constant. */ +#define __mk_unsigned_long(x) x ## UL +#define mk_unsigned_long(x) __mk_unsigned_long(x) + +__DEFINE_XEN_GUEST_HANDLE(uint8, uint8_t); +__DEFINE_XEN_GUEST_HANDLE(uint16, uint16_t); +__DEFINE_XEN_GUEST_HANDLE(uint32, uint32_t); +__DEFINE_XEN_GUEST_HANDLE(uint64, uint64_t); + +#else /* __ASSEMBLY__ */ + +/* In assembly code we cannot use C numeric constant suffixes. */ +#define mk_unsigned_long(x) x + +#endif /* !__ASSEMBLY__ */ + +/* Default definitions for macros used by domctl/sysctl. */ +#if defined(__XEN__) || defined(__XEN_TOOLS__) + +#ifndef uint64_aligned_t +#define uint64_aligned_t uint64_t +#endif +#ifndef XEN_GUEST_HANDLE_64 +#define XEN_GUEST_HANDLE_64(name) XEN_GUEST_HANDLE(name) +#endif + +#ifndef __ASSEMBLY__ +struct xenctl_bitmap { + XEN_GUEST_HANDLE_64(uint8) bitmap; + uint32_t nr_bits; +}; +#endif + +#endif /* defined(__XEN__) || defined(__XEN_TOOLS__) */ + +#endif /* __XEN_PUBLIC_XEN_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/roms/ipxe/src/interface/efi/efi_autoboot.c b/roms/ipxe/src/interface/efi/efi_autoboot.c new file mode 100644 index 0000000..ab0f365 --- /dev/null +++ b/roms/ipxe/src/interface/efi/efi_autoboot.c @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2014 Michael Brown . + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include + +/** @file + * + * EFI autoboot device + * + */ + +/** + * Identify autoboot device + * + */ +void efi_set_autoboot ( void ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + union { + EFI_SIMPLE_NETWORK_PROTOCOL *snp; + void *interface; + } snp; + EFI_SIMPLE_NETWORK_MODE *mode; + EFI_STATUS efirc; + + /* Look for an SNP instance on the image's device handle */ + if ( ( efirc = bs->OpenProtocol ( efi_loaded_image->DeviceHandle, + &efi_simple_network_protocol_guid, + &snp.interface, efi_image_handle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ + DBGC ( efi_loaded_image, "EFI found no autoboot device\n" ); + return; + } + + /* Record autoboot device */ + mode = snp.snp->Mode; + set_autoboot_ll_addr ( &mode->CurrentAddress, mode->HwAddressSize ); + DBGC ( efi_loaded_image, "EFI found autoboot link-layer address:\n" ); + DBGC_HDA ( efi_loaded_image, 0, &mode->CurrentAddress, + mode->HwAddressSize ); + + /* Close protocol */ + bs->CloseProtocol ( efi_loaded_image->DeviceHandle, + &efi_simple_network_protocol_guid, + efi_image_handle, NULL ); +} diff --git a/roms/ipxe/src/interface/efi/efi_bofm.c b/roms/ipxe/src/interface/efi/efi_bofm.c index 4982b22..bdb7051 100644 --- a/roms/ipxe/src/interface/efi/efi_bofm.c +++ b/roms/ipxe/src/interface/efi/efi_bofm.c @@ -21,7 +21,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include -#include #include #include #include @@ -156,49 +155,37 @@ static EFI_GUID bofm2_protocol_guid = /** * Check if device is supported * - * @v driver EFI driver - * @v device EFI device - * @v child Path to child device, if any - * @ret efirc EFI status code + * @v device EFI device handle + * @ret rc Return status code */ -static EFI_STATUS EFIAPI -efi_bofm_supported ( EFI_DRIVER_BINDING_PROTOCOL *driver, - EFI_HANDLE device, - EFI_DEVICE_PATH_PROTOCOL *child ) { - struct efi_driver *efidrv = - container_of ( driver, struct efi_driver, driver ); +static int efi_bofm_supported ( EFI_HANDLE device ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + struct pci_device pci; union { IBM_BOFM_DRIVER_CONFIGURATION_PROTOCOL *bofm1; void *interface; } bofm1; - struct efi_pci_device *efipci; EFI_STATUS efirc; int rc; - DBGCP ( efidrv, "EFIBOFM DRIVER_SUPPORTED %p (%p)\n", device, child ); - - /* Create corresponding PCI device, if any */ - efipci = efipci_create ( efidrv, device ); - if ( ! efipci ) { - rc = -ENOTSUP; - goto err_not_pci; - } + /* Get PCI device information */ + if ( ( rc = efipci_info ( device, &pci ) ) != 0 ) + return rc; /* Look for a BOFM driver */ - if ( ( rc = bofm_find_driver ( &efipci->pci ) ) != 0 ) { - DBGCP ( efidrv, "EFIBOFM " PCI_FMT " has no driver\n", - PCI_ARGS ( &efipci->pci ) ); - goto err_no_driver; + if ( ( rc = bofm_find_driver ( &pci ) ) != 0 ) { + DBGCP ( device, "EFIBOFM %p %s has no driver\n", + device, efi_handle_name ( device ) ); + return rc; } /* Locate BOFM protocol */ if ( ( efirc = bs->LocateProtocol ( &bofm1_protocol_guid, NULL, &bofm1.interface ) ) != 0 ) { rc = -EEFI ( efirc ); - DBGC ( efidrv, "EFIBOFM " PCI_FMT " cannot find BOFM " - "protocol\n", PCI_ARGS ( &efipci->pci ) ); - goto err_not_bofm; + DBGC ( device, "EFIBOFM %p %s cannot find BOFM protocol\n", + device, efi_handle_name ( device ) ); + return rc; } /* Register support for this device */ @@ -207,42 +194,25 @@ efi_bofm_supported ( EFI_DRIVER_BINDING_PROTOCOL *driver, 0x00 /* No iSCSI */, 0x02 /* Version */ ))!=0){ rc = -EEFI ( efirc ); - DBGC ( efidrv, "EFIBOFM " PCI_FMT " could not register " - "support: %s\n", PCI_ARGS ( &efipci->pci ), - strerror ( rc ) ); - goto err_cannot_register; + DBGC ( device, "EFIBOFM %p %s could not register support: %s\n", + device, efi_handle_name ( device ), strerror ( rc ) ); + return rc; } - DBGC ( efidrv, "EFIBOFM " PCI_FMT " is supported by driver \"%s\"\n", - PCI_ARGS ( &efipci->pci ), efipci->pci.id->name ); - - /* Destroy temporary PCI device */ - efipci_destroy ( efidrv, efipci ); - + DBGC ( device, "EFIBOFM %p %s has driver \"%s\"\n", + device, efi_handle_name ( device ), pci.id->name ); return 0; - - err_cannot_register: - err_not_bofm: - err_no_driver: - efipci_destroy ( efidrv, efipci ); - err_not_pci: - return EFIRC ( rc ); } /** * Attach driver to device * - * @v driver EFI driver - * @v device EFI device - * @v child Path to child device, if any - * @ret efirc EFI status code + * @v efidev EFI device + * @ret rc Return status code */ -static EFI_STATUS EFIAPI efi_bofm_start ( EFI_DRIVER_BINDING_PROTOCOL *driver, - EFI_HANDLE device, - EFI_DEVICE_PATH_PROTOCOL *child ) { - struct efi_driver *efidrv = - container_of ( driver, struct efi_driver, driver ); +static int efi_bofm_start ( struct efi_device *efidev ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE device = efidev->device; union { IBM_BOFM_DRIVER_CONFIGURATION_PROTOCOL *bofm1; void *interface; @@ -251,75 +221,66 @@ static EFI_STATUS EFIAPI efi_bofm_start ( EFI_DRIVER_BINDING_PROTOCOL *driver, IBM_BOFM_DRIVER_CONFIGURATION_PROTOCOL2 *bofm2; void *interface; } bofm2; - struct efi_pci_device *efipci; + struct pci_device pci; IBM_BOFM_TABLE *bofmtab; IBM_BOFM_TABLE *bofmtab2; int bofmrc; EFI_STATUS efirc; int rc; - DBGCP ( efidrv, "EFIBOFM DRIVER_START %p (%p)\n", device, child ); - - /* Create corresponding PCI device */ - efipci = efipci_create ( efidrv, device ); - if ( ! efipci ) { - rc = -ENOMEM; - goto err_create; - } - - /* Enable PCI device */ - if ( ( rc = efipci_enable ( efipci ) ) != 0 ) - goto err_enable; + /* Open PCI device, if possible */ + if ( ( rc = efipci_open ( device, EFI_OPEN_PROTOCOL_GET_PROTOCOL, + &pci ) ) != 0 ) + goto err_open; /* Locate BOFM protocol */ if ( ( efirc = bs->LocateProtocol ( &bofm1_protocol_guid, NULL, &bofm1.interface ) ) != 0 ) { rc = -EEFI ( efirc ); - DBGC ( efidrv, "EFIBOFM " PCI_FMT " cannot find BOFM " - "protocol\n", PCI_ARGS ( &efipci->pci ) ); + DBGC ( device, "EFIBOFM %p %s cannot find BOFM protocol\n", + device, efi_handle_name ( device ) ); goto err_locate_bofm; } bofmtab = &bofm1.bofm1->BofmTable; - DBGC ( efidrv, "EFIBOFM " PCI_FMT " found version 1 BOFM table at " - "%p+%04x\n", PCI_ARGS ( &efipci->pci ), bofmtab, + DBGC ( device, "EFIBOFM %p %s found version 1 BOFM table at %p+%04x\n", + device, efi_handle_name ( device ), bofmtab, bofmtab->Parameters.Length ); /* Locate BOFM2 protocol, if available */ if ( ( efirc = bs->LocateProtocol ( &bofm2_protocol_guid, NULL, &bofm2.interface ) ) == 0 ) { bofmtab2 = &bofm2.bofm2->BofmTable; - DBGC ( efidrv, "EFIBOFM " PCI_FMT " found version 2 BOFM table " - "at %p+%04x\n", PCI_ARGS ( &efipci->pci ), bofmtab2, - bofmtab2->Parameters.Length ); + DBGC ( device, "EFIBOFM %p %s found version 2 BOFM table at " + "%p+%04x\n", device, efi_handle_name ( device ), + bofmtab2, bofmtab2->Parameters.Length ); assert ( bofm2.bofm2->RegisterSupport == bofm1.bofm1->RegisterSupport ); } else { - DBGC ( efidrv, "EFIBOFM " PCI_FMT " cannot find BOFM2 " - "protocol\n", PCI_ARGS ( &efipci->pci ) ); + DBGC ( device, "EFIBOFM %p %s cannot find BOFM2 protocol\n", + device, efi_handle_name ( device ) ); /* Not a fatal error; may be a BOFM1-only system */ bofmtab2 = NULL; } /* Process BOFM table */ - DBGC2 ( efidrv, "EFIBOFM " PCI_FMT " version 1 before processing:\n", - PCI_ARGS ( &efipci->pci ) ); - DBGC2_HD ( efidrv, bofmtab, bofmtab->Parameters.Length ); + DBGC2 ( device, "EFIBOFM %p %s version 1 before processing:\n", + device, efi_handle_name ( device ) ); + DBGC2_HD ( device, bofmtab, bofmtab->Parameters.Length ); if ( bofmtab2 ) { - DBGC2 ( efidrv, "EFIBOFM " PCI_FMT " version 2 before " - "processing:\n", PCI_ARGS ( &efipci->pci ) ); - DBGC2_HD ( efidrv, bofmtab2, bofmtab2->Parameters.Length ); + DBGC2 ( device, "EFIBOFM %p %s version 2 before processing:\n", + device, efi_handle_name ( device ) ); + DBGC2_HD ( device, bofmtab2, bofmtab2->Parameters.Length ); } - bofmrc = bofm ( virt_to_user ( bofmtab2 ? bofmtab2 : bofmtab ), - &efipci->pci ); - DBGC ( efidrv, "EFIBOFM " PCI_FMT " status %08x\n", - PCI_ARGS ( &efipci->pci ), bofmrc ); - DBGC2 ( efidrv, "EFIBOFM " PCI_FMT " version 1 after processing:\n", - PCI_ARGS ( &efipci->pci ) ); - DBGC2_HD ( efidrv, bofmtab, bofmtab->Parameters.Length ); + bofmrc = bofm ( virt_to_user ( bofmtab2 ? bofmtab2 : bofmtab ), &pci ); + DBGC ( device, "EFIBOFM %p %s status %08x\n", + device, efi_handle_name ( device ), bofmrc ); + DBGC2 ( device, "EFIBOFM %p %s version 1 after processing:\n", + device, efi_handle_name ( device ) ); + DBGC2_HD ( device, bofmtab, bofmtab->Parameters.Length ); if ( bofmtab2 ) { - DBGC2 ( efidrv, "EFIBOFM " PCI_FMT " version 2 after " - "processing:\n", PCI_ARGS ( &efipci->pci ) ); - DBGC2_HD ( efidrv, bofmtab2, bofmtab2->Parameters.Length ); + DBGC2 ( device, "EFIBOFM %p %s version 2 after processing:\n", + device, efi_handle_name ( device ) ); + DBGC2_HD ( device, bofmtab2, bofmtab2->Parameters.Length ); } /* Return BOFM status */ @@ -327,93 +288,47 @@ static EFI_STATUS EFIAPI efi_bofm_start ( EFI_DRIVER_BINDING_PROTOCOL *driver, if ( ( efirc = bofm2.bofm2->SetStatus ( bofm2.bofm2, device, FALSE, bofmrc ) ) != 0){ rc = -EEFI ( efirc ); - DBGC ( efidrv, "EFIBOFM " PCI_FMT " could not set " - "BOFM2 status: %s\n", PCI_ARGS ( &efipci->pci ), - strerror ( rc ) ); + DBGC ( device, "EFIBOFM %p %s could not set BOFM2 " + "status: %s\n", device, + efi_handle_name ( device ), strerror ( rc ) ); goto err_set_status; } } else { if ( ( efirc = bofm1.bofm1->SetStatus ( bofm1.bofm1, device, FALSE, bofmrc ) ) != 0){ rc = -EEFI ( efirc ); - DBGC ( efidrv, "EFIBOFM " PCI_FMT " could not set " - "BOFM status: %s\n", PCI_ARGS ( &efipci->pci ), - strerror ( rc ) ); + DBGC ( device, "EFIBOFM %p %s could not set BOFM " + "status: %s\n", device, + efi_handle_name ( device ), strerror ( rc ) ); goto err_set_status; } } - /* Destroy the PCI device anyway; we have no further use for it */ - efipci_destroy ( efidrv, efipci ); - /* BOFM (ab)uses the "start" method to mean "process and exit" */ - return EFI_NOT_READY; + rc = -EAGAIN; err_set_status: err_locate_bofm: - err_enable: - efipci_destroy ( efidrv, efipci ); - err_create: - return EFIRC ( rc ); + efipci_close ( device ); + err_open: + return rc; } /** * Detach driver from device * - * @v driver EFI driver * @v device EFI device - * @v num_children Number of child devices - * @v children List of child devices - * @ret efirc EFI status code */ -static EFI_STATUS EFIAPI efi_bofm_stop ( EFI_DRIVER_BINDING_PROTOCOL *driver, - EFI_HANDLE device, UINTN num_children, - EFI_HANDLE *children ) { - struct efi_driver *efidrv = - container_of ( driver, struct efi_driver, driver ); - - DBGCP ( efidrv, "EFIBOFM DRIVER_STOP %p (%ld %p)\n", - device, ( ( unsigned long ) num_children ), children ); +static void efi_bofm_stop ( struct efi_device *efidev __unused ) { - return 0; + /* Should never happen */ + assert ( 0 ); } /** EFI BOFM driver */ -static struct efi_driver efi_bofm_driver = - EFI_DRIVER_INIT ( "BOFM", - efi_bofm_supported, efi_bofm_start, efi_bofm_stop ); - -/** - * Install EFI BOFM driver - * - */ -static void efi_bofm_driver_startup ( void ) { - struct efi_driver *efidrv = &efi_bofm_driver; - int rc; - - /* Install driver */ - if ( ( rc = efi_driver_install ( efidrv ) ) != 0 ) { - DBGC ( efidrv, "EFIBOFM could not install driver: %s\n", - strerror ( rc ) ); - return; - } - - DBGC ( efidrv, "EFIBOFM driver installed\n" ); -} - -/** - * Shut down EFI BOFM driver - * - * @v booting System is shutting down for OS boot - */ -static void efi_bofm_driver_shutdown ( int booting __unused ) { - struct efi_driver *efidrv = &efi_bofm_driver; - - efi_driver_uninstall ( efidrv ); -} - -/** EFI BOFM startup function */ -struct startup_fn startup_bofm __startup_fn ( STARTUP_EARLY ) = { - .startup = efi_bofm_driver_startup, - .shutdown = efi_bofm_driver_shutdown, +struct efi_driver efi_bofm_driver __efi_driver ( EFI_DRIVER_EARLY ) = { + .name = "BOFM", + .supported = efi_bofm_supported, + .start = efi_bofm_start, + .stop = efi_bofm_stop, }; diff --git a/roms/ipxe/src/interface/efi/efi_console.c b/roms/ipxe/src/interface/efi/efi_console.c index af60d4f..3b30f30 100644 --- a/roms/ipxe/src/interface/efi/efi_console.c +++ b/roms/ipxe/src/interface/efi/efi_console.c @@ -24,8 +24,10 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include #include +#include #include #define ATTR_BOLD 0x08 @@ -61,6 +63,10 @@ FILE_LICENCE ( GPL2_OR_LATER ); /** Current character attribute */ static unsigned int efi_attr = ATTR_DEFAULT; +/** Console control protocol */ +static EFI_CONSOLE_CONTROL_PROTOCOL *conctrl; +EFI_REQUEST_PROTOCOL ( EFI_CONSOLE_CONTROL_PROTOCOL, &conctrl ); + /** * Handle ANSI CUP (cursor position) * @@ -146,11 +152,43 @@ static void efi_handle_sgr ( struct ansiesc_context *ctx __unused, conout->SetAttribute ( conout, efi_attr ); } +/** + * Handle ANSI DECTCEM set (show cursor) + * + * @v ctx ANSI escape sequence context + * @v count Parameter count + * @v params List of graphic rendition aspects + */ +static void efi_handle_dectcem_set ( struct ansiesc_context *ctx __unused, + unsigned int count __unused, + int params[] __unused ) { + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *conout = efi_systab->ConOut; + + conout->EnableCursor ( conout, TRUE ); +} + +/** + * Handle ANSI DECTCEM reset (hide cursor) + * + * @v ctx ANSI escape sequence context + * @v count Parameter count + * @v params List of graphic rendition aspects + */ +static void efi_handle_dectcem_reset ( struct ansiesc_context *ctx __unused, + unsigned int count __unused, + int params[] __unused ) { + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *conout = efi_systab->ConOut; + + conout->EnableCursor ( conout, FALSE ); +} + /** EFI console ANSI escape sequence handlers */ static struct ansiesc_handler efi_ansiesc_handlers[] = { { ANSIESC_CUP, efi_handle_cup }, { ANSIESC_ED, efi_handle_ed }, { ANSIESC_SGR, efi_handle_sgr }, + { ANSIESC_DECTCEM_SET, efi_handle_dectcem_set }, + { ANSIESC_DECTCEM_RESET, efi_handle_dectcem_reset }, { 0, NULL } }; @@ -286,9 +324,37 @@ static int efi_iskey ( void ) { return 0; } +/** EFI console driver */ struct console_driver efi_console __console_driver = { .putchar = efi_putchar, .getchar = efi_getchar, .iskey = efi_iskey, .usage = CONSOLE_EFI, }; + +/** + * Initialise EFI console + * + */ +static void efi_console_init ( void ) { + EFI_CONSOLE_CONTROL_SCREEN_MODE mode; + + /* On some older EFI 1.10 implementations, we must use the + * (now obsolete) EFI_CONSOLE_CONTROL_PROTOCOL to switch the + * console into text mode. + */ + if ( conctrl ) { + conctrl->GetMode ( conctrl, &mode, NULL, NULL ); + if ( mode != EfiConsoleControlScreenText ) { + conctrl->SetMode ( conctrl, + EfiConsoleControlScreenText ); + } + } +} + +/** + * EFI console initialisation function + */ +struct init_fn efi_console_init_fn __init_fn ( INIT_EARLY ) = { + .initialise = efi_console_init, +}; diff --git a/roms/ipxe/src/interface/efi/efi_debug.c b/roms/ipxe/src/interface/efi/efi_debug.c index 4ed6660..d239601 100644 --- a/roms/ipxe/src/interface/efi/efi_debug.c +++ b/roms/ipxe/src/interface/efi/efi_debug.c @@ -30,14 +30,119 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include -#include -#include +#include +#include +#include #include +#include /** Device path to text protocol */ static EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *efidpt; -EFI_REQUIRE_PROTOCOL ( EFI_DEVICE_PATH_TO_TEXT_PROTOCOL, &efidpt ); +EFI_REQUEST_PROTOCOL ( EFI_DEVICE_PATH_TO_TEXT_PROTOCOL, &efidpt ); + +/** Iscsi4Dxe module GUID */ +static EFI_GUID efi_iscsi4_dxe_guid = { + 0x4579b72d, 0x7ec4, 0x4dd4, + { 0x84, 0x86, 0x08, 0x3c, 0x86, 0xb1, 0x82, 0xa7 } +}; + +/** VlanConfigDxe module GUID */ +static EFI_GUID efi_vlan_config_dxe_guid = { + 0xe4f61863, 0xfe2c, 0x4b56, + { 0xa8, 0xf4, 0x08, 0x51, 0x9b, 0xc4, 0x39, 0xdf } +}; + +/** A well-known GUID */ +struct efi_well_known_guid { + /** GUID */ + EFI_GUID *guid; + /** Name */ + const char *name; +}; + +/** Well-known GUIDs */ +static struct efi_well_known_guid efi_well_known_guids[] = { + { &efi_arp_protocol_guid, + "Arp" }, + { &efi_arp_service_binding_protocol_guid, + "ArpSb" }, + { &efi_block_io_protocol_guid, + "BlockIo" }, + { &efi_bus_specific_driver_override_protocol_guid, + "BusSpecificDriverOverride" }, + { &efi_component_name_protocol_guid, + "ComponentName" }, + { &efi_component_name2_protocol_guid, + "ComponentName2" }, + { &efi_device_path_protocol_guid, + "DevicePath" }, + { &efi_driver_binding_protocol_guid, + "DriverBinding" }, + { &efi_dhcp4_protocol_guid, + "Dhcp4" }, + { &efi_dhcp4_service_binding_protocol_guid, + "Dhcp4Sb" }, + { &efi_disk_io_protocol_guid, + "DiskIo" }, + { &efi_graphics_output_protocol_guid, + "GraphicsOutput" }, + { &efi_hii_config_access_protocol_guid, + "HiiConfigAccess" }, + { &efi_ip4_protocol_guid, + "Ip4" }, + { &efi_ip4_config_protocol_guid, + "Ip4Config" }, + { &efi_ip4_service_binding_protocol_guid, + "Ip4Sb" }, + { &efi_iscsi4_dxe_guid, + "IScsi4Dxe" }, + { &efi_load_file_protocol_guid, + "LoadFile" }, + { &efi_load_file2_protocol_guid, + "LoadFile2" }, + { &efi_loaded_image_protocol_guid, + "LoadedImage" }, + { &efi_loaded_image_device_path_protocol_guid, + "LoadedImageDevicePath"}, + { &efi_managed_network_protocol_guid, + "ManagedNetwork" }, + { &efi_managed_network_service_binding_protocol_guid, + "ManagedNetworkSb" }, + { &efi_mtftp4_protocol_guid, + "Mtftp4" }, + { &efi_mtftp4_service_binding_protocol_guid, + "Mtftp4Sb" }, + { &efi_nii_protocol_guid, + "Nii" }, + { &efi_nii31_protocol_guid, + "Nii31" }, + { &efi_pci_io_protocol_guid, + "PciIo" }, + { &efi_pci_root_bridge_io_protocol_guid, + "PciRootBridgeIo" }, + { &efi_pxe_base_code_protocol_guid, + "PxeBaseCode" }, + { &efi_simple_file_system_protocol_guid, + "SimpleFileSystem" }, + { &efi_simple_network_protocol_guid, + "SimpleNetwork" }, + { &efi_tcg_protocol_guid, + "Tcg" }, + { &efi_tcp4_protocol_guid, + "Tcp4" }, + { &efi_tcp4_service_binding_protocol_guid, + "Tcp4Sb" }, + { &efi_udp4_protocol_guid, + "Udp4" }, + { &efi_udp4_service_binding_protocol_guid, + "Udp4Sb" }, + { &efi_vlan_config_protocol_guid, + "VlanConfig" }, + { &efi_vlan_config_dxe_guid, + "VlanConfigDxe" }, +}; /** * Convert GUID to a printable string @@ -50,6 +155,20 @@ const char * efi_guid_ntoa ( EFI_GUID *guid ) { union uuid uuid; EFI_GUID guid; } u; + unsigned int i; + + /* Sanity check */ + if ( ! guid ) + return NULL; + + /* Check for a match against well-known GUIDs */ + for ( i = 0 ; i < ( sizeof ( efi_well_known_guids ) / + sizeof ( efi_well_known_guids[0] ) ) ; i++ ) { + if ( memcmp ( guid, efi_well_known_guids[i].guid, + sizeof ( *guid ) ) == 0 ) { + return efi_well_known_guids[i].name; + } + } /* Convert GUID to standard endianness */ memcpy ( &u.guid, guid, sizeof ( u.guid ) ); @@ -58,18 +177,103 @@ const char * efi_guid_ntoa ( EFI_GUID *guid ) { } /** + * Name protocol open attributes + * + * @v attributes Protocol open attributes + * @ret name Protocol open attributes name + * + * Returns a (static) string with characters for each set bit + * corresponding to BY_(H)ANDLE_PROTOCOL, (G)ET_PROTOCOL, + * (T)EST_PROTOCOL, BY_(C)HILD_CONTROLLER, BY_(D)RIVER, and + * E(X)CLUSIVE. + */ +static const char * efi_open_attributes_name ( unsigned int attributes ) { + static char attribute_chars[] = "HGTCDX"; + static char name[ sizeof ( attribute_chars ) ]; + char *tmp = name; + unsigned int i; + + for ( i = 0 ; i < ( sizeof ( attribute_chars ) - 1 ) ; i++ ) { + if ( attributes & ( 1 << i ) ) + *(tmp++) = attribute_chars[i]; + } + *tmp = '\0'; + + return name; +} + +/** + * Print list of openers of a given protocol on a given handle + * + * @v handle EFI handle + * @v protocol Protocol GUID + */ +void dbg_efi_openers ( EFI_HANDLE handle, EFI_GUID *protocol ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *openers; + EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *opener; + UINTN count; + unsigned int i; + EFI_STATUS efirc; + int rc; + + /* Sanity check */ + if ( ( ! handle ) || ( ! protocol ) ) { + printf ( "EFI could not retrieve openers for %s on %p\n", + efi_guid_ntoa ( protocol ), handle ); + return; + } + + /* Retrieve list of openers */ + if ( ( efirc = bs->OpenProtocolInformation ( handle, protocol, &openers, + &count ) ) != 0 ) { + rc = -EEFI ( efirc ); + printf ( "EFI could not retrieve openers for %s on %p: %s\n", + efi_guid_ntoa ( protocol ), handle, strerror ( rc ) ); + return; + } + + /* Dump list of openers */ + for ( i = 0 ; i < count ; i++ ) { + opener = &openers[i]; + printf ( "HANDLE %p %s %s opened %dx (%s)", + handle, efi_handle_name ( handle ), + efi_guid_ntoa ( protocol ), opener->OpenCount, + efi_open_attributes_name ( opener->Attributes ) ); + printf ( " by %p %s", opener->AgentHandle, + efi_handle_name ( opener->AgentHandle ) ); + if ( opener->ControllerHandle == handle ) { + printf ( "\n" ); + } else { + printf ( " for %p %s\n", opener->ControllerHandle, + efi_handle_name ( opener->ControllerHandle ) ); + } + } + + /* Free list */ + bs->FreePool ( openers ); +} + +/** * Print list of protocol handlers attached to a handle * * @v handle EFI handle */ void dbg_efi_protocols ( EFI_HANDLE handle ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - EFI_GUID **protocols; + EFI_GUID **protocols; + EFI_GUID *protocol; UINTN count; unsigned int i; EFI_STATUS efirc; int rc; + /* Sanity check */ + if ( ! handle ) { + printf ( "EFI could not retrieve protocols for %p\n", handle ); + return; + } + /* Retrieve list of protocols */ if ( ( efirc = bs->ProtocolsPerHandle ( handle, &protocols, &count ) ) != 0 ) { @@ -80,38 +284,378 @@ void dbg_efi_protocols ( EFI_HANDLE handle ) { } /* Dump list of protocols */ - for ( i = 0 ; i < count ; i++ ) - printf ( "%s\n", efi_guid_ntoa ( protocols[i] ) ); + for ( i = 0 ; i < count ; i++ ) { + protocol = protocols[i]; + printf ( "HANDLE %p %s %s supported\n", + handle, efi_handle_name ( handle ), + efi_guid_ntoa ( protocol ) ); + dbg_efi_openers ( handle, protocol ); + } /* Free list */ bs->FreePool ( protocols ); } /** - * Print device path + * Get textual representation of device path * * @v path Device path + * @ret text Textual representation of device path, or NULL */ -void dbg_efi_devpath ( EFI_DEVICE_PATH_PROTOCOL *path ) { +const char * efi_devpath_text ( EFI_DEVICE_PATH_PROTOCOL *path ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - EFI_DEVICE_PATH_PROTOCOL *end; - CHAR16 *text; + static char text[256]; + void *start; + void *end; + size_t max_len; size_t len; + CHAR16 *wtext; - /* Convert path to a textual representation */ - text = efidpt->ConvertDevicePathToText ( path, TRUE, FALSE ); - if ( ! text ) { - printf ( ":\n" ); + /* Sanity checks */ + if ( ! path ) { + DBG ( "[NULL DevicePath]" ); + return NULL; + } + + /* If we have no DevicePathToText protocol then use a raw hex string */ + if ( ! efidpt ) { + DBG ( "[No DevicePathToText]" ); + start = path; end = efi_devpath_end ( path ); - len = ( ( ( void * ) end ) - ( ( void * ) path ) + - sizeof ( *end ) ); - dbg_hex_dump_da ( 0, path, len ); - return; + len = ( end - start ); + max_len = ( ( sizeof ( text ) - 1 /* NUL */ ) / 2 /* "xx" */ ); + if ( len > max_len ) + len = max_len; + base16_encode ( start, len, text ); + return text; } - /* Print path */ - printf ( "%ls", text ); + /* Convert path to a textual representation */ + wtext = efidpt->ConvertDevicePathToText ( path, TRUE, FALSE ); + if ( ! wtext ) + return NULL; + + /* Store path in buffer */ + snprintf ( text, sizeof ( text ), "%ls", wtext ); /* Free path */ - bs->FreePool ( text ); + bs->FreePool ( wtext ); + + return text; +} + +/** + * Get driver name + * + * @v wtf Component name protocol + * @ret name Driver name, or NULL + */ +static const char * efi_driver_name ( EFI_COMPONENT_NAME_PROTOCOL *wtf ) { + static char name[64]; + CHAR16 *driver_name; + EFI_STATUS efirc; + + /* Sanity check */ + if ( ! wtf ) { + DBG ( "[NULL ComponentName]" ); + return NULL; + } + + /* Try "eng" first; if that fails then try the first language */ + if ( ( ( efirc = wtf->GetDriverName ( wtf, "eng", + &driver_name ) ) != 0 ) && + ( ( efirc = wtf->GetDriverName ( wtf, wtf->SupportedLanguages, + &driver_name ) ) != 0 ) ) { + return NULL; + } + + /* Convert name from CHAR16 to char */ + snprintf ( name, sizeof ( name ), "%ls", driver_name ); + return name; +} + +/** + * Get driver name + * + * @v wtf Component name protocol + * @ret name Driver name, or NULL + */ +static const char * efi_driver_name2 ( EFI_COMPONENT_NAME2_PROTOCOL *wtf ) { + static char name[64]; + CHAR16 *driver_name; + EFI_STATUS efirc; + + /* Sanity check */ + if ( ! wtf ) { + DBG ( "[NULL ComponentName2]" ); + return NULL; + } + + /* Try "en" first; if that fails then try the first language */ + if ( ( ( efirc = wtf->GetDriverName ( wtf, "en", + &driver_name ) ) != 0 ) && + ( ( efirc = wtf->GetDriverName ( wtf, wtf->SupportedLanguages, + &driver_name ) ) != 0 ) ) { + return NULL; + } + + /* Convert name from CHAR16 to char */ + snprintf ( name, sizeof ( name ), "%ls", driver_name ); + return name; +} + +/** + * Get PE/COFF debug filename + * + * @v loaded Loaded image + * @ret name PE/COFF debug filename, or NULL + */ +static const char * +efi_pecoff_debug_name ( EFI_LOADED_IMAGE_PROTOCOL *loaded ) { + static char buf[32]; + EFI_IMAGE_DOS_HEADER *dos; + EFI_IMAGE_OPTIONAL_HEADER_UNION *pe; + EFI_IMAGE_OPTIONAL_HEADER32 *opt32; + EFI_IMAGE_OPTIONAL_HEADER64 *opt64; + EFI_IMAGE_DATA_DIRECTORY *datadir; + EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *debug; + EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY *codeview_nb10; + EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY *codeview_rsds; + EFI_IMAGE_DEBUG_CODEVIEW_MTOC_ENTRY *codeview_mtoc; + uint16_t dos_magic; + uint32_t pe_magic; + uint16_t opt_magic; + uint32_t codeview_magic; + size_t max_len; + char *name; + char *tmp; + + /* Sanity check */ + if ( ! loaded ) { + DBG ( "[NULL LoadedImage]" ); + return NULL; + } + + /* Parse DOS header */ + dos = loaded->ImageBase; + if ( ! dos ) { + DBG ( "[Missing DOS header]" ); + return NULL; + } + dos_magic = dos->e_magic; + if ( dos_magic != EFI_IMAGE_DOS_SIGNATURE ) { + DBG ( "[Bad DOS signature %#04x]", dos_magic ); + return NULL; + } + pe = ( loaded->ImageBase + dos->e_lfanew ); + + /* Parse PE header */ + pe_magic = pe->Pe32.Signature; + if ( pe_magic != EFI_IMAGE_NT_SIGNATURE ) { + DBG ( "[Bad PE signature %#08x]", pe_magic ); + return NULL; + } + opt32 = &pe->Pe32.OptionalHeader; + opt64 = &pe->Pe32Plus.OptionalHeader; + opt_magic = opt32->Magic; + if ( opt_magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC ) { + datadir = opt32->DataDirectory; + } else if ( opt_magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC ) { + datadir = opt64->DataDirectory; + } else { + DBG ( "[Bad optional header signature %#04x]", opt_magic ); + return NULL; + } + + /* Parse data directory entry */ + if ( ! datadir[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress ) { + DBG ( "[Empty debug directory entry]" ); + return NULL; + } + debug = ( loaded->ImageBase + + datadir[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress ); + + /* Parse debug directory entry */ + if ( debug->Type != EFI_IMAGE_DEBUG_TYPE_CODEVIEW ) { + DBG ( "[Not a CodeView debug directory entry (type %d)]", + debug->Type ); + return NULL; + } + codeview_nb10 = ( loaded->ImageBase + debug->RVA ); + codeview_rsds = ( loaded->ImageBase + debug->RVA ); + codeview_mtoc = ( loaded->ImageBase + debug->RVA ); + codeview_magic = codeview_nb10->Signature; + + /* Parse CodeView entry */ + if ( codeview_magic == CODEVIEW_SIGNATURE_NB10 ) { + name = ( ( void * ) ( codeview_nb10 + 1 ) ); + } else if ( codeview_magic == CODEVIEW_SIGNATURE_RSDS ) { + name = ( ( void * ) ( codeview_rsds + 1 ) ); + } else if ( codeview_magic == CODEVIEW_SIGNATURE_MTOC ) { + name = ( ( void * ) ( codeview_mtoc + 1 ) ); + } else { + DBG ( "[Bad CodeView signature %#08x]", codeview_magic ); + return NULL; + } + + /* Sanity check - avoid scanning endlessly through memory */ + max_len = EFI_PAGE_SIZE; /* Reasonably sane */ + if ( strnlen ( name, max_len ) == max_len ) { + DBG ( "[Excessively long or invalid CodeView name]" ); + return NULL; + } + + /* Skip any directory components. We cannot modify this data + * or create a temporary buffer, so do not use basename(). + */ + while ( ( ( tmp = strchr ( name, '/' ) ) != NULL ) || + ( ( tmp = strchr ( name, '\\' ) ) != NULL ) ) { + name = ( tmp + 1 ); + } + + /* Copy base name to buffer */ + snprintf ( buf, sizeof ( buf ), "%s", name ); + + /* Strip file suffix, if present */ + if ( ( tmp = strrchr ( name, '.' ) ) != NULL ) + *tmp = '\0'; + + return name; +} + +/** + * Get initial loaded image name + * + * @v loaded Loaded image + * @ret name Initial loaded image name, or NULL + */ +static const char * +efi_first_loaded_image_name ( EFI_LOADED_IMAGE_PROTOCOL *loaded ) { + + /* Sanity check */ + if ( ! loaded ) { + DBG ( "[NULL LoadedImage]" ); + return NULL; + } + + return ( ( loaded->ParentHandle == NULL ) ? "DxeCore(?)" : NULL ); +} + +/** + * Get loaded image name from file path + * + * @v loaded Loaded image + * @ret name Loaded image name, or NULL + */ +static const char * +efi_loaded_image_filepath_name ( EFI_LOADED_IMAGE_PROTOCOL *loaded ) { + + /* Sanity check */ + if ( ! loaded ) { + DBG ( "[NULL LoadedImage]" ); + return NULL; + } + + return efi_devpath_text ( loaded->FilePath ); +} + +/** An EFI handle name type */ +struct efi_handle_name_type { + /** Protocol */ + EFI_GUID *protocol; + /** + * Get name + * + * @v interface Protocol interface + * @ret name Name of handle, or NULL on failure + */ + const char * ( * name ) ( void *interface ); +}; + +/** + * Define an EFI handle name type + * + * @v protocol Protocol interface + * @v name Method to get name + * @ret type EFI handle name type + */ +#define EFI_HANDLE_NAME_TYPE( protocol, name ) { \ + (protocol), \ + ( const char * ( * ) ( void * ) ) (name), \ + } + +/** EFI handle name types */ +static struct efi_handle_name_type efi_handle_name_types[] = { + /* Device path */ + EFI_HANDLE_NAME_TYPE ( &efi_device_path_protocol_guid, + efi_devpath_text ), + /* Driver name (for driver image handles) */ + EFI_HANDLE_NAME_TYPE ( &efi_component_name2_protocol_guid, + efi_driver_name2 ), + /* Driver name (via obsolete original ComponentName protocol) */ + EFI_HANDLE_NAME_TYPE ( &efi_component_name_protocol_guid, + efi_driver_name ), + /* PE/COFF debug filename (for image handles) */ + EFI_HANDLE_NAME_TYPE ( &efi_loaded_image_protocol_guid, + efi_pecoff_debug_name ), + /* Loaded image device path (for image handles) */ + EFI_HANDLE_NAME_TYPE ( &efi_loaded_image_device_path_protocol_guid, + efi_devpath_text ), + /* First loaded image name (for the DxeCore image) */ + EFI_HANDLE_NAME_TYPE ( &efi_loaded_image_protocol_guid, + efi_first_loaded_image_name ), + /* Handle's loaded image file path (for image handles) */ + EFI_HANDLE_NAME_TYPE ( &efi_loaded_image_protocol_guid, + efi_loaded_image_filepath_name ), +}; + +/** + * Get name of an EFI handle + * + * @v handle EFI handle + * @ret text Name of handle, or NULL + */ +const char * efi_handle_name ( EFI_HANDLE handle ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + struct efi_handle_name_type *type; + unsigned int i; + void *interface; + const char *name; + EFI_STATUS efirc; + + /* Fail immediately for NULL handles */ + if ( ! handle ) + return NULL; + + /* Try each name type in turn */ + for ( i = 0 ; i < ( sizeof ( efi_handle_name_types ) / + sizeof ( efi_handle_name_types[0] ) ) ; i++ ) { + type = &efi_handle_name_types[i]; + DBG2 ( "<%d", i ); + + /* Try to open the applicable protocol */ + efirc = bs->OpenProtocol ( handle, type->protocol, &interface, + efi_image_handle, handle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ); + if ( efirc != 0 ) { + DBG2 ( ">" ); + continue; + } + + /* Try to get name from this protocol */ + DBG2 ( "-" ); + name = type->name ( interface ); + DBG2 ( "%c", ( name ? ( name[0] ? 'Y' : 'E' ) : 'N' ) ); + + /* Close protocol */ + bs->CloseProtocol ( handle, type->protocol, + efi_image_handle, handle ); + DBG2 ( ">" ); + + /* Use this name, if possible */ + if ( name && name[0] ) + return name; + } + + return "UNKNOWN"; } diff --git a/roms/ipxe/src/interface/efi/efi_download.c b/roms/ipxe/src/interface/efi/efi_download.c index 80279f7..1218852 100644 --- a/roms/ipxe/src/interface/efi/efi_download.c +++ b/roms/ipxe/src/interface/efi/efi_download.c @@ -26,6 +26,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include /** iPXE download protocol GUID */ @@ -63,6 +64,8 @@ static void efi_download_close ( struct efi_download_file *file, int rc ) { file->finish_callback ( file->context, EFIRC ( rc ) ); intf_shutdown ( &file->xfer, rc ); + + efi_snp_release(); } /** @@ -147,6 +150,7 @@ efi_download_start ( IPXE_DOWNLOAD_PROTOCOL *This __unused, return EFIRC ( rc ); } + efi_snp_claim(); file->pos = 0; file->data_callback = DataCallback; file->finish_callback = FinishCallback; @@ -201,13 +205,13 @@ static IPXE_DOWNLOAD_PROTOCOL ipxe_download_protocol_interface = { * @v handle EFI handle * @ret rc Return status code */ -int efi_download_install ( EFI_HANDLE *handle ) { +int efi_download_install ( EFI_HANDLE handle ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; EFI_STATUS efirc; int rc; efirc = bs->InstallMultipleProtocolInterfaces ( - handle, + &handle, &ipxe_download_protocol_guid, &ipxe_download_protocol_interface, NULL ); diff --git a/roms/ipxe/src/interface/efi/efi_driver.c b/roms/ipxe/src/interface/efi/efi_driver.c index 1bc28e7..ba7784c 100644 --- a/roms/ipxe/src/interface/efi/efi_driver.c +++ b/roms/ipxe/src/interface/efi/efi_driver.c @@ -20,15 +20,17 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include +#include #include #include #include +#include #include #include #include +#include #include #include -#include /** @file * @@ -36,33 +38,215 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ -/** EFI driver binding protocol GUID */ -static EFI_GUID efi_driver_binding_protocol_guid - = EFI_DRIVER_BINDING_PROTOCOL_GUID; +static EFI_DRIVER_BINDING_PROTOCOL efi_driver_binding; -/** EFI component name protocol GUID */ -static EFI_GUID efi_component_name2_protocol_guid - = EFI_COMPONENT_NAME2_PROTOCOL_GUID; +/** List of controlled EFI devices */ +static LIST_HEAD ( efi_devices ); /** - * Find end of device path + * Find EFI device * - * @v path Path to device - * @ret path_end End of device path + * @v device EFI device handle + * @ret efidev EFI device, or NULL if not found */ -EFI_DEVICE_PATH_PROTOCOL * efi_devpath_end ( EFI_DEVICE_PATH_PROTOCOL *path ) { +static struct efi_device * efidev_find ( EFI_HANDLE device ) { + struct efi_device *efidev; - while ( path->Type != END_DEVICE_PATH_TYPE ) { - path = ( ( ( void * ) path ) + - /* There's this amazing new-fangled thing known as - * a UINT16, but who wants to use one of those? */ - ( ( path->Length[1] << 8 ) | path->Length[0] ) ); + /* Look for an existing EFI device */ + list_for_each_entry ( efidev, &efi_devices, dev.siblings ) { + if ( efidev->device == device ) + return efidev; } - return path; + return NULL; } /** + * Get parent EFI device + * + * @v dev Generic device + * @ret efidev Parent EFI device, or NULL + */ +struct efi_device * efidev_parent ( struct device *dev ) { + struct device *parent = dev->parent; + struct efi_device *efidev; + + /* Check that parent exists and is an EFI device */ + if ( ! parent ) + return NULL; + if ( parent->desc.bus_type != BUS_TYPE_EFI ) + return NULL; + + /* Get containing EFI device */ + efidev = container_of ( parent, struct efi_device, dev ); + return efidev; +} + +/** + * Check to see if driver supports a device + * + * @v driver EFI driver + * @v device EFI device + * @v child Path to child device, if any + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_driver_supported ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, + EFI_HANDLE device, EFI_DEVICE_PATH_PROTOCOL *child ) { + struct efi_driver *efidrv; + int rc; + + DBGCP ( device, "EFIDRV %p %s DRIVER_SUPPORTED", + device, efi_handle_name ( device ) ); + if ( child ) + DBGCP ( device, " (child %s)", efi_devpath_text ( child ) ); + DBGCP ( device, "\n" ); + + /* Do nothing if we are already driving this device */ + if ( efidev_find ( device ) != NULL ) { + DBGCP ( device, "EFIDRV %p %s is already started\n", + device, efi_handle_name ( device ) ); + return EFI_ALREADY_STARTED; + } + + /* Look for a driver claiming to support this device */ + for_each_table_entry ( efidrv, EFI_DRIVERS ) { + if ( ( rc = efidrv->supported ( device ) ) == 0 ) { + DBGC ( device, "EFIDRV %p %s has driver \"%s\"\n", + device, efi_handle_name ( device ), + efidrv->name ); + return 0; + } + } + DBGCP ( device, "EFIDRV %p %s has no driver\n", + device, efi_handle_name ( device ) ); + + return EFI_UNSUPPORTED; +} + +/** + * Attach driver to device + * + * @v driver EFI driver + * @v device EFI device + * @v child Path to child device, if any + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, + EFI_HANDLE device, EFI_DEVICE_PATH_PROTOCOL *child ) { + struct efi_driver *efidrv; + struct efi_device *efidev; + EFI_STATUS efirc; + int rc; + + DBGC ( device, "EFIDRV %p %s DRIVER_START", + device, efi_handle_name ( device ) ); + if ( child ) + DBGC ( device, " (child %s)", efi_devpath_text ( child ) ); + DBGC ( device, "\n" ); + + /* Do nothing if we are already driving this device */ + efidev = efidev_find ( device ); + if ( efidev ) { + DBGCP ( device, "EFIDRV %p %s is already started\n", + device, efi_handle_name ( device ) ); + efirc = EFI_ALREADY_STARTED; + goto err_already_started; + } + + /* Allocate and initialise structure */ + efidev = zalloc ( sizeof ( *efidev ) ); + if ( ! efidev ) { + efirc = EFI_OUT_OF_RESOURCES; + goto err_alloc; + } + efidev->device = device; + efidev->dev.desc.bus_type = BUS_TYPE_EFI; + INIT_LIST_HEAD ( &efidev->dev.children ); + list_add ( &efidev->dev.siblings, &efi_devices ); + + /* Try to start this device */ + for_each_table_entry ( efidrv, EFI_DRIVERS ) { + if ( ( rc = efidrv->supported ( device ) ) != 0 ) { + DBGC ( device, "EFIDRV %p %s is not supported by " + "driver \"%s\": %s\n", device, + efi_handle_name ( device ), efidrv->name, + strerror ( rc ) ); + continue; + } + if ( ( rc = efidrv->start ( efidev ) ) == 0 ) { + efidev->driver = efidrv; + DBGC ( device, "EFIDRV %p %s using driver \"%s\"\n", + device, efi_handle_name ( device ), + efidev->driver->name ); + return 0; + } + DBGC ( device, "EFIDRV %p %s could not start driver \"%s\": " + "%s\n", device, efi_handle_name ( device ), + efidrv->name, strerror ( rc ) ); + } + efirc = EFI_UNSUPPORTED; + + list_del ( &efidev->dev.siblings ); + free ( efidev ); + err_alloc: + err_already_started: + return efirc; +} + +/** + * Detach driver from device + * + * @v driver EFI driver + * @v device EFI device + * @v pci PCI device + * @v num_children Number of child devices + * @v children List of child devices + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_driver_stop ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, + EFI_HANDLE device, UINTN num_children, + EFI_HANDLE *children ) { + struct efi_driver *efidrv; + struct efi_device *efidev; + UINTN i; + + DBGC ( device, "EFIDRV %p %s DRIVER_STOP", + device, efi_handle_name ( device ) ); + for ( i = 0 ; i < num_children ; i++ ) { + DBGC ( device, "%s%p %s", ( i ? ", " : " child " ), + children[i], efi_handle_name ( children[i] ) ); + } + DBGC ( device, "\n" ); + + /* Do nothing unless we are driving this device */ + efidev = efidev_find ( device ); + if ( ! efidev ) { + DBGCP ( device, "EFIDRV %p %s is not started\n", + device, efi_handle_name ( device ) ); + return 0; + } + + /* Stop this device */ + efidrv = efidev->driver; + assert ( efidrv != NULL ); + efidrv->stop ( efidev ); + list_del ( &efidev->dev.siblings ); + free ( efidev ); + + return 0; +} + +/** EFI driver binding protocol */ +static EFI_DRIVER_BINDING_PROTOCOL efi_driver_binding = { + .Supported = efi_driver_supported, + .Start = efi_driver_start, + .Stop = efi_driver_stop, +}; + +/** * Look up driver name * * @v wtf Component name protocol @@ -71,12 +255,12 @@ EFI_DEVICE_PATH_PROTOCOL * efi_devpath_end ( EFI_DEVICE_PATH_PROTOCOL *path ) { * @ret efirc EFI status code */ static EFI_STATUS EFIAPI -efi_driver_get_driver_name ( EFI_COMPONENT_NAME2_PROTOCOL *wtf, - CHAR8 *language __unused, CHAR16 **driver_name ) { - struct efi_driver *efidrv = - container_of ( wtf, struct efi_driver, wtf ); +efi_driver_name ( EFI_COMPONENT_NAME2_PROTOCOL *wtf __unused, + CHAR8 *language __unused, CHAR16 **driver_name ) { + const wchar_t *name; - *driver_name = efidrv->wname; + name = ( product_wname[0] ? product_wname : build_wname ); + *driver_name = ( ( wchar_t * ) name ); return 0; } @@ -91,9 +275,9 @@ efi_driver_get_driver_name ( EFI_COMPONENT_NAME2_PROTOCOL *wtf, * @ret efirc EFI status code */ static EFI_STATUS EFIAPI -efi_driver_get_controller_name ( EFI_COMPONENT_NAME2_PROTOCOL *wtf __unused, - EFI_HANDLE device, EFI_HANDLE child, - CHAR8 *language, CHAR16 **controller_name ) { +efi_driver_controller_name ( EFI_COMPONENT_NAME2_PROTOCOL *wtf __unused, + EFI_HANDLE device, EFI_HANDLE child, + CHAR8 *language, CHAR16 **controller_name ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; union { EFI_COMPONENT_NAME2_PROTOCOL *name2; @@ -118,43 +302,155 @@ efi_driver_get_controller_name ( EFI_COMPONENT_NAME2_PROTOCOL *wtf __unused, return EFI_UNSUPPORTED; } +/** EFI component name protocol */ +static EFI_COMPONENT_NAME2_PROTOCOL efi_wtf = { + .GetDriverName = efi_driver_name, + .GetControllerName = efi_driver_controller_name, + .SupportedLanguages = "en", +}; + +/** + * Install EFI driver + * + * @ret rc Return status code + */ +int efi_driver_install ( void ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_STATUS efirc; + int rc; + + /* Calculate driver version number. We use the build + * timestamp (in seconds since the Epoch) shifted right by six + * bits: this gives us an approximately one-minute resolution + * and a scheme which will last until the year 10680. + */ + efi_driver_binding.Version = ( build_timestamp >> 6 ); + + /* Install protocols on image handle */ + efi_driver_binding.ImageHandle = efi_image_handle; + efi_driver_binding.DriverBindingHandle = efi_image_handle; + if ( ( efirc = bs->InstallMultipleProtocolInterfaces ( + &efi_image_handle, + &efi_driver_binding_protocol_guid, &efi_driver_binding, + &efi_component_name2_protocol_guid, &efi_wtf, + NULL ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( &efi_driver_binding, "EFIDRV could not install " + "protocols: %s\n", strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Uninstall EFI driver + * + */ +void efi_driver_uninstall ( void ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + + /* Uninstall protocols */ + bs->UninstallMultipleProtocolInterfaces ( + efi_image_handle, + &efi_driver_binding_protocol_guid, &efi_driver_binding, + &efi_component_name2_protocol_guid, &efi_wtf, NULL ); +} + /** * Try to connect EFI driver * - * @v efidrv EFI driver - * @v handle Controller handle + * @v device EFI device + * @ret rc Return status code */ -static void efi_driver_connect ( struct efi_driver *efidrv, EFI_HANDLE handle ){ +static int efi_driver_connect ( EFI_HANDLE device ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - EFI_HANDLE drivers[2] = { efidrv->driver.DriverBindingHandle, NULL }; + EFI_HANDLE drivers[2] = + { efi_driver_binding.DriverBindingHandle, NULL }; + EFI_STATUS efirc; + int rc; + + /* Check if we want to drive this device */ + if ( ( efirc = efi_driver_supported ( &efi_driver_binding, device, + NULL ) ) != 0 ) { + /* Not supported; not an error */ + return 0; + } - bs->ConnectController ( handle, drivers, NULL, FALSE ); + /* Disconnect any existing drivers */ + DBGC2 ( device, "EFIDRV %p %s before disconnecting:\n", + device, efi_handle_name ( device ) ); + DBGC2_EFI_PROTOCOLS ( device, device ); + DBGC ( device, "EFIDRV %p %s disconnecting existing drivers\n", + device, efi_handle_name ( device ) ); + if ( ( efirc = bs->DisconnectController ( device, NULL, + NULL ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( device, "EFIDRV %p %s could not disconnect existing " + "drivers: %s\n", device, efi_handle_name ( device ), + strerror ( rc ) ); + /* Ignore the error and attempt to connect our drivers */ + } + DBGC2 ( device, "EFIDRV %p %s after disconnecting:\n", + device, efi_handle_name ( device ) ); + DBGC2_EFI_PROTOCOLS ( device, device ); + + /* Connect our driver */ + DBGC ( device, "EFIDRV %p %s connecting new drivers\n", + device, efi_handle_name ( device ) ); + if ( ( efirc = bs->ConnectController ( device, drivers, NULL, + FALSE ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( device, "EFIDRV %p %s could not connect new drivers: " + "%s\n", device, efi_handle_name ( device ), + strerror ( rc ) ); + return rc; + } + DBGC2 ( device, "EFIDRV %p %s after connecting:\n", + device, efi_handle_name ( device ) ); + DBGC2_EFI_PROTOCOLS ( device, device ); + + return 0; } /** * Try to disconnect EFI driver * - * @v efidrv EFI driver - * @v handle Controller handle + * @v device EFI device + * @ret rc Return status code */ -static void efi_driver_disconnect ( struct efi_driver *efidrv, - EFI_HANDLE handle ) { +static int efi_driver_disconnect ( EFI_HANDLE device ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - bs->DisconnectController ( handle, efidrv->driver.DriverBindingHandle, + /* Disconnect our driver */ + bs->DisconnectController ( device, + efi_driver_binding.DriverBindingHandle, NULL ); + return 0; +} + +/** + * Reconnect original EFI driver + * + * @v device EFI device + * @ret rc Return status code + */ +static int efi_driver_reconnect ( EFI_HANDLE device ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + + /* Reconnect any available driver */ + bs->ConnectController ( device, NULL, NULL, FALSE ); + + return 0; } /** * Connect/disconnect EFI driver from all handles * - * @v efidrv EFI driver * @v method Connect/disconnect method * @ret rc Return status code */ -static int efi_driver_handles ( struct efi_driver *efidrv, - void ( * method ) ( struct efi_driver *efidrv, - EFI_HANDLE handle ) ) { +static int efi_driver_handles ( int ( * method ) ( EFI_HANDLE handle ) ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; EFI_HANDLE *handles; UINTN num_handles; @@ -167,87 +463,55 @@ static int efi_driver_handles ( struct efi_driver *efidrv, &num_handles, &handles ) ) != 0 ) { rc = -EEFI ( efirc ); - DBGC ( efidrv, "EFIDRV %s could not list handles: %s\n", - efidrv->name, strerror ( rc ) ); - return rc; + DBGC ( &efi_driver_binding, "EFIDRV could not list handles: " + "%s\n", strerror ( rc ) ); + goto err_locate; } /* Connect/disconnect driver from all handles */ - for ( i = 0 ; i < num_handles ; i++ ) - method ( efidrv, handles[i] ); + for ( i = 0 ; i < num_handles ; i++ ) { + if ( ( rc = method ( handles[i] ) ) != 0 ) + goto err_method; + } - /* Free list of handles */ - bs->FreePool ( handles ); + /* Success */ + rc = 0; - return 0; + err_method: + bs->FreePool ( handles ); + err_locate: + return rc; } /** - * Install EFI driver + * Connect EFI driver to all possible devices * - * @v efidrv EFI driver * @ret rc Return status code */ -int efi_driver_install ( struct efi_driver *efidrv ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - EFI_DRIVER_BINDING_PROTOCOL *driver = &efidrv->driver; - EFI_COMPONENT_NAME2_PROTOCOL *wtf = &efidrv->wtf; - EFI_STATUS efirc; - int rc; +int efi_driver_connect_all ( void ) { - /* Configure driver binding protocol */ - driver->ImageHandle = efi_image_handle; - - /* Configure component name protocol */ - wtf->GetDriverName = efi_driver_get_driver_name; - wtf->GetControllerName = efi_driver_get_controller_name; - wtf->SupportedLanguages = "en"; - - /* Fill in driver name */ - efi_snprintf ( efidrv->wname, - ( sizeof ( efidrv->wname ) / - sizeof ( efidrv->wname[0] ) ), - PRODUCT_SHORT_NAME "%s%s", - ( efidrv->name ? " - " : "" ), - ( efidrv->name ? efidrv->name : "" ) ); - - /* Install driver */ - if ( ( efirc = bs->InstallMultipleProtocolInterfaces ( - &driver->DriverBindingHandle, - &efi_driver_binding_protocol_guid, driver, - &efi_component_name2_protocol_guid, wtf, - NULL ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( efidrv, "EFIDRV %s could not install protocol: %s\n", - efidrv->name, strerror ( rc ) ); - return rc; - } + DBGC ( &efi_driver_binding, "EFIDRV connecting our drivers\n" ); + return efi_driver_handles ( efi_driver_connect ); +} - /* Connect devices */ - DBGC ( efidrv, "EFIDRV %s connecting devices\n", efidrv->name ); - efi_driver_handles ( efidrv, efi_driver_connect ); +/** + * Disconnect EFI driver from all possible devices + * + * @ret rc Return status code + */ +void efi_driver_disconnect_all ( void ) { - DBGC ( efidrv, "EFIDRV %s installed\n", efidrv->name ); - return 0; + DBGC ( &efi_driver_binding, "EFIDRV disconnecting our drivers\n" ); + efi_driver_handles ( efi_driver_disconnect ); } /** - * Uninstall EFI driver + * Reconnect original EFI drivers to all possible devices * - * @v efidrv EFI driver + * @ret rc Return status code */ -void efi_driver_uninstall ( struct efi_driver *efidrv ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; +void efi_driver_reconnect_all ( void ) { - /* Disconnect the driver from its devices */ - DBGC ( efidrv, "EFIDRV %s disconnecting devices\n", efidrv->name ); - efi_driver_handles ( efidrv, efi_driver_disconnect ); - - /* Uninstall the driver */ - bs->UninstallMultipleProtocolInterfaces ( - efidrv->driver.DriverBindingHandle, - &efi_driver_binding_protocol_guid, &efidrv->driver, - &efi_component_name2_protocol_guid, &efidrv->wtf, - NULL ); - DBGC ( efidrv, "EFIDRV %s uninstalled\n", efidrv->name ); + DBGC ( &efi_driver_binding, "EFIDRV reconnecting old drivers\n" ); + efi_driver_handles ( efi_driver_reconnect ); } diff --git a/roms/ipxe/src/interface/efi/efi_file.c b/roms/ipxe/src/interface/efi/efi_file.c index 4c482d2..2ef3c57 100644 --- a/roms/ipxe/src/interface/efi/efi_file.c +++ b/roms/ipxe/src/interface/efi/efi_file.c @@ -30,31 +30,25 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include #include -/** EFI simple file system protocol GUID */ -static EFI_GUID efi_simple_file_system_protocol_guid - = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID; - /** EFI file information GUID */ static EFI_GUID efi_file_info_id = EFI_FILE_INFO_ID; /** EFI file system information GUID */ static EFI_GUID efi_file_system_info_id = EFI_FILE_SYSTEM_INFO_ID; -/** EFI block I/O protocol GUID */ -static EFI_GUID efi_block_io_protocol_guid - = EFI_BLOCK_IO_PROTOCOL_GUID; - /** EFI media ID */ #define EFI_MEDIA_ID_MAGIC 0x69505845 @@ -82,6 +76,27 @@ static const char * efi_file_name ( struct efi_file *file ) { } /** + * Find EFI file image + * + * @v wname Filename + * @ret image Image, or NULL + */ +static struct image * efi_file_find ( const CHAR16 *wname ) { + char name[ wcslen ( wname ) + 1 /* NUL */ ]; + struct image *image; + + /* Find image */ + snprintf ( name, sizeof ( name ), "%ls", wname ); + list_for_each_entry ( image, &images, list ) { + if ( strcasecmp ( image->name, name ) == 0 ) + return image; + } + + return NULL; + +} + +/** * Open file * * @v this EFI file @@ -96,7 +111,6 @@ efi_file_open ( EFI_FILE_PROTOCOL *this, EFI_FILE_PROTOCOL **new, CHAR16 *wname, UINT64 mode __unused, UINT64 attributes __unused ) { struct efi_file *file = container_of ( this, struct efi_file, file ); - char name[ wcslen ( wname ) + 1 /* NUL */ ]; struct efi_file *new_file; struct image *image; @@ -120,10 +134,9 @@ efi_file_open ( EFI_FILE_PROTOCOL *this, EFI_FILE_PROTOCOL **new, } /* Identify image */ - snprintf ( name, sizeof ( name ), "%ls", wname ); - image = find_image ( name ); + image = efi_file_find ( wname ); if ( ! image ) { - DBGC ( file, "EFIFILE \"%s\" does not exist\n", name ); + DBGC ( file, "EFIFILE \"%ls\" does not exist\n", wname ); return EFI_NOT_FOUND; } @@ -484,6 +497,7 @@ static EFI_STATUS EFIAPI efi_file_open_volume ( EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *filesystem __unused, EFI_FILE_PROTOCOL **file ) { + DBGC ( &efi_file_root, "EFIFILE open volume\n" ); *file = &efi_file_root.file; return 0; } @@ -496,38 +510,49 @@ static EFI_SIMPLE_FILE_SYSTEM_PROTOCOL efi_simple_file_system_protocol = { /** Dummy block I/O reset */ static EFI_STATUS EFIAPI -efi_block_io_reset ( EFI_BLOCK_IO_PROTOCOL *this __unused, - BOOLEAN extended __unused ) { +efi_block_io_reset ( EFI_BLOCK_IO_PROTOCOL *this __unused, BOOLEAN extended ) { + + DBGC ( &efi_file_root, "EFIFILE block %sreset\n", + ( extended ? "extended " : "" ) ); return 0; } /** Dummy block I/O read */ static EFI_STATUS EFIAPI -efi_block_io_read_blocks ( EFI_BLOCK_IO_PROTOCOL *this __unused, - UINT32 MediaId __unused, EFI_LBA lba __unused, - UINTN len __unused, VOID *data __unused ) { - return EFI_DEVICE_ERROR; +efi_block_io_read_blocks ( EFI_BLOCK_IO_PROTOCOL *this __unused, UINT32 MediaId, + EFI_LBA lba, UINTN len, VOID *data ) { + + DBGC ( &efi_file_root, "EFIFILE block read ID %#08x LBA %#08llx -> " + "%p+%zx\n", MediaId, ( ( unsigned long long ) lba ), + data, ( ( size_t ) len ) ); + return EFI_NO_MEDIA; } /** Dummy block I/O write */ static EFI_STATUS EFIAPI efi_block_io_write_blocks ( EFI_BLOCK_IO_PROTOCOL *this __unused, - UINT32 MediaId __unused, EFI_LBA lba __unused, - UINTN len __unused, VOID *data __unused ) { - return EFI_DEVICE_ERROR; + UINT32 MediaId, EFI_LBA lba, UINTN len, + VOID *data ) { + + DBGC ( &efi_file_root, "EFIFILE block write ID %#08x LBA %#08llx <- " + "%p+%zx\n", MediaId, ( ( unsigned long long ) lba ), + data, ( ( size_t ) len ) ); + return EFI_NO_MEDIA; } /** Dummy block I/O flush */ static EFI_STATUS EFIAPI efi_block_io_flush_blocks ( EFI_BLOCK_IO_PROTOCOL *this __unused ) { + + DBGC ( &efi_file_root, "EFIFILE block flush\n" ); return 0; } /** Dummy block I/O media */ static EFI_BLOCK_IO_MEDIA efi_block_io_media = { .MediaId = EFI_MEDIA_ID_MAGIC, - .MediaPresent = 1, - .ReadOnly = 1, + .MediaPresent = TRUE, + .ReadOnly = TRUE, .BlockSize = 1, }; @@ -541,37 +566,117 @@ static EFI_BLOCK_IO_PROTOCOL efi_block_io_protocol = { .FlushBlocks = efi_block_io_flush_blocks, }; +/** Dummy disk I/O read */ +static EFI_STATUS EFIAPI +efi_disk_io_read_disk ( EFI_DISK_IO_PROTOCOL *this __unused, UINT32 MediaId, + UINT64 offset, UINTN len, VOID *data ) { + + DBGC ( &efi_file_root, "EFIFILE disk read ID %#08x offset %#08llx -> " + "%p+%zx\n", MediaId, ( ( unsigned long long ) offset ), + data, ( ( size_t ) len ) ); + return EFI_NO_MEDIA; +} + +/** Dummy disk I/O write */ +static EFI_STATUS EFIAPI +efi_disk_io_write_disk ( EFI_DISK_IO_PROTOCOL *this __unused, UINT32 MediaId, + UINT64 offset, UINTN len, VOID *data ) { + + DBGC ( &efi_file_root, "EFIFILE disk write ID %#08x offset %#08llx <- " + "%p+%zx\n", MediaId, ( ( unsigned long long ) offset ), + data, ( ( size_t ) len ) ); + return EFI_NO_MEDIA; +} + +/** Dummy EFI disk I/O protocol */ +static EFI_DISK_IO_PROTOCOL efi_disk_io_protocol = { + .Revision = EFI_DISK_IO_PROTOCOL_REVISION, + .ReadDisk = efi_disk_io_read_disk, + .WriteDisk = efi_disk_io_write_disk, +}; + /** * Install EFI simple file system protocol * * @v handle EFI handle * @ret rc Return status code */ -int efi_file_install ( EFI_HANDLE *handle ) { +int efi_file_install ( EFI_HANDLE handle ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + union { + EFI_DISK_IO_PROTOCOL *diskio; + void *interface; + } diskio; EFI_STATUS efirc; int rc; - /* Install the simple file system protocol and the block I/O - * protocol. We don't have a block device, but large parts of - * the EDK2 codebase make the assumption that file systems are - * normally attached to block devices, and so we create a - * dummy block device on the same handle just to keep things - * looking normal. + /* Install the simple file system protocol, block I/O + * protocol, and disk I/O protocol. We don't have a block + * device, but large parts of the EDK2 codebase make the + * assumption that file systems are normally attached to block + * devices, and so we create a dummy block device on the same + * handle just to keep things looking normal. */ if ( ( efirc = bs->InstallMultipleProtocolInterfaces ( - handle, + &handle, &efi_block_io_protocol_guid, &efi_block_io_protocol, + &efi_disk_io_protocol_guid, + &efi_disk_io_protocol, &efi_simple_file_system_protocol_guid, &efi_simple_file_system_protocol, NULL ) ) != 0 ) { rc = -EEFI ( efirc ); - DBGC ( handle, "Could not install simple file system protocol: " - "%s\n", strerror ( rc ) ); - return rc; + DBGC ( handle, "Could not install simple file system " + "protocols: %s\n", strerror ( rc ) ); + goto err_install; } + /* The FAT filesystem driver has a bug: if a block device + * contains no FAT filesystem but does have an + * EFI_SIMPLE_FILE_SYSTEM_PROTOCOL instance, the FAT driver + * will assume that it must have previously installed the + * EFI_SIMPLE_FILE_SYSTEM_PROTOCOL. This causes the FAT + * driver to claim control of our device, and to refuse to + * stop driving it, which prevents us from later uninstalling + * correctly. + * + * Work around this bug by opening the disk I/O protocol + * ourselves, thereby preventing the FAT driver from opening + * it. + * + * Note that the alternative approach of opening the block I/O + * protocol (and thereby in theory preventing DiskIo from + * attaching to the block I/O protocol) causes an endless loop + * of calls to our DRIVER_STOP method when starting the EFI + * shell. I have no idea why this is. + */ + if ( ( efirc = bs->OpenProtocol ( handle, &efi_disk_io_protocol_guid, + &diskio.interface, efi_image_handle, + handle, + EFI_OPEN_PROTOCOL_BY_DRIVER ) ) != 0){ + rc = -EEFI ( efirc ); + DBGC ( handle, "Could not open disk I/O protocol: %s\n", + strerror ( rc ) ); + DBGC_EFI_OPENERS ( handle, handle, &efi_disk_io_protocol_guid ); + goto err_open; + } + assert ( diskio.diskio == &efi_disk_io_protocol ); + return 0; + + bs->CloseProtocol ( handle, &efi_disk_io_protocol_guid, + efi_image_handle, handle ); + err_open: + bs->UninstallMultipleProtocolInterfaces ( + handle, + &efi_simple_file_system_protocol_guid, + &efi_simple_file_system_protocol, + &efi_disk_io_protocol_guid, + &efi_disk_io_protocol, + &efi_block_io_protocol_guid, + &efi_block_io_protocol, NULL ); + err_install: + return rc; } /** @@ -581,16 +686,29 @@ int efi_file_install ( EFI_HANDLE *handle ) { */ void efi_file_uninstall ( EFI_HANDLE handle ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_STATUS efirc; + int rc; + + /* Close our own disk I/O protocol */ + bs->CloseProtocol ( handle, &efi_disk_io_protocol_guid, + efi_image_handle, handle ); /* We must install the file system protocol first, since * otherwise the EDK2 code will attempt to helpfully uninstall * it when the block I/O protocol is uninstalled, leading to a * system lock-up. */ - bs->UninstallMultipleProtocolInterfaces ( + if ( ( efirc = bs->UninstallMultipleProtocolInterfaces ( handle, &efi_simple_file_system_protocol_guid, &efi_simple_file_system_protocol, + &efi_disk_io_protocol_guid, + &efi_disk_io_protocol, &efi_block_io_protocol_guid, - &efi_block_io_protocol, NULL ); + &efi_block_io_protocol, NULL ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( handle, "Could not uninstall simple file system " + "protocols: %s\n", strerror ( rc ) ); + /* Oh dear */ + } } diff --git a/roms/ipxe/src/interface/efi/efi_guid.c b/roms/ipxe/src/interface/efi/efi_guid.c new file mode 100644 index 0000000..52ba58a --- /dev/null +++ b/roms/ipxe/src/interface/efi/efi_guid.c @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2014 Michael Brown . + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * EFI GUIDs + * + */ + +/** ARP protocol GUID */ +EFI_GUID efi_arp_protocol_guid + = EFI_ARP_PROTOCOL_GUID; + +/** ARP service binding protocol GUID */ +EFI_GUID efi_arp_service_binding_protocol_guid + = EFI_ARP_SERVICE_BINDING_PROTOCOL_GUID; + +/** Block I/O protocol GUID */ +EFI_GUID efi_block_io_protocol_guid + = EFI_BLOCK_IO_PROTOCOL_GUID; + +/** Bus specific driver override protocol GUID */ +EFI_GUID efi_bus_specific_driver_override_protocol_guid + = EFI_BUS_SPECIFIC_DRIVER_OVERRIDE_PROTOCOL_GUID; + +/** Component name protocol GUID */ +EFI_GUID efi_component_name_protocol_guid + = EFI_COMPONENT_NAME_PROTOCOL_GUID; + +/** Component name 2 protocol GUID */ +EFI_GUID efi_component_name2_protocol_guid + = EFI_COMPONENT_NAME2_PROTOCOL_GUID; + +/** Device path protocol GUID */ +EFI_GUID efi_device_path_protocol_guid + = EFI_DEVICE_PATH_PROTOCOL_GUID; + +/** DHCPv4 protocol GUID */ +EFI_GUID efi_dhcp4_protocol_guid + = EFI_DHCP4_PROTOCOL_GUID; + +/** DHCPv4 service binding protocol GUID */ +EFI_GUID efi_dhcp4_service_binding_protocol_guid + = EFI_DHCP4_SERVICE_BINDING_PROTOCOL_GUID; + +/** Disk I/O protocol GUID */ +EFI_GUID efi_disk_io_protocol_guid + = EFI_DISK_IO_PROTOCOL_GUID; + +/** Driver binding protocol GUID */ +EFI_GUID efi_driver_binding_protocol_guid + = EFI_DRIVER_BINDING_PROTOCOL_GUID; + +/** Graphics output protocol GUID */ +EFI_GUID efi_graphics_output_protocol_guid + = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; + +/** HII configuration access protocol GUID */ +EFI_GUID efi_hii_config_access_protocol_guid + = EFI_HII_CONFIG_ACCESS_PROTOCOL_GUID; + +/** IPv4 protocol GUID */ +EFI_GUID efi_ip4_protocol_guid + = EFI_IP4_PROTOCOL_GUID; + +/** IPv4 configuration protocol GUID */ +EFI_GUID efi_ip4_config_protocol_guid + = EFI_IP4_CONFIG_PROTOCOL_GUID; + +/** IPv4 service binding protocol GUID */ +EFI_GUID efi_ip4_service_binding_protocol_guid + = EFI_IP4_SERVICE_BINDING_PROTOCOL_GUID; + +/** Load file protocol GUID */ +EFI_GUID efi_load_file_protocol_guid + = EFI_LOAD_FILE_PROTOCOL_GUID; + +/** Load file 2 protocol GUID */ +EFI_GUID efi_load_file2_protocol_guid + = EFI_LOAD_FILE2_PROTOCOL_GUID; + +/** Loaded image protocol GUID */ +EFI_GUID efi_loaded_image_protocol_guid + = EFI_LOADED_IMAGE_PROTOCOL_GUID; + +/** Loaded image device path protocol GUID */ +EFI_GUID efi_loaded_image_device_path_protocol_guid + = EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID; + +/** Managed network protocol GUID */ +EFI_GUID efi_managed_network_protocol_guid + = EFI_MANAGED_NETWORK_PROTOCOL_GUID; + +/** Managed network service binding protocol GUID */ +EFI_GUID efi_managed_network_service_binding_protocol_guid + = EFI_MANAGED_NETWORK_SERVICE_BINDING_PROTOCOL_GUID; + +/** MTFTPv4 protocol GUID */ +EFI_GUID efi_mtftp4_protocol_guid + = EFI_MTFTP4_PROTOCOL_GUID; + +/** MTFTPv4 service binding protocol GUID */ +EFI_GUID efi_mtftp4_service_binding_protocol_guid + = EFI_MTFTP4_SERVICE_BINDING_PROTOCOL_GUID; + +/** Network interface identifier protocol GUID (old version) */ +EFI_GUID efi_nii_protocol_guid + = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_GUID; + +/** Network interface identifier protocol GUID (new version) */ +EFI_GUID efi_nii31_protocol_guid + = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_GUID_31; + +/** PCI I/O protocol GUID */ +EFI_GUID efi_pci_io_protocol_guid + = EFI_PCI_IO_PROTOCOL_GUID; + +/** PCI root bridge I/O protocol GUID */ +EFI_GUID efi_pci_root_bridge_io_protocol_guid + = EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_GUID; + +/** PXE base code protocol GUID */ +EFI_GUID efi_pxe_base_code_protocol_guid + = EFI_PXE_BASE_CODE_PROTOCOL_GUID; + +/** Simple file system protocol GUID */ +EFI_GUID efi_simple_file_system_protocol_guid + = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID; + +/** Simple network protocol GUID */ +EFI_GUID efi_simple_network_protocol_guid + = EFI_SIMPLE_NETWORK_PROTOCOL_GUID; + +/** TCG protocol GUID */ +EFI_GUID efi_tcg_protocol_guid + = EFI_TCG_PROTOCOL_GUID; + +/** TCPv4 protocol GUID */ +EFI_GUID efi_tcp4_protocol_guid + = EFI_TCP4_PROTOCOL_GUID; + +/** TCPv4 service binding protocol GUID */ +EFI_GUID efi_tcp4_service_binding_protocol_guid + = EFI_TCP4_SERVICE_BINDING_PROTOCOL_GUID; + +/** UDPv4 protocol GUID */ +EFI_GUID efi_udp4_protocol_guid + = EFI_UDP4_PROTOCOL_GUID; + +/** UDPv4 service binding protocol GUID */ +EFI_GUID efi_udp4_service_binding_protocol_guid + = EFI_UDP4_SERVICE_BINDING_PROTOCOL_GUID; + +/** VLAN configuration protocol GUID */ +EFI_GUID efi_vlan_config_protocol_guid + = EFI_VLAN_CONFIG_PROTOCOL_GUID; diff --git a/roms/ipxe/src/interface/efi/efi_init.c b/roms/ipxe/src/interface/efi/efi_init.c index b4ed5c1..93ada21 100644 --- a/roms/ipxe/src/interface/efi/efi_init.c +++ b/roms/ipxe/src/interface/efi/efi_init.c @@ -21,12 +21,10 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include +#include #include #include #include -#include -#include -#include /** Image handle passed to entry point */ EFI_HANDLE efi_image_handle; @@ -34,20 +32,9 @@ EFI_HANDLE efi_image_handle; /** Loaded image protocol for this image */ EFI_LOADED_IMAGE_PROTOCOL *efi_loaded_image; -/** Loaded image protocol device path for this image */ -EFI_DEVICE_PATH_PROTOCOL *efi_loaded_image_path; - /** System table passed to entry point */ EFI_SYSTEM_TABLE *efi_systab; -/** EFI loaded image protocol GUID */ -static EFI_GUID efi_loaded_image_protocol_guid - = EFI_LOADED_IMAGE_PROTOCOL_GUID; - -/** EFI loaded image device path protocol GUID */ -static EFI_GUID efi_loaded_image_device_path_protocol_guid - = EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID; - /** Event used to signal shutdown */ static EFI_EVENT efi_shutdown_event; @@ -55,61 +42,6 @@ static EFI_EVENT efi_shutdown_event; static EFI_STATUS EFIAPI efi_unload ( EFI_HANDLE image_handle ); /** - * Check to see if driver supports a device - * - * @v driver EFI driver - * @v device EFI device - * @v child Path to child device, if any - * @ret efirc EFI status code - */ -static EFI_STATUS EFIAPI -efi_image_supported ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, - EFI_HANDLE device __unused, - EFI_DEVICE_PATH_PROTOCOL *child __unused ) { - - return EFI_UNSUPPORTED; -} - -/** - * Attach driver to device - * - * @v driver EFI driver - * @v device EFI device - * @v child Path to child device, if any - * @ret efirc EFI status code - */ -static EFI_STATUS EFIAPI -efi_image_start ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, - EFI_HANDLE device __unused, - EFI_DEVICE_PATH_PROTOCOL *child __unused ) { - - return EFI_UNSUPPORTED; -} - -/** - * Detach driver from device - * - * @v driver EFI driver - * @v device EFI device - * @v pci PCI device - * @v num_children Number of child devices - * @v children List of child devices - * @ret efirc EFI status code - */ -static EFI_STATUS EFIAPI -efi_image_stop ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, - EFI_HANDLE device __unused, UINTN num_children __unused, - EFI_HANDLE *children __unused ) { - - return EFI_UNSUPPORTED; -} - -/** EFI loaded image driver */ -static struct efi_driver efi_image_driver = - EFI_DRIVER_INIT ( NULL, efi_image_supported, efi_image_start, - efi_image_stop ); - -/** * Shut down in preparation for booting an OS. * * This hook gets called at ExitBootServices time in order to make @@ -152,7 +84,6 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle, struct efi_protocol *prot; struct efi_config_table *tab; void *loaded_image; - void *loaded_image_path; EFI_STATUS efirc; int rc; @@ -161,18 +92,24 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle, efi_systab = systab; /* Sanity checks */ - if ( ! systab ) - return EFI_NOT_AVAILABLE_YET; - if ( ! systab->ConOut ) - return EFI_NOT_AVAILABLE_YET; + if ( ! systab ) { + efirc = EFI_NOT_AVAILABLE_YET; + goto err_sanity; + } + if ( ! systab->ConOut ) { + efirc = EFI_NOT_AVAILABLE_YET; + goto err_sanity; + } if ( ! systab->BootServices ) { DBGC ( systab, "EFI provided no BootServices entry point\n" ); - return EFI_NOT_AVAILABLE_YET; + efirc = EFI_NOT_AVAILABLE_YET; + goto err_sanity; } if ( ! systab->RuntimeServices ) { DBGC ( systab, "EFI provided no RuntimeServices entry " "point\n" ); - return EFI_NOT_AVAILABLE_YET; + efirc = EFI_NOT_AVAILABLE_YET; + goto err_sanity; } DBGC ( systab, "EFI handle %p systab %p\n", image_handle, systab ); bs = systab->BootServices; @@ -187,8 +124,9 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle, } else { DBGC ( systab, "EFI does not provide protocol %s\n", efi_guid_ntoa ( &prot->guid ) ); - /* All protocols are required */ - return efirc; + /* Fail if protocol is required */ + if ( prot->required ) + goto err_missing_protocol; } } @@ -200,8 +138,10 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle, } else { DBGC ( systab, "EFI does not provide configuration " "table %s\n", efi_guid_ntoa ( &tab->guid ) ); - if ( tab->required ) - return EFI_NOT_AVAILABLE_YET; + if ( tab->required ) { + efirc = EFI_NOT_AVAILABLE_YET; + goto err_missing_table; + } } } @@ -213,27 +153,12 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle, rc = -EEFI ( efirc ); DBGC ( systab, "EFI could not get loaded image protocol: %s", strerror ( rc ) ); - return efirc; + goto err_no_loaded_image; } efi_loaded_image = loaded_image; DBGC ( systab, "EFI image base address %p\n", efi_loaded_image->ImageBase ); - /* Get loaded image device path protocol */ - if ( ( efirc = bs->OpenProtocol ( image_handle, - &efi_loaded_image_device_path_protocol_guid, - &loaded_image_path, image_handle, NULL, - EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( systab, "EFI could not get loaded image device path " - "protocol: %s", strerror ( rc ) ); - return efirc; - } - efi_loaded_image_path = loaded_image_path; - DBGC ( systab, "EFI image device path " ); - DBGC_EFI_DEVPATH ( systab, efi_loaded_image_path ); - DBGC ( systab, "\n" ); - /* EFI is perfectly capable of gracefully shutting down any * loaded devices if it decides to fall back to a legacy boot. * For no particularly comprehensible reason, it doesn't @@ -245,23 +170,31 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle, rc = -EEFI ( efirc ); DBGC ( systab, "EFI could not create ExitBootServices event: " "%s\n", strerror ( rc ) ); - return efirc; + goto err_create_event; } - /* Install an EFI driver on the image handle, to allow the - * driver to be subsequently unloaded. - */ - efi_image_driver.driver.DriverBindingHandle = image_handle; - if ( ( rc = efi_driver_install ( &efi_image_driver ) ) != 0 ) { - DBGC ( systab, "EFI could not install loaded image driver: " - "%s\n", strerror ( rc ) ); - return EFIRC ( rc ); + /* Install driver binding protocol */ + if ( ( rc = efi_driver_install() ) != 0 ) { + DBGC ( systab, "EFI could not install driver: %s\n", + strerror ( rc ) ); + efirc = EFIRC ( rc ); + goto err_driver_install; } /* Install image unload method */ efi_loaded_image->Unload = efi_unload; return 0; + + efi_driver_uninstall(); + err_driver_install: + bs->CloseEvent ( efi_shutdown_event ); + err_create_event: + err_no_loaded_image: + err_missing_table: + err_missing_protocol: + err_sanity: + return efirc; } /** @@ -278,12 +211,15 @@ static EFI_STATUS EFIAPI efi_unload ( EFI_HANDLE image_handle __unused ) { /* Shut down */ shutdown_exit(); + /* Disconnect any remaining devices */ + efi_driver_disconnect_all(); + + /* Uninstall driver binding protocol */ + efi_driver_uninstall(); + /* Uninstall exit boot services event */ bs->CloseEvent ( efi_shutdown_event ); - /* Uninstall loaded image driver */ - efi_driver_uninstall ( &efi_image_driver ); - DBGC ( systab, "EFI image unloaded\n" ); return 0; diff --git a/roms/ipxe/src/interface/efi/efi_pci.c b/roms/ipxe/src/interface/efi/efi_pci.c index dc7304a..86c781d 100644 --- a/roms/ipxe/src/interface/efi/efi_pci.c +++ b/roms/ipxe/src/interface/efi/efi_pci.c @@ -22,7 +22,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include -#include #include #include #include @@ -35,6 +34,22 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +/* Disambiguate the various error causes */ +#define EINFO_EEFI_PCI \ + __einfo_uniqify ( EINFO_EPLATFORM, 0x01, \ + "Could not open PCI I/O protocol" ) +#define EINFO_EEFI_PCI_NOT_PCI \ + __einfo_platformify ( EINFO_EEFI_PCI, EFI_UNSUPPORTED, \ + "Not a PCI device" ) +#define EEFI_PCI_NOT_PCI __einfo_error ( EINFO_EEFI_PCI_NOT_PCI ) +#define EINFO_EEFI_PCI_IN_USE \ + __einfo_platformify ( EINFO_EEFI_PCI, EFI_ACCESS_DENIED, \ + "PCI device already has a driver" ) +#define EEFI_PCI_IN_USE __einfo_error ( EINFO_EEFI_PCI_IN_USE ) +#define EEFI_PCI( efirc ) \ + EPLATFORM ( EINFO_EEFI_PCI, efirc, \ + EEFI_PCI_NOT_PCI, EEFI_PCI_IN_USE ) + /****************************************************************************** * * iPXE PCI API @@ -44,7 +59,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); /** PCI root bridge I/O protocol */ static EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *efipci; -EFI_REQUIRE_PROTOCOL ( EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL, &efipci ); +EFI_REQUEST_PROTOCOL ( EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL, &efipci ); static unsigned long efipci_address ( struct pci_device *pci, unsigned long location ) { @@ -59,6 +74,9 @@ int efipci_read ( struct pci_device *pci, unsigned long location, EFI_STATUS efirc; int rc; + if ( ! efipci ) + return -ENOTSUP; + if ( ( efirc = efipci->Pci.Read ( efipci, EFIPCI_WIDTH ( location ), efipci_address ( pci, location ), 1, value ) ) != 0 ) { @@ -77,6 +95,9 @@ int efipci_write ( struct pci_device *pci, unsigned long location, EFI_STATUS efirc; int rc; + if ( ! efipci ) + return -ENOTSUP; + if ( ( efirc = efipci->Pci.Write ( efipci, EFIPCI_WIDTH ( location ), efipci_address ( pci, location ), 1, &value ) ) != 0 ) { @@ -105,241 +126,115 @@ PROVIDE_PCIAPI_INLINE ( efi, pci_write_config_dword ); ****************************************************************************** */ -/** EFI PCI I/O protocol GUID */ -static EFI_GUID efi_pci_io_protocol_guid - = EFI_PCI_IO_PROTOCOL_GUID; - -/** EFI device path protocol GUID */ -static EFI_GUID efi_device_path_protocol_guid - = EFI_DEVICE_PATH_PROTOCOL_GUID; - -/** EFI PCI devices */ -static LIST_HEAD ( efi_pci_devices ); - /** - * Create EFI PCI device + * Open EFI PCI device * - * @v efidrv EFI driver - * @v device EFI device - * @ret efipci EFI PCI device, or NULL + * @v device EFI device handle + * @v attributes Protocol opening attributes + * @v pci PCI device to fill in + * @ret rc Return status code */ -struct efi_pci_device * efipci_create ( struct efi_driver *efidrv, - EFI_HANDLE device ) { +int efipci_open ( EFI_HANDLE device, UINT32 attributes, + struct pci_device *pci ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - struct efi_pci_device *efipci; union { EFI_PCI_IO_PROTOCOL *pci_io; void *interface; } pci_io; - union { - EFI_DEVICE_PATH_PROTOCOL *path; - void *interface; - } path; UINTN pci_segment, pci_bus, pci_dev, pci_fn; EFI_STATUS efirc; int rc; - /* Allocate PCI device */ - efipci = zalloc ( sizeof ( *efipci ) ); - if ( ! efipci ) - goto err_zalloc; - efipci->device = device; - efipci->efidrv = efidrv; - /* See if device is a PCI device */ - if ( ( efirc = bs->OpenProtocol ( device, - &efi_pci_io_protocol_guid, - &pci_io.interface, - efidrv->driver.DriverBindingHandle, - device, - EFI_OPEN_PROTOCOL_BY_DRIVER )) !=0 ){ - rc = -EEFI ( efirc ); - DBGCP ( efipci, "EFIPCI device %p is not a PCI device\n", - device ); + if ( ( efirc = bs->OpenProtocol ( device, &efi_pci_io_protocol_guid, + &pci_io.interface, efi_image_handle, + device, attributes ) ) != 0 ) { + rc = -EEFI_PCI ( efirc ); + DBGCP ( device, "EFIPCI %p %s cannot open PCI protocols: %s\n", + device, efi_handle_name ( device ), strerror ( rc ) ); goto err_open_protocol; } - efipci->pci_io = pci_io.pci_io; /* Get PCI bus:dev.fn address */ - if ( ( efirc = pci_io.pci_io->GetLocation ( pci_io.pci_io, - &pci_segment, + if ( ( efirc = pci_io.pci_io->GetLocation ( pci_io.pci_io, &pci_segment, &pci_bus, &pci_dev, &pci_fn ) ) != 0 ) { rc = -EEFI ( efirc ); - DBGC ( efipci, "EFIPCI device %p could not get PCI " - "location: %s\n", device, strerror ( rc ) ); + DBGC ( device, "EFIPCI %p %s could not get PCI location: %s\n", + device, efi_handle_name ( device ), strerror ( rc ) ); goto err_get_location; } - DBGC2 ( efipci, "EFIPCI device %p is PCI %04lx:%02lx:%02lx.%lx\n", - device, ( ( unsigned long ) pci_segment ), + DBGC2 ( device, "EFIPCI %p %s is PCI %04lx:%02lx:%02lx.%lx\n", device, + efi_handle_name ( device ), ( ( unsigned long ) pci_segment ), ( ( unsigned long ) pci_bus ), ( ( unsigned long ) pci_dev ), ( ( unsigned long ) pci_fn ) ); - /* Populate PCI device */ - pci_init ( &efipci->pci, PCI_BUSDEVFN ( pci_bus, pci_dev, pci_fn ) ); - if ( ( rc = pci_read_config ( &efipci->pci ) ) != 0 ) { - DBGC ( efipci, "EFIPCI " PCI_FMT " cannot read PCI " - "configuration: %s\n", - PCI_ARGS ( &efipci->pci ), strerror ( rc ) ); - goto err_pci_read_config; - } - - /* Retrieve device path */ - if ( ( efirc = bs->OpenProtocol ( device, - &efi_device_path_protocol_guid, - &path.interface, - efidrv->driver.DriverBindingHandle, - device, - EFI_OPEN_PROTOCOL_BY_DRIVER )) !=0 ){ - rc = -EEFI ( efirc ); - DBGC ( efipci, "EFIPCI " PCI_FMT " has no device path\n", - PCI_ARGS ( &efipci->pci ) ); - goto err_no_device_path; - } - efipci->path = path.path; - - /* Add to list of PCI devices */ - list_add ( &efipci->list, &efi_pci_devices ); - - return efipci; - - bs->CloseProtocol ( device, &efi_device_path_protocol_guid, - efidrv->driver.DriverBindingHandle, device ); - err_no_device_path: - err_pci_read_config: - err_get_location: - bs->CloseProtocol ( device, &efi_pci_io_protocol_guid, - efidrv->driver.DriverBindingHandle, device ); - err_open_protocol: - free ( efipci ); - err_zalloc: - return NULL; -} - -/** - * Enable EFI PCI device - * - * @v efipci EFI PCI device - * @ret rc Return status code - */ -int efipci_enable ( struct efi_pci_device *efipci ) { - EFI_PCI_IO_PROTOCOL *pci_io = efipci->pci_io; - /* Try to enable I/O cycles, memory cycles, and bus mastering. * Some platforms will 'helpfully' report errors if these bits * can't be enabled (for example, if the card doesn't actually * support I/O cycles). Work around any such platforms by * enabling bits individually and simply ignoring any errors. */ - pci_io->Attributes ( pci_io, EfiPciIoAttributeOperationEnable, - EFI_PCI_IO_ATTRIBUTE_IO, NULL ); - pci_io->Attributes ( pci_io, EfiPciIoAttributeOperationEnable, - EFI_PCI_IO_ATTRIBUTE_MEMORY, NULL ); - pci_io->Attributes ( pci_io, EfiPciIoAttributeOperationEnable, - EFI_PCI_IO_ATTRIBUTE_BUS_MASTER, NULL ); - - return 0; -} + pci_io.pci_io->Attributes ( pci_io.pci_io, + EfiPciIoAttributeOperationEnable, + EFI_PCI_IO_ATTRIBUTE_IO, NULL ); + pci_io.pci_io->Attributes ( pci_io.pci_io, + EfiPciIoAttributeOperationEnable, + EFI_PCI_IO_ATTRIBUTE_MEMORY, NULL ); + pci_io.pci_io->Attributes ( pci_io.pci_io, + EfiPciIoAttributeOperationEnable, + EFI_PCI_IO_ATTRIBUTE_BUS_MASTER, NULL ); -/** - * Find EFI PCI device by EFI device - * - * @v device EFI device - * @ret efipci EFI PCI device, or NULL - */ -struct efi_pci_device * efipci_find_efi ( EFI_HANDLE device ) { - struct efi_pci_device *efipci; - - list_for_each_entry ( efipci, &efi_pci_devices, list ) { - if ( efipci->device == device ) - return efipci; + /* Populate PCI device */ + pci_init ( pci, PCI_BUSDEVFN ( pci_bus, pci_dev, pci_fn ) ); + if ( ( rc = pci_read_config ( pci ) ) != 0 ) { + DBGC ( device, "EFIPCI %p %s cannot read PCI configuration: " + "%s\n", device, efi_handle_name ( device ), + strerror ( rc ) ); + goto err_pci_read_config; } - return NULL; -} -/** - * Find EFI PCI device by iPXE device - * - * @v dev Device - * @ret efipci EFI PCI device, or NULL - */ -struct efi_pci_device * efipci_find ( struct device *dev ) { - struct efi_pci_device *efipci; + return 0; - list_for_each_entry ( efipci, &efi_pci_devices, list ) { - if ( &efipci->pci.dev == dev ) - return efipci; - } - return NULL; + err_pci_read_config: + err_get_location: + bs->CloseProtocol ( device, &efi_pci_io_protocol_guid, + efi_image_handle, device ); + err_open_protocol: + return rc; } /** - * Add EFI device as child of EFI PCI device + * Close EFI PCI device * - * @v efipci EFI PCI device - * @v device EFI child device - * @ret efirc EFI status code + * @v device EFI device handle */ -int efipci_child_add ( struct efi_pci_device *efipci, EFI_HANDLE device ) { +void efipci_close ( EFI_HANDLE device ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - struct efi_driver *efidrv = efipci->efidrv; - union { - EFI_PCI_IO_PROTOCOL *pci_io; - void *interface; - } pci_io; - EFI_STATUS efirc; - int rc; - - /* Re-open the PCI_IO_PROTOCOL */ - if ( ( efirc = bs->OpenProtocol ( efipci->device, - &efi_pci_io_protocol_guid, - &pci_io.interface, - efidrv->driver.DriverBindingHandle, - device, - EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER - ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( efipci, "EFIPCI " PCI_FMT " could not add child: %s\n", - PCI_ARGS ( &efipci->pci ), strerror ( rc ) ); - return rc; - } - return 0; + bs->CloseProtocol ( device, &efi_pci_io_protocol_guid, + efi_image_handle, device ); } /** - * Remove EFI device as child of PCI device + * Get EFI PCI device information * - * @v efipci EFI PCI device - * @v device EFI child device - * @ret efirc EFI status code + * @v device EFI device handle + * @v pci PCI device to fill in + * @ret rc Return status code */ -void efipci_child_del ( struct efi_pci_device *efipci, EFI_HANDLE device ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - struct efi_driver *efidrv = efipci->efidrv; +int efipci_info ( EFI_HANDLE device, struct pci_device *pci ) { + int rc; - bs->CloseProtocol ( efipci->device, &efi_pci_io_protocol_guid, - efidrv->driver.DriverBindingHandle, device ); -} + /* Open PCI device, if possible */ + if ( ( rc = efipci_open ( device, EFI_OPEN_PROTOCOL_GET_PROTOCOL, + pci ) ) != 0 ) + return rc; -/** - * Destroy EFI PCI device - * - * @v efidrv EFI driver - * @v efipci EFI PCI device - */ -void efipci_destroy ( struct efi_driver *efidrv, - struct efi_pci_device *efipci ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + /* Close PCI device */ + efipci_close ( device ); - list_del ( &efipci->list ); - bs->CloseProtocol ( efipci->device, &efi_device_path_protocol_guid, - efidrv->driver.DriverBindingHandle, - efipci->device ); - bs->CloseProtocol ( efipci->device, &efi_pci_io_protocol_guid, - efidrv->driver.DriverBindingHandle, - efipci->device ); - free ( efipci ); + return 0; } /****************************************************************************** @@ -352,187 +247,111 @@ void efipci_destroy ( struct efi_driver *efidrv, /** * Check to see if driver supports a device * - * @v driver EFI driver - * @v device EFI device - * @v child Path to child device, if any - * @ret efirc EFI status code + * @v device EFI device handle + * @ret rc Return status code */ -static EFI_STATUS EFIAPI -efipci_supported ( EFI_DRIVER_BINDING_PROTOCOL *driver, EFI_HANDLE device, - EFI_DEVICE_PATH_PROTOCOL *child ) { - struct efi_driver *efidrv = - container_of ( driver, struct efi_driver, driver ); - struct efi_pci_device *efipci; +static int efipci_supported ( EFI_HANDLE device ) { + struct pci_device pci; int rc; - DBGCP ( efidrv, "EFIPCI DRIVER_SUPPORTED %p (%p)\n", device, child ); - - /* Create temporary corresponding PCI device, if any */ - efipci = efipci_create ( efidrv, device ); - if ( ! efipci ) { - /* Non-PCI devices are simply unsupported */ - rc = -ENOTSUP; - goto err_not_pci; - } + /* Get PCI device information */ + if ( ( rc = efipci_info ( device, &pci ) ) != 0 ) + return rc; /* Look for a driver */ - if ( ( rc = pci_find_driver ( &efipci->pci ) ) != 0 ) { - DBGCP ( efipci, "EFIPCI " PCI_FMT " has no driver\n", - PCI_ARGS ( &efipci->pci ) ); - goto err_no_driver; + if ( ( rc = pci_find_driver ( &pci ) ) != 0 ) { + DBGCP ( device, "EFIPCI %p %s has no driver\n", + device, efi_handle_name ( device ) ); + return rc; } - - DBGC ( efipci, "EFIPCI " PCI_FMT " is supported by driver \"%s\"\n", - PCI_ARGS ( &efipci->pci ), efipci->pci.id->name ); - - /* Destroy temporary PCI device */ - efipci_destroy ( efidrv, efipci ); + DBGC ( device, "EFIPCI %p %s has driver \"%s\"\n", + device, efi_handle_name ( device ), pci.id->name ); return 0; - - err_no_driver: - efipci_destroy ( efidrv, efipci ); - err_not_pci: - return EFIRC ( rc ); } /** * Attach driver to device * - * @v driver EFI driver - * @v device EFI device - * @v child Path to child device, if any - * @ret efirc EFI status code + * @v efidev EFI device + * @ret rc Return status code */ -static EFI_STATUS EFIAPI -efipci_start ( EFI_DRIVER_BINDING_PROTOCOL *driver, EFI_HANDLE device, - EFI_DEVICE_PATH_PROTOCOL *child ) { - struct efi_driver *efidrv = - container_of ( driver, struct efi_driver, driver ); - struct efi_pci_device *efipci; +static int efipci_start ( struct efi_device *efidev ) { + EFI_HANDLE device = efidev->device; + struct pci_device *pci; int rc; - DBGC ( efidrv, "EFIPCI DRIVER_START %p (%p)\n", device, child ); - - /* Create corresponding PCI device */ - efipci = efipci_create ( efidrv, device ); - if ( ! efipci ) { + /* Allocate PCI device */ + pci = zalloc ( sizeof ( *pci ) ); + if ( ! pci ) { rc = -ENOMEM; - goto err_create; + goto err_alloc; + } + + /* Open PCI device */ + if ( ( rc = efipci_open ( device, ( EFI_OPEN_PROTOCOL_BY_DRIVER | + EFI_OPEN_PROTOCOL_EXCLUSIVE ), + pci ) ) != 0 ) { + DBGC ( device, "EFIPCI %p %s could not open PCI device: %s\n", + device, efi_handle_name ( device ), strerror ( rc ) ); + DBGC_EFI_OPENERS ( device, device, &efi_pci_io_protocol_guid ); + goto err_open; } /* Find driver */ - if ( ( rc = pci_find_driver ( &efipci->pci ) ) != 0 ) { - DBGC ( efipci, "EFIPCI " PCI_FMT " has no driver\n", - PCI_ARGS ( &efipci->pci ) ); + if ( ( rc = pci_find_driver ( pci ) ) != 0 ) { + DBGC ( device, "EFIPCI %p %s has no driver\n", + device, efi_handle_name ( device ) ); goto err_find_driver; } - /* Enable PCI device */ - if ( ( rc = efipci_enable ( efipci ) ) != 0 ) - goto err_enable; + /* Mark PCI device as a child of the EFI device */ + pci->dev.parent = &efidev->dev; + list_add ( &pci->dev.siblings, &efidev->dev.children ); /* Probe driver */ - if ( ( rc = pci_probe ( &efipci->pci ) ) != 0 ) { - DBGC ( efipci, "EFIPCI " PCI_FMT " could not probe driver " - "\"%s\": %s\n", PCI_ARGS ( &efipci->pci ), - efipci->pci.id->name, strerror ( rc ) ); + if ( ( rc = pci_probe ( pci ) ) != 0 ) { + DBGC ( device, "EFIPCI %p %s could not probe driver \"%s\": " + "%s\n", device, efi_handle_name ( device ), + pci->id->name, strerror ( rc ) ); goto err_probe; } + DBGC ( device, "EFIPCI %p %s using driver \"%s\"\n", + device, efi_handle_name ( device ), pci->id->name ); + efidev_set_drvdata ( efidev, pci ); return 0; - pci_remove ( &efipci->pci ); + pci_remove ( pci ); err_probe: - err_enable: + list_del ( &pci->dev.siblings ); err_find_driver: - efipci_destroy ( efidrv, efipci ); - err_create: - return EFIRC ( rc ); + efipci_close ( device ); + err_open: + free ( pci ); + err_alloc: + return rc; } /** * Detach driver from device * - * @v driver EFI driver - * @v device EFI device - * @v pci PCI device - * @v num_children Number of child devices - * @v children List of child devices - * @ret efirc EFI status code - */ -static EFI_STATUS EFIAPI -efipci_stop ( EFI_DRIVER_BINDING_PROTOCOL *driver, EFI_HANDLE device, - UINTN num_children, EFI_HANDLE *children ) { - struct efi_driver *efidrv = - container_of ( driver, struct efi_driver, driver ); - struct efi_pci_device *efipci; - - DBGC ( efidrv, "EFIPCI DRIVER_STOP %p (%ld %p)\n", - device, ( ( unsigned long ) num_children ), children ); - - /* Find PCI device */ - efipci = efipci_find_efi ( device ); - if ( ! efipci ) { - DBGC ( efidrv, "EFIPCI device %p not started!\n", device ); - return EFI_INVALID_PARAMETER; - } - - /* Remove device */ - pci_remove ( &efipci->pci ); - - /* Delete EFI PCI device */ - efipci_destroy ( efidrv, efipci ); - - return 0; + * @v efidev EFI device + */ +static void efipci_stop ( struct efi_device *efidev ) { + struct pci_device *pci = efidev_get_drvdata ( efidev ); + EFI_HANDLE device = efidev->device; + + pci_remove ( pci ); + list_del ( &pci->dev.siblings ); + efipci_close ( device ); + free ( pci ); } /** EFI PCI driver */ -static struct efi_driver efipci_driver = - EFI_DRIVER_INIT ( "PCI", efipci_supported, efipci_start, efipci_stop ); - -/** - * Install EFI PCI driver - * - */ -static void efipci_driver_startup ( void ) { - struct efi_driver *efidrv = &efipci_driver; - int rc; - - /* Install driver */ - if ( ( rc = efi_driver_install ( efidrv ) ) != 0 ) { - DBGC ( efidrv, "EFIPCI could not install driver: %s\n", - strerror ( rc ) ); - return; - } - - DBGC ( efidrv, "EFIPCI driver installed\n" ); -} - -/** - * Shut down EFI PCI driver - * - * @v booting System is shutting down for OS boot - */ -static void efipci_driver_shutdown ( int booting __unused ) { - struct efi_driver *efidrv = &efipci_driver; - struct efi_pci_device *efipci; - struct efi_pci_device *tmp; - - /* Uninstall driver */ - efi_driver_uninstall ( efidrv ); - - /* Shut down any remaining devices */ - list_for_each_entry_safe ( efipci, tmp, &efi_pci_devices, list ) { - DBGC ( efipci, "EFIPCI " PCI_FMT " still active at shutdown; " - "forcing close\n", PCI_ARGS ( &efipci->pci ) ); - pci_remove ( &efipci->pci ); - efipci_destroy ( efidrv, efipci ); - } -} - -/** EFI PCI startup function */ -struct startup_fn startup_pci __startup_fn ( STARTUP_NORMAL ) = { - .startup = efipci_driver_startup, - .shutdown = efipci_driver_shutdown, +struct efi_driver efipci_driver __efi_driver ( EFI_DRIVER_NORMAL ) = { + .name = "PCI", + .supported = efipci_supported, + .start = efipci_start, + .stop = efipci_stop, }; diff --git a/roms/ipxe/src/interface/efi/efi_snp.c b/roms/ipxe/src/interface/efi/efi_snp.c index 9c54155..67fba34 100644 --- a/roms/ipxe/src/interface/efi/efi_snp.c +++ b/roms/ipxe/src/interface/efi/efi_snp.c @@ -27,48 +27,20 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include -#include +#include #include -#include #include #include +#include #include -#include #include -/** EFI simple network protocol GUID */ -static EFI_GUID efi_simple_network_protocol_guid - = EFI_SIMPLE_NETWORK_PROTOCOL_GUID; - -/** EFI device path protocol GUID */ -static EFI_GUID efi_device_path_protocol_guid - = EFI_DEVICE_PATH_PROTOCOL_GUID; - -/** EFI network interface identifier GUID */ -static EFI_GUID efi_nii_protocol_guid - = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_GUID; - -/** EFI network interface identifier GUID (extra special version) */ -static EFI_GUID efi_nii31_protocol_guid = { - /* At some point, it seems that someone decided to change the - * GUID. Current EFI builds ignore the older GUID, older EFI - * builds ignore the newer GUID, so we have to expose both. - */ - 0x1ACED566, 0x76ED, 0x4218, - { 0xBC, 0x81, 0x76, 0x7F, 0x1F, 0x97, 0x7A, 0x89 } -}; - -/** EFI component name protocol GUID */ -static EFI_GUID efi_component_name2_protocol_guid - = EFI_COMPONENT_NAME2_PROTOCOL_GUID; - -/** EFI load file protocol GUID */ -static EFI_GUID efi_load_file_protocol_guid - = EFI_LOAD_FILE_PROTOCOL_GUID; - /** List of SNP devices */ static LIST_HEAD ( efi_snp_devices ); +/** Network devices are currently claimed for use by iPXE */ +static int efi_snp_claimed; + /** * Set EFI SNP mode state * @@ -85,7 +57,7 @@ static void efi_snp_set_state ( struct efi_snp_device *snpdev ) { } else if ( ! netdev_is_open ( netdev ) ) { /* Network device not opened; report as Started */ mode->State = EfiSimpleNetworkStarted; - } else if ( ! netdev_rx_frozen ( netdev ) ) { + } else if ( efi_snp_claimed ) { /* Network device opened but claimed for use by iPXE; report * as Started to inhibit receive polling. */ @@ -164,7 +136,7 @@ efi_snp_start ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) { DBGC2 ( snpdev, "SNPDEV %p START\n", snpdev ); /* Fail if net device is currently claimed for use by iPXE */ - if ( ! netdev_rx_frozen ( snpdev->netdev ) ) + if ( efi_snp_claimed ) return EFI_NOT_READY; snpdev->started = 1; @@ -186,7 +158,7 @@ efi_snp_stop ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) { DBGC2 ( snpdev, "SNPDEV %p STOP\n", snpdev ); /* Fail if net device is currently claimed for use by iPXE */ - if ( ! netdev_rx_frozen ( snpdev->netdev ) ) + if ( efi_snp_claimed ) return EFI_NOT_READY; snpdev->started = 0; @@ -214,7 +186,7 @@ efi_snp_initialize ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, ( ( unsigned long ) extra_tx_bufsize ) ); /* Fail if net device is currently claimed for use by iPXE */ - if ( ! netdev_rx_frozen ( snpdev->netdev ) ) + if ( efi_snp_claimed ) return EFI_NOT_READY; if ( ( rc = netdev_open ( snpdev->netdev ) ) != 0 ) { @@ -244,7 +216,7 @@ efi_snp_reset ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN ext_verify ) { snpdev, ( ext_verify ? "with" : "without" ) ); /* Fail if net device is currently claimed for use by iPXE */ - if ( ! netdev_rx_frozen ( snpdev->netdev ) ) + if ( efi_snp_claimed ) return EFI_NOT_READY; netdev_close ( snpdev->netdev ); @@ -274,7 +246,7 @@ efi_snp_shutdown ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) { DBGC2 ( snpdev, "SNPDEV %p SHUTDOWN\n", snpdev ); /* Fail if net device is currently claimed for use by iPXE */ - if ( ! netdev_rx_frozen ( snpdev->netdev ) ) + if ( efi_snp_claimed ) return EFI_NOT_READY; netdev_close ( snpdev->netdev ); @@ -311,7 +283,7 @@ efi_snp_receive_filters ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, UINT32 enable, } /* Fail if net device is currently claimed for use by iPXE */ - if ( ! netdev_rx_frozen ( snpdev->netdev ) ) + if ( efi_snp_claimed ) return EFI_NOT_READY; /* Lie through our teeth, otherwise MNP refuses to accept us */ @@ -337,7 +309,7 @@ efi_snp_station_address ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN reset, ( reset ? "reset" : ll_protocol->ntoa ( new ) ) ); /* Fail if net device is currently claimed for use by iPXE */ - if ( ! netdev_rx_frozen ( snpdev->netdev ) ) + if ( efi_snp_claimed ) return EFI_NOT_READY; /* Set the MAC address */ @@ -348,7 +320,7 @@ efi_snp_station_address ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN reset, /* MAC address changes take effect only on netdev_open() */ if ( netdev_is_open ( snpdev->netdev ) ) { DBGC ( snpdev, "SNPDEV %p MAC address changed while net " - "devive open\n", snpdev ); + "device open\n", snpdev ); } return 0; @@ -374,7 +346,7 @@ efi_snp_statistics ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN reset, ( reset ? " reset" : "" ) ); /* Fail if net device is currently claimed for use by iPXE */ - if ( ! netdev_rx_frozen ( snpdev->netdev ) ) + if ( efi_snp_claimed ) return EFI_NOT_READY; /* Gather statistics */ @@ -426,7 +398,7 @@ efi_snp_mcast_ip_to_mac ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN ipv6, DBGC2 ( snpdev, "SNPDEV %p MCAST_IP_TO_MAC %s\n", snpdev, ip_str ); /* Fail if net device is currently claimed for use by iPXE */ - if ( ! netdev_rx_frozen ( snpdev->netdev ) ) + if ( efi_snp_claimed ) return EFI_NOT_READY; /* Try to hash the address */ @@ -463,7 +435,7 @@ efi_snp_nvdata ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN read, DBGC2_HDA ( snpdev, offset, data, len ); /* Fail if net device is currently claimed for use by iPXE */ - if ( ! netdev_rx_frozen ( snpdev->netdev ) ) + if ( efi_snp_claimed ) return EFI_NOT_READY; return EFI_UNSUPPORTED; @@ -486,7 +458,7 @@ efi_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, DBGC2 ( snpdev, "SNPDEV %p GET_STATUS", snpdev ); /* Fail if net device is currently claimed for use by iPXE */ - if ( ! netdev_rx_frozen ( snpdev->netdev ) ) + if ( efi_snp_claimed ) return EFI_NOT_READY; /* Poll the network device */ @@ -585,7 +557,7 @@ efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, DBGC2 ( snpdev, "\n" ); /* Fail if net device is currently claimed for use by iPXE */ - if ( ! netdev_rx_frozen ( snpdev->netdev ) ) + if ( efi_snp_claimed ) return EFI_NOT_READY; /* Sanity checks */ @@ -697,7 +669,7 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, ( ( unsigned long ) *len ) ); /* Fail if net device is currently claimed for use by iPXE */ - if ( ! netdev_rx_frozen ( snpdev->netdev ) ) + if ( efi_snp_claimed ) return EFI_NOT_READY; /* Poll the network device */ @@ -761,7 +733,7 @@ static VOID EFIAPI efi_snp_wait_for_packet ( EFI_EVENT event, return; /* Do nothing if net device is currently claimed for use by iPXE */ - if ( ! netdev_rx_frozen ( snpdev->netdev ) ) + if ( efi_snp_claimed ) return; /* Poll the network device */ @@ -922,36 +894,34 @@ static struct efi_snp_device * efi_snp_demux ( struct net_device *netdev ) { */ static int efi_snp_probe ( struct net_device *netdev ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - struct efi_pci_device *efipci; + struct efi_device *efidev; struct efi_snp_device *snpdev; + union { + EFI_DEVICE_PATH_PROTOCOL *path; + void *interface; + } path; EFI_DEVICE_PATH_PROTOCOL *path_end; MAC_ADDR_DEVICE_PATH *macpath; size_t path_prefix_len = 0; EFI_STATUS efirc; int rc; - /* Find EFI PCI device */ - efipci = efipci_find ( netdev->dev ); - if ( ! efipci ) { - DBG ( "SNP skipping non-PCI device %s\n", netdev->name ); + /* Find parent EFI device */ + efidev = efidev_parent ( netdev->dev ); + if ( ! efidev ) { + DBG ( "SNP skipping non-EFI device %s\n", netdev->name ); rc = 0; - goto err_no_pci; + goto err_no_efidev; } - /* Calculate device path prefix length */ - path_end = efi_devpath_end ( efipci->path ); - path_prefix_len = ( ( ( void * ) path_end ) - - ( ( void * ) efipci->path ) ); - /* Allocate the SNP device */ - snpdev = zalloc ( sizeof ( *snpdev ) + path_prefix_len + - sizeof ( *macpath ) ); + snpdev = zalloc ( sizeof ( *snpdev ) ); if ( ! snpdev ) { rc = -ENOMEM; goto err_alloc_snp; } snpdev->netdev = netdev_get ( netdev ); - snpdev->efipci = efipci; + snpdev->efidev = efidev; /* Sanity check */ if ( netdev->ll_protocol->ll_addr_len > sizeof ( EFI_MAC_ADDRESS ) ) { @@ -988,12 +958,13 @@ static int efi_snp_probe ( struct net_device *netdev ) { efi_snprintf ( snpdev->driver_name, ( sizeof ( snpdev->driver_name ) / sizeof ( snpdev->driver_name[0] ) ), - PRODUCT_SHORT_NAME " %s", netdev->dev->driver_name ); + "%s %s", product_short_name, netdev->dev->driver_name ); efi_snprintf ( snpdev->controller_name, ( sizeof ( snpdev->controller_name ) / sizeof ( snpdev->controller_name[0] ) ), - PRODUCT_SHORT_NAME " %s (%s)", - netdev->name, netdev_addr ( netdev ) ); + "%s %s (%s, %s)", product_short_name, + netdev->dev->driver_name, netdev->dev->name, + netdev_addr ( netdev ) ); snpdev->name2.GetDriverName = efi_snp_get_driver_name; snpdev->name2.GetControllerName = efi_snp_get_controller_name; snpdev->name2.SupportedLanguages = "en"; @@ -1007,9 +978,32 @@ static int efi_snp_probe ( struct net_device *netdev ) { sizeof ( snpdev->name[0] ) ), "%s", netdev->name ); + /* Get the parent device path */ + if ( ( efirc = bs->OpenProtocol ( efidev->device, + &efi_device_path_protocol_guid, + &path.interface, efi_image_handle, + efidev->device, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ + rc = -EEFI ( efirc ); + DBGC ( snpdev, "SNPDEV %p cannot get %p %s device path: %s\n", + snpdev, efidev->device, + efi_handle_name ( efidev->device ), strerror ( rc ) ); + goto err_open_device_path; + } + + /* Allocate the new device path */ + path_end = efi_devpath_end ( path.path ); + path_prefix_len = ( ( ( void * ) path_end ) - ( ( void * ) path.path )); + snpdev->path = zalloc ( path_prefix_len + sizeof ( *macpath ) + + sizeof ( *path_end ) ); + if ( ! snpdev->path ) { + rc = -ENOMEM; + goto err_alloc_device_path; + } + /* Populate the device path */ - memcpy ( &snpdev->path, efipci->path, path_prefix_len ); - macpath = ( ( ( void * ) &snpdev->path ) + path_prefix_len ); + memcpy ( snpdev->path, path.path, path_prefix_len ); + macpath = ( ( ( void * ) snpdev->path ) + path_prefix_len ); path_end = ( ( void * ) ( macpath + 1 ) ); memset ( macpath, 0, sizeof ( *macpath ) ); macpath->Header.Type = MESSAGING_DEVICE_PATH; @@ -1027,7 +1021,7 @@ static int efi_snp_probe ( struct net_device *netdev ) { if ( ( efirc = bs->InstallMultipleProtocolInterfaces ( &snpdev->handle, &efi_simple_network_protocol_guid, &snpdev->snp, - &efi_device_path_protocol_guid, &snpdev->path, + &efi_device_path_protocol_guid, snpdev->path, &efi_nii_protocol_guid, &snpdev->nii, &efi_nii31_protocol_guid, &snpdev->nii, &efi_component_name2_protocol_guid, &snpdev->name2, @@ -1039,49 +1033,62 @@ static int efi_snp_probe ( struct net_device *netdev ) { goto err_install_protocol_interface; } - /* Add as child of PCI device */ - if ( ( rc = efipci_child_add ( efipci, snpdev->handle ) ) != 0 ) { - DBGC ( snpdev, "SNPDEV %p could not become child of " PCI_FMT - ": %s\n", snpdev, PCI_ARGS ( &efipci->pci ), - strerror ( rc ) ); - goto err_efipci_child_add; + /* Add as child of EFI parent device */ + if ( ( rc = efi_child_add ( efidev->device, snpdev->handle ) ) != 0 ) { + DBGC ( snpdev, "SNPDEV %p could not become child of %p %s: " + "%s\n", snpdev, efidev->device, + efi_handle_name ( efidev->device ), strerror ( rc ) ); + goto err_efi_child_add; } /* Install HII */ if ( ( rc = efi_snp_hii_install ( snpdev ) ) != 0 ) { DBGC ( snpdev, "SNPDEV %p could not install HII: %s\n", snpdev, strerror ( rc ) ); - goto err_hii_install; + /* HII fails on several platforms. It's + * non-essential, so treat this as a non-fatal + * error. + */ } /* Add to list of SNP devices */ list_add ( &snpdev->list, &efi_snp_devices ); - DBGC ( snpdev, "SNPDEV %p installed for %s as device %p\n", - snpdev, netdev->name, snpdev->handle ); + /* Close device path */ + bs->CloseProtocol ( efidev->device, &efi_device_path_protocol_guid, + efi_image_handle, efidev->device ); + + DBGC ( snpdev, "SNPDEV %p installed for %s as device %p %s\n", + snpdev, netdev->name, snpdev->handle, + efi_handle_name ( snpdev->handle ) ); return 0; - efi_snp_hii_uninstall ( snpdev ); - err_hii_install: - efipci_child_del ( efipci, snpdev->handle ); - err_efipci_child_add: + if ( snpdev->package_list ) + efi_snp_hii_uninstall ( snpdev ); + efi_child_del ( efidev->device, snpdev->handle ); + err_efi_child_add: bs->UninstallMultipleProtocolInterfaces ( snpdev->handle, &efi_simple_network_protocol_guid, &snpdev->snp, - &efi_device_path_protocol_guid, &snpdev->path, + &efi_device_path_protocol_guid, snpdev->path, &efi_nii_protocol_guid, &snpdev->nii, &efi_nii31_protocol_guid, &snpdev->nii, &efi_component_name2_protocol_guid, &snpdev->name2, &efi_load_file_protocol_guid, &snpdev->load_file, NULL ); err_install_protocol_interface: + free ( snpdev->path ); + err_alloc_device_path: + bs->CloseProtocol ( efidev->device, &efi_device_path_protocol_guid, + efi_image_handle, efidev->device ); + err_open_device_path: bs->CloseEvent ( snpdev->snp.WaitForPacket ); err_create_event: err_ll_addr_len: netdev_put ( netdev ); free ( snpdev ); err_alloc_snp: - err_no_pci: + err_no_efidev: return rc; } @@ -1127,18 +1134,20 @@ static void efi_snp_remove ( struct net_device *netdev ) { } /* Uninstall the SNP */ - efi_snp_hii_uninstall ( snpdev ); - efipci_child_del ( snpdev->efipci, snpdev->handle ); + if ( snpdev->package_list ) + efi_snp_hii_uninstall ( snpdev ); + efi_child_del ( snpdev->efidev->device, snpdev->handle ); list_del ( &snpdev->list ); bs->UninstallMultipleProtocolInterfaces ( snpdev->handle, &efi_simple_network_protocol_guid, &snpdev->snp, - &efi_device_path_protocol_guid, &snpdev->path, + &efi_device_path_protocol_guid, snpdev->path, &efi_nii_protocol_guid, &snpdev->nii, &efi_nii31_protocol_guid, &snpdev->nii, &efi_component_name2_protocol_guid, &snpdev->name2, &efi_load_file_protocol_guid, &snpdev->load_file, NULL ); + free ( snpdev->path ); bs->CloseEvent ( snpdev->snp.WaitForPacket ); netdev_put ( snpdev->netdev ); free ( snpdev ); @@ -1153,6 +1162,22 @@ struct net_driver efi_snp_driver __net_driver = { }; /** + * Find SNP device by EFI device handle + * + * @v handle EFI device handle + * @ret snpdev SNP device, or NULL + */ +struct efi_snp_device * find_snpdev ( EFI_HANDLE handle ) { + struct efi_snp_device *snpdev; + + list_for_each_entry ( snpdev, &efi_snp_devices, list ) { + if ( snpdev->handle == handle ) + return snpdev; + } + return NULL; +} + +/** * Get most recently opened SNP device * * @ret snpdev Most recently opened SNP device, or NULL @@ -1168,23 +1193,17 @@ struct efi_snp_device * last_opened_snpdev ( void ) { } /** - * Claim network devices for use by iPXE + * Set SNP claimed/released state * + * @v claimed Network devices are claimed for use by iPXE */ -void efi_snp_claim ( void ) { - struct net_device *netdev; - - for_each_netdev ( netdev ) - netdev_rx_unfreeze ( netdev ); -} +void efi_snp_set_claimed ( int claimed ) { + struct efi_snp_device *snpdev; -/** - * Release network devices for use via SNP - * - */ -void efi_snp_release ( void ) { - struct net_device *netdev; + /* Claim SNP devices */ + efi_snp_claimed = claimed; - for_each_netdev ( netdev ) - netdev_rx_freeze ( netdev ); + /* Update SNP mode state for each interface */ + list_for_each_entry ( snpdev, &efi_snp_devices, list ) + efi_snp_set_state ( snpdev ); } diff --git a/roms/ipxe/src/interface/efi/efi_snp_hii.c b/roms/ipxe/src/interface/efi/efi_snp_hii.c index c272527..c49c76a 100644 --- a/roms/ipxe/src/interface/efi/efi_snp_hii.c +++ b/roms/ipxe/src/interface/efi/efi_snp_hii.c @@ -59,11 +59,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include -#include - -/** EFI configuration access protocol GUID */ -static EFI_GUID efi_hii_config_access_protocol_guid - = EFI_HII_CONFIG_ACCESS_PROTOCOL_GUID; /** EFI platform setup formset GUID */ static EFI_GUID efi_hii_platform_setup_formset_guid @@ -75,7 +70,7 @@ static EFI_GUID efi_hii_ibm_ucm_compliant_formset_guid /** EFI HII database protocol */ static EFI_HII_DATABASE_PROTOCOL *efihii; -EFI_REQUIRE_PROTOCOL ( EFI_HII_DATABASE_PROTOCOL, &efihii ); +EFI_REQUEST_PROTOCOL ( EFI_HII_DATABASE_PROTOCOL, &efihii ); /** * Identify settings to be exposed via HII @@ -162,7 +157,7 @@ efi_snp_hii_package_list ( struct efi_snp_device *snpdev ) { struct device *dev = netdev->dev; struct efi_ifr_builder ifr; EFI_HII_PACKAGE_LIST_HEADER *package; - const char *product_name; + const char *name; EFI_GUID package_guid; EFI_GUID formset_guid; EFI_GUID varstore_guid; @@ -173,7 +168,7 @@ efi_snp_hii_package_list ( struct efi_snp_device *snpdev ) { efi_ifr_init ( &ifr ); /* Determine product name */ - product_name = ( PRODUCT_NAME[0] ? PRODUCT_NAME : PRODUCT_SHORT_NAME ); + name = ( product_name[0] ? product_name : product_short_name ); /* Generate GUIDs */ efi_snp_hii_random_guid ( &package_guid ); @@ -181,13 +176,13 @@ efi_snp_hii_package_list ( struct efi_snp_device *snpdev ) { efi_snp_hii_random_guid ( &varstore_guid ); /* Generate title string (used more than once) */ - title_id = efi_ifr_string ( &ifr, "%s (%s)", product_name, + title_id = efi_ifr_string ( &ifr, "%s (%s)", name, netdev_addr ( netdev ) ); /* Generate opcodes */ efi_ifr_form_set_op ( &ifr, &formset_guid, title_id, - efi_ifr_string ( &ifr, - "Configure " PRODUCT_SHORT_NAME), + efi_ifr_string ( &ifr, "Configure %s", + product_short_name ), &efi_hii_platform_setup_formset_guid, &efi_hii_ibm_ucm_compliant_formset_guid, NULL ); efi_ifr_guid_class_op ( &ifr, EFI_NETWORK_DEVICE_CLASS ); @@ -197,7 +192,7 @@ efi_snp_hii_package_list ( struct efi_snp_device *snpdev ) { efi_ifr_text_op ( &ifr, efi_ifr_string ( &ifr, "Name" ), efi_ifr_string ( &ifr, "Firmware product name" ), - efi_ifr_string ( &ifr, "%s", product_name ) ); + efi_ifr_string ( &ifr, "%s", name ) ); efi_ifr_text_op ( &ifr, efi_ifr_string ( &ifr, "Version" ), efi_ifr_string ( &ifr, "Firmware version" ), @@ -649,6 +644,12 @@ int efi_snp_hii_install ( struct efi_snp_device *snpdev ) { int efirc; int rc; + /* Do nothing if HII database protocol is not supported */ + if ( ! efihii ) { + rc = -ENOTSUP; + goto err_no_hii; + } + /* Initialise HII protocol */ memcpy ( &snpdev->hii, &efi_snp_device_hii, sizeof ( snpdev->hii ) ); @@ -694,6 +695,7 @@ int efi_snp_hii_install ( struct efi_snp_device *snpdev ) { free ( snpdev->package_list ); snpdev->package_list = NULL; err_build_package_list: + err_no_hii: return rc; } @@ -705,6 +707,11 @@ int efi_snp_hii_install ( struct efi_snp_device *snpdev ) { void efi_snp_hii_uninstall ( struct efi_snp_device *snpdev ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + /* Do nothing if HII database protocol is not supported */ + if ( ! efihii ) + return; + + /* Uninstall protocols and remove package list */ bs->UninstallMultipleProtocolInterfaces ( snpdev->handle, &efi_hii_config_access_protocol_guid, &snpdev->hii, diff --git a/roms/ipxe/src/interface/efi/efi_utils.c b/roms/ipxe/src/interface/efi/efi_utils.c new file mode 100644 index 0000000..936ad48 --- /dev/null +++ b/roms/ipxe/src/interface/efi/efi_utils.c @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2011 Michael Brown . + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include +#include + +/** @file + * + * EFI utilities + * + */ + +/** + * Find end of device path + * + * @v path Path to device + * @ret path_end End of device path + */ +EFI_DEVICE_PATH_PROTOCOL * efi_devpath_end ( EFI_DEVICE_PATH_PROTOCOL *path ) { + + while ( path->Type != END_DEVICE_PATH_TYPE ) { + path = ( ( ( void * ) path ) + + /* There's this amazing new-fangled thing known as + * a UINT16, but who wants to use one of those? */ + ( ( path->Length[1] << 8 ) | path->Length[0] ) ); + } + + return path; +} + +/** + * Locate parent device supporting a given protocol + * + * @v device EFI device handle + * @v protocol Protocol GUID + * @v parent Parent EFI device handle to fill in + * @ret rc Return status code + */ +int efi_locate_device ( EFI_HANDLE device, EFI_GUID *protocol, + EFI_HANDLE *parent ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + union { + EFI_DEVICE_PATH_PROTOCOL *path; + void *interface; + } path; + EFI_DEVICE_PATH_PROTOCOL *devpath; + EFI_STATUS efirc; + int rc; + + /* Get device path */ + if ( ( efirc = bs->OpenProtocol ( device, + &efi_device_path_protocol_guid, + &path.interface, + efi_image_handle, device, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ + rc = -EEFI ( efirc ); + DBGC ( device, "EFIDEV %p %s cannot open device path: %s\n", + device, efi_handle_name ( device ), strerror ( rc ) ); + goto err_open_device_path; + } + devpath = path.path; + + /* Check for presence of specified protocol */ + if ( ( efirc = bs->LocateDevicePath ( protocol, &devpath, + parent ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( device, "EFIDEV %p %s has no parent supporting %s: %s\n", + device, efi_handle_name ( device ), + efi_guid_ntoa ( protocol ), strerror ( rc ) ); + goto err_locate_protocol; + } + + /* Success */ + rc = 0; + + err_locate_protocol: + bs->CloseProtocol ( device, &efi_device_path_protocol_guid, + efi_image_handle, device ); + err_open_device_path: + return rc; +} + +/** + * Add EFI device as child of another EFI device + * + * @v parent EFI parent device handle + * @v child EFI child device handle + * @ret rc Return status code + */ +int efi_child_add ( EFI_HANDLE parent, EFI_HANDLE child ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + void *devpath; + EFI_STATUS efirc; + int rc; + + /* Re-open the device path protocol */ + if ( ( efirc = bs->OpenProtocol ( parent, + &efi_device_path_protocol_guid, + &devpath, + efi_image_handle, child, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( parent, "EFIDEV %p %s could not add child", + parent, efi_handle_name ( parent ) ); + DBGC ( parent, " %p %s: %s\n", child, + efi_handle_name ( child ), strerror ( rc ) ); + DBGC_EFI_OPENERS ( parent, parent, + &efi_device_path_protocol_guid ); + return rc; + } + + DBGC2 ( parent, "EFIDEV %p %s added child", + parent, efi_handle_name ( parent ) ); + DBGC2 ( parent, " %p %s\n", child, efi_handle_name ( child ) ); + return 0; +} + +/** + * Remove EFI device as child of another EFI device + * + * @v parent EFI parent device handle + * @v child EFI child device handle + */ +void efi_child_del ( EFI_HANDLE parent, EFI_HANDLE child ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + + bs->CloseProtocol ( parent, &efi_device_path_protocol_guid, + efi_image_handle, child ); + DBGC2 ( parent, "EFIDEV %p %s removed child", + parent, efi_handle_name ( parent ) ); + DBGC2 ( parent, " %p %s\n", + child, efi_handle_name ( child ) ); +} + +/** + * Get underlying PCI device information + * + * @v device EFI device handle + * @v prefix Device name prefix + * @v dev Generic device to fill in + * @ret rc Return status code + */ +static int efi_pci_info ( EFI_HANDLE device, const char *prefix, + struct device *dev ) { + EFI_HANDLE pci_device; + struct pci_device pci; + int rc; + + /* Find parent PCI device */ + if ( ( rc = efi_locate_device ( device, &efi_pci_io_protocol_guid, + &pci_device ) ) != 0 ) { + DBGC ( device, "EFIDEV %p %s is not a PCI device: %s\n", + device, efi_handle_name ( device ), strerror ( rc ) ); + return rc; + } + + /* Get PCI device information */ + if ( ( rc = efipci_info ( pci_device, &pci ) ) != 0 ) { + DBGC ( device, "EFIDEV %p %s could not get PCI information: " + "%s\n", device, efi_handle_name ( device ), + strerror ( rc ) ); + return rc; + } + + /* Populate device information */ + memcpy ( &dev->desc, &pci.dev.desc, sizeof ( dev->desc ) ); + snprintf ( dev->name, sizeof ( dev->name ), "%s-%s", + prefix, pci.dev.name ); + + return 0; +} + +/** + * Get underlying device information + * + * @v device EFI device handle + * @v prefix Device name prefix + * @v dev Generic device to fill in + */ +void efi_device_info ( EFI_HANDLE device, const char *prefix, + struct device *dev ) { + int rc; + + /* Try getting underlying PCI device information */ + if ( ( rc = efi_pci_info ( device, prefix, dev ) ) == 0 ) + return; + + /* If we cannot get any underlying device information, fall + * back to providing information about the EFI handle. + */ + DBGC ( device, "EFIDEV %p %s could not get underlying device " + "information\n", device, efi_handle_name ( device ) ); + dev->desc.bus_type = BUS_TYPE_EFI; + snprintf ( dev->name, sizeof ( dev->name ), "%s-%p", prefix, device ); +} diff --git a/roms/ipxe/src/interface/efi/efi_wrap.c b/roms/ipxe/src/interface/efi/efi_wrap.c new file mode 100644 index 0000000..ff46b76 --- /dev/null +++ b/roms/ipxe/src/interface/efi/efi_wrap.c @@ -0,0 +1,315 @@ +/* + * Copyright (C) 2014 Michael Brown . + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** + * @file + * + * EFI image wrapping + * + */ + +#include +#include +#include +#include +#include +#include + +/** EFI system table wrapper */ +static EFI_SYSTEM_TABLE efi_systab_wrapper; + +/** EFI boot services table wrapper */ +static EFI_BOOT_SERVICES efi_bs_wrapper; + +/** Colour for debug messages */ +#define colour &efi_systab_wrapper + +/** + * Convert EFI status code to text + * + * @v efirc EFI status code + * @ret text EFI status code text + */ +static const char * efi_status ( EFI_STATUS efirc ) { + static char buf[ 19 /* "0xXXXXXXXXXXXXXXXX" + NUL */ ]; + + switch ( efirc ) { + case EFI_SUCCESS : return "0"; + case EFI_LOAD_ERROR : return "LOAD_ERROR"; + case EFI_INVALID_PARAMETER : return "INVALID_PARAMETER"; + case EFI_UNSUPPORTED : return "UNSUPPORTED"; + case EFI_BAD_BUFFER_SIZE : return "BAD_BUFFER_SIZE"; + case EFI_BUFFER_TOO_SMALL : return "BUFFER_TOO_SMALL"; + case EFI_NOT_READY : return "NOT_READY"; + case EFI_DEVICE_ERROR : return "DEVICE_ERROR"; + case EFI_WRITE_PROTECTED : return "WRITE_PROTECTED"; + case EFI_OUT_OF_RESOURCES : return "OUT_OF_RESOURCES"; + case EFI_VOLUME_CORRUPTED : return "VOLUME_CORRUPTED"; + case EFI_VOLUME_FULL : return "VOLUME_FULL"; + case EFI_NO_MEDIA : return "NO_MEDIA"; + case EFI_MEDIA_CHANGED : return "MEDIA_CHANGED"; + case EFI_NOT_FOUND : return "NOT_FOUND"; + case EFI_ACCESS_DENIED : return "ACCESS_DENIED"; + case EFI_NO_RESPONSE : return "NO_RESPONSE"; + case EFI_NO_MAPPING : return "NO_MAPPING"; + case EFI_TIMEOUT : return "TIMEOUT"; + case EFI_NOT_STARTED : return "NOT_STARTED"; + case EFI_ALREADY_STARTED : return "ALREADY_STARTED"; + case EFI_ABORTED : return "ABORTED"; + case EFI_ICMP_ERROR : return "ICMP_ERROR"; + case EFI_TFTP_ERROR : return "TFTP_ERROR"; + case EFI_PROTOCOL_ERROR : return "PROTOCOL_ERROR"; + case EFI_INCOMPATIBLE_VERSION : return "INCOMPATIBLE_VERSION"; + case EFI_SECURITY_VIOLATION : return "SECURITY_VIOLATION"; + case EFI_CRC_ERROR : return "CRC_ERROR"; + case EFI_END_OF_MEDIA : return "END_OF_MEDIA"; + case EFI_END_OF_FILE : return "END_OF_FILE"; + case EFI_INVALID_LANGUAGE : return "INVALID_LANGUAGE"; + case EFI_COMPROMISED_DATA : return "COMPROMISED_DATA"; + case EFI_WARN_UNKNOWN_GLYPH : return "WARN_UNKNOWN_GLYPH"; + case EFI_WARN_DELETE_FAILURE : return "WARN_DELETE_FAILURE"; + case EFI_WARN_WRITE_FAILURE : return "WARN_WRITE_FAILURE"; + case EFI_WARN_BUFFER_TOO_SMALL : return "WARN_BUFFER_TOO_SMALL"; + case EFI_WARN_STALE_DATA : return "WARN_STALE_DATA"; + default: + snprintf ( buf, sizeof ( buf ), "%#lx", + ( unsigned long ) efirc ); + return buf; + } +} + +/** + * Wrap HandleProtocol() + * + */ +static EFI_STATUS EFIAPI +efi_handle_protocol_wrapper ( EFI_HANDLE handle, EFI_GUID *protocol, + VOID **interface ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + void *retaddr = __builtin_return_address ( 0 ); + EFI_STATUS efirc; + + DBGC ( colour, "HandleProtocol ( %p %s, %s, ... ) ", handle, + efi_handle_name ( handle ), efi_guid_ntoa ( protocol ) ); + efirc = bs->HandleProtocol ( handle, protocol, interface ); + DBGC ( colour, "= %s ( %p ) -> %p\n", + efi_status ( efirc ), *interface, retaddr ); + return efirc; +} + +/** + * Wrap LocateHandle() + * + */ +static EFI_STATUS EFIAPI +efi_locate_handle_wrapper ( EFI_LOCATE_SEARCH_TYPE search_type, + EFI_GUID *protocol, VOID *search_key, + UINTN *buffer_size, EFI_HANDLE *buffer ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + void *retaddr = __builtin_return_address ( 0 ); + EFI_STATUS efirc; + + DBGC ( colour, "LocateHandle ( %d, %s, ..., %zd, ... ) ", search_type, + efi_guid_ntoa ( protocol ), ( ( size_t ) *buffer_size ) ); + efirc = bs->LocateHandle ( search_type, protocol, search_key, + buffer_size, buffer ); + DBGC ( colour, "= %s ( %zd ) -> %p\n", + efi_status ( efirc ), ( ( size_t ) *buffer_size ), retaddr ); + return efirc; +} + +/** + * Wrap LocateDevicePath() + * + */ +static EFI_STATUS EFIAPI +efi_locate_device_path_wrapper ( EFI_GUID *protocol, + EFI_DEVICE_PATH_PROTOCOL **device_path, + EFI_HANDLE *device ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + void *retaddr = __builtin_return_address ( 0 ); + EFI_STATUS efirc; + + DBGC ( colour, "LocateDevicePath ( %s, %s, ... ) ", + efi_guid_ntoa ( protocol ), efi_devpath_text ( *device_path ) ); + efirc = bs->LocateDevicePath ( protocol, device_path, device ); + DBGC ( colour, "= %s ( %p, ", + efi_status ( efirc ), efi_devpath_text ( *device_path ) ); + DBGC ( colour, "%p %s ) -> %p\n", + *device, efi_handle_name ( *device ), retaddr ); + return efirc; +} + +/** + * Wrap LoadImage() + * + */ +static EFI_STATUS EFIAPI +efi_load_image_wrapper ( BOOLEAN boot_policy, EFI_HANDLE parent_image_handle, + EFI_DEVICE_PATH_PROTOCOL *device_path, + VOID *source_buffer, UINTN source_size, + EFI_HANDLE *image_handle ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + void *retaddr = __builtin_return_address ( 0 ); + EFI_STATUS efirc; + + DBGC ( colour, "LoadImage ( %d, %p %s, ", boot_policy, + parent_image_handle, efi_handle_name ( parent_image_handle ) ); + DBGC ( colour, "%s, %p, %#llx, ... ) ", + efi_devpath_text ( device_path ), source_buffer, + ( ( unsigned long long ) source_size ) ); + efirc = bs->LoadImage ( boot_policy, parent_image_handle, device_path, + source_buffer, source_size, image_handle ); + DBGC ( colour, "= %s ( ", efi_status ( efirc ) ); + if ( efirc == 0 ) { + DBGC ( colour, "%p %s ", *image_handle, + efi_handle_name ( *image_handle ) ); + } + DBGC ( colour, ") -> %p\n", retaddr ); + + /* Wrap the new image */ + if ( efirc == 0 ) + efi_wrap ( *image_handle ); + + return efirc; +} + +/** + * Wrap ExitBootServices() + * + */ +static EFI_STATUS EFIAPI +efi_exit_boot_services_wrapper ( EFI_HANDLE image_handle, UINTN map_key ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + void *retaddr = __builtin_return_address ( 0 ); + EFI_STATUS efirc; + + DBGC ( colour, "ExitBootServices ( %p %s, %#llx ) ", + image_handle, efi_handle_name ( image_handle ), + ( ( unsigned long long ) map_key ) ); + efirc = bs->ExitBootServices ( image_handle, map_key ); + DBGC ( colour, "= %s -> %p\n", efi_status ( efirc ), retaddr ); + return efirc; +} + +/** + * Wrap OpenProtocol() + * + */ +static EFI_STATUS EFIAPI +efi_open_protocol_wrapper ( EFI_HANDLE handle, EFI_GUID *protocol, + VOID **interface, EFI_HANDLE agent_handle, + EFI_HANDLE controller_handle, UINT32 attributes ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + void *retaddr = __builtin_return_address ( 0 ); + EFI_STATUS efirc; + + DBGC ( colour, "OpenProtocol ( %p %s, %s, ..., ", handle, + efi_handle_name ( handle ), efi_guid_ntoa ( protocol ) ); + DBGC ( colour, "%p %s, ", agent_handle, + efi_handle_name ( agent_handle ) ); + DBGC ( colour, "%p %s, %#x ) ", controller_handle, + efi_handle_name ( controller_handle ), attributes ); + efirc = bs->OpenProtocol ( handle, protocol, interface, agent_handle, + controller_handle, attributes ); + DBGC ( colour, "= %s ( %p ) -> %p\n", + efi_status ( efirc ), *interface, retaddr ); + return efirc; +} + +/** + * Wrap LocateProtocol() + * + */ +static EFI_STATUS EFIAPI +efi_locate_protocol_wrapper ( EFI_GUID *protocol, VOID *registration, + VOID **interface ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + void *retaddr = __builtin_return_address ( 0 ); + EFI_STATUS efirc; + + DBGC ( colour, "LocateProtocol ( %s, %p, ... ) ", + efi_guid_ntoa ( protocol ), registration ); + efirc = bs->LocateProtocol ( protocol, registration, interface ); + DBGC ( colour, "= %s ( %p ) -> %p\n", + efi_status ( efirc ), *interface, retaddr ); + return efirc; +} + +/** + * Wrap the calls made by a loaded image + * + * @v handle Image handle + */ + void efi_wrap ( EFI_HANDLE handle ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + union { + EFI_LOADED_IMAGE_PROTOCOL *image; + void *intf; + } loaded; + EFI_STATUS efirc; + int rc; + + /* Do nothing unless debugging is enabled */ + if ( ! DBG_LOG ) + return; + + /* Populate table wrappers */ + memcpy ( &efi_systab_wrapper, efi_systab, + sizeof ( efi_systab_wrapper ) ); + memcpy ( &efi_bs_wrapper, bs, sizeof ( efi_bs_wrapper ) ); + efi_systab_wrapper.BootServices = &efi_bs_wrapper; + efi_bs_wrapper.HandleProtocol = efi_handle_protocol_wrapper; + efi_bs_wrapper.LocateHandle = efi_locate_handle_wrapper; + efi_bs_wrapper.LocateDevicePath = efi_locate_device_path_wrapper; + efi_bs_wrapper.LoadImage = efi_load_image_wrapper; + efi_bs_wrapper.ExitBootServices = efi_exit_boot_services_wrapper; + efi_bs_wrapper.OpenProtocol = efi_open_protocol_wrapper; + efi_bs_wrapper.LocateProtocol = efi_locate_protocol_wrapper; + + /* Open loaded image protocol */ + if ( ( efirc = bs->OpenProtocol ( handle, + &efi_loaded_image_protocol_guid, + &loaded.intf, efi_image_handle, NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ + rc = -EEFI ( efirc ); + DBGC ( colour, "Could not get loaded image protocol for %p %s: " + "%s\n", handle, efi_handle_name ( handle ), + strerror ( rc ) ); + return; + } + + /* Provide system table wrapper to image */ + loaded.image->SystemTable = &efi_systab_wrapper; + DBGC ( colour, "Wrapped image %p %s at base %p has protocols:\n", + handle, efi_handle_name ( handle ), loaded.image->ImageBase ); + DBGC_EFI_PROTOCOLS ( colour, handle ); + DBGC ( colour, "Parent image %p %s\n", loaded.image->ParentHandle, + efi_handle_name ( loaded.image->ParentHandle ) ); + DBGC ( colour, "Device %p %s ", loaded.image->DeviceHandle, + efi_handle_name ( loaded.image->DeviceHandle ) ); + DBGC ( colour, "file %p %s\n", loaded.image->FilePath, + efi_devpath_text ( loaded.image->FilePath ) ); + + /* Close loaded image protocol */ + bs->CloseProtocol ( handle, &efi_loaded_image_protocol_guid, + efi_image_handle, NULL ); +} diff --git a/roms/ipxe/src/interface/smbios/smbios_settings.c b/roms/ipxe/src/interface/smbios/smbios_settings.c index 5dcf002..83e4320 100644 --- a/roms/ipxe/src/interface/smbios/smbios_settings.c +++ b/roms/ipxe/src/interface/smbios/smbios_settings.c @@ -241,3 +241,15 @@ const struct setting asset_setting __setting ( SETTING_HOST_EXTRA, asset ) = { .type = &setting_type_string, .scope = &smbios_settings_scope, }; + +/** Board serial number setting (may differ from chassis serial number) */ +const struct setting board_serial_setting __setting ( SETTING_HOST_EXTRA, + board_serial ) = { + .name = "board-serial", + .description = "Base board serial", + .tag = SMBIOS_STRING_TAG ( SMBIOS_TYPE_BASE_BOARD_INFORMATION, + struct smbios_base_board_information, + serial ), + .type = &setting_type_string, + .scope = &smbios_settings_scope, +}; diff --git a/roms/ipxe/src/interface/xen/xenbus.c b/roms/ipxe/src/interface/xen/xenbus.c new file mode 100644 index 0000000..ffc8aba --- /dev/null +++ b/roms/ipxe/src/interface/xen/xenbus.c @@ -0,0 +1,393 @@ +/* + * Copyright (C) 2014 Michael Brown . + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Xen device bus + * + */ + +/* Disambiguate the various error causes */ +#define ETIMEDOUT_UNKNOWN \ + __einfo_error ( EINFO_ETIMEDOUT_UNKNOWN ) +#define EINFO_ETIMEDOUT_UNKNOWN \ + __einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateUnknown, \ + "Unknown" ) +#define ETIMEDOUT_INITIALISING \ + __einfo_error ( EINFO_ETIMEDOUT_INITIALISING ) +#define EINFO_ETIMEDOUT_INITIALISING \ + __einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateInitialising, \ + "Initialising" ) +#define ETIMEDOUT_INITWAIT \ + __einfo_error ( EINFO_ETIMEDOUT_INITWAIT ) +#define EINFO_ETIMEDOUT_INITWAIT \ + __einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateInitWait, \ + "InitWait" ) +#define ETIMEDOUT_INITIALISED \ + __einfo_error ( EINFO_ETIMEDOUT_INITIALISED ) +#define EINFO_ETIMEDOUT_INITIALISED \ + __einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateInitialised, \ + "Initialised" ) +#define ETIMEDOUT_CONNECTED \ + __einfo_error ( EINFO_ETIMEDOUT_CONNECTED ) +#define EINFO_ETIMEDOUT_CONNECTED \ + __einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateConnected, \ + "Connected" ) +#define ETIMEDOUT_CLOSING \ + __einfo_error ( EINFO_ETIMEDOUT_CLOSING ) +#define EINFO_ETIMEDOUT_CLOSING \ + __einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateClosing, \ + "Closing" ) +#define ETIMEDOUT_CLOSED \ + __einfo_error ( EINFO_ETIMEDOUT_CLOSED ) +#define EINFO_ETIMEDOUT_CLOSED \ + __einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateClosed, \ + "Closed" ) +#define ETIMEDOUT_RECONFIGURING \ + __einfo_error ( EINFO_ETIMEDOUT_RECONFIGURING ) +#define EINFO_ETIMEDOUT_RECONFIGURING \ + __einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateReconfiguring, \ + "Reconfiguring" ) +#define ETIMEDOUT_RECONFIGURED \ + __einfo_error ( EINFO_ETIMEDOUT_RECONFIGURED ) +#define EINFO_ETIMEDOUT_RECONFIGURED \ + __einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateReconfigured, \ + "Reconfigured" ) +#define ETIMEDOUT_STATE( state ) \ + EUNIQ ( EINFO_ETIMEDOUT, (state), ETIMEDOUT_UNKNOWN, \ + ETIMEDOUT_INITIALISING, ETIMEDOUT_INITWAIT, \ + ETIMEDOUT_INITIALISED, ETIMEDOUT_CONNECTED, \ + ETIMEDOUT_CLOSING, ETIMEDOUT_CLOSED, \ + ETIMEDOUT_RECONFIGURING, ETIMEDOUT_RECONFIGURED ) + +/** Maximum time to wait for backend to reach a given state, in ticks */ +#define XENBUS_BACKEND_TIMEOUT ( 5 * TICKS_PER_SEC ) + +/** + * Set device state + * + * @v xendev Xen device + * @v state New state + * @ret rc Return status code + */ +int xenbus_set_state ( struct xen_device *xendev, int state ) { + int rc; + + /* Attempt to set state */ + if ( ( rc = xenstore_write_num ( xendev->xen, state, xendev->key, + "state", NULL ) ) != 0 ) { + DBGC ( xendev, "XENBUS %s could not set state=\"%d\": %s\n", + xendev->key, state, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Get backend state + * + * @v xendev Xen device + * @ret state Backend state, or negative error + */ +int xenbus_backend_state ( struct xen_device *xendev ) { + unsigned long state; + int rc; + + /* Attempt to get backend state */ + if ( ( rc = xenstore_read_num ( xendev->xen, &state, xendev->backend, + "state", NULL ) ) != 0 ) { + DBGC ( xendev, "XENBUS %s could not read %s/state: %s\n", + xendev->key, xendev->backend, strerror ( rc ) ); + return rc; + } + + return state; +} + +/** + * Wait for backend to reach a given state + * + * @v xendev Xen device + * @v state Desired backend state + * @ret rc Return status code + */ +int xenbus_backend_wait ( struct xen_device *xendev, int state ) { + unsigned long started = currticks(); + unsigned long elapsed; + unsigned int attempts = 0; + int current_state; + int rc; + + /* Wait for backend to reach this state */ + do { + + /* Get current backend state */ + current_state = xenbus_backend_state ( xendev ); + if ( current_state < 0 ) { + rc = current_state; + return rc; + } + if ( current_state == state ) + return 0; + + /* Allow time for backend to react */ + cpu_nap(); + + /* XenStore is a very slow interface; any fixed delay + * time would be dwarfed by the XenStore access time. + * We therefore use wall clock to time out this + * operation. + */ + elapsed = ( currticks() - started ); + attempts++; + + } while ( elapsed < XENBUS_BACKEND_TIMEOUT ); + + /* Construct status code from current backend state */ + rc = -ETIMEDOUT_STATE ( current_state ); + DBGC ( xendev, "XENBUS %s timed out after %d attempts waiting for " + "%s/state=\"%d\": %s\n", xendev->key, attempts, xendev->backend, + state, strerror ( rc ) ); + + return rc; +} + +/** + * Find driver for Xen device + * + * @v type Device type + * @ret driver Driver, or NULL + */ +static struct xen_driver * xenbus_find_driver ( const char *type ) { + struct xen_driver *xendrv; + + for_each_table_entry ( xendrv, XEN_DRIVERS ) { + if ( strcmp ( xendrv->type, type ) == 0 ) + return xendrv; + } + return NULL; +} + +/** + * Probe Xen device + * + * @v xen Xen hypervisor + * @v parent Parent device + * @v type Device type + * @v instance Device instance + * @ret rc Return status code + */ +static int xenbus_probe_device ( struct xen_hypervisor *xen, + struct device *parent, const char *type, + const char *instance ) { + struct xen_device *xendev; + size_t key_len; + int rc; + + /* Allocate and initialise structure */ + key_len = ( 7 /* "device/" */ + strlen ( type ) + 1 /* "/" */ + + strlen ( instance ) + 1 /* NUL */ ); + xendev = zalloc ( sizeof ( *xendev ) + key_len ); + if ( ! xendev ) { + rc = -ENOMEM; + goto err_alloc; + } + snprintf ( xendev->dev.name, sizeof ( xendev->dev.name ), "%s/%s", + type, instance ); + xendev->dev.desc.bus_type = BUS_TYPE_XEN; + INIT_LIST_HEAD ( &xendev->dev.children ); + list_add_tail ( &xendev->dev.siblings, &parent->children ); + xendev->dev.parent = parent; + xendev->xen = xen; + xendev->key = ( ( void * ) ( xendev + 1 ) ); + snprintf ( xendev->key, key_len, "device/%s/%s", type, instance ); + + /* Read backend key */ + if ( ( rc = xenstore_read ( xen, &xendev->backend, xendev->key, + "backend", NULL ) ) != 0 ) { + DBGC ( xendev, "XENBUS %s could not read backend: %s\n", + xendev->key, strerror ( rc ) ); + goto err_read_backend; + } + + /* Read backend domain ID */ + if ( ( rc = xenstore_read_num ( xen, &xendev->backend_id, xendev->key, + "backend-id", NULL ) ) != 0 ) { + DBGC ( xendev, "XENBUS %s could not read backend-id: %s\n", + xendev->key, strerror ( rc ) ); + goto err_read_backend_id; + } + DBGC ( xendev, "XENBUS %s backend=\"%s\" in domain %ld\n", + xendev->key, xendev->backend, xendev->backend_id ); + + /* Look for a driver */ + xendev->driver = xenbus_find_driver ( type ); + if ( ! xendev->driver ) { + DBGC ( xendev, "XENBUS %s has no driver\n", xendev->key ); + /* Not a fatal error */ + rc = 0; + goto err_no_driver; + } + xendev->dev.driver_name = xendev->driver->name; + DBGC ( xendev, "XENBUS %s has driver \"%s\"\n", xendev->key, + xendev->driver->name ); + + /* Probe driver */ + if ( ( rc = xendev->driver->probe ( xendev ) ) != 0 ) { + DBGC ( xendev, "XENBUS could not probe %s: %s\n", + xendev->key, strerror ( rc ) ); + goto err_probe; + } + + return 0; + + xendev->driver->remove ( xendev ); + err_probe: + err_no_driver: + err_read_backend_id: + free ( xendev->backend ); + err_read_backend: + list_del ( &xendev->dev.siblings ); + free ( xendev ); + err_alloc: + return rc; +} + +/** + * Remove Xen device + * + * @v xendev Xen device + */ +static void xenbus_remove_device ( struct xen_device *xendev ) { + + /* Remove device */ + xendev->driver->remove ( xendev ); + free ( xendev->backend ); + list_del ( &xendev->dev.siblings ); + free ( xendev ); +} + +/** + * Probe Xen devices of a given type + * + * @v xen Xen hypervisor + * @v parent Parent device + * @v type Device type + * @ret rc Return status code + */ +static int xenbus_probe_type ( struct xen_hypervisor *xen, + struct device *parent, const char *type ) { + char *children; + char *child; + size_t len; + int rc; + + /* Get children of this key */ + if ( ( rc = xenstore_directory ( xen, &children, &len, "device", + type, NULL ) ) != 0 ) { + DBGC ( xen, "XENBUS could not list \"%s\" devices: %s\n", + type, strerror ( rc ) ); + goto err_directory; + } + + /* Probe each child */ + for ( child = children ; child < ( children + len ) ; + child += ( strlen ( child ) + 1 /* NUL */ ) ) { + if ( ( rc = xenbus_probe_device ( xen, parent, type, + child ) ) != 0 ) + goto err_probe_device; + } + + free ( children ); + return 0; + + err_probe_device: + free ( children ); + err_directory: + return rc; +} + +/** + * Probe Xen bus + * + * @v xen Xen hypervisor + * @v parent Parent device + * @ret rc Return status code + */ +int xenbus_probe ( struct xen_hypervisor *xen, struct device *parent ) { + char *types; + char *type; + size_t len; + int rc; + + /* Get children of "device" key */ + if ( ( rc = xenstore_directory ( xen, &types, &len, "device", + NULL ) ) != 0 ) { + DBGC ( xen, "XENBUS could not list device types: %s\n", + strerror ( rc ) ); + goto err_directory; + } + + /* Probe each child type */ + for ( type = types ; type < ( types + len ) ; + type += ( strlen ( type ) + 1 /* NUL */ ) ) { + if ( ( rc = xenbus_probe_type ( xen, parent, type ) ) != 0 ) + goto err_probe_type; + } + + free ( types ); + return 0; + + xenbus_remove ( xen, parent ); + err_probe_type: + free ( types ); + err_directory: + return rc; +} + +/** + * Remove Xen bus + * + * @v xen Xen hypervisor + * @v parent Parent device + */ +void xenbus_remove ( struct xen_hypervisor *xen __unused, + struct device *parent ) { + struct xen_device *xendev; + struct xen_device *tmp; + + /* Remove devices */ + list_for_each_entry_safe ( xendev, tmp, &parent->children, + dev.siblings ) { + xenbus_remove_device ( xendev ); + } +} diff --git a/roms/ipxe/src/interface/xen/xengrant.c b/roms/ipxe/src/interface/xen/xengrant.c new file mode 100644 index 0000000..be12b23 --- /dev/null +++ b/roms/ipxe/src/interface/xen/xengrant.c @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2014 Michael Brown . + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Xen grant tables + * + */ + +/** Grant table version to try setting + * + * Using version 1 grant tables limits guests to using 16TB of + * grantable RAM, and prevents the use of subpage grants. Some + * versions of the Xen hypervisor refuse to allow the grant table + * version to be set after the first grant references have been + * created, so the loaded operating system may be stuck with whatever + * choice we make here. We therefore currently use version 2 grant + * tables, since they give the most flexibility to the loaded OS. + * + * Current versions (7.2.0) of the Windows PV drivers have no support + * for version 2 grant tables, and will merrily create version 1 + * entries in what the hypervisor believes to be a version 2 table. + * This causes some confusion. + * + * Avoid this problem by attempting to use version 1 tables, since + * otherwise we may render Windows unable to boot. + * + * Play nicely with other potential bootloaders by accepting either + * version 1 or version 2 grant tables (if we are unable to set our + * requested version). + */ +#define XENGRANT_TRY_VERSION 1 + +/** + * Initialise grant table + * + * @v xen Xen hypervisor + * @ret rc Return status code + */ +int xengrant_init ( struct xen_hypervisor *xen ) { + struct gnttab_query_size size; + struct gnttab_set_version set_version; + struct gnttab_get_version get_version; + struct grant_entry_v1 *v1; + union grant_entry_v2 *v2; + unsigned int version; + int xenrc; + int rc; + + /* Get grant table size */ + size.dom = DOMID_SELF; + if ( ( xenrc = xengrant_query_size ( xen, &size ) ) != 0 ) { + rc = -EXEN ( xenrc ); + DBGC ( xen, "XENGRANT could not get table size: %s\n", + strerror ( rc ) ); + return rc; + } + xen->grant.len = ( size.nr_frames * PAGE_SIZE ); + + /* Set grant table version, if applicable */ + set_version.version = XENGRANT_TRY_VERSION; + if ( ( xenrc = xengrant_set_version ( xen, &set_version ) ) != 0 ) { + rc = -EXEN ( xenrc ); + DBGC ( xen, "XENGRANT could not set version %d: %s\n", + XENGRANT_TRY_VERSION, strerror ( rc ) ); + /* Continue; use whatever version is current */ + } + + /* Get grant table version */ + get_version.dom = DOMID_SELF; + get_version.pad = 0; + if ( ( xenrc = xengrant_get_version ( xen, &get_version ) ) == 0 ) { + version = get_version.version; + switch ( version ) { + + case 0: + /* Version not yet specified: will be version 1 */ + version = 1; + break; + + case 1 : + /* Version 1 table: nothing special to do */ + break; + + case 2: + /* Version 2 table: configure shift appropriately */ + xen->grant.shift = ( fls ( sizeof ( *v2 ) / + sizeof ( *v1 ) ) - 1 ); + break; + + default: + /* Unsupported version */ + DBGC ( xen, "XENGRANT detected unsupported version " + "%d\n", version ); + return -ENOTSUP; + + } + } else { + rc = -EXEN ( xenrc ); + DBGC ( xen, "XENGRANT could not get version (assuming v1): " + "%s\n", strerror ( rc ) ); + version = 1; + } + + DBGC ( xen, "XENGRANT using v%d table with %d entries\n", + version, xengrant_entries ( xen ) ); + return 0; +} + +/** + * Allocate grant references + * + * @v xen Xen hypervisor + * @v refs Grant references to fill in + * @v count Number of references + * @ret rc Return status code + */ +int xengrant_alloc ( struct xen_hypervisor *xen, grant_ref_t *refs, + unsigned int count ) { + struct grant_entry_header *hdr; + unsigned int entries = xengrant_entries ( xen ); + unsigned int mask = ( entries - 1 ); + unsigned int check = 0; + unsigned int avail; + unsigned int ref; + + /* Fail unless we have enough references available */ + avail = ( entries - xen->grant.used - GNTTAB_NR_RESERVED_ENTRIES ); + if ( avail < count ) { + DBGC ( xen, "XENGRANT cannot allocate %d references (only %d " + "of %d available)\n", count, avail, entries ); + return -ENOBUFS; + } + DBGC ( xen, "XENGRANT allocating %d references (from %d of %d " + "available)\n", count, avail, entries ); + + /* Update number of references used */ + xen->grant.used += count; + + /* Find unused references */ + for ( ref = xen->grant.ref ; count ; ref = ( ( ref + 1 ) & mask ) ) { + + /* Sanity check */ + assert ( check++ < entries ); + + /* Skip reserved references */ + if ( ref < GNTTAB_NR_RESERVED_ENTRIES ) + continue; + + /* Skip in-use references */ + hdr = xengrant_header ( xen, ref ); + if ( readw ( &hdr->flags ) & GTF_type_mask ) + continue; + if ( readw ( &hdr->domid ) == DOMID_SELF ) + continue; + + /* Zero reference */ + xengrant_zero ( xen, hdr ); + + /* Mark reference as in-use. We leave the flags as + * empty (to avoid creating a valid grant table entry) + * and set the domid to DOMID_SELF. + */ + writew ( DOMID_SELF, &hdr->domid ); + DBGC2 ( xen, "XENGRANT allocated ref %d\n", ref ); + + /* Record reference */ + refs[--count] = ref; + } + + /* Update cursor */ + xen->grant.ref = ref; + + return 0; +} + +/** + * Free grant references + * + * @v xen Xen hypervisor + * @v refs Grant references + * @v count Number of references + */ +void xengrant_free ( struct xen_hypervisor *xen, grant_ref_t *refs, + unsigned int count ) { + struct grant_entry_header *hdr; + unsigned int ref; + unsigned int i; + + /* Free references */ + for ( i = 0 ; i < count ; i++ ) { + + /* Sanity check */ + ref = refs[i]; + assert ( ref < xengrant_entries ( xen ) ); + + /* Zero reference */ + hdr = xengrant_header ( xen, ref ); + xengrant_zero ( xen, hdr ); + DBGC2 ( xen, "XENGRANT freed ref %d\n", ref ); + } +} diff --git a/roms/ipxe/src/interface/xen/xenstore.c b/roms/ipxe/src/interface/xen/xenstore.c new file mode 100644 index 0000000..b969829 --- /dev/null +++ b/roms/ipxe/src/interface/xen/xenstore.c @@ -0,0 +1,547 @@ +/* + * Copyright (C) 2014 Michael Brown . + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * xs_wire.h attempts to define a static error table xsd_errors, which + * interacts badly with the dynamically generated error numbers used + * by iPXE. Prevent this table from being constructed by including + * errno.h only after including xs_wire.h. + * + */ +#include +#include + +/** @file + * + * XenStore interface + * + */ + +/** Request identifier */ +static uint32_t xenstore_req_id; + +/** + * Send XenStore request raw data + * + * @v xen Xen hypervisor + * @v data Data buffer + * @v len Length of data + */ +static void xenstore_send ( struct xen_hypervisor *xen, const void *data, + size_t len ) { + struct xenstore_domain_interface *intf = xen->store.intf; + XENSTORE_RING_IDX prod = readl ( &intf->req_prod ); + XENSTORE_RING_IDX cons; + XENSTORE_RING_IDX idx; + const char *bytes = data; + size_t offset = 0; + size_t fill; + + DBGCP ( intf, "XENSTORE raw request:\n" ); + DBGCP_HDA ( intf, MASK_XENSTORE_IDX ( prod ), data, len ); + + /* Write one byte at a time */ + while ( offset < len ) { + + /* Wait for space to become available */ + while ( 1 ) { + cons = readl ( &intf->req_cons ); + fill = ( prod - cons ); + if ( fill < XENSTORE_RING_SIZE ) + break; + DBGC2 ( xen, "." ); + cpu_nap(); + rmb(); + } + + /* Write byte */ + idx = MASK_XENSTORE_IDX ( prod++ ); + writeb ( bytes[offset++], &intf->req[idx] ); + } + + /* Update producer counter */ + wmb(); + writel ( prod, &intf->req_prod ); + wmb(); +} + +/** + * Send XenStore request string (excluding terminating NUL) + * + * @v xen Xen hypervisor + * @v string String + */ +static void xenstore_send_string ( struct xen_hypervisor *xen, + const char *string ) { + + xenstore_send ( xen, string, strlen ( string ) ); +} + +/** + * Receive XenStore response raw data + * + * @v xen Xen hypervisor + * @v data Data buffer, or NULL to discard data + * @v len Length of data + */ +static void xenstore_recv ( struct xen_hypervisor *xen, void *data, + size_t len ) { + struct xenstore_domain_interface *intf = xen->store.intf; + XENSTORE_RING_IDX cons = readl ( &intf->rsp_cons ); + XENSTORE_RING_IDX prod; + XENSTORE_RING_IDX idx; + char *bytes = data; + size_t offset = 0; + size_t fill; + + DBGCP ( intf, "XENSTORE raw response:\n" ); + + /* Read one byte at a time */ + while ( offset < len ) { + + /* Wait for data to be ready */ + while ( 1 ) { + prod = readl ( &intf->rsp_prod ); + fill = ( prod - cons ); + if ( fill > 0 ) + break; + DBGC2 ( xen, "." ); + cpu_nap(); + rmb(); + } + + /* Read byte */ + idx = MASK_XENSTORE_IDX ( cons++ ); + if ( data ) + bytes[offset++] = readb ( &intf->rsp[idx] ); + } + if ( data ) + DBGCP_HDA ( intf, MASK_XENSTORE_IDX ( cons - len ), data, len ); + + /* Update consumer counter */ + writel ( cons, &intf->rsp_cons ); + wmb(); +} + +/** + * Send XenStore request + * + * @v xen Xen hypervisor + * @v type Message type + * @v req_id Request ID + * @v value Value, or NULL to omit + * @v key Key path components + * @ret rc Return status code + */ +static int xenstore_request ( struct xen_hypervisor *xen, + enum xsd_sockmsg_type type, uint32_t req_id, + const char *value, va_list key ) { + struct xsd_sockmsg msg; + struct evtchn_send event; + const char *string; + va_list tmp; + int xenrc; + int rc; + + /* Construct message header */ + msg.type = type; + msg.req_id = req_id; + msg.tx_id = 0; + msg.len = 0; + DBGC2 ( xen, "XENSTORE request ID %d type %d ", req_id, type ); + + /* Calculate total length */ + va_copy ( tmp, key ); + while ( ( string = va_arg ( tmp, const char * ) ) != NULL ) { + DBGC2 ( xen, "%s%s", ( msg.len ? "/" : "" ), string ); + msg.len += ( strlen ( string ) + 1 /* '/' or NUL */ ); + } + va_end ( tmp ); + if ( value ) { + DBGC2 ( xen, " = \"%s\"", value ); + msg.len += strlen ( value ); + } + DBGC2 ( xen, "\n" ); + + /* Send message */ + xenstore_send ( xen, &msg, sizeof ( msg ) ); + string = va_arg ( key, const char * ); + assert ( string != NULL ); + xenstore_send_string ( xen, string ); + while ( ( string = va_arg ( key, const char * ) ) != NULL ) { + xenstore_send_string ( xen, "/" ); + xenstore_send_string ( xen, string ); + } + xenstore_send ( xen, "", 1 ); /* Separating NUL */ + if ( value ) + xenstore_send_string ( xen, value ); + + /* Notify the back end */ + event.port = xen->store.port; + if ( ( xenrc = xenevent_send ( xen, &event ) ) != 0 ) { + rc = -EXEN ( xenrc ); + DBGC ( xen, "XENSTORE could not notify back end: %s\n", + strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Receive XenStore response + * + * @v xen Xen hypervisor + * @v req_id Request ID + * @v value Value to fill in + * @v len Length to fill in + * @ret rc Return status code + * + * The caller is responsible for eventually calling free() on the + * returned value. Note that the value may comprise multiple + * NUL-terminated strings concatenated together. A terminating NUL + * will always be appended to the returned value. + */ +static int xenstore_response ( struct xen_hypervisor *xen, uint32_t req_id, + char **value, size_t *len ) { + struct xsd_sockmsg msg; + char *string; + int rc; + + /* Receive message header */ + xenstore_recv ( xen, &msg, sizeof ( msg ) ); + *len = msg.len; + + /* Allocate space for response */ + *value = zalloc ( msg.len + 1 /* terminating NUL */ ); + + /* Receive data. Do this even if allocation failed, or if the + * request ID was incorrect, to avoid leaving data in the + * ring. + */ + xenstore_recv ( xen, *value, msg.len ); + + /* Validate request ID */ + if ( msg.req_id != req_id ) { + DBGC ( xen, "XENSTORE response ID mismatch (got %d, expected " + "%d)\n", msg.req_id, req_id ); + rc = -EPROTO; + goto err_req_id; + } + + /* Check for allocation failure */ + if ( ! *value ) { + DBGC ( xen, "XENSTORE could not allocate %d bytes for " + "response\n", msg.len ); + rc = -ENOMEM; + goto err_alloc; + } + + /* Check for explicit errors */ + if ( msg.type == XS_ERROR ) { + DBGC ( xen, "XENSTORE response error \"%s\"\n", *value ); + rc = -EIO; + goto err_explicit; + } + + DBGC2 ( xen, "XENSTORE response ID %d\n", req_id ); + if ( DBG_EXTRA ) { + for ( string = *value ; string < ( *value + msg.len ) ; + string += ( strlen ( string ) + 1 /* NUL */ ) ) { + DBGC2 ( xen, " - \"%s\"\n", string ); + } + } + return 0; + + err_explicit: + err_alloc: + err_req_id: + free ( *value ); + *value = NULL; + return rc; +} + +/** + * Issue a XenStore message + * + * @v xen Xen hypervisor + * @v type Message type + * @v response Response value to fill in, or NULL to discard + * @v len Response length to fill in, or NULL to ignore + * @v request Request value, or NULL to omit + * @v key Key path components + * @ret rc Return status code + */ +static int xenstore_message ( struct xen_hypervisor *xen, + enum xsd_sockmsg_type type, char **response, + size_t *len, const char *request, va_list key ) { + char *response_value; + size_t response_len; + int rc; + + /* Send request */ + if ( ( rc = xenstore_request ( xen, type, ++xenstore_req_id, + request, key ) ) != 0 ) + return rc; + + /* Receive response */ + if ( ( rc = xenstore_response ( xen, xenstore_req_id, &response_value, + &response_len ) ) != 0 ) + return rc; + + /* Return response, if applicable */ + if ( response ) { + *response = response_value; + } else { + free ( response_value ); + } + if ( len ) + *len = response_len; + + return 0; +} + +/** + * Read XenStore value + * + * @v xen Xen hypervisor + * @v value Value to fill in + * @v key Key path components + * @ret rc Return status code + * + * On a successful return, the caller is responsible for calling + * free() on the returned value. + */ +static int xenstore_vread ( struct xen_hypervisor *xen, char **value, + va_list key ) { + + return xenstore_message ( xen, XS_READ, value, NULL, NULL, key ); +} + +/** + * Read XenStore value + * + * @v xen Xen hypervisor + * @v value Value to fill in + * @v ... Key path components + * @ret rc Return status code + * + * On a successful return, the caller is responsible for calling + * free() on the returned value. + */ +__attribute__ (( sentinel )) int +xenstore_read ( struct xen_hypervisor *xen, char **value, ... ) { + va_list key; + int rc; + + va_start ( key, value ); + rc = xenstore_vread ( xen, value, key ); + va_end ( key ); + return rc; +} + +/** + * Read XenStore numeric value + * + * @v xen Xen hypervisor + * @v num Numeric value to fill in + * @v ... Key path components + * @ret rc Return status code + */ +__attribute__ (( sentinel )) int +xenstore_read_num ( struct xen_hypervisor *xen, unsigned long *num, ... ) { + va_list key; + char *value; + char *endp; + int rc; + + /* Try to read text value */ + va_start ( key, num ); + rc = xenstore_vread ( xen, &value, key ); + va_end ( key ); + if ( rc != 0 ) + goto err_read; + + /* Try to parse as numeric value */ + *num = strtoul ( value, &endp, 10 ); + if ( ( *value == '\0' ) || ( *endp != '\0' ) ) { + DBGC ( xen, "XENSTORE found invalid numeric value \"%s\"\n", + value ); + rc = -EINVAL; + goto err_strtoul; + } + + err_strtoul: + free ( value ); + err_read: + return rc; +} + +/** + * Write XenStore value + * + * @v xen Xen hypervisor + * @v value Value + * @v key Key path components + * @ret rc Return status code + */ +static int xenstore_vwrite ( struct xen_hypervisor *xen, const char *value, + va_list key ) { + + return xenstore_message ( xen, XS_WRITE, NULL, NULL, value, key ); +} + +/** + * Write XenStore value + * + * @v xen Xen hypervisor + * @v value Value + * @v ... Key path components + * @ret rc Return status code + */ +__attribute__ (( sentinel )) int +xenstore_write ( struct xen_hypervisor *xen, const char *value, ... ) { + va_list key; + int rc; + + va_start ( key, value ); + rc = xenstore_vwrite ( xen, value, key ); + va_end ( key ); + return rc; +} + +/** + * Write XenStore numeric value + * + * @v xen Xen hypervisor + * @v num Numeric value + * @v ... Key path components + * @ret rc Return status code + */ +__attribute__ (( sentinel )) int +xenstore_write_num ( struct xen_hypervisor *xen, unsigned long num, ... ) { + char value[ 21 /* "18446744073709551615" + NUL */ ]; + va_list key; + int rc; + + /* Construct value */ + snprintf ( value, sizeof ( value ), "%ld", num ); + + /* Write value */ + va_start ( key, num ); + rc = xenstore_vwrite ( xen, value, key ); + va_end ( key ); + return rc; +} + +/** + * Delete XenStore value + * + * @v xen Xen hypervisor + * @v ... Key path components + * @ret rc Return status code + */ +__attribute__ (( sentinel )) int +xenstore_rm ( struct xen_hypervisor *xen, ... ) { + va_list key; + int rc; + + va_start ( key, xen ); + rc = xenstore_message ( xen, XS_RM, NULL, NULL, NULL, key ); + va_end ( key ); + return rc; +} + +/** + * Read XenStore directory + * + * @v xen Xen hypervisor + * @v children Child key names to fill in + * @v len Length of child key names to fill in + * @v ... Key path components + * @ret rc Return status code + */ +__attribute__ (( sentinel )) int +xenstore_directory ( struct xen_hypervisor *xen, char **children, size_t *len, + ... ) { + va_list key; + int rc; + + va_start ( key, len ); + rc = xenstore_message ( xen, XS_DIRECTORY, children, len, NULL, key ); + va_end ( key ); + return rc; +} + +/** + * Dump XenStore directory contents (for debugging) + * + * @v xen Xen hypervisor + * @v key Key + */ +void xenstore_dump ( struct xen_hypervisor *xen, const char *key ) { + char *value; + char *children; + char *child; + char *child_key; + size_t len; + int rc; + + /* Try to dump current key as a value */ + if ( ( rc = xenstore_read ( xen, &value, key, NULL ) ) == 0 ) { + DBGC ( xen, "%s = \"%s\"\n", key, value ); + free ( value ); + } + + /* Try to recurse into each child in turn */ + if ( ( rc = xenstore_directory ( xen, &children, &len, key, + NULL ) ) == 0 ) { + for ( child = children ; child < ( children + len ) ; + child += ( strlen ( child ) + 1 /* NUL */ ) ) { + + /* Construct child key */ + asprintf ( &child_key, "%s/%s", key, child ); + if ( ! child_key ) { + DBGC ( xen, "XENSTORE could not allocate child " + "key \"%s/%s\"\n", key, child ); + rc = -ENOMEM; + break; + } + + /* Recurse into child key, continuing on error */ + xenstore_dump ( xen, child_key ); + free ( child_key ); + } + free ( children ); + } +} diff --git a/roms/ipxe/src/net/eth_slow.c b/roms/ipxe/src/net/eth_slow.c index 69c38f3..db54b55 100644 --- a/roms/ipxe/src/net/eth_slow.c +++ b/roms/ipxe/src/net/eth_slow.c @@ -167,7 +167,8 @@ static int eth_slow_lacp_rx ( struct io_buffer *iobuf, lacp->actor.key = htons ( 1 ); lacp->actor.port_priority = htons ( LACP_PORT_PRIORITY_MAX ); lacp->actor.port = htons ( 1 ); - lacp->actor.state = ( LACP_STATE_IN_SYNC | + lacp->actor.state = ( LACP_STATE_AGGREGATABLE | + LACP_STATE_IN_SYNC | LACP_STATE_COLLECTING | LACP_STATE_DISTRIBUTING | ( lacp->partner.state & LACP_STATE_FAST ) ); diff --git a/roms/ipxe/src/net/ethernet.c b/roms/ipxe/src/net/ethernet.c index a2e5658..03978c2 100644 --- a/roms/ipxe/src/net/ethernet.c +++ b/roms/ipxe/src/net/ethernet.c @@ -20,6 +20,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include +#include #include #include #include @@ -113,6 +114,21 @@ void eth_init_addr ( const void *hw_addr, void *ll_addr ) { } /** + * Generate random Ethernet address + * + * @v hw_addr Generated hardware address + */ +void eth_random_addr ( void *hw_addr ) { + uint8_t *addr = hw_addr; + unsigned int i; + + for ( i = 0 ; i < ETH_ALEN ; i++ ) + addr[i] = random(); + addr[0] &= ~0x01; /* Clear multicast bit */ + addr[0] |= 0x02; /* Set locally-assigned bit */ +} + +/** * Transcribe Ethernet address * * @v ll_addr Link-layer address diff --git a/roms/ipxe/src/net/fcp.c b/roms/ipxe/src/net/fcp.c index 241b546..9c36a4c 100644 --- a/roms/ipxe/src/net/fcp.c +++ b/roms/ipxe/src/net/fcp.c @@ -551,7 +551,6 @@ static int fcpcmd_recv_rsp ( struct fcp_command *fcpcmd, struct fcp_device *fcpdev = fcpcmd->fcpdev; struct scsi_cmd *command = &fcpcmd->command; struct fcp_rsp *rsp = iobuf->data; - struct scsi_sense *sense; struct scsi_rsp response; int rc; @@ -607,8 +606,8 @@ static int fcpcmd_recv_rsp ( struct fcp_command *fcpcmd, if ( rsp->flags & FCP_RSP_RESIDUAL_UNDERRUN ) response.overrun = -response.overrun; } - if ( ( sense = fcp_rsp_sense_data ( rsp ) ) != NULL ) - memcpy ( &response.sense, sense, sizeof ( response.sense ) ); + scsi_parse_sense ( fcp_rsp_sense_data ( rsp ), + fcp_rsp_sense_data_len ( rsp ), &response.sense ); /* Free buffer before sending response, to minimise * out-of-memory errors. diff --git a/roms/ipxe/src/net/ipv6.c b/roms/ipxe/src/net/ipv6.c index f753751..3c37416 100644 --- a/roms/ipxe/src/net/ipv6.c +++ b/roms/ipxe/src/net/ipv6.c @@ -515,7 +515,8 @@ static int ipv6_tx ( struct io_buffer *iobuf, } if ( sin6_src && ! IN6_IS_ADDR_UNSPECIFIED ( &sin6_src->sin6_addr ) ) src = &sin6_src->sin6_addr; - memcpy ( &iphdr->src, src, sizeof ( iphdr->src ) ); + if ( src ) + memcpy ( &iphdr->src, src, sizeof ( iphdr->src ) ); /* Fix up checksums */ if ( trans_csum ) { @@ -900,7 +901,7 @@ static const char * ipv6_sock_ntoa ( struct sockaddr *sa ) { const char *netdev_name; /* Identify network device, if applicable */ - if ( IN6_IS_ADDR_LINKLOCAL ( in ) ) { + if ( IN6_IS_ADDR_LINKLOCAL ( in ) || IN6_IS_ADDR_MULTICAST ( in ) ) { netdev = find_netdev_by_index ( sin6->sin6_scope_id ); netdev_name = ( netdev ? netdev->name : "UNKNOWN" ); } else { diff --git a/roms/ipxe/src/net/ndp.c b/roms/ipxe/src/net/ndp.c index 6450aa9..e62f7d5 100644 --- a/roms/ipxe/src/net/ndp.c +++ b/roms/ipxe/src/net/ndp.c @@ -120,7 +120,6 @@ static int ndp_tx_request ( struct net_device *netdev, sin6_src.sin6_family = AF_INET6; memcpy ( &sin6_src.sin6_addr, net_source, sizeof ( sin6_src.sin6_addr ) ); - sin6_src.sin6_scope_id = netdev->index; /* Construct multicast destination address */ memset ( &sin6_dest, 0, sizeof ( sin6_dest ) ); diff --git a/roms/ipxe/src/net/netdevice.c b/roms/ipxe/src/net/netdevice.c index a05d661..a55e6b7 100644 --- a/roms/ipxe/src/net/netdevice.c +++ b/roms/ipxe/src/net/netdevice.c @@ -50,6 +50,9 @@ struct list_head net_devices = LIST_HEAD_INIT ( net_devices ); /** List of open network devices, in reverse order of opening */ static struct list_head open_net_devices = LIST_HEAD_INIT ( open_net_devices ); +/** Network device index */ +static unsigned int netdev_index = 0; + /** Network polling profiler */ static struct profiler net_poll_profiler __profiler = { .name = "net.poll" }; @@ -597,24 +600,36 @@ struct net_device * alloc_netdev ( size_t priv_len ) { * devices. */ int register_netdev ( struct net_device *netdev ) { - static unsigned int ifindex = 0; struct ll_protocol *ll_protocol = netdev->ll_protocol; struct net_driver *driver; + struct net_device *duplicate; uint32_t seed; int rc; + /* Set initial link-layer address, if not already set */ + if ( ! netdev_has_ll_addr ( netdev ) ) { + ll_protocol->init_addr ( netdev->hw_addr, netdev->ll_addr ); + } + + /* Reject network devices that are already available via a + * different hardware device. + */ + duplicate = find_netdev_by_ll_addr ( ll_protocol, netdev->ll_addr ); + if ( duplicate && ( duplicate->dev != netdev->dev ) ) { + DBGC ( netdev, "NETDEV rejecting duplicate (phys %s) of %s " + "(phys %s)\n", netdev->dev->name, duplicate->name, + duplicate->dev->name ); + rc = -EEXIST; + goto err_duplicate; + } + /* Record device index and create device name */ - netdev->index = ifindex++; + netdev->index = netdev_index++; if ( netdev->name[0] == '\0' ) { snprintf ( netdev->name, sizeof ( netdev->name ), "net%d", netdev->index ); } - /* Set initial link-layer address, if not already set */ - if ( ! netdev_has_ll_addr ( netdev ) ) { - ll_protocol->init_addr ( netdev->hw_addr, netdev->ll_addr ); - } - /* Use least significant bits of the link-layer address to * improve the randomness of the (non-cryptographic) random * number generator. @@ -658,6 +673,7 @@ int register_netdev ( struct net_device *netdev ) { clear_settings ( netdev_settings ( netdev ) ); unregister_settings ( netdev_settings ( netdev ) ); err_register_settings: + err_duplicate: return rc; } @@ -764,6 +780,10 @@ void unregister_netdev ( struct net_device *netdev ) { DBGC ( netdev, "NETDEV %s unregistered\n", netdev->name ); list_del ( &netdev->list ); netdev_put ( netdev ); + + /* Reset network device index if no devices remain */ + if ( list_empty ( &net_devices ) ) + netdev_index = 0; } /** Enable or disable interrupts @@ -847,6 +867,27 @@ struct net_device * find_netdev_by_location ( unsigned int bus_type, } /** + * Get network device by link-layer address + * + * @v ll_protocol Link-layer protocol + * @v ll_addr Link-layer address + * @ret netdev Network device, or NULL + */ +struct net_device * find_netdev_by_ll_addr ( struct ll_protocol *ll_protocol, + const void *ll_addr ) { + struct net_device *netdev; + + list_for_each_entry ( netdev, &net_devices, list ) { + if ( ( netdev->ll_protocol == ll_protocol ) && + ( memcmp ( netdev->ll_addr, ll_addr, + ll_protocol->ll_addr_len ) == 0 ) ) + return netdev; + } + + return NULL; +} + +/** * Get most recently opened network device * * @ret netdev Most recently opened network device, or NULL @@ -1039,7 +1080,7 @@ static unsigned int net_discard ( void ) { /* Discard first deferred packet */ list_del ( &iobuf->list ); - free ( iobuf ); + free_iob ( iobuf ); /* Report discard */ discarded++; diff --git a/roms/ipxe/src/net/oncrpc/nfs_open.c b/roms/ipxe/src/net/oncrpc/nfs_open.c index 349957f..c0dceb8 100644 --- a/roms/ipxe/src/net/oncrpc/nfs_open.c +++ b/roms/ipxe/src/net/oncrpc/nfs_open.c @@ -40,6 +40,7 @@ #include #include #include +#include /** @file * @@ -101,10 +102,7 @@ struct nfs_request { struct oncrpc_cred_sys auth_sys; char * hostname; - char * path; - char * mountpoint; - char * filename; - size_t filename_offset; + struct nfs_uri uri; struct nfs_fh readlink_fh; struct nfs_fh current_fh; @@ -127,8 +125,9 @@ static void nfs_free ( struct refcnt *refcnt ) { nfs = container_of ( refcnt, struct nfs_request, refcnt ); DBGC ( nfs, "NFS_OPEN %p freed\n", nfs ); + nfs_uri_free ( &nfs->uri ); + free ( nfs->hostname ); - free ( nfs->path ); free ( nfs->auth_sys.hostname ); free ( nfs ); } @@ -274,10 +273,10 @@ static void nfs_mount_step ( struct nfs_request *nfs ) { if ( nfs->mount_state == NFS_MOUNT_NONE ) { DBGC ( nfs, "NFS_OPEN %p MNT call (%s)\n", nfs, - nfs->mountpoint ); + nfs_uri_mountpoint ( &nfs->uri ) ); rc = mount_mnt ( &nfs->mount_intf, &nfs->mount_session, - nfs->mountpoint ); + nfs_uri_mountpoint ( &nfs->uri ) ); if ( rc != 0 ) goto err; @@ -289,7 +288,7 @@ static void nfs_mount_step ( struct nfs_request *nfs ) { DBGC ( nfs, "NFS_OPEN %p UMNT call\n", nfs ); rc = mount_umnt ( &nfs->mount_intf, &nfs->mount_session, - nfs->mountpoint ); + nfs_uri_mountpoint ( &nfs->uri ) ); if ( rc != 0 ) goto err; } @@ -303,7 +302,6 @@ static int nfs_mount_deliver ( struct nfs_request *nfs, struct io_buffer *io_buf, struct xfer_metadata *meta __unused ) { int rc; - char *sep; struct oncrpc_reply reply; struct mount_mnt_reply mnt_reply; @@ -318,19 +316,29 @@ static int nfs_mount_deliver ( struct nfs_request *nfs, DBGC ( nfs, "NFS_OPEN %p got MNT reply\n", nfs ); rc = mount_get_mnt_reply ( &mnt_reply, &reply ); if ( rc != 0 ) { - if ( mnt_reply.status != MNT3ERR_NOTDIR ) + switch ( mnt_reply.status ) { + case MNT3ERR_NOTDIR: + case MNT3ERR_NOENT: + case MNT3ERR_ACCES: + break; + + default: + goto err; + } + + if ( ! strcmp ( nfs_uri_mountpoint ( &nfs->uri ), + "/" ) ) goto err; - if ( strcmp ( nfs->mountpoint, "/" ) == 0 ) + if ( ( rc = nfs_uri_next_mountpoint ( &nfs->uri ) ) ) goto err; - sep = strrchr ( nfs->mountpoint, '/' ); - nfs->filename[-1] = '/'; - nfs->filename = sep + 1; - *sep = '\0'; + DBGC ( nfs, "NFS_OPEN %p MNT failed retrying with " \ + "%s\n", nfs, nfs_uri_mountpoint ( &nfs->uri ) ); + + nfs->mount_state--; + nfs_mount_step ( nfs ); - DBGC ( nfs, "NFS_OPEN %p ENOTDIR received retrying" \ - "with %s\n", nfs, nfs->mountpoint ); goto done; } @@ -358,22 +366,13 @@ done: static void nfs_step ( struct nfs_request *nfs ) { int rc; - char *path_component = NULL; + char *path_component; if ( ! xfer_window ( &nfs->nfs_intf ) ) return; if ( nfs->nfs_state == NFS_LOOKUP ) { - while ( path_component == NULL || path_component[0] == '\0') { - path_component = nfs->filename; - while ( *nfs->filename != '\0' ) { - nfs->filename_offset++; - if ( *nfs->filename++ == '/' ) { - *(nfs->filename - 1) = '\0'; - break; - } - } - } + path_component = nfs_uri_next_path_component ( &nfs->uri ); DBGC ( nfs, "NFS_OPEN %p LOOKUP call (%s)\n", nfs, path_component ); @@ -443,11 +442,11 @@ static int nfs_deliver ( struct nfs_request *nfs, if ( lookup_reply.ent_type == NFS_ATTR_SYMLINK ) { nfs->readlink_fh = lookup_reply.fh; - nfs->nfs_state = NFS_READLINK; + nfs->nfs_state = NFS_READLINK; } else { nfs->current_fh = lookup_reply.fh; - if ( nfs->filename[0] == '\0' ) + if ( nfs->uri.lookup_pos[0] == '\0' ) nfs->nfs_state = NFS_READ; else nfs->nfs_state--; @@ -458,7 +457,7 @@ static int nfs_deliver ( struct nfs_request *nfs, } if ( nfs->nfs_state == NFS_READLINK_SENT ) { - char *new_filename; + char *path; struct nfs_readlink_reply readlink_reply; DBGC ( nfs, "NFS_OPEN %p got READLINK reply\n", nfs ); @@ -468,46 +467,23 @@ static int nfs_deliver ( struct nfs_request *nfs, goto err; if ( readlink_reply.path_len == 0 ) - return -EINVAL; - - if ( readlink_reply.path[0] == '/' ) { - if ( strncmp ( readlink_reply.path, nfs->mountpoint, - strlen ( nfs->mountpoint ) ) != 0 ) - return -EINVAL; - - /* The mountpoint part of the path is ended by a '/' */ - if ( strlen ( nfs->mountpoint ) != - readlink_reply.path_len ) { - readlink_reply.path += 1; - readlink_reply.path_len -= 1; - } - - /* We are considering the last part of the absolute - * path as a relative path from the mountpoint. - */ - readlink_reply.path += strlen ( nfs->mountpoint ); - readlink_reply.path_len -= strlen ( nfs->mountpoint ); + { + rc = -EINVAL; + goto err; } - new_filename = malloc ( readlink_reply.path_len + - strlen ( nfs->filename ) + 2 ); - if ( ! new_filename ) { + if ( ! ( path = strndup ( readlink_reply.path, + readlink_reply.path_len ) ) ) + { rc = -ENOMEM; goto err; } - memcpy ( new_filename, readlink_reply.path, - readlink_reply.path_len ); - strcpy ( new_filename + readlink_reply.path_len + 1, - nfs->filename ); - new_filename[readlink_reply.path_len] = '/'; - - free ( nfs->filename - nfs->filename_offset ); - nfs->filename = new_filename; - nfs->filename_offset = 0; + nfs_uri_symlink ( &nfs->uri, path ); + free ( path ); - DBGC ( nfs, "NFS_OPEN %p new filename: %s\n", nfs, - nfs->filename ); + DBGC ( nfs, "NFS_OPEN %p new path: %s\n", nfs, + nfs->uri.path ); nfs->nfs_state = NFS_LOOKUP; nfs_step ( nfs ); @@ -626,30 +602,21 @@ static int nfs_parse_uri ( struct nfs_request *nfs, const struct uri *uri ) { if ( ! uri || ! uri->host || ! uri->path ) return -EINVAL; - if ( ! ( nfs->path = strdup ( uri->path ) ) ) - return -ENOMEM; + if ( ( rc = nfs_uri_init ( &nfs->uri, uri ) ) != 0 ) + return rc; if ( ! ( nfs->hostname = strdup ( uri->host ) ) ) { rc = -ENOMEM; goto err_hostname; } - nfs->filename = basename ( nfs->path ); - nfs->mountpoint = dirname ( nfs->path ); - - if ( nfs->filename[0] == '\0' ) - goto err_filename; - - DBGC ( nfs, "NFS_OPEN %p URI parsed: (mountpoint=%s, filename=%s)\n", - nfs, nfs->mountpoint, nfs->filename ); + DBGC ( nfs, "NFS_OPEN %p URI parsed: (mountpoint=%s, path=%s)\n", + nfs, nfs_uri_mountpoint ( &nfs->uri), nfs->uri.path ); return 0; -err_filename: - rc = -EINVAL; - free ( nfs->hostname ); err_hostname: - free ( nfs->path ); + nfs_uri_free ( &nfs->uri ); return rc; } @@ -686,6 +653,9 @@ static int nfs_open ( struct interface *xfer, struct uri *uri ) { mount_init_session ( &nfs->mount_session, &nfs->auth_sys.credential ); nfs_init_session ( &nfs->nfs_session, &nfs->auth_sys.credential ); + DBGC ( nfs, "NFS_OPEN %p connecting to port mapper (%s:%d)...\n", nfs, + nfs->hostname, PORTMAP_PORT ); + rc = nfs_connect ( &nfs->pm_intf, PORTMAP_PORT, nfs->hostname ); if ( rc != 0 ) goto err_connect; @@ -699,6 +669,8 @@ static int nfs_open ( struct interface *xfer, struct uri *uri ) { err_connect: free ( nfs->auth_sys.hostname ); err_cred: + nfs_uri_free ( &nfs->uri ); + free ( nfs->hostname ); err_uri: free ( nfs ); return rc; diff --git a/roms/ipxe/src/net/oncrpc/nfs_uri.c b/roms/ipxe/src/net/oncrpc/nfs_uri.c new file mode 100644 index 0000000..c4c3f21 --- /dev/null +++ b/roms/ipxe/src/net/oncrpc/nfs_uri.c @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2014 Marin Hannache . + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include +#include +#include +#include +#include + +/** @file + * + * Network File System protocol URI handling functions + * + */ + +int nfs_uri_init ( struct nfs_uri *nfs_uri, const struct uri *uri ) { + if ( ! ( nfs_uri->mountpoint = strdup ( uri->path ) ) ) + return -ENOMEM; + + nfs_uri->filename = basename ( nfs_uri->mountpoint ); + if ( strchr ( uri->path, '/' ) != NULL ) + nfs_uri->mountpoint = dirname ( nfs_uri->mountpoint ); + + if ( nfs_uri->filename[0] == '\0' ) { + free ( nfs_uri->mountpoint ); + return -EINVAL; + } + + if ( ! ( nfs_uri->path = strdup ( nfs_uri->filename ) ) ) { + free ( nfs_uri->mountpoint ); + return -ENOMEM; + } + nfs_uri->lookup_pos = nfs_uri->path; + + return 0; +} + +char *nfs_uri_mountpoint ( const struct nfs_uri *uri ) { + if ( uri->mountpoint + 1 == uri->filename || + uri->mountpoint == uri->filename ) + return "/"; + + return uri->mountpoint; +} + +int nfs_uri_next_mountpoint ( struct nfs_uri *uri ) { + char *sep; + + if ( uri->mountpoint + 1 == uri->filename || + uri->mountpoint == uri->filename ) + return -ENOENT; + + sep = strrchr ( uri->mountpoint, '/' ); + uri->filename[-1] = '/'; + uri->filename = sep + 1; + *sep = '\0'; + + free ( uri->path ); + if ( ! ( uri->path = strdup ( uri->filename ) ) ) { + uri->path = NULL; + return -ENOMEM; + } + uri->lookup_pos = uri->path; + + return 0; +} + +int nfs_uri_symlink ( struct nfs_uri *uri, const char *symlink ) { + size_t len; + char *new_path; + + if ( ! uri->path ) + return -EINVAL; + + if ( *symlink == '/' ) + { + if ( strncmp ( symlink, uri->mountpoint, + strlen ( uri->mountpoint ) ) != 0 ) + return -EINVAL; + + len = strlen ( uri->lookup_pos ) + strlen ( symlink ) - \ + strlen ( uri->mountpoint ); + if ( ! ( new_path = malloc ( len * sizeof ( char ) ) ) ) + return -ENOMEM; + + strcpy ( new_path, symlink + strlen ( uri->mountpoint ) ); + strcpy ( new_path + strlen ( new_path ), uri->lookup_pos ); + + } else { + len = strlen ( uri->lookup_pos ) + strlen ( symlink ); + if ( ! ( new_path = malloc ( len * sizeof ( char ) ) ) ) + return -ENOMEM; + + + strcpy ( new_path, symlink ); + strcpy ( new_path + strlen ( new_path ), uri->lookup_pos ); + } + + free ( uri->path ); + uri->lookup_pos = uri->path = new_path; + + return 0; +} + +char *nfs_uri_next_path_component ( struct nfs_uri *uri ) { + char *sep; + char *start; + + if ( ! uri->path ) + return NULL; + + for ( sep = uri->lookup_pos ; *sep != '\0' && *sep != '/'; sep++ ) + ; + + start = uri->lookup_pos; + uri->lookup_pos = sep; + if ( *sep != '\0' ) { + uri->lookup_pos++; + *sep = '\0'; + if ( *start == '\0' ) + return nfs_uri_next_path_component ( uri ); + } + + return start; +} + +void nfs_uri_free ( struct nfs_uri *uri ) { + free ( uri->mountpoint ); + free ( uri->path ); + uri->mountpoint = NULL; + uri->path = NULL; +} diff --git a/roms/ipxe/src/net/tcp.c b/roms/ipxe/src/net/tcp.c index 1c48769..987cb63 100644 --- a/roms/ipxe/src/net/tcp.c +++ b/roms/ipxe/src/net/tcp.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -107,6 +108,8 @@ struct tcp_connection { struct list_head tx_queue; /** Receive queue */ struct list_head rx_queue; + /** Transmission process */ + struct process process; /** Retransmission timer */ struct retry_timer timer; /** Shutdown (TIME_WAIT) timer */ @@ -166,6 +169,7 @@ static struct profiler tcp_rx_profiler __profiler = { .name = "tcp.rx" }; static struct profiler tcp_xfer_profiler __profiler = { .name = "tcp.xfer" }; /* Forward declarations */ +static struct process_descriptor tcp_process_desc; static struct interface_descriptor tcp_xfer_desc; static void tcp_expired ( struct retry_timer *timer, int over ); static void tcp_wait_expired ( struct retry_timer *timer, int over ); @@ -273,6 +277,7 @@ static int tcp_open ( struct interface *xfer, struct sockaddr *peer, DBGC ( tcp, "TCP %p allocated\n", tcp ); ref_init ( &tcp->refcnt, NULL ); intf_init ( &tcp->xfer, &tcp_xfer_desc, &tcp->refcnt ); + process_init_stopped ( &tcp->process, &tcp_process_desc, &tcp->refcnt ); timer_init ( &tcp->timer, tcp_expired, &tcp->refcnt ); timer_init ( &tcp->wait, tcp_wait_expired, &tcp->refcnt ); tcp->prev_tcp_state = TCP_CLOSED; @@ -369,6 +374,7 @@ static void tcp_close ( struct tcp_connection *tcp, int rc ) { pending_put ( &tcp->pending_flags ); /* Remove from list and drop reference */ + process_del ( &tcp->process ); stop_timer ( &tcp->timer ); stop_timer ( &tcp->wait ); list_del ( &tcp->list ); @@ -497,7 +503,7 @@ static size_t tcp_process_tx_queue ( struct tcp_connection *tcp, size_t max_len, * will have been started if necessary, and so the stack will * eventually attempt to retransmit the failed packet. */ -static int tcp_xmit ( struct tcp_connection *tcp ) { +static void tcp_xmit ( struct tcp_connection *tcp ) { struct io_buffer *iobuf; struct tcp_header *tcphdr; struct tcp_mss_option *mssopt; @@ -517,7 +523,7 @@ static int tcp_xmit ( struct tcp_connection *tcp ) { /* If retransmission timer is already running, do nothing */ if ( timer_running ( &tcp->timer ) ) - return 0; + return; /* Calculate both the actual (payload) and sequence space * lengths that we wish to transmit. @@ -537,7 +543,7 @@ static int tcp_xmit ( struct tcp_connection *tcp ) { /* If we have nothing to transmit, stop now */ if ( ( seq_len == 0 ) && ! ( tcp->flags & TCP_ACK_PENDING ) ) - return 0; + return; /* If we are transmitting anything that requires * acknowledgement (i.e. consumes sequence space), start the @@ -553,7 +559,7 @@ static int tcp_xmit ( struct tcp_connection *tcp ) { DBGC ( tcp, "TCP %p could not allocate iobuf for %08x..%08x " "%08x\n", tcp, tcp->snd_seq, ( tcp->snd_seq + seq_len ), tcp->rcv_ack ); - return -ENOMEM; + return; } iob_reserve ( iobuf, TCP_MAX_HEADER_LEN ); @@ -620,16 +626,19 @@ static int tcp_xmit ( struct tcp_connection *tcp ) { DBGC ( tcp, "TCP %p could not transmit %08x..%08x %08x: %s\n", tcp, tcp->snd_seq, ( tcp->snd_seq + tcp->snd_sent ), tcp->rcv_ack, strerror ( rc ) ); - return rc; + return; } /* Clear ACK-pending flag */ tcp->flags &= ~TCP_ACK_PENDING; profile_stop ( &tcp_tx_profiler ); - return 0; } +/** TCP process descriptor */ +static struct process_descriptor tcp_process_desc = + PROC_DESC_ONCE ( struct tcp_connection, process, tcp_xmit ); + /** * Retransmission timer expired * @@ -1272,8 +1281,16 @@ static int tcp_rx ( struct io_buffer *iobuf, /* Dump out any state change as a result of the received packet */ tcp_dump_state ( tcp ); - /* Send out any pending data */ - tcp_xmit ( tcp ); + /* Schedule transmission of ACK (and any pending data). If we + * have received any out-of-order packets (i.e. if the receive + * queue remains non-empty after processing) then send the ACK + * immediately in order to trigger Fast Retransmission. + */ + if ( list_empty ( &tcp->rx_queue ) ) { + process_add ( &tcp->process ); + } else { + tcp_xmit ( tcp ); + } /* If this packet was the last we expect to receive, set up * timer to expire and cause the connection to be freed. diff --git a/roms/ipxe/src/net/tcp/iscsi.c b/roms/ipxe/src/net/tcp/iscsi.c index a6fcd25..03c6d0f 100644 --- a/roms/ipxe/src/net/tcp/iscsi.c +++ b/roms/ipxe/src/net/tcp/iscsi.c @@ -412,11 +412,12 @@ static int iscsi_rx_scsi_response ( struct iscsi_session *iscsi, = &iscsi->rx_bhs.scsi_response; struct scsi_rsp rsp; uint32_t residual_count; + size_t data_len; int rc; /* Buffer up the PDU data */ if ( ( rc = iscsi_rx_buffered_data ( iscsi, data, len ) ) != 0 ) { - DBGC ( iscsi, "iSCSI %p could not buffer login response: %s\n", + DBGC ( iscsi, "iSCSI %p could not buffer SCSI response: %s\n", iscsi, strerror ( rc ) ); return rc; } @@ -432,9 +433,11 @@ static int iscsi_rx_scsi_response ( struct iscsi_session *iscsi, } else if ( response->flags & ISCSI_DATA_FLAG_UNDERFLOW ) { rsp.overrun = -(residual_count); } - if ( ISCSI_DATA_LEN ( response->lengths ) ) - memcpy ( &rsp.sense, ( iscsi->rx_buffer + 2 ), - sizeof ( rsp.sense ) ); + data_len = ISCSI_DATA_LEN ( response->lengths ); + if ( data_len ) { + scsi_parse_sense ( ( iscsi->rx_buffer + 2 ), ( data_len - 2 ), + &rsp.sense ); + } iscsi_rx_buffered_data_done ( iscsi ); /* Check for errors */ diff --git a/roms/ipxe/src/net/tcp/oncrpc.c b/roms/ipxe/src/net/tcp/oncrpc.c index cd33ece..6469867 100644 --- a/roms/ipxe/src/net/tcp/oncrpc.c +++ b/roms/ipxe/src/net/tcp/oncrpc.c @@ -37,7 +37,7 @@ #include #include #include -#include +#include /** @file * @@ -88,7 +88,7 @@ int oncrpc_init_cred_sys ( struct oncrpc_cred_sys *auth_sys ) { fetch_string_setting_copy ( NULL, &hostname_setting, &auth_sys->hostname ); if ( ! auth_sys->hostname ) - if ( ! ( auth_sys->hostname = strdup ( PRODUCT_SHORT_NAME ) ) ) + if ( ! ( auth_sys->hostname = strdup ( product_short_name ) ) ) return -ENOMEM; auth_sys->uid = fetch_uintz_setting ( NULL, &uid_setting ); diff --git a/roms/ipxe/src/net/udp/dhcp.c b/roms/ipxe/src/net/udp/dhcp.c index e6d3edd..04fad04 100644 --- a/roms/ipxe/src/net/udp/dhcp.c +++ b/roms/ipxe/src/net/udp/dhcp.c @@ -842,53 +842,6 @@ static struct dhcp_session_state dhcp_state_pxebs = { */ /** - * Construct DHCP client hardware address field and broadcast flag - * - * @v netdev Network device - * @v chaddr Hardware address buffer - * @v flags Flags to set (or NULL) - * @ret hlen Hardware address length - */ -unsigned int dhcp_chaddr ( struct net_device *netdev, void *chaddr, - uint16_t *flags ) { - struct ll_protocol *ll_protocol = netdev->ll_protocol; - struct dhcphdr *dhcphdr; - int rc; - - /* If the link-layer address cannot fit into the chaddr field - * (as is the case for IPoIB) then try using the Ethernet- - * compatible link-layer address. If we do this, set the - * broadcast flag, since chaddr then does not represent a - * valid link-layer address for the return path. - * - * If we cannot produce an Ethernet-compatible link-layer - * address, try using the hardware address. - * - * If even the hardware address is too large, use an empty - * chaddr field and set the broadcast flag. - * - * This goes against RFC4390, but RFC4390 mandates that we use - * a DHCP client identifier that conforms with RFC4361, which - * we cannot do without either persistent (NIC-independent) - * storage, or by eliminating the hardware address completely - * from the DHCP packet, which seems unfriendly to users. - */ - if ( ll_protocol->ll_addr_len <= sizeof ( dhcphdr->chaddr ) ) { - memcpy ( chaddr, netdev->ll_addr, ll_protocol->ll_addr_len ); - return ll_protocol->ll_addr_len; - } - if ( flags ) - *flags |= htons ( BOOTP_FL_BROADCAST ); - if ( ( rc = ll_protocol->eth_addr ( netdev->ll_addr, chaddr ) ) == 0 ) - return ETH_ALEN; - if ( ll_protocol->hw_addr_len <= sizeof ( dhcphdr->chaddr ) ) { - memcpy ( chaddr, netdev->hw_addr, ll_protocol->hw_addr_len ); - return ll_protocol->hw_addr_len; - } - return 0; -} - -/** * Create a DHCP packet * * @v dhcppkt DHCP packet structure to fill in @@ -1153,6 +1106,8 @@ static int dhcp_tx ( struct dhcp_session *dhcp ) { static int dhcp_deliver ( struct dhcp_session *dhcp, struct io_buffer *iobuf, struct xfer_metadata *meta ) { + struct net_device *netdev = dhcp->netdev; + struct ll_protocol *ll_protocol = netdev->ll_protocol; struct sockaddr_in *peer; size_t data_len; struct dhcp_packet *dhcppkt; @@ -1203,9 +1158,21 @@ static int dhcp_deliver ( struct dhcp_session *dhcp, goto err_xid; }; + /* Check for matching client hardware address */ + if ( memcmp ( dhcphdr->chaddr, netdev->ll_addr, + ll_protocol->ll_addr_len ) != 0 ) { + DBGC ( dhcp, "DHCP %p %s from %s:%d has bad chaddr %s\n", + dhcp, dhcp_msgtype_name ( msgtype ), + inet_ntoa ( peer->sin_addr ), ntohs ( peer->sin_port ), + ll_protocol->ntoa ( dhcphdr->chaddr ) ); + rc = -EINVAL; + goto err_chaddr; + } + /* Handle packet based on current state */ dhcp->state->rx ( dhcp, dhcppkt, peer, msgtype, server_id ); + err_chaddr: err_xid: dhcppkt_put ( dhcppkt ); err_alloc_dhcppkt: diff --git a/roms/ipxe/src/net/udp/dhcpv6.c b/roms/ipxe/src/net/udp/dhcpv6.c index cbc8d79..f7736d0 100644 --- a/roms/ipxe/src/net/udp/dhcpv6.c +++ b/roms/ipxe/src/net/udp/dhcpv6.c @@ -924,7 +924,6 @@ int start_dhcpv6 ( struct interface *job, struct net_device *netdev, /* Construct client and server addresses */ memset ( &addresses, 0, sizeof ( addresses ) ); addresses.client.sin6.sin6_family = AF_INET6; - addresses.client.sin6.sin6_scope_id = netdev->index; addresses.client.sin6.sin6_port = htons ( DHCPV6_CLIENT_PORT ); addresses.server.sin6.sin6_family = AF_INET6; ipv6_all_dhcp_relay_and_servers ( &addresses.server.sin6.sin6_addr ); diff --git a/roms/ipxe/src/net/udp/syslog.c b/roms/ipxe/src/net/udp/syslog.c index d517371..d65d19a 100644 --- a/roms/ipxe/src/net/udp/syslog.c +++ b/roms/ipxe/src/net/udp/syslog.c @@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include +#include #include #include #include @@ -91,15 +92,15 @@ static char *syslog_domain; */ int syslog_send ( struct interface *xfer, unsigned int severity, const char *message, const char *terminator ) { + const char *hostname = ( syslog_hostname ? syslog_hostname : "" ); + const char *domain = ( ( hostname[0] && syslog_domain ) ? + syslog_domain : "" ); return xfer_printf ( xfer, "<%d>%s%s%s%sipxe: %s%s", SYSLOG_PRIORITY ( SYSLOG_DEFAULT_FACILITY, - severity ), - ( syslog_hostname ? syslog_hostname : "" ), - ( syslog_domain ? "." : "" ), - ( syslog_domain ? syslog_domain : "" ), - ( ( syslog_hostname || syslog_domain ) ? " " : ""), - message, terminator ); + severity ), hostname, + ( domain[0] ? "." : "" ), domain, + ( hostname[0] ? " " : "" ), message, terminator ); } /****************************************************************************** @@ -212,6 +213,28 @@ const struct setting syslog6_setting __setting ( SETTING_MISC, syslog6 ) = { }; /** + * Strip invalid characters from host/domain name + * + * @v name Name to strip + */ +static void syslog_fix_name ( char *name ) { + char *fixed = name; + int c; + + /* Do nothing if name does not exist */ + if ( ! name ) + return; + + /* Strip any non-printable or whitespace characters from the name */ + do { + c = *(name++); + *fixed = c; + if ( isprint ( c ) && ! isspace ( c ) ) + fixed++; + } while ( c ); +} + +/** * Apply syslog settings * * @ret rc Return status code @@ -223,8 +246,10 @@ static int apply_syslog_settings ( void ) { /* Fetch hostname and domain name */ free ( syslog_hostname ); fetch_string_setting_copy ( NULL, &hostname_setting, &syslog_hostname ); + syslog_fix_name ( syslog_hostname ); free ( syslog_domain ); fetch_string_setting_copy ( NULL, &domain_setting, &syslog_domain ); + syslog_fix_name ( syslog_domain ); /* Fetch log server */ syslog_console.disabled = CONSOLE_DISABLED; diff --git a/roms/ipxe/src/tests/ipv6_test.c b/roms/ipxe/src/tests/ipv6_test.c index 4de310a..e16fc7c 100644 --- a/roms/ipxe/src/tests/ipv6_test.c +++ b/roms/ipxe/src/tests/ipv6_test.c @@ -37,6 +37,30 @@ FILE_LICENCE ( GPL2_OR_LATER ); /** Define inline IPv6 address */ #define IPV6(...) { __VA_ARGS__ } +/** The unspecified IPv6 address */ +static const struct in6_addr sample_unspecified = { + .s6_addr = IPV6 ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ), +}; + +/** A sample link-local IPv6 address */ +static const struct in6_addr sample_link_local = { + .s6_addr = IPV6 ( 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x69, 0xff, 0xfe, 0x50, 0x58, 0x45 ), +}; + +/** A sample global IPv6 address */ +static const struct in6_addr sample_global = { + .s6_addr = IPV6 ( 0x20, 0x01, 0x0b, 0xa8, 0x00, 0x00, 0x01, 0xd4, + 0x00, 0x00, 0x00, 0x00, 0x69, 0x50, 0x58, 0x45 ), +}; + +/** A sample multicast IPv6 address */ +static const struct in6_addr sample_multicast = { + .s6_addr = IPV6 ( 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ), +}; + /** * Report an inet6_ntoa() test result * @@ -97,6 +121,20 @@ FILE_LICENCE ( GPL2_OR_LATER ); */ static void ipv6_test_exec ( void ) { + /* Address testing macros */ + ok ( IN6_IS_ADDR_UNSPECIFIED ( &sample_unspecified ) ); + ok ( ! IN6_IS_ADDR_UNSPECIFIED ( &sample_link_local ) ); + ok ( ! IN6_IS_ADDR_UNSPECIFIED ( &sample_global ) ); + ok ( ! IN6_IS_ADDR_UNSPECIFIED ( &sample_multicast ) ); + ok ( ! IN6_IS_ADDR_MULTICAST ( &sample_unspecified ) ); + ok ( ! IN6_IS_ADDR_MULTICAST ( &sample_link_local ) ); + ok ( ! IN6_IS_ADDR_MULTICAST ( &sample_global ) ); + ok ( IN6_IS_ADDR_MULTICAST ( &sample_multicast ) ); + ok ( ! IN6_IS_ADDR_LINKLOCAL ( &sample_unspecified ) ); + ok ( IN6_IS_ADDR_LINKLOCAL ( &sample_link_local ) ); + ok ( ! IN6_IS_ADDR_LINKLOCAL ( &sample_global ) ); + ok ( ! IN6_IS_ADDR_LINKLOCAL ( &sample_multicast ) ); + /* inet6_ntoa() tests */ inet6_ntoa_ok ( IPV6 ( 0x20, 0x01, 0x0b, 0xa8, 0x00, 0x00, 0x01, 0xd4, 0x00, 0x00, 0x00, 0x00, 0x69, 0x50, 0x58, 0x45 ), diff --git a/roms/ipxe/src/tests/ocsp_test.c b/roms/ipxe/src/tests/ocsp_test.c index 24405db..a318c18 100644 --- a/roms/ipxe/src/tests/ocsp_test.c +++ b/roms/ipxe/src/tests/ocsp_test.c @@ -825,6 +825,253 @@ CERTIFICATE ( startssl_crt, 0x1c, 0xec, 0xc2, 0x1c, 0xc5, 0xc2, 0xf5, 0x67, 0x48, 0xa7, 0x11, 0x01, 0x69, 0x83, 0xfd, 0x8e ) ); +/* + * subject RapidSSL SHA256 CA - G3 + * issuer GeoTrust Global CA + */ +CERTIFICATE ( rapidssl_crt, + DATA ( 0x30, 0x82, 0x04, 0x25, 0x30, 0x82, 0x03, 0x0d, 0xa0, 0x03, + 0x02, 0x01, 0x02, 0x02, 0x03, 0x02, 0x3a, 0x77, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x0b, 0x05, 0x00, 0x30, 0x42, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, + 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, + 0x63, 0x2e, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x12, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, + 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x43, + 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x30, 0x38, 0x32, + 0x39, 0x32, 0x31, 0x33, 0x39, 0x33, 0x32, 0x5a, 0x17, 0x0d, + 0x32, 0x32, 0x30, 0x35, 0x32, 0x30, 0x32, 0x31, 0x33, 0x39, + 0x33, 0x32, 0x5a, 0x30, 0x47, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, + 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, + 0x63, 0x2e, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x17, 0x52, 0x61, 0x70, 0x69, 0x64, 0x53, 0x53, + 0x4c, 0x20, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x20, 0x43, + 0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30, 0x82, 0x01, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xaf, + 0x54, 0x9b, 0xd9, 0x58, 0x5d, 0x1e, 0x2c, 0x56, 0xc6, 0xd5, + 0xe8, 0x7f, 0xf4, 0x7d, 0x16, 0x03, 0xff, 0xd0, 0x8b, 0x5a, + 0xe4, 0x8e, 0xa7, 0xdd, 0x54, 0x2e, 0xd4, 0x04, 0xc0, 0x5d, + 0x98, 0x9c, 0x8d, 0x90, 0x0f, 0xbc, 0x10, 0x65, 0x5f, 0xda, + 0x9a, 0xd6, 0x44, 0x7c, 0xc0, 0x9f, 0xb5, 0xe9, 0x4a, 0x8c, + 0x0b, 0x06, 0x43, 0x04, 0xbb, 0xf4, 0x96, 0xe2, 0x26, 0xf6, + 0x61, 0x01, 0x91, 0x66, 0x31, 0x22, 0xc3, 0x34, 0x34, 0x5f, + 0x3f, 0x3f, 0x91, 0x2f, 0x44, 0x5f, 0xdc, 0xc7, 0x14, 0xb6, + 0x03, 0x9f, 0x86, 0x4b, 0x0e, 0xa3, 0xff, 0xa0, 0x80, 0x02, + 0x83, 0xc3, 0xd3, 0x1f, 0x69, 0x52, 0xd6, 0x9d, 0x64, 0x0f, + 0xc9, 0x83, 0xe7, 0x1b, 0xc4, 0x70, 0xac, 0x94, 0xe7, 0xc3, + 0xa4, 0x6a, 0x2c, 0xbd, 0xb8, 0x9e, 0x69, 0xd8, 0xbe, 0x0a, + 0x8f, 0x16, 0x63, 0x5a, 0x68, 0x71, 0x80, 0x7b, 0x30, 0xde, + 0x15, 0x04, 0xbf, 0xcc, 0xd3, 0xbf, 0x3e, 0x48, 0x05, 0x55, + 0x7a, 0xb3, 0xd7, 0x10, 0x0c, 0x03, 0xfc, 0x9b, 0xfd, 0x08, + 0xa7, 0x8c, 0x8c, 0xdb, 0xa7, 0x8e, 0xf1, 0x1e, 0x63, 0xdc, + 0xb3, 0x01, 0x2f, 0x7f, 0xaf, 0x57, 0xc3, 0x3c, 0x48, 0xa7, + 0x83, 0x68, 0x21, 0xa7, 0x2f, 0xe7, 0xa7, 0x3f, 0xf0, 0xb5, + 0x0c, 0xfc, 0xf5, 0x84, 0xd1, 0x53, 0xbc, 0x0e, 0x72, 0x4f, + 0x60, 0x0c, 0x42, 0xb8, 0x98, 0xad, 0x19, 0x88, 0x57, 0xd7, + 0x04, 0xec, 0x87, 0xbf, 0x7e, 0x87, 0x4e, 0xa3, 0x21, 0xf9, + 0x53, 0xfd, 0x36, 0x98, 0x48, 0x8d, 0xd6, 0xf8, 0xbb, 0x48, + 0xf2, 0x29, 0xc8, 0x64, 0xd1, 0xcc, 0x54, 0x48, 0x53, 0x8b, + 0xaf, 0xb7, 0x65, 0x1e, 0xbf, 0x29, 0x33, 0x29, 0xd9, 0x29, + 0x60, 0x48, 0xf8, 0xff, 0x91, 0xbc, 0x57, 0x58, 0xe5, 0x35, + 0x2e, 0xbb, 0x69, 0xb6, 0x59, 0x02, 0x03, 0x01, 0x00, 0x01, + 0xa3, 0x82, 0x01, 0x1d, 0x30, 0x82, 0x01, 0x19, 0x30, 0x1f, + 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, + 0x14, 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb, 0xab, 0x05, + 0x64, 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc, + 0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, + 0x04, 0x14, 0xc3, 0x9c, 0xf3, 0xfc, 0xd3, 0x46, 0x08, 0x34, + 0xbb, 0xce, 0x46, 0x7f, 0xa0, 0x7c, 0x5b, 0xf3, 0xe2, 0x08, + 0xcb, 0x59, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, + 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, + 0x01, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, + 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x35, + 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2e, 0x30, 0x2c, 0x30, + 0x2a, 0xa0, 0x28, 0xa0, 0x26, 0x86, 0x24, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e, 0x73, 0x79, 0x6d, 0x63, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, + 0x2f, 0x67, 0x74, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x2e, + 0x63, 0x72, 0x6c, 0x30, 0x2e, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x22, 0x30, 0x20, 0x30, + 0x1e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, + 0x01, 0x86, 0x12, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x67, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x64, 0x2e, 0x63, 0x6f, + 0x6d, 0x30, 0x4c, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x45, + 0x30, 0x43, 0x30, 0x41, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, + 0x86, 0xf8, 0x45, 0x01, 0x07, 0x36, 0x30, 0x33, 0x30, 0x31, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, + 0x16, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, + 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, + 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xa3, + 0x58, 0x1e, 0xc6, 0x43, 0x32, 0xac, 0xac, 0x2f, 0x93, 0x78, + 0xb7, 0xea, 0xae, 0x54, 0x40, 0x47, 0x2d, 0x7e, 0x78, 0x8d, + 0x50, 0xf6, 0xf8, 0x66, 0xac, 0xd6, 0x4f, 0x73, 0xd6, 0x44, + 0xef, 0xaf, 0x0b, 0xcc, 0x5b, 0xc1, 0xf4, 0x4f, 0x9a, 0x8f, + 0x49, 0x7e, 0x60, 0xaf, 0xc2, 0x27, 0xc7, 0x16, 0xf1, 0xfb, + 0x93, 0x81, 0x90, 0xa9, 0x7c, 0xef, 0x6f, 0x7e, 0x6e, 0x45, + 0x94, 0x16, 0x84, 0xbd, 0xec, 0x49, 0xf1, 0xc4, 0x0e, 0xf4, + 0xaf, 0x04, 0x59, 0x83, 0x87, 0x0f, 0x2c, 0x3b, 0x97, 0xc3, + 0x5a, 0x12, 0x9b, 0x7b, 0x04, 0x35, 0x7b, 0xa3, 0x95, 0x33, + 0x08, 0x7b, 0x93, 0x71, 0x22, 0x42, 0xb3, 0xa9, 0xd9, 0x6f, + 0x4f, 0x81, 0x92, 0xfc, 0x07, 0xb6, 0x79, 0xbc, 0x84, 0x4a, + 0x9d, 0x77, 0x09, 0xf1, 0xc5, 0x89, 0xf2, 0xf0, 0xb4, 0x9c, + 0x54, 0xaa, 0x12, 0x7b, 0x0d, 0xba, 0x4f, 0xef, 0x93, 0x19, + 0xec, 0xef, 0x7d, 0x4e, 0x61, 0xa3, 0x8e, 0x76, 0x9c, 0x59, + 0xcf, 0x8c, 0x94, 0xb1, 0x84, 0x97, 0xf7, 0x1a, 0xb9, 0x07, + 0xb8, 0xb2, 0xc6, 0x4f, 0x13, 0x79, 0xdb, 0xbf, 0x4f, 0x51, + 0x1b, 0x7f, 0x69, 0x0d, 0x51, 0x2a, 0xc1, 0xd6, 0x15, 0xff, + 0x37, 0x51, 0x34, 0x65, 0x51, 0xf4, 0x1e, 0xbe, 0x38, 0x6a, + 0xec, 0x0e, 0xab, 0xbf, 0x3d, 0x7b, 0x39, 0x05, 0x7b, 0xf4, + 0xf3, 0xfb, 0x1a, 0xa1, 0xd0, 0xc8, 0x7e, 0x4e, 0x64, 0x8d, + 0xcd, 0x8c, 0x61, 0x55, 0x90, 0xfe, 0x3a, 0xca, 0x5d, 0x25, + 0x0f, 0xf8, 0x1d, 0xa3, 0x4a, 0x74, 0x56, 0x4f, 0x1a, 0x55, + 0x40, 0x70, 0x75, 0x25, 0xa6, 0x33, 0x2e, 0xba, 0x4b, 0xa5, + 0x5d, 0x53, 0x9a, 0x0d, 0x30, 0xe1, 0x8d, 0x5f, 0x61, 0x2c, + 0xaf, 0xcc, 0xef, 0xb0, 0x99, 0xa1, 0x80, 0xff, 0x0b, 0xf2, + 0x62, 0x4c, 0x70, 0x26, 0x98 ), + FINGERPRINT ( 0xbc, 0x3f, 0x03, 0xa4, 0x36, 0x24, 0x0e, 0xdb, + 0xa5, 0xf8, 0x37, 0x14, 0xf6, 0xf6, 0x77, 0xe3, + 0x4b, 0x37, 0xf9, 0xb1, 0xf0, 0xc0, 0x8c, 0x1e, + 0x55, 0x8d, 0x98, 0x1e, 0x27, 0x9e, 0x82, 0x09 ) ); + +/* + * subject *.vultr.com + * issuer RapidSSL SHA256 CA - G3 + */ +CERTIFICATE ( vultr_crt, + DATA ( 0x30, 0x82, 0x04, 0xa8, 0x30, 0x82, 0x03, 0x90, 0xa0, 0x03, + 0x02, 0x01, 0x02, 0x02, 0x03, 0x00, 0x95, 0x4d, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x0b, 0x05, 0x00, 0x30, 0x47, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, + 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47, + 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, + 0x63, 0x2e, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x17, 0x52, 0x61, 0x70, 0x69, 0x64, 0x53, 0x53, + 0x4c, 0x20, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x20, 0x43, + 0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30, 0x1e, 0x17, 0x0d, + 0x31, 0x34, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x33, 0x31, + 0x32, 0x38, 0x5a, 0x17, 0x0d, 0x31, 0x37, 0x31, 0x32, 0x32, + 0x32, 0x31, 0x39, 0x32, 0x39, 0x31, 0x30, 0x5a, 0x30, 0x81, + 0x8f, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x0b, + 0x13, 0x0a, 0x47, 0x54, 0x36, 0x32, 0x30, 0x37, 0x39, 0x37, + 0x32, 0x31, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, + 0x0b, 0x13, 0x28, 0x53, 0x65, 0x65, 0x20, 0x77, 0x77, 0x77, + 0x2e, 0x72, 0x61, 0x70, 0x69, 0x64, 0x73, 0x73, 0x6c, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x2f, 0x63, 0x70, 0x73, 0x20, 0x28, 0x63, + 0x29, 0x31, 0x33, 0x31, 0x2f, 0x30, 0x2d, 0x06, 0x03, 0x55, + 0x04, 0x0b, 0x13, 0x26, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, + 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x20, 0x56, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x20, 0x2d, + 0x20, 0x52, 0x61, 0x70, 0x69, 0x64, 0x53, 0x53, 0x4c, 0x28, + 0x52, 0x29, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x0c, 0x0b, 0x2a, 0x2e, 0x76, 0x75, 0x6c, 0x74, 0x72, + 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, + 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0x9f, 0xa8, 0x2e, + 0x65, 0x41, 0x05, 0xec, 0xef, 0x69, 0x92, 0xf6, 0xd3, 0x53, + 0x4f, 0xd4, 0x8e, 0xd3, 0x49, 0x8c, 0xc7, 0x85, 0x6d, 0xb0, + 0x71, 0xe0, 0x28, 0x04, 0x80, 0x85, 0x82, 0x3e, 0x3f, 0xdb, + 0x0c, 0xed, 0xcd, 0x2b, 0x04, 0xc8, 0x67, 0x15, 0x8c, 0x25, + 0xd6, 0x7a, 0x54, 0x67, 0x94, 0xbe, 0x33, 0x12, 0x33, 0x88, + 0xfe, 0x5d, 0x1d, 0x36, 0x62, 0x4e, 0xbc, 0x1e, 0x7e, 0xd3, + 0x3e, 0x7c, 0x3c, 0x59, 0x4a, 0x99, 0x0b, 0xca, 0x9b, 0x1e, + 0xc7, 0xf7, 0xe7, 0x6d, 0xdc, 0x57, 0xbe, 0x3a, 0xab, 0xc2, + 0x0b, 0xb1, 0xbe, 0x90, 0xa1, 0x54, 0x07, 0xc5, 0x48, 0x65, + 0xc1, 0x32, 0x99, 0x92, 0x26, 0x97, 0x90, 0x3e, 0x68, 0x6b, + 0xac, 0xbd, 0x4f, 0x0e, 0x88, 0x38, 0xd3, 0xdc, 0x80, 0x9e, + 0xb9, 0x66, 0x5d, 0xeb, 0x19, 0xfd, 0x85, 0xff, 0xba, 0xf0, + 0x89, 0x20, 0x08, 0xea, 0xd8, 0x01, 0x76, 0x29, 0xdc, 0xf0, + 0x1c, 0xfa, 0xbf, 0x6f, 0x7b, 0x67, 0xf4, 0xc4, 0xee, 0xe3, + 0xde, 0x95, 0xa2, 0x76, 0x65, 0x98, 0xc5, 0x21, 0xdc, 0xe9, + 0x95, 0xee, 0x83, 0x97, 0xae, 0xdd, 0xab, 0xdb, 0xc0, 0x47, + 0xc8, 0x68, 0x00, 0xb3, 0xab, 0x11, 0x4f, 0x81, 0xf5, 0x45, + 0x01, 0xd1, 0x64, 0xfd, 0x53, 0xbf, 0x86, 0xef, 0x87, 0xca, + 0x8e, 0x55, 0xa0, 0x27, 0x27, 0xe5, 0x9e, 0xc1, 0x69, 0x28, + 0x2a, 0x9f, 0x2d, 0xe2, 0x2c, 0x25, 0xef, 0x74, 0x1b, 0x52, + 0xe4, 0x81, 0xf4, 0xc2, 0x71, 0x0a, 0x48, 0xff, 0x47, 0xa5, + 0xea, 0x0a, 0xf5, 0xb1, 0x6d, 0xae, 0x09, 0x2b, 0xf9, 0x23, + 0x6a, 0x28, 0x58, 0x3d, 0xbb, 0x9f, 0x80, 0xb2, 0x81, 0x03, + 0xae, 0xe5, 0xea, 0xbe, 0x97, 0xae, 0xec, 0x3c, 0x33, 0xc7, + 0x68, 0xf0, 0x6c, 0x89, 0x9d, 0x01, 0x2a, 0x1e, 0x2b, 0xd7, + 0x93, 0x5a, 0xa9, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, + 0x01, 0x52, 0x30, 0x82, 0x01, 0x4e, 0x30, 0x1f, 0x06, 0x03, + 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xc3, + 0x9c, 0xf3, 0xfc, 0xd3, 0x46, 0x08, 0x34, 0xbb, 0xce, 0x46, + 0x7f, 0xa0, 0x7c, 0x5b, 0xf3, 0xe2, 0x08, 0xcb, 0x59, 0x30, + 0x57, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, + 0x01, 0x04, 0x4b, 0x30, 0x49, 0x30, 0x1f, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x13, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x76, 0x2e, 0x73, + 0x79, 0x6d, 0x63, 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x26, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, + 0x86, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, + 0x76, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x67, 0x76, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x0e, + 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, + 0x03, 0x02, 0x05, 0xa0, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, + 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, 0x21, 0x06, 0x03, 0x55, + 0x1d, 0x11, 0x04, 0x1a, 0x30, 0x18, 0x82, 0x0b, 0x2a, 0x2e, + 0x76, 0x75, 0x6c, 0x74, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x82, + 0x09, 0x76, 0x75, 0x6c, 0x74, 0x72, 0x2e, 0x63, 0x6f, 0x6d, + 0x30, 0x2b, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x24, 0x30, + 0x22, 0x30, 0x20, 0xa0, 0x1e, 0xa0, 0x1c, 0x86, 0x1a, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x76, 0x2e, 0x73, + 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, + 0x76, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0c, 0x06, 0x03, 0x55, + 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, + 0x45, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x3e, 0x30, 0x3c, + 0x30, 0x3a, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, + 0x45, 0x01, 0x07, 0x36, 0x30, 0x2c, 0x30, 0x2a, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1e, + 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, + 0x77, 0x2e, 0x72, 0x61, 0x70, 0x69, 0x64, 0x73, 0x73, 0x6c, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x65, 0x67, 0x61, 0x6c, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, + 0x93, 0x63, 0x02, 0x6a, 0xa1, 0x2a, 0xf3, 0xbe, 0x64, 0x2b, + 0x36, 0xaf, 0x66, 0x16, 0x49, 0x29, 0x56, 0x6c, 0xc7, 0x75, + 0x74, 0xf3, 0x69, 0xd8, 0x85, 0xce, 0x50, 0x3b, 0x43, 0x89, + 0xaf, 0x74, 0x99, 0x26, 0x34, 0xa4, 0x27, 0xa8, 0xfa, 0xfe, + 0x45, 0xaf, 0xbe, 0x75, 0x22, 0x3d, 0x15, 0xca, 0xa6, 0x38, + 0xc9, 0x2b, 0x3c, 0xf4, 0x11, 0x9f, 0x96, 0xa7, 0x69, 0x3e, + 0xf8, 0xf0, 0x88, 0xd8, 0xd2, 0x9c, 0x1c, 0x0e, 0x4c, 0xfd, + 0xf3, 0x3b, 0x48, 0x25, 0x68, 0xb3, 0x8d, 0x7e, 0x26, 0x73, + 0x73, 0xcb, 0x3a, 0x41, 0x92, 0x16, 0x55, 0xe1, 0xff, 0x00, + 0x07, 0xa2, 0xfe, 0xfe, 0x25, 0xfc, 0x86, 0x0f, 0x49, 0xff, + 0x96, 0x78, 0x02, 0x65, 0xd7, 0xad, 0xcd, 0x0c, 0x82, 0x35, + 0x56, 0xfe, 0x12, 0x25, 0xa9, 0x8f, 0xc2, 0xa4, 0xe9, 0x43, + 0xbb, 0x85, 0x62, 0x21, 0x62, 0x5d, 0x70, 0x76, 0x79, 0x75, + 0xeb, 0xd6, 0x82, 0x53, 0x0d, 0xde, 0x77, 0x95, 0x56, 0x87, + 0x44, 0x13, 0x82, 0x7d, 0xa9, 0x9a, 0x94, 0x7e, 0x1c, 0x6d, + 0x7f, 0x72, 0xef, 0x04, 0x84, 0xdf, 0x65, 0x98, 0x17, 0xb4, + 0xbe, 0xfe, 0x30, 0x0f, 0xfa, 0x8d, 0x9f, 0x29, 0x1d, 0xbd, + 0x99, 0xa7, 0xe3, 0x09, 0x1d, 0x13, 0x21, 0xfd, 0x9e, 0x03, + 0x17, 0xb9, 0x9e, 0x48, 0x35, 0x66, 0xe5, 0x86, 0x0a, 0x0f, + 0x04, 0xfd, 0xcd, 0x3b, 0x13, 0x59, 0xd6, 0x0c, 0x05, 0x8c, + 0xd2, 0x6b, 0x5c, 0x45, 0x33, 0x43, 0x91, 0xac, 0xb8, 0xdd, + 0xe3, 0xbe, 0xdf, 0x7b, 0x5c, 0x94, 0xa9, 0xfd, 0xc0, 0xf8, + 0xa9, 0xd2, 0x82, 0xcd, 0xbf, 0x60, 0xd7, 0xf8, 0x3d, 0x53, + 0xf7, 0x6a, 0xdc, 0x46, 0xc4, 0x22, 0x84, 0x98, 0x6a, 0x32, + 0xf2, 0xdf, 0x43, 0xd9, 0x5a, 0xdb, 0x97, 0x20, 0x05, 0x0b, + 0x3e, 0x06, 0x38, 0x13, 0x3a, 0x21 ), + FINGERPRINT ( 0x2c, 0x58, 0x63, 0x6e, 0xf1, 0xab, 0x8f, 0xff, + 0x86, 0x5d, 0x4f, 0x8d, 0x3f, 0xa9, 0x4d, 0x63, + 0xe7, 0xe6, 0xc6, 0x03, 0x1e, 0xc9, 0x0e, 0xfb, + 0xe8, 0xaa, 0xa6, 0xf4, 0x6f, 0x21, 0xc7, 0x7b ) ); + OCSP ( barclays_ocsp, &barclays_crt, &verisign_crt, DATA ( 0x30, 0x51, 0x30, 0x4f, 0x30, 0x4d, 0x30, 0x4b, 0x30, 0x49, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, @@ -1301,12 +1548,164 @@ OCSP ( unknown_ocsp, &thawte_crt, &startssl_crt, 0x8c, 0x3e, 0xb4, 0x66, 0x76, 0x67, 0x34, 0x70, 0x00, 0x63, 0xcf, 0x9e, 0xc8, 0xc5, 0x5f, 0x48, 0x06, 0x53, 0x26, 0x55 ) ); +OCSP ( vultr_ocsp, &vultr_crt, &rapidssl_crt, + DATA ( 0x30, 0x44, 0x30, 0x42, 0x30, 0x40, 0x30, 0x3e, 0x30, 0x3c, + 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, + 0x00, 0x04, 0x14, 0x40, 0x0b, 0x46, 0x7a, 0xf1, 0xe6, 0xb2, + 0xd3, 0x09, 0x83, 0xba, 0x0d, 0x60, 0x7e, 0x7e, 0x59, 0x37, + 0x48, 0x24, 0xc4, 0x04, 0x14, 0xc3, 0x9c, 0xf3, 0xfc, 0xd3, + 0x46, 0x08, 0x34, 0xbb, 0xce, 0x46, 0x7f, 0xa0, 0x7c, 0x5b, + 0xf3, 0xe2, 0x08, 0xcb, 0x59, 0x02, 0x03, 0x00, 0x95, 0x4d ), + DATA ( 0x30, 0x82, 0x05, 0x70, 0x0a, 0x01, 0x00, 0xa0, 0x82, 0x05, + 0x69, 0x30, 0x82, 0x05, 0x65, 0x06, 0x09, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x30, 0x01, 0x01, 0x04, 0x82, 0x05, 0x56, + 0x30, 0x82, 0x05, 0x52, 0x30, 0x81, 0x91, 0xa2, 0x16, 0x04, + 0x14, 0xfa, 0x58, 0xdb, 0x09, 0x53, 0xbc, 0x19, 0xc5, 0xe7, + 0xb5, 0x8b, 0xf6, 0x10, 0xf8, 0x1e, 0x84, 0x6d, 0x3a, 0x8f, + 0xd8, 0x18, 0x0f, 0x32, 0x30, 0x31, 0x34, 0x31, 0x31, 0x32, + 0x32, 0x32, 0x33, 0x30, 0x38, 0x35, 0x36, 0x5a, 0x30, 0x66, + 0x30, 0x64, 0x30, 0x3c, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, + 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14, 0x40, 0x0b, 0x46, + 0x7a, 0xf1, 0xe6, 0xb2, 0xd3, 0x09, 0x83, 0xba, 0x0d, 0x60, + 0x7e, 0x7e, 0x59, 0x37, 0x48, 0x24, 0xc4, 0x04, 0x14, 0xc3, + 0x9c, 0xf3, 0xfc, 0xd3, 0x46, 0x08, 0x34, 0xbb, 0xce, 0x46, + 0x7f, 0xa0, 0x7c, 0x5b, 0xf3, 0xe2, 0x08, 0xcb, 0x59, 0x02, + 0x03, 0x00, 0x95, 0x4d, 0x80, 0x00, 0x18, 0x0f, 0x32, 0x30, + 0x31, 0x34, 0x31, 0x31, 0x32, 0x32, 0x32, 0x33, 0x30, 0x38, + 0x35, 0x36, 0x5a, 0xa0, 0x11, 0x18, 0x0f, 0x32, 0x30, 0x31, + 0x34, 0x31, 0x31, 0x32, 0x39, 0x32, 0x33, 0x30, 0x38, 0x35, + 0x36, 0x5a, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, + 0x01, 0x00, 0x6a, 0x71, 0x8f, 0x84, 0x66, 0xb5, 0x75, 0xe6, + 0x97, 0xa4, 0xb9, 0xc6, 0xa0, 0x37, 0x6f, 0x23, 0x76, 0x3c, + 0x59, 0x4c, 0x1c, 0x2d, 0x9f, 0x70, 0xab, 0x83, 0xbf, 0xa9, + 0xbf, 0x79, 0x31, 0x69, 0xdd, 0x78, 0xd5, 0x59, 0x90, 0x68, + 0xbe, 0x25, 0xb7, 0x53, 0x7d, 0x8b, 0xcf, 0x66, 0x3b, 0xcd, + 0xe0, 0xd2, 0x40, 0x1d, 0xc8, 0x29, 0xe4, 0x37, 0xbf, 0x20, + 0x7e, 0x64, 0x8d, 0x0d, 0xc7, 0xed, 0x0d, 0x08, 0x05, 0x36, + 0x27, 0x4f, 0xb8, 0xe3, 0x19, 0xec, 0xf0, 0x96, 0xe8, 0x48, + 0x9b, 0x8b, 0x2c, 0x18, 0xdb, 0x1e, 0x68, 0x11, 0xf3, 0xfb, + 0x9c, 0x68, 0xad, 0xcc, 0x15, 0xe0, 0x25, 0x08, 0x98, 0xd2, + 0xbf, 0xd0, 0x57, 0xe6, 0x4c, 0x73, 0x5a, 0x2c, 0xc8, 0x89, + 0xd6, 0xe4, 0xd0, 0x47, 0x6d, 0x8c, 0xc7, 0x75, 0xb1, 0x4e, + 0x10, 0x34, 0xe5, 0x40, 0xa3, 0xb1, 0x50, 0x07, 0x3d, 0x7d, + 0xad, 0xeb, 0x1d, 0x91, 0x7f, 0x77, 0x2e, 0x0d, 0x9a, 0xa7, + 0xbb, 0x68, 0x89, 0xd2, 0x05, 0x58, 0x16, 0xf1, 0x5e, 0x1d, + 0x05, 0xf6, 0x9e, 0xe9, 0x89, 0x52, 0x35, 0xb7, 0x29, 0x7a, + 0x68, 0x02, 0x6f, 0xc7, 0x20, 0x30, 0xc8, 0xde, 0x97, 0x3f, + 0xb7, 0x28, 0x38, 0x39, 0xd1, 0x4b, 0x4b, 0x90, 0x71, 0xe5, + 0x58, 0xa4, 0xa3, 0xbd, 0x78, 0x95, 0xb5, 0x54, 0xdd, 0xf7, + 0x4f, 0x8e, 0x78, 0x73, 0x86, 0xbf, 0x28, 0xb0, 0xdd, 0xc0, + 0xe9, 0x4a, 0xf5, 0x9f, 0x02, 0x8e, 0x63, 0x8f, 0x59, 0xf1, + 0x93, 0xf0, 0x45, 0x97, 0x30, 0xdb, 0x0a, 0x04, 0x3e, 0x81, + 0x99, 0x20, 0x7a, 0xb2, 0xe6, 0x8c, 0x8f, 0x2a, 0x4c, 0x31, + 0xf1, 0x64, 0xbc, 0xb7, 0xec, 0xb1, 0xf9, 0x69, 0x1f, 0x99, + 0x89, 0x3e, 0x3e, 0xa0, 0xf4, 0xde, 0x79, 0xa7, 0xae, 0xa3, + 0x23, 0xbd, 0x16, 0xbb, 0x6d, 0x0f, 0x15, 0x68, 0xa0, 0x82, + 0x03, 0xa6, 0x30, 0x82, 0x03, 0xa2, 0x30, 0x82, 0x03, 0x9e, + 0x30, 0x82, 0x02, 0x86, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, + 0x01, 0x04, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x47, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, + 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x20, 0x30, 0x1e, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x17, 0x52, 0x61, 0x70, + 0x69, 0x64, 0x53, 0x53, 0x4c, 0x20, 0x53, 0x48, 0x41, 0x32, + 0x35, 0x36, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, + 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x30, 0x38, 0x32, 0x39, + 0x32, 0x33, 0x33, 0x39, 0x33, 0x30, 0x5a, 0x17, 0x0d, 0x31, + 0x35, 0x30, 0x35, 0x32, 0x32, 0x32, 0x33, 0x33, 0x39, 0x33, + 0x30, 0x5a, 0x30, 0x31, 0x31, 0x2f, 0x30, 0x2d, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x13, 0x26, 0x52, 0x61, 0x70, 0x69, 0x64, + 0x53, 0x53, 0x4c, 0x20, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, + 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x20, 0x4f, + 0x43, 0x53, 0x50, 0x20, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x64, 0x65, 0x72, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, + 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, + 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0x9b, 0xf2, 0x8e, 0xe9, + 0x57, 0x3e, 0xa8, 0x5c, 0xfd, 0x00, 0x14, 0x21, 0xe7, 0xe4, + 0x57, 0xbb, 0x55, 0xc8, 0xa8, 0x50, 0x93, 0xdc, 0xbf, 0xfc, + 0xde, 0x46, 0x8a, 0x53, 0x9f, 0x12, 0xaa, 0x7c, 0xf1, 0xdd, + 0x89, 0x9e, 0x02, 0x27, 0x9c, 0x1a, 0xa0, 0x94, 0xf5, 0xec, + 0x06, 0xa3, 0xdb, 0xf3, 0x3f, 0x6d, 0xfd, 0x30, 0x6d, 0xab, + 0xcb, 0xc3, 0x72, 0xa9, 0x25, 0x35, 0x69, 0x67, 0x07, 0xaf, + 0x9c, 0x91, 0x3a, 0x24, 0x03, 0x74, 0x59, 0xfd, 0x69, 0xa6, + 0xfe, 0x23, 0xa4, 0x6c, 0x2f, 0xbe, 0x44, 0x56, 0x47, 0xee, + 0xdb, 0x07, 0xc3, 0x72, 0x3f, 0x14, 0xdc, 0x16, 0xb9, 0x66, + 0x48, 0x7c, 0x6e, 0x69, 0x6f, 0xa1, 0x05, 0xc6, 0x36, 0x08, + 0x01, 0xdd, 0x1c, 0xb8, 0x52, 0xf4, 0x86, 0x96, 0x85, 0x39, + 0x89, 0xb0, 0x31, 0x67, 0x62, 0xc5, 0x52, 0x91, 0x72, 0xd7, + 0x96, 0x8c, 0xe1, 0x0a, 0x02, 0x6a, 0xfe, 0x82, 0xca, 0xc0, + 0x34, 0xc9, 0xbc, 0x45, 0xa7, 0xc0, 0x4b, 0xa0, 0x7c, 0x7c, + 0xcc, 0x29, 0xe5, 0x8c, 0xf6, 0x91, 0x65, 0x33, 0xf1, 0x7b, + 0xda, 0x55, 0x69, 0x93, 0x2d, 0x4e, 0xb9, 0xb4, 0x7f, 0x56, + 0xe6, 0x80, 0xbe, 0x23, 0x4a, 0x4a, 0x65, 0xa6, 0xab, 0xa2, + 0x40, 0xb1, 0x75, 0x62, 0x13, 0xc1, 0xfd, 0x52, 0xe1, 0xbb, + 0x7b, 0xb1, 0x7f, 0x8a, 0x0c, 0x27, 0x35, 0xec, 0x27, 0x3b, + 0xa5, 0xe7, 0x75, 0xb8, 0xe3, 0xc4, 0xcf, 0x4d, 0x8a, 0x02, + 0x57, 0x57, 0x16, 0xa2, 0x8e, 0x9d, 0x87, 0x5a, 0x32, 0xb6, + 0xf6, 0x1d, 0xf5, 0xe3, 0xd7, 0xcf, 0x79, 0xc8, 0x77, 0x74, + 0xdc, 0xe5, 0xba, 0xde, 0x5c, 0x22, 0xad, 0xc0, 0xfa, 0x67, + 0xf3, 0x26, 0xbf, 0xcc, 0xd4, 0x88, 0xd5, 0xda, 0x87, 0x4d, + 0x9d, 0x99, 0xc1, 0xce, 0xa4, 0x9a, 0xda, 0x99, 0xa5, 0xa2, + 0xe1, 0xc5, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xaa, + 0x30, 0x81, 0xa7, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, + 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xc3, 0x9c, 0xf3, 0xfc, + 0xd3, 0x46, 0x08, 0x34, 0xbb, 0xce, 0x46, 0x7f, 0xa0, 0x7c, + 0x5b, 0xf3, 0xe2, 0x08, 0xcb, 0x59, 0x30, 0x0f, 0x06, 0x09, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x05, 0x04, + 0x02, 0x05, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, + 0x04, 0x16, 0x04, 0x14, 0xfa, 0x58, 0xdb, 0x09, 0x53, 0xbc, + 0x19, 0xc5, 0xe7, 0xb5, 0x8b, 0xf6, 0x10, 0xf8, 0x1e, 0x84, + 0x6d, 0x3a, 0x8f, 0xd8, 0x30, 0x13, 0x06, 0x03, 0x55, 0x1d, + 0x25, 0x04, 0x0c, 0x30, 0x0a, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x03, 0x09, 0x30, 0x0c, 0x06, 0x03, 0x55, + 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, + 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, + 0x04, 0x03, 0x02, 0x07, 0x80, 0x30, 0x21, 0x06, 0x03, 0x55, + 0x1d, 0x11, 0x04, 0x1a, 0x30, 0x18, 0xa4, 0x16, 0x30, 0x14, + 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x09, 0x54, 0x47, 0x56, 0x2d, 0x42, 0x2d, 0x32, 0x31, 0x34, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, + 0x3e, 0x45, 0xce, 0x3d, 0x53, 0x8c, 0x88, 0xcd, 0xde, 0xf1, + 0x38, 0x0c, 0x00, 0x7a, 0x7e, 0x22, 0xe7, 0x1a, 0xa5, 0xbe, + 0xee, 0x1c, 0x17, 0x20, 0xc3, 0x65, 0x68, 0x86, 0x27, 0x83, + 0x62, 0xd7, 0xdc, 0x1d, 0x6c, 0xfa, 0x24, 0x2e, 0x66, 0x50, + 0xe5, 0xe0, 0x42, 0xa5, 0x73, 0x67, 0x2a, 0xea, 0x5a, 0x17, + 0x20, 0x3b, 0x14, 0xd4, 0x74, 0x14, 0xbd, 0x18, 0x60, 0xbe, + 0xa6, 0x46, 0xb1, 0xc2, 0x82, 0xc9, 0xb6, 0x99, 0x67, 0x56, + 0xbe, 0x17, 0xda, 0x78, 0x05, 0x48, 0x65, 0x9d, 0x48, 0xb5, + 0xda, 0x1d, 0x52, 0x59, 0x2a, 0xac, 0x09, 0x2d, 0x29, 0x18, + 0x96, 0xc1, 0x58, 0x79, 0xfc, 0x73, 0x0b, 0x70, 0x4d, 0x31, + 0x61, 0x80, 0xc7, 0x77, 0x02, 0xf1, 0x12, 0xb3, 0x80, 0x6f, + 0xb9, 0x05, 0x69, 0xcf, 0x4f, 0x80, 0x7d, 0xf5, 0x06, 0xe6, + 0x2e, 0xc7, 0x53, 0x99, 0x8b, 0x07, 0xc7, 0x7a, 0xe6, 0xf3, + 0x12, 0x86, 0xd1, 0xbb, 0x8a, 0x8a, 0xfb, 0x9d, 0xd1, 0x0b, + 0xe7, 0x9f, 0x12, 0x06, 0xfb, 0x7d, 0x8e, 0xe7, 0xb7, 0x39, + 0xe0, 0x3c, 0xd0, 0xe8, 0x35, 0x44, 0x28, 0xb7, 0xcb, 0xee, + 0xef, 0xa7, 0x14, 0xfa, 0x0e, 0x34, 0xaf, 0x78, 0x59, 0x1e, + 0x91, 0xd9, 0xe0, 0x9b, 0x3c, 0x9e, 0x3a, 0xbf, 0xf5, 0xf5, + 0x11, 0x5b, 0x04, 0x48, 0xcd, 0x3a, 0x3f, 0xee, 0x46, 0x6d, + 0x69, 0x68, 0x39, 0xc1, 0x4d, 0x54, 0xfd, 0x6c, 0x27, 0x1e, + 0x5b, 0x58, 0x00, 0xbb, 0x4f, 0x1b, 0x12, 0xd3, 0xbb, 0x46, + 0xf4, 0x7c, 0x4a, 0x44, 0xb5, 0xcb, 0x4f, 0xf2, 0x3d, 0xc3, + 0x51, 0xfc, 0x7a, 0x2c, 0x59, 0xd0, 0x82, 0x73, 0xe3, 0x88, + 0xfc, 0x25, 0x4c, 0x35, 0x6f, 0x88, 0x85, 0xff, 0xad, 0x8c, + 0x83, 0xc4, 0x76, 0x58, 0x6b, 0xfa, 0xf2, 0xed, 0x5b, 0x95, + 0xd9, 0x07, 0x55, 0x58, 0xfe, 0x08 ) ); + /** Time at which OCSP responses are valid */ static time_t test_time = 1337062083ULL; /* Tue 15 May 2012 06:08:03 */ /** Time at which OCSP responses are not valid */ static time_t test_stale = 1375573111ULL; /* Sat Aug 3 23:38:31 2013 */ +/** Time at which "vultr" OCSP response (generated more recently) is valid */ +static time_t test_vultr = 1416697736ULL; /* Sat 22 Nov 23:08:56 2014 */ + /** * Report certificate parsing test result * @@ -1398,12 +1797,15 @@ static void ocsp_test_exec ( void ) { ocsp_certificate_ok ( &verisign_crt ); ocsp_certificate_ok ( &thawte_crt ); ocsp_certificate_ok ( &startssl_crt ); + ocsp_certificate_ok ( &rapidssl_crt ); + ocsp_certificate_ok ( &vultr_crt ); /* Parse OCSP checks */ ocsp_check_ok ( &barclays_ocsp ); ocsp_check_ok ( &google_ocsp ); ocsp_check_ok ( &unauthorized_ocsp ); ocsp_check_ok ( &unknown_ocsp ); + ocsp_check_ok ( &vultr_ocsp ); /* "barclays" test */ ocsp_request_ok ( &barclays_ocsp ); @@ -1425,13 +1827,22 @@ static void ocsp_test_exec ( void ) { ocsp_request_ok ( &unknown_ocsp ); ocsp_response_fail_ok ( &unknown_ocsp ); + /* "vultr" test */ + ocsp_request_ok ( &vultr_ocsp ); + ocsp_response_ok ( &vultr_ocsp ); + ocsp_validate_ok ( &vultr_ocsp, test_vultr ); + ocsp_validate_fail_ok ( &vultr_ocsp, test_stale ); + /* Drop OCSP check references */ ocsp_put ( unknown_ocsp.ocsp ); ocsp_put ( unauthorized_ocsp.ocsp ); ocsp_put ( google_ocsp.ocsp ); ocsp_put ( barclays_ocsp.ocsp ); + ocsp_put ( vultr_ocsp.ocsp ); /* Drop certificate references */ + x509_put ( vultr_crt.cert ); + x509_put ( rapidssl_crt.cert ); x509_put ( startssl_crt.cert ); x509_put ( thawte_crt.cert ); x509_put ( verisign_crt.cert ); diff --git a/roms/ipxe/src/tests/string_test.c b/roms/ipxe/src/tests/string_test.c index 88181cc..3b48d9f 100644 --- a/roms/ipxe/src/tests/string_test.c +++ b/roms/ipxe/src/tests/string_test.c @@ -124,6 +124,36 @@ static void string_test_exec ( void ) { memswap ( ( test + 1 ), ( test + 4 ), 3 ); ok ( memcmp ( test, expected, sizeof ( test ) ) == 0 ); } + + /* Test strdup() */ + { + const char *orig = "testing testing"; + char *dup = strdup ( orig ); + ok ( dup != NULL ); + ok ( dup != orig ); + ok ( strcmp ( dup, orig ) == 0 ); + free ( dup ); + } + + /* Test strndup() */ + { + const char *normal = "testing testing"; + const char unterminated[6] = { 'h', 'e', 'l', 'l', 'o', '!' }; + char *dup; + dup = strndup ( normal, 32 ); + ok ( dup != NULL ); + ok ( dup != normal ); + ok ( strcmp ( dup, normal ) == 0 ); + free ( dup ); + dup = strndup ( normal, 4 ); + ok ( dup != NULL ); + ok ( strcmp ( dup, "test" ) == 0 ); + free ( dup ); + dup = strndup ( unterminated, 5 ); + ok ( dup != NULL ); + ok ( strcmp ( dup, "hello" ) == 0 ); + free ( dup ); + } } /** String self-test */ diff --git a/roms/ipxe/src/tests/x509_test.c b/roms/ipxe/src/tests/x509_test.c index d3e01fa..fd39e12 100644 --- a/roms/ipxe/src/tests/x509_test.c +++ b/roms/ipxe/src/tests/x509_test.c @@ -413,8 +413,8 @@ CERTIFICATE ( useless_crt, * issuer iPXE self-test leaf CA */ CERTIFICATE ( server_crt, - DATA ( 0x30, 0x82, 0x02, 0xba, 0x30, 0x82, 0x02, 0x23, 0xa0, 0x03, - 0x02, 0x01, 0x02, 0x02, 0x01, 0x18, 0x30, 0x0d, 0x06, 0x09, + DATA ( 0x30, 0x82, 0x02, 0xd2, 0x30, 0x82, 0x02, 0x3b, 0xa0, 0x03, + 0x02, 0x01, 0x02, 0x02, 0x01, 0x1e, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81, 0x88, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x47, 0x42, 0x31, 0x17, 0x30, @@ -431,9 +431,9 @@ CERTIFICATE ( server_crt, 0x58, 0x45, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x2d, 0x74, 0x65, 0x73, 0x74, 0x20, 0x6c, 0x65, 0x61, 0x66, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x32, 0x30, 0x33, 0x30, 0x35, - 0x31, 0x33, 0x34, 0x35, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x31, + 0x31, 0x33, 0x34, 0x35, 0x30, 0x35, 0x5a, 0x17, 0x0d, 0x31, 0x33, 0x30, 0x33, 0x30, 0x35, 0x31, 0x33, 0x34, 0x35, 0x30, - 0x30, 0x5a, 0x30, 0x81, 0x84, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x35, 0x5a, 0x30, 0x81, 0x84, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x47, 0x42, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0e, 0x43, 0x61, 0x6d, 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x73, 0x68, @@ -449,45 +449,47 @@ CERTIFICATE ( server_crt, 0x70, 0x78, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, - 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0x9d, 0x87, 0xe4, 0xa7, - 0xcf, 0x12, 0x08, 0x43, 0x4c, 0x90, 0x8b, 0x10, 0x7d, 0xcc, - 0x94, 0x1e, 0x5e, 0xef, 0xa7, 0x90, 0xbc, 0xe8, 0xe4, 0xee, - 0xd9, 0xb4, 0xd9, 0x63, 0x55, 0xc7, 0x03, 0x98, 0x42, 0xd7, - 0x4e, 0xaf, 0xd7, 0xdc, 0x40, 0x83, 0x61, 0x1b, 0xcc, 0x7b, - 0xf5, 0x1d, 0xba, 0x9f, 0x66, 0xfb, 0xe7, 0x42, 0xbd, 0xd7, - 0xac, 0xeb, 0x3c, 0xa2, 0x99, 0x6a, 0xe4, 0x8f, 0xb4, 0x06, - 0x4e, 0xc3, 0x3b, 0x62, 0xcd, 0x6a, 0x30, 0x0a, 0xe0, 0xb1, - 0x50, 0x83, 0x77, 0xc4, 0x97, 0x15, 0xc4, 0x7c, 0x40, 0xb8, - 0x60, 0x39, 0x07, 0x72, 0x4b, 0xd2, 0x61, 0x5c, 0xd0, 0xac, - 0x21, 0x9b, 0x85, 0xba, 0x53, 0x39, 0x1d, 0xef, 0xe9, 0xb7, - 0x69, 0xed, 0x7f, 0x1c, 0x38, 0x56, 0x0a, 0xe5, 0x24, 0xd0, - 0x1a, 0xa5, 0x9a, 0xd2, 0x5e, 0x1b, 0x47, 0x42, 0x49, 0x08, - 0x0d, 0x68, 0x2d, 0xc9, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, - 0x36, 0x30, 0x34, 0x30, 0x32, 0x06, 0x03, 0x55, 0x1d, 0x11, - 0x04, 0x2b, 0x30, 0x29, 0x82, 0x12, 0x64, 0x65, 0x6d, 0x6f, + 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xac, 0x7b, 0x54, 0xc1, + 0x97, 0x4d, 0x56, 0xbd, 0xb2, 0x52, 0xb3, 0x5c, 0x1b, 0x28, + 0xae, 0x91, 0x33, 0xf0, 0xc8, 0xc2, 0x3c, 0x7d, 0xe8, 0x95, + 0x72, 0xaf, 0xfe, 0xa1, 0x68, 0xe1, 0xbd, 0xe2, 0x9d, 0x4c, + 0xe8, 0x95, 0x56, 0x94, 0xce, 0x47, 0x57, 0x1b, 0xb1, 0x08, + 0xa1, 0x5b, 0x02, 0x8f, 0x56, 0x75, 0x1e, 0x4f, 0xfd, 0xc5, + 0x87, 0x5c, 0x1c, 0x3f, 0xab, 0x4f, 0xba, 0x25, 0x14, 0x6d, + 0xe3, 0xa2, 0x47, 0x33, 0xd0, 0x78, 0x63, 0xcc, 0x11, 0x37, + 0x08, 0x73, 0x25, 0x42, 0x20, 0xa9, 0x57, 0x29, 0xeb, 0x44, + 0x80, 0x0d, 0xe6, 0x76, 0x4b, 0x02, 0x8b, 0x67, 0xb2, 0x99, + 0xfe, 0xb3, 0x44, 0x62, 0xdf, 0x34, 0x0e, 0xf3, 0xe2, 0x17, + 0x42, 0x8f, 0x36, 0x42, 0x5a, 0x1c, 0x03, 0x3e, 0x06, 0x0d, + 0x5e, 0x08, 0x52, 0xd1, 0x06, 0xfb, 0xa9, 0xdb, 0x13, 0x15, + 0x08, 0x6d, 0x03, 0x85, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, + 0x4e, 0x30, 0x4c, 0x30, 0x4a, 0x06, 0x03, 0x55, 0x1d, 0x11, + 0x04, 0x43, 0x30, 0x41, 0x82, 0x12, 0x64, 0x65, 0x6d, 0x6f, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x69, 0x70, 0x78, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x82, 0x13, 0x2a, 0x2e, 0x61, 0x6c, 0x74, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x69, 0x70, 0x78, - 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x30, 0x0d, 0x06, 0x09, 0x2a, - 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, - 0x03, 0x81, 0x81, 0x00, 0x23, 0x16, 0x6a, 0x10, 0x55, 0x44, - 0xb9, 0x9d, 0x9f, 0x9f, 0x53, 0x51, 0x3d, 0x7d, 0x33, 0xa1, - 0x84, 0xb2, 0x5a, 0xfb, 0x1d, 0x76, 0xd5, 0xb1, 0x79, 0x66, - 0xf5, 0xe3, 0xa6, 0x58, 0x2e, 0x3d, 0xec, 0x9f, 0xcf, 0x7d, - 0x75, 0x3b, 0xd7, 0xe8, 0xf6, 0x96, 0xd7, 0xdd, 0x89, 0x1e, - 0x30, 0x25, 0xd9, 0xbb, 0xc0, 0x99, 0xc0, 0x1f, 0x1b, 0x4f, - 0xa6, 0x8e, 0xd5, 0x76, 0x50, 0x18, 0xa1, 0x7a, 0x48, 0x08, - 0xd5, 0x75, 0xee, 0x20, 0x82, 0x12, 0xc0, 0xe8, 0xeb, 0xf1, - 0x50, 0xee, 0x9d, 0xbd, 0x73, 0x7c, 0xb5, 0x13, 0x05, 0x91, - 0x1f, 0xc6, 0x50, 0x08, 0xbc, 0x98, 0xde, 0x43, 0x9a, 0xa4, - 0x9f, 0x69, 0xf7, 0x6e, 0x36, 0x20, 0x42, 0x80, 0x72, 0xba, - 0x0d, 0x63, 0x4c, 0xc5, 0x00, 0x0d, 0x85, 0xaa, 0x14, 0x38, - 0x28, 0x11, 0x3e, 0xa2, 0xcc, 0xc2, 0xac, 0xe8, 0xa7, 0xbe, - 0x0a, 0xa0 ), - FINGERPRINT ( 0x2f, 0xd3, 0xe0, 0x69, 0xde, 0xbc, 0x7c, 0x39, - 0xa7, 0xee, 0x23, 0x3b, 0xf5, 0x92, 0xf5, 0xbe, - 0x05, 0xab, 0xb5, 0xf8, 0x42, 0x9e, 0xf5, 0x9c, - 0x24, 0xde, 0x9e, 0x1f, 0xeb, 0xed, 0xd1, 0x20 ) ); + 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x87, 0x04, 0xc0, 0xa8, 0x00, + 0x01, 0x87, 0x10, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x69, 0xff, 0xfe, 0x50, 0x58, 0x45, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x63, 0x83, + 0xf5, 0xde, 0xf7, 0x59, 0x81, 0xd3, 0x34, 0x61, 0xfd, 0x2c, + 0x0c, 0xec, 0x1c, 0x25, 0xd2, 0x2c, 0xe8, 0x90, 0x4f, 0x34, + 0x43, 0x2c, 0x86, 0x18, 0x9e, 0x66, 0x26, 0x0d, 0x02, 0x2a, + 0xea, 0x28, 0xc6, 0xbb, 0x51, 0x02, 0xbe, 0x8f, 0x51, 0x50, + 0xc7, 0x04, 0x49, 0x97, 0xb9, 0xd4, 0xa5, 0x74, 0x39, 0xaa, + 0x22, 0xbb, 0x4e, 0x46, 0x57, 0x15, 0x0e, 0xcf, 0x64, 0x60, + 0xc8, 0x13, 0xdf, 0x82, 0x09, 0x3b, 0x92, 0xf5, 0x69, 0x80, + 0xd2, 0x5e, 0x53, 0x9d, 0x3a, 0xcd, 0x9e, 0x81, 0xa1, 0xbd, + 0x5b, 0x66, 0x89, 0x4d, 0xf7, 0xa4, 0xd6, 0x92, 0xe4, 0xe1, + 0x80, 0x87, 0xfa, 0xa5, 0x47, 0x25, 0x9c, 0x35, 0x77, 0xa5, + 0x11, 0x1b, 0x48, 0x4c, 0x5e, 0x5e, 0x2f, 0xc7, 0xf8, 0x78, + 0x4c, 0x36, 0x41, 0xfb, 0x91, 0x5d, 0xf6, 0x43, 0x99, 0x7c, + 0xcd, 0x7f, 0x27, 0x4c, 0x75, 0xca ), + FINGERPRINT ( 0x82, 0xd3, 0xa0, 0x4c, 0x0d, 0x7d, 0x3c, 0xb1, + 0x90, 0x63, 0xd8, 0xef, 0x1e, 0xd2, 0xdd, 0x10, + 0xd5, 0x89, 0x40, 0x35, 0xb9, 0x5e, 0x98, 0x44, + 0x30, 0xa2, 0x48, 0x9a, 0xb8, 0x2f, 0xcf, 0xe3 ) ); /* * subject not.a.ca.test.ipxe.org @@ -1033,6 +1035,11 @@ static void x509_test_exec ( void ) { x509_check_name_fail_ok ( &server_crt, "ipxe.org" ); x509_check_name_fail_ok ( &server_crt, "org" ); x509_check_name_fail_ok ( &server_crt, "" ); + x509_check_name_ok ( &server_crt, "192.168.0.1" ); + x509_check_name_fail_ok ( &server_crt, "192.168.0.2" ); + x509_check_name_ok ( &server_crt, "fe80::69ff:fe50:5845" ); + x509_check_name_ok ( &server_crt, "FE80:0:0:0:0:69FF:FE50:5845" ); + x509_check_name_fail_ok ( &server_crt, "fe80::69ff:fe50:5846" ); /* Parse all certificate chains */ x509_chain_ok ( &server_chain ); @@ -1101,3 +1108,5 @@ struct self_test x509_test __self_test = { REQUIRE_OBJECT ( rsa ); REQUIRE_OBJECT ( sha1 ); REQUIRE_OBJECT ( sha256 ); +REQUIRE_OBJECT ( ipv4 ); +REQUIRE_OBJECT ( ipv6 ); diff --git a/roms/ipxe/src/usr/autoboot.c b/roms/ipxe/src/usr/autoboot.c index af3d1f7..47476ae 100644 --- a/roms/ipxe/src/usr/autoboot.c +++ b/roms/ipxe/src/usr/autoboot.c @@ -49,8 +49,14 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ -/** Device location of preferred autoboot device */ -struct device_description autoboot_device; +/** Link-layer address of preferred autoboot device, if known */ +static uint8_t autoboot_ll_addr[MAX_LL_ADDR_LEN]; + +/** Device location of preferred autoboot device, if known */ +static struct device_description autoboot_desc; + +/** Autoboot device tester */ +static int ( * is_autoboot_device ) ( struct net_device *netdev ); /* Disambiguate the various error causes */ #define ENOENT_BOOT __einfo_error ( EINFO_ENOENT_BOOT ) @@ -422,15 +428,60 @@ int netboot ( struct net_device *netdev ) { } /** - * Test if network device matches the autoboot device location + * Test if network device matches the autoboot device bus type and location * * @v netdev Network device - * @ret is_autoboot Network device matches the autoboot device location + * @ret is_autoboot Network device matches the autoboot device + */ +static int is_autoboot_busloc ( struct net_device *netdev ) { + + return ( ( netdev->dev->desc.bus_type == autoboot_desc.bus_type ) && + ( netdev->dev->desc.location == autoboot_desc.location ) ); +} + +/** + * Identify autoboot device by bus type and location + * + * @v bus_type Bus type + * @v location Location */ -static int is_autoboot_device ( struct net_device *netdev ) { +void set_autoboot_busloc ( unsigned int bus_type, unsigned int location ) { + + /* Record autoboot device description */ + autoboot_desc.bus_type = bus_type; + autoboot_desc.location = location; + + /* Mark autoboot device as present */ + is_autoboot_device = is_autoboot_busloc; +} + +/** + * Test if network device matches the autoboot device link-layer address + * + * @v netdev Network device + * @ret is_autoboot Network device matches the autoboot device + */ +static int is_autoboot_ll_addr ( struct net_device *netdev ) { + + return ( memcmp ( netdev->ll_addr, autoboot_ll_addr, + netdev->ll_protocol->ll_addr_len ) == 0 ); +} + +/** + * Identify autoboot device by link-layer address + * + * @v ll_addr Link-layer address + * @v len Length of link-layer address + */ +void set_autoboot_ll_addr ( const void *ll_addr, size_t len ) { + + /* Record autoboot link-layer address (truncated if necessary) */ + if ( len > sizeof ( autoboot_ll_addr ) ) + len = sizeof ( autoboot_ll_addr ); + memcpy ( autoboot_ll_addr, ll_addr, len ); - return ( ( netdev->dev->desc.bus_type == autoboot_device.bus_type ) && - ( netdev->dev->desc.location == autoboot_device.location ) ); + /* Mark autoboot device as present */ + is_autoboot_device = is_autoboot_ll_addr; } /** @@ -447,8 +498,7 @@ static int autoboot ( void ) { for_each_netdev ( netdev ) { /* Skip any non-matching devices, if applicable */ - if ( autoboot_device.bus_type && - ( ! is_autoboot_device ( netdev ) ) ) + if ( is_autoboot_device && ( ! is_autoboot_device ( netdev ) ) ) continue; /* Attempt booting from this device */ @@ -499,10 +549,10 @@ void ipxe ( struct net_device *netdev ) { * do so. * */ - printf ( NORMAL "\n\n" PRODUCT_NAME "\n" BOLD "iPXE %s" + printf ( NORMAL "\n\n%s\n" BOLD "iPXE %s" NORMAL " -- Open Source Network Boot Firmware -- " CYAN "http://ipxe.org" NORMAL "\n" - "Features:", product_version ); + "Features:", product_name, product_version ); for_each_table_entry ( feature, FEATURES ) printf ( " %s", feature->name ); printf ( "\n" ); diff --git a/roms/ipxe/src/usr/ifmgmt.c b/roms/ipxe/src/usr/ifmgmt.c index cab1cd9..3d05895 100644 --- a/roms/ipxe/src/usr/ifmgmt.c +++ b/roms/ipxe/src/usr/ifmgmt.c @@ -28,7 +28,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include -#include #include #include @@ -142,9 +141,6 @@ struct ifpoller { static int ifpoller_progress ( struct ifpoller *ifpoller, struct job_progress *progress __unused ) { - /* Reduce CPU utilisation */ - cpu_nap(); - /* Hand off to current progress checker */ return ifpoller->progress ( ifpoller ); } diff --git a/roms/ipxe/src/usr/lotest.c b/roms/ipxe/src/usr/lotest.c index 9e2ac33..ad7a2fa 100644 --- a/roms/ipxe/src/usr/lotest.c +++ b/roms/ipxe/src/usr/lotest.c @@ -39,8 +39,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ -/** Loopback testing in progress flag */ -static int lotest_active; +/** Current loopback test receiver */ +static struct net_device *lotest_receiver; /** Loopback testing received packets */ static LIST_HEAD ( lotest_queue ); @@ -56,13 +56,13 @@ static LIST_HEAD ( lotest_queue ); * @ret rc Return status code */ static int lotest_rx ( struct io_buffer *iobuf, - struct net_device *netdev __unused, + struct net_device *netdev, const void *ll_dest __unused, const void *ll_source __unused, unsigned int flags __unused ) { /* Add to received packet queue if currently performing a test */ - if ( lotest_active ) { + if ( netdev == lotest_receiver ) { list_add_tail ( &iobuf->list, &lotest_queue ); } else { free_iob ( iobuf ); @@ -223,7 +223,7 @@ int loopback_test ( struct net_device *sender, struct net_device *receiver, /* Start loopback test */ lotest_flush(); - lotest_active = 1; + lotest_receiver = receiver; /* Perform loopback test */ for ( successes = 0 ; ; successes++ ) { @@ -261,7 +261,7 @@ int loopback_test ( struct net_device *sender, struct net_device *receiver, printf ( "\n"); /* Stop loopback testing */ - lotest_active = 0; + lotest_receiver = NULL; lotest_flush(); /* Dump final statistics */ diff --git a/roms/ipxe/src/usr/pingmgmt.c b/roms/ipxe/src/usr/pingmgmt.c index 2d4db49..16b3ec9 100644 --- a/roms/ipxe/src/usr/pingmgmt.c +++ b/roms/ipxe/src/usr/pingmgmt.c @@ -36,7 +36,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); /** * Display ping result * - * @v src Source socket address + * @v src Source socket address, or NULL * @v sequence Sequence number * @v len Payload length * @v rc Status code @@ -46,7 +46,7 @@ static void ping_callback ( struct sockaddr *peer, unsigned int sequence, /* Display ping response */ printf ( "%zd bytes from %s: seq=%d", - len, sock_ntoa ( peer ), sequence ); + len, ( peer ? sock_ntoa ( peer ) : "" ), sequence ); if ( rc != 0 ) printf ( ": %s", strerror ( rc ) ); printf ( "\n" ); @@ -58,21 +58,25 @@ static void ping_callback ( struct sockaddr *peer, unsigned int sequence, * @v hostname Hostname * @v timeout Timeout between pings, in ticks * @v len Payload length + * @v count Number of packets to send (or zero for no limit) + * @v quiet Inhibit output * @ret rc Return status code */ -int ping ( const char *hostname, unsigned long timeout, size_t len ) { +int ping ( const char *hostname, unsigned long timeout, size_t len, + unsigned int count, int quiet ) { int rc; /* Create pinger */ - if ( ( rc = create_pinger ( &monojob, hostname, timeout, - len, ping_callback ) ) != 0 ) { + if ( ( rc = create_pinger ( &monojob, hostname, timeout, len, count, + ( quiet ? NULL : ping_callback ) ) ) != 0 ){ printf ( "Could not start ping: %s\n", strerror ( rc ) ); return rc; } /* Wait for ping to complete */ if ( ( rc = monojob_wait ( NULL, 0 ) ) != 0 ) { - printf ( "Finished: %s\n", strerror ( rc ) ); + if ( ! quiet ) + printf ( "Finished: %s\n", strerror ( rc ) ); return rc; } diff --git a/roms/ipxe/src/util/.gitignore b/roms/ipxe/src/util/.gitignore index 633ca32..33bedef 100644 --- a/roms/ipxe/src/util/.gitignore +++ b/roms/ipxe/src/util/.gitignore @@ -5,5 +5,6 @@ prototester elf2efi32 elf2efi64 efirom +efifatbin iccfix einfo diff --git a/roms/ipxe/src/util/Option/ROM.pm b/roms/ipxe/src/util/Option/ROM.pm index fb37ce4..6c39673 100644 --- a/roms/ipxe/src/util/Option/ROM.pm +++ b/roms/ipxe/src/util/Option/ROM.pm @@ -266,11 +266,10 @@ sub set { # Split out any data belonging to the next image delete $self->{next_image}; - my $length = ( $hash->{length} * 512 ); my $pci_header = $hash->pci_header(); - if ( ( $length < length $data ) && - ( defined $pci_header ) && + if ( ( defined $pci_header ) && ( ! ( $pci_header->{last_image} & PCI_LAST_IMAGE ) ) ) { + my $length = ( $pci_header->{image_length} * 512 ); my $remainder = substr ( $data, $length ); $data = substr ( $data, 0, $length ); $self->{next_image} = new Option::ROM; diff --git a/roms/ipxe/src/util/efifatbin.c b/roms/ipxe/src/util/efifatbin.c new file mode 100644 index 0000000..c02f750 --- /dev/null +++ b/roms/ipxe/src/util/efifatbin.c @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2014 Michael Brown . + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define eprintf(...) fprintf ( stderr, __VA_ARGS__ ) + +/** Command-line options */ +struct options { +}; + +/** EFI fat binary file header */ +struct efifatbin_file_header { + /** Signature */ + uint32_t signature; + /** Count */ + uint32_t count; +} __attribute__ (( packed )); + +/** EFI fat binary signature */ +#define EFIFATBIN_SIGNATURE 0x0ef1fab9 + +/** EFI fat binary image header */ +struct efifatbin_image_header { + /** Flags */ + uint64_t flags; + /** Offset */ + uint32_t offset; + /** Length */ + uint32_t len; + /** Padding */ + uint32_t pad; +} __attribute__ (( packed )); + +/** EFI fat binary default flags */ +#define EFIFATBIN_FLAGS 0x0000000300000007ULL + +/** EFI fat binary 64-bit flag */ +#define EFIFATBIN_64BIT 0x0000000001000000ULL + +/** + * Allocate memory + * + * @v len Length of memory to allocate + * @ret ptr Pointer to allocated memory + */ +static void * xmalloc ( size_t len ) { + void *ptr; + + ptr = malloc ( len ); + if ( ! ptr ) { + eprintf ( "Could not allocate %zd bytes\n", len ); + exit ( 1 ); + } + + return ptr; +} + +/** + * Generate EFI fat binary + * + * @v count Number of input files + * @v infile_names Input filenames + * @v outfile_name Output filename + */ +static void make_efifatbin ( unsigned int count, char **infile_names, + const char *outfile_name ) { + FILE *infile[count]; + FILE *outfile; + struct stat stat[count]; + void *buf[count]; + struct efifatbin_file_header file_header; + struct efifatbin_image_header header[count]; + size_t offset; + EFI_IMAGE_DOS_HEADER *dos; + union { + EFI_IMAGE_NT_HEADERS32 nt32; + EFI_IMAGE_NT_HEADERS64 nt64; + } *nt; + unsigned int i; + + /* Generate file header */ + file_header.signature = EFIFATBIN_SIGNATURE; + file_header.count = count; + offset = ( sizeof ( file_header ) + sizeof ( header ) ); + + /* Process input files */ + for ( i = 0 ; i < count ; i++ ) { + + /* Open input file */ + infile[i] = fopen ( infile_names[i], "r" ); + if ( ! infile[i] ) { + eprintf ( "Could not open %s for reading: %s\n", + infile_names[i], strerror ( errno ) ); + exit ( 1 ); + } + + /* Determine PE file size */ + if ( fstat ( fileno ( infile[i] ), &stat[i] ) != 0 ) { + eprintf ( "Could not stat %s: %s\n", + infile_names[i], strerror ( errno ) ); + exit ( 1 ); + } + + /* Allocate buffer and read in PE file */ + buf[i] = xmalloc ( stat[i].st_size ); + if ( fread ( buf[i], stat[i].st_size, 1, infile[i] ) != 1 ) { + eprintf ( "Could not read %s: %s\n", + infile_names[i], strerror ( errno ) ); + exit ( 1 ); + } + + /* Close input file */ + fclose ( infile[i] ); + + /* Generate image header */ + header[i].flags = EFIFATBIN_FLAGS; + header[i].offset = offset; + header[i].len = stat[i].st_size; + header[i].pad = 0; + + /* Determine architecture */ + dos = buf[i]; + nt = ( buf[i] + dos->e_lfanew ); + if ( nt->nt32.FileHeader.Machine == EFI_IMAGE_MACHINE_X64 ) + header[i].flags |= EFIFATBIN_64BIT; + + /* Allow space for this image */ + offset += stat[i].st_size; + } + + /* Open output file */ + outfile = fopen ( outfile_name, "w" ); + if ( ! outfile ) { + eprintf ( "Could not open %s for writing: %s\n", + outfile_name, strerror ( errno ) ); + exit ( 1 ); + } + + /* Write fat binary header */ + if ( fwrite ( &file_header, sizeof ( file_header ), 1, outfile ) != 1 ){ + eprintf ( "Could not write %s: %s\n", + outfile_name, strerror ( errno ) ); + exit ( 1 ); + } + for ( i = 0 ; i < count ; i++ ) { + if ( fwrite ( &header[i], sizeof ( header[i] ), 1, + outfile ) != 1 ) { + eprintf ( "Could not write %s: %s\n", + outfile_name, strerror ( errno ) ); + exit ( 1 ); + } + } + + /* Write images */ + for ( i = 0 ; i < count ; i++ ) { + if ( fwrite ( buf[i], stat[i].st_size, 1, outfile ) != 1 ) { + eprintf ( "Could not write %s: %s\n", + outfile_name, strerror ( errno ) ); + exit ( 1 ); + } + } + + /* Close output file */ + fclose ( outfile ); +} + +/** + * Print help + * + * @v program_name Program name + */ +static void print_help ( const char *program_name ) { + eprintf ( "Syntax: %s infile [infile...] outfile\n", program_name ); +} + +/** + * Parse command-line options + * + * @v argc Argument count + * @v argv Argument list + * @v opts Options structure to populate + */ +static int parse_options ( const int argc, char **argv, + struct options *opts __attribute__ (( unused )) ) { + int c; + + while (1) { + int option_index = 0; + static struct option long_options[] = { + { "help", 0, NULL, 'h' }, + { 0, 0, 0, 0 } + }; + + if ( ( c = getopt_long ( argc, argv, "h", + long_options, + &option_index ) ) == -1 ) { + break; + } + + switch ( c ) { + case 'h': + print_help ( argv[0] ); + exit ( 0 ); + case '?': + default: + exit ( 2 ); + } + } + return optind; +} + +int main ( int argc, char **argv ) { + struct options opts; + int infile_index; + int outfile_index; + int count; + + /* Parse command-line arguments */ + memset ( &opts, 0, sizeof ( opts ) ); + infile_index = parse_options ( argc, argv, &opts ); + outfile_index = ( argc - 1 ); + count = ( outfile_index - infile_index ); + if ( count <= 0 ) { + print_help ( argv[0] ); + exit ( 2 ); + } + + /* Generate fat binary */ + make_efifatbin ( count, &argv[infile_index], argv[outfile_index] ); + + return 0; +} diff --git a/roms/ipxe/src/util/geniso b/roms/ipxe/src/util/geniso index 4dc7219..521c929 100755 --- a/roms/ipxe/src/util/geniso +++ b/roms/ipxe/src/util/geniso @@ -1,80 +1,142 @@ #!/bin/bash # # Generate a isolinux ISO boot image -# -# geniso foo.iso foo.lkrn -# -# the ISO image is the first argument so that a list of .lkrn images -# to include can be specified -# -case $# in -0|1) - echo Usage: $0 foo.iso foo.lkrn ... - exit 1 - ;; -esac -# This should be the default location of the isolinux.bin file -isolinux_bin=${ISOLINUX_BIN:-util/isolinux.bin} -if [ ! -r $isolinux_bin ] -then - echo $0: $isolinux_bin not found, please install, or set ISOLINUX_BIN in arch/i386/Makefile correctly +function help() { + echo "usage: ${0} [OPTIONS] foo.lkrn [bar.lkrn,...]" + echo + echo "where OPTIONS are:" + echo " -h show this help" + echo " -l build legacy image with floppy emulation" + echo " -o FILE save iso image to file" +} + +LEGACY=0 +FIRST="" + +while getopts "hlo:" opt; do + case ${opt} in + h) + help + exit 0 + ;; + l) + LEGACY=1 + ;; + o) + OUT="${OPTARG}" + ;; + esac +done + +shift $((OPTIND - 1)) + +if [ -z "${OUT}" ]; then + echo "${0}: no output file given" >&2 + help exit 1 fi # There should either be mkisofs or the compatible genisoimage program -mkisofs=`which mkisofs genisoimage 2>/dev/null | head -n1` -if [ -z $mkisofs ] -then - echo $0: mkisofs or genisoimage not found, please install or set PATH +for command in genisoimage mkisofs; do + if ${command} --version >/dev/null 2>/dev/null; then + mkisofs=(${command}) + break + fi +done + +if [ -z "${mkisofs}" ]; then + echo "${0}: mkisofs or genisoimage not found, please install or set PATH" >&2 exit 1 fi -# isohybrid will be used if available -isohybrid=`which isohybrid 2>/dev/null` +dir=$(mktemp -d bin/iso.dir.XXXXXX) +cfg=${dir}/isolinux.cfg -out=$1 -shift -dir=`mktemp -d bin/iso.dir.XXXXXX` -cfg=$dir/isolinux.cfg -cp -p $isolinux_bin $dir +mkisofs+=(-quiet -l -volid "iPXE" -preparer "iPXE build system" + -appid "iPXE ${VERSION} - Open Source Network Boot Firmware" + -publisher "http://ipxe.org/" -c boot.cat) -# syslinux 6.x needs a file called ldlinux.c32 -ldlinux_c32=$(dirname ${isolinux_bin})/ldlinux.c32 -if [ -s ${ldlinux_c32} ] -then - cp -p ${ldlinux_c32} ${dir} -fi - -cat > $cfg < ${cfg} <&2 +for f; do + if [ ! -r ${f} ]; then + echo "${f} does not exist, skipping" >&2 continue fi - b=$(basename $f) + b=$(basename ${f}) g=${b%.lkrn} - g=${g//[^a-z0-9]}.krn - case "$first" in - "") - echo DEFAULT $b - ;; + g=${g//[^a-z0-9]} + g=${g:0:8}.krn + case "${FIRST}" in + "") + echo "DEFAULT ${b}" + FIRST=${g} + ;; esac - first=$g - echo LABEL $b - echo "" KERNEL $g - cp -p $f $dir/$g -done >> $cfg -$mkisofs -quiet -l -o $out -c boot.cat -b isolinux.bin -no-emul-boot -boot-load-size 4 -boot-info-table $dir -rm -fr $dir -if [ -n "$isohybrid" ] -then - $isohybrid $out >/dev/null -fi + echo "LABEL ${b}" + echo " KERNEL ${g}" + cp ${f} ${dir}/${g} +done >> ${cfg} + +case "${LEGACY}" in + 1) + # check for mtools + case "$(mtools -V)" in + Mtools\ version\ 3.9.9*|Mtools\ version\ 3.9.1[0-9]*|[mM]tools\ *\ [4-9].*) + ;; + *) + echo "Mtools version 3.9.9 or later is required" >&2 + exit 1 + ;; + esac + + # generate floppy image + img=${dir}/boot.img + mformat -f 1440 -C -i ${img} :: + + # copy lkrn file to floppy image + for f in ${dir}/*.krn; do + mcopy -m -i ${img} ${f} ::$(basename ${g}) + rm -f ${f} + done + + # copy config file to floppy image + mcopy -i ${img} ${cfg} ::syslinux.cfg + rm -f ${cfg} + + # write syslinux bootloader to floppy image + if ! syslinux ${img}; then + echo "${0}: failed writing syslinux to floppy image ${img}" >&2 + exit 1 + fi + + # generate the iso image + "${mkisofs[@]}" -b boot.img -output ${OUT} ${dir} + ;; + 0) + # copy isolinux bootloader + cp ${ISOLINUX_BIN} ${dir} + + # syslinux 6.x needs a file called ldlinux.c32 + LDLINUX_C32=$(dirname ${ISOLINUX_BIN})/ldlinux.c32 + if [ -s ${LDLINUX_C32} ]; then + cp ${LDLINUX_C32} ${dir} + fi + + # generate the iso image + "${mkisofs[@]}" -b isolinux.bin -no-emul-boot -boot-load-size 4 -boot-info-table -output ${OUT} ${dir} + + # isohybrid will be used if available + if isohybrid --version >/dev/null 2>/dev/null; then + isohybrid ${OUT} >/dev/null + fi + ;; +esac + +# clean up temporary dir +rm -fr ${dir} diff --git a/roms/ipxe/src/util/genliso b/roms/ipxe/src/util/genliso deleted file mode 100755 index 7a112a1..0000000 --- a/roms/ipxe/src/util/genliso +++ /dev/null @@ -1,74 +0,0 @@ -#!/bin/bash -# -# Generate a legacy floppy emulation ISO boot image -# -# genliso foo.liso foo.lkrn bar.lkrn ... -# -# The .liso image filename is the first argument followed by -# a list of .lkrn images include in .liso image - -case $# in -0|1) - echo Usage: $0 foo.liso foo.lkrn ... - exit 1 - ;; -esac - -case "`mtools -V`" in -Mtools\ version\ 3.9.9*|Mtools\ version\ 3.9.1[0-9]*|[mM]tools\ *\ [4-9].*) - ;; -*) - echo Mtools version 3.9.9 or later is required - exit 1 - ;; -esac - -out=$1 -shift - -dir=`mktemp -d bin/liso.dir.XXXXXX` - -img=$dir/boot.img -mformat -f 1440 -C -i $img :: - -cfg=$dir/syslinux.cfg -cat > $cfg <&2 - continue - fi - # shorten name for 8.3 filesystem - b=$(basename $f) - g=${b%.lkrn} - g=${g//[^a-z0-9]} - g=${g:0:8}.krn - case "$first" in - "") - echo DEFAULT $g - ;; - esac - first=$g - echo LABEL $g - echo "" KERNEL $g - mcopy -m -i $img $f ::$g -done >> $cfg - -mcopy -i $img $cfg ::syslinux.cfg - -if ! syslinux $img -then - exit 1 -fi - -mkisofs -q -o $out -c boot.cat -b boot.img $dir - -rm -fr $dir diff --git a/roms/openbios/arch/sparc32/boot.c b/roms/openbios/arch/sparc32/boot.c index c309891..49ec4cf 100644 --- a/roms/openbios/arch/sparc32/boot.c +++ b/roms/openbios/arch/sparc32/boot.c @@ -147,9 +147,6 @@ static void setup_romvec(void) (**pp).start_adr = (char *)intprop_ptr[1]; (**pp).num_bytes = intprop_ptr[2]; - printk("start_adr: %x\n", (int)(**pp).start_adr); - printk("num_bytes: %x\n", (**pp).num_bytes); - intprop_ptr += 3; } @@ -180,7 +177,7 @@ static void setup_romvec(void) (**pp).num_bytes = (intprop_ptr[4] + intprop_ptr[5]) - (intprop_ptr[1] + intprop_ptr[2]); } else { /* Tail (size from top of virtual memory) */ - (**pp).num_bytes = 0xffffffffUL - (intprop_ptr[1] + intprop_ptr[2]); + (**pp).num_bytes = 0xffffffffUL - (intprop_ptr[1] + intprop_ptr[2]) + 1; } intprop_ptr += 3; diff --git a/roms/openbios/arch/sparc32/entry.S b/roms/openbios/arch/sparc32/entry.S index d95a308..72cb338 100644 --- a/roms/openbios/arch/sparc32/entry.S +++ b/roms/openbios/arch/sparc32/entry.S @@ -122,7 +122,44 @@ entry: or %g3, %g4, %g1 ! %g1 contains end of memory + ! Get kernel address from configuration device + ! NB: little endian format + mov FW_CFG_KERNEL_ADDR, %g2 + sub %g5, 2, %g5 + stha %g2, [%g5] CFG_ASI + add %g5, 2, %g5 + lduba [%g5] CFG_ASI, %g4 + + lduba [%g5] CFG_ASI, %g3 + sll %g3, 8, %g3 + or %g3, %g4, %g4 + + lduba [%g5] CFG_ASI, %g3 + sll %g3, 16, %g3 + or %g3, %g4, %g4 + + lduba [%g5] CFG_ASI, %g3 + sll %g3, 24, %g3 + or %g3, %g4, %g4 + + ! If kernel address is set, don't clear from base of RAM in order to + ! leave the kernel image intact + mov 0, %g6 + cmp %g4, 0 + beq clear_mem + nop + + ! Start from 16M + set 0x1000000, %g6 + +clear_mem: + sta %g0, [%g6] ASI_M_BYPASS + add %g6, 0x4, %g6 + cmp %g6, %g1 + bl clear_mem + nop +clear_done: ! Start of private memory in %g6 set 0x2000, %g3 sub %g1, %g3, %g6 diff --git a/roms/openbios/arch/sparc32/lib.c b/roms/openbios/arch/sparc32/lib.c index af1812d..d27b604 100644 --- a/roms/openbios/arch/sparc32/lib.c +++ b/roms/openbios/arch/sparc32/lib.c @@ -367,7 +367,7 @@ init_mmu_swift(void) for (i = 1; i < NCTX_SWIFT; i++) { context_table[i] = SRMMU_ET_INVALID; } - for (i = 0; i < 256; i += 4) { + for (i = 0; i < 256; i++) { l1[i] = SRMMU_ET_INVALID; } diff --git a/roms/openbios/arch/sparc32/openbios.c b/roms/openbios/arch/sparc32/openbios.c index 32255b6..6f4ee45 100644 --- a/roms/openbios/arch/sparc32/openbios.c +++ b/roms/openbios/arch/sparc32/openbios.c @@ -865,9 +865,14 @@ arch_init( void ) romvec = init_openprom(); kernel_size = fw_cfg_read_i32(FW_CFG_KERNEL_SIZE); - if (kernel_size) + if (kernel_size) { kernel_image = fw_cfg_read_i32(FW_CFG_KERNEL_ADDR); + /* Mark the kernel memory as in use */ + ofmem_claim_phys(PAGE_ALIGN(kernel_image), PAGE_ALIGN(kernel_size), 0); + ofmem_claim_virt(PAGE_ALIGN(kernel_image), PAGE_ALIGN(kernel_size), 0); + } + kernel_cmdline = (const char *) fw_cfg_read_i32(FW_CFG_KERNEL_CMDLINE); if (kernel_cmdline) { cmdline = strdup(kernel_cmdline); diff --git a/roms/openbios/arch/sparc64/openbios.c b/roms/openbios/arch/sparc64/openbios.c index 3a36146..4557f7f 100644 --- a/roms/openbios/arch/sparc64/openbios.c +++ b/roms/openbios/arch/sparc64/openbios.c @@ -27,21 +27,18 @@ #define UUID_FMT "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x" -#define NVRAM_ADDR_LO 0x74 -#define NVRAM_ADDR_HI 0x75 -#define NVRAM_DATA 0x77 - #define APB_SPECIAL_BASE 0x1fe00000000ULL #define APB_MEM_BASE 0x1ff00000000ULL #define MEMORY_SIZE (512*1024) /* 512K ram for hosted system */ // XXX +#define NVRAM_BASE 0x2000 #define NVRAM_SIZE 0x2000 #define NVRAM_IDPROM 0x1fd8 #define NVRAM_IDPROM_SIZE 32 #define NVRAM_OB_START (0) -#define NVRAM_OB_SIZE ((0x1fd0 - NVRAM_OB_START) & ~15) +#define NVRAM_OB_SIZE ((NVRAM_IDPROM - NVRAM_OB_START) & ~15) static uint8_t idprom[NVRAM_IDPROM_SIZE]; @@ -352,34 +349,22 @@ id_cpu(void) for (;;); } -static uint8_t nvram_read_byte(uint16_t offset) -{ - outb(offset & 0xff, NVRAM_ADDR_LO); - outb(offset >> 8, NVRAM_ADDR_HI); - return inb(NVRAM_DATA); -} - static void nvram_read(uint16_t offset, char *buf, unsigned int nbytes) { unsigned int i; - for (i = 0; i < nbytes; i++) - buf[i] = nvram_read_byte(offset + i); -} - -static void nvram_write_byte(uint16_t offset, uint8_t val) -{ - outb(offset & 0xff, NVRAM_ADDR_LO); - outb(offset >> 8, NVRAM_ADDR_HI); - outb(val, NVRAM_DATA); + for (i = 0; i < nbytes; i++) { + buf[i] = inb(NVRAM_BASE + offset + i); + } } static void nvram_write(uint16_t offset, const char *buf, unsigned int nbytes) { unsigned int i; - for (i = 0; i < nbytes; i++) - nvram_write_byte(offset + i, buf[i]); + for (i = 0; i < nbytes; i++) { + outb(buf[i], NVRAM_BASE + offset + i); + } } static uint8_t qemu_uuid[16]; diff --git a/roms/openbios/drivers/pci.c b/roms/openbios/drivers/pci.c index 3260354..366f4a1 100644 --- a/roms/openbios/drivers/pci.c +++ b/roms/openbios/drivers/pci.c @@ -831,6 +831,26 @@ int ebus_config_cb(const pci_config_t *config) set_property(dev, "ranges", (char *)props, ncells * sizeof(props[0])); + /* Build eeprom node */ + fword("new-device"); + PUSH(0x14); + fword("encode-int"); + PUSH(0x2000); + fword("encode-int"); + fword("encode+"); + PUSH(0x2000); + fword("encode-int"); + fword("encode+"); + push_str("reg"); + fword("property"); + + push_str("mk48t59"); + fword("model"); + + push_str("eeprom"); + fword("device-name"); + fword("finish-device"); + #ifdef CONFIG_DRIVER_FLOPPY ob_floppy_init(config->path, "fdthree", 0x3f0ULL, 0); #endif diff --git a/roms/openbios/forth/device/package.fs b/roms/openbios/forth/device/package.fs index eb17551..d5b52c3 100644 --- a/roms/openbios/forth/device/package.fs +++ b/roms/openbios/forth/device/package.fs @@ -49,6 +49,10 @@ ; : child ( phandle.parent -- phandle.child ) + \ Assume phandle == 0 indicates root node (not documented but similar + \ behaviour to "peer"). Used by some versions of Solaris (e.g. 9). + ?dup if else device-tree @ then + >dn.child @ ; diff --git a/roms/seabios/.version b/roms/seabios/.version index 0bd308b..98a67e1 100644 --- a/roms/seabios/.version +++ b/roms/seabios/.version @@ -1 +1 @@ -rel-1.7.5.1-0-g8936dbb +rel-1.8.1-0-g4adadbd diff --git a/roms/seabios/Makefile b/roms/seabios/Makefile index 7c2b33c..a84a5f7 100644 --- a/roms/seabios/Makefile +++ b/roms/seabios/Makefile @@ -12,6 +12,7 @@ export HOSTCC := $(CC) export CONFIG_SHELL := sh export KCONFIG_AUTOHEADER := autoconf.h export KCONFIG_CONFIG := $(CURDIR)/.config +export LC_ALL := C CROSS_PREFIX= ifneq ($(CROSS_PREFIX),) CC=$(CROSS_PREFIX)gcc @@ -27,21 +28,20 @@ IASL:=iasl LD32BIT_FLAG:=-melf_i386 # Source files -SRCBOTH=misc.c stacks.c output.c string.c x86.c block.c cdrom.c mouse.c kbd.c \ - serial.c clock.c resume.c pnpbios.c vgahooks.c pcibios.c apm.c \ - fw/smp.c \ +SRCBOTH=misc.c stacks.c output.c string.c block.c cdrom.c disk.c mouse.c kbd.c \ + system.c serial.c clock.c resume.c pnpbios.c vgahooks.c pcibios.c apm.c \ hw/pci.c hw/timer.c hw/rtc.c hw/dma.c hw/pic.c hw/ps2port.c hw/serialio.c \ hw/usb.c hw/usb-uhci.c hw/usb-ohci.c hw/usb-ehci.c \ hw/usb-hid.c hw/usb-msc.c hw/usb-uas.c \ hw/blockcmd.c hw/floppy.c hw/ata.c hw/ramdisk.c \ hw/virtio-ring.c hw/virtio-pci.c hw/virtio-blk.c hw/virtio-scsi.c \ hw/lsi-scsi.c hw/esp-scsi.c hw/megasas.c -SRC16=$(SRCBOTH) system.c disk.c font.c -SRC32FLAT=$(SRCBOTH) post.c memmap.c malloc.c pmm.c romfile.c optionroms.c \ - boot.c bootsplash.c jpeg.c bmp.c \ - hw/ahci.c hw/pvscsi.c hw/usb-xhci.c hw/usb-hub.c \ +SRC16=$(SRCBOTH) +SRC32FLAT=$(SRCBOTH) post.c memmap.c malloc.c romfile.c x86.c optionroms.c \ + pmm.c font.c boot.c bootsplash.c jpeg.c bmp.c \ + hw/ahci.c hw/pvscsi.c hw/usb-xhci.c hw/usb-hub.c hw/sdcard.c \ fw/coreboot.c fw/lzmadecode.c fw/csm.c fw/biostables.c \ - fw/paravirt.c fw/shadow.c fw/pciinit.c fw/smm.c fw/mtrr.c fw/xen.c \ + fw/paravirt.c fw/shadow.c fw/pciinit.c fw/smm.c fw/smp.c fw/mtrr.c fw/xen.c \ fw/acpi.c fw/mptable.c fw/pirtable.c fw/smbios.c fw/romfile_loader.c SRC32SEG=string.c output.c pcibios.c apm.c stacks.c hw/pci.c hw/serialio.c DIRS=src src/hw src/fw vgasrc @@ -58,17 +58,19 @@ COMMONCFLAGS := -I$(OUT) -Isrc -Os -MD -g \ -m32 -march=i386 -mregparm=3 -mpreferred-stack-boundary=2 \ -minline-all-stringops \ -freg-struct-return -ffreestanding -fno-delete-null-pointer-checks \ - -ffunction-sections -fdata-sections -fno-common + -ffunction-sections -fdata-sections -fno-common -fno-merge-constants COMMONCFLAGS += $(call cc-option,$(CC),-nopie,) COMMONCFLAGS += $(call cc-option,$(CC),-fno-stack-protector,) COMMONCFLAGS += $(call cc-option,$(CC),-fno-stack-protector-all,) +COMMA := , CFLAGS32FLAT := $(COMMONCFLAGS) -DMODE16=0 -DMODESEGMENT=0 -fomit-frame-pointer CFLAGSSEG := $(COMMONCFLAGS) -DMODESEGMENT=1 -fno-defer-pop \ $(call cc-option,$(CC),-fno-jump-tables,-DMANUAL_NO_JUMP_TABLE) \ $(call cc-option,$(CC),-fno-tree-switch-conversion,) CFLAGS32SEG := $(CFLAGSSEG) -DMODE16=0 -fomit-frame-pointer -CFLAGS16INC := $(CFLAGSSEG) -DMODE16=1 -Wa,src/code16gcc.s \ +CFLAGS16INC := $(CFLAGSSEG) -DMODE16=1 \ + $(call cc-option,$(CC),-m16,-Wa$(COMMA)src/code16gcc.s) \ $(call cc-option,$(CC),--param large-stack-frame=4,-fno-inline) CFLAGS16 := $(CFLAGS16INC) -fomit-frame-pointer diff --git a/roms/seabios/README b/roms/seabios/README index 1e61e2f..e18be16 100644 --- a/roms/seabios/README +++ b/roms/seabios/README @@ -1,182 +1,17 @@ -This code implements an X86 legacy bios. It is intended to be -compiled using standard gnu tools (eg, gas and gcc). +Welcome to the SeaBIOS project! This project implements an X86 legacy +bios that is built with standard GNU tools. -To build for QEMU, one should be able to run "make" in the main -directory. The resulting file "out/bios.bin" contains the processed -bios image. To build for coreboot, please see the coreboot wiki. To -build for CSM, please see README.CSM. +Please see build and developer information at: + http://seabios.org/Developer_Documentation -Testing of images: +For the impatient, SeaBIOS is built for QEMU and tested on QEMU with: -To test the bios under bochs, one will need to instruct bochs to use -the new bios image. Use the 'romimage' option - for example: + make + qemu -bios out/bios.bin -bochs -q 'floppya: 1_44=myfdimage.img' 'romimage: file=out/bios.bin' +SeaBIOS can be configured with kconfig. To change the default +configuration one can run "make menuconfig" prior to running "make". -To test under qemu, one will need to create a directory with all the -bios images and then overwrite the main bios image. For example: - -cp /usr/share/qemu/*.bin mybiosdir/ -cp out/bios.bin mybiosdir/ -cp out/*.aml mybiosdir/ - -Once this is setup, one can instruct qemu to use the newly created -directory for rom images. For example: - -qemu -L mybiosdir/ -fda myfdimage.img - - -Overview of files: - -The src/ directory contains the bios source code. Several of the -files are compiled twice - once for 16bit mode and once for 32bit -mode. (The build system will remove code that is not needed for a -particular mode.) - -The vgasrc/ directory contains code for VGA BIOS implementations. -This code is separate from the main BIOS code in the src/ directory. -It produces a VGA BIOS rom in out/vgabios.bin. The VGA BIOS code is -always compiled in 16bit mode. - -The scripts/ directory contains helper utilities for manipulating and -building the final rom. - -The out/ directory is created by the build process - it contains all -temporary and final files. - - -Build overview: - -The 16bit code is compiled via gcc to assembler (file out/ccode.16.s). -The gcc "-fwhole-program" and "-ffunction-sections -fdata-sections" -options are used to optimize the process so that gcc can efficiently -compile and discard unneeded code. (In the code, one can use the -macros 'VISIBLE16' and 'VISIBLE32FLAT' to instruct a symbol to be -outputted in 16bit and 32bit mode respectively.) - -This resulting assembler code is pulled into romlayout.S. The gas -option ".code16gcc" is used prior to including the gcc generated -assembler - this option enables gcc to generate valid 16 bit code. - -The post code (post.c) is entered, via the function handle_post(), in -32bit mode. The 16bit post vector (in romlayout.S) transitions the -cpu into 32 bit mode before calling the post.c code. - -In the last step of compilation, the 32 bit code is merged into the 16 -bit code so that one binary file contains both. Currently, both 16bit -and 32bit code will be located in the memory at 0xe0000-0xfffff. - - -GCC 16 bit limitations: - -Although the 16bit code is compiled with gcc, developers need to be -aware of the environment. In particular, global variables _must_ be -treated specially. - -The code has full access to stack variables and general purpose -registers. The entry code in romlayout.S will push the original -registers on the stack before calling the C code and then pop them off -(including any required changes) before returning from the interrupt. -Changes to CS, DS, and ES segment registers in C code is also safe. -Changes to other segment registers (SS, FS, GS) need to be restored -manually. - -Stack variables (and pointers to stack variables) work as they -normally do in standard C code. - -However, variables stored outside the stack need to be accessed via -the GET_VAR and SET_VAR macros (or one of the helper macros described -below). This is due to the 16bit segment nature of the X86 cpu when -it is in "real mode". The C entry code will set DS and SS to point to -the stack segment. Variables not on the stack need to be accessed via -an explicit segment register. Any other access requires altering one -of the other segment registers (usually ES) and then accessing the -variable via that segment register. - -There are three low-level ways to access a remote variable: -GET/SET_VAR, GET/SET_FARVAR, and GET/SET_FLATPTR. The first set takes -an explicit segment descriptor (eg, "CS") and offset. The second set -will take a segment id and offset, set ES to the segment id, and then -make the access via the ES segment. The last method is similar to the -second, except it takes a pointer that would be valid in 32-bit flat -mode instead of a segment/offset pair. - -Most BIOS variables are stored in global variables, the "BDA", or -"EBDA" memory areas. Because this is common, three sets of helper -macros (GET_GLOBAL, GET/SET_BDA, and GET/SET_EBDA) are available to -simplify these accesses. Also, an area in the 0xc0000-0xf0000 memory -range is made available for internal BIOS run-time variables that are -marked with the VARLOW attribute. These variables can then be -accessed with the GET/SET_LOW macros. - -Global variables defined in the C code can be read in 16bit mode if -the variable declaration is marked with VAR16, VARFSEG, or VAR16FIXED. -The GET_GLOBAL macro will then allow read access to the variable. -Global variables are stored in the 0xf000 segment. Because the -f-segment is marked read-only during run-time, the 16bit code is not -permitted to change the value of 16bit variables. Code running in -32bit mode can not access variables with VAR16, but can access -variables marked with VARFSEG, VARLOW, VAR16FIXED, or with no marking -at all. The 32bit code can use the GET_GLOBAL macros, but they are -not required. - - -GCC 16 bit stack limitations: - -Another limitation of gcc is its use of 32-bit temporaries. Gcc will -allocate 32-bits of space for every variable - even if that variable -is only defined as a 'u8' or 'u16'. If one is not careful, using too -much stack space can break old DOS applications. - -There does not appear to be explicit documentation on the minimum -stack space available for bios calls. However, Freedos has been -observed to call into the bios with less than 150 bytes available. - -Note that the post code and boot code (irq 18/19) do not have a stack -limitation because the entry points for these functions transition the -cpu to 32bit mode and reset the stack to a known state. Only the -general purpose 16-bit service entry points are affected. - -There are some ways to reduce stack usage: making sure functions are -tail-recursive often helps, reducing the number of parameters passed -to functions often helps, sometimes reordering variable declarations -helps, inlining of functions can sometimes help, and passing of packed -structures can also help. It is also possible to transition to/from -an extra stack stored in the EBDA using the stack_hop helper function. - -Some useful stats: the overhead for the entry to a bios handler that -takes a 'struct bregs' is 42 bytes of stack space (6 bytes from -interrupt insn, 32 bytes to store registers, and 4 bytes for call -insn). An entry to an ISR handler without args takes 30 bytes (6 + 20 -+ 4). - - -Debugging the bios: - -The bios will output information messages to a special debug port. -Under qemu, one can view these messages by adding '-chardev -stdio,id=seabios -device isa-debugcon,iobase=0x402,chardev=seabios' to -the qemu command line. Once this is done, one should see status -messages on the console. - -The gdb-server mechanism of qemu is also useful. One can use gdb with -qemu to debug system images. To use this, add '-s -S' to the qemu -command line. For example: - -qemu -L mybiosdir/ -fda myfdimage.img -s -S - -Then, in another session, run gdb with either out/rom16.o (to debug -bios 16bit code) or out/rom32.o (to debug bios 32bit code). For -example: - -gdb out/rom16.o - -Once in gdb, use the command "target remote localhost:1234" to have -gdb connect to qemu. See the qemu documentation for more information -on using gdb and qemu in this mode. Note that gdb seems to get -breakpoints confused when the cpu is in 16-bit real mode. This makes -stepping through the program difficult (though 'step instruction' -still works). Also, one may need to set 16bit break points at both -the cpu address and memory address (eg, break *0x1234 ; break -*0xf1234). +For other types of builds, and for more detailed developer +documentation, please see the online documentation listed above. diff --git a/roms/seabios/README.CSM b/roms/seabios/README.CSM deleted file mode 100644 index b904e65..0000000 --- a/roms/seabios/README.CSM +++ /dev/null @@ -1,22 +0,0 @@ -Enabling CONFIG_CSM allows SeaBIOS to be built as a Compatibility Support -Module for use with the OMVF/EDK-II UEFI firmware. - -It will provide "legacy" BIOS services for booting non-EFI operating -systems and will also allow OVMF to display on otherwise unsupported -video hardware by using the traditional VGA BIOS. - -Windows 2008r2 is known to use INT 10h BIOS calls even when booted via -EFI, and the presence of a CSM makes this work as expected too. - -Having built SeaBIOS with CONFIG_CSM, you should be able to drop the -result (out/Csm16.bin) into your OVMF build tree at -OvmfPkg/Csm/Csm16/Csm16.bin and then build OVMF with 'build -D -CSM_ENABLE'. The SeaBIOS binary will be included as a discrete file -within the 'Flash Volume' which is created, and there are tools which -will extract it and allow it to be replaced; satisfying the -requirements of the LGPL licence. - -A patch to OVMF is required, to prevent it from marking the region from -0xC0000-0xFFFFF as read-only before invoking our Legacy16Boot method. See -http://www.sourceforge.net/mailarchive/forum.php?thread_name=50FD7290.9060003%40redhat.com&forum_name=edk2-devel - diff --git a/roms/seabios/TODO b/roms/seabios/TODO deleted file mode 100644 index 23f26c0..0000000 --- a/roms/seabios/TODO +++ /dev/null @@ -1,21 +0,0 @@ -Review changes committed to coreboot, virtualbox, qemu, kvm, and bochs -cvs tip. - * bochs cvs (20100104): - -- changes synched - * coreboot (r3348): (bochs 20060708) - -- no noteworthy enhancements - * qemu - now uses SeaBIOS - * kvm - now uses SeaBIOS - * virtualbox (r13560): (bochs 20061231) - -- lots of mouse changes, logo, scsi/etherboot hooks, - floppy data rate?, int19 calls post - -The __call16 code does a long jump to the interrupt trampolines - this -is unnecessary. - -Support PCIv3 roms? Add support for PCI "configuration code" -extensions? - -Possibly add option to eliminate tsc based delays on emulators. - -Possibly support sending debug information over EHCI debug port. diff --git a/roms/seabios/docs/Build_overview.md b/roms/seabios/docs/Build_overview.md new file mode 100644 index 0000000..26db226 --- /dev/null +++ b/roms/seabios/docs/Build_overview.md @@ -0,0 +1,80 @@ +The SeaBIOS code can be built using standard GNU tools. A recent Linux +distribution should be able to build SeaBIOS using the standard +compiler tools. + +Building SeaBIOS +================ + +First, [obtain the code](Download). SeaBIOS can be compiled for +several different build targets. It is also possible to configure +additional compile time options - run **make menuconfig** to do this. + +Build for QEMU (along with KVM, Xen, and Bochs) +----------------------------------------------- + +To build for QEMU (and similar), one should be able to run "make" in +the main directory. The resulting file "out/bios.bin" contains the +processed bios image. + +One can use the resulting binary with QEMU by using QEMU's "-bios" +option. For example: + +`qemu -bios out/bios.bin -fda myfdimage.img` + +One can also use the resulting binary with Bochs. For example: + +`bochs -q 'floppya: 1_44=myfdimage.img' 'romimage: file=out/bios.bin'` + +Build for coreboot +------------------ + +To build for coreboot please see the coreboot build instructions at: + + +Build as a UEFI Compatibility Support Module (CSM) +-------------------------------------------------- + +To build as a CSM, first run kconfig (make menuconfig) and enable +CONFIG_CSM. Then build SeaBIOS (make) - the resulting binary will be +in "out/Csm16.bin". + +This binary may be used with the OMVF/EDK-II UEFI firmware. It will +provide "legacy" BIOS services for booting non-EFI operating systems +and will also allow OVMF to display on otherwise unsupported video +hardware by using the traditional VGA BIOS. (Windows 2008r2 is known +to use INT 10h BIOS calls even when booted via EFI, and the presence +of a CSM makes this work as expected too.) + +Having built SeaBIOS with CONFIG_CSM, one should be able to drop the +result (out/Csm16.bin) into an OVMF build tree at +OvmfPkg/Csm/Csm16/Csm16.bin and then build OVMF with 'build -D +CSM_ENABLE'. The SeaBIOS binary will be included as a discrete file +within the 'Flash Volume' which is created, and there are tools which +will extract it and allow it to be replaced. + +Overview of files in the repository +=================================== + +The **src/** directory contains the main bios source code. The +**src/hw/** directory contains source code specific to hardware +drivers. The **src/fw/** directory contains source code for platform +firmware initialization. The **src/std/** directory contains header +files describing standard bios, firmware, and hardware interfaces. + +The **vgasrc/** directory contains code for VGA BIOS implementations. +This code is separate from the main BIOS code in the src/ directory. +When the build is configured to produce a VGA BIOS the resulting +binary is found in out/vgabios.bin. The VGA BIOS code is always +compiled in 16bit mode. + +The **scripts/** directory contains helper utilities for manipulating +and building the final roms. + +The **out/** directory is created by the build process - it contains +all intermediate and final files. + +When reading the C code be aware that code that runs in 16bit mode can +not arbitrarily access non-stack memory - see [Memory Model](Memory +Model) for more details. For information on the major C code functions +and where code execution starts see [Execution and code +flow](Execution and code flow). diff --git a/roms/seabios/docs/Debugging.md b/roms/seabios/docs/Debugging.md new file mode 100644 index 0000000..03567de --- /dev/null +++ b/roms/seabios/docs/Debugging.md @@ -0,0 +1,106 @@ +This page describes the process of obtaining diagnostic information +from SeaBIOS and for reporting problems. + +Diagnostic information +====================== + +SeaBIOS has the ability to output diagnostic messages. This is +implemented in the code via calls to the "dprintf()" C function. + +On QEMU these messages are written to a special debug port. One can +view these messages by adding '-chardev stdio,id=seabios -device +isa-debugcon,iobase=0x402,chardev=seabios' to the QEMU command line. +Once this is done, one should see status messages on the console. + +On coreboot these messages are generally written to the "cbmem" +console (CONFIG_DEBUG_COREBOOT). If SeaBIOS launches a Linux operating +system, one can obtain the cbmem tool from the coreboot repository and +run "cbmem -c" to view the SeaBIOS diagnostic messages. + +Additionally, if a serial port is available, one may compile SeaBIOS +to send the diagnostic messages to the serial port. See the SeaBIOS +CONFIG_DEBUG_SERIAL option. + +Trouble reporting +================= + +If you are experiencing problems with SeaBIOS, it's useful to increase +the debugging level. This is done by running "make menuconfig" and +setting CONFIG_DEBUG_LEVEL to a higher value. A debug level of 8 will +show a lot of diagnostic information without flooding the serial port +(levels above 8 will frequently cause too much data). + +To report an issue, please collect the serial boot log with SeaBIOS +set to a debug level of 8 and forward the full log along with a +description of the problem to the SeaBIOS [mailing list](Mailinglist). + +Timing debug messages +===================== + +The SeaBIOS repository has a tool (**scripts/readserial.py**) that can +timestamp each diagnostic message produced. The timestamps can provide +some additional information on how long internal processes take. It +also provides a simple profiling mechanism. + +The tool can be used on coreboot builds that have diagnostic messages +sent to a serial port. Make sure SeaBIOS is configured with +CONFIG_DEBUG_SERIAL and run the following on the host receiving serial +output: + +`/path/to/seabios/scripts/readserial.py /dev/ttyS0 115200` + +Update the above command with the appropriate serial device and baud +rate. + +The tool can also timestamp the messages from the QEMU debug port. To +use with QEMU run the following: + +`mkfifo qemudebugpipe`\ +`qemu -chardev pipe,path=qemudebugpipe,id=seabios -device isa-debugcon,iobase=0x402,chardev=seabios ...` + +and then in another session: + +`/path/to/seabios/scripts/readserial.py -nf qemudebugpipe` + +The mkfifo command only needs to be run once to create the pipe file. + +When readserial.py is running, it shows a timestamp with millisecond +precision of the amount of time since the start of the log. If one +presses the "enter" key in the readserial.py session it will add a +blank line to the screen and also reset the time back to zero. The +readserial.py program also keeps a log of all output in files that +look like "seriallog-YYYYMMDD_HHMMSS.log". + +Debugging with gdb on QEMU +========================== + +One can use gdb with QEMU to debug system images. To do this, add '-s +-S' to the qemu command line. For example: + +`qemu -bios out/bios.bin -fda myfdimage.img -s -S` + +Then, in another session, run gdb with either out/rom16.o (to debug +bios 16bit code) or out/rom.o (to debug bios 32bit code). For example: + +`gdb out/rom16.o` + +Once in gdb, use the command "target remote localhost:1234" to have +gdb connect to QEMU. See the QEMU documentation for more information +on using gdb and QEMU in this mode. + +When debugging 16bit code, also run the following commands in gdb: + +`set architecture i8086`\ +`add-symbol-file out/rom16.o 0xf0000` + +The second command loads the 16bit symbols a second time at an offset +of 0xf0000, which helps gdb set and catch breakpoints correctly. + +To debug a VGA BIOS image, run "gdb out/vgarom.o" add use the gdb +command "add-symbol-file out/vgarom.o 0xc0000" to load the 16bit VGA +BIOS symbols twice. + +If debugging the 32bit SeaBIOS initialization code with gdb, note that +SeaBIOS does self relocation by default. This relocation will alter +the location of initialization code symbols. Disable +CONFIG_RELOCATE_INIT to prevent SeaBIOS from doing this. diff --git a/roms/seabios/docs/Developer_Documentation.md b/roms/seabios/docs/Developer_Documentation.md new file mode 100644 index 0000000..d50455d --- /dev/null +++ b/roms/seabios/docs/Developer_Documentation.md @@ -0,0 +1,24 @@ +This page is intended for developers interested in understanding and +enhancing SeaBIOS. Please also consider joining the [mailing +list](Mailinglist). + +The SeaBIOS code can be obtained via the [download](Download) +page. For specific information on building SeaBIOS for coreboot, +please see the [coreboot SeaBIOS](http://www.coreboot.org/SeaBIOS) +page. + +See details on [building SeaBIOS](Build overview). + +There is also information on the SeaBIOS [Memory Model](Memory Model). + +Along with information on SeaBIOS [Execution and code flow](Execution +and code flow). + +A description of the process of linking the final SeaBIOS binary is +available at [Linking overview](Linking overview). + +To debug SeaBIOS and report problems see SeaBIOS +[debugging](Debugging). + +Useful links to specifications is available at [Developer +links](Developer links). diff --git a/roms/seabios/docs/Developer_links.md b/roms/seabios/docs/Developer_links.md new file mode 100644 index 0000000..67a047e --- /dev/null +++ b/roms/seabios/docs/Developer_links.md @@ -0,0 +1,86 @@ +Links to pages with more information. + +BIOS interfaces +=============== + +Ralf Brown's interrupt list + +* + +Memory layout info + +* + +Old PNP BIOS spec + +* + +T13 BIOS Enhanced Disk Drive (drafts): + +* + +Exported BIOS tables +==================== + +ACPI spec + +* + +PCI IRQ Routing Table Specification + +* + +MP configuration table + +* + +SM BIOS (aka DMI): + +* + +Hardware information +==================== + +info on PIC + +* + +info on kbd + +* + +info on vga + +* + +info on lpt + +* + +info on floppy + +* + +info on ata + +* +* + +info on serial + +* + +General information +=================== + +Bochs tech document list + +* + +Phoenix documents + +* + +Dosemu information + +* diff --git a/roms/seabios/docs/Download.md b/roms/seabios/docs/Download.md new file mode 100644 index 0000000..a49c6fb --- /dev/null +++ b/roms/seabios/docs/Download.md @@ -0,0 +1,25 @@ +SeaBIOS may be distributed under the terms of the [GNU +LGPLv3](http://www.gnu.org/licenses/lgpl-3.0-standalone.html) license. +Both source code and binaries are available. + +Latest source code +================== + +The SeaBIOS project uses the [git](http://git-scm.com/) revision +control system. To download the latest source from revision control, +run: + +`$ git clone git://git.seabios.org/seabios.git seabios`\ +`$ cd seabios` + +There's also a [website](http://git.seabios.org/) to browse the latest +source code online. + +Released versions +================= + +Released versions of the source code are available at: + + + +Please see [releases](Releases) for information on each release. diff --git a/roms/seabios/docs/Execution_and_code_flow.md b/roms/seabios/docs/Execution_and_code_flow.md new file mode 100644 index 0000000..9396eca --- /dev/null +++ b/roms/seabios/docs/Execution_and_code_flow.md @@ -0,0 +1,178 @@ +This page provides a high-level description of some of the major code +phases that SeaBIOS transitions through and general information on +overall code flow. + +SeaBIOS code phases +=================== + +The SeaBIOS code goes through a few distinct code phases during its +execution lifecycle. Understanding these code phases can help when +reading and enhancing the code. + +POST phase +---------- + +The Power On Self Test (POST) phase is the initialization phase of the +BIOS. This phase is entered when SeaBIOS first starts execution. The +goal of the phase is to initialize internal state, initialize external +interfaces, detect and setup hardware, and to then start the boot +phase. + +On emulators, this phase starts when the CPU starts execution in 16bit +mode at 0xFFFF0000:FFF0. The emulators map the SeaBIOS binary to this +address, and SeaBIOS arranges for romlayout.S:reset_vector() to be +present there. This code calls romlayout.S:entry_post() which then +calls post.c:handle_post() in 32bit mode. + +On coreboot, the build arranges for romlayout.S:entry_elf() to be +called in 32bit mode. This then calls post.c:handle_post(). + +On CSM, the build arranges for romlayout.S:entry_csm() to be called +(in 16bit mode). This then calls csm.c:handle_csm() in 32bit mode. +Unlike on the emulators and coreboot, the SeaBIOS CSM POST phase is +orchastrated with UEFI and there are several calls back and forth +between SeaBIOS and UEFI via handle_csm() throughout the POST +process. + +The POST phase itself has several sub-phases. + +* The "preinit" sub-phase: code run prior to code relocation. +* The "init" sub-phase: code to initialize internal variables and + interfaces. +* The "setup" sub-phase: code to setup hardware and drivers. +* The "prepboot" sub-phase: code to finalize interfaces and prepare + for the boot phase. + +At completion of the POST phase, SeaBIOS invokes an "int 0x19" +software interrupt in 16bit mode which begins the boot phase. + +Boot phase +---------- + +The goal of the boot phase is to load the first portion of the +operating system's boot loader into memory and start execution of that +boot loader. This phase starts when a software interrupt ("int 0x19" +or "int 0x18") is invoked. The code flow starts in 16bit mode in +romlayout.S:entry_19() or romlayout.S:entry_18() which then +transition to 32bit mode and call boot.c:handle_19() or +boot.c:handle_18(). + +The boot phase is technically also part of the "runtime" phase of +SeaBIOS. It is typically invoked immiediately after the POST phase, +but it can also be invoked by an operating system or be invoked +multiple times in an attempt to find a valid boot media. Although the +boot phase C code runs in 32bit mode it does not have write access to +the 0x0f0000-0x100000 memory region and can not call the various +malloc_X() calls. See [Memory Model](Memory Model) for +more information. + +Main runtime phase +------------------ + +The main runtime phase occurs after the boot phase starts the +operating system. Once in this phase, the SeaBIOS code may be invoked +by the operating system using various 16bit and 32bit calls. The goal +of this phase is to support these legacy calling interfaces and to +provide compatibility with BIOS standards. There are multiple entry +points for the BIOS - see the entry_XXX() assembler functions in +romlayout.S. + +Callers use most of these legacy entry points by setting up a +particular CPU register state, invoking the BIOS, and then inspecting +the returned CPU register state. To handle this, SeaBIOS will backup +the current register state into a "struct bregs" (see romlayout.S, +entryfuncs.S, and bregs.h) on call entry and then pass this struct to +the C code. The C code can then inspect the register state and modify +it. The assembler entry functions will then restore the (possibly +modified) register state from the "struct bregs" on return to the +caller. + +Resume and reboot +----------------- + +As noted above, on emulators SeaBIOS handles the 0xFFFF0000:FFF0 +machine startup execution vector. This vector is also called on +machine faults and on some machine "resume" events. It can also be +called (as 0xF0000:FFF0) by software as a request to reboot the +machine (on emulators, coreboot, and CSM). + +The SeaBIOS "resume and reboot" code handles these calls and attempts +to determine the desired action of the caller. Code flow starts in +16bit mode in romlayout.S:reset_vector() which calls +romlayout.S:entry_post() which calls romlayout.S:entry_resume() which +calls resume.c:handle_resume(). Depending on the request the +handle_resume() code may transition to 32bit mode. + +Technically this code is part of the "runtime" phase, so even though +parts of it run in 32bit mode it still has the same limitations of the +runtime phase. + +Threads +======= + +Internally SeaBIOS implements a simple cooperative multi-tasking +system. The system works by giving each "thread" its own stack, and +the system round-robins between these stacks whenever a thread issues +a yield() call. This "threading" system may be more appropriately +described as [coroutines](http://en.wikipedia.org/wiki/Coroutine). +These "threads" do not run on multiple CPUs and are not preempted, so +atomic memory accesses and complex locking is not required. + +The goal of these threads is to reduce overall boot time by +parallelizing hardware delays. (For example, by allowing the wait for +an ATA harddrive to spinup and respond to commands to occur in +parallel with the wait for a PS/2 keyboard to respond to a setup +command.) These hardware setup threads are only available during the +"setup" sub-phase of the [POST phase](#POST_phase). + +The code that implements threads is in stacks.c. + +Hardware interrupts +=================== + +The SeaBIOS C code always runs with hardware interrupts disabled. All +of the C code entry points (see romlayout.S) are careful to explicitly +disable hardware interrupts (via "cli"). Because running with +interrupts disabled increases interrupt latency, any C code that could +loop for a significant amount of time (more than about 1 ms) should +periodically call yield(). The yield() call will briefly enable +hardware interrupts to occur, then disable interrupts, and then resume +execution of the C code. + +There are two main reasons why SeaBIOS always runs C code with +interrupts disabled. The first reason is that external software may +override the default SeaBIOS handlers that are called on a hardware +interrupt event. Indeed, it is common for DOS based applications to do +this. These legacy third party interrupt handlers may have +undocumented expections (such as stack location and stack size) and +may attempt to call back into the various SeaBIOS software services. +Greater compatibility and more reproducible results can be achieved by +only permitting hardware interrupts at specific points (via yield() +calls). The second reason is that much of SeaBIOS runs in 32bit mode. +Attempting to handle interrupts in both 16bit mode and 32bit mode and +switching between modes to delegate those interrupts is an unneeded +complexity. Although disabling interrupts can increase interrupt +latency, this only impacts legacy systems where the small increase in +interrupt latency is unlikely to be noticeable. + +Extra 16bit stack +================= + +SeaBIOS implements 16bit real mode handlers for both hardware +interrupts and software request "interrupts". In a traditional BIOS, +these requests would use the caller's stack space. However, the +minimum amount of space the caller must provide has not been +standardized and very old DOS programs have been observed to allocate +very small amounts of stack space (100 bytes or less). + +By default, SeaBIOS now switches to its own stack on most 16bit real +mode entry points. This extra stack space is allocated in ["low +memory"](Memory Model). It ensures SeaBIOS uses a minimal amount of a +callers stack (typically no more than 16 bytes) for these legacy +calls. (More recently defined BIOS interfaces such as those that +support 16bit protected and 32bit protected mode calls standardize a +minimum stack size with adequete space, and SeaBIOS generally will not +use its extra stack in these cases.) + +The code to implement this stack "hopping" is in romlayout.S and in +stacks.c. diff --git a/roms/seabios/docs/Linking_overview.md b/roms/seabios/docs/Linking_overview.md new file mode 100644 index 0000000..fb938b6 --- /dev/null +++ b/roms/seabios/docs/Linking_overview.md @@ -0,0 +1,166 @@ +This page describes the process that the SeaBIOS build uses to link +the compiled code into the final binary objects. + +Unfortunately, the SeaBIOS linking phase is complex. This complexity +is due to several unusual requirements: + +* Some BIOS entry points must reside at specific hardcoded memory + locations. The build must support positioning code and variables at + specific locations. +* In order to support multiple [memory models](Memory Model) the same + C code can be complied in three modes (16bit mode, 32bit segmented + mode, and 32bit "flat" mode). Binary code from these three modes + must be able to co-exist and on occasion reference each other. +* There is a finite amount of memory available to the BIOS. The build + will attempt to weed out unused code and variables from the final + binary. It also supports self-relocation of one-time initialization + code. + +Code layout +=========== + +To support the unusual build requirements, several +[gcc](http://en.wikipedia.org/wiki/GNU_Compiler_Collection) compiler +options are used. The "-ffunction-sections" and "-fdata-sections" +flags instruct the compiler to place each variable and function into +its own +[ELF](http://en.wikipedia.org/wiki/Executable_and_Linkable_Format) +section. + +The C code is compiled three times into three separate objects for +each of the major supported [memory models](Memory Model): +**code16.o**, **code32seg.o**, and **code32flat.o**. Information on +the sections and symbols of these three objects are extracted (using +**objdump**) and passed in to the **scripts/layoutrom.py** python +script. This script analyzes this information and produces gnu +[ld](http://en.wikipedia.org/wiki/GNU_linker) "linker scripts" which +provide precise location information to the linker. These linker +scripts are then used during the link phase which produces a **rom.o** +object containing all the code. + +Fixed location entry points +--------------------------- + +The build supports placing code entry points and variables at fixed +memory locations. This support is required in order to support the +legacy BIOS standards. For example, a program might execute an "int +0x15" to request system information from the BIOS, but another old +program might use "ljmpw $0xf000, $0xf859" instead. Both must provide +the same results and so the build must position the 0x15 interrupt +entry point in physical memory at 0xff859. + +This support is accomplished by placing the given code/variables into +ELF sections that have a name containing the substring +".fixedaddr.0x1234" (where 0x1234 is the desired address). For +variables in C code this is accomplished by marking the variables with +the VARFSEGFIXED(0x1234) macro. For assembler entry points the ORG +macro is used (see **romlayout.S**). + +During the build, the **layoutrom.py** script will detect sections +that contain the ".fixedaddr." substring and will arrange for the +final linker scripts to specify the desired address for the given +section. + +Due to the sparse nature of these fixed address sections, the +layoutrom.py script will also arrange to pack in other unrelated 16bit +code into the free space between fixed address sections (see +layoutrom.py:fitSections()). This maximizes the space available and +reduces the overall size of the final binary. + +C code in three modes +--------------------- + +SeaBIOS must support multiple [memory models](Memory Model). This is +accomplished by compiling the C code three separate times into three +separate objects. + +The C code within a mode must not accidentally call a C function in +another mode, but multiple modes must all access the same single copy +of global variables. Further, it is occasionally necessary for the C +code in one mode to obtain the address of C code in another mode. + +In order to use the same global variables between all modes, the +layoutrom.py script will detect references to global variables and +emit specific symbol definitions for those global variables in the +linker scripts so that all references use the same physical memory +address (see layoutrom.py:outXRefs()). + +To ensure C code does not accidentally call C code compiled in a +different mode, the build will ensure the symbols for C code in each +mode are isolated from each other during the linking stage. To support +those situations where an address of a C function in another mode is +required the build supports symbols with a special "\_cfuncX_" +prefix. The layoutrom.py script detects these references and will emit +a corresponding symbol definitions in the linker script that points to +the C code of the specified mode. This is typically seen with code +like: + +`extern void _cfunc32flat_process_op(void);`\ +`return call32(_cfunc32flat_process_op, 0, 0);` + +In the above example, when the build finds the symbol +"\_cfunc32flat_process_op" it will emit that symbol with the physical +address of the 32bit "flat" version of the process_op() C function. + +Build garbage collection +------------------------ + +To reduce the overall size of the final SeaBIOS binary the build +supports automatically weeding out of unused code and variables. This +is done with two separate processes: when supported the gcc +"-fwhole-program" compilation flag is used, and the layoutrom.py +script checks for unreferenced ELF sections. The layoutrom.py script +builds the final linker scripts with only referenced ELF sections, and +thus unreferenced sections are weeded out from the final objects. + +When writing C code, it is necessary to mark C functions with the +VISIBLE16, VISIBLE32SEG, or VISIBLE32FLAT macros if the functions are +ever referenced from assembler code. These macros ensure the +corresponding C function is emitted by the C compiler when compiling +for the given memory mode. These macros, however, do not affect the +layoutrom.py reference check, so even a function decorated with one of +the above macros can be weeded out from the final object if it is +never referenced. + +Code relocation +--------------- + +To further reduce the runtime memory size of the BIOS, the build +supports runtime self-relocation. Normally SeaBIOS is loaded into +memory in the memory region at 0xC0000-0x100000. This is convenient +for initial binary deployment, but the space competes with memory +requirements for Option ROMs, BIOS tables, and runtime storage. By +default, SeaBIOS will self-relocate its one-time initialization code +to free up space in this region. + +To support this feature, the build attempts to automatically detect +which C code is exclusively initialization phase code (see +layoutrom.py:checkRuntime()). It does this by finding all functions +decorated with the VISIBLE32INIT macro and all functions only +reachable via functions with that macro. These "init only" functions +are then grouped together and their location and size is stored in the +binary for the runtime code to relocate (see post.c:reloc_preinit()). + +The build also locates all cross section code references along with +all absolute memory addresses in the "init only" code. These addresses +need to be modified with the new run-time address in order for the +code to successfully run at a new address. The build finds the +location of the addresses (see layoutrom.py:getRelocs()) and stores +the information in the final binary. + +Final binary checks +=================== + +At the conclusion of the main linking stage, the code is contained in +the file **rom.o**. This object file contains all of the assembler +code, variables, and the C code from all three memory model modes. + +At this point the **scripts/checkrom.py** script is run to perform +final checks on the code. The script performs some sanity checks, it +may update some tables in the binary, and it reports some size +information. + +After the checkrom.py script is run the final user visible binary is +produced. The name of the final binary is either **bios.bin**, +**Csm16.bin**, or **bios.bin.elf** depending on the SeaBIOS build +requested. diff --git a/roms/seabios/docs/Mailinglist.md b/roms/seabios/docs/Mailinglist.md new file mode 100644 index 0000000..21e74a4 --- /dev/null +++ b/roms/seabios/docs/Mailinglist.md @@ -0,0 +1,8 @@ +For questions and general information about SeaBIOS, please subscribe +to the [SeaBIOS mailing +list](http://www.seabios.org/mailman/listinfo/seabios). If you're not +subscribed, your post will be held temporarily for moderator approval +(to combat spam). + +A mailing list archive is available at + diff --git a/roms/seabios/docs/Memory_Model.md b/roms/seabios/docs/Memory_Model.md new file mode 100644 index 0000000..0668bd8 --- /dev/null +++ b/roms/seabios/docs/Memory_Model.md @@ -0,0 +1,253 @@ +The SeaBIOS code is required to support multiple x86 CPU memory +models. This requirement impacts the code layout and internal storage +of SeaBIOS. + +x86 Memory Models +================= + +The x86 line of CPUs has evolved over many years. The original 8086 +chip used 16bit pointers and could only address 1 megabyte of memory. +The 80286 CPU still used 16bit pointers, but could address up to 16 +megabytes of memory. The 80386 chips could process 32bit instructions +and could access up to 4 gigabyte of memory. The most recent x86 chips +can process 64bit instructions and access 16 exabytes of ram. + +During the evolution of the x86 CPUs from the 8086 to the 80386 the +BIOS was extended to handle calls in the various modes that the CPU +implemented. + +This section outlines the five different x86 CPU execution and memory +access models that SeaBIOS supports. + +16bit real mode +--------------- + +This mode is a +[segmented](http://en.wikipedia.org/wiki/Memory_segmentation) memory +mode invoked by callers. The CPU defaults to executing 16bit +instructions. Callers typically invoke the BIOS by issuing an "int x" +instruction which causes a software +[interrupt](http://en.wikipedia.org/wiki/Interrupt) that is handled by +the BIOS. The SeaBIOS code also handles hardware interrupts in this +mode. SeaBIOS can only access the first 1 megabyte of memory in this +mode, but it can access any part of that first megabyte. + +16bit bigreal mode +------------------ + +This mode is a segmented memory mode that is used for [option +roms](http://en.wikipedia.org/wiki/Option_ROM). The CPU defaults to +executing 16bit instructions and segmented memory accesses are still +used. However, the segment limits are increased so that the entire +first 4 gigabytes of memory is fully accessible. Callers can invoke +all the [16bit real mode](#16bit_real_mode) functions while in this +mode and can also invoke the Post Memory Manager (PMM) functions that +are available during option rom execution. + +16bit protected mode +-------------------- + +CPU execution in this mode is similar to [16bit real +mode](#16bit_real_mode). The CPU defaults to executing 16bit +instructions. However, each segment register indexes a "descriptor +table", and it is difficult or impossible to know what the physical +address of each segment is. Generally speaking, the BIOS can only +access code and data in the f-segment. The PCIBIOS, APM BIOS, and PNP +BIOS all have documented 16bit protected mode entry points. + +Some old code may attempt to invoke the standard [16bit real +mode](#16bit_real_mode) entry points while in 16bit protected +mode. The PCI BIOS specification explicitly requires that the legacy +"int 1a" real mode entry point support 16bit protected mode calls if +they are for the PCI BIOS. Callers of other legacy entry points in +protected mode have not been observed and SeaBIOS does not support +them. + +32bit segmented mode +-------------------- + +In this mode the processor runs in 32bit mode, but the segment +registers may have a limit and may have a non-zero offset. In effect, +this mode has all of the limitations of [16bit protected +mode](#16bit_protected_mode) - the main difference between the modes +is that the processor defaults to executing 32bit instructions. In +addition to these limitations, callers may also run the SeaBIOS code +at varying virtual addresses and so the code must support code +relocation. The PCI BIOS specification and APM BIOS specification +define 32bit segmented mode interfaces. + +32bit flat mode +--------------- + +In this mode the processor defaults to executing 32bit instructions, +and all segment registers have an offset of zero and allow access to +the entire first 4 gigabytes of memory. This is the only "sane" mode +for 32bit code - modern compilers and modern operating systems will +generally only support this mode (when running 32bit code). +Ironically, it's the only mode that is not strictly required for a +BIOS to support. SeaBIOS uses this mode internally to support the POST +and BOOT [phases of execution](Execution and code flow). + +code16gcc +========= + +In order to produce code that can run when the processor is in a 16bit +mode, SeaBIOS uses the +[binutils](http://en.wikipedia.org/wiki/GNU_Binutils) ".code16gcc" +assembler flag. This instructs the assembler to emit extra prefix +opcodes so that the 32bit code produced by +[gcc](http://en.wikipedia.org/wiki/GNU_Compiler_Collection) will run +even when the processor is in 16bit mode. Note that gcc always +produces 32bit code - it does not know about the ".code16gcc" flag and +does not know that the code will run in a 16bit mode. + +SeaBIOS uses the same code for all of the 16bit modes ([16bit real +mode](#16bit_real_mode), [16bit bigreal mode](#16bit_bigreal_mode), +and [16bit protected mode](#16bit_protected_mode)) and that code is +assembled using ".code16gcc". SeaBIOS is careful to use segment +registers properly so that the same code can run in the different +16bit modes that it needs to support. + +C code mode flags +================= + +Two compile time flags are available to determine the memory model the +code is intended for: MODE16 and MODESEGMENT. When compiling for the +16 bit modes, MODE16 is true and MODESEGMENT is true. In 32bit +segmented mode, MODE16 is false and MODESEGMENT is true. In 32bit flat +mode both MODE16 and MODESEGMENT are false. + +Common memory used at run-time +============================== + +There are several memory areas that the SeaBIOS "runtime" +[phase](Execution and code flow) makes use of: + +* 0x000000-0x000400: Interrupt descriptor table (IDT). This area + defines 256 interrupt vectors as defined by the Intel CPU + specification for 16bit irq handlers. This area is read/writable at + runtime and can be accessed from 16bit real mode and 16bit bigreal + mode calls. SeaBIOS only uses this area to maintain compatibility + with legacy systems. + +* 0x000400-0x000500: BIOS Data Area (BDA). This area contains various + legacy flags and attributes. The area is read/writable at runtime + and can be accessed from 16bit real mode and 16bit bigreal mode + calls. SeaBIOS only uses this area to maintain compatibility with + legacy systems. + +* 0x09FC00-0x0A0000 (typical): Extended BIOS Data Area (EBDA). This + area contains a few legacy flags and attributes. The area is + typically located at 0x9FC00, but it can be moved by option roms, by + legacy operating systems, and by SeaBIOS if + CONFIG_MALLOC_UPPERMEMORY is not set. Its actual location is + determined by a pointer in the BDA. The area is read/writable at + runtime and can be accessed from 16bit real mode and 16bit bigreal + mode calls. SeaBIOS only uses this area to maintain compatibility + with legacy systems. + +* 0x0E0000-0x0F0000 (typical): "low" memory. This area is used for + custom read/writable storage internal to SeaBIOS. The area is + read/writable at runtime and can be accessed from 16bit real mode + and 16bit bigreal mode calls. The area is typically located at the + end of the e-segment, but the build may position it anywhere in the + 0x0C0000-0x0F0000 region. However, if CONFIG_MALLOC_UPPERMEMORY is + not set, then this region is between 0x090000-0x0A0000. Space is + allocated in this region by either marking a global variable with + the "VARLOW" flag or by calling malloc_low() during + initialization. The area can be grown dynamically (via malloc_low), + but it will never exceed 64K. + +* 0x0F0000-0x100000: The BIOS segment. This area is used for both + runtime code and static variables. Space is allocated in this region + by either marking a global variable with VAR16, one of the VARFSEG + flags, or by calling malloc_fseg() during initialization. The area + is read-only at runtime and can be accessed from 16bit real mode, + 16bit bigreal mode, 16bit protected mode, and 32bit segmented mode + calls. + +All of the above areas are also read/writable during the SeaBIOS +initialization phase and are accessible when in 32bit flat mode. + +Segmented mode memory access +============================ + +The assembler entry functions for segmented mode calls (all modes +except [32bit flat mode](#32bit_flat_mode)) will arrange +to set the data segment (%ds) to be the same as the stack segment +(%ss) before calling any C code. This permits all C variables located +on the stack and C pointers to data located on the stack to work as +normal. + +However, all code running in segmented mode must wrap non-stack memory +accesses in special macros. These macros ensure the correct segment +register is used. Failure to use the correct macro will result in an +incorrect memory access that will likely cause hard to find errors. + +There are three low-level memory access macros: + +* GET_VAR / SET_VAR : Accesses a variable using the specified segment + register. This isn't typically used directly by C code. + +* GET_FARVAR / SET_FARVAR : Assigns the extra segment (%es) to the + given segment id and then performs the given memory access via %es. + +* GET_FLATVAR / SET_FLATVAR : These macros take a 32bit pointer, + construct a segment/offset pair valid in real mode, and then perform + the given access. These macros must not be used in 16bit protected + mode or 32bit segmented mode. + +Since most memory accesses are to [common memory used at +run-time](#Common_memory_used_at_run-time), several helper +macros are also available. + +* GET_IDT / SET_IDT : Access the interrupt descriptor table (IDT). + +* GET_BDA / SET_BDA : Access the BIOS Data Area (BDA). + +* GET_EBDA / SET_EBDA : Access the Extended BIOS Data Area (EBDA). + +* GET_LOW / SET_LOW : Access internal variables marked with + VARLOW. (There are also related macros GET_LOWFLAT / SET_LOWFLAT for + accessing storage allocated with malloc_low). + +* GET_GLOBAL : Access internal variables marked with the VAR16 or + VARFSEG flags. (There is also the related macro GET_GLOBALFLAT for + accessing storage allocated with malloc_fseg). + +Memory available during initialization +====================================== + +During the POST [phase](Execution and code flow) the code +can fully access the first 4 gigabytes of memory. However, memory +accesses are generally limited to the [common memory used at +run-time](#Common_memory_used_at_run-time) and areas +allocated at runtime via one of the malloc calls: + +* malloc_high : Permanent high-memory zone. This area is used for + custom read/writable storage internal to SeaBIOS. The area is + located at the top of the first 4 gigabytes of ram. It is commonly + used for storing standard tables accessed by the operating system at + runtime (ACPI, SMBIOS, and MPTable) and for DMA buffers used by + hardware drivers. The area is read/writable at runtime and an entry + in the e820 memory map is used to reserve it. When running on an + emulator that has only 1 megabyte of ram this zone will be empty. + +* malloc_tmphigh : Temporary high-memory zone. This area is used for + custom read/writable storage during the SeaBIOS initialization + phase. The area generally starts after the first 1 megabyte of ram + (0x100000) and ends prior to the Permanent high-memory zone. When + running on an emulator that has only 1 megabyte of ram this zone + will be empty. The area is not reserved from the operating system, + so it must not be accessed after the SeaBIOS initialization phase. + +* malloc_tmplow : Temporary low-memory zone. This area is used for + custom read/writable storage during the SeaBIOS initialization + phase. The area resides between 0x07000-0x90000. The area is not + reserved from the operating system and by specification it is + required to be zero'd at the end of the initialization phase. + +The "tmplow" and "tmphigh" regions are only available during the +initialization phase. Any access (either read or write) after +completion of the initialization phase can result in difficult to find +errors. diff --git a/roms/seabios/docs/README b/roms/seabios/docs/README new file mode 100644 index 0000000..430e0fe --- /dev/null +++ b/roms/seabios/docs/README @@ -0,0 +1,5 @@ +This directory contains SeaBIOS documentation as found on the SeaBIOS +wiki. All the files in this directory (with the exclusion of this +README file) correspond to a page on the wiki. + +The documentation files use markdown syntax. diff --git a/roms/seabios/docs/Releases.md b/roms/seabios/docs/Releases.md new file mode 100644 index 0000000..6a1ecd5 --- /dev/null +++ b/roms/seabios/docs/Releases.md @@ -0,0 +1,377 @@ +History of SeaBIOS releases. Please see [download](Download) for +information on obtaining these releases. + +SeaBIOS 1.8.0 +============= + +Available on 20150218. Major changes in this release: + +* Several USB timing fixes for USB controllers on real hardware +* Initial support for USB3 hubs +* Initial support for SD cards (on QEMU only) +* Initial support for transitioning to 32bit mode using SMIs (on QEMU + TCG only) +* SeaVGABIOS improvements + * Added cursor emulation to coreboot native init vgabios (cbvga) + * Added support for read character calls when in graphics mode +* Developer documentation added to "docs/" directory in the code + repository and several documentation updates +* Several bug fixes and code cleanups + +As of the 1.8.0 release, new feature releases will modify the first +two release numbers (eg, 1.8) and stable releases will use three +numbers (eg, 1.8.1). The prior behavior of using a forth number +(eg, 1.7.5.1) for stable releases will no longer be used. + +SeaBIOS 1.7.5 +============= + +Available on 20140528. Major changes in this release: + +* Support for obtaining SMBIOS tables directly from QEMU. +* XHCI USB controller fixes for real hardware (now tested on several + boards) +* SeaVGABIOS improvements + * New driver for "coreboot native vga" support + * Improved detection of older x86emu versions with incorrect + emulation. +* Several bug fixes and code cleanups + +SeaBIOS 1.7.5.1 +--------------- + +Available on 20141113. Stable release containing only bug fixes. + +SeaBIOS 1.7.5.2 +--------------- + +Available on 20150112. Stable release containing only bug fixes. + +SeaBIOS 1.7.4 +============= + +Available on 20131223. Major changes in this release: + +* Support for obtaining ACPI tables directly from QEMU. +* Initial support for XHCI USB controllers (initially for QEMU only). +* Support for booting from "pvscsi" devices on QEMU. +* Enhanced floppy driver - improved support for real hardware. +* coreboot cbmem console support. +* Optional support for using the 9-segment instead of the e-segment + for local variables. +* Improved internal timer code and accuracy. +* SeaVGABIOS improvements + * Better support for legacy X.org releases with incomplete x86emu + emulation. + * Support for using an internal stack to reduce caller's stack + usage. + * Back port of new "bochs dispi" interface video modes. +* Several bug fixes and code cleanups + * Source code separated out into additional hardware and firmware + directories. + * Update to latest version of Kconfig + +SeaBIOS 1.7.3 +============= + +Available on 20130707. Major changes in this release: + +* Initial support for using SeaBIOS as a UEFI Compatibility Support + Module (CSM) +* Support for detecting and using ACPI reboot ports. +* By default, all 16bit entry points now use an internal stack to + reduce stack footprint. +* Floppy controller code has been rewritten to improve + compatibility. Non-standard floppy sizes now work again with recent + QEMU versions. +* Several bug fixes and code cleanups + +SeaBIOS 1.7.2 +============= + +Available on 20130118. Major changes in this release: + +* Support for ICH9 host chipset ("q35") on emulators +* Support for booting from LSI MegaRAID SAS controllers +* Support for using the ACPI PM timer on emulators +* Improved Geode VGA BIOS support. +* Several bug fixes + +SeaBIOS 1.7.2.1 +--------------- + +Available on 20130227. Stable release containing only bug fixes. + +SeaBIOS 1.7.2.2 +--------------- + +Available on 20130527. Stable release containing only bug fixes. + +SeaBIOS 1.7.1 +============= + +Available on 20120831. Major changes in this release: + +* Initial support for booting from USB attached scsi (USB UAS) drives +* USB EHCI 64bit controller support +* USB MSC multi-LUN device support +* Support for booting from LSI SCSI controllers on emulators +* Support for booting from AMD PCscsi controllers on emulators +* New PCI allocation code on emulators. Support 64bit PCI bars and + mapping them above 4G. +* Support for non-linear APIC ids on emulators. +* Stack switching for 16bit real mode irq handlers to reduce stack + footprint. +* Support for custom storage in the memory at 0xc0000-0xf0000. No + longer reserve memory for custom storage in first 640k. +* Improved code generation for 16bit segment register loads +* Boot code will now (by default) reboot after 60 seconds if no boot + device found +* CBFS and FWCFG "files" are now only scanned one time +* Several bug fixes + +SeaBIOS 1.7.0 +============= + +Available on 20120414. Major changes in this release: + +* Many enhancements to VGA BIOS code - it should now be feature + complete with LGPL vgabios. +* Support for virtio-scsi. +* Improved USB drive (usb-msc) support. +* Several USB controller bug fixes and improvements. +* Runtime ACPI AML PCI hotplug construction. +* Support for running on i386 and i486 CPUs. +* Enhancements to PCI init when running on emulators. +* Several bug fixes + +SeaBIOS 1.6.3 +============= + +Available on 20111004. Major changes in this release: + +* Initial support for Xen +* PCI init (on emulators) uses a two-phase initialization +* Fixes for AHCI so it can work on real hardware. AHCI is now enabled + by default. +* Bootsplash support for BMP files +* Several configuration options can now be configured at runtime via + CBFS files (eg, "etc/boot-menu-wait") +* PCI device scan is cached during POST phase +* Several bug fixes + +The SeaBIOS 1.6.3 release was an incremental feature release. The +first release number (1) was incremented as the project was no longer +in a beta stage, and the third release number (3) was also incremented +to indicate the release was a regular feature release. + +SeaBIOS 1.6.3.1 +--------------- + +Available on 20111124. Stable release containing only bug fixes. + +SeaBIOS 1.6.3.2 +--------------- + +Available on 20120311. Stable release containing only bug fixes. + +SeaBIOS 0.6.2 +============= + +Available on 20110228. Major changes in this release: + +* Setup code can relocate to high-memory to save space in c-f segments +* Build now configured via Kconfig +* Experimental support for AHCI controllers +* Support for run-time configuration of the boot order (via + CBFS/fw_cfg "bootorder" file) +* Support T13 EDD3.0 spec +* Improved bounds checking on PCI memory allocation +* Several bug fixes + +SeaBIOS 0.6.1 +============= + +Available on 20100913. Major changes in this release: + +* Support for virtio drives +* Add ACPI definitions for cpu hotplug support +* Support for a graphical bootsplash screen +* USB mouse support +* The PCI support for emulators is less dependent on i440 chipset +* New malloc implementation which improves memalign and free +* The build system no longer double links objects +* Several bug fixes + +SeaBIOS 0.6.1.1 +--------------- + +Available on 20101031. Stable release containing only bug fixes. + +SeaBIOS 0.6.1.2 +--------------- + +Available on 20101113. Stable release containing only bug fixes. + +SeaBIOS 0.6.1.3 +--------------- + +Available on 20101226. Stable release containing only bug fixes. + +SeaBIOS 0.6.0 +============= + +Available on 20100326. Major changes in this release: + +* USB hub support +* USB drive booting support +* USB keyboard auto-repeat support +* USB EHCI controller support +* Several improvements to compatibility of PS2 port handlers for old + code +* Support for qemu e820 interface +* Several bug fixes and code cleanups + +SeaBIOS 0.5.1 +============= + +Available on 20100108. Major changes in this release: + +* Support for 32bit PCI BIOS calls +* Support for int1589 calls +* MPTable fixes for OpenBSD +* ATA DMA and bus-mastering support +* Several bug fixes and code cleanups + +SeaBIOS 0.5.0 +============= + +Available on 20091218. Major changes in this release: + +* Several enhancements ported from the Bochs BIOS derived code in qemu + and kvm +* Support for parallel hardware initialization to reduce bootup times +* Enable PCI option rom support by default (Bochs users must now + enable CONFIG_OPTIONROMS_DEPLOYED in src/config.h). Support added + for extracting option roms from qemu "fw_cfg". +* Support USB UHCI and OHCI controllers +* Initial support for USB keyboards +* SeaBIOS can now be greater than 64K +* Support for permanent low memory allocations +* APIC "local interrupts" now enabled in SeaBIOS (on emulators) +* Several bug fixes and code cleanups + +SeaBIOS 0.4.2 +============= + +Available on 20090909. Major changes in this release: + +* Implement Post Memory Manager (PMM) support. Use equivalent "malloc" + functions for internal allocations as well. +* Refactor disk "block" interface for greater expandability +* Support CBFS based floppy images +* Allow boot menu to select either floppy to boot from +* Increase ebda size to store a CDROM harddrive/floppy emulation + buffer +* Support systems with multiple vga cards (only the card with the + legacy IO ranges mapped will have its option rom executed) +* Make option rom memory be writable during option rom execution (on + emulators) +* Compile version number into code and report on each boot +* Several bug fixes and code cleanups + +SeaBIOS 0.4.1 +============= + +Available on 20090714. Major changes in this release: + +* Support older versions of gcc that predate "-fwhole-program" (eg, + v3.x) +* Add initial port of "LGPL vga bios" code into tree in "vgasrc/" + directory +* Handle ATA drives still "spinning up" during SeaBIOS drive detect +* Add support for option rom Boot Connection Vectors (BCV) +* Enhance boot menu to support booting from any drive or any cdrom +* Support flash based Coreboot File System (CBFS) +* Support booting from a CBFS "payload" +* Support coreboot table forwarder +* Support compile time definitions for multiple root PCI buses +* New tools/readserial.py tool +* Several bug fixes and code cleanups + +SeaBIOS 0.4.0 +============= + +Available on 20090206. Major changes in this release: + +* Add Bios Boot Specification (BBS) calls; add PnP call stubs +* Support option roms stored in PCI rom BAR +* Support rebooting on ctrl+alt+delete key press +* Scan PCI devices for ATA adapters (don't assume legacy ISA ATA ports + are valid) +* Attempt to automatically determine gcc capabilities/bugs during + build +* Add script to layout 16bit sections at fixed offsets and in + compacted space +* Introduce timestamp counter based delays +* Support POST calls that are really a resume +* Use new stack in EBDA for int13 disk calls to reduce stack usage +* Support the EBDA being relocated by option roms +* Move many variables from EBDA to global variables (stored in + f-segment) +* Support for PCI bridges when iterating through PCI device list +* Initial port of several KVM specific features from their Bochs BIOS + derived code +* Access BDA using segment 0x40 and IVT using segment 0x00 (which + could be important for 16bit protected mode callers) +* Several bug fixes and code cleanups + +SeaBIOS 0.3.0 +============= + +Available on 20080817. Major changes in this release: + +* Run boot code (int18/19) in 32bit mode +* Rewrite of PS2 port handling - new code is more compatible with real + hardware +* Initial support for int155f VGA option rom calls +* Several bug fixes and code cleanups + +SeaBIOS 0.2.3 +============= + +Available on 20080702. Major changes in this release: + +* Initial support for running on real hardware with coreboot +* Support parsing coreboot tables +* Support relocating bios tables from high memory when running under + coreboot +* Dynamic e820 map generation +* Serial debug support +* New tools/checkstack.py tool +* Several bug fixes and code cleanups + +SeaBIOS 0.2.2 +============= + +Formerly known as "legacybios". Available on 20080501. Major changes +in this release: + +* Several bug fixes and code cleanups + +SeaBIOS 0.2.1 +============= + +Formerly known as "legacybios". Available on 20080406. Major changes +in this release: + +* Port of boot menu code from Bochs BIOS +* Several bug fixes and code cleanups + +SeaBIOS 0.2.0 +============= + +Formerly known as "legacybios". Available on 20080330. Major changes +in this release: + +* Completion of initial port of Bochs BIOS code to gcc. diff --git a/roms/seabios/docs/SeaBIOS.md b/roms/seabios/docs/SeaBIOS.md new file mode 100644 index 0000000..831bfce --- /dev/null +++ b/roms/seabios/docs/SeaBIOS.md @@ -0,0 +1,15 @@ +SeaBIOS is an open source implementation of a 16bit X86 BIOS. SeaBIOS +can run in an emulator or it can run natively on X86 hardware with the +use of [coreboot](http://www.coreboot.org/). + +SeaBIOS is the default BIOS for [qemu](http://www.qemu.org/) and +[kvm](http://www.linux-kvm.org/). + +The [coreboot SeaBIOS](http://www.coreboot.org/SeaBIOS) page has +information on using SeaBIOS in coreboot. Please see the +[releases](Releases) page for information on recent releases. See the +[download](Download) page to obtain SeaBIOS. + +Please join the [mailing list](Mailinglist) to contribute to +SeaBIOS. Information on the internals of SeaBIOS is available on the +[Developer Documentation](Developer Documentation) page. diff --git a/roms/seabios/scripts/acpi_extract_preprocess.py b/roms/seabios/scripts/acpi_extract_preprocess.py index 6ef7df0..2698118 100755 --- a/roms/seabios/scripts/acpi_extract_preprocess.py +++ b/roms/seabios/scripts/acpi_extract_preprocess.py @@ -6,7 +6,7 @@ # Read a preprocessed ASL listing and put each ACPI_EXTRACT # directive in a comment, to make iasl skip it. # We also put each directive on a new line, the machinery -# in tools/acpi_extract.py requires this. +# in scripts/acpi_extract.py requires this. import re import sys diff --git a/roms/seabios/scripts/checkstack.py b/roms/seabios/scripts/checkstack.py index 62fef36..b49b6c8 100755 --- a/roms/seabios/scripts/checkstack.py +++ b/roms/seabios/scripts/checkstack.py @@ -7,7 +7,7 @@ # This file may be distributed under the terms of the GNU GPLv3 license. # Usage: -# objdump -m i386 -M i8086 -M suffix -d out/rom16.o | tools/checkstack.py +# objdump -m i386 -M i8086 -M suffix -d out/rom16.o | scripts/checkstack.py import sys import re @@ -181,6 +181,8 @@ def calc(): noteCall(cur, subfuncs, insnaddr, calladdr, 0) elif insn.startswith('calll'): noteCall(cur, subfuncs, insnaddr, calladdr, stackusage + 4) + elif insn.startswith('callw'): + noteCall(cur, subfuncs, insnaddr, calladdr, stackusage + 2) else: print("unknown call", ref) noteCall(cur, subfuncs, insnaddr, calladdr, stackusage) diff --git a/roms/seabios/scripts/kconfig/Makefile b/roms/seabios/scripts/kconfig/Makefile index 3f37c96..1c12936 100644 --- a/roms/seabios/scripts/kconfig/Makefile +++ b/roms/seabios/scripts/kconfig/Makefile @@ -34,11 +34,11 @@ oldconfig: $(obj)/conf silentoldconfig: $(obj)/conf @echo " Build Kconfig config file" - $(Q)mkdir -p include/generated + $(Q)mkdir -p include/config include/generated $(Q)$< --$@ $(Kconfig) localyesconfig localmodconfig: $(obj)/streamline_config.pl $(obj)/conf - $(Q)mkdir -p include/generated + $(Q)mkdir -p include/config include/generated $(Q)perl $< --$@ $(srctree) $(Kconfig) > .tmp.config $(Q)if [ -f .config ]; then \ cmp -s .tmp.config .config || \ diff --git a/roms/seabios/scripts/kconfig/check.sh b/roms/seabios/scripts/kconfig/check.sh index 854d9c7..55b79ba 100755 --- a/roms/seabios/scripts/kconfig/check.sh +++ b/roms/seabios/scripts/kconfig/check.sh @@ -11,4 +11,3 @@ EOF if [ ! "$?" -eq "0" ]; then echo -DKBUILD_NO_NLS; fi - diff --git a/roms/seabios/scripts/kconfig/conf.c b/roms/seabios/scripts/kconfig/conf.c index d19944f..fef75fc 100644 --- a/roms/seabios/scripts/kconfig/conf.c +++ b/roms/seabios/scripts/kconfig/conf.c @@ -696,7 +696,7 @@ int main(int ac, char **av) } else if (input_mode == savedefconfig) { if (conf_write_defconfig(defconfig_file)) { fprintf(stderr, _("n*** Error while saving defconfig to: %s\n\n"), - defconfig_file); + defconfig_file); return 1; } } else if (input_mode != listnewconfig) { diff --git a/roms/seabios/scripts/kconfig/confdata.c b/roms/seabios/scripts/kconfig/confdata.c index eb52ed5..08e7559 100644 --- a/roms/seabios/scripts/kconfig/confdata.c +++ b/roms/seabios/scripts/kconfig/confdata.c @@ -1186,7 +1186,10 @@ bool conf_set_all_new_symbols(enum conf_def_mode mode) sym->def[S_DEF_USER].tri = mod; break; case def_no: - sym->def[S_DEF_USER].tri = no; + if (sym->flags & SYMBOL_ALLNOCONFIG_Y) + sym->def[S_DEF_USER].tri = yes; + else + sym->def[S_DEF_USER].tri = no; break; case def_random: sym->def[S_DEF_USER].tri = no; diff --git a/roms/seabios/scripts/kconfig/expr.h b/roms/seabios/scripts/kconfig/expr.h index ba663e1..412ea8a 100644 --- a/roms/seabios/scripts/kconfig/expr.h +++ b/roms/seabios/scripts/kconfig/expr.h @@ -109,6 +109,9 @@ struct symbol { /* choice values need to be set before calculating this symbol value */ #define SYMBOL_NEED_SET_CHOICE_VALUES 0x100000 +/* Set symbol to y if allnoconfig; used for symbols that hide others */ +#define SYMBOL_ALLNOCONFIG_Y 0x200000 + #define SYMBOL_MAXLENGTH 256 #define SYMBOL_HASHSIZE 9973 diff --git a/roms/seabios/scripts/kconfig/gconf.c b/roms/seabios/scripts/kconfig/gconf.c index f2bee70..d0a35b2 100644 --- a/roms/seabios/scripts/kconfig/gconf.c +++ b/roms/seabios/scripts/kconfig/gconf.c @@ -1404,7 +1404,7 @@ static void display_tree(struct menu *menu) && (tree == tree2)) continue; /* - if (((menu != &rootmenu) && !(menu->flags & MENU_ROOT)) + if (((menu != &rootmenu) && !(menu->flags & MENU_ROOT)) || (view_mode == FULL_VIEW) || (view_mode == SPLIT_VIEW))*/ diff --git a/roms/seabios/scripts/kconfig/lkc.h b/roms/seabios/scripts/kconfig/lkc.h index 09f4edf..d5daa7a 100644 --- a/roms/seabios/scripts/kconfig/lkc.h +++ b/roms/seabios/scripts/kconfig/lkc.h @@ -61,6 +61,7 @@ enum conf_def_mode { #define T_OPT_MODULES 1 #define T_OPT_DEFCONFIG_LIST 2 #define T_OPT_ENV 3 +#define T_OPT_ALLNOCONFIG_Y 4 struct kconf_id { int name; diff --git a/roms/seabios/scripts/kconfig/lxdialog/checklist.c b/roms/seabios/scripts/kconfig/lxdialog/checklist.c index 3b15c08..8d016fa 100644 --- a/roms/seabios/scripts/kconfig/lxdialog/checklist.c +++ b/roms/seabios/scripts/kconfig/lxdialog/checklist.c @@ -168,13 +168,13 @@ do_resize: /* create new window for the list */ list = subwin(dialog, list_height, list_width, y + box_y + 1, - x + box_x + 1); + x + box_x + 1); keypad(list, TRUE); /* draw a box around the list items */ draw_box(dialog, box_y, box_x, list_height + 2, list_width + 2, - dlg.menubox_border.atr, dlg.menubox.atr); + dlg.menubox_border.atr, dlg.menubox.atr); /* Find length of longest item in order to center checklist */ check_x = 0; diff --git a/roms/seabios/scripts/kconfig/lxdialog/inputbox.c b/roms/seabios/scripts/kconfig/lxdialog/inputbox.c index 447a582..d58de1d 100644 --- a/roms/seabios/scripts/kconfig/lxdialog/inputbox.c +++ b/roms/seabios/scripts/kconfig/lxdialog/inputbox.c @@ -42,7 +42,7 @@ static void print_buttons(WINDOW * dialog, int height, int width, int selected) * Display a dialog box for inputing a string */ int dialog_inputbox(const char *title, const char *prompt, int height, int width, - const char *init) + const char *init) { int i, x, y, box_y, box_x, box_width; int input_x = 0, key = 0, button = -1; diff --git a/roms/seabios/scripts/kconfig/lxdialog/menubox.c b/roms/seabios/scripts/kconfig/lxdialog/menubox.c index c93de0b..11ae9ad 100644 --- a/roms/seabios/scripts/kconfig/lxdialog/menubox.c +++ b/roms/seabios/scripts/kconfig/lxdialog/menubox.c @@ -64,7 +64,7 @@ static int menu_width, item_x; * Print menu item */ static void do_print_item(WINDOW * win, const char *item, int line_y, - int selected, int hotkey) + int selected, int hotkey) { int j; char *menu_item = malloc(menu_width + 1); @@ -182,7 +182,7 @@ static void do_scroll(WINDOW *win, int *scroll, int n) * Display a menu for choosing among a number of options */ int dialog_menu(const char *title, const char *prompt, - const void *selected, int *s_scroll) + const void *selected, int *s_scroll) { int i, j, x, y, box_x, box_y; int height, width, menu_height; diff --git a/roms/seabios/scripts/kconfig/lxdialog/util.c b/roms/seabios/scripts/kconfig/lxdialog/util.c index 58a8289..f7abdeb 100644 --- a/roms/seabios/scripts/kconfig/lxdialog/util.c +++ b/roms/seabios/scripts/kconfig/lxdialog/util.c @@ -623,7 +623,7 @@ void item_make(const char *fmt, ...) void item_add_str(const char *fmt, ...) { va_list ap; - size_t avail; + size_t avail; avail = sizeof(item_cur->node.str) - strlen(item_cur->node.str); diff --git a/roms/seabios/scripts/kconfig/mconf.c b/roms/seabios/scripts/kconfig/mconf.c index 59184bb..14cea74 100644 --- a/roms/seabios/scripts/kconfig/mconf.c +++ b/roms/seabios/scripts/kconfig/mconf.c @@ -299,7 +299,7 @@ static void set_config_filename(const char *config_filename) int size; size = snprintf(menu_backtitle, sizeof(menu_backtitle), - "%s - %s", config_filename, rootmenu.prompt->text); + "%s - %s", config_filename, rootmenu.prompt->text); if (size >= sizeof(menu_backtitle)) menu_backtitle[sizeof(menu_backtitle)-1] = '\0'; set_dialog_backtitle(menu_backtitle); @@ -1034,4 +1034,3 @@ int main(int ac, char **av) return res; } - diff --git a/roms/seabios/scripts/kconfig/menu.c b/roms/seabios/scripts/kconfig/menu.c index db1512a..a26cc5d 100644 --- a/roms/seabios/scripts/kconfig/menu.c +++ b/roms/seabios/scripts/kconfig/menu.c @@ -217,6 +217,9 @@ void menu_add_option(int token, char *arg) case T_OPT_ENV: prop_add_env(arg); break; + case T_OPT_ALLNOCONFIG_Y: + current_entry->sym->flags |= SYMBOL_ALLNOCONFIG_Y; + break; } } @@ -255,8 +258,8 @@ static void sym_check_prop(struct symbol *sym) "config symbol '%s' uses select, but is " "not boolean or tristate", sym->name); else if (sym2->type != S_UNKNOWN && - sym2->type != S_BOOLEAN && - sym2->type != S_TRISTATE) + sym2->type != S_BOOLEAN && + sym2->type != S_TRISTATE) prop_warn(prop, "'%s' has wrong type. 'select' only " "accept arguments of boolean and " @@ -265,7 +268,7 @@ static void sym_check_prop(struct symbol *sym) case P_RANGE: if (sym->type != S_INT && sym->type != S_HEX) prop_warn(prop, "range is only allowed " - "for int or hex symbols"); + "for int or hex symbols"); if (!menu_validate_number(sym, prop->expr->left.sym) || !menu_validate_number(sym, prop->expr->right.sym)) prop_warn(prop, "range is invalid"); diff --git a/roms/seabios/scripts/kconfig/nconf.c b/roms/seabios/scripts/kconfig/nconf.c index 4fbecd2..984489e 100644 --- a/roms/seabios/scripts/kconfig/nconf.c +++ b/roms/seabios/scripts/kconfig/nconf.c @@ -1554,4 +1554,3 @@ int main(int ac, char **av) endwin(); return 0; } - diff --git a/roms/seabios/scripts/kconfig/streamline_config.pl b/roms/seabios/scripts/kconfig/streamline_config.pl index 4606cdf..9cb8522 100644 --- a/roms/seabios/scripts/kconfig/streamline_config.pl +++ b/roms/seabios/scripts/kconfig/streamline_config.pl @@ -219,6 +219,13 @@ sub read_kconfig { $depends{$config} = $1; } elsif ($state eq "DEP" && /^\s*depends\s+on\s+(.*)$/) { $depends{$config} .= " " . $1; + } elsif ($state eq "DEP" && /^\s*def(_(bool|tristate)|ault)\s+(\S.*)$/) { + my $dep = $3; + if ($dep !~ /^\s*(y|m|n)\s*$/) { + $dep =~ s/.*\sif\s+//; + $depends{$config} .= " " . $dep; + dprint "Added default depends $dep to $config\n"; + } # Get the configs that select this config } elsif ($state ne "NONE" && /^\s*select\s+(\S+)/) { @@ -582,7 +589,7 @@ while ($repeat) { # Now we need to see if we have to check selects; loop_select; -} +} my %setconfigs; diff --git a/roms/seabios/scripts/kconfig/util.c b/roms/seabios/scripts/kconfig/util.c index 6e7fbf1..94f9c83 100644 --- a/roms/seabios/scripts/kconfig/util.c +++ b/roms/seabios/scripts/kconfig/util.c @@ -155,5 +155,3 @@ void *xcalloc(size_t nmemb, size_t size) fprintf(stderr, "Out of memory.\n"); exit(1); } - - diff --git a/roms/seabios/scripts/kconfig/zconf.gperf b/roms/seabios/scripts/kconfig/zconf.gperf index f14ab41..b6ac02d 100644 --- a/roms/seabios/scripts/kconfig/zconf.gperf +++ b/roms/seabios/scripts/kconfig/zconf.gperf @@ -44,4 +44,5 @@ on, T_ON, TF_PARAM modules, T_OPT_MODULES, TF_OPTION defconfig_list, T_OPT_DEFCONFIG_LIST,TF_OPTION env, T_OPT_ENV, TF_OPTION +allnoconfig_y, T_OPT_ALLNOCONFIG_Y,TF_OPTION %% diff --git a/roms/seabios/scripts/kconfig/zconf.hash.c_shipped b/roms/seabios/scripts/kconfig/zconf.hash.c_shipped index 40df000..c77a8ef 100644 --- a/roms/seabios/scripts/kconfig/zconf.hash.c_shipped +++ b/roms/seabios/scripts/kconfig/zconf.hash.c_shipped @@ -55,10 +55,10 @@ kconf_id_hash (register const char *str, register unsigned int len) 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, - 73, 73, 73, 73, 73, 73, 73, 73, 25, 25, + 73, 73, 73, 73, 73, 73, 73, 5, 25, 25, 0, 0, 0, 5, 0, 0, 73, 73, 5, 0, 10, 5, 45, 73, 20, 20, 0, 15, 15, 73, - 20, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 20, 5, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, @@ -106,6 +106,7 @@ struct kconf_id_strings_t char kconf_id_strings_str23[sizeof("mainmenu")]; char kconf_id_strings_str25[sizeof("menuconfig")]; char kconf_id_strings_str27[sizeof("modules")]; + char kconf_id_strings_str28[sizeof("allnoconfig_y")]; char kconf_id_strings_str29[sizeof("menu")]; char kconf_id_strings_str31[sizeof("select")]; char kconf_id_strings_str32[sizeof("comment")]; @@ -141,6 +142,7 @@ static const struct kconf_id_strings_t kconf_id_strings_contents = "mainmenu", "menuconfig", "modules", + "allnoconfig_y", "menu", "select", "comment", @@ -170,7 +172,7 @@ kconf_id_lookup (register const char *str, register unsigned int len) { enum { - TOTAL_KEYWORDS = 32, + TOTAL_KEYWORDS = 33, MIN_WORD_LENGTH = 2, MAX_WORD_LENGTH = 14, MIN_HASH_VALUE = 2, @@ -219,7 +221,8 @@ kconf_id_lookup (register const char *str, register unsigned int len) {-1}, #line 44 "scripts/kconfig/zconf.gperf" {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str27, T_OPT_MODULES, TF_OPTION}, - {-1}, +#line 47 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str28, T_OPT_ALLNOCONFIG_Y,TF_OPTION}, #line 16 "scripts/kconfig/zconf.gperf" {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str29, T_MENU, TF_COMMAND}, {-1}, @@ -282,5 +285,5 @@ kconf_id_lookup (register const char *str, register unsigned int len) } return 0; } -#line 47 "scripts/kconfig/zconf.gperf" +#line 48 "scripts/kconfig/zconf.gperf" diff --git a/roms/seabios/scripts/kconfig/zconf.l b/roms/seabios/scripts/kconfig/zconf.l index 1a9f53e..6c62d93 100644 --- a/roms/seabios/scripts/kconfig/zconf.l +++ b/roms/seabios/scripts/kconfig/zconf.l @@ -27,8 +27,8 @@ static char *text; static int text_size, text_asize; struct buffer { - struct buffer *parent; - YY_BUFFER_STATE state; + struct buffer *parent; + YY_BUFFER_STATE state; }; struct buffer *current_buf; diff --git a/roms/seabios/scripts/kconfig/zconf.lex.c_shipped b/roms/seabios/scripts/kconfig/zconf.lex.c_shipped index a0521aa..349a7f2 100644 --- a/roms/seabios/scripts/kconfig/zconf.lex.c_shipped +++ b/roms/seabios/scripts/kconfig/zconf.lex.c_shipped @@ -789,8 +789,8 @@ static char *text; static int text_size, text_asize; struct buffer { - struct buffer *parent; - YY_BUFFER_STATE state; + struct buffer *parent; + YY_BUFFER_STATE state; }; struct buffer *current_buf; diff --git a/roms/seabios/scripts/kconfig/zconf.tab.c_shipped b/roms/seabios/scripts/kconfig/zconf.tab.c_shipped index 25ae16a..de5e84e 100644 --- a/roms/seabios/scripts/kconfig/zconf.tab.c_shipped +++ b/roms/seabios/scripts/kconfig/zconf.tab.c_shipped @@ -2314,7 +2314,7 @@ void conf_parse(const char *name) for_all_symbols(i, sym) { if (sym_check_deps(sym)) zconfnerrs++; - } + } if (zconfnerrs) exit(1); sym_set_change_count(1); diff --git a/roms/seabios/scripts/kconfig/zconf.y b/roms/seabios/scripts/kconfig/zconf.y index 0653886..0f683cf 100644 --- a/roms/seabios/scripts/kconfig/zconf.y +++ b/roms/seabios/scripts/kconfig/zconf.y @@ -510,7 +510,7 @@ void conf_parse(const char *name) for_all_symbols(i, sym) { if (sym_check_deps(sym)) zconfnerrs++; - } + } if (zconfnerrs) exit(1); sym_set_change_count(1); diff --git a/roms/seabios/scripts/layoutrom.py b/roms/seabios/scripts/layoutrom.py index b325406..dd770fe 100755 --- a/roms/seabios/scripts/layoutrom.py +++ b/roms/seabios/scripts/layoutrom.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # Script to analyze code and arrange ld sections. # -# Copyright (C) 2008-2010 Kevin O'Connor +# Copyright (C) 2008-2014 Kevin O'Connor # # This file may be distributed under the terms of the GNU GPLv3 license. @@ -10,7 +10,7 @@ import sys # LD script headers/trailers COMMONHEADER = """ -/* DO NOT EDIT! This is an autogenerated file. See tools/layoutrom.py. */ +/* DO NOT EDIT! This is an autogenerated file. See scripts/layoutrom.py. */ OUTPUT_FORMAT("elf32-i386") OUTPUT_ARCH("i386") SECTIONS @@ -149,6 +149,10 @@ def fitSections(sections, fillsections): def getSectionsCategory(sections, category): return [section for section in sections if section.category == category] +# Return the subset of sections with a given fileid +def getSectionsFileid(sections, fileid): + return [section for section in sections if section.fileid == fileid] + # Return the subset of sections with a given name prefix def getSectionsPrefix(sections, prefix): return [section for section in sections @@ -156,127 +160,131 @@ def getSectionsPrefix(sections, prefix): # The sections (and associated information) to be placed in output rom class LayoutInfo: + sections = None genreloc = None - sections16 = sec16_start = sec16_align = None - sections32seg = sec32seg_start = sec32seg_align = None - sections32flat = sec32flat_start = sec32flat_align = None - sections32init = sec32init_start = sec32init_align = None - sections32low = sec32low_start = sec32low_align = None - sections32fseg = sec32fseg_start = sec32fseg_align = None + sec32init_start = sec32init_end = sec32init_align = None + sec32low_start = sec32low_end = None + zonelow_base = final_sec32low_start = None zonefseg_start = zonefseg_end = None final_readonly_start = None - zonelow_base = final_sec32low_start = None - exportsyms = varlowsyms = None + varlowsyms = entrysym = None # Determine final memory addresses for sections def doLayout(sections, config, genreloc): li = LayoutInfo() + li.sections = sections li.genreloc = genreloc # Determine 16bit positions - li.sections16 = getSectionsCategory(sections, '16') - textsections = getSectionsPrefix(li.sections16, '.text.') - rodatasections = ( - getSectionsPrefix(li.sections16, '.rodata.str1.1') - + getSectionsPrefix(li.sections16, '.rodata.__func__.') - + getSectionsPrefix(li.sections16, '.rodata.__PRETTY_FUNCTION__.')) - datasections = getSectionsPrefix(li.sections16, '.data16.') - fixedsections = getSectionsPrefix(li.sections16, '.fixedaddr.') + sections16 = getSectionsCategory(sections, '16') + textsections = getSectionsPrefix(sections16, '.text.') + rodatasections = getSectionsPrefix(sections16, '.rodata') + datasections = getSectionsPrefix(sections16, '.data16.') + fixedsections = getSectionsCategory(sections, 'fixed') firstfixed = fitSections(fixedsections, textsections) remsections = [s for s in textsections+rodatasections+datasections if s.finalloc is None] - li.sec16_start, li.sec16_align = setSectionsStart( + sec16_start, sec16_align = setSectionsStart( remsections, firstfixed, segoffset=BUILD_BIOS_ADDR) # Determine 32seg positions - li.sections32seg = getSectionsCategory(sections, '32seg') - textsections = getSectionsPrefix(li.sections32seg, '.text.') - rodatasections = ( - getSectionsPrefix(li.sections32seg, '.rodata.str1.1') - + getSectionsPrefix(li.sections32seg, '.rodata.__func__.') - + getSectionsPrefix(li.sections32seg, '.rodata.__PRETTY_FUNCTION__.')) - datasections = getSectionsPrefix(li.sections32seg, '.data32seg.') - - li.sec32seg_start, li.sec32seg_align = setSectionsStart( - textsections + rodatasections + datasections, li.sec16_start + sections32seg = getSectionsCategory(sections, '32seg') + textsections = getSectionsPrefix(sections32seg, '.text.') + rodatasections = getSectionsPrefix(sections32seg, '.rodata') + datasections = getSectionsPrefix(sections32seg, '.data32seg.') + + sec32seg_start, sec32seg_align = setSectionsStart( + textsections + rodatasections + datasections, sec16_start , segoffset=BUILD_BIOS_ADDR) - # Determine "fseg memory" data positions - li.sections32fseg = getSectionsCategory(sections, '32fseg') + # Determine 32bit "fseg memory" data positions + sections32textfseg = getSectionsCategory(sections, '32textfseg') + sec32textfseg_start, sec32textfseg_align = setSectionsStart( + sections32textfseg, sec32seg_start, 16) - li.sec32fseg_start, li.sec32fseg_align = setSectionsStart( - li.sections32fseg, li.sec32seg_start, 16 + sections32fseg = getSectionsCategory(sections, '32fseg') + sec32fseg_start, sec32fseg_align = setSectionsStart( + sections32fseg, sec32textfseg_start, 16 , segoffset=BUILD_BIOS_ADDR) # Determine 32flat runtime positions - li.sections32flat = getSectionsCategory(sections, '32flat') - textsections = getSectionsPrefix(li.sections32flat, '.text.') - rodatasections = getSectionsPrefix(li.sections32flat, '.rodata') - datasections = getSectionsPrefix(li.sections32flat, '.data.') - bsssections = getSectionsPrefix(li.sections32flat, '.bss.') + sections32flat = getSectionsCategory(sections, '32flat') + textsections = getSectionsPrefix(sections32flat, '.text.') + rodatasections = getSectionsPrefix(sections32flat, '.rodata') + datasections = getSectionsPrefix(sections32flat, '.data.') + bsssections = getSectionsPrefix(sections32flat, '.bss.') - li.sec32flat_start, li.sec32flat_align = setSectionsStart( + sec32flat_start, sec32flat_align = setSectionsStart( textsections + rodatasections + datasections + bsssections - , li.sec32fseg_start, 16) + , sec32fseg_start, 16) # Determine 32flat init positions - li.sections32init = getSectionsCategory(sections, '32init') - init32_textsections = getSectionsPrefix(li.sections32init, '.text.') - init32_rodatasections = getSectionsPrefix(li.sections32init, '.rodata') - init32_datasections = getSectionsPrefix(li.sections32init, '.data.') - init32_bsssections = getSectionsPrefix(li.sections32init, '.bss.') + sections32init = getSectionsCategory(sections, '32init') + init32_textsections = getSectionsPrefix(sections32init, '.text.') + init32_rodatasections = getSectionsPrefix(sections32init, '.rodata') + init32_datasections = getSectionsPrefix(sections32init, '.data.') + init32_bsssections = getSectionsPrefix(sections32init, '.bss.') - li.sec32init_start, li.sec32init_align = setSectionsStart( + sec32init_start, sec32init_align = setSectionsStart( init32_textsections + init32_rodatasections + init32_datasections + init32_bsssections - , li.sec32flat_start, 16) + , sec32flat_start, 16) # Determine location of ZoneFSeg memory. - li.zonefseg_end = li.sec32flat_start + zonefseg_end = sec32flat_start if not genreloc: - li.zonefseg_end = li.sec32init_start - li.zonefseg_start = BUILD_BIOS_ADDR - if li.zonefseg_start + BUILD_MIN_BIOSTABLE > li.zonefseg_end: + zonefseg_end = sec32init_start + zonefseg_start = BUILD_BIOS_ADDR + if zonefseg_start + BUILD_MIN_BIOSTABLE > zonefseg_end: # Not enough ZoneFSeg space - force a minimum space. - li.zonefseg_end = li.sec32fseg_start - li.zonefseg_start = li.zonefseg_end - BUILD_MIN_BIOSTABLE - li.sec32flat_start, li.sec32flat_align = setSectionsStart( + zonefseg_end = sec32fseg_start + zonefseg_start = zonefseg_end - BUILD_MIN_BIOSTABLE + sec32flat_start, sec32flat_align = setSectionsStart( textsections + rodatasections + datasections + bsssections - , li.zonefseg_start, 16) - li.sec32init_start, li.sec32init_align = setSectionsStart( + , zonefseg_start, 16) + sec32init_start, sec32init_align = setSectionsStart( init32_textsections + init32_rodatasections + init32_datasections + init32_bsssections - , li.sec32flat_start, 16) - li.final_readonly_start = min(BUILD_BIOS_ADDR, li.sec32flat_start) + , sec32flat_start, 16) + li.sec32init_start = sec32init_start + li.sec32init_end = sec32flat_start + li.sec32init_align = sec32init_align + final_readonly_start = min(BUILD_BIOS_ADDR, sec32flat_start) if not genreloc: - li.final_readonly_start = min(BUILD_BIOS_ADDR, li.sec32init_start) + final_readonly_start = min(BUILD_BIOS_ADDR, sec32init_start) + li.zonefseg_start = zonefseg_start + li.zonefseg_end = zonefseg_end + li.final_readonly_start = final_readonly_start # Determine "low memory" data positions - li.sections32low = getSectionsCategory(sections, '32low') - sec32low_end = li.sec32init_start + sections32low = getSectionsCategory(sections, '32low') + sec32low_end = sec32init_start if config.get('CONFIG_MALLOC_UPPERMEMORY'): - final_sec32low_end = li.final_readonly_start + final_sec32low_end = final_readonly_start zonelow_base = final_sec32low_end - 64*1024 - li.zonelow_base = max(BUILD_ROM_START, alignpos(zonelow_base, 2*1024)) + zonelow_base = max(BUILD_ROM_START, alignpos(zonelow_base, 2*1024)) else: final_sec32low_end = BUILD_LOWRAM_END - li.zonelow_base = final_sec32low_end - 64*1024 + zonelow_base = final_sec32low_end - 64*1024 relocdelta = final_sec32low_end - sec32low_end li.sec32low_start, li.sec32low_align = setSectionsStart( - li.sections32low, sec32low_end, 16 - , segoffset=li.zonelow_base - relocdelta) + sections32low, sec32low_end, 16 + , segoffset=zonelow_base - relocdelta) + li.sec32low_end = sec32low_end + li.zonelow_base = zonelow_base li.final_sec32low_start = li.sec32low_start + relocdelta # Print statistics - size16 = BUILD_BIOS_ADDR + BUILD_BIOS_SIZE - li.sec16_start - size32seg = li.sec16_start - li.sec32seg_start - size32fseg = li.sec32seg_start - li.sec32fseg_start - size32flat = li.sec32fseg_start - li.sec32flat_start - size32init = li.sec32flat_start - li.sec32init_start - sizelow = sec32low_end - li.sec32low_start + size16 = BUILD_BIOS_ADDR + BUILD_BIOS_SIZE - sec16_start + size32seg = sec16_start - sec32seg_start + size32textfseg = sec32seg_start - sec32textfseg_start + size32fseg = sec32textfseg_start - sec32fseg_start + size32flat = sec32fseg_start - sec32flat_start + size32init = sec32flat_start - sec32init_start + sizelow = li.sec32low_end - li.sec32low_start print("16bit size: %d" % size16) print("32bit segmented size: %d" % size32seg) - print("32bit flat size: %d" % size32flat) + print("32bit flat size: %d" % (size32flat + size32textfseg)) print("32bit flat init size: %d" % size32init) print("Lowmem size: %d" % sizelow) print("f-segment var size: %d" % size32fseg) @@ -305,6 +313,16 @@ def outXRefs(sections, useseg=0, exportsyms=[], forcedelta=0): out += "%s = 0x%x ;\n" % (symbolname, loc + forcedelta + symbol.offset) return out +# Write LD script includes for the given sections +def outSections(sections, useseg=0): + out = "" + for section in sections: + loc = section.finalloc + if useseg: + loc = section.finalsegloc + out += "%s 0x%x : { *(%s) }\n" % (section.name, loc, section.name) + return out + # Write LD script includes for the given sections using relative offsets def outRelSections(sections, startsym, useseg=0): sections = [(section.finalloc, section) for section in sections @@ -316,9 +334,9 @@ def outRelSections(sections, startsym, useseg=0): if useseg: loc = section.finalsegloc out += ". = ( 0x%x - %s ) ;\n" % (loc, startsym) - if section.name == '.rodata.str1.1': - out += "_rodata = . ;\n" - out += "*(%s)\n" % (section.name,) + if section.name in ('.rodata.str1.1', '.rodata'): + out += "_rodata%s = . ;\n" % (section.fileid,) + out += "*%s.*(%s)\n" % (section.fileid, section.name) return out # Build linker script output for a list of relocations. @@ -329,99 +347,74 @@ def strRelocs(outname, outrel, relocs): for pos in relocs]) + " %s_end = ABSOLUTE(.) ;\n" % (outname,)) -# Find all relocations in the given sections with the given attributes -def getRelocs(sections, type=None, category=None, notcategory=None): - out = [] - for section in sections: - for reloc in section.relocs: - if reloc.symbol.section is None: - continue - destcategory = reloc.symbol.section.category - if ((type is None or reloc.type == type) - and (category is None or destcategory == category) - and (notcategory is None or destcategory != notcategory)): - out.append(section.finalloc + reloc.offset) - return out - -# Return the start address and minimum alignment for a set of sections -def getSectionsStart(sections, defaddr=0): - return min([section.finalloc for section in sections - if section.finalloc is not None] or [defaddr]) +# Find relocations to the given sections +def getRelocs(sections, tosection, type=None): + return [section.finalloc + reloc.offset + for section in sections + for reloc in section.relocs + if (reloc.symbol.section in tosection + and (type is None or reloc.type == type))] # Output the linker scripts for all required sections. def writeLinkerScripts(li, out16, out32seg, out32flat): # Write 16bit linker script - out = outXRefs(li.sections16, useseg=1) + """ + filesections16 = getSectionsFileid(li.sections, '16') + out = outXRefs(filesections16, useseg=1) + """ zonelow_base = 0x%x ; _zonelow_seg = 0x%x ; - code16_start = 0x%x ; - .text16 code16_start : { %s - } """ % (li.zonelow_base, int(li.zonelow_base / 16), - li.sec16_start - BUILD_BIOS_ADDR, - outRelSections(li.sections16, 'code16_start', useseg=1)) + outSections(filesections16, useseg=1)) outfile = open(out16, 'w') outfile.write(COMMONHEADER + out + COMMONTRAILER) outfile.close() # Write 32seg linker script - out = outXRefs(li.sections32seg, useseg=1) + """ - code32seg_start = 0x%x ; - .text32seg code32seg_start : { -%s - } -""" % (li.sec32seg_start - BUILD_BIOS_ADDR, - outRelSections(li.sections32seg, 'code32seg_start', useseg=1)) + filesections32seg = getSectionsFileid(li.sections, '32seg') + out = (outXRefs(filesections32seg, useseg=1) + + outSections(filesections32seg, useseg=1)) outfile = open(out32seg, 'w') outfile.write(COMMONHEADER + out + COMMONTRAILER) outfile.close() # Write 32flat linker script - sections32all = (li.sections32flat + li.sections32init + li.sections32fseg) sec32all_start = li.sec32low_start relocstr = "" if li.genreloc: # Generate relocations - absrelocs = getRelocs( - li.sections32init, type='R_386_32', category='32init') - relrelocs = getRelocs( - li.sections32init, type='R_386_PC32', notcategory='32init') - initrelocs = getRelocs( - li.sections32flat + li.sections32low + li.sections16 - + li.sections32seg + li.sections32fseg, category='32init') + initsections = dict([ + (s, 1) for s in getSectionsCategory(li.sections, '32init')]) + noninitsections = dict([(s, 1) for s in li.sections + if s not in initsections]) + absrelocs = getRelocs(initsections, initsections, type='R_386_32') + relrelocs = getRelocs(initsections, noninitsections, type='R_386_PC32') + initrelocs = getRelocs(noninitsections, initsections) relocstr = (strRelocs("_reloc_abs", "code32init_start", absrelocs) + strRelocs("_reloc_rel", "code32init_start", relrelocs) + strRelocs("_reloc_init", "code32flat_start", initrelocs)) numrelocs = len(absrelocs + relrelocs + initrelocs) sec32all_start -= numrelocs * 4 - out = outXRefs(li.sections32low, exportsyms=li.varlowsyms + filesections32flat = getSectionsFileid(li.sections, '32flat') + out = outXRefs([], exportsyms=li.varlowsyms , forcedelta=li.final_sec32low_start-li.sec32low_start) - out += outXRefs(sections32all, exportsyms=li.exportsyms) + """ + out += outXRefs(filesections32flat, exportsyms=[li.entrysym]) + """ _reloc_min_align = 0x%x ; zonefseg_start = 0x%x ; zonefseg_end = 0x%x ; zonelow_base = 0x%x ; final_varlow_start = 0x%x ; final_readonly_start = 0x%x ; + varlow_start = 0x%x ; + varlow_end = 0x%x ; + code32init_start = 0x%x ; + code32init_end = 0x%x ; code32flat_start = 0x%x ; .text code32flat_start : { %s - varlow_start = ABSOLUTE(.) ; -%s - varlow_end = ABSOLUTE(.) ; - code32init_start = ABSOLUTE(.) ; -%s - code32init_end = ABSOLUTE(.) ; %s -%s - . = ( 0x%x - code32flat_start ) ; - *(.text32seg) - . = ( 0x%x - code32flat_start ) ; - *(.text16) code32flat_end = ABSOLUTE(.) ; } :text """ % (li.sec32init_align, @@ -430,122 +423,91 @@ def writeLinkerScripts(li, out16, out32seg, out32flat): li.zonelow_base, li.final_sec32low_start, li.final_readonly_start, + li.sec32low_start, + li.sec32low_end, + li.sec32init_start, + li.sec32init_end, sec32all_start, relocstr, - outRelSections(li.sections32low, 'code32flat_start'), - outRelSections(li.sections32init, 'code32flat_start'), - outRelSections(li.sections32flat, 'code32flat_start'), - outRelSections(li.sections32fseg, 'code32flat_start'), - li.sec32seg_start, - li.sec16_start) + outRelSections(li.sections, 'code32flat_start')) out = COMMONHEADER + out + COMMONTRAILER + """ -ENTRY(entry_elf) +ENTRY(%s) PHDRS { text PT_LOAD AT ( code32flat_start ) ; } -""" +""" % (li.entrysym.name,) outfile = open(out32flat, 'w') outfile.write(out) outfile.close() ###################################################################### -# Detection of init code +# Detection of unused sections and init sections ###################################################################### -def markRuntime(section, sections, chain=[]): - if (section is None or not section.keep or section.category is not None - or '.init.' in section.name or section.fileid != '32flat'): - return +# Visit all sections reachable from a given set of start sections +def findReachable(anchorsections, checkreloc, data): + anchorsections = dict([(section, []) for section in anchorsections]) + pending = list(anchorsections) + while pending: + section = pending.pop() + for reloc in section.relocs: + chain = anchorsections[section] + [section.name] + if not checkreloc(reloc, section, data, chain): + continue + nextsection = reloc.symbol.section + if nextsection not in anchorsections: + anchorsections[nextsection] = chain + pending.append(nextsection) + return anchorsections + +# Find "runtime" sections (ie, not init only sections). +def checkRuntime(reloc, rsection, data, chain): + section = reloc.symbol.section + if section is None or '.init.' in section.name: + return 0 if '.data.varinit.' in section.name: print("ERROR: %s is VARVERIFY32INIT but used from %s" % ( section.name, chain)) sys.exit(1) - section.category = '32flat' - # Recursively mark all sections this section points to - for reloc in section.relocs: - markRuntime(reloc.symbol.section, sections, chain + [section.name]) - -def findInit(sections): - # Recursively find and mark all "runtime" sections. - for section in sections: - if ('.data.varlow.' in section.name or '.data.varfseg.' in section.name - or '.runtime.' in section.name or '.export.' in section.name): - markRuntime(section, sections) - for section in sections: - if section.category is not None: - continue - if section.fileid == '32flat': - section.category = '32init' - else: - section.category = section.fileid - - -###################################################################### -# Section garbage collection -###################################################################### - -CFUNCPREFIX = [('_cfunc16_', 0), ('_cfunc32seg_', 1), ('_cfunc32flat_', 2)] + return 1 # Find and keep the section associated with a symbol (if available). -def keepsymbol(reloc, infos, pos, isxref): +def checkKeepSym(reloc, syms, fileid, isxref): symbolname = reloc.symbolname - mustbecfunc = 0 - for symprefix, needpos in CFUNCPREFIX: - if symbolname.startswith(symprefix): - if needpos != pos: - return -1 - symbolname = symbolname[len(symprefix):] - mustbecfunc = 1 - break - symbol = infos[pos][1].get(symbolname) + mustbecfunc = symbolname.startswith('_cfunc') + if mustbecfunc: + symprefix = '_cfunc' + fileid + '_' + if not symbolname.startswith(symprefix): + return 0 + symbolname = symbolname[len(symprefix):] + symbol = syms.get(symbolname) if (symbol is None or symbol.section is None or symbol.section.name.startswith('.discard.')): - return -1 + return 0 isdestcfunc = (symbol.section.name.startswith('.text.') and not symbol.section.name.startswith('.text.asm.')) if ((mustbecfunc and not isdestcfunc) or (not mustbecfunc and isdestcfunc and isxref)): - return -1 + return 0 reloc.symbol = symbol - keepsection(symbol.section, infos, pos) + return 1 + +# Resolve a relocation and check if it should be kept in the final binary. +def checkKeep(reloc, section, symbols, chain): + ret = checkKeepSym(reloc, symbols[section.fileid], section.fileid, 0) + if ret: + return ret + # Not in primary sections - it may be a cross 16/32 reference + for fileid in ('16', '32seg', '32flat'): + if fileid != section.fileid: + ret = checkKeepSym(reloc, symbols[fileid], fileid, 1) + if ret: + return ret return 0 -# Note required section, and recursively set all referenced sections -# as required. -def keepsection(section, infos, pos=0): - if section.keep: - # Already kept - nothing to do. - return - section.keep = 1 - # Keep all sections that this section points to - for reloc in section.relocs: - ret = keepsymbol(reloc, infos, pos, 0) - if not ret: - continue - # Not in primary sections - it may be a cross 16/32 reference - ret = keepsymbol(reloc, infos, (pos+1)%3, 1) - if not ret: - continue - ret = keepsymbol(reloc, infos, (pos+2)%3, 1) - if not ret: - continue - -# Determine which sections are actually referenced and need to be -# placed into the output file. -def gc(info16, info32seg, info32flat): - # infos = ((sections16, symbols16), (sect32seg, sym32seg) - # , (sect32flat, sym32flat)) - infos = (info16, info32seg, info32flat) - # Start by keeping sections that are globally visible. - for section in info16[0]: - if section.name.startswith('.fixedaddr.') or '.export.' in section.name: - keepsection(section, infos) - return [section for section in info16[0]+info32seg[0]+info32flat[0] - if section.keep] - ###################################################################### # Startup and input parsing @@ -553,7 +515,7 @@ def gc(info16, info32seg, info32flat): class Section: name = size = alignment = fileid = relocs = None - finalloc = finalsegloc = category = keep = None + finalloc = finalsegloc = category = None class Reloc: offset = type = symbolname = symbol = None class Symbol: @@ -676,31 +638,51 @@ def main(): config = scanconfig(cfgfile) # Figure out which sections to keep. - sections = gc(info16, info32seg, info32flat) - - # Separate 32bit flat into runtime and init parts - findInit(sections) - - # Note "low memory" and "fseg memory" parts - for section in getSectionsPrefix(sections, '.data.varlow.'): - section.category = '32low' - for section in getSectionsPrefix(sections, '.data.varfseg.'): - section.category = '32fseg' + allsections = info16[0] + info32seg[0] + info32flat[0] + symbols = {'16': info16[1], '32seg': info32seg[1], '32flat': info32flat[1]} + if config.get('CONFIG_COREBOOT'): + entrysym = symbols['16'].get('entry_elf') + elif config.get('CONFIG_CSM'): + entrysym = symbols['16'].get('entry_csm') + else: + entrysym = symbols['16'].get('reset_vector') + anchorsections = [entrysym.section] + [ + section for section in allsections + if section.name.startswith('.fixedaddr.')] + keepsections = findReachable(anchorsections, checkKeep, symbols) + sections = [section for section in allsections if section in keepsections] + + # Separate 32bit flat into runtime, init, and special variable parts + anchorsections = [ + section for section in sections + if ('.data.varlow.' in section.name or '.data.varfseg.' in section.name + or '.fixedaddr.' in section.name or '.runtime.' in section.name)] + runtimesections = findReachable(anchorsections, checkRuntime, None) + for section in sections: + if section.name.startswith('.data.varlow.'): + section.category = '32low' + elif section.name.startswith('.data.varfseg.'): + section.category = '32fseg' + elif section.name.startswith('.text.32fseg.'): + section.category = '32textfseg' + elif section.name.startswith('.fixedaddr.'): + section.category = 'fixed' + elif section.fileid == '32flat' and section not in runtimesections: + section.category = '32init' + else: + section.category = section.fileid # Determine the final memory locations of each kept section. - genreloc = '_reloc_abs_start' in info32flat[1] + genreloc = '_reloc_abs_start' in symbols['32flat'] li = doLayout(sections, config, genreloc) # Exported symbols - li.exportsyms = [symbol for symbol in info16[1].values() - if (symbol.section is not None - and '.export.' in symbol.section.name - and symbol.name != symbol.section.name)] - li.varlowsyms = [symbol for symbol in info32flat[1].values() + li.varlowsyms = [symbol for symbol in symbols['32flat'].values() if (symbol.section is not None and symbol.section.finalloc is not None and '.data.varlow.' in symbol.section.name and symbol.name != symbol.section.name)] + li.entrysym = entrysym # Write out linker script files. writeLinkerScripts(li, out16, out32seg, out32flat) diff --git a/roms/seabios/scripts/readserial.py b/roms/seabios/scripts/readserial.py index 4f29648..a7383e8 100755 --- a/roms/seabios/scripts/readserial.py +++ b/roms/seabios/scripts/readserial.py @@ -6,12 +6,9 @@ # This file may be distributed under the terms of the GNU GPLv3 license. # Usage: -# tools/readserial.py /dev/ttyUSB0 115200 +# scripts/readserial.py /dev/ttyUSB0 115200 -import sys -import time -import select -import optparse +import sys, os, time, select, optparse from python23compat import as_bytes @@ -65,7 +62,7 @@ def readserial(infile, logfile, byteadjust): res = select.select([infile, sys.stdin], [], []) except KeyboardInterrupt: sys.stdout.write("\n") - break + return -1 if sys.stdin in res[0]: # Got keyboard input - force reset on next serial input sys.stdin.read(1) @@ -74,7 +71,7 @@ def readserial(infile, logfile, byteadjust): continue d = infile.read(4096) if not d: - break + return 0 datatime = time.time() datatime -= len(d) * byteadjust @@ -128,7 +125,7 @@ def main(): opts = optparse.OptionParser(usage) opts.add_option("-f", "--file", action="store_false", dest="serial", default=True, - help="read from file instead of serialdevice") + help="read from unix named pipe instead of serialdevice") opts.add_option("-n", "--no-adjust", action="store_false", dest="adjustbaud", default=True, help="don't adjust times by serial rate") @@ -168,13 +165,6 @@ Or: apt-get install python-serial """) sys.exit(1) ser = serial.Serial(serialport, baud, timeout=0) - else: - # Read from a file - ser = open(serialport, 'rb') - import fcntl - import os - fcntl.fcntl(ser, fcntl.F_SETFL - , fcntl.fcntl(ser, fcntl.F_GETFL) | os.O_NONBLOCK) if options.calibrate_read: calibrateserialread(ser, byteadjust) @@ -185,7 +175,16 @@ Or: apt-get install python-serial logname = time.strftime("seriallog-%Y%m%d_%H%M%S.log") f = open(logname, 'wb') - readserial(ser, f, byteadjust) + if options.serial: + readserial(ser, f, byteadjust) + else: + # Read from a pipe + while 1: + ser = os.fdopen(os.open(serialport, os.O_RDONLY|os.O_NONBLOCK), 'rb') + res = readserial(ser, f, byteadjust) + ser.close() + if res < 0: + break if __name__ == '__main__': main() diff --git a/roms/seabios/scripts/tarball.sh b/roms/seabios/scripts/tarball.sh new file mode 100755 index 0000000..06d8554 --- /dev/null +++ b/roms/seabios/scripts/tarball.sh @@ -0,0 +1,36 @@ +#!/bin/sh +# +# Script to create seabios release and snapshot tarballs. +# Accepts conmmit (hash, tag, branch, ...) as first argument, +# uses HEAD if unspecified. +# + +commit="${1-HEAD}" + +# figure name for the tarball +reltag="$(git describe --tags --match 'rel-*' --exact $commit 2>/dev/null)" +if test "$reltag" != ""; then + # release + name="${reltag#rel-}" +else + # snapshot + reltag="$(git describe --tags --match 'rel-*' $commit 2>/dev/null)" + name="snap-${reltag#rel-}" +fi + +# export tarball archive from git +prefix="seabios-${name}/" +output="seabios-${name}.tar" +echo "# commit $commit -> tarball: ${output}.gz" +rm -f "$output" "${output}.gz" +git archive --format=tar --prefix="$prefix" "$commit" > "$output" + +# add .version file to tarball +dotver="$(mktemp dotver.XXXXXX)" +echo "$name" > "$dotver" +tar --append --file="$output" --owner=root --group=root --mode=0664 \ + --transform "s:${dotver}:${prefix}.version:" "$dotver" +rm -f "$dotver" + +# finally compress it +gzip "$output" diff --git a/roms/seabios/src/Kconfig b/roms/seabios/src/Kconfig index a863866..45ca59c 100644 --- a/roms/seabios/src/Kconfig +++ b/roms/seabios/src/Kconfig @@ -159,6 +159,12 @@ menu "Hardware support" default y help Support for AHCI disk code. + config SDCARD + depends on DRIVES && QEMU_HARDWARE + bool "SD controllers" + default y + help + Support for SD cards on PCI host controllers. config VIRTIO_BLK depends on DRIVES && QEMU_HARDWARE bool "virtio-blk controllers" @@ -292,6 +298,10 @@ menu "Hardware support" default y help Support System Management Mode (on emulators). + config CALL32_SMM + bool + depends on USE_SMM + default y config MTRR_INIT depends on QEMU bool "Initialize MTRRs" diff --git a/roms/seabios/src/asm-offsets.c b/roms/seabios/src/asm-offsets.c index 576bf34..b98f3b5 100644 --- a/roms/seabios/src/asm-offsets.c +++ b/roms/seabios/src/asm-offsets.c @@ -20,5 +20,4 @@ void foo(void) OFFSET(BREGS_edi, bregs, edi); OFFSET(BREGS_flags, bregs, flags); OFFSET(BREGS_code, bregs, code); - DEFINE(BREGS_size, sizeof(struct bregs)); } diff --git a/roms/seabios/src/block.c b/roms/seabios/src/block.c index 264f376..3f7ecb1 100644 --- a/roms/seabios/src/block.c +++ b/roms/seabios/src/block.c @@ -7,10 +7,10 @@ #include "biosvar.h" // GET_GLOBAL #include "block.h" // process_op -#include "bregs.h" // struct bregs #include "hw/ata.h" // process_ata_op #include "hw/ahci.h" // process_ahci_op #include "hw/blockcmd.h" // cdb_* +#include "hw/pci.h" // pci_bdf_to_bus #include "hw/rtc.h" // rtc_read #include "hw/virtio-blk.h" // process_virtio_blk_op #include "malloc.h" // malloc_low @@ -24,7 +24,6 @@ u8 FloppyCount VARFSEG; u8 CDCount; struct drive_s *IDMap[3][BUILD_MAX_EXTDRIVE] VARFSEG; u8 *bounce_buf_fl VARFSEG; -struct dpte_s DefaultDPTE VARLOW; struct drive_s * getDrive(u8 exttype, u8 extdriveoffset) @@ -280,58 +279,194 @@ map_floppy_drive(struct drive_s *drive) /**************************************************************** - * Return status functions + * Extended Disk Drive (EDD) get drive parameters ****************************************************************/ -void -__disk_ret(struct bregs *regs, u32 linecode, const char *fname) +static int +fill_generic_edd(u16 seg, struct int13dpt_s *param_far, struct drive_s *drive_gf + , u32 dpte_so, char *iface_type + , int bdf, u8 channel, u16 iobase, u64 device_path) { - u8 code = linecode; - if (regs->dl < EXTSTART_HD) - SET_BDA(floppy_last_status, code); - else - SET_BDA(disk_last_status, code); - if (code) - __set_code_invalid(regs, linecode, fname); - else - set_code_success(regs); -} + u16 size = GET_FARVAR(seg, param_far->size); + u16 t13 = size == 74; -void -__disk_ret_unimplemented(struct bregs *regs, u32 linecode, const char *fname) -{ - u8 code = linecode; - if (regs->dl < EXTSTART_HD) - SET_BDA(floppy_last_status, code); - else - SET_BDA(disk_last_status, code); - __set_code_unimplemented(regs, linecode, fname); + // Buffer is too small + if (size < 26) + return DISK_RET_EPARAM; + + // EDD 1.x + + u8 type = GET_GLOBALFLAT(drive_gf->type); + u16 npc = GET_GLOBALFLAT(drive_gf->pchs.cylinder); + u16 nph = GET_GLOBALFLAT(drive_gf->pchs.head); + u16 nps = GET_GLOBALFLAT(drive_gf->pchs.sector); + u64 lba = GET_GLOBALFLAT(drive_gf->sectors); + u16 blksize = GET_GLOBALFLAT(drive_gf->blksize); + + dprintf(DEBUG_HDL_13, "disk_1348 size=%d t=%d chs=%d,%d,%d lba=%d bs=%d\n" + , size, type, npc, nph, nps, (u32)lba, blksize); + + SET_FARVAR(seg, param_far->size, 26); + if (lba == (u64)-1) { + // 0x74 = removable, media change, lockable, max values + SET_FARVAR(seg, param_far->infos, 0x74); + SET_FARVAR(seg, param_far->cylinders, 0xffffffff); + SET_FARVAR(seg, param_far->heads, 0xffffffff); + SET_FARVAR(seg, param_far->spt, 0xffffffff); + } else { + if (lba > (u64)nps*nph*0x3fff) { + SET_FARVAR(seg, param_far->infos, 0x00); // geometry is invalid + SET_FARVAR(seg, param_far->cylinders, 0x3fff); + } else { + SET_FARVAR(seg, param_far->infos, 0x02); // geometry is valid + SET_FARVAR(seg, param_far->cylinders, (u32)npc); + } + SET_FARVAR(seg, param_far->heads, (u32)nph); + SET_FARVAR(seg, param_far->spt, (u32)nps); + } + SET_FARVAR(seg, param_far->sector_count, lba); + SET_FARVAR(seg, param_far->blksize, blksize); + + if (size < 30 || !dpte_so) + return DISK_RET_SUCCESS; + + // EDD 2.x + + SET_FARVAR(seg, param_far->size, 30); + SET_FARVAR(seg, param_far->dpte.segoff, dpte_so); + + if (size < 66 || !iface_type) + return DISK_RET_SUCCESS; + + // EDD 3.x + SET_FARVAR(seg, param_far->key, 0xbedd); + SET_FARVAR(seg, param_far->dpi_length, t13 ? 44 : 36); + SET_FARVAR(seg, param_far->reserved1, 0); + SET_FARVAR(seg, param_far->reserved2, 0); + + int i; + for (i=0; iiface_type); i++) + SET_FARVAR(seg, param_far->iface_type[i], GET_GLOBAL(iface_type[i])); + + if (bdf != -1) { + SET_FARVAR(seg, param_far->host_bus[0], 'P'); + SET_FARVAR(seg, param_far->host_bus[1], 'C'); + SET_FARVAR(seg, param_far->host_bus[2], 'I'); + SET_FARVAR(seg, param_far->host_bus[3], ' '); + + u32 path = (pci_bdf_to_bus(bdf) | (pci_bdf_to_dev(bdf) << 8) + | (pci_bdf_to_fn(bdf) << 16)); + if (t13) + path |= channel << 24; + + SET_FARVAR(seg, param_far->iface_path, path); + } else { + // ISA + SET_FARVAR(seg, param_far->host_bus[0], 'I'); + SET_FARVAR(seg, param_far->host_bus[1], 'S'); + SET_FARVAR(seg, param_far->host_bus[2], 'A'); + SET_FARVAR(seg, param_far->host_bus[3], ' '); + + SET_FARVAR(seg, param_far->iface_path, iobase); + } + + if (t13) { + SET_FARVAR(seg, param_far->t13.device_path[0], device_path); + SET_FARVAR(seg, param_far->t13.device_path[1], 0); + + SET_FARVAR(seg, param_far->t13.checksum + , -checksum_far(seg, (void*)param_far+30, 43)); + } else { + SET_FARVAR(seg, param_far->phoenix.device_path, device_path); + + SET_FARVAR(seg, param_far->phoenix.checksum + , -checksum_far(seg, (void*)param_far+30, 35)); + } + + return DISK_RET_SUCCESS; } +struct dpte_s DefaultDPTE VARLOW; + +static int +fill_ata_edd(u16 seg, struct int13dpt_s *param_far, struct drive_s *drive_gf) +{ + if (!CONFIG_ATA) + return DISK_RET_EPARAM; -/**************************************************************** - * 16bit calling interface - ****************************************************************/ + // Fill in dpte + struct atadrive_s *adrive_gf = container_of( + drive_gf, struct atadrive_s, drive); + struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf); + u8 slave = GET_GLOBALFLAT(adrive_gf->slave); + u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2); + u8 irq = GET_GLOBALFLAT(chan_gf->irq); + u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1); + int bdf = GET_GLOBALFLAT(chan_gf->pci_bdf); + u8 channel = GET_GLOBALFLAT(chan_gf->chanid); + + u16 options = 0; + if (GET_GLOBALFLAT(drive_gf->type) == DTYPE_ATA) { + u8 translation = GET_GLOBALFLAT(drive_gf->translation); + if (translation != TRANSLATION_NONE) { + options |= 1<<3; // CHS translation + if (translation == TRANSLATION_LBA) + options |= 1<<9; + if (translation == TRANSLATION_RECHS) + options |= 3<<9; + } + } else { + // ATAPI + options |= 1<<5; // removable device + options |= 1<<6; // atapi device + } + options |= 1<<4; // lba translation + if (CONFIG_ATA_PIO32) + options |= 1<<7; + + SET_LOW(DefaultDPTE.iobase1, iobase1); + SET_LOW(DefaultDPTE.iobase2, iobase2 + ATA_CB_DC); + SET_LOW(DefaultDPTE.prefix, ((slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) + | ATA_CB_DH_LBA)); + SET_LOW(DefaultDPTE.unused, 0xcb); + SET_LOW(DefaultDPTE.irq, irq); + SET_LOW(DefaultDPTE.blkcount, 1); + SET_LOW(DefaultDPTE.dma, 0); + SET_LOW(DefaultDPTE.pio, 0); + SET_LOW(DefaultDPTE.options, options); + SET_LOW(DefaultDPTE.reserved, 0); + SET_LOW(DefaultDPTE.revision, 0x11); + + u8 sum = checksum_far(SEG_LOW, &DefaultDPTE, 15); + SET_LOW(DefaultDPTE.checksum, -sum); + + return fill_generic_edd( + seg, param_far, drive_gf, SEGOFF(SEG_LOW, (u32)&DefaultDPTE).segoff + , "ATA ", bdf, channel, iobase1, slave); +} -int VISIBLE32FLAT -process_scsi_op(struct disk_op_s *op) +int noinline +fill_edd(u16 seg, struct int13dpt_s *param_far, struct drive_s *drive_gf) { - switch (op->command) { - case CMD_READ: - return cdb_read(op); - case CMD_WRITE: - return cdb_write(op); - case CMD_FORMAT: - case CMD_RESET: - case CMD_ISREADY: - case CMD_VERIFY: - case CMD_SEEK: - return DISK_RET_SUCCESS; + switch (GET_GLOBALFLAT(drive_gf->type)) { + case DTYPE_ATA: + case DTYPE_ATA_ATAPI: + return fill_ata_edd(seg, param_far, drive_gf); + case DTYPE_VIRTIO_BLK: + case DTYPE_VIRTIO_SCSI: + return fill_generic_edd( + seg, param_far, drive_gf, 0xffffffff + , "SCSI ", GET_GLOBALFLAT(drive_gf->cntl_id), 0, 0, 0); default: - return DISK_RET_EPARAM; + return fill_generic_edd(seg, param_far, drive_gf, 0, NULL, 0, 0, 0, 0); } } + +/**************************************************************** + * 16bit calling interface + ****************************************************************/ + int VISIBLE32FLAT process_atapi_op(struct disk_op_s *op) { @@ -340,7 +475,7 @@ process_atapi_op(struct disk_op_s *op) case CMD_FORMAT: return DISK_RET_EWRITEPROTECT; default: - return process_scsi_op(op); + return scsi_process_op(op); } } @@ -350,6 +485,10 @@ process_op(struct disk_op_s *op) { ASSERT16(); int ret, origcount = op->count; + if (origcount * GET_GLOBALFLAT(op->drive_gf->blksize) > 64*1024) { + op->count = 0; + return DISK_RET_EBOUNDARY; + } u8 type = GET_GLOBALFLAT(op->drive_gf->type); switch (type) { case DTYPE_FLOPPY: @@ -380,19 +519,24 @@ process_op(struct disk_op_s *op) ret = call32(_cfunc32flat_process_atapi_op , (u32)MAKE_FLATPTR(GET_SEG(SS), op), DISK_RET_EPARAM); break; + case DTYPE_SDCARD: ; + extern void _cfunc32flat_process_sdcard_op(void); + ret = call32(_cfunc32flat_process_sdcard_op + , (u32)MAKE_FLATPTR(GET_SEG(SS), op), DISK_RET_EPARAM); + break; case DTYPE_USB: case DTYPE_UAS: case DTYPE_VIRTIO_SCSI: case DTYPE_LSI_SCSI: case DTYPE_ESP_SCSI: case DTYPE_MEGASAS: - ret = process_scsi_op(op); + ret = scsi_process_op(op); break; case DTYPE_USB_32: case DTYPE_UAS_32: case DTYPE_PVSCSI: ; - extern void _cfunc32flat_process_scsi_op(void); - ret = call32(_cfunc32flat_process_scsi_op + extern void _cfunc32flat_scsi_process_op(void); + ret = call32(_cfunc32flat_scsi_process_op , (u32)MAKE_FLATPTR(GET_SEG(SS), op), DISK_RET_EPARAM); break; default: diff --git a/roms/seabios/src/block.h b/roms/seabios/src/block.h index 5d0afb5..8182288 100644 --- a/roms/seabios/src/block.h +++ b/roms/seabios/src/block.h @@ -36,21 +36,6 @@ struct chs_s { u16 pad; }; -// ElTorito Device Emulation data -struct cdemu_s { - struct drive_s *emulated_drive_gf; - u32 ilba; - u16 buffer_segment; - u16 load_segment; - u16 sector_count; - u8 active; - u8 media; - u8 emulated_extdrive; - - // Virtual device - struct chs_s lchs; -}; - struct drive_s { u8 type; // Driver type (DTYPE_*) u8 floppy_type; // Type of floppy (only for floppy drives). @@ -86,6 +71,7 @@ struct drive_s { #define DTYPE_ESP_SCSI 0x81 #define DTYPE_MEGASAS 0x82 #define DTYPE_PVSCSI 0x83 +#define DTYPE_SDCARD 0x90 #define MAXDESCSIZE 80 @@ -107,7 +93,6 @@ struct drive_s { ****************************************************************/ // block.c -extern struct dpte_s DefaultDPTE; extern u8 FloppyCount, CDCount; extern u8 *bounce_buf_fl; struct drive_s *getDrive(u8 exttype, u8 extdriveoffset); @@ -115,18 +100,10 @@ int getDriveId(u8 exttype, struct drive_s *drive); void map_floppy_drive(struct drive_s *drive); void map_hd_drive(struct drive_s *drive); void map_cd_drive(struct drive_s *drive); -struct bregs; -void __disk_ret(struct bregs *regs, u32 linecode, const char *fname); -void __disk_ret_unimplemented(struct bregs *regs, u32 linecode - , const char *fname); +struct int13dpt_s; +int fill_edd(u16 seg, struct int13dpt_s *param_far, struct drive_s *drive_gf); int process_op(struct disk_op_s *op); int send_disk_op(struct disk_op_s *op); int create_bounce_buf(void); -// Helper function for setting up a return code. -#define disk_ret(regs, code) \ - __disk_ret((regs), (code) | (__LINE__ << 8), __func__) -#define disk_ret_unimplemented(regs, code) \ - __disk_ret_unimplemented((regs), (code) | (__LINE__ << 8), __func__) - #endif // block.h diff --git a/roms/seabios/src/boot.c b/roms/seabios/src/boot.c index 97de89c..f23e9e1 100644 --- a/roms/seabios/src/boot.c +++ b/roms/seabios/src/boot.c @@ -643,7 +643,7 @@ boot_cdrom(struct drive_s *drive_g) return; } - u8 bootdrv = CDEmu.emulated_extdrive; + u8 bootdrv = CDEmu.emulated_drive; u16 bootseg = CDEmu.load_segment; /* Canonicalize bootseg:bootip */ u16 bootip = (bootseg & 0x0fff) << 4; @@ -689,10 +689,7 @@ boot_fail(void) yield_toirq(); } printf("Rebooting.\n"); - struct bregs br; - memset(&br, 0, sizeof(br)); - br.code = SEGOFF(SEG_BIOS, (u32)reset_vector); - farcall16big(&br); + reset(); } // Determine next boot method and attempt a boot using it. diff --git a/roms/seabios/src/cdrom.c b/roms/seabios/src/cdrom.c index ff419c0..92f34f4 100644 --- a/roms/seabios/src/cdrom.c +++ b/roms/seabios/src/cdrom.c @@ -24,13 +24,14 @@ u8 CDRom_locks[BUILD_MAX_EXTDRIVE] VARLOW; * CD emulation ****************************************************************/ -struct cdemu_s CDEmu VARLOW; +struct eltorito_s CDEmu VARLOW = { .size=sizeof(CDEmu) }; +struct drive_s *emulated_drive_gf VARLOW; struct drive_s *cdemu_drive_gf VARFSEG; static int cdemu_read(struct disk_op_s *op) { - struct drive_s *drive_gf = GET_LOW(CDEmu.emulated_drive_gf); + struct drive_s *drive_gf = GET_LOW(emulated_drive_gf); struct disk_op_s dop; dop.drive_gf = drive_gf; dop.command = op->command; @@ -131,42 +132,6 @@ cdrom_prepboot(void) drive->sectors = (u64)-1; } -#define SET_INT13ET(regs,var,val) \ - SET_FARVAR((regs)->ds, ((struct eltorito_s*)((regs)->si+0))->var, (val)) - -// ElTorito - Terminate disk emu -void -cdemu_134b(struct bregs *regs) -{ - // FIXME ElTorito Hardcoded - SET_INT13ET(regs, size, 0x13); - SET_INT13ET(regs, media, GET_LOW(CDEmu.media)); - SET_INT13ET(regs, emulated_drive, GET_LOW(CDEmu.emulated_extdrive)); - struct drive_s *drive_gf = GET_LOW(CDEmu.emulated_drive_gf); - u8 cntl_id = 0; - if (drive_gf) - cntl_id = GET_GLOBALFLAT(drive_gf->cntl_id); - SET_INT13ET(regs, controller_index, cntl_id / 2); - SET_INT13ET(regs, device_spec, cntl_id % 2); - SET_INT13ET(regs, ilba, GET_LOW(CDEmu.ilba)); - SET_INT13ET(regs, buffer_segment, GET_LOW(CDEmu.buffer_segment)); - SET_INT13ET(regs, load_segment, GET_LOW(CDEmu.load_segment)); - SET_INT13ET(regs, sector_count, GET_LOW(CDEmu.sector_count)); - SET_INT13ET(regs, cylinders, GET_LOW(CDEmu.lchs.cylinder)); - SET_INT13ET(regs, sectors, GET_LOW(CDEmu.lchs.sector)); - SET_INT13ET(regs, heads, GET_LOW(CDEmu.lchs.head)); - - // If we have to terminate emulation - if (regs->al == 0x00) { - // FIXME ElTorito Various. Should be handled accordingly to spec - SET_LOW(CDEmu.active, 0x00); // bye bye - - // XXX - update floppy/hd count. - } - - disk_ret(regs, DISK_RET_SUCCESS); -} - /**************************************************************** * CD booting @@ -189,10 +154,11 @@ cdrom_boot(struct drive_s *drive) // Read the Boot Record Volume Descriptor u8 buffer[CDROM_SECTOR_SIZE]; + dop.command = CMD_READ; dop.lba = 0x11; dop.count = 1; dop.buf_fl = buffer; - ret = cdb_read(&dop); + ret = scsi_process_op(&dop); if (ret) return 3; @@ -208,7 +174,7 @@ cdrom_boot(struct drive_s *drive) // And we read the Boot Catalog dop.lba = lba; dop.count = 1; - ret = cdb_read(&dop); + ret = scsi_process_op(&dop); if (ret) return 7; @@ -226,10 +192,9 @@ cdrom_boot(struct drive_s *drive) if (buffer[0x20] != 0x88) return 11; // Bootable + // Fill in el-torito cdrom emulation fields. + emulated_drive_gf = drive; u8 media = buffer[0x21]; - CDEmu.media = media; - - CDEmu.emulated_drive_gf = dop.drive_gf; u16 boot_segment = *(u16*)&buffer[0x22]; if (!boot_segment) @@ -243,17 +208,29 @@ cdrom_boot(struct drive_s *drive) lba = *(u32*)&buffer[0x28]; CDEmu.ilba = lba; + CDEmu.controller_index = drive->cntl_id / 2; + CDEmu.device_spec = drive->cntl_id % 2; + // And we read the image in memory + nbsectors = DIV_ROUND_UP(nbsectors, 4); dop.lba = lba; - dop.count = DIV_ROUND_UP(nbsectors, 4); dop.buf_fl = MAKE_FLATPTR(boot_segment, 0); - ret = cdb_read(&dop); - if (ret) - return 12; + while (nbsectors) { + int count = nbsectors; + if (count > 64*1024/CDROM_SECTOR_SIZE) + count = 64*1024/CDROM_SECTOR_SIZE; + dop.count = count; + ret = scsi_process_op(&dop); + if (ret) + return 12; + nbsectors -= count; + dop.lba += count; + dop.buf_fl += count*CDROM_SECTOR_SIZE; + } if (media == 0) { // No emulation requested - return success. - CDEmu.emulated_extdrive = EXTSTART_CD + cdid; + CDEmu.emulated_drive = EXTSTART_CD + cdid; return 0; } @@ -265,45 +242,39 @@ cdrom_boot(struct drive_s *drive) // number of devices if (media < 4) { // Floppy emulation - CDEmu.emulated_extdrive = 0x00; + CDEmu.emulated_drive = 0x00; // XXX - get and set actual floppy count. set_equipment_flags(0x41, 0x41); switch (media) { case 0x01: // 1.2M floppy - CDEmu.lchs.sector = 15; - CDEmu.lchs.cylinder = 80; - CDEmu.lchs.head = 2; + CDEmu.chs.sptcyl = 15; + CDEmu.chs.cyllow = 79; + CDEmu.chs.heads = 1; break; case 0x02: // 1.44M floppy - CDEmu.lchs.sector = 18; - CDEmu.lchs.cylinder = 80; - CDEmu.lchs.head = 2; + CDEmu.chs.sptcyl = 18; + CDEmu.chs.cyllow = 79; + CDEmu.chs.heads = 1; break; case 0x03: // 2.88M floppy - CDEmu.lchs.sector = 36; - CDEmu.lchs.cylinder = 80; - CDEmu.lchs.head = 2; + CDEmu.chs.sptcyl = 36; + CDEmu.chs.cyllow = 79; + CDEmu.chs.heads = 1; break; } } else { // Harddrive emulation - CDEmu.emulated_extdrive = 0x80; + CDEmu.emulated_drive = 0x80; SET_BDA(hdcount, GET_BDA(hdcount) + 1); // Peak at partition table to get chs. - struct mbr_s *mbr = (void*)0; - u8 sptcyl = GET_FARVAR(boot_segment, mbr->partitions[0].last.sptcyl); - u8 cyllow = GET_FARVAR(boot_segment, mbr->partitions[0].last.cyllow); - u8 heads = GET_FARVAR(boot_segment, mbr->partitions[0].last.heads); - - CDEmu.lchs.sector = sptcyl & 0x3f; - CDEmu.lchs.cylinder = ((sptcyl<<2)&0x300) + cyllow + 1; - CDEmu.lchs.head = heads + 1; + struct mbr_s *mbr = MAKE_FLATPTR(boot_segment, 0); + CDEmu.chs = mbr->partitions[0].last; } // everything is ok, so from now on, the emulation is active - CDEmu.active = 0x01; + CDEmu.media = media; dprintf(6, "cdemu media=%d\n", media); return 0; diff --git a/roms/seabios/src/config.h b/roms/seabios/src/config.h index d705615..6da067d 100644 --- a/roms/seabios/src/config.h +++ b/roms/seabios/src/config.h @@ -39,9 +39,8 @@ #define BUILD_EXTRA_STACK_SIZE 0x800 // 32KB for shadow ram copying (works around emulator deficiencies) #define BUILD_BIOS_TMP_ADDR 0x30000 -#define BUILD_SMM_INIT_ADDR 0x38000 -#define BUILD_SMM_ADDR 0xa8000 -#define BUILD_SMM_SIZE 0x8000 +#define BUILD_SMM_INIT_ADDR 0x30000 +#define BUILD_SMM_ADDR 0xa0000 #define BUILD_PCIMEM_START 0xe0000000 #define BUILD_PCIMEM_END 0xfec00000 /* IOAPIC is mapped at */ @@ -95,6 +94,8 @@ #define DEBUG_ISR_76 10 #define DEBUG_ISR_hwpic1 5 #define DEBUG_ISR_hwpic2 5 +#define DEBUG_HDL_smi 9 +#define DEBUG_HDL_smp 1 #define DEBUG_HDL_pnp 1 #define DEBUG_HDL_pmm 1 #define DEBUG_HDL_pcibios 9 diff --git a/roms/seabios/src/disk.c b/roms/seabios/src/disk.c index 4421d9d..0e0af24 100644 --- a/roms/seabios/src/disk.c +++ b/roms/seabios/src/disk.c @@ -9,7 +9,6 @@ #include "bregs.h" // struct bregs #include "config.h" // CONFIG_* #include "hw/ata.h" // ATA_CB_DC -#include "hw/pci.h" // pci_bdf_to_bus #include "hw/pic.h" // pic_eoi2 #include "output.h" // debug_enter #include "stacks.h" // call16_int @@ -19,19 +18,53 @@ /**************************************************************** - * Helper functions + * Return status functions ****************************************************************/ static void +__disk_ret(struct bregs *regs, u32 linecode, const char *fname) +{ + u8 code = linecode; + if (regs->dl < EXTSTART_HD) + SET_BDA(floppy_last_status, code); + else + SET_BDA(disk_last_status, code); + if (code) + __set_code_invalid(regs, linecode, fname); + else + set_code_success(regs); +} + +static void +__disk_ret_unimplemented(struct bregs *regs, u32 linecode, const char *fname) +{ + u8 code = linecode; + if (regs->dl < EXTSTART_HD) + SET_BDA(floppy_last_status, code); + else + SET_BDA(disk_last_status, code); + __set_code_unimplemented(regs, linecode, fname); +} + +static void __disk_stub(struct bregs *regs, int lineno, const char *fname) { __warn_unimplemented(regs, lineno, fname); __disk_ret(regs, DISK_RET_SUCCESS | (lineno << 8), fname); } +#define disk_ret(regs, code) \ + __disk_ret((regs), (code) | (__LINE__ << 8), __func__) +#define disk_ret_unimplemented(regs, code) \ + __disk_ret_unimplemented((regs), (code) | (__LINE__ << 8), __func__) #define DISK_STUB(regs) \ __disk_stub((regs), __LINE__, __func__) + +/**************************************************************** + * Helper functions + ****************************************************************/ + // Get the cylinders/heads/sectors for the given drive. static struct chs_s getLCHS(struct drive_s *drive_gf) @@ -42,9 +75,10 @@ getLCHS(struct drive_s *drive_gf) // populate the geometry directly in the driveid because the // geometry is only known after the bios segment is made // read-only). - res.cylinder = GET_LOW(CDEmu.lchs.cylinder); - res.head = GET_LOW(CDEmu.lchs.head); - res.sector = GET_LOW(CDEmu.lchs.sector); + u8 sptcyl = GET_LOW(CDEmu.chs.sptcyl); + res.cylinder = GET_LOW(CDEmu.chs.cyllow) + ((sptcyl << 2) & 0x300) + 1; + res.head = GET_LOW(CDEmu.chs.heads) + 1; + res.sector = sptcyl & 0x3f; return res; } res.cylinder = GET_GLOBALFLAT(drive_gf->lchs.cylinder); @@ -139,6 +173,7 @@ disk_1300(struct bregs *regs, struct drive_s *drive_gf) struct disk_op_s dop; dop.drive_gf = drive_gf; dop.command = CMD_RESET; + dop.count = 0; int status = send_disk_op(&dop); disk_ret(regs, status); } @@ -239,8 +274,8 @@ disk_1308(struct bregs *regs, struct drive_s *drive_gf) return; } - if (CONFIG_CDROM_EMU && GET_LOW(CDEmu.active)) { - u8 emudrive = GET_LOW(CDEmu.emulated_extdrive); + if (CONFIG_CDROM_EMU && GET_LOW(CDEmu.media)) { + u8 emudrive = GET_LOW(CDEmu.emulated_drive); if (((emudrive ^ regs->dl) & 0x80) == 0) // Note extra drive due to emulation. count++; @@ -288,6 +323,7 @@ disk_1310(struct bregs *regs, struct drive_s *drive_gf) struct disk_op_s dop; dop.drive_gf = drive_gf; dop.command = CMD_ISREADY; + dop.count = 0; int status = send_disk_op(&dop); disk_ret(regs, status); } @@ -480,185 +516,11 @@ disk_1347(struct bregs *regs, struct drive_s *drive_gf) } // IBM/MS get drive parameters -static void noinline +static void disk_1348(struct bregs *regs, struct drive_s *drive_gf) { - u16 seg = regs->ds; - struct int13dpt_s *param_far = (struct int13dpt_s*)(regs->si+0); - u16 size = GET_FARVAR(seg, param_far->size); - u16 t13 = size == 74; - - // Buffer is too small - if (size < 26) { - disk_ret(regs, DISK_RET_EPARAM); - return; - } - - // EDD 1.x - - u8 type = GET_GLOBALFLAT(drive_gf->type); - u16 npc = GET_GLOBALFLAT(drive_gf->pchs.cylinder); - u16 nph = GET_GLOBALFLAT(drive_gf->pchs.head); - u16 nps = GET_GLOBALFLAT(drive_gf->pchs.sector); - u64 lba = GET_GLOBALFLAT(drive_gf->sectors); - u16 blksize = GET_GLOBALFLAT(drive_gf->blksize); - - dprintf(DEBUG_HDL_13, "disk_1348 size=%d t=%d chs=%d,%d,%d lba=%d bs=%d\n" - , size, type, npc, nph, nps, (u32)lba, blksize); - - SET_FARVAR(seg, param_far->size, 26); - if (type == DTYPE_ATA_ATAPI) { - // 0x74 = removable, media change, lockable, max values - SET_FARVAR(seg, param_far->infos, 0x74); - SET_FARVAR(seg, param_far->cylinders, 0xffffffff); - SET_FARVAR(seg, param_far->heads, 0xffffffff); - SET_FARVAR(seg, param_far->spt, 0xffffffff); - SET_FARVAR(seg, param_far->sector_count, (u64)-1); - } else { - if (lba > (u64)nps*nph*0x3fff) { - SET_FARVAR(seg, param_far->infos, 0x00); // geometry is invalid - SET_FARVAR(seg, param_far->cylinders, 0x3fff); - } else { - SET_FARVAR(seg, param_far->infos, 0x02); // geometry is valid - SET_FARVAR(seg, param_far->cylinders, (u32)npc); - } - SET_FARVAR(seg, param_far->heads, (u32)nph); - SET_FARVAR(seg, param_far->spt, (u32)nps); - SET_FARVAR(seg, param_far->sector_count, lba); - } - SET_FARVAR(seg, param_far->blksize, blksize); - - if (size < 30 || - (type != DTYPE_ATA && type != DTYPE_ATA_ATAPI && - type != DTYPE_VIRTIO_BLK && type != DTYPE_VIRTIO_SCSI)) { - disk_ret(regs, DISK_RET_SUCCESS); - return; - } - - // EDD 2.x - - int bdf; - u16 iobase1 = 0; - u64 device_path = 0; - u8 channel = 0; - SET_FARVAR(seg, param_far->size, 30); - if (type == DTYPE_ATA || type == DTYPE_ATA_ATAPI) { - SET_FARVAR(seg, param_far->dpte, SEGOFF(SEG_LOW, (u32)&DefaultDPTE)); - - // Fill in dpte - struct atadrive_s *adrive_gf = container_of( - drive_gf, struct atadrive_s, drive); - struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf); - u8 slave = GET_GLOBALFLAT(adrive_gf->slave); - u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2); - u8 irq = GET_GLOBALFLAT(chan_gf->irq); - iobase1 = GET_GLOBALFLAT(chan_gf->iobase1); - bdf = GET_GLOBALFLAT(chan_gf->pci_bdf); - device_path = slave; - channel = GET_GLOBALFLAT(chan_gf->chanid); - - u16 options = 0; - if (type == DTYPE_ATA) { - u8 translation = GET_GLOBALFLAT(drive_gf->translation); - if (translation != TRANSLATION_NONE) { - options |= 1<<3; // CHS translation - if (translation == TRANSLATION_LBA) - options |= 1<<9; - if (translation == TRANSLATION_RECHS) - options |= 3<<9; - } - } else { - // ATAPI - options |= 1<<5; // removable device - options |= 1<<6; // atapi device - } - options |= 1<<4; // lba translation - if (CONFIG_ATA_PIO32) - options |= 1<<7; - - SET_LOW(DefaultDPTE.iobase1, iobase1); - SET_LOW(DefaultDPTE.iobase2, iobase2 + ATA_CB_DC); - SET_LOW(DefaultDPTE.prefix, ((slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) - | ATA_CB_DH_LBA)); - SET_LOW(DefaultDPTE.unused, 0xcb); - SET_LOW(DefaultDPTE.irq, irq); - SET_LOW(DefaultDPTE.blkcount, 1); - SET_LOW(DefaultDPTE.dma, 0); - SET_LOW(DefaultDPTE.pio, 0); - SET_LOW(DefaultDPTE.options, options); - SET_LOW(DefaultDPTE.reserved, 0); - SET_LOW(DefaultDPTE.revision, 0x11); - - u8 sum = checksum_far(SEG_LOW, &DefaultDPTE, 15); - SET_LOW(DefaultDPTE.checksum, -sum); - } else { - SET_FARVAR(seg, param_far->dpte.segoff, 0xffffffff); - bdf = GET_GLOBALFLAT(drive_gf->cntl_id); - } - - if (size < 66) { - disk_ret(regs, DISK_RET_SUCCESS); - return; - } - - // EDD 3.x - SET_FARVAR(seg, param_far->key, 0xbedd); - SET_FARVAR(seg, param_far->dpi_length, t13 ? 44 : 36); - SET_FARVAR(seg, param_far->reserved1, 0); - SET_FARVAR(seg, param_far->reserved2, 0); - - if (bdf != -1) { - SET_FARVAR(seg, param_far->host_bus[0], 'P'); - SET_FARVAR(seg, param_far->host_bus[1], 'C'); - SET_FARVAR(seg, param_far->host_bus[2], 'I'); - SET_FARVAR(seg, param_far->host_bus[3], ' '); - - u32 path = (pci_bdf_to_bus(bdf) | (pci_bdf_to_dev(bdf) << 8) - | (pci_bdf_to_fn(bdf) << 16)); - if (t13) - path |= channel << 24; - - SET_FARVAR(seg, param_far->iface_path, path); - } else { - // ISA - SET_FARVAR(seg, param_far->host_bus[0], 'I'); - SET_FARVAR(seg, param_far->host_bus[1], 'S'); - SET_FARVAR(seg, param_far->host_bus[2], 'A'); - SET_FARVAR(seg, param_far->host_bus[3], ' '); - - SET_FARVAR(seg, param_far->iface_path, iobase1); - } - - if (type != DTYPE_VIRTIO_BLK) { - SET_FARVAR(seg, param_far->iface_type[0], 'A'); - SET_FARVAR(seg, param_far->iface_type[1], 'T'); - SET_FARVAR(seg, param_far->iface_type[2], 'A'); - SET_FARVAR(seg, param_far->iface_type[3], ' '); - } else { - SET_FARVAR(seg, param_far->iface_type[0], 'S'); - SET_FARVAR(seg, param_far->iface_type[1], 'C'); - SET_FARVAR(seg, param_far->iface_type[2], 'S'); - SET_FARVAR(seg, param_far->iface_type[3], 'I'); - } - SET_FARVAR(seg, param_far->iface_type[4], ' '); - SET_FARVAR(seg, param_far->iface_type[5], ' '); - SET_FARVAR(seg, param_far->iface_type[6], ' '); - SET_FARVAR(seg, param_far->iface_type[7], ' '); - - if (t13) { - SET_FARVAR(seg, param_far->t13.device_path[0], device_path); - SET_FARVAR(seg, param_far->t13.device_path[1], 0); - - SET_FARVAR(seg, param_far->t13.checksum - , -checksum_far(seg, (void*)param_far+30, 43)); - } else { - SET_FARVAR(seg, param_far->phoenix.device_path, device_path); - - SET_FARVAR(seg, param_far->phoenix.checksum - , -checksum_far(seg, (void*)param_far+30, 35)); - } - - disk_ret(regs, DISK_RET_SUCCESS); + int ret = fill_edd(regs->ds, (void*)(regs->si+0), drive_gf); + disk_ret(regs, ret); } // IBM/MS extended media change @@ -782,6 +644,23 @@ floppy_13(struct bregs *regs, struct drive_s *drive_gf) } } +// ElTorito - Terminate disk emu +static void +cdemu_134b(struct bregs *regs) +{ + memcpy_far(regs->ds, (void*)(regs->si+0), SEG_LOW, &CDEmu, sizeof(CDEmu)); + + // If we have to terminate emulation + if (regs->al == 0x00) { + // FIXME ElTorito Various. Should be handled accordingly to spec + SET_LOW(CDEmu.media, 0x00); // bye bye + + // XXX - update floppy/hd count. + } + + disk_ret(regs, DISK_RET_SUCCESS); +} + /**************************************************************** * Entry points @@ -838,8 +717,8 @@ handle_13(struct bregs *regs) cdemu_134b(regs); return; } - if (GET_LOW(CDEmu.active)) { - u8 emudrive = GET_LOW(CDEmu.emulated_extdrive); + if (GET_LOW(CDEmu.media)) { + u8 emudrive = GET_LOW(CDEmu.emulated_drive); if (extdrive == emudrive) { // Access to an emulated drive. struct drive_s *cdemu_gf = GET_GLOBAL(cdemu_drive_gf); @@ -867,6 +746,3 @@ handle_76(void) SET_BDA(disk_interrupt_flag, 0xff); pic_eoi2(); } - -// Old Fixed Disk Parameter Table (newer tables are in the ebda). -struct fdpt_s OldFDPT VAR16FIXED(0xe401); diff --git a/roms/seabios/src/entryfuncs.S b/roms/seabios/src/entryfuncs.S index ea6f990..7368bb6 100644 --- a/roms/seabios/src/entryfuncs.S +++ b/roms/seabios/src/entryfuncs.S @@ -1,16 +1,19 @@ // Macros for entering C code // -// Copyright (C) 2008,2009 Kevin O'Connor +// Copyright (C) 2008-2014 Kevin O'Connor // // This file may be distributed under the terms of the GNU LGPLv3 license. /**************************************************************** - * Entry macros + * Macros for save and restore of 'struct bregs' registers ****************************************************************/ +#define PUSHBREGS_size 32 + + // Save registers (matches struct bregs) to stack .macro PUSHBREGS - pushl %eax // Save registers (matches struct bregs) + pushl %eax pushl %ecx pushl %edx pushl %ebx @@ -21,8 +24,9 @@ pushw %ds .endm + // Restore registers (from struct bregs) from stack .macro POPBREGS - popw %ds // Restore registers (from struct bregs) + popw %ds popw %es popl %edi popl %esi @@ -33,6 +37,40 @@ popl %eax .endm + // Save registers to struct bregs at %ds:%eax. The caller + // should "pushw %ds ; pushl %eax" prior to calling - this macro + // will pop them off. + .macro SAVEBREGS_POP_DSEAX + popl BREGS_eax(%eax) + popw BREGS_ds(%eax) + movl %edi, BREGS_edi(%eax) + movl %esi, BREGS_esi(%eax) + movl %ebp, BREGS_ebp(%eax) + movl %ebx, BREGS_ebx(%eax) + movl %edx, BREGS_edx(%eax) + movl %ecx, BREGS_ecx(%eax) + movw %es, BREGS_es(%eax) + .endm + + // Restore registers from struct bregs at %ds:%eax + .macro RESTOREBREGS_DSEAX + movl BREGS_edi(%eax), %edi + movl BREGS_esi(%eax), %esi + movl BREGS_ebp(%eax), %ebp + movl BREGS_ebx(%eax), %ebx + movl BREGS_edx(%eax), %edx + movl BREGS_ecx(%eax), %ecx + movw BREGS_es(%eax), %es + pushl BREGS_eax(%eax) + movw BREGS_ds(%eax), %ds + popl %eax + .endm + + +/**************************************************************** + * Entry macros + ****************************************************************/ + // Call a C function - this does the minimal work necessary to // call into C. It sets up %ds, backs up %es, and backs up // those registers that are call clobbered by the C compiler. @@ -57,29 +95,6 @@ popl %eax .endm - // As above, but get calling function from stack. - .macro ENTRY_ST - cli - cld - pushl %ecx - pushl %edx - pushw %es - pushw %ds - movw %ss, %cx // Move %ss to %ds - movw %cx, %ds - pushl %esp // Backup %esp, then clear high bits - movzwl %sp, %esp - movl 16(%esp), %ecx // Get calling function - movl %eax, 16(%esp) // Save %eax - calll *%ecx - popl %esp // Restore %esp (including high bits) - popw %ds // Restore registers saved above - popw %es - popl %edx - popl %ecx - popl %eax - .endm - // Call a C function with current register list as an // argument. This backs up the registers and sets %eax // to point to the backup. On return, the registers are @@ -135,10 +150,9 @@ .endm // Reset stack, transition to 32bit mode, and call a C function. - // Clobbers %ax .macro ENTRY_INTO32 cfunc - xorw %ax, %ax - movw %ax, %ss + xorw %dx, %dx + movw %dx, %ss movl $ BUILD_STACK_ADDR , %esp movl $ \cfunc , %edx jmp transition32 @@ -149,9 +163,3 @@ .section .text.asm.\func .global \func .endm - - // Declare an exported function - .macro EXPORTFUNC func - .section .text.asm.export.\func - .global \func - .endm diff --git a/roms/seabios/src/font.c b/roms/seabios/src/font.c index 3f8662f..67e5d46 100644 --- a/roms/seabios/src/font.c +++ b/roms/seabios/src/font.c @@ -7,7 +7,7 @@ * found at ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip * This font is public domain */ -u8 vgafont8[128*8] VAR16FIXED(0xfa6e) = { +u8 vgafont8[128*8] VARFSEGFIXED(0xfa6e) = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e, 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e, diff --git a/roms/seabios/src/fw/acpi.c b/roms/seabios/src/fw/acpi.c index 733ca4d..ecd1adc 100644 --- a/roms/seabios/src/fw/acpi.c +++ b/roms/seabios/src/fw/acpi.c @@ -9,6 +9,7 @@ #include "byteorder.h" // cpu_to_le16 #include "config.h" // CONFIG_* #include "dev-q35.h" +#include "dev-piix.h" #include "hw/pci.h" // pci_find_init_device #include "hw/pci_ids.h" // PCI_VENDOR_ID_INTEL #include "hw/pci_regs.h" // PCI_INTERRUPT_LINE @@ -38,36 +39,31 @@ build_header(struct acpi_table_header *h, u32 sig, int len, u8 rev) h->checksum -= checksum(h, len); } -#define PIIX4_ACPI_ENABLE 0xf1 -#define PIIX4_ACPI_DISABLE 0xf0 -#define PIIX4_GPE0_BLK 0xafe0 -#define PIIX4_GPE0_BLK_LEN 4 - -#define PIIX4_PM_INTRRUPT 9 // irq 9 - static void piix4_fadt_setup(struct pci_device *pci, void *arg) { struct fadt_descriptor_rev1 *fadt = arg; fadt->model = 1; fadt->reserved1 = 0; - fadt->sci_int = cpu_to_le16(PIIX4_PM_INTRRUPT); + fadt->sci_int = cpu_to_le16(PIIX_PM_INTRRUPT); fadt->smi_cmd = cpu_to_le32(PORT_SMI_CMD); - fadt->acpi_enable = PIIX4_ACPI_ENABLE; - fadt->acpi_disable = PIIX4_ACPI_DISABLE; + fadt->acpi_enable = PIIX_ACPI_ENABLE; + fadt->acpi_disable = PIIX_ACPI_DISABLE; fadt->pm1a_evt_blk = cpu_to_le32(acpi_pm_base); fadt->pm1a_cnt_blk = cpu_to_le32(acpi_pm_base + 0x04); fadt->pm_tmr_blk = cpu_to_le32(acpi_pm_base + 0x08); - fadt->gpe0_blk = cpu_to_le32(PIIX4_GPE0_BLK); + fadt->gpe0_blk = cpu_to_le32(PIIX_GPE0_BLK); fadt->pm1_evt_len = 4; fadt->pm1_cnt_len = 2; fadt->pm_tmr_len = 4; - fadt->gpe0_blk_len = PIIX4_GPE0_BLK_LEN; + fadt->gpe0_blk_len = PIIX_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 - /* WBINVD + PROC_C1 + SLP_BUTTON + RTC_S4 + USE_PLATFORM_CLOCK */ - fadt->flags = cpu_to_le32((1 << 0) | (1 << 2) | (1 << 5) | (1 << 7) | - (1 << 15)); + fadt->flags = cpu_to_le32(ACPI_FADT_F_WBINVD | + ACPI_FADT_F_PROC_C1 | + ACPI_FADT_F_SLP_BUTTON | + ACPI_FADT_F_RTC_S4 | + ACPI_FADT_F_USE_PLATFORM_CLOCK); } /* PCI_VENDOR_ID_INTEL && PCI_DEVICE_ID_INTEL_ICH9_LPC */ @@ -91,9 +87,11 @@ static void ich9_lpc_fadt_setup(struct pci_device *dev, void *arg) fadt->gpe0_blk_len = ICH9_PMIO_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 - /* WBINVD + PROC_C1 + SLP_BUTTON + RTC_S4 + USE_PLATFORM_CLOCK */ - fadt->flags = cpu_to_le32((1 << 0) | (1 << 2) | (1 << 5) | (1 << 7) | - (1 << 15)); + fadt->flags = cpu_to_le32(ACPI_FADT_F_WBINVD | + ACPI_FADT_F_PROC_C1 | + ACPI_FADT_F_SLP_BUTTON | + ACPI_FADT_F_RTC_S4 | + ACPI_FADT_F_USE_PLATFORM_CLOCK); } static const struct pci_device_id fadt_init_tbl[] = { diff --git a/roms/seabios/src/fw/csm.c b/roms/seabios/src/fw/csm.c index a44ed26..7cdb398 100644 --- a/roms/seabios/src/fw/csm.c +++ b/roms/seabios/src/fw/csm.c @@ -46,8 +46,14 @@ extern void __csm_return(struct bregs *regs) __noreturn; static void csm_return(struct bregs *regs) { + u32 rommax = rom_get_max(); + extern u8 final_readonly_start[]; + dprintf(3, "handle_csm returning AX=%04x\n", regs->ax); + csm_compat_table.UmaAddress = rommax; + csm_compat_table.UmaSize = (u32)final_readonly_start - rommax; + PICMask = pic_irqmask_read(); __csm_return(regs); } diff --git a/roms/seabios/src/fw/dev-piix.h b/roms/seabios/src/fw/dev-piix.h new file mode 100644 index 0000000..c389f17 --- /dev/null +++ b/roms/seabios/src/fw/dev-piix.h @@ -0,0 +1,29 @@ +#ifndef __DEV_PIIX_H +#define __DEV_PIIX_H + +#define I440FX_PAM0 0x59 +#define I440FX_SMRAM 0x72 + +#define PIIX_PMBASE 0x40 +#define PIIX_PMREGMISC 0x80 +#define PIIX_SMBHSTBASE 0x90 +#define PIIX_SMBHSTCFG 0xd2 +#define PIIX_DEVACTB 0x58 +#define PIIX_DEVACTB_APMC_EN (1 << 25) + +#define PIIX_PORT_ELCR1 0x4d0 +#define PIIX_PORT_ELCR2 0x4d1 + +/* ICH9 PM I/O registers */ +#define PIIX_GPE0_BLK 0xafe0 +#define PIIX_GPE0_BLK_LEN 4 +#define PIIX_PMIO_GLBCTL 0x28 +#define PIIX_PMIO_GLBCTL_SMI_EN 1 + +/* FADT ACPI_ENABLE/ACPI_DISABLE */ +#define PIIX_ACPI_ENABLE 0xf1 +#define PIIX_ACPI_DISABLE 0xf0 + +#define PIIX_PM_INTRRUPT 9 // irq 9 + +#endif // dev-piix.h diff --git a/roms/seabios/src/fw/dev-q35.h b/roms/seabios/src/fw/dev-q35.h index 6ae039f..c6f8bd9 100644 --- a/roms/seabios/src/fw/dev-q35.h +++ b/roms/seabios/src/fw/dev-q35.h @@ -23,6 +23,8 @@ #define ICH9_LPC_PIRQA_ROUT 0x60 #define ICH9_LPC_PIRQE_ROUT 0x68 #define ICH9_LPC_PIRQ_ROUT_IRQEN 0x80 +#define ICH9_LPC_GEN_PMCON_1 0xa0 +#define ICH9_LPC_GEN_PMCON_1_SMI_LOCK (1 << 4) #define ICH9_LPC_PORT_ELCR1 0x4d0 #define ICH9_LPC_PORT_ELCR2 0x4d1 #define PCI_DEVICE_ID_INTEL_ICH9_SMBUS 0x2930 @@ -38,6 +40,7 @@ #define ICH9_PMIO_GPE0_BLK_LEN 0x10 #define ICH9_PMIO_SMI_EN 0x30 #define ICH9_PMIO_SMI_EN_APMC_EN (1 << 5) +#define ICH9_PMIO_SMI_EN_GLB_SMI_EN (1 << 0) /* FADT ACPI_ENABLE/ACPI_DISABLE */ #define ICH9_APM_ACPI_ENABLE 0x2 diff --git a/roms/seabios/src/fw/pciinit.c b/roms/seabios/src/fw/pciinit.c index 0e5d51b..46ae709 100644 --- a/roms/seabios/src/fw/pciinit.c +++ b/roms/seabios/src/fw/pciinit.c @@ -8,6 +8,7 @@ #include "byteorder.h" // le64_to_cpu #include "config.h" // CONFIG_* #include "dev-q35.h" // Q35_HOST_BRIDGE_PCIEXBAR_ADDR +#include "dev-piix.h" // PIIX_* #include "hw/ata.h" // PORT_ATA1_CMD_BASE #include "hw/pci.h" // pci_config_readl #include "hw/pci_ids.h" // PCI_VENDOR_ID_INTEL @@ -114,27 +115,18 @@ static int piix_pci_slot_get_irq(struct pci_device *pci, int pin) static int mch_pci_slot_get_irq(struct pci_device *pci, int pin) { - int irq, slot, pin_addend = 0; - + int pin_addend = 0; while (pci->parent != NULL) { pin_addend += pci_bdf_to_dev(pci->bdf); pci = pci->parent; } - slot = pci_bdf_to_dev(pci->bdf); - - switch (slot) { - /* Slots 0-24 rotate slot:pin mapping similar to piix above, but - with a different starting index - see q35-acpi-dsdt.dsl */ - case 0 ... 24: - irq = pci_irqs[(pin - 1 + pin_addend + slot) & 3]; - break; + u8 slot = pci_bdf_to_dev(pci->bdf); + if (slot <= 24) + /* Slots 0-24 rotate slot:pin mapping similar to piix above, but + with a different starting index - see q35-acpi-dsdt.dsl */ + return pci_irqs[(pin - 1 + pin_addend + slot) & 3]; /* Slots 25-31 all use LNKA mapping (or LNKE, but A:D = E:H) */ - case 25 ... 31: - irq = pci_irqs[(pin - 1 + pin_addend) & 3]; - break; - } - - return irq; + return pci_irqs[(pin - 1 + pin_addend) & 3]; } /* PIIX3/PIIX4 PCI to ISA bridge */ @@ -152,8 +144,8 @@ static void piix_isa_bridge_setup(struct pci_device *pci, void *arg) /* activate irq remapping in PIIX */ pci_config_writeb(pci->bdf, 0x60 + i, irq); } - outb(elcr[0], 0x4d0); - outb(elcr[1], 0x4d1); + outb(elcr[0], PIIX_PORT_ELCR1); + outb(elcr[1], PIIX_PORT_ELCR2); dprintf(1, "PIIX3/PIIX4 init: elcr=%02x %02x\n", elcr[0], elcr[1]); } @@ -229,10 +221,10 @@ static void piix4_pm_config_setup(u16 bdf) // acpi sci is hardwired to 9 pci_config_writeb(bdf, PCI_INTERRUPT_LINE, 9); - pci_config_writel(bdf, 0x40, acpi_pm_base | 1); - pci_config_writeb(bdf, 0x80, 0x01); /* enable PM io space */ - pci_config_writel(bdf, 0x90, (acpi_pm_base + 0x100) | 1); - pci_config_writeb(bdf, 0xd2, 0x09); /* enable SMBus io space */ + pci_config_writel(bdf, PIIX_PMBASE, acpi_pm_base | 1); + pci_config_writeb(bdf, PIIX_PMREGMISC, 0x01); /* enable PM io space */ + pci_config_writel(bdf, PIIX_SMBHSTBASE, (acpi_pm_base + 0x100) | 1); + pci_config_writeb(bdf, PIIX_SMBHSTCFG, 0x09); /* enable SMBus io space */ } static int PiixPmBDF = -1; @@ -498,8 +490,17 @@ pci_bios_init_bus_rec(int bus, u8 *pci_bus) static void pci_bios_init_bus(void) { + u8 extraroots = romfile_loadint("etc/extra-pci-roots", 0); u8 pci_bus = 0; + pci_bios_init_bus_rec(0 /* host bus */, &pci_bus); + + if (extraroots) { + while (pci_bus < 0xff) { + pci_bus++; + pci_bios_init_bus_rec(pci_bus, &pci_bus); + } + } } @@ -676,6 +677,11 @@ static int pci_bios_check_devices(struct pci_bus *busses) busses[pci->secondary_bus].bus_dev = pci; struct pci_bus *bus = &busses[pci_bdf_to_bus(pci->bdf)]; + if (!bus->bus_dev) + /* + * Resources for all root busses go in busses[0] + */ + bus = &busses[0]; int i; for (i = 0; i < PCI_NUM_REGIONS; i++) { if ((pci->class == PCI_CLASS_BRIDGE_PCI) && @@ -706,6 +712,11 @@ static int pci_bios_check_devices(struct pci_bus *busses) if (!s->bus_dev) continue; struct pci_bus *parent = &busses[pci_bdf_to_bus(s->bus_dev->bdf)]; + if (!parent->bus_dev) + /* + * Resources for all root busses go in busses[0] + */ + parent = &busses[0]; int type; int hotplug_support = pci_bus_hotplug_support(s); for (type = 0; type < PCI_REGION_TYPE_COUNT; type++) { diff --git a/roms/seabios/src/fw/shadow.c b/roms/seabios/src/fw/shadow.c index 82d6753..4f00006 100644 --- a/roms/seabios/src/fw/shadow.c +++ b/roms/seabios/src/fw/shadow.c @@ -7,6 +7,7 @@ #include "config.h" // CONFIG_* #include "dev-q35.h" // PCI_VENDOR_ID_INTEL +#include "dev-piix.h" // I440FX_PAM0 #include "hw/pci.h" // pci_config_writeb #include "hw/pci_ids.h" // PCI_VENDOR_ID_INTEL #include "hw/pci_regs.h" // PCI_VENDOR_ID @@ -20,8 +21,6 @@ // On the emulators, the bios at 0xf0000 is also at 0xffff0000 #define BIOS_SRC_OFFSET 0xfff00000 -#define I440FX_PAM0 0x59 - // Enable shadowing and copy bios. static void __make_bios_writable_intel(u16 bdf, u32 pam0) diff --git a/roms/seabios/src/fw/smm.c b/roms/seabios/src/fw/smm.c index 0f59f20..dabc677 100644 --- a/roms/seabios/src/fw/smm.c +++ b/roms/seabios/src/fw/smm.c @@ -1,67 +1,138 @@ // System Management Mode support (on emulators) // -// Copyright (C) 2008 Kevin O'Connor +// Copyright (C) 2008-2014 Kevin O'Connor // Copyright (C) 2006 Fabrice Bellard // // This file may be distributed under the terms of the GNU LGPLv3 license. #include "config.h" // CONFIG_* #include "dev-q35.h" +#include "dev-piix.h" #include "hw/pci.h" // pci_config_writel #include "hw/pci_ids.h" // PCI_VENDOR_ID_INTEL #include "hw/pci_regs.h" // PCI_DEVICE_ID #include "output.h" // dprintf #include "paravirt.h" // PORT_SMI_STATUS +#include "stacks.h" // HaveSmmCall32 #include "string.h" // memcpy #include "util.h" // smm_setup #include "x86.h" // wbinvd -extern u8 smm_relocation_start, smm_relocation_end; -ASM32FLAT( - ".global smm_relocation_start, smm_relocation_end\n" - " .code16gcc\n" - - /* code to relocate SMBASE to 0xa0000 */ - "smm_relocation_start:\n" - " movl $" __stringify(BUILD_SMM_INIT_ADDR) " + 0x7efc, %ebx\n" - " addr32 movb (%ebx), %al\n" /* revision ID to see if x86_64 or x86 */ - " cmpb $0x64, %al\n" - " je 1f\n" - " movl $" __stringify(BUILD_SMM_INIT_ADDR) " + 0x7ef8, %ebx\n" - " jmp 2f\n" - "1:\n" - " movl $" __stringify(BUILD_SMM_INIT_ADDR) " + 0x7f00, %ebx\n" - "2:\n" - " movl $" __stringify(BUILD_SMM_ADDR) " - 0x8000, %eax\n" - " addr32 movl %eax, (%ebx)\n" - /* indicate to the BIOS that the SMM code was executed */ - " movb $0x00, %al\n" - " movw $" __stringify(PORT_SMI_STATUS) ", %dx\n" - " outb %al, %dx\n" - " rsm\n" - "smm_relocation_end:\n" - " .code32\n" - ); - -extern u8 smm_code_start, smm_code_end; -ASM32FLAT( - ".global smm_code_start, smm_code_end\n" - " .code16gcc\n" - "smm_code_start:\n" - " rsm\n" - "smm_code_end:\n" - " .code32\n" - ); +#define SMM_REV_I32 0x00020000 +#define SMM_REV_I64 0x00020064 + +struct smm_state { + union { + struct { + u8 pad_000[0xf8]; + u32 smm_base; + u32 smm_rev; + u8 pad_100[0xd0]; + u32 eax, ecx, edx, ebx, esp, ebp, esi, edi, eip, eflags; + u8 pad_1f8[0x08]; + } i32; + struct { + u8 pad_000[0xfc]; + u32 smm_rev; + u32 smm_base; + u8 pad_104[0x6c]; + u64 rflags, rip, r15, r14, r13, r12, r11, r10, r9, r8; + u64 rdi, rsi, rbp, rsp, rbx, rdx, rcx, rax; + } i64; + }; +}; + +struct smm_layout { + struct smm_state backup1; + struct smm_state backup2; + u8 stack[0x7c00]; + u64 codeentry; + u8 pad_8008[0x7df8]; + struct smm_state cpu; +}; + +void VISIBLE32FLAT +handle_smi(u16 cs) +{ + if (!CONFIG_USE_SMM) + return; + u8 cmd = inb(PORT_SMI_CMD); + struct smm_layout *smm = MAKE_FLATPTR(cs, 0); + dprintf(DEBUG_HDL_smi, "handle_smi cmd=%x smbase=%p\n", cmd, smm); + + if (smm == (void*)BUILD_SMM_INIT_ADDR) { + // relocate SMBASE to 0xa0000 + if (smm->cpu.i32.smm_rev == SMM_REV_I32) { + smm->cpu.i32.smm_base = BUILD_SMM_ADDR; + } else if (smm->cpu.i64.smm_rev == SMM_REV_I64) { + smm->cpu.i64.smm_base = BUILD_SMM_ADDR; + } else { + warn_internalerror(); + return; + } + // indicate to smm_relocate_and_restore() that the SMM code was executed + outb(0x00, PORT_SMI_STATUS); + + if (CONFIG_CALL32_SMM) { + // Backup current cpu state for SMM trampolining + struct smm_layout *newsmm = (void*)BUILD_SMM_ADDR; + memcpy(&newsmm->backup1, &smm->cpu, sizeof(newsmm->backup1)); + memcpy(&newsmm->backup2, &smm->cpu, sizeof(newsmm->backup2)); + HaveSmmCall32 = 1; + } + + return; + } + + if (CONFIG_CALL32_SMM && cmd == CALL32SMM_CMDID) { + if (smm->cpu.i32.smm_rev == SMM_REV_I32) { + u32 regs[8]; + memcpy(regs, &smm->cpu.i32.eax, sizeof(regs)); + if (smm->cpu.i32.ecx == CALL32SMM_ENTERID) { + dprintf(9, "smm cpu call pc=%x esp=%x\n", regs[3], regs[4]); + memcpy(&smm->backup2, &smm->cpu, sizeof(smm->backup2)); + memcpy(&smm->cpu, &smm->backup1, sizeof(smm->cpu)); + memcpy(&smm->cpu.i32.eax, regs, sizeof(regs)); + smm->cpu.i32.eip = regs[3]; + } else if (smm->cpu.i32.ecx == CALL32SMM_RETURNID) { + dprintf(9, "smm cpu ret %x esp=%x\n", regs[3], regs[4]); + memcpy(&smm->cpu, &smm->backup2, sizeof(smm->cpu)); + memcpy(&smm->cpu.i32.eax, regs, sizeof(regs)); + smm->cpu.i32.eip = regs[3]; + } + } else if (smm->cpu.i64.smm_rev == SMM_REV_I64) { + u64 regs[8]; + memcpy(regs, &smm->cpu.i64.rdi, sizeof(regs)); + if ((u32)smm->cpu.i64.rcx == CALL32SMM_ENTERID) { + memcpy(&smm->backup2, &smm->cpu, sizeof(smm->backup2)); + memcpy(&smm->cpu, &smm->backup1, sizeof(smm->cpu)); + memcpy(&smm->cpu.i64.rdi, regs, sizeof(regs)); + smm->cpu.i64.rip = (u32)regs[4]; + } else if ((u32)smm->cpu.i64.rcx == CALL32SMM_RETURNID) { + memcpy(&smm->cpu, &smm->backup2, sizeof(smm->cpu)); + memcpy(&smm->cpu.i64.rdi, regs, sizeof(regs)); + smm->cpu.i64.rip = (u32)regs[4]; + } + } + } +} + +extern void entry_smi(void); +// movw %cs, %ax; ljmpw $SEG_BIOS, $(entry_smi - BUILD_BIOS_ADDR) +#define SMI_INSN (0xeac88c | ((u64)SEG_BIOS<<40) \ + | ((u64)((u32)entry_smi - BUILD_BIOS_ADDR) << 24)) static void smm_save_and_copy(void) { - /* save original memory content */ - memcpy((void *)BUILD_SMM_ADDR, (void *)BUILD_SMM_INIT_ADDR, BUILD_SMM_SIZE); - - /* copy the SMM relocation code */ - memcpy((void *)BUILD_SMM_INIT_ADDR, &smm_relocation_start, - &smm_relocation_end - &smm_relocation_start); + // save original memory content + struct smm_layout *initsmm = (void*)BUILD_SMM_INIT_ADDR; + struct smm_layout *smm = (void*)BUILD_SMM_ADDR; + memcpy(&smm->cpu, &initsmm->cpu, sizeof(smm->cpu)); + memcpy(&smm->codeentry, &initsmm->codeentry, sizeof(smm->codeentry)); + + // Setup code entry point. + initsmm->codeentry = SMI_INSN; } static void @@ -78,24 +149,22 @@ smm_relocate_and_restore(void) ; /* restore original memory content */ - memcpy((void *)BUILD_SMM_INIT_ADDR, (void *)BUILD_SMM_ADDR, BUILD_SMM_SIZE); + struct smm_layout *initsmm = (void*)BUILD_SMM_INIT_ADDR; + struct smm_layout *smm = (void*)BUILD_SMM_ADDR; + memcpy(&initsmm->cpu, &smm->cpu, sizeof(initsmm->cpu)); + memcpy(&initsmm->codeentry, &smm->codeentry, sizeof(initsmm->codeentry)); - /* copy the SMM code */ - memcpy((void *)BUILD_SMM_ADDR, &smm_code_start - , &smm_code_end - &smm_code_start); + // Setup code entry point. + smm->codeentry = SMI_INSN; wbinvd(); } -#define I440FX_SMRAM 0x72 -#define PIIX_DEVACTB 0x58 -#define PIIX_APMC_EN (1 << 25) - // This code is hardcoded for PIIX4 Power Management device. static void piix4_apmc_smm_setup(int isabdf, int i440_bdf) { /* check if SMM init is already done */ u32 value = pci_config_readl(isabdf, PIIX_DEVACTB); - if (value & PIIX_APMC_EN) + if (value & PIIX_DEVACTB_APMC_EN) return; /* enable the SMM memory window */ @@ -104,7 +173,11 @@ static void piix4_apmc_smm_setup(int isabdf, int i440_bdf) smm_save_and_copy(); /* enable SMI generation when writing to the APMC register */ - pci_config_writel(isabdf, PIIX_DEVACTB, value | PIIX_APMC_EN); + pci_config_writel(isabdf, PIIX_DEVACTB, value | PIIX_DEVACTB_APMC_EN); + + /* enable SMI generation */ + value = inl(acpi_pm_base + PIIX_PMIO_GLBCTL); + outl(acpi_pm_base + PIIX_PMIO_GLBCTL, value | PIIX_PMIO_GLBCTL_SMI_EN); smm_relocate_and_restore(); @@ -126,9 +199,14 @@ void ich9_lpc_apmc_smm_setup(int isabdf, int mch_bdf) smm_save_and_copy(); /* enable SMI generation when writing to the APMC register */ - outl(value | ICH9_PMIO_SMI_EN_APMC_EN, + outl(value | ICH9_PMIO_SMI_EN_APMC_EN | ICH9_PMIO_SMI_EN_GLB_SMI_EN, acpi_pm_base + ICH9_PMIO_SMI_EN); + /* lock SMI generation */ + value = pci_config_readw(isabdf, ICH9_LPC_GEN_PMCON_1); + pci_config_writel(isabdf, ICH9_LPC_GEN_PMCON_1, + value | ICH9_LPC_GEN_PMCON_1_SMI_LOCK); + smm_relocate_and_restore(); /* close the SMM memory window and enable normal SMM */ diff --git a/roms/seabios/src/fw/smp.c b/roms/seabios/src/fw/smp.c index 38fe383..a466ea6 100644 --- a/roms/seabios/src/fw/smp.c +++ b/roms/seabios/src/fw/smp.c @@ -1,4 +1,4 @@ -// CPU count detection +// QEMU multi-CPU initialization code // // Copyright (C) 2008 Kevin O'Connor // Copyright (C) 2006 Fabrice Bellard @@ -20,8 +20,8 @@ #define APIC_ENABLED 0x0100 -struct { u32 ecx, eax, edx; } smp_mtrr[32] VARFSEG; -u32 smp_mtrr_count VARFSEG; +static struct { u32 index; u64 val; } smp_mtrr[32]; +static u32 smp_mtrr_count; void wrmsr_smp(u32 index, u64 val) @@ -31,58 +31,51 @@ wrmsr_smp(u32 index, u64 val) warn_noalloc(); return; } - smp_mtrr[smp_mtrr_count].ecx = index; - smp_mtrr[smp_mtrr_count].eax = val; - smp_mtrr[smp_mtrr_count].edx = val >> 32; + smp_mtrr[smp_mtrr_count].index = index; + smp_mtrr[smp_mtrr_count].val = val; smp_mtrr_count++; } -u32 CountCPUs VARFSEG; u32 MaxCountCPUs; +static u32 CountCPUs; // 256 bits for the found APIC IDs -u32 FoundAPICIDs[256/32] VARFSEG; -extern void smp_ap_boot_code(void); -ASM16( - " .global smp_ap_boot_code\n" - "smp_ap_boot_code:\n" - - // Setup data segment - " movw $" __stringify(SEG_BIOS) ", %ax\n" - " movw %ax, %ds\n" - - // MTRR setup - " movl $smp_mtrr, %esi\n" - " movl smp_mtrr_count, %ebx\n" - "1:testl %ebx, %ebx\n" - " jz 2f\n" - " movl 0(%esi), %ecx\n" - " movl 4(%esi), %eax\n" - " movl 8(%esi), %edx\n" - " wrmsr\n" - " addl $12, %esi\n" - " decl %ebx\n" - " jmp 1b\n" - "2:\n" - - // get apic ID on EBX, set bit on FoundAPICIDs - " movl $1, %eax\n" - " cpuid\n" - " shrl $24, %ebx\n" - " lock btsl %ebx, FoundAPICIDs\n" - - // Increment the cpu counter - " lock incl CountCPUs\n" - - // Halt the processor. - "1:hlt\n" - " jmp 1b\n" - ); +static u32 FoundAPICIDs[256/32]; int apic_id_is_present(u8 apic_id) { return !!(FoundAPICIDs[apic_id/32] & (1ul << (apic_id % 32))); } +void VISIBLE32FLAT +handle_smp(void) +{ + if (!CONFIG_QEMU) + return; + + // Enable CPU caching + setcr0(getcr0() & ~(CR0_CD|CR0_NW)); + + // Detect apic_id + u32 eax, ebx, ecx, cpuid_features; + cpuid(1, &eax, &ebx, &ecx, &cpuid_features); + u8 apic_id = ebx>>24; + dprintf(DEBUG_HDL_smp, "handle_smp: apic_id=%d\n", apic_id); + + // MTRR setup + int i; + for (i=0; i>24; FoundAPICIDs[apic_id/32] |= (1 << (apic_id % 32)); - - // Init the counter. - writel(&CountCPUs, 1); + CountCPUs = 1; // Setup jump trampoline to counter code. u64 old = *(u64*)BUILD_AP_BOOT_ADDR; - // ljmpw $SEG_BIOS, $(smp_ap_boot_code - BUILD_BIOS_ADDR) + // ljmpw $SEG_BIOS, $(entry_smp - BUILD_BIOS_ADDR) + extern void entry_smp(void); u64 new = (0xea | ((u64)SEG_BIOS<<24) - | (((u32)smp_ap_boot_code - BUILD_BIOS_ADDR) << 8)); + | (((u32)entry_smp - BUILD_BIOS_ADDR) << 8)); *(u64*)BUILD_AP_BOOT_ADDR = new; // enable local APIC @@ -125,6 +117,9 @@ smp_setup(void) /* Set LINT1 as NMI, level triggered */ writel(APIC_LINT1, 0x8400); + // Init the lock. + writel(&SMPLock, 1); + // broadcast SIPI barrier(); writel(APIC_ICR_LOW, 0x000C4500); @@ -132,9 +127,19 @@ smp_setup(void) writel(APIC_ICR_LOW, 0x000C4600 | sipi_vector); // Wait for other CPUs to process the SIPI. - u8 cmos_smp_count = rtc_read(CMOS_BIOS_SMP_COUNT); - while (cmos_smp_count + 1 != readl(&CountCPUs)) - yield(); + u8 cmos_smp_count = rtc_read(CMOS_BIOS_SMP_COUNT) + 1; + while (cmos_smp_count != CountCPUs) + asm volatile( + // Release lock and allow other processors to use the stack. + " movl %%esp, %1\n" + " movl $0, %0\n" + // Reacquire lock and take back ownership of stack. + "1:rep ; nop\n" + " lock btsl $0, %0\n" + " jc 1b\n" + : "+m" (SMPLock), "+m" (SMPStack) + : : "cc", "memory"); + yield(); // Restore memory. *(u64*)BUILD_AP_BOOT_ADDR = old; @@ -143,6 +148,6 @@ smp_setup(void) if (!MaxCountCPUs || MaxCountCPUs < CountCPUs) MaxCountCPUs = CountCPUs; - dprintf(1, "Found %d cpu(s) max supported %d cpu(s)\n", readl(&CountCPUs), - MaxCountCPUs); + dprintf(1, "Found %d cpu(s) max supported %d cpu(s)\n", CountCPUs, + MaxCountCPUs); } diff --git a/roms/seabios/src/hw/blockcmd.c b/roms/seabios/src/hw/blockcmd.c index eb531d4..78c0e65 100644 --- a/roms/seabios/src/hw/blockcmd.c +++ b/roms/seabios/src/hw/blockcmd.c @@ -67,6 +67,123 @@ cdb_is_read(u8 *cdbcmd, u16 blocksize) return blocksize && cdbcmd[0] != CDB_CMD_WRITE_10; } + +/**************************************************************** + * Low level command requests + ****************************************************************/ + +static int +cdb_get_inquiry(struct disk_op_s *op, struct cdbres_inquiry *data) +{ + struct cdb_request_sense cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = CDB_CMD_INQUIRY; + cmd.length = sizeof(*data); + op->count = 1; + op->buf_fl = data; + return cdb_cmd_data(op, &cmd, sizeof(*data)); +} + +// Request SENSE +static int +cdb_get_sense(struct disk_op_s *op, struct cdbres_request_sense *data) +{ + struct cdb_request_sense cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = CDB_CMD_REQUEST_SENSE; + cmd.length = sizeof(*data); + op->count = 1; + op->buf_fl = data; + return cdb_cmd_data(op, &cmd, sizeof(*data)); +} + +// Test unit ready +static int +cdb_test_unit_ready(struct disk_op_s *op) +{ + struct cdb_request_sense cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = CDB_CMD_TEST_UNIT_READY; + op->count = 0; + op->buf_fl = NULL; + return cdb_cmd_data(op, &cmd, 0); +} + +// Request capacity +static int +cdb_read_capacity(struct disk_op_s *op, struct cdbres_read_capacity *data) +{ + struct cdb_read_capacity cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = CDB_CMD_READ_CAPACITY; + op->count = 1; + op->buf_fl = data; + return cdb_cmd_data(op, &cmd, sizeof(*data)); +} + +// Mode sense, geometry page. +static int +cdb_mode_sense_geom(struct disk_op_s *op, struct cdbres_mode_sense_geom *data) +{ + struct cdb_mode_sense cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = CDB_CMD_MODE_SENSE; + cmd.flags = 8; /* DBD */ + cmd.page = MODE_PAGE_HD_GEOMETRY; + cmd.count = cpu_to_be16(sizeof(*data)); + op->count = 1; + op->buf_fl = data; + return cdb_cmd_data(op, &cmd, sizeof(*data)); +} + +// Read sectors. +static int +cdb_read(struct disk_op_s *op) +{ + struct cdb_rwdata_10 cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = CDB_CMD_READ_10; + cmd.lba = cpu_to_be32(op->lba); + cmd.count = cpu_to_be16(op->count); + return cdb_cmd_data(op, &cmd, GET_GLOBALFLAT(op->drive_gf->blksize)); +} + +// Write sectors. +static int +cdb_write(struct disk_op_s *op) +{ + struct cdb_rwdata_10 cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = CDB_CMD_WRITE_10; + cmd.lba = cpu_to_be32(op->lba); + cmd.count = cpu_to_be16(op->count); + return cdb_cmd_data(op, &cmd, GET_GLOBALFLAT(op->drive_gf->blksize)); +} + + +/**************************************************************** + * Main SCSI commands + ****************************************************************/ + +int VISIBLE32FLAT +scsi_process_op(struct disk_op_s *op) +{ + switch (op->command) { + case CMD_READ: + return cdb_read(op); + case CMD_WRITE: + return cdb_write(op); + case CMD_FORMAT: + case CMD_RESET: + case CMD_ISREADY: + case CMD_VERIFY: + case CMD_SEEK: + return DISK_RET_SUCCESS; + default: + return DISK_RET_EPARAM; + } +} + int scsi_is_ready(struct disk_op_s *op) { @@ -200,91 +317,3 @@ scsi_drive_setup(struct drive_s *drive, const char *s, int prio) boot_add_hd(drive, desc, prio); return 0; } - -int -cdb_get_inquiry(struct disk_op_s *op, struct cdbres_inquiry *data) -{ - struct cdb_request_sense cmd; - memset(&cmd, 0, sizeof(cmd)); - cmd.command = CDB_CMD_INQUIRY; - cmd.length = sizeof(*data); - op->count = 1; - op->buf_fl = data; - return cdb_cmd_data(op, &cmd, sizeof(*data)); -} - -// Request SENSE -int -cdb_get_sense(struct disk_op_s *op, struct cdbres_request_sense *data) -{ - struct cdb_request_sense cmd; - memset(&cmd, 0, sizeof(cmd)); - cmd.command = CDB_CMD_REQUEST_SENSE; - cmd.length = sizeof(*data); - op->count = 1; - op->buf_fl = data; - return cdb_cmd_data(op, &cmd, sizeof(*data)); -} - -// Test unit ready -int -cdb_test_unit_ready(struct disk_op_s *op) -{ - struct cdb_request_sense cmd; - memset(&cmd, 0, sizeof(cmd)); - cmd.command = CDB_CMD_TEST_UNIT_READY; - op->count = 0; - op->buf_fl = NULL; - return cdb_cmd_data(op, &cmd, 0); -} - -// Request capacity -int -cdb_read_capacity(struct disk_op_s *op, struct cdbres_read_capacity *data) -{ - struct cdb_read_capacity cmd; - memset(&cmd, 0, sizeof(cmd)); - cmd.command = CDB_CMD_READ_CAPACITY; - op->count = 1; - op->buf_fl = data; - return cdb_cmd_data(op, &cmd, sizeof(*data)); -} - -// Mode sense, geometry page. -int -cdb_mode_sense_geom(struct disk_op_s *op, struct cdbres_mode_sense_geom *data) -{ - struct cdb_mode_sense cmd; - memset(&cmd, 0, sizeof(cmd)); - cmd.command = CDB_CMD_MODE_SENSE; - cmd.flags = 8; /* DBD */ - cmd.page = MODE_PAGE_HD_GEOMETRY; - cmd.count = cpu_to_be16(sizeof(*data)); - op->count = 1; - op->buf_fl = data; - return cdb_cmd_data(op, &cmd, sizeof(*data)); -} - -// Read sectors. -int -cdb_read(struct disk_op_s *op) -{ - struct cdb_rwdata_10 cmd; - memset(&cmd, 0, sizeof(cmd)); - cmd.command = CDB_CMD_READ_10; - cmd.lba = cpu_to_be32(op->lba); - cmd.count = cpu_to_be16(op->count); - return cdb_cmd_data(op, &cmd, GET_GLOBALFLAT(op->drive_gf->blksize)); -} - -// Write sectors. -int -cdb_write(struct disk_op_s *op) -{ - struct cdb_rwdata_10 cmd; - memset(&cmd, 0, sizeof(cmd)); - cmd.command = CDB_CMD_WRITE_10; - cmd.lba = cpu_to_be32(op->lba); - cmd.count = cpu_to_be16(op->count); - return cdb_cmd_data(op, &cmd, GET_GLOBALFLAT(op->drive_gf->blksize)); -} diff --git a/roms/seabios/src/hw/blockcmd.h b/roms/seabios/src/hw/blockcmd.h index 8bacfcf..df12a6d 100644 --- a/roms/seabios/src/hw/blockcmd.h +++ b/roms/seabios/src/hw/blockcmd.h @@ -102,15 +102,7 @@ struct cdbres_mode_sense_geom { // blockcmd.c int cdb_is_read(u8 *cdbcmd, u16 blocksize); struct disk_op_s; -int cdb_get_inquiry(struct disk_op_s *op, struct cdbres_inquiry *data); -int cdb_get_sense(struct disk_op_s *op, struct cdbres_request_sense *data); -int cdb_test_unit_ready(struct disk_op_s *op); -int cdb_read_capacity(struct disk_op_s *op, struct cdbres_read_capacity *data); -int cdb_mode_sense_geom(struct disk_op_s *op, struct cdbres_mode_sense_geom *data); -int cdb_inquiry(struct disk_op_s *op, struct cdbres_inquiry *data); -int cdb_read(struct disk_op_s *op); -int cdb_write(struct disk_op_s *op); - +int scsi_process_op(struct disk_op_s *op); int scsi_is_ready(struct disk_op_s *op); struct drive_s; int scsi_drive_setup(struct drive_s *drive, const char *s, int prio); diff --git a/roms/seabios/src/hw/floppy.c b/roms/seabios/src/hw/floppy.c index f4d7c01..d60362a 100644 --- a/roms/seabios/src/hw/floppy.c +++ b/roms/seabios/src/hw/floppy.c @@ -58,8 +58,6 @@ struct floppy_ext_dbt_s diskette_param_table2 VARFSEG = { .drive_type = 4, // drive type in cmos }; -struct floppy_dbt_s diskette_param_table VAR16FIXED(0xefc7); - struct floppyinfo_s { struct chs_s chs; u8 floppy_size; @@ -246,6 +244,7 @@ floppy_pio(int command, u8 *param) floppy_disable_controller(); return DISK_RET_ETIMEOUT; } + yield(); continue; } if (sts & 0x40) { @@ -279,6 +278,7 @@ floppy_pio(int command, u8 *param) floppy_disable_controller(); return DISK_RET_ETIMEOUT; } + yield(); continue; } if (i >= receive) { diff --git a/roms/seabios/src/hw/ps2port.h b/roms/seabios/src/hw/ps2port.h index 2e6f25a..e5d9014 100644 --- a/roms/seabios/src/hw/ps2port.h +++ b/roms/seabios/src/hw/ps2port.h @@ -2,13 +2,10 @@ #ifndef __PS2PORT_H #define __PS2PORT_H +#include "types.h" // u8 + #define PORT_PS2_DATA 0x0060 -#define PORT_PS2_CTRLB 0x0061 #define PORT_PS2_STATUS 0x0064 -#define PORT_A20 0x0092 - -// PORT_A20 bitdefs -#define A20_ENABLE_BIT 0x02 // Standard commands. #define I8042_CMD_CTL_RCTR 0x0120 @@ -60,16 +57,10 @@ #define I8042_CTR_AUXDIS 0x20 #define I8042_CTR_XLATE 0x40 -#ifndef __ASSEMBLY__ - -#include "types.h" // u8 - -// functions +// ps2port.c void i8042_reboot(void); int ps2_kbd_command(int command, u8 *param); int ps2_mouse_command(int command, u8 *param); void ps2port_setup(void); -#endif // !__ASSEMBLY__ - #endif // ps2port.h diff --git a/roms/seabios/src/hw/sdcard.c b/roms/seabios/src/hw/sdcard.c new file mode 100644 index 0000000..6ff93c8 --- /dev/null +++ b/roms/seabios/src/hw/sdcard.c @@ -0,0 +1,321 @@ +// PCI SD Host Controller Interface +// +// Copyright (C) 2014 Kevin O'Connor +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "block.h" // struct drive_s +#include "fw/paravirt.h" // runningOnQEMU +#include "malloc.h" // malloc_fseg +#include "output.h" // znprintf +#include "pci.h" // pci_config_readl +#include "pci_ids.h" // PCI_CLASS_SYSTEM_SDHCI +#include "pci_regs.h" // PCI_BASE_ADDRESS_0 +#include "stacks.h" // wait_preempt +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // boot_add_hd +#include "x86.h" // writel + +// SDHCI MMIO registers +struct sdhci_s { + u32 sdma_addr; + u16 block_size; + u16 block_count; + u32 arg; + u16 transfer_mode; + u16 cmd; + u32 response[4]; + u32 data; + u32 present_state; + u8 host_control; + u8 power_control; + u8 block_gap_control; + u8 wakeup_control; + u16 clock_control; + u8 timeout_control; + u8 software_reset; + u16 irq_status; + u16 error_irq_status; + u16 irq_enable; + u16 error_irq_enable; + u16 irq_signal; + u16 error_signal; + u16 auto_cmd12; + u8 pad_3E[2]; + u64 cap; + u64 max_current; + u16 force_auto_cmd12; + u16 force_error; + u8 adma_error; + u8 pad_55[3]; + u64 adma_addr; + u8 pad_60[156]; + u16 slot_irq; + u16 controller_version; +} PACKED; + +// SDHCI commands +#define SC_ALL_SEND_CID ((2<<8) | 0x21) +#define SC_SEND_RELATIVE_ADDR ((3<<8) | 0x22) +#define SC_SELECT_DESELECT_CARD ((7<<8) | 0x23) +#define SC_READ_SINGLE ((17<<8) | 0x22) +#define SC_READ_MULTIPLE ((18<<8) | 0x22) +#define SC_WRITE_SINGLE ((24<<8) | 0x22) +#define SC_WRITE_MULTIPLE ((25<<8) | 0x22) +#define SC_APP_CMD ((55<<8) | 0x22) +#define SC_APP_SEND_OP_COND ((41<<8) | 0x22) + +// SDHCI irqs +#define SI_CMD_COMPLETE (1<<0) +#define SI_TRANS_DONE (1<<1) +#define SI_WRITE_READY (1<<4) +#define SI_READ_READY (1<<5) + +// SDHCI present_state flags +#define SP_CMD_INHIBIT (1<<0) +#define SP_DAT_INHIBIT (1<<1) + +// SDHCI transfer_mode flags +#define ST_BLOCKCOUNT (1<<1) +#define ST_AUTO_CMD12 (1<<2) +#define ST_READ (1<<4) +#define ST_MULTIPLE (1<<5) + +// SDHCI result flags +#define SR_OCR_CCS (1<<30) + +// SDHCI timeouts +#define SDHCI_PIO_TIMEOUT 1000 // XXX - these are just made up +#define SDHCI_TRANSFER_TIMEOUT 10000 + +// Internal 'struct drive_s' storage for a detected card +struct sddrive_s { + struct drive_s drive; + struct sdhci_s *regs; + int card_type; +}; + +// SD card types +#define SF_MMC 0 +#define SF_SDSC 1 +#define SF_SDHC 2 + +// Repeatedly read a u16 register until the specific value is found +static int +waitw(u16 *reg, u16 mask, u16 value, u32 end) +{ + for (;;) { + u16 v = readw(reg); + if ((v & mask) == value) + return 0; + if (timer_check(end)) { + warn_timeout(); + return -1; + } + yield(); + } +} + +// Send a command to the card. +static int +sdcard_pio(struct sdhci_s *regs, int cmd, u32 *param) +{ + u32 end = timer_calc(SDHCI_PIO_TIMEOUT); + u16 busyf = SP_CMD_INHIBIT | ((cmd & 0x03) == 0x03 ? SP_DAT_INHIBIT : 0); + int ret = waitw((u16*)®s->present_state, busyf, 0, end); + if (ret) + return ret; + // Send command + writel(®s->arg, *param); + writew(®s->cmd, cmd); + ret = waitw(®s->irq_status, SI_CMD_COMPLETE, SI_CMD_COMPLETE, end); + if (ret) + return ret; + writew(®s->irq_status, SI_CMD_COMPLETE); + // Read response + memcpy(param, regs->response, sizeof(regs->response)); + return 0; +} + +// Send an "app specific" command to the card. +static int +sdcard_pio_app(struct sdhci_s *regs, int cmd, u32 *param) +{ + u32 aparam[4] = {}; + int ret = sdcard_pio(regs, SC_APP_CMD, aparam); + if (ret) + return ret; + return sdcard_pio(regs, cmd, param); +} + +// Send a command to the card which transfers data. +static int +sdcard_pio_transfer(struct sddrive_s *drive, int cmd, u32 addr + , void *data, int count) +{ + // Send command + writel(&drive->regs->block_size, DISK_SECTOR_SIZE); + writew(&drive->regs->block_count, count); // XXX - SC_SET_BLOCK_COUNT? + int isread = cmd != SC_WRITE_SINGLE && cmd != SC_WRITE_MULTIPLE; + u16 tmode = ((count > 1 ? ST_MULTIPLE|ST_AUTO_CMD12|ST_BLOCKCOUNT : 0) + | (isread ? ST_READ : 0)); + writew(&drive->regs->transfer_mode, tmode); + if (drive->card_type < SF_SDHC) + addr *= DISK_SECTOR_SIZE; + u32 param[4] = { addr }; + int ret = sdcard_pio(drive->regs, cmd, param); + if (ret) + return ret; + // Read/write data + u32 end = timer_calc(SDHCI_TRANSFER_TIMEOUT); + u16 cbit = isread ? SI_READ_READY : SI_WRITE_READY; + while (count--) { + ret = waitw(&drive->regs->irq_status, cbit, cbit, end); + if (ret) + return ret; + writew(&drive->regs->irq_status, cbit); + int i; + for (i=0; iregs->data); + else + writel(&drive->regs->data, *(u32*)data); + data += 4; + } + } + // Complete command + // XXX - SC_STOP_TRANSMISSION? + ret = waitw(&drive->regs->irq_status, SI_TRANS_DONE, SI_TRANS_DONE, end); + if (ret) + return ret; + writew(&drive->regs->irq_status, SI_TRANS_DONE); + return 0; +} + +// Read/write a block of data to/from the card. +static int +sdcard_readwrite(struct disk_op_s *op, int iswrite) +{ + struct sddrive_s *drive = container_of( + op->drive_gf, struct sddrive_s, drive); + int cmd = iswrite ? SC_WRITE_SINGLE : SC_READ_SINGLE; + if (op->count > 1) + cmd = iswrite ? SC_WRITE_MULTIPLE : SC_READ_MULTIPLE; + int ret = sdcard_pio_transfer(drive, cmd, op->lba, op->buf_fl, op->count); + if (ret) + return DISK_RET_EBADTRACK; + return DISK_RET_SUCCESS; +} + +int VISIBLE32FLAT +process_sdcard_op(struct disk_op_s *op) +{ + if (!CONFIG_SDCARD) + return 0; + switch (op->command) { + case CMD_READ: + return sdcard_readwrite(op, 0); + case CMD_WRITE: + return sdcard_readwrite(op, 1); + case CMD_FORMAT: + case CMD_RESET: + case CMD_ISREADY: + case CMD_VERIFY: + case CMD_SEEK: + return DISK_RET_SUCCESS; + default: + return DISK_RET_EPARAM; + } +} + + +/**************************************************************** + * Setup + ****************************************************************/ + +// Initialize an SD card +static int +sdcard_card_setup(struct sdhci_s *regs) +{ + // XXX - works on QEMU; probably wont on real hardware! + u32 param[4] = { 0x01 }; + int ret = sdcard_pio_app(regs, SC_APP_SEND_OP_COND, param); + if (ret) + return ret; + int card_type = (param[0] & SR_OCR_CCS) ? SF_SDHC : SF_SDSC; + param[0] = 0x00; + ret = sdcard_pio(regs, SC_ALL_SEND_CID, param); + if (ret) + return ret; + param[0] = 0x01 << 16; + ret = sdcard_pio(regs, SC_SEND_RELATIVE_ADDR, param); + if (ret) + return ret; + u16 rca = param[0] >> 16; + param[0] = rca << 16; + ret = sdcard_pio(regs, SC_SELECT_DESELECT_CARD, param); + if (ret) + return ret; + return card_type; +} + +// Setup and configure an SD card controller +static void +sdcard_controller_setup(void *data) +{ + struct pci_device *pci = data; + u16 bdf = pci->bdf; + wait_preempt(); // Avoid pci_config_readl when preempting + struct sdhci_s *regs = (void*)pci_config_readl(bdf, PCI_BASE_ADDRESS_0); + pci_config_maskw(bdf, PCI_COMMAND, 0, + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + + // Initialize controller + if (!runningOnQEMU()) + // XXX - this init logic will probably only work on qemu! + return; + writew(®s->irq_signal, 0); + writew(®s->irq_enable, 0xffff); + writew(®s->error_signal, 0); + writeb(®s->power_control, 0x0f); + writew(®s->clock_control, 0x0005); + + // Initialize card + int card_type = sdcard_card_setup(regs); + if (card_type < 0) + return; + + // Register drive + struct sddrive_s *drive = malloc_fseg(sizeof(*drive)); + if (!drive) { + warn_noalloc(); + return; + } + memset(drive, 0, sizeof(*drive)); + drive->drive.type = DTYPE_SDCARD; + drive->drive.blksize = DISK_SECTOR_SIZE; + drive->drive.sectors = (u64)-1; // XXX + drive->regs = regs; + drive->card_type = card_type; + + dprintf(1, "Found SD Card at %02x:%02x.%x\n" + , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), pci_bdf_to_fn(bdf)); + char *desc = znprintf(MAXDESCSIZE, "SD Card"); // XXX + boot_add_hd(&drive->drive, desc, bootprio_find_pci_device(pci)); +} + +void +sdcard_setup(void) +{ + if (!CONFIG_SDCARD) + return; + + struct pci_device *pci; + foreachpci(pci) { + if (pci->class != PCI_CLASS_SYSTEM_SDHCI || pci->prog_if >= 2) + // Not an SDHCI controller following SDHCI spec + continue; + run_thread(sdcard_controller_setup, pci); + } +} diff --git a/roms/seabios/src/hw/timer.c b/roms/seabios/src/hw/timer.c index 2832dec..5edc9fd 100644 --- a/roms/seabios/src/hw/timer.c +++ b/roms/seabios/src/hw/timer.c @@ -7,7 +7,6 @@ #include "biosvar.h" // GET_LOW #include "config.h" // CONFIG_* #include "output.h" // dprintf -#include "ps2port.h" // PORT_PS2_CTRLB #include "stacks.h" // yield #include "util.h" // timer_setup #include "x86.h" // cpuid @@ -16,6 +15,7 @@ #define PORT_PIT_COUNTER1 0x0041 #define PORT_PIT_COUNTER2 0x0042 #define PORT_PIT_MODE 0x0043 +#define PORT_PS2_CTRLB 0x0061 // Bits for PORT_PIT_MODE #define PM_SEL_TIMER0 (0<<6) diff --git a/roms/seabios/src/hw/usb-ehci.c b/roms/seabios/src/hw/usb-ehci.c index 17a7e8b..41f8579 100644 --- a/roms/seabios/src/hw/usb-ehci.c +++ b/roms/seabios/src/hw/usb-ehci.c @@ -8,6 +8,7 @@ #include "config.h" // CONFIG_* #include "output.h" // dprintf #include "malloc.h" // free +#include "memmap.h" // PAGE_SIZE #include "pci.h" // pci_bdf_to_bus #include "pci_ids.h" // PCI_CLASS_SERIAL_USB_UHCI #include "pci_regs.h" // PCI_BASE_ADDRESS_0 @@ -32,7 +33,7 @@ struct ehci_pipe { struct usb_pipe pipe; }; -static int PendingEHCIPorts; +static int PendingEHCI; /**************************************************************** @@ -50,26 +51,14 @@ ehci_hub_detect(struct usbhub_s *hub, u32 port) u32 *portreg = &cntl->regs->portsc[port]; u32 portsc = readl(portreg); - // Power up port. - if (!(portsc & PORT_POWER)) { - portsc |= PORT_POWER; - writel(portreg, portsc); - msleep(EHCI_TIME_POSTPOWER); - } else { - // Port is already powered up, but we don't know how long it - // has been powered up, so wait the 20ms. - msleep(EHCI_TIME_POSTPOWER); - } - portsc = readl(portreg); - if (!(portsc & PORT_CONNECT)) // No device present - goto doneearly; + return 0; if ((portsc & PORT_LINESTATUS_MASK) == PORT_LINESTATUS_KSTATE) { // low speed device writel(portreg, portsc | PORT_OWNER); - goto doneearly; + return -1; } // XXX - if just powered up, need to wait for USB_TIME_ATTDB? @@ -78,11 +67,7 @@ ehci_hub_detect(struct usbhub_s *hub, u32 port) portsc = (portsc & ~PORT_PE) | PORT_RESET; writel(portreg, portsc); msleep(USB_TIME_DRSTR); - return 0; - -doneearly: - PendingEHCIPorts--; - return -1; + return 1; } // Reset device on port @@ -98,21 +83,17 @@ ehci_hub_reset(struct usbhub_s *hub, u32 port) writel(portreg, portsc); msleep(EHCI_TIME_POSTRESET); - int rv = -1; portsc = readl(portreg); if (!(portsc & PORT_CONNECT)) // No longer connected - goto resetfail; + return -1; if (!(portsc & PORT_PE)) { // full speed device writel(portreg, portsc | PORT_OWNER); - goto resetfail; + return -1; } - rv = USB_HIGHSPEED; -resetfail: - PendingEHCIPorts--; - return rv; + return USB_HIGHSPEED; } // Disable port @@ -135,7 +116,18 @@ static struct usbhub_op_s ehci_HubOp = { static int check_ehci_ports(struct usb_ehci_s *cntl) { - ASSERT32FLAT(); + // Power up ports. + int i; + for (i=0; icheckports; i++) { + u32 *portreg = &cntl->regs->portsc[i]; + u32 portsc = readl(portreg); + if (!(portsc & PORT_POWER)) { + portsc |= PORT_POWER; + writel(portreg, portsc); + } + } + msleep(EHCI_TIME_POSTPOWER); + struct usbhub_s hub; memset(&hub, 0, sizeof(hub)); hub.cntl = &cntl->usb; @@ -204,7 +196,7 @@ ehci_free_pipes(struct usb_ehci_s *cntl) if (next == start) break; struct ehci_pipe *pipe = container_of(next, struct ehci_pipe, qh); - if (pipe->pipe.cntl != &cntl->usb) + if (usb_is_freelist(&cntl->usb, &pipe->pipe)) pos->next = next->next; else pos = next; @@ -231,6 +223,7 @@ configure_ehci(void *data) struct ehci_qh *async_qh = memalign_high(EHCI_QH_ALIGN, sizeof(*async_qh)); if (!fl || !intr_qh || !async_qh) { warn_noalloc(); + PendingEHCI--; goto fail; } @@ -246,6 +239,7 @@ configure_ehci(void *data) break; if (timer_check(end)) { warn_timeout(); + PendingEHCI--; goto fail; } yield(); @@ -279,6 +273,7 @@ configure_ehci(void *data) // Set default of high speed for root hub. writel(&cntl->regs->configflag, 1); + PendingEHCI--; // Find devices int count = check_ehci_ports(cntl); @@ -319,7 +314,7 @@ ehci_controller_setup(struct pci_device *pci) cntl->regs = (void*)caps + readb(&caps->caplength); if (hcc_params & HCC_64BIT_ADDR) cntl->regs->ctrldssegment = 0; - PendingEHCIPorts += cntl->checkports; + PendingEHCI++; dprintf(1, "EHCI init on dev %02x:%02x.%x (regs=%p)\n" , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf) @@ -343,9 +338,9 @@ ehci_setup(void) ehci_controller_setup(pci); } - // Wait for all EHCI ports to initialize. This forces OHCI/UHCI - // setup to always be after any EHCI ports are set to low speed. - while (PendingEHCIPorts) + // Wait for all EHCI controllers to initialize. This forces OHCI/UHCI + // setup to always be after any EHCI ports are routed to EHCI. + while (PendingEHCI) yield(); } @@ -392,7 +387,7 @@ ehci_alloc_intr_pipe(struct usbdevice_s *usbdev { struct usb_ehci_s *cntl = container_of( usbdev->hub->cntl, struct usb_ehci_s, usb); - int frameexp = usb_getFrameExp(usbdev, epdesc); + int frameexp = usb_get_period(usbdev, epdesc); dprintf(7, "ehci_alloc_intr_pipe %p %d\n", &cntl->usb, frameexp); if (frameexp > 10) @@ -451,11 +446,14 @@ fail: } struct usb_pipe * -ehci_alloc_pipe(struct usbdevice_s *usbdev - , struct usb_endpoint_descriptor *epdesc) +ehci_realloc_pipe(struct usbdevice_s *usbdev, struct usb_pipe *upipe + , struct usb_endpoint_descriptor *epdesc) { if (! CONFIG_USB_EHCI) return NULL; + usb_add_freelist(upipe); + if (!epdesc) + return NULL; u8 eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; if (eptype == USB_ENDPOINT_XFER_INT) return ehci_alloc_intr_pipe(usbdev, epdesc); @@ -463,7 +461,7 @@ ehci_alloc_pipe(struct usbdevice_s *usbdev usbdev->hub->cntl, struct usb_ehci_s, usb); dprintf(7, "ehci_alloc_async_pipe %p %d\n", &cntl->usb, eptype); - struct usb_pipe *usbpipe = usb_getFreePipe(&cntl->usb, eptype); + struct usb_pipe *usbpipe = usb_get_freelist(&cntl->usb, eptype); if (usbpipe) { // Use previously allocated pipe. struct ehci_pipe *pipe = container_of(usbpipe, struct ehci_pipe, pipe); @@ -503,9 +501,8 @@ ehci_reset_pipe(struct ehci_pipe *pipe) } static int -ehci_wait_td(struct ehci_pipe *pipe, struct ehci_qtd *td, int timeout) +ehci_wait_td(struct ehci_pipe *pipe, struct ehci_qtd *td, u32 end) { - u32 end = timer_calc(timeout); u32 status; for (;;) { status = td->token; @@ -534,132 +531,84 @@ ehci_wait_td(struct ehci_pipe *pipe, struct ehci_qtd *td, int timeout) return 0; } -static int -fillTDbuffer(struct ehci_qtd *td, u16 maxpacket, const void *buf, int bytes) -{ - u32 dest = (u32)buf; - u32 *pos = td->buf; - while (bytes) { - if (pos >= &td->buf[ARRAY_SIZE(td->buf)]) - // More data than can transfer in a single qtd - only use - // full packets to prevent a babble error. - return ALIGN_DOWN(dest - (u32)buf, maxpacket); - u32 count = bytes; - u32 max = 0x1000 - (dest & 0xfff); - if (count > max) - count = max; - *pos = dest; - bytes -= count; - dest += count; - pos++; - } - return dest - (u32)buf; -} - -int -ehci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize - , void *data, int datasize) +static void +ehci_fill_tdbuf(struct ehci_qtd *td, u32 dest, int transfer) { - ASSERT32FLAT(); - if (! CONFIG_USB_EHCI) - return -1; - dprintf(5, "ehci_control %p (dir=%d cmd=%d data=%d)\n" - , p, dir, cmdsize, datasize); - if (datasize > 4*4096 || cmdsize > 4*4096) { - // XXX - should support larger sizes. - warn_noalloc(); - return -1; - } - struct ehci_pipe *pipe = container_of(p, struct ehci_pipe, pipe); - - // Setup transfer descriptors - struct ehci_qtd *tds = memalign_tmphigh(EHCI_QTD_ALIGN, sizeof(*tds) * 3); - if (!tds) { - warn_noalloc(); - return -1; - } - memset(tds, 0, sizeof(*tds) * 3); - struct ehci_qtd *td = tds; - - td->qtd_next = (u32)&td[1]; - td->alt_next = EHCI_PTR_TERM; - td->token = (ehci_explen(cmdsize) | QTD_STS_ACTIVE - | QTD_PID_SETUP | ehci_maxerr(3)); - u16 maxpacket = pipe->pipe.maxpacket; - fillTDbuffer(td, maxpacket, cmd, cmdsize); - td++; - - if (datasize) { - td->qtd_next = (u32)&td[1]; - td->alt_next = EHCI_PTR_TERM; - td->token = (QTD_TOGGLE | ehci_explen(datasize) | QTD_STS_ACTIVE - | (dir ? QTD_PID_IN : QTD_PID_OUT) | ehci_maxerr(3)); - fillTDbuffer(td, maxpacket, data, datasize); - td++; - } - - td->qtd_next = EHCI_PTR_TERM; - td->alt_next = EHCI_PTR_TERM; - td->token = (QTD_TOGGLE | QTD_STS_ACTIVE - | (dir ? QTD_PID_OUT : QTD_PID_IN) | ehci_maxerr(3)); - - // Transfer data - barrier(); - pipe->qh.qtd_next = (u32)tds; - int i, ret=0; - for (i=0; i<3; i++) { - struct ehci_qtd *td = &tds[i]; - ret = ehci_wait_td(pipe, td, 500); - if (ret) - break; - } - free(tds); - return ret; + u32 *pos = td->buf, end = dest + transfer; + for (; dest < end; dest = ALIGN_DOWN(dest + PAGE_SIZE, PAGE_SIZE)) + *pos++ = dest; } -#define STACKQTDS 4 +#define STACKQTDS 6 int -ehci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize) +ehci_send_pipe(struct usb_pipe *p, int dir, const void *cmd + , void *data, int datasize) { if (! CONFIG_USB_EHCI) return -1; struct ehci_pipe *pipe = container_of(p, struct ehci_pipe, pipe); - dprintf(7, "ehci_send_bulk qh=%p dir=%d data=%p size=%d\n" + dprintf(7, "ehci_send_pipe qh=%p dir=%d data=%p size=%d\n" , &pipe->qh, dir, data, datasize); - // Allocate 4 tds on stack (with required alignment) + // Allocate tds on stack (with required alignment) u8 tdsbuf[sizeof(struct ehci_qtd) * STACKQTDS + EHCI_QTD_ALIGN - 1]; - struct ehci_qtd *tds = (void*)ALIGN((u32)tdsbuf, EHCI_QTD_ALIGN); + struct ehci_qtd *tds = (void*)ALIGN((u32)tdsbuf, EHCI_QTD_ALIGN), *td = tds; memset(tds, 0, sizeof(*tds) * STACKQTDS); - barrier(); - SET_LOWFLAT(pipe->qh.qtd_next, (u32)MAKE_FLATPTR(GET_SEG(SS), tds)); + // Setup transfer descriptors u16 maxpacket = GET_LOWFLAT(pipe->pipe.maxpacket); - int tdpos = 0; - while (datasize) { - struct ehci_qtd *td = &tds[tdpos++ % STACKQTDS]; - int ret = ehci_wait_td(pipe, td, 5000); - if (ret) + u32 toggle = 0; + if (cmd) { + // Send setup pid on control transfers + td->qtd_next = (u32)MAKE_FLATPTR(GET_SEG(SS), td+1); + td->alt_next = EHCI_PTR_TERM; + td->token = (ehci_explen(USB_CONTROL_SETUP_SIZE) | QTD_STS_ACTIVE + | QTD_PID_SETUP | ehci_maxerr(3)); + ehci_fill_tdbuf(td, (u32)cmd, USB_CONTROL_SETUP_SIZE); + td++; + toggle = QTD_TOGGLE; + } + u32 dest = (u32)data, dataend = dest + datasize; + while (dest < dataend) { + // Send data pids + if (td >= &tds[STACKQTDS]) { + warn_noalloc(); return -1; - - struct ehci_qtd *nexttd_fl = MAKE_FLATPTR(GET_SEG(SS) - , &tds[tdpos % STACKQTDS]); - - int transfer = fillTDbuffer(td, maxpacket, data, datasize); - td->qtd_next = (transfer==datasize ? EHCI_PTR_TERM : (u32)nexttd_fl); + } + int maxtransfer = 5*PAGE_SIZE - (dest & (PAGE_SIZE-1)); + int transfer = dataend - dest; + if (transfer > maxtransfer) + transfer = ALIGN_DOWN(maxtransfer, maxpacket); + td->qtd_next = (u32)MAKE_FLATPTR(GET_SEG(SS), td+1); td->alt_next = EHCI_PTR_TERM; - barrier(); - td->token = (ehci_explen(transfer) | QTD_STS_ACTIVE + td->token = (ehci_explen(transfer) | toggle | QTD_STS_ACTIVE | (dir ? QTD_PID_IN : QTD_PID_OUT) | ehci_maxerr(3)); - - data += transfer; - datasize -= transfer; + ehci_fill_tdbuf(td, dest, transfer); + td++; + dest += transfer; } + if (cmd) { + // Send status pid on control transfers + if (td >= &tds[STACKQTDS]) { + warn_noalloc(); + return -1; + } + td->qtd_next = EHCI_PTR_TERM; + td->alt_next = EHCI_PTR_TERM; + td->token = (QTD_TOGGLE | QTD_STS_ACTIVE + | (dir ? QTD_PID_OUT : QTD_PID_IN) | ehci_maxerr(3)); + td++; + } + + // Transfer data + (td-1)->qtd_next = EHCI_PTR_TERM; + barrier(); + SET_LOWFLAT(pipe->qh.qtd_next, (u32)MAKE_FLATPTR(GET_SEG(SS), tds)); + u32 end = timer_calc(usb_xfer_time(p, datasize)); int i; - for (i=0; ispeed == USB_SUPERSPEED) + req.wValue = USB_DT_HUB3<<8; + else + req.wValue = USB_DT_HUB<<8; req.wIndex = 0; req.wLength = sizeof(*desc); - return send_default_control(pipe, &req, desc); + return usb_send_default_control(pipe, &req, desc); +} + +static int +set_hub_depth(struct usb_pipe *pipe, u16 depth) +{ + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE; + req.bRequest = HUB_REQ_SET_HUB_DEPTH; + req.wValue = depth; + req.wIndex = 0; + req.wLength = 0; + return usb_send_default_control(pipe, &req, NULL); } static int @@ -33,7 +48,7 @@ set_port_feature(struct usbhub_s *hub, int port, int feature) req.wIndex = port + 1; req.wLength = 0; mutex_lock(&hub->lock); - int ret = send_default_control(hub->usbdev->defpipe, &req, NULL); + int ret = usb_send_default_control(hub->usbdev->defpipe, &req, NULL); mutex_unlock(&hub->lock); return ret; } @@ -48,7 +63,7 @@ clear_port_feature(struct usbhub_s *hub, int port, int feature) req.wIndex = port + 1; req.wLength = 0; mutex_lock(&hub->lock); - int ret = send_default_control(hub->usbdev->defpipe, &req, NULL); + int ret = usb_send_default_control(hub->usbdev->defpipe, &req, NULL); mutex_unlock(&hub->lock); return ret; } @@ -63,7 +78,7 @@ get_port_status(struct usbhub_s *hub, int port, struct usb_port_status *sts) req.wIndex = port + 1; req.wLength = sizeof(*sts); mutex_lock(&hub->lock); - int ret = send_default_control(hub->usbdev->defpipe, &req, sts); + int ret = usb_send_default_control(hub->usbdev->defpipe, &req, sts); mutex_unlock(&hub->lock); return ret; } @@ -72,37 +87,13 @@ get_port_status(struct usbhub_s *hub, int port, struct usb_port_status *sts) static int usb_hub_detect(struct usbhub_s *hub, u32 port) { - // Turn on power to port. - int ret = set_port_feature(hub, port, USB_PORT_FEAT_POWER); - if (ret) - goto fail; - - // Wait for port power to stabilize. - msleep(hub->powerwait); - - // Check periodically for a device connect. struct usb_port_status sts; - u32 end = timer_calc(USB_TIME_SIGATT); - for (;;) { - ret = get_port_status(hub, port, &sts); - if (ret) - goto fail; - if (sts.wPortStatus & USB_PORT_STAT_CONNECTION) - // Device connected. - break; - if (timer_check(end)) - // No device found. - return -1; - msleep(5); + int ret = get_port_status(hub, port, &sts); + if (ret) { + dprintf(1, "Failure on hub port %d detect\n", port); + return -1; } - - // XXX - wait USB_TIME_ATTDB time? - - return 0; - -fail: - dprintf(1, "Failure on hub port %d detect\n", port); - return -1; + return (sts.wPortStatus & USB_PORT_STAT_CONNECTION) ? 1 : 0; } // Disable port @@ -129,7 +120,9 @@ usb_hub_reset(struct usbhub_s *hub, u32 port) ret = get_port_status(hub, port, &sts); if (ret) goto fail; - if (!(sts.wPortStatus & USB_PORT_STAT_RESET)) + if (!(sts.wPortStatus & USB_PORT_STAT_RESET) + && (hub->usbdev->speed != USB_SUPERSPEED + || !(sts.wPortStatus & USB_PORT_STAT_LINK_MASK))) break; if (timer_check(end)) { warn_timeout(); @@ -143,6 +136,8 @@ usb_hub_reset(struct usbhub_s *hub, u32 port) // Device no longer present return -1; + if (hub->usbdev->speed == USB_SUPERSPEED) + return USB_SUPERSPEED; return ((sts.wPortStatus & USB_PORT_STAT_SPEED_MASK) >> USB_PORT_STAT_SPEED_SHIFT); @@ -175,9 +170,32 @@ usb_hub_setup(struct usbdevice_s *usbdev) memset(&hub, 0, sizeof(hub)); hub.usbdev = usbdev; hub.cntl = usbdev->defpipe->cntl; - hub.powerwait = desc.bPwrOn2PwrGood * 2; hub.portcount = desc.bNbrPorts; hub.op = &HubOp; + + if (usbdev->speed == USB_SUPERSPEED) { + int depth = 0; + struct usbdevice_s *parent = usbdev->hub->usbdev; + while (parent) { + depth++; + parent = parent->hub->usbdev; + } + + ret = set_hub_depth(usbdev->defpipe, depth); + if (ret) + return ret; + } + + // Turn on power to ports. + int port; + for (port=0; portcntl, struct usb_ohci_s, usb); u32 sts = readl(&cntl->regs->roothub_portstatus[port]); - if (!(sts & RH_PS_CCS)) - // No device. - return -1; - - // XXX - need to wait for USB_TIME_ATTDB if just powered up? - - return 0; + return (sts & RH_PS_CCS) ? 1 : 0; } // Disable port @@ -124,10 +120,10 @@ check_ohci_ports(struct usb_ohci_s *cntl) // Wait for next USB frame to start - for ensuring safe memory release. static void -ohci_waittick(struct usb_ohci_s *cntl) +ohci_waittick(struct ohci_regs *regs) { barrier(); - struct ohci_hcca *hcca = (void*)cntl->regs->hcca; + struct ohci_hcca *hcca = (void*)regs->hcca; u32 startframe = hcca->frame_no; u32 end = timer_calc(1000 * 5); for (;;) { @@ -149,7 +145,7 @@ ohci_free_pipes(struct usb_ohci_s *cntl) u32 creg = readl(&cntl->regs->control); if (creg & (OHCI_CTRL_CLE|OHCI_CTRL_BLE)) { writel(&cntl->regs->control, creg & ~(OHCI_CTRL_CLE|OHCI_CTRL_BLE)); - ohci_waittick(cntl); + ohci_waittick(cntl->regs); } u32 *pos = &cntl->regs->ed_controlhead; @@ -158,7 +154,7 @@ ohci_free_pipes(struct usb_ohci_s *cntl) if (!next) break; struct ohci_pipe *pipe = container_of(next, struct ohci_pipe, ed); - if (pipe->pipe.cntl != &cntl->usb) { + if (usb_is_freelist(&cntl->usb, &pipe->pipe)) { *pos = next->hwNextED; free(pipe); } else { @@ -215,7 +211,7 @@ start_ohci(struct usb_ohci_s *cntl, struct ohci_hcca *hcca) // Go into operational state writel(&cntl->regs->control - , (OHCI_CTRL_CBSR | OHCI_CTRL_CLE | OHCI_CTRL_PLE + , (OHCI_CTRL_CBSR | OHCI_CTRL_CLE | OHCI_CTRL_BLE | OHCI_CTRL_PLE | OHCI_USB_OPER | oldrwc)); readl(&cntl->regs->control); // flush writes @@ -326,6 +322,9 @@ ohci_desc2pipe(struct ohci_pipe *pipe, struct usbdevice_s *usbdev pipe->ed.hwINFO = (ED_SKIP | usbdev->devaddr | (pipe->pipe.ep << 7) | (epdesc->wMaxPacketSize << 16) | (usbdev->speed ? ED_LOWSPEED : 0)); + struct usb_ohci_s *cntl = container_of( + usbdev->hub->cntl, struct usb_ohci_s, usb); + pipe->regs = cntl->regs; } static struct usb_pipe * @@ -334,7 +333,7 @@ ohci_alloc_intr_pipe(struct usbdevice_s *usbdev { struct usb_ohci_s *cntl = container_of( usbdev->hub->cntl, struct usb_ohci_s, usb); - int frameexp = usb_getFrameExp(usbdev, epdesc); + int frameexp = usb_get_period(usbdev, epdesc); dprintf(7, "ohci_alloc_intr_pipe %p %d\n", &cntl->usb, frameexp); if (frameexp > 5) @@ -393,23 +392,22 @@ err: } struct usb_pipe * -ohci_alloc_pipe(struct usbdevice_s *usbdev - , struct usb_endpoint_descriptor *epdesc) +ohci_realloc_pipe(struct usbdevice_s *usbdev, struct usb_pipe *upipe + , struct usb_endpoint_descriptor *epdesc) { if (! CONFIG_USB_OHCI) return NULL; + usb_add_freelist(upipe); + if (!epdesc) + return NULL; u8 eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; if (eptype == USB_ENDPOINT_XFER_INT) return ohci_alloc_intr_pipe(usbdev, epdesc); - if (eptype != USB_ENDPOINT_XFER_CONTROL) { - dprintf(1, "OHCI Bulk transfers not supported.\n"); - return NULL; - } struct usb_ohci_s *cntl = container_of( usbdev->hub->cntl, struct usb_ohci_s, usb); dprintf(7, "ohci_alloc_async_pipe %p\n", &cntl->usb); - struct usb_pipe *usbpipe = usb_getFreePipe(&cntl->usb, eptype); + struct usb_pipe *usbpipe = usb_get_freelist(&cntl->usb, eptype); if (usbpipe) { // Use previously allocated pipe. struct ohci_pipe *pipe = container_of(usbpipe, struct ohci_pipe, pipe); @@ -418,7 +416,11 @@ ohci_alloc_pipe(struct usbdevice_s *usbdev } // Allocate a new queue head. - struct ohci_pipe *pipe = malloc_tmphigh(sizeof(*pipe)); + struct ohci_pipe *pipe; + if (eptype == USB_ENDPOINT_XFER_CONTROL) + pipe = malloc_tmphigh(sizeof(*pipe)); + else + pipe = malloc_low(sizeof(*pipe)); if (!pipe) { warn_noalloc(); return NULL; @@ -427,91 +429,109 @@ ohci_alloc_pipe(struct usbdevice_s *usbdev ohci_desc2pipe(pipe, usbdev, epdesc); // Add queue head to controller list. - pipe->ed.hwNextED = cntl->regs->ed_controlhead; + u32 *head = &cntl->regs->ed_controlhead; + if (eptype != USB_ENDPOINT_XFER_CONTROL) + head = &cntl->regs->ed_bulkhead; + pipe->ed.hwNextED = *head; barrier(); - cntl->regs->ed_controlhead = (u32)&pipe->ed; + *head = (u32)&pipe->ed; return &pipe->pipe; } static int -wait_ed(struct ohci_ed *ed) +wait_ed(struct ohci_ed *ed, int timeout) { - // XXX - 500ms just a guess - u32 end = timer_calc(500); + u32 end = timer_calc(timeout); for (;;) { - if (ed->hwHeadP == ed->hwTailP) + if ((ed->hwHeadP & ~(ED_C|ED_H)) == ed->hwTailP) return 0; if (timer_check(end)) { warn_timeout(); + dprintf(1, "ohci ed info=%x tail=%x head=%x next=%x\n" + , ed->hwINFO, ed->hwTailP, ed->hwHeadP, ed->hwNextED); return -1; } yield(); } } +#define STACKOTDS 18 +#define OHCI_TD_ALIGN 16 + int -ohci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize - , void *data, int datasize) +ohci_send_pipe(struct usb_pipe *p, int dir, const void *cmd + , void *data, int datasize) { + ASSERT32FLAT(); if (! CONFIG_USB_OHCI) return -1; - dprintf(5, "ohci_control %p\n", p); - if (datasize > 4096) { - // XXX - should support larger sizes. - warn_noalloc(); - return -1; - } + dprintf(7, "ohci_send_pipe %p\n", p); struct ohci_pipe *pipe = container_of(p, struct ohci_pipe, pipe); - struct usb_ohci_s *cntl = container_of( - pipe->pipe.cntl, struct usb_ohci_s, usb); + + // Allocate tds on stack (with required alignment) + u8 tdsbuf[sizeof(struct ohci_td) * STACKOTDS + OHCI_TD_ALIGN - 1]; + struct ohci_td *tds = (void*)ALIGN((u32)tdsbuf, OHCI_TD_ALIGN), *td = tds; + memset(tds, 0, sizeof(*tds) * STACKOTDS); // Setup transfer descriptors - struct ohci_td *tds = malloc_tmphigh(sizeof(*tds) * 3); - if (!tds) { - warn_noalloc(); - return -1; + u16 maxpacket = pipe->pipe.maxpacket; + u32 toggle = 0, statuscmd = OHCI_BLF; + if (cmd) { + // Send setup pid on control transfers + td->hwINFO = TD_DP_SETUP | TD_T_DATA0 | TD_CC; + td->hwCBP = (u32)cmd; + td->hwNextTD = (u32)&td[1]; + td->hwBE = (u32)cmd + USB_CONTROL_SETUP_SIZE - 1; + td++; + toggle = TD_T_DATA1; + statuscmd = OHCI_CLF; } - struct ohci_td *td = tds; - td->hwINFO = TD_DP_SETUP | TD_T_DATA0 | TD_CC; - td->hwCBP = (u32)cmd; - td->hwNextTD = (u32)&td[1]; - td->hwBE = (u32)cmd + cmdsize - 1; - td++; - if (datasize) { - td->hwINFO = (dir ? TD_DP_IN : TD_DP_OUT) | TD_T_DATA1 | TD_CC; - td->hwCBP = (u32)data; + u32 dest = (u32)data, dataend = dest + datasize; + while (dest < dataend) { + // Send data pids + if (td >= &tds[STACKOTDS]) { + warn_noalloc(); + return -1; + } + int maxtransfer = 2*PAGE_SIZE - (dest & (PAGE_SIZE-1)); + int transfer = dataend - dest; + if (transfer > maxtransfer) + transfer = ALIGN_DOWN(maxtransfer, maxpacket); + td->hwINFO = (dir ? TD_DP_IN : TD_DP_OUT) | toggle | TD_CC; + td->hwCBP = dest; td->hwNextTD = (u32)&td[1]; - td->hwBE = (u32)data + datasize - 1; + td->hwBE = dest + transfer - 1; + td++; + dest += transfer; + } + if (cmd) { + // Send status pid on control transfers + if (td >= &tds[STACKOTDS]) { + warn_noalloc(); + return -1; + } + td->hwINFO = (dir ? TD_DP_OUT : TD_DP_IN) | TD_T_DATA1 | TD_CC; + td->hwCBP = 0; + td->hwNextTD = (u32)&td[1]; + td->hwBE = 0; td++; } - td->hwINFO = (dir ? TD_DP_OUT : TD_DP_IN) | TD_T_DATA1 | TD_CC; - td->hwCBP = 0; - td->hwNextTD = (u32)&td[1]; - td->hwBE = 0; - td++; // Transfer data - pipe->ed.hwHeadP = (u32)tds; + pipe->ed.hwHeadP = (u32)tds | (pipe->ed.hwHeadP & ED_C); pipe->ed.hwTailP = (u32)td; barrier(); pipe->ed.hwINFO &= ~ED_SKIP; - writel(&cntl->regs->cmdstatus, OHCI_CLF); + writel(&pipe->regs->cmdstatus, statuscmd); - int ret = wait_ed(&pipe->ed); + int ret = wait_ed(&pipe->ed, usb_xfer_time(p, datasize)); pipe->ed.hwINFO |= ED_SKIP; if (ret) - ohci_waittick(cntl); - free(tds); + ohci_waittick(pipe->regs); return ret; } int -ohci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize) -{ - return -1; -} - -int ohci_poll_intr(struct usb_pipe *p, void *data) { ASSERT16(); diff --git a/roms/seabios/src/hw/usb-ohci.h b/roms/seabios/src/hw/usb-ohci.h index 3cae21f..5a275a3 100644 --- a/roms/seabios/src/hw/usb-ohci.h +++ b/roms/seabios/src/hw/usb-ohci.h @@ -5,12 +5,12 @@ void ohci_setup(void); struct usbdevice_s; struct usb_endpoint_descriptor; -struct usb_pipe *ohci_alloc_pipe(struct usbdevice_s *usbdev - , struct usb_endpoint_descriptor *epdesc); struct usb_pipe; -int ohci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize - , void *data, int datasize); -int ohci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize); +struct usb_pipe *ohci_realloc_pipe(struct usbdevice_s *usbdev + , struct usb_pipe *upipe + , struct usb_endpoint_descriptor *epdesc); +int ohci_send_pipe(struct usb_pipe *p, int dir, const void *cmd + , void *data, int datasize); int ohci_poll_intr(struct usb_pipe *p, void *data); @@ -106,6 +106,7 @@ struct ohci_regs { #define OHCI_HCR (1 << 0) #define OHCI_CLF (1 << 1) +#define OHCI_BLF (1 << 2) #define OHCI_INTR_MIE (1 << 31) diff --git a/roms/seabios/src/hw/usb-uas.c b/roms/seabios/src/hw/usb-uas.c index 9474383..6ef8d09 100644 --- a/roms/seabios/src/hw/usb-uas.c +++ b/roms/seabios/src/hw/usb-uas.c @@ -266,9 +266,9 @@ usb_uas_setup(struct usbdevice_s *usbdev) return 0; fail: - free_pipe(command); - free_pipe(status); - free_pipe(data_in); - free_pipe(data_out); + usb_free_pipe(usbdev, command); + usb_free_pipe(usbdev, status); + usb_free_pipe(usbdev, data_in); + usb_free_pipe(usbdev, data_out); return -1; } diff --git a/roms/seabios/src/hw/usb-uhci.c b/roms/seabios/src/hw/usb-uhci.c index 2321e21..69c33ee 100644 --- a/roms/seabios/src/hw/usb-uhci.c +++ b/roms/seabios/src/hw/usb-uhci.c @@ -43,18 +43,17 @@ uhci_hub_detect(struct usbhub_s *hub, u32 port) { struct usb_uhci_s *cntl = container_of(hub->cntl, struct usb_uhci_s, usb); u16 ioport = cntl->iobase + USBPORTSC1 + port * 2; - u16 status = inw(ioport); if (!(status & USBPORTSC_CCS)) - // No device - return -1; + // No device found. + return 0; // XXX - if just powered up, need to wait for USB_TIME_ATTDB? // Begin reset on port outw(USBPORTSC_PR, ioport); msleep(USB_TIME_DRSTR); - return 0; + return 1; } // Reset device on port @@ -139,7 +138,7 @@ uhci_free_pipes(struct usb_uhci_s *cntl) break; struct uhci_qh *next = (void*)(link & ~UHCI_PTR_BITS); struct uhci_pipe *pipe = container_of(next, struct uhci_pipe, qh); - if (pipe->pipe.cntl != &cntl->usb) + if (usb_is_freelist(&cntl->usb, &pipe->pipe)) pos->link = next->link; else pos = next; @@ -288,7 +287,7 @@ uhci_alloc_intr_pipe(struct usbdevice_s *usbdev { struct usb_uhci_s *cntl = container_of( usbdev->hub->cntl, struct usb_uhci_s, usb); - int frameexp = usb_getFrameExp(usbdev, epdesc); + int frameexp = usb_get_period(usbdev, epdesc); dprintf(7, "uhci_alloc_intr_pipe %p %d\n", &cntl->usb, frameexp); if (frameexp > 10) @@ -353,11 +352,14 @@ fail: } struct usb_pipe * -uhci_alloc_pipe(struct usbdevice_s *usbdev - , struct usb_endpoint_descriptor *epdesc) +uhci_realloc_pipe(struct usbdevice_s *usbdev, struct usb_pipe *upipe + , struct usb_endpoint_descriptor *epdesc) { if (! CONFIG_USB_UHCI) return NULL; + usb_add_freelist(upipe); + if (!epdesc) + return NULL; u8 eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; if (eptype == USB_ENDPOINT_XFER_INT) return uhci_alloc_intr_pipe(usbdev, epdesc); @@ -365,7 +367,7 @@ uhci_alloc_pipe(struct usbdevice_s *usbdev usbdev->hub->cntl, struct usb_uhci_s, usb); dprintf(7, "uhci_alloc_async_pipe %p %d\n", &cntl->usb, eptype); - struct usb_pipe *usbpipe = usb_getFreePipe(&cntl->usb, eptype); + struct usb_pipe *usbpipe = usb_get_freelist(&cntl->usb, eptype); if (usbpipe) { // Use previously allocated pipe. usb_desc2pipe(usbpipe, usbdev, epdesc); @@ -398,9 +400,8 @@ uhci_alloc_pipe(struct usbdevice_s *usbdev } static int -wait_pipe(struct uhci_pipe *pipe, int timeout) +wait_pipe(struct uhci_pipe *pipe, u32 end) { - u32 end = timer_calc(timeout); for (;;) { u32 el_link = GET_LOWFLAT(pipe->qh.element); if (el_link & UHCI_PTR_TERM) @@ -422,9 +423,8 @@ wait_pipe(struct uhci_pipe *pipe, int timeout) } static int -wait_td(struct uhci_td *td) +wait_td(struct uhci_td *td, u32 end) { - u32 end = timer_calc(5000); // XXX - lookup real time. u32 status; for (;;) { status = td->status; @@ -443,73 +443,17 @@ wait_td(struct uhci_td *td) return 0; } -int -uhci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize - , void *data, int datasize) -{ - ASSERT32FLAT(); - if (! CONFIG_USB_UHCI) - return -1; - dprintf(5, "uhci_control %p\n", p); - struct uhci_pipe *pipe = container_of(p, struct uhci_pipe, pipe); - - int maxpacket = pipe->pipe.maxpacket; - int lowspeed = pipe->pipe.speed; - int devaddr = pipe->pipe.devaddr | (pipe->pipe.ep << 7); - - // Setup transfer descriptors - int count = 2 + DIV_ROUND_UP(datasize, maxpacket); - struct uhci_td *tds = malloc_tmphigh(sizeof(*tds) * count); - if (!tds) { - warn_noalloc(); - return -1; - } - - tds[0].link = (u32)&tds[1] | UHCI_PTR_DEPTH; - tds[0].status = (uhci_maxerr(3) | (lowspeed ? TD_CTRL_LS : 0) - | TD_CTRL_ACTIVE); - tds[0].token = (uhci_explen(cmdsize) | (devaddr << TD_TOKEN_DEVADDR_SHIFT) - | USB_PID_SETUP); - tds[0].buffer = (void*)cmd; - int toggle = TD_TOKEN_TOGGLE; - int i; - for (i=1; iqh.element = (u32)&tds[0]; - int ret = wait_pipe(pipe, 500); - free(tds); - return ret; -} - -#define STACKTDS 4 +#define STACKTDS 16 #define TDALIGN 16 int -uhci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize) +uhci_send_pipe(struct usb_pipe *p, int dir, const void *cmd + , void *data, int datasize) { if (! CONFIG_USB_UHCI) return -1; struct uhci_pipe *pipe = container_of(p, struct uhci_pipe, pipe); - dprintf(7, "uhci_send_bulk qh=%p dir=%d data=%p size=%d\n" + dprintf(7, "uhci_send_pipe qh=%p dir=%d data=%p size=%d\n" , &pipe->qh, dir, data, datasize); int maxpacket = GET_LOWFLAT(pipe->pipe.maxpacket); int lowspeed = GET_LOWFLAT(pipe->pipe.speed); @@ -517,28 +461,44 @@ uhci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize) | (GET_LOWFLAT(pipe->pipe.ep) << 7)); int toggle = GET_LOWFLAT(pipe->toggle) ? TD_TOKEN_TOGGLE : 0; - // Allocate 4 tds on stack (16byte aligned) + // Allocate 16 tds on stack (16byte aligned) u8 tdsbuf[sizeof(struct uhci_td) * STACKTDS + TDALIGN - 1]; struct uhci_td *tds = (void*)ALIGN((u32)tdsbuf, TDALIGN); memset(tds, 0, sizeof(*tds) * STACKTDS); + int tdpos = 0; // Enable tds + u32 end = timer_calc(usb_xfer_time(p, datasize)); barrier(); SET_LOWFLAT(pipe->qh.element, (u32)MAKE_FLATPTR(GET_SEG(SS), tds)); - int tdpos = 0; + // Setup transfer descriptors + if (cmd) { + // Send setup pid on control transfers + struct uhci_td *td = &tds[tdpos++ % STACKTDS]; + u32 nexttd = (u32)MAKE_FLATPTR(GET_SEG(SS), &tds[tdpos % STACKTDS]); + td->link = nexttd | UHCI_PTR_DEPTH; + td->token = (uhci_explen(USB_CONTROL_SETUP_SIZE) + | (devaddr << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_SETUP); + td->buffer = (void*)cmd; + barrier(); + td->status = (uhci_maxerr(3) | (lowspeed ? TD_CTRL_LS : 0) + | TD_CTRL_ACTIVE); + toggle = TD_TOKEN_TOGGLE; + } while (datasize) { + // Send data pids struct uhci_td *td = &tds[tdpos++ % STACKTDS]; - int ret = wait_td(td); + int ret = wait_td(td, end); if (ret) goto fail; int transfer = datasize; if (transfer > maxpacket) transfer = maxpacket; - struct uhci_td *nexttd_fl = MAKE_FLATPTR(GET_SEG(SS) - , &tds[tdpos % STACKTDS]); - td->link = (transfer==datasize ? UHCI_PTR_TERM : (u32)nexttd_fl); + u32 nexttd = (u32)MAKE_FLATPTR(GET_SEG(SS), &tds[tdpos % STACKTDS]); + td->link = ((transfer==datasize && !cmd) + ? UHCI_PTR_TERM : (nexttd | UHCI_PTR_DEPTH)); td->token = (uhci_explen(transfer) | toggle | (devaddr << TD_TOKEN_DEVADDR_SHIFT) | (dir ? USB_PID_IN : USB_PID_OUT)); @@ -551,8 +511,23 @@ uhci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize) data += transfer; datasize -= transfer; } + if (cmd) { + // Send status pid on control transfers + struct uhci_td *td = &tds[tdpos++ % STACKTDS]; + int ret = wait_td(td, end); + if (ret) + goto fail; + td->link = UHCI_PTR_TERM; + td->token = (uhci_explen(0) | TD_TOKEN_TOGGLE + | (devaddr << TD_TOKEN_DEVADDR_SHIFT) + | (dir ? USB_PID_OUT : USB_PID_IN)); + td->buffer = 0; + barrier(); + td->status = (uhci_maxerr(0) | (lowspeed ? TD_CTRL_LS : 0) + | TD_CTRL_ACTIVE); + } SET_LOWFLAT(pipe->toggle, !!toggle); - return wait_pipe(pipe, 5000); + return wait_pipe(pipe, end); fail: dprintf(1, "uhci_send_bulk failed\n"); SET_LOWFLAT(pipe->qh.element, UHCI_PTR_TERM); diff --git a/roms/seabios/src/hw/usb-uhci.h b/roms/seabios/src/hw/usb-uhci.h index 2916465..bff70c6 100644 --- a/roms/seabios/src/hw/usb-uhci.h +++ b/roms/seabios/src/hw/usb-uhci.h @@ -5,12 +5,12 @@ void uhci_setup(void); struct usbdevice_s; struct usb_endpoint_descriptor; -struct usb_pipe *uhci_alloc_pipe(struct usbdevice_s *usbdev - , struct usb_endpoint_descriptor *epdesc); struct usb_pipe; -int uhci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize - , void *data, int datasize); -int uhci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize); +struct usb_pipe *uhci_realloc_pipe(struct usbdevice_s *usbdev + , struct usb_pipe *upipe + , struct usb_endpoint_descriptor *epdesc); +int uhci_send_pipe(struct usb_pipe *p, int dir, const void *cmd + , void *data, int datasize); int uhci_poll_intr(struct usb_pipe *p, void *data); diff --git a/roms/seabios/src/hw/usb-xhci.c b/roms/seabios/src/hw/usb-xhci.c index ca1fe25..fd58334 100644 --- a/roms/seabios/src/hw/usb-xhci.c +++ b/roms/seabios/src/hw/usb-xhci.c @@ -5,7 +5,6 @@ // // This file may be distributed under the terms of the GNU LGPLv3 license. -#include "biosvar.h" // GET_LOWFLAT #include "config.h" // CONFIG_* #include "malloc.h" // memalign_low #include "memmap.h" // PAGE_SIZE @@ -13,7 +12,7 @@ #include "pci.h" // pci_bdf_to_bus #include "pci_ids.h" // PCI_CLASS_SERIAL_USB_XHCI #include "pci_regs.h" // PCI_BASE_ADDRESS_0 -#include "string.h" // memcpy_fl +#include "string.h" // memcpy #include "usb.h" // struct usb_s #include "usb-xhci.h" // struct ehci_qh #include "util.h" // timer_calc @@ -229,7 +228,6 @@ struct xhci_ring { struct usb_xhci_s { struct usb_s usb; - struct usbhub_s hub; /* devinfo */ u32 baseaddr; @@ -303,343 +301,128 @@ static const int eptype_to_xhci_out[] = { [ USB_ENDPOINT_XFER_INT ] = 3, }; -// -------------------------------------------------------------- -// internal functions, 16bit + 32bit - -static void xhci_doorbell(struct usb_xhci_s *xhci, u32 slotid, u32 value) -{ - struct xhci_db *db = GET_LOWFLAT(xhci->db); - void *addr = &db[slotid].doorbell; - writel(addr, value); -} - -static void xhci_process_events(struct usb_xhci_s *xhci) -{ - struct xhci_ring *evts = GET_LOWFLAT(xhci->evts); - - for (;;) { - /* check for event */ - u32 nidx = GET_LOWFLAT(evts->nidx); - u32 cs = GET_LOWFLAT(evts->cs); - struct xhci_trb *etrb = evts->ring + nidx; - u32 control = GET_LOWFLAT(etrb->control); - if ((control & TRB_C) != (cs ? 1 : 0)) - return; - - /* process event */ - u32 evt_type = TRB_TYPE(control); - u32 evt_cc = (GET_LOWFLAT(etrb->status) >> 24) & 0xff; - switch (evt_type) { - case ER_TRANSFER: - case ER_COMMAND_COMPLETE: - { - struct xhci_trb *rtrb = (void*)GET_LOWFLAT(etrb->ptr_low); - struct xhci_ring *ring = XHCI_RING(rtrb); - struct xhci_trb *evt = &ring->evt; - u32 eidx = rtrb - ring->ring + 1; - dprintf(5, "%s: ring %p [trb %p, evt %p, type %d, eidx %d, cc %d]\n", - __func__, ring, rtrb, evt, evt_type, eidx, evt_cc); - memcpy_fl(evt, etrb, sizeof(*etrb)); - SET_LOWFLAT(ring->eidx, eidx); - break; - } - case ER_PORT_STATUS_CHANGE: - { - u32 portid = (GET_LOWFLAT(etrb->ptr_low) >> 24) & 0xff; - dprintf(3, "%s: status change port #%d\n", - __func__, portid); - break; - } - default: - dprintf(1, "%s: unknown event, type %d, cc %d\n", - __func__, evt_type, evt_cc); - break; - } - - /* move ring index, notify xhci */ - nidx++; - if (nidx == XHCI_RING_ITEMS) { - nidx = 0; - cs = cs ? 0 : 1; - SET_LOWFLAT(evts->cs, cs); - } - SET_LOWFLAT(evts->nidx, nidx); - struct xhci_ir *ir = GET_LOWFLAT(xhci->ir); - u32 erdp = (u32)(evts->ring + nidx); - writel(&ir->erdp_low, erdp); - writel(&ir->erdp_high, 0); - } -} - -static int xhci_ring_busy(struct xhci_ring *ring) -{ - u32 eidx = GET_LOWFLAT(ring->eidx); - u32 nidx = GET_LOWFLAT(ring->nidx); - return (eidx != nidx); -} - -static int xhci_event_wait(struct usb_xhci_s *xhci, - struct xhci_ring *ring, - u32 timeout) +static int wait_bit(u32 *reg, u32 mask, int value, u32 timeout) { u32 end = timer_calc(timeout); - for (;;) { - xhci_process_events(xhci); - if (!xhci_ring_busy(ring)) { - u32 status = GET_LOWFLAT(ring->evt.status); - return (status >> 24) & 0xff; - } + while ((readl(reg) & mask) != value) { if (timer_check(end)) { warn_timeout(); return -1; } yield(); } + return 0; } -static void xhci_trb_queue(struct xhci_ring *ring, - struct xhci_trb *trb) -{ - u32 nidx = GET_LOWFLAT(ring->nidx); - u32 cs = GET_LOWFLAT(ring->cs); - struct xhci_trb *dst; - u32 control; - - if (nidx == XHCI_RING_ITEMS-1) { - dst = ring->ring + nidx; - control = (TR_LINK << 10); // trb type - control |= TRB_LK_TC; - control |= (cs ? TRB_C : 0); - SET_LOWFLAT(dst->ptr_low, (u32)&ring[0]); - SET_LOWFLAT(dst->ptr_high, 0); - SET_LOWFLAT(dst->status, 0); - SET_LOWFLAT(dst->control, control); - nidx = 0; - cs = cs ? 0 : 1; - SET_LOWFLAT(ring->nidx, nidx); - SET_LOWFLAT(ring->cs, cs); - - dprintf(5, "%s: ring %p [linked]\n", __func__, ring); - } - - dst = ring->ring + nidx; - control = GET_LOWFLAT(trb->control) | (cs ? TRB_C : 0); - SET_LOWFLAT(dst->ptr_low, GET_LOWFLAT(trb->ptr_low)); - SET_LOWFLAT(dst->ptr_high, GET_LOWFLAT(trb->ptr_high)); - SET_LOWFLAT(dst->status, GET_LOWFLAT(trb->status)); - SET_LOWFLAT(dst->control, control); - nidx++; - SET_LOWFLAT(ring->nidx, nidx); - - dprintf(5, "%s: ring %p [nidx %d, len %d]\n", - __func__, ring, nidx, - GET_LOWFLAT(trb->status) & 0xffff); -} +/**************************************************************** + * Root hub + ****************************************************************/ -static void xhci_xfer_queue(struct xhci_pipe *pipe, - struct xhci_trb *trb) -{ - xhci_trb_queue(&pipe->reqs, trb); -} +#define XHCI_TIME_POSTPOWER 20 -static void xhci_xfer_kick(struct xhci_pipe *pipe) +// Check if device attached to port +static void +xhci_print_port_state(int loglevel, const char *prefix, u32 port, u32 portsc) { - struct usb_xhci_s *xhci = container_of( - GET_LOWFLAT(pipe->pipe.cntl), struct usb_xhci_s, usb); - u32 slotid = GET_LOWFLAT(pipe->slotid); - u32 epid = GET_LOWFLAT(pipe->epid); + u32 pls = xhci_get_field(portsc, XHCI_PORTSC_PLS); + u32 speed = xhci_get_field(portsc, XHCI_PORTSC_SPEED); - dprintf(5, "%s: ring %p, slotid %d, epid %d\n", - __func__, &pipe->reqs, slotid, epid); - xhci_doorbell(xhci, slotid, epid); + dprintf(loglevel, "%s port #%d: 0x%08x,%s%s pls %d, speed %d [%s]\n", + prefix, port + 1, portsc, + (portsc & XHCI_PORTSC_PP) ? " powered," : "", + (portsc & XHCI_PORTSC_PED) ? " enabled," : "", + pls, speed, speed_name[speed]); } -static void xhci_xfer_normal(struct xhci_pipe *pipe, - void *data, int datalen) +static int +xhci_hub_detect(struct usbhub_s *hub, u32 port) { - struct xhci_trb trb; - - memset(&trb, 0, sizeof(trb)); - trb.ptr_low = (u32)data; - trb.status = datalen; - trb.control |= (TR_NORMAL << 10); // trb type - trb.control |= TRB_TR_IOC; - - xhci_xfer_queue(pipe, MAKE_FLATPTR(GET_SEG(SS), &trb)); - xhci_xfer_kick(pipe); + struct usb_xhci_s *xhci = container_of(hub->cntl, struct usb_xhci_s, usb); + u32 portsc = readl(&xhci->pr[port].portsc); + return (portsc & XHCI_PORTSC_CCS) ? 1 : 0; } -// -------------------------------------------------------------- -// internal functions, pure 32bit - -static int wait_bit(u32 *reg, u32 mask, int value, u32 timeout) +// Reset device on port +static int +xhci_hub_reset(struct usbhub_s *hub, u32 port) { - ASSERT32FLAT(); - u32 end = timer_calc(timeout); + struct usb_xhci_s *xhci = container_of(hub->cntl, struct usb_xhci_s, usb); + u32 portsc = readl(&xhci->pr[port].portsc); + int rc; - while ((readl(reg) & mask) != value) { - if (timer_check(end)) { - warn_timeout(); + switch (xhci_get_field(portsc, XHCI_PORTSC_PLS)) { + case PLS_U0: + rc = speed_from_xhci[xhci_get_field(portsc, XHCI_PORTSC_SPEED)]; + break; + case PLS_POLLING: + xhci_print_port_state(3, __func__, port, portsc); + portsc |= XHCI_PORTSC_PR; + writel(&xhci->pr[port].portsc, portsc); + if (wait_bit(&xhci->pr[port].portsc, XHCI_PORTSC_PED, XHCI_PORTSC_PED, 100) != 0) return -1; - } - yield(); + portsc = readl(&xhci->pr[port].portsc); + rc = speed_from_xhci[xhci_get_field(portsc, XHCI_PORTSC_SPEED)]; + break; + default: + rc = -1; + break; } - return 0; -} -static int xhci_cmd_submit(struct usb_xhci_s *xhci, - struct xhci_trb *cmd) -{ - ASSERT32FLAT(); - int rc; - - mutex_lock(&xhci->cmds->lock); - xhci_trb_queue(xhci->cmds, cmd); - xhci_doorbell(xhci, 0, 0); - rc = xhci_event_wait(xhci, xhci->cmds, 1000); - mutex_unlock(&xhci->cmds->lock); + xhci_print_port_state(1, "XHCI", port, portsc); return rc; } -static int xhci_cmd_enable_slot(struct usb_xhci_s *xhci) -{ - ASSERT32FLAT(); - struct xhci_trb cmd = { - .ptr_low = 0, - .ptr_high = 0, - .status = 0, - .control = (CR_ENABLE_SLOT << 10) - }; - dprintf(3, "%s:\n", __func__); - int cc = xhci_cmd_submit(xhci, &cmd); - if (cc != CC_SUCCESS) - return -1; - return (xhci->cmds->evt.control >> 24) & 0xff; -} - -#if 0 -static int xhci_cmd_disable_slot(struct usb_xhci_s *xhci, u32 slotid) -{ - ASSERT32FLAT(); - struct xhci_trb cmd = { - .ptr_low = 0, - .ptr_high = 0, - .status = 0, - .control = (slotid << 24) | (CR_DISABLE_SLOT << 10) - }; - dprintf(3, "%s: slotid %d\n", __func__, slotid); - return xhci_cmd_submit(xhci, &cmd); -} -#endif - -static int xhci_cmd_address_device(struct usb_xhci_s *xhci, u32 slotid - , struct xhci_inctx *inctx) -{ - ASSERT32FLAT(); - struct xhci_trb cmd = { - .ptr_low = (u32)inctx, - .ptr_high = 0, - .status = 0, - .control = (slotid << 24) | (CR_ADDRESS_DEVICE << 10) - }; - dprintf(3, "%s: slotid %d\n", __func__, slotid); - return xhci_cmd_submit(xhci, &cmd); -} - -static int xhci_cmd_configure_endpoint(struct usb_xhci_s *xhci, u32 slotid - , struct xhci_inctx *inctx) +static void +xhci_hub_disconnect(struct usbhub_s *hub, u32 port) { - ASSERT32FLAT(); - struct xhci_trb cmd = { - .ptr_low = (u32)inctx, - .ptr_high = 0, - .status = 0, - .control = (slotid << 24) | (CR_CONFIGURE_ENDPOINT << 10) - }; - dprintf(3, "%s: slotid %d, add 0x%x, del 0x%x\n", __func__, - slotid, inctx->add, inctx->del); - return xhci_cmd_submit(xhci, &cmd); + // XXX - should turn the port power off. } -static int xhci_cmd_evaluate_context(struct usb_xhci_s *xhci, u32 slotid - , struct xhci_inctx *inctx) -{ - ASSERT32FLAT(); - struct xhci_trb cmd = { - .ptr_low = (u32)inctx, - .ptr_high = 0, - .status = 0, - .control = (slotid << 24) | (CR_EVALUATE_CONTEXT << 10) - }; - dprintf(3, "%s: slotid %d, add 0x%x, del 0x%x\n", __func__, - slotid, inctx->add, inctx->del); - return xhci_cmd_submit(xhci, &cmd); -} +static struct usbhub_op_s xhci_hub_ops = { + .detect = xhci_hub_detect, + .reset = xhci_hub_reset, + .disconnect = xhci_hub_disconnect, +}; -static void xhci_xfer_setup(struct xhci_pipe *pipe, - const struct usb_ctrlrequest *req, - int dir, int datalen) +// Find any devices connected to the root hub. +static int +xhci_check_ports(struct usb_xhci_s *xhci) { - ASSERT32FLAT(); - struct xhci_trb trb; - - memset(&trb, 0, sizeof(trb)); - trb.ptr_low |= req->bRequestType; - trb.ptr_low |= (req->bRequest) << 8; - trb.ptr_low |= (req->wValue) << 16; - trb.ptr_high |= req->wIndex; - trb.ptr_high |= (req->wLength) << 16; - trb.status |= 8; // length - trb.control |= (TR_SETUP << 10); // trb type - trb.control |= TRB_TR_IDT; - if (datalen) - trb.control |= (dir ? 3 : 2) << 16; // transfer type - xhci_xfer_queue(pipe, &trb); + // Wait for port power to stabilize. + msleep(XHCI_TIME_POSTPOWER); + + struct usbhub_s hub; + memset(&hub, 0, sizeof(hub)); + hub.cntl = &xhci->usb; + hub.portcount = xhci->ports; + hub.op = &xhci_hub_ops; + usb_enumerate(&hub); + return hub.devcount; } -static void xhci_xfer_data(struct xhci_pipe *pipe, - int dir, void *data, int datalen) -{ - ASSERT32FLAT(); - struct xhci_trb trb; - memset(&trb, 0, sizeof(trb)); - trb.ptr_low = (u32)data; - trb.status = datalen; - trb.control |= (TR_DATA << 10); // trb type - if (dir) - trb.control |= (1 << 16); - xhci_xfer_queue(pipe, &trb); -} +/**************************************************************** + * Setup + ****************************************************************/ -static void xhci_xfer_status(struct xhci_pipe *pipe, int dir, int datalen) +static void +xhci_free_pipes(struct usb_xhci_s *xhci) { - ASSERT32FLAT(); - struct xhci_trb trb; - - memset(&trb, 0, sizeof(trb)); - trb.control |= (TR_STATUS << 10); // trb type - trb.control |= TRB_TR_IOC; - if (!datalen || !dir) - trb.control |= (1 << 16); - - xhci_xfer_queue(pipe, &trb); - xhci_xfer_kick(pipe); + // XXX - should walk list of pipes and free unused pipes. } static void configure_xhci(void *data) { - ASSERT32FLAT(); struct usb_xhci_s *xhci = data; u32 reg; xhci->devs = memalign_high(64, sizeof(*xhci->devs) * (xhci->slots + 1)); xhci->eseg = memalign_high(64, sizeof(*xhci->eseg)); xhci->cmds = memalign_high(XHCI_RING_SIZE, sizeof(*xhci->cmds)); - xhci->evts = memalign_low(XHCI_RING_SIZE, sizeof(*xhci->evts)); + xhci->evts = memalign_high(XHCI_RING_SIZE, sizeof(*xhci->evts)); if (!xhci->devs || !xhci->cmds || !xhci->evts || !xhci->eseg) { warn_noalloc(); goto fail; @@ -704,12 +487,11 @@ configure_xhci(void *data) reg |= XHCI_CMD_RS; writel(&xhci->op->usbcmd, reg); - // FIXME: try find a more elegant way than a fixed delay - msleep(100); - - usb_enumerate(&xhci->hub); - // XXX - should walk list of pipes and free unused pipes. - if (xhci->hub.devcount) + // Find devices + int count = xhci_check_ports(xhci); + xhci_free_pipes(xhci); + if (count) + // Success return; // No devices found - shutdown and free controller. @@ -727,88 +509,315 @@ fail: free(xhci); } -// -------------------------------------------------------------- -// xhci root hub - -// Check if device attached to port static void -xhci_print_port_state(int loglevel, const char *prefix, u32 port, u32 portsc) +xhci_controller_setup(struct pci_device *pci) { - ASSERT32FLAT(); - u32 pls = xhci_get_field(portsc, XHCI_PORTSC_PLS); - u32 speed = xhci_get_field(portsc, XHCI_PORTSC_SPEED); + struct usb_xhci_s *xhci = malloc_high(sizeof(*xhci)); + if (!xhci) { + warn_noalloc(); + return; + } + memset(xhci, 0, sizeof(*xhci)); - dprintf(loglevel, "%s port #%d: 0x%08x,%s%s pls %d, speed %d [%s]\n", - prefix, port + 1, portsc, - (portsc & XHCI_PORTSC_PP) ? " powered," : "", - (portsc & XHCI_PORTSC_PED) ? " enabled," : "", - pls, speed, speed_name[speed]); + wait_preempt(); // Avoid pci_config_readl when preempting + xhci->baseaddr = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0) + & PCI_BASE_ADDRESS_MEM_MASK; + xhci->caps = (void*)(xhci->baseaddr); + xhci->op = (void*)(xhci->baseaddr + readb(&xhci->caps->caplength)); + xhci->pr = (void*)(xhci->baseaddr + readb(&xhci->caps->caplength) + 0x400); + xhci->db = (void*)(xhci->baseaddr + readl(&xhci->caps->dboff)); + xhci->ir = (void*)(xhci->baseaddr + readl(&xhci->caps->rtsoff) + 0x20); + + u32 hcs1 = readl(&xhci->caps->hcsparams1); + u32 hcc = readl(&xhci->caps->hccparams); + xhci->ports = (hcs1 >> 24) & 0xff; + xhci->slots = hcs1 & 0xff; + xhci->xcap = ((hcc >> 16) & 0xffff) << 2; + xhci->context64 = (hcc & 0x04) ? 1 : 0; + + xhci->usb.pci = pci; + xhci->usb.type = USB_TYPE_XHCI; + + dprintf(1, "XHCI init on dev %02x:%02x.%x: regs @ %p, %d ports, %d slots" + ", %d byte contexts\n" + , pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf) + , pci_bdf_to_fn(pci->bdf), xhci->caps + , xhci->ports, xhci->slots, xhci->context64 ? 64 : 32); + + if (xhci->xcap) { + u32 off, addr = xhci->baseaddr + xhci->xcap; + do { + struct xhci_xcap *xcap = (void*)addr; + u32 ports, name, cap = readl(&xcap->cap); + switch (cap & 0xff) { + case 0x02: + name = readl(&xcap->data[0]); + ports = readl(&xcap->data[1]); + dprintf(1, "XHCI protocol %c%c%c%c %x.%02x" + ", %d ports (offset %d), def %x\n" + , (name >> 0) & 0xff + , (name >> 8) & 0xff + , (name >> 16) & 0xff + , (name >> 24) & 0xff + , (cap >> 24) & 0xff + , (cap >> 16) & 0xff + , (ports >> 8) & 0xff + , (ports >> 0) & 0xff + , ports >> 16); + break; + default: + dprintf(1, "XHCI extcap 0x%x @ %x\n", cap & 0xff, addr); + break; + } + off = (cap >> 8) & 0xff; + addr += off << 2; + } while (off > 0); + } + + u32 pagesize = readl(&xhci->op->pagesize); + if (PAGE_SIZE != (pagesize<<12)) { + dprintf(1, "XHCI driver does not support page size code %d\n" + , pagesize<<12); + free(xhci); + return; + } + + pci_config_maskw(pci->bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER); + + run_thread(configure_xhci, xhci); } -static int -xhci_hub_detect(struct usbhub_s *hub, u32 port) +void +xhci_setup(void) { - ASSERT32FLAT(); - struct usb_xhci_s *xhci = container_of(hub->cntl, struct usb_xhci_s, usb); - u32 portsc = readl(&xhci->pr[port].portsc); + if (! CONFIG_USB_XHCI) + return; + struct pci_device *pci; + foreachpci(pci) { + if (pci_classprog(pci) == PCI_CLASS_SERIAL_USB_XHCI) + xhci_controller_setup(pci); + } +} + + +/**************************************************************** + * End point communication + ****************************************************************/ + +static void xhci_doorbell(struct usb_xhci_s *xhci, u32 slotid, u32 value) +{ + struct xhci_db *db = xhci->db; + void *addr = &db[slotid].doorbell; + writel(addr, value); +} + +static void xhci_process_events(struct usb_xhci_s *xhci) +{ + struct xhci_ring *evts = xhci->evts; + + for (;;) { + /* check for event */ + u32 nidx = evts->nidx; + u32 cs = evts->cs; + struct xhci_trb *etrb = evts->ring + nidx; + u32 control = etrb->control; + if ((control & TRB_C) != (cs ? 1 : 0)) + return; + + /* process event */ + u32 evt_type = TRB_TYPE(control); + u32 evt_cc = (etrb->status >> 24) & 0xff; + switch (evt_type) { + case ER_TRANSFER: + case ER_COMMAND_COMPLETE: + { + struct xhci_trb *rtrb = (void*)etrb->ptr_low; + struct xhci_ring *ring = XHCI_RING(rtrb); + struct xhci_trb *evt = &ring->evt; + u32 eidx = rtrb - ring->ring + 1; + dprintf(5, "%s: ring %p [trb %p, evt %p, type %d, eidx %d, cc %d]\n", + __func__, ring, rtrb, evt, evt_type, eidx, evt_cc); + memcpy(evt, etrb, sizeof(*etrb)); + ring->eidx = eidx; + break; + } + case ER_PORT_STATUS_CHANGE: + { + u32 portid = (etrb->ptr_low >> 24) & 0xff; + dprintf(3, "%s: status change port #%d\n", + __func__, portid); + break; + } + default: + dprintf(1, "%s: unknown event, type %d, cc %d\n", + __func__, evt_type, evt_cc); + break; + } + + /* move ring index, notify xhci */ + nidx++; + if (nidx == XHCI_RING_ITEMS) { + nidx = 0; + cs = cs ? 0 : 1; + evts->cs = cs; + } + evts->nidx = nidx; + struct xhci_ir *ir = xhci->ir; + u32 erdp = (u32)(evts->ring + nidx); + writel(&ir->erdp_low, erdp); + writel(&ir->erdp_high, 0); + } +} + +static int xhci_ring_busy(struct xhci_ring *ring) +{ + u32 eidx = ring->eidx; + u32 nidx = ring->nidx; + return (eidx != nidx); +} + +static int xhci_event_wait(struct usb_xhci_s *xhci, + struct xhci_ring *ring, + u32 timeout) +{ + u32 end = timer_calc(timeout); + + for (;;) { + xhci_process_events(xhci); + if (!xhci_ring_busy(ring)) { + u32 status = ring->evt.status; + return (status >> 24) & 0xff; + } + if (timer_check(end)) { + warn_timeout(); + return -1; + } + yield(); + } +} + +static void xhci_trb_queue(struct xhci_ring *ring, + struct xhci_trb *trb) +{ + u32 nidx = ring->nidx; + u32 cs = ring->cs; + struct xhci_trb *dst; + u32 control; + + if (nidx == XHCI_RING_ITEMS-1) { + dst = ring->ring + nidx; + control = (TR_LINK << 10); // trb type + control |= TRB_LK_TC; + control |= (cs ? TRB_C : 0); + dst->ptr_low = (u32)&ring[0]; + dst->ptr_high = 0; + dst->status = 0; + dst->control = control; + nidx = 0; + cs = cs ? 0 : 1; + ring->nidx = nidx; + ring->cs = cs; - xhci_print_port_state(3, __func__, port, portsc); - switch (xhci_get_field(portsc, XHCI_PORTSC_PLS)) { - case PLS_U0: - case PLS_POLLING: - return 0; - default: - return -1; + dprintf(5, "%s: ring %p [linked]\n", __func__, ring); } + + dst = ring->ring + nidx; + control = trb->control | (cs ? TRB_C : 0); + + dst->ptr_low = trb->ptr_low; + dst->ptr_high = trb->ptr_high; + dst->status = trb->status; + dst->control = control; + nidx++; + ring->nidx = nidx; + + dprintf(5, "%s: ring %p [nidx %d, len %d]\n", + __func__, ring, nidx, + trb->status & 0xffff); } -// Reset device on port -static int -xhci_hub_reset(struct usbhub_s *hub, u32 port) +static int xhci_cmd_submit(struct usb_xhci_s *xhci, + struct xhci_trb *cmd) { - ASSERT32FLAT(); - struct usb_xhci_s *xhci = container_of(hub->cntl, struct usb_xhci_s, usb); - u32 portsc = readl(&xhci->pr[port].portsc); int rc; - switch (xhci_get_field(portsc, XHCI_PORTSC_PLS)) { - case PLS_U0: - rc = speed_from_xhci[xhci_get_field(portsc, XHCI_PORTSC_SPEED)]; - break; - case PLS_POLLING: - xhci_print_port_state(3, __func__, port, portsc); - portsc |= XHCI_PORTSC_PR; - writel(&xhci->pr[port].portsc, portsc); - if (wait_bit(&xhci->pr[port].portsc, XHCI_PORTSC_PED, XHCI_PORTSC_PED, 100) != 0) - return -1; - portsc = readl(&xhci->pr[port].portsc); - rc = speed_from_xhci[xhci_get_field(portsc, XHCI_PORTSC_SPEED)]; - break; - default: - rc = -1; - break; - } - - xhci_print_port_state(1, "XHCI", port, portsc); + mutex_lock(&xhci->cmds->lock); + xhci_trb_queue(xhci->cmds, cmd); + xhci_doorbell(xhci, 0, 0); + rc = xhci_event_wait(xhci, xhci->cmds, 1000); + mutex_unlock(&xhci->cmds->lock); return rc; } -static void -xhci_hub_disconnect(struct usbhub_s *hub, u32 port) +static int xhci_cmd_enable_slot(struct usb_xhci_s *xhci) { - ASSERT32FLAT(); - // XXX - should turn the port power off. + struct xhci_trb cmd = { + .ptr_low = 0, + .ptr_high = 0, + .status = 0, + .control = (CR_ENABLE_SLOT << 10) + }; + dprintf(3, "%s:\n", __func__); + int cc = xhci_cmd_submit(xhci, &cmd); + if (cc != CC_SUCCESS) + return -1; + return (xhci->cmds->evt.control >> 24) & 0xff; } -static struct usbhub_op_s xhci_hub_ops = { - .detect = xhci_hub_detect, - .reset = xhci_hub_reset, - .disconnect = xhci_hub_disconnect, -}; +#if 0 +static int xhci_cmd_disable_slot(struct usb_xhci_s *xhci, u32 slotid) +{ + struct xhci_trb cmd = { + .ptr_low = 0, + .ptr_high = 0, + .status = 0, + .control = (slotid << 24) | (CR_DISABLE_SLOT << 10) + }; + dprintf(3, "%s: slotid %d\n", __func__, slotid); + return xhci_cmd_submit(xhci, &cmd); +} +#endif -// -------------------------------------------------------------- -// external interface +static int xhci_cmd_address_device(struct usb_xhci_s *xhci, u32 slotid + , struct xhci_inctx *inctx) +{ + struct xhci_trb cmd = { + .ptr_low = (u32)inctx, + .ptr_high = 0, + .status = 0, + .control = (slotid << 24) | (CR_ADDRESS_DEVICE << 10) + }; + dprintf(3, "%s: slotid %d\n", __func__, slotid); + return xhci_cmd_submit(xhci, &cmd); +} + +static int xhci_cmd_configure_endpoint(struct usb_xhci_s *xhci, u32 slotid + , struct xhci_inctx *inctx) +{ + struct xhci_trb cmd = { + .ptr_low = (u32)inctx, + .ptr_high = 0, + .status = 0, + .control = (slotid << 24) | (CR_CONFIGURE_ENDPOINT << 10) + }; + dprintf(3, "%s: slotid %d, add 0x%x, del 0x%x\n", __func__, + slotid, inctx->add, inctx->del); + return xhci_cmd_submit(xhci, &cmd); +} +static int xhci_cmd_evaluate_context(struct usb_xhci_s *xhci, u32 slotid + , struct xhci_inctx *inctx) +{ + struct xhci_trb cmd = { + .ptr_low = (u32)inctx, + .ptr_high = 0, + .status = 0, + .control = (slotid << 24) | (CR_EVALUATE_CONTEXT << 10) + }; + dprintf(3, "%s: slotid %d, add 0x%x, del 0x%x\n", __func__, + slotid, inctx->add, inctx->del); + return xhci_cmd_submit(xhci, &cmd); +} static struct xhci_inctx * xhci_alloc_inctx(struct usbdevice_s *usbdev, int maxepid) @@ -882,13 +891,10 @@ static int xhci_config_hub(struct usbhub_s *hub) return 0; } -struct usb_pipe * +static struct usb_pipe * xhci_alloc_pipe(struct usbdevice_s *usbdev , struct usb_endpoint_descriptor *epdesc) { - ASSERT32FLAT(); - if (!CONFIG_USB_XHCI) - return NULL; u8 eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; struct usb_xhci_s *xhci = container_of( usbdev->hub->cntl, struct usb_xhci_s, usb); @@ -916,7 +922,7 @@ xhci_alloc_pipe(struct usbdevice_s *usbdev pipe->epid = epid; pipe->reqs.cs = 1; if (eptype == USB_ENDPOINT_XFER_INT) - pipe->buf = malloc_low(pipe->pipe.maxpacket); + pipe->buf = malloc_high(pipe->pipe.maxpacket); // Allocate input context and initialize endpoint info. struct xhci_inctx *in = xhci_alloc_inctx(usbdev, epid); @@ -925,7 +931,7 @@ xhci_alloc_pipe(struct usbdevice_s *usbdev in->add = 0x01 | (1 << epid); struct xhci_epctx *ep = (void*)&in[(pipe->epid+1) << xhci->context64]; if (eptype == USB_ENDPOINT_XFER_INT) - ep->ctx[0] = (usb_getFrameExp(usbdev, epdesc) + 3) << 16; + ep->ctx[0] = (usb_get_period(usbdev, epdesc) + 3) << 16; ep->ctx[1] |= eptype << 3; if (epdesc->bEndpointAddress & USB_DIR_IN || eptype == USB_ENDPOINT_XFER_CONTROL) @@ -988,85 +994,115 @@ fail: } struct usb_pipe * -xhci_update_pipe(struct usbdevice_s *usbdev, struct usb_pipe *upipe - , struct usb_endpoint_descriptor *epdesc) +xhci_realloc_pipe(struct usbdevice_s *usbdev, struct usb_pipe *upipe + , struct usb_endpoint_descriptor *epdesc) { - ASSERT32FLAT(); if (!CONFIG_USB_XHCI) return NULL; + if (!epdesc) { + usb_add_freelist(upipe); + return NULL; + } + if (!upipe) + return xhci_alloc_pipe(usbdev, epdesc); u8 eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + int oldmaxpacket = upipe->maxpacket; + usb_desc2pipe(upipe, usbdev, epdesc); struct xhci_pipe *pipe = container_of(upipe, struct xhci_pipe, pipe); struct usb_xhci_s *xhci = container_of( pipe->pipe.cntl, struct usb_xhci_s, usb); dprintf(3, "%s: usbdev %p, ring %p, slotid %d, epid %d\n", __func__, usbdev, &pipe->reqs, pipe->slotid, pipe->epid); - if (eptype == USB_ENDPOINT_XFER_CONTROL && - pipe->pipe.maxpacket != epdesc->wMaxPacketSize) { - dprintf(1, "%s: reconf ctl endpoint pkt size: %d -> %d\n", - __func__, pipe->pipe.maxpacket, epdesc->wMaxPacketSize); - pipe->pipe.maxpacket = epdesc->wMaxPacketSize; - struct xhci_inctx *in = xhci_alloc_inctx(usbdev, 1); - if (!in) - return upipe; - in->add = (1 << 1); - struct xhci_epctx *ep = (void*)&in[2 << xhci->context64]; - ep->ctx[1] |= (pipe->pipe.maxpacket << 16); - int cc = xhci_cmd_evaluate_context(xhci, pipe->slotid, in); - if (cc != CC_SUCCESS) { - dprintf(1, "%s: reconf ctl endpoint: failed (cc %d)\n", - __func__, cc); - } - free(in); + if (eptype != USB_ENDPOINT_XFER_CONTROL || upipe->maxpacket == oldmaxpacket) + return upipe; + + // maxpacket has changed on control endpoint - update controller. + dprintf(1, "%s: reconf ctl endpoint pkt size: %d -> %d\n", + __func__, oldmaxpacket, pipe->pipe.maxpacket); + struct xhci_inctx *in = xhci_alloc_inctx(usbdev, 1); + if (!in) + return upipe; + in->add = (1 << 1); + struct xhci_epctx *ep = (void*)&in[2 << xhci->context64]; + ep->ctx[1] |= (pipe->pipe.maxpacket << 16); + int cc = xhci_cmd_evaluate_context(xhci, pipe->slotid, in); + if (cc != CC_SUCCESS) { + dprintf(1, "%s: reconf ctl endpoint: failed (cc %d)\n", + __func__, cc); } + free(in); + return upipe; } -int -xhci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize - , void *data, int datalen) +static void xhci_xfer_queue(struct xhci_pipe *pipe, + void *data, int datalen, u32 flags) +{ + struct xhci_trb trb; + memset(&trb, 0, sizeof(trb)); + if (flags & TRB_TR_IDT) + memcpy(&trb.ptr_low, data, datalen); + else + trb.ptr_low = (u32)data; + trb.status = datalen; + trb.control = flags; + xhci_trb_queue(&pipe->reqs, &trb); +} + +static void xhci_xfer_kick(struct xhci_pipe *pipe) { - ASSERT32FLAT(); - if (!CONFIG_USB_XHCI) - return -1; - const struct usb_ctrlrequest *req = cmd; - struct xhci_pipe *pipe = container_of(p, struct xhci_pipe, pipe); struct usb_xhci_s *xhci = container_of( pipe->pipe.cntl, struct usb_xhci_s, usb); + u32 slotid = pipe->slotid; + u32 epid = pipe->epid; - if (req->bRequest == USB_REQ_SET_ADDRESS) - // Set address command sent during xhci_alloc_pipe. - return 0; - - xhci_xfer_setup(pipe, req, dir, datalen); - if (datalen) - xhci_xfer_data(pipe, dir, data, datalen); - xhci_xfer_status(pipe, dir, datalen); - - int cc = xhci_event_wait(xhci, &pipe->reqs, 1000); - if (cc != CC_SUCCESS) { - dprintf(1, "%s: control xfer failed (cc %d)\n", __func__, cc); - return -1; - } + dprintf(5, "%s: ring %p, slotid %d, epid %d\n", + __func__, &pipe->reqs, slotid, epid); + xhci_doorbell(xhci, slotid, epid); +} - return 0; +static void xhci_xfer_normal(struct xhci_pipe *pipe, + void *data, int datalen) +{ + xhci_xfer_queue(pipe, data, datalen, (TR_NORMAL << 10) | TRB_TR_IOC); + xhci_xfer_kick(pipe); } -int VISIBLE32FLAT -xhci_send_bulk(struct usb_pipe *p, int dir, void *data, int datalen) +int +xhci_send_pipe(struct usb_pipe *p, int dir, const void *cmd + , void *data, int datalen) { if (!CONFIG_USB_XHCI) return -1; - struct xhci_pipe *pipe = container_of(p, struct xhci_pipe, pipe); struct usb_xhci_s *xhci = container_of( - GET_LOWFLAT(pipe->pipe.cntl), struct usb_xhci_s, usb); + pipe->pipe.cntl, struct usb_xhci_s, usb); + + if (cmd) { + const struct usb_ctrlrequest *req = cmd; + if (req->bRequest == USB_REQ_SET_ADDRESS) + // Set address command sent during xhci_alloc_pipe. + return 0; + + xhci_xfer_queue(pipe, (void*)req, USB_CONTROL_SETUP_SIZE + , (TR_SETUP << 10) | TRB_TR_IDT + | ((datalen ? (dir ? 3 : 2) : 0) << 16)); + if (datalen) + xhci_xfer_queue(pipe, data, datalen, (TR_DATA << 10) + | ((dir ? 1 : 0) << 16)); + xhci_xfer_queue(pipe, NULL, 0, (TR_STATUS << 10) | TRB_TR_IOC + | ((dir ? 0 : 1) << 16)); + xhci_xfer_kick(pipe); + } else { + xhci_xfer_normal(pipe, data, datalen); + } - xhci_xfer_normal(pipe, data, datalen); - int cc = xhci_event_wait(xhci, &pipe->reqs, 1000); + int cc = xhci_event_wait(xhci, &pipe->reqs, usb_xfer_time(p, datalen)); if (cc != CC_SUCCESS) { - dprintf(1, "%s: bulk xfer failed (cc %d)\n", __func__, cc); + dprintf(1, "%s: xfer failed (cc %d)\n", __func__, cc); return -1; } + return 0; } @@ -1078,15 +1114,15 @@ xhci_poll_intr(struct usb_pipe *p, void *data) struct xhci_pipe *pipe = container_of(p, struct xhci_pipe, pipe); struct usb_xhci_s *xhci = container_of( - GET_LOWFLAT(pipe->pipe.cntl), struct usb_xhci_s, usb); - u32 len = GET_LOWFLAT(pipe->pipe.maxpacket); - void *buf = GET_LOWFLAT(pipe->buf); - int bufused = GET_LOWFLAT(pipe->bufused); + pipe->pipe.cntl, struct usb_xhci_s, usb); + u32 len = pipe->pipe.maxpacket; + void *buf = pipe->buf; + int bufused = pipe->bufused; if (!bufused) { xhci_xfer_normal(pipe, buf, len); bufused = 1; - SET_LOWFLAT(pipe->bufused, bufused); + pipe->bufused = bufused; return -1; } @@ -1094,103 +1130,10 @@ xhci_poll_intr(struct usb_pipe *p, void *data) if (xhci_ring_busy(&pipe->reqs)) return -1; dprintf(5, "%s: st %x ct %x [ %p <= %p / %d ]\n", __func__, - GET_LOWFLAT(pipe->reqs.evt.status), - GET_LOWFLAT(pipe->reqs.evt.control), - MAKE_FLATPTR(GET_SEG(SS), data), buf, len); - memcpy_fl(MAKE_FLATPTR(GET_SEG(SS), data), buf, len); + pipe->reqs.evt.status, + pipe->reqs.evt.control, + data, buf, len); + memcpy(data, buf, len); xhci_xfer_normal(pipe, buf, len); return 0; } - -static void -xhci_controller_setup(struct pci_device *pci) -{ - struct usb_xhci_s *xhci = malloc_low(sizeof(*xhci)); - if (!xhci) { - warn_noalloc(); - return; - } - memset(xhci, 0, sizeof(*xhci)); - - wait_preempt(); // Avoid pci_config_readl when preempting - xhci->baseaddr = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0) - & PCI_BASE_ADDRESS_MEM_MASK; - xhci->caps = (void*)(xhci->baseaddr); - xhci->op = (void*)(xhci->baseaddr + readb(&xhci->caps->caplength)); - xhci->pr = (void*)(xhci->baseaddr + readb(&xhci->caps->caplength) + 0x400); - xhci->db = (void*)(xhci->baseaddr + readl(&xhci->caps->dboff)); - xhci->ir = (void*)(xhci->baseaddr + readl(&xhci->caps->rtsoff) + 0x20); - - u32 hcs1 = readl(&xhci->caps->hcsparams1); - u32 hcc = readl(&xhci->caps->hccparams); - xhci->ports = (hcs1 >> 24) & 0xff; - xhci->slots = hcs1 & 0xff; - xhci->xcap = ((hcc >> 16) & 0xffff) << 2; - xhci->context64 = (hcc & 0x04) ? 1 : 0; - - xhci->usb.pci = pci; - xhci->usb.type = USB_TYPE_XHCI; - xhci->hub.cntl = &xhci->usb; - xhci->hub.portcount = xhci->ports; - xhci->hub.op = &xhci_hub_ops; - - dprintf(1, "XHCI init on dev %02x:%02x.%x: regs @ %p, %d ports, %d slots" - ", %d byte contexts\n" - , pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf) - , pci_bdf_to_fn(pci->bdf), xhci->caps - , xhci->ports, xhci->slots, xhci->context64 ? 64 : 32); - - if (xhci->xcap) { - u32 off, addr = xhci->baseaddr + xhci->xcap; - do { - struct xhci_xcap *xcap = (void*)addr; - u32 ports, name, cap = readl(&xcap->cap); - switch (cap & 0xff) { - case 0x02: - name = readl(&xcap->data[0]); - ports = readl(&xcap->data[1]); - dprintf(1, "XHCI protocol %c%c%c%c %x.%02x" - ", %d ports (offset %d), def %x\n" - , (name >> 0) & 0xff - , (name >> 8) & 0xff - , (name >> 16) & 0xff - , (name >> 24) & 0xff - , (cap >> 24) & 0xff - , (cap >> 16) & 0xff - , (ports >> 8) & 0xff - , (ports >> 0) & 0xff - , ports >> 16); - break; - default: - dprintf(1, "XHCI extcap 0x%x @ %x\n", cap & 0xff, addr); - break; - } - off = (cap >> 8) & 0xff; - addr += off << 2; - } while (off > 0); - } - - u32 pagesize = readl(&xhci->op->pagesize); - if (PAGE_SIZE != (pagesize<<12)) { - dprintf(1, "XHCI driver does not support page size code %d\n" - , pagesize<<12); - free(xhci); - return; - } - - pci_config_maskw(pci->bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER); - - run_thread(configure_xhci, xhci); -} - -void -xhci_setup(void) -{ - if (! CONFIG_USB_XHCI) - return; - struct pci_device *pci; - foreachpci(pci) { - if (pci_classprog(pci) == PCI_CLASS_SERIAL_USB_XHCI) - xhci_controller_setup(pci); - } -} diff --git a/roms/seabios/src/hw/usb-xhci.h b/roms/seabios/src/hw/usb-xhci.h index bd98d17..c768c5b 100644 --- a/roms/seabios/src/hw/usb-xhci.h +++ b/roms/seabios/src/hw/usb-xhci.h @@ -9,14 +9,11 @@ struct usb_pipe; // usb-xhci.c void xhci_setup(void); -struct usb_pipe *xhci_alloc_pipe(struct usbdevice_s *usbdev - , struct usb_endpoint_descriptor *epdesc); -struct usb_pipe *xhci_update_pipe(struct usbdevice_s *usbdev - , struct usb_pipe *pipe - , struct usb_endpoint_descriptor *epdesc); -int xhci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize - , void *data, int datasize); -int xhci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize); +struct usb_pipe *xhci_realloc_pipe(struct usbdevice_s *usbdev + , struct usb_pipe *upipe + , struct usb_endpoint_descriptor *epdesc); +int xhci_send_pipe(struct usb_pipe *p, int dir, const void *cmd + , void *data, int datasize); int xhci_poll_intr(struct usb_pipe *p, void *data); // -------------------------------------------------------------- diff --git a/roms/seabios/src/hw/usb.c b/roms/seabios/src/hw/usb.c index 7b8a9f5..1b4ea8b 100644 --- a/roms/seabios/src/hw/usb.c +++ b/roms/seabios/src/hw/usb.c @@ -26,72 +26,43 @@ * Controller function wrappers ****************************************************************/ -// Allocate an async pipe (control or bulk). -struct usb_pipe * -usb_alloc_pipe(struct usbdevice_s *usbdev - , struct usb_endpoint_descriptor *epdesc) +// Allocate, update, or free a usb pipe. +static struct usb_pipe * +usb_realloc_pipe(struct usbdevice_s *usbdev, struct usb_pipe *pipe + , struct usb_endpoint_descriptor *epdesc) { switch (usbdev->hub->cntl->type) { default: case USB_TYPE_UHCI: - return uhci_alloc_pipe(usbdev, epdesc); + return uhci_realloc_pipe(usbdev, pipe, epdesc); case USB_TYPE_OHCI: - return ohci_alloc_pipe(usbdev, epdesc); + return ohci_realloc_pipe(usbdev, pipe, epdesc); case USB_TYPE_EHCI: - return ehci_alloc_pipe(usbdev, epdesc); - case USB_TYPE_XHCI: - return xhci_alloc_pipe(usbdev, epdesc); - } -} - -// Update an pipe (used for control only) -struct usb_pipe * -usb_update_pipe(struct usbdevice_s *usbdev, struct usb_pipe *pipe - , struct usb_endpoint_descriptor *epdesc) -{ - switch (usbdev->hub->cntl->type) { + return ehci_realloc_pipe(usbdev, pipe, epdesc); case USB_TYPE_XHCI: - return xhci_update_pipe(usbdev, pipe, epdesc); - default: - free_pipe(pipe); - return usb_alloc_pipe(usbdev, epdesc); + return xhci_realloc_pipe(usbdev, pipe, epdesc); } } // Send a message on a control pipe using the default control descriptor. static int -send_control(struct usb_pipe *pipe, int dir, const void *cmd, int cmdsize - , void *data, int datasize) -{ - ASSERT32FLAT(); - switch (pipe->type) { - default: - case USB_TYPE_UHCI: - return uhci_control(pipe, dir, cmd, cmdsize, data, datasize); - case USB_TYPE_OHCI: - return ohci_control(pipe, dir, cmd, cmdsize, data, datasize); - case USB_TYPE_EHCI: - return ehci_control(pipe, dir, cmd, cmdsize, data, datasize); - case USB_TYPE_XHCI: - return xhci_control(pipe, dir, cmd, cmdsize, data, datasize); - } -} - -int -usb_send_bulk(struct usb_pipe *pipe_fl, int dir, void *data, int datasize) +usb_send_pipe(struct usb_pipe *pipe_fl, int dir, const void *cmd + , void *data, int datasize) { switch (GET_LOWFLAT(pipe_fl->type)) { default: case USB_TYPE_UHCI: - return uhci_send_bulk(pipe_fl, dir, data, datasize); + return uhci_send_pipe(pipe_fl, dir, cmd, data, datasize); case USB_TYPE_OHCI: - return ohci_send_bulk(pipe_fl, dir, data, datasize); + if (MODESEGMENT) + return -1; + return ohci_send_pipe(pipe_fl, dir, cmd, data, datasize); case USB_TYPE_EHCI: - return ehci_send_bulk(pipe_fl, dir, data, datasize); + return ehci_send_pipe(pipe_fl, dir, cmd, data, datasize); case USB_TYPE_XHCI: if (MODESEGMENT) return -1; - return xhci_send_bulk(pipe_fl, dir, data, datasize); + return xhci_send_pipe(pipe_fl, dir, cmd, data, datasize); } } @@ -116,30 +87,61 @@ usb_poll_intr(struct usb_pipe *pipe_fl, void *data) int usb_32bit_pipe(struct usb_pipe *pipe_fl) { - return CONFIG_USB_XHCI && GET_LOWFLAT(pipe_fl->type) == USB_TYPE_XHCI; + return (CONFIG_USB_XHCI && GET_LOWFLAT(pipe_fl->type) == USB_TYPE_XHCI) + || (CONFIG_USB_OHCI && GET_LOWFLAT(pipe_fl->type) == USB_TYPE_OHCI); } + /**************************************************************** * Helper functions ****************************************************************/ +// Allocate a usb pipe. +struct usb_pipe * +usb_alloc_pipe(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + return usb_realloc_pipe(usbdev, NULL, epdesc); +} + +// Free an allocated control or bulk pipe. +void +usb_free_pipe(struct usbdevice_s *usbdev, struct usb_pipe *pipe) +{ + if (!pipe) + return; + usb_realloc_pipe(usbdev, pipe, NULL); +} + // Send a message to the default control pipe of a device. int -send_default_control(struct usb_pipe *pipe, const struct usb_ctrlrequest *req - , void *data) +usb_send_default_control(struct usb_pipe *pipe, const struct usb_ctrlrequest *req + , void *data) { - return send_control(pipe, req->bRequestType & USB_DIR_IN - , req, sizeof(*req), data, req->wLength); + return usb_send_pipe(pipe, req->bRequestType & USB_DIR_IN, req + , data, req->wLength); } -// Free an allocated control or bulk pipe. +// Send a message to a bulk endpoint +int +usb_send_bulk(struct usb_pipe *pipe_fl, int dir, void *data, int datasize) +{ + return usb_send_pipe(pipe_fl, dir, NULL, data, datasize); +} + +// Check if a pipe for a given controller is on the freelist +int +usb_is_freelist(struct usb_s *cntl, struct usb_pipe *pipe) +{ + return pipe->cntl != cntl; +} + +// Add a pipe to the controller's freelist void -free_pipe(struct usb_pipe *pipe) +usb_add_freelist(struct usb_pipe *pipe) { - ASSERT32FLAT(); if (!pipe) return; - // Add to controller's free list. struct usb_s *cntl = pipe->cntl; pipe->freenext = cntl->freelist; cntl->freelist = pipe; @@ -147,7 +149,7 @@ free_pipe(struct usb_pipe *pipe) // Check for an available pipe on the freelist. struct usb_pipe * -usb_getFreePipe(struct usb_s *cntl, u8 eptype) +usb_get_freelist(struct usb_s *cntl, u8 eptype) { struct usb_pipe **pfree = &cntl->freelist; for (;;) { @@ -178,8 +180,8 @@ usb_desc2pipe(struct usb_pipe *pipe, struct usbdevice_s *usbdev // Find the exponential period of the requested interrupt end point. int -usb_getFrameExp(struct usbdevice_s *usbdev - , struct usb_endpoint_descriptor *epdesc) +usb_get_period(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) { int period = epdesc->bInterval; if (usbdev->speed != USB_HIGHSPEED) @@ -187,9 +189,22 @@ usb_getFrameExp(struct usbdevice_s *usbdev return (period <= 4) ? 0 : period - 4; } -// Find the first endpoing of a given type in an interface description. +// Maximum time (in ms) a data transfer should take +int +usb_xfer_time(struct usb_pipe *pipe, int datalen) +{ + // Use the maximum command time (5 seconds), except for + // set_address commands where we don't want to stall the boot if + // the device doesn't actually exist. Add 100ms to account for + // any controller delays. + if (!GET_LOWFLAT(pipe->devaddr)) + return USB_TIME_STATUS + 100; + return USB_TIME_COMMAND + 100; +} + +// Find the first endpoint of a given type in an interface description. struct usb_endpoint_descriptor * -findEndPointDesc(struct usbdevice_s *usbdev, int type, int dir) +usb_find_desc(struct usbdevice_s *usbdev, int type, int dir) { struct usb_endpoint_descriptor *epdesc = (void*)&usbdev->iface[1]; for (;;) { @@ -215,7 +230,7 @@ get_device_info8(struct usb_pipe *pipe, struct usb_device_descriptor *dinfo) req.wValue = USB_DT_DEVICE<<8; req.wIndex = 0; req.wLength = 8; - return send_default_control(pipe, &req, dinfo); + return usb_send_default_control(pipe, &req, dinfo); } static struct usb_config_descriptor * @@ -229,7 +244,7 @@ get_device_config(struct usb_pipe *pipe) req.wValue = USB_DT_CONFIG<<8; req.wIndex = 0; req.wLength = sizeof(cfg); - int ret = send_default_control(pipe, &req, &cfg); + int ret = usb_send_default_control(pipe, &req, &cfg); if (ret) return NULL; @@ -237,9 +252,11 @@ get_device_config(struct usb_pipe *pipe) if (!config) return NULL; req.wLength = cfg.wTotalLength; - ret = send_default_control(pipe, &req, config); - if (ret) + ret = usb_send_default_control(pipe, &req, config); + if (ret) { + free(config); return NULL; + } //hexdump(config, cfg.wTotalLength); return config; } @@ -253,7 +270,7 @@ set_configuration(struct usb_pipe *pipe, u16 val) req.wValue = val; req.wIndex = 0; req.wLength = 0; - return send_default_control(pipe, &req, NULL); + return usb_send_default_control(pipe, &req, NULL); } @@ -297,9 +314,9 @@ usb_set_address(struct usbdevice_s *usbdev) req.wValue = cntl->maxaddr + 1; req.wIndex = 0; req.wLength = 0; - int ret = send_default_control(usbdev->defpipe, &req, NULL); + int ret = usb_send_default_control(usbdev->defpipe, &req, NULL); if (ret) { - free_pipe(usbdev->defpipe); + usb_free_pipe(usbdev, usbdev->defpipe); return -1; } @@ -307,7 +324,7 @@ usb_set_address(struct usbdevice_s *usbdev) cntl->maxaddr++; usbdev->devaddr = cntl->maxaddr; - usbdev->defpipe = usb_update_pipe(usbdev, usbdev->defpipe, &epdesc); + usbdev->defpipe = usb_realloc_pipe(usbdev, usbdev->defpipe, &epdesc); if (!usbdev->defpipe) return -1; return 0; @@ -338,7 +355,7 @@ configure_usb_device(struct usbdevice_s *usbdev) .wMaxPacketSize = maxpacket, .bmAttributes = USB_ENDPOINT_XFER_CONTROL, }; - usbdev->defpipe = usb_update_pipe(usbdev, usbdev->defpipe, &epdesc); + usbdev->defpipe = usb_realloc_pipe(usbdev, usbdev->defpipe, &epdesc); if (!usbdev->defpipe) return -1; @@ -391,15 +408,23 @@ usb_hub_port_setup(void *data) struct usbhub_s *hub = usbdev->hub; u32 port = usbdev->port; - // Detect if device present (and possibly start reset) - int ret = hub->op->detect(hub, port); - if (ret) - // No device present - goto done; + for (;;) { + // Detect if device present (and possibly start reset) + int ret = hub->op->detect(hub, port); + if (ret > 0) + // Device connected. + break; + if (ret < 0 || timer_check(hub->detectend)) + // No device found. + goto done; + msleep(5); + } + + // XXX - wait USB_TIME_ATTDB time? // Reset port and determine device speed mutex_lock(&hub->cntl->resetlock); - ret = hub->op->reset(hub, port); + int ret = hub->op->reset(hub, port); if (ret < 0) // Reset failed goto resetfail; @@ -415,7 +440,7 @@ usb_hub_port_setup(void *data) // Configure the device int count = configure_usb_device(usbdev); - free_pipe(usbdev->defpipe); + usb_free_pipe(usbdev, usbdev->defpipe); if (!count) hub->op->disconnect(hub, port); hub->devcount += count; @@ -434,6 +459,7 @@ usb_enumerate(struct usbhub_s *hub) { u32 portcount = hub->portcount; hub->threads = portcount; + hub->detectend = timer_calc(USB_TIME_SIGATT); // Launch a thread for every port. int i; diff --git a/roms/seabios/src/hw/usb.h b/roms/seabios/src/hw/usb.h index 6196296..efb5e6f 100644 --- a/roms/seabios/src/hw/usb.h +++ b/roms/seabios/src/hw/usb.h @@ -46,7 +46,7 @@ struct usbhub_s { struct usbdevice_s *usbdev; struct usb_s *cntl; struct mutex_s lock; - u32 powerwait; + u32 detectend; u32 port; u32 threads; u32 portcount; @@ -84,6 +84,10 @@ struct usbhub_op_s { #define USB_TIME_DRSTR 50 #define USB_TIME_RSTRCY 10 +#define USB_TIME_STATUS 50 +#define USB_TIME_DATAIN 500 +#define USB_TIME_COMMAND 5000 + #define USB_TIME_SETADDR_RECOVERY 2 #define USB_PID_OUT 0xe1 @@ -207,6 +211,8 @@ struct usb_endpoint_descriptor { #define USB_ENDPOINT_XFER_INT 3 #define USB_ENDPOINT_MAX_ADJUSTABLE 0x80 +#define USB_CONTROL_SETUP_SIZE 8 + /**************************************************************** * usb mass storage flags @@ -224,21 +230,24 @@ struct usb_endpoint_descriptor { ****************************************************************/ // usb.c -struct usb_pipe *usb_alloc_pipe(struct usbdevice_s *usbdev - , struct usb_endpoint_descriptor *epdesc); int usb_send_bulk(struct usb_pipe *pipe, int dir, void *data, int datasize); int usb_poll_intr(struct usb_pipe *pipe, void *data); int usb_32bit_pipe(struct usb_pipe *pipe_fl); -int send_default_control(struct usb_pipe *pipe, const struct usb_ctrlrequest *req - , void *data); -void free_pipe(struct usb_pipe *pipe); -struct usb_pipe *usb_getFreePipe(struct usb_s *cntl, u8 eptype); +struct usb_pipe *usb_alloc_pipe(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc); +void usb_free_pipe(struct usbdevice_s *usbdev, struct usb_pipe *pipe); +int usb_send_default_control(struct usb_pipe *pipe + , const struct usb_ctrlrequest *req, void *data); +int usb_is_freelist(struct usb_s *cntl, struct usb_pipe *pipe); +void usb_add_freelist(struct usb_pipe *pipe); +struct usb_pipe *usb_get_freelist(struct usb_s *cntl, u8 eptype); void usb_desc2pipe(struct usb_pipe *pipe, struct usbdevice_s *usbdev , struct usb_endpoint_descriptor *epdesc); -int usb_getFrameExp(struct usbdevice_s *usbdev - , struct usb_endpoint_descriptor *epdesc); -struct usb_endpoint_descriptor *findEndPointDesc(struct usbdevice_s *usbdev - , int type, int dir); +int usb_get_period(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc); +int usb_xfer_time(struct usb_pipe *pipe, int datalen); +struct usb_endpoint_descriptor *usb_find_desc(struct usbdevice_s *usbdev + , int type, int dir); void usb_enumerate(struct usbhub_s *hub); void usb_setup(void); diff --git a/roms/seabios/src/kbd.c b/roms/seabios/src/kbd.c index 33a95a3..a5a1ad9 100644 --- a/roms/seabios/src/kbd.c +++ b/roms/seabios/src/kbd.c @@ -11,7 +11,7 @@ #include "hw/ps2port.h" // ps2_kbd_command #include "hw/usb-hid.h" // usb_kbd_command #include "output.h" // debug_enter -#include "stacks.h" // stack_hop +#include "stacks.h" // yield #include "string.h" // memset #include "util.h" // kbd_init @@ -117,8 +117,8 @@ static int kbd_command(int command, u8 *param) { if (usb_kbd_active()) - return stack_hop(command, (u32)param, usb_kbd_command); - return stack_hop(command, (u32)param, ps2_kbd_command); + return usb_kbd_command(command, param); + return ps2_kbd_command(command, param); } // read keyboard input @@ -502,7 +502,7 @@ __process_key(u8 scancode) == (KF0_CTRLACTIVE|KF0_ALTACTIVE))) { // Ctrl+alt+del - reset machine. SET_BDA(soft_reset_flag, 0x1234); - reset_vector(); + reset(); } if (scancode >= ARRAY_SIZE(scan_to_scanascii)) { dprintf(1, "KBD: int09h_handler(): unknown scancode read: 0x%02x!\n" diff --git a/roms/seabios/src/misc.c b/roms/seabios/src/misc.c index 6712355..8caaf31 100644 --- a/roms/seabios/src/misc.c +++ b/roms/seabios/src/misc.c @@ -117,7 +117,7 @@ handle_75(void) // INT 16/AH=09h (keyboard functionality) supported #define CBT_F2_INT1609 (1<<6) -struct bios_config_table_s BIOS_CONFIG_TABLE VAR16FIXED(0xe6f5) = { +struct bios_config_table_s BIOS_CONFIG_TABLE VARFSEGFIXED(0xe6f5) = { .size = sizeof(BIOS_CONFIG_TABLE) - 2, .model = BUILD_MODEL_ID, .submodel = BUILD_SUBMODEL_ID, @@ -181,18 +181,23 @@ struct descloc_s rombios32_gdt_48 VARFSEG = { * Misc fixed vars ****************************************************************/ -char BiosCopyright[] VAR16FIXED(0xff00) = - "(c) 2002 MandrakeSoft S.A. Written by Kevin Lawton & the Bochs team."; - // BIOS build date -char BiosDate[] VAR16FIXED(0xfff5) = "06/23/99"; +char BiosDate[] VARFSEGFIXED(0xfff5) = "06/23/99"; + +u8 BiosModelId VARFSEGFIXED(0xfffe) = BUILD_MODEL_ID; + +u8 BiosChecksum VARFSEGFIXED(0xffff); + +struct floppy_dbt_s diskette_param_table VARFSEGFIXED(0xefc7); -u8 BiosModelId VAR16FIXED(0xfffe) = BUILD_MODEL_ID; +// Old Fixed Disk Parameter Table (newer tables are in the ebda). +struct fdpt_s OldFDPT VARFSEGFIXED(0xe401); -u8 BiosChecksum VAR16FIXED(0xffff); +// XXX - Baud Rate Generator Table +u8 BaudTable[16] VARFSEGFIXED(0xe729); // XXX - Initial Interrupt Vector Offsets Loaded by POST -u8 InitVectors[13] VAR16FIXED(0xfef3); +u8 InitVectors[13] VARFSEGFIXED(0xfef3); // XXX - INT 1D - SYSTEM DATA - VIDEO PARAMETER TABLES -u8 VideoParams[88] VAR16FIXED(0xf0a4); +u8 VideoParams[88] VARFSEGFIXED(0xf0a4); diff --git a/roms/seabios/src/mouse.c b/roms/seabios/src/mouse.c index 92ae921..6d1f5b7 100644 --- a/roms/seabios/src/mouse.c +++ b/roms/seabios/src/mouse.c @@ -10,7 +10,7 @@ #include "hw/ps2port.h" // ps2_mouse_command #include "hw/usb-hid.h" // usb_mouse_command #include "output.h" // dprintf -#include "stacks.h" // stack_hop +#include "stacks.h" // stack_hop_back #include "util.h" // mouse_init void @@ -27,8 +27,8 @@ static int mouse_command(int command, u8 *param) { if (usb_mouse_active()) - return stack_hop(command, (u32)param, usb_mouse_command); - return stack_hop(command, (u32)param, ps2_mouse_command); + return usb_mouse_command(command, param); + return ps2_mouse_command(command, param); } #define RET_SUCCESS 0x00 @@ -274,9 +274,18 @@ handle_15c2(struct bregs *regs) } } -static void -invoke_mouse_handler(u16 ebda_seg) +void VISIBLE16 +invoke_mouse_handler(void) { + if (!CONFIG_MOUSE) + return; + if (need_hop_back()) { + extern void _cfunc16_invoke_mouse_handler(void); + stack_hop_back(0, 0, _cfunc16_invoke_mouse_handler); + return; + } + ASSERT16(); + u16 ebda_seg = get_ebda_seg(); u16 status = GET_EBDA(ebda_seg, mouse_data[0]); u16 X = GET_EBDA(ebda_seg, mouse_data[1]); u16 Y = GET_EBDA(ebda_seg, mouse_data[2]); @@ -330,5 +339,5 @@ process_mouse(u8 data) } SET_EBDA(ebda_seg, mouse_flag1, 0); - stack_hop_back(ebda_seg, 0, invoke_mouse_handler); + invoke_mouse_handler(); } diff --git a/roms/seabios/src/post.c b/roms/seabios/src/post.c index 0fdd28e..9ea5620 100644 --- a/roms/seabios/src/post.c +++ b/roms/seabios/src/post.c @@ -143,6 +143,7 @@ device_hardware_setup(void) floppy_setup(); ata_setup(); ahci_setup(); + sdcard_setup(); cbfs_payload_setup(); ramdisk_setup(); virtio_blk_setup(); diff --git a/roms/seabios/src/romlayout.S b/roms/seabios/src/romlayout.S index 57e8bcc..93b6874 100644 --- a/roms/seabios/src/romlayout.S +++ b/roms/seabios/src/romlayout.S @@ -8,10 +8,11 @@ #include "asm-offsets.h" // BREGS_* #include "config.h" // CONFIG_* #include "entryfuncs.S" // ENTRY_* -#include "hw/ps2port.h" // PORT_A20 #include "hw/rtc.h" // CMOS_RESET_CODE #include "x86.h" // CR0_* + .code16 + /**************************************************************** * 16bit / 32bit call trampolines @@ -21,7 +22,10 @@ // %edx = return location (in 32bit mode) // Clobbers: ecx, flags, segment registers, cr0, idt/gdt DECLFUNC transition32 - .code16gcc +transition32_nmi_off: + // transition32 when NMI and A20 are already initialized + movl %eax, %ecx + jmp 1f transition32: movl %eax, %ecx @@ -40,7 +44,7 @@ transition32: outb %al, $PORT_A20 // Set segment descriptors - lidtw %cs:pmode_IDT_info +1: lidtw %cs:pmode_IDT_info lgdtw %cs:rombios32_gdt_48 // Enable protected mode @@ -49,12 +53,11 @@ transition32: movl %eax, %cr0 // start 32bit protected mode code - ljmpl $SEG32_MODE32_CS, $(BUILD_BIOS_ADDR + 1f) + ljmpl $SEG32_MODE32_CS, $(BUILD_BIOS_ADDR + 2f) .code32 -1: // init data segments - movl $SEG32_MODE32_DS, %eax +2: movl $SEG32_MODE32_DS, %eax movw %ax, %ds movw %ax, %es movw %ax, %ss @@ -63,12 +66,14 @@ transition32: movl %ecx, %eax jmpl *%edx + .code16 // Place CPU into 16bit mode from 32bit mode. // %edx = return location (in 16bit mode) // Clobbers: ecx, flags, segment registers, cr0, idt/gdt DECLFUNC transition16 .global transition16big + .code32 transition16: movl %eax, %ecx @@ -102,7 +107,7 @@ transition16big: ljmpw $SEG32_MODE16BIG_CS, $1f - .code16gcc + .code16 1: // Disable protected mode movl %cr0, %eax @@ -127,48 +132,6 @@ transition16big: movl %ecx, %eax jmpl *%edx -// Call a 16bit SeaBIOS function from SeaBIOS 32bit C code. -// %ecx = calling function -// Clobbers: %ecx, %edx, flags, segment registers, idt/gdt - DECLFUNC __call16 - .global __call16big - .code32 -__call16: - pushl %edx - pushl %ecx - movl $1f, %edx - jmp transition16 -__call16big: - pushl %edx - pushl %ecx - movl $1f, %edx - jmp transition16big - - // Make call. - .code16gcc -1: movl $_zonelow_seg, %edx // Adjust %ds, %ss, and %esp - movl %edx, %ds - movzwl StackSeg, %edx - movl %edx, %ecx - shll $4, %ecx - movl %edx, %ss - subl %ecx, %esp - movl %edx, %ds - - popl %ecx // Call function - popl %edx - calll *%ecx - - movl %ss, %edx // Readjust %esp - shll $4, %edx - addl %edx, %esp - - // Return via transition32 - movl $(2f + BUILD_BIOS_ADDR), %edx - jmp transition32 - .code32 -2: retl - /**************************************************************** * External calling trampolines @@ -177,7 +140,6 @@ __call16big: // Far call a 16bit function from 16bit mode with a specified cpu register state // %eax = address of struct bregs, %edx = segment of struct bregs // Clobbers: %e[bc]x, %e[ds]i, flags - .code16gcc DECLFUNC __farcall16 __farcall16: // Save %edx/%eax, %ebp @@ -192,19 +154,8 @@ __farcall16: pushw BREGS_flags(%eax) // flags pushl BREGS_code(%eax) // CS:IP - // Load calling registers. - movl BREGS_edi(%eax), %edi - movl BREGS_esi(%eax), %esi - movl BREGS_ebp(%eax), %ebp - movl BREGS_ebx(%eax), %ebx - movl BREGS_edx(%eax), %edx - movl BREGS_ecx(%eax), %ecx - movw BREGS_es(%eax), %es - pushl BREGS_eax(%eax) - movw BREGS_ds(%eax), %ds - popl %eax - - // Invoke call + // Load calling registers and invoke call + RESTOREBREGS_DSEAX iretw // XXX - just do a lcalll 1: // Store flags, es, eax @@ -215,18 +166,8 @@ __farcall16: pushl %eax movw 0x08(%esp), %ds movl 0x0c(%esp), %eax - popl BREGS_eax(%eax) - popw BREGS_es(%eax) + SAVEBREGS_POP_DSEAX popw BREGS_flags(%eax) - - // Store remaining registers - movl %edi, BREGS_edi(%eax) - movl %esi, BREGS_esi(%eax) - movl %ebp, BREGS_ebp(%eax) - movl %ebx, BREGS_ebx(%eax) - movl %edx, BREGS_edx(%eax) - movl %ecx, BREGS_ecx(%eax) - movw %es, BREGS_ds(%eax) movw %ss, %cx movw %cx, %ds // Restore %ds == %ss @@ -260,6 +201,40 @@ __farcall16: * Misc. entry points. ****************************************************************/ +// Entry point for QEMU smi interrupts. + DECLFUNC entry_smi +entry_smi: + // Transition to 32bit mode. + movl $1f + BUILD_BIOS_ADDR, %edx + jmp transition32_nmi_off + .code32 +1: movl $BUILD_SMM_ADDR + 0x8000, %esp + calll _cfunc32flat_handle_smi - BUILD_BIOS_ADDR + rsm + .code16 + +// Entry point for QEMU smp sipi interrupts. + DECLFUNC entry_smp +entry_smp: + // Transition to 32bit mode. + cli + cld + movl $2f + BUILD_BIOS_ADDR, %edx + jmp transition32_nmi_off + .code32 + // Acquire lock and take ownership of shared stack +1: rep ; nop +2: lock btsl $0, SMPLock + jc 1b + movl SMPStack, %esp + // Call handle_smp + calll _cfunc32flat_handle_smp - BUILD_BIOS_ADDR + // Release lock and halt processor. + movl $0, SMPLock +3: hlt + jmp 3b + .code16 + // Resume (and reboot) entry point - called from entry_post DECLFUNC entry_resume entry_resume: @@ -282,25 +257,18 @@ entry_pmm: pushfl // Save registers clobbered by C code cli cld - pushl %eax - pushl %ecx - pushl %edx - pushw %es - pushw %ds - movw %ss, %cx // Move %ss to %ds + PUSHBREGS + movl %ss, %ecx // Move %ss to %ds movw %cx, %ds + shll $4, %ecx movl $_cfunc32flat_handle_pmm, %eax // Setup: call32(handle_pmm, args, -1) - leal 28(%esp), %edx // %edx points to start of args + leal PUSHBREGS_size+12(%esp, %ecx), %edx // %edx points to start of args movl $-1, %ecx calll call32 - movw %ax, 12(%esp) // Modify %ax:%dx to return %eax + movw %ax, BREGS_eax(%esp) // Modify %ax:%dx to return %eax shrl $16, %eax - movw %ax, 4(%esp) - popw %ds // Restore saved registers - popw %es - popl %edx - popl %ecx - popl %eax + movw %ax, BREGS_edx(%esp) + POPBREGS popfl popl %esp lretw @@ -318,21 +286,13 @@ entry_pnp_real: pushfl // Save registers clobbered by C code cli cld - pushl %eax - pushl %ecx - pushl %edx - pushw %es - pushw %ds + PUSHBREGS movw %ss, %cx // Move %ss to %ds movw %cx, %ds - leal 28(%esp), %eax // %eax points to start of u16 args + leal PUSHBREGS_size+12(%esp), %eax // %eax points to start of u16 args calll handle_pnp - movw %ax, 12(%esp) // Modify %eax to return %ax - popw %ds - popw %es - popl %edx - popl %ecx - popl %eax + movw %ax, BREGS_eax(%esp) // Modify %eax to return %ax + POPBREGS popfl popl %esp lretw @@ -347,8 +307,8 @@ entry_apm16: popfw // restore flags lretw - .code32 DECLFUNC entry_apm32 + .code32 entry_apm32: pushfl pushl %gs @@ -359,9 +319,11 @@ entry_apm32: popl %gs popfl lretl + .code16 // PCI-BIOS entry points DECLFUNC entry_pcibios32 + .code32 entry_pcibios32: pushfl pushl %gs // Backup %gs and set %gs=%ds @@ -371,8 +333,8 @@ entry_pcibios32: popl %gs popfl lretl + .code16 - .code16gcc DECLFUNC entry_pcibios16 entry_pcibios16: ENTRY_ARG handle_pcibios @@ -385,8 +347,8 @@ entry_1589: iretw // BIOS32 support - .code32 DECLFUNC entry_bios32 + .code32 entry_bios32: pushfl #if CONFIG_PCIBIOS @@ -404,9 +366,11 @@ entry_bios32: // Return to caller 2: popfl lretl + .code16 // 32bit elf entry point - EXPORTFUNC entry_elf + DECLFUNC entry_elf + .code32 entry_elf: cli cld @@ -420,11 +384,10 @@ entry_elf: movw %ax, %ss movl $BUILD_STACK_ADDR, %esp ljmpl $SEG32_MODE32_CS, $_cfunc32flat_handle_post - - .code16gcc + .code16 // UEFI Compatibility Support Module (CSM) entry point - EXPORTFUNC entry_csm + DECLFUNC entry_csm entry_csm: // Backup register state pushfw @@ -439,21 +402,15 @@ entry_csm: shll $4, %eax addl %esp, %eax - // Change to BUILD_STACK_ADDR stack - xorl %ebx, %ebx - movw %bx, %ss - movl $BUILD_STACK_ADDR, %esp - - // Jump to 32bit mode and call handle_csm(bregs) - movl $_cfunc32flat_handle_csm, %edx - jmp transition32 + // Change to BUILD_STACK_ADDR stack and call handle_csm(bregs) + ENTRY_INTO32 _cfunc32flat_handle_csm DECLFUNC __csm_return .code32 __csm_return: movl $1f, %edx jmp transition16big - .code16gcc + .code16 // Switch back to original stack 1: movzwl BREGS_code+2(%eax), %edx @@ -474,7 +431,7 @@ __csm_return: * Interrupt entry points ****************************************************************/ - // Main entry point for interrupts handled on extra stack + // Main entry point for hardware interrupts handled on extra stack DECLFUNC irqentry_extrastack irqentry_extrastack: cli @@ -484,15 +441,11 @@ irqentry_extrastack: movl $_zonelow_seg, %eax movl %eax, %ds movl StackPos, %eax - subl $24, %eax - popl 0(%eax) // Backup %eax, %ds, %es, %ecx, %edx - popw 4(%eax) - movw %es, 6(%eax) - movl %ecx, 8(%eax) + subl $PUSHBREGS_size+8, %eax + SAVEBREGS_POP_DSEAX popl %ecx - movl %edx, 12(%eax) - movl %esp, 16(%eax) - movw %ss, 20(%eax) + movl %esp, PUSHBREGS_size(%eax) + movw %ss, PUSHBREGS_size+4(%eax) movw %ds, %dx // Setup %ss/%esp and call function movw %dx, %ss @@ -500,18 +453,12 @@ irqentry_extrastack: calll *%ecx movl %esp, %eax // Restore registers and return - movw 20(%eax), %ss - movl 16(%eax), %esp - movl 12(%eax), %edx - movl 8(%eax), %ecx - movw 6(%eax), %es - pushw 4(%eax) - pushl 0(%eax) - popl %eax - popw %ds + movw PUSHBREGS_size+4(%eax), %ss + movl PUSHBREGS_size(%eax), %esp + RESTOREBREGS_DSEAX iretw - // Main entry point for interrupts handled on extra stack + // Main entry point for software interrupts handled on extra stack DECLFUNC irqentry_arg_extrastack irqentry_arg_extrastack: cli @@ -521,19 +468,11 @@ irqentry_arg_extrastack: movl $_zonelow_seg, %eax movl %eax, %ds movl StackPos, %eax - subl $BREGS_size+8, %eax - popl BREGS_eax(%eax) // Backup registers - popw BREGS_ds(%eax) - movl %edi, BREGS_edi(%eax) - movl %esi, BREGS_esi(%eax) - movl %ebp, BREGS_ebp(%eax) - movl %ebx, BREGS_ebx(%eax) - movl %edx, BREGS_edx(%eax) - movl %ecx, BREGS_ecx(%eax) + subl $PUSHBREGS_size+16, %eax + SAVEBREGS_POP_DSEAX // Save registers on extra stack popl %ecx - movw %es, BREGS_es(%eax) - movl %esp, BREGS_size+0(%eax) - movw %ss, BREGS_size+4(%eax) + movl %esp, PUSHBREGS_size+8(%eax) + movw %ss, PUSHBREGS_size+12(%eax) popl BREGS_code(%eax) popw BREGS_flags(%eax) @@ -543,32 +482,22 @@ irqentry_arg_extrastack: calll *%ecx movl %esp, %eax // Restore registers and return - movw BREGS_size+4(%eax), %ss - movl BREGS_size+0(%eax), %esp + movw PUSHBREGS_size+12(%eax), %ss + movl PUSHBREGS_size+8(%eax), %esp popl %edx popw %dx pushw BREGS_flags(%eax) pushl BREGS_code(%eax) - movl BREGS_edi(%eax), %edi - movl BREGS_esi(%eax), %esi - movl BREGS_ebp(%eax), %ebp - movl BREGS_ebx(%eax), %ebx - movl BREGS_edx(%eax), %edx - movl BREGS_ecx(%eax), %ecx - movw BREGS_es(%eax), %es - pushw BREGS_ds(%eax) - pushl BREGS_eax(%eax) - popl %eax - popw %ds + RESTOREBREGS_DSEAX iretw - // Main entry point for interrupts with args - DECLFUNC irqentryarg -irqentryarg: + // Main entry point for software interrupts (using caller's stack) + DECLFUNC irqentry_arg +irqentry_arg: ENTRY_ARG_ST iretw - // Define an entry point for hardware interrupts. + // Helper macros for hardware interrupt declaration .macro IRQ_ENTRY num .global entry_\num entry_\num : @@ -581,7 +510,7 @@ irqentryarg: IRQ_ENTRY \num .endm - // Define an entry point for an interrupt (can read/modify args). + // Helper macros for software interrupt declaration .macro IRQ_ENTRY_ARG num .global entry_\num entry_\num : @@ -589,7 +518,7 @@ irqentryarg: #if CONFIG_ENTRY_EXTRASTACK jmp irqentry_arg_extrastack #else - jmp irqentryarg + jmp irqentry_arg #endif .endm @@ -640,7 +569,7 @@ entry_post: entry_13_official: jmp entry_13 - // 0xe401 - OldFDPT in disk.c + // 0xe401 - OldFDPT in misc.c ORG 0xe6f2 .global entry_19_official @@ -649,7 +578,7 @@ entry_19_official: // 0xe6f5 - BIOS_CONFIG_TABLE in misc.c - // 0xe729 - BaudTable in serial.c + // 0xe729 - BaudTable in misc.c ORG 0xe739 IRQ_ENTRY_ARG 14 @@ -666,7 +595,7 @@ entry_19_official: ORG 0xef57 IRQ_ENTRY 0e - // 0xefc7 - diskette_param_table in floppy.c + // 0xefc7 - diskette_param_table in misc.c ORG 0xefd2 IRQ_ENTRY_ARG 17 @@ -708,8 +637,6 @@ entry_1a_official: // 0xfef3 - InitVectors in misc.c - // 0xff00 - BiosCopyright in misc.c - ORG 0xff53 .global entry_iret_official entry_iret_official: diff --git a/roms/seabios/src/serial.c b/roms/seabios/src/serial.c index 00c6eb7..88349c8 100644 --- a/roms/seabios/src/serial.c +++ b/roms/seabios/src/serial.c @@ -181,9 +181,6 @@ handle_14(struct bregs *regs) } } -// XXX - Baud Rate Generator Table -u8 BaudTable[16] VAR16FIXED(0xe729); - /**************************************************************** * LPT ports diff --git a/roms/seabios/src/stacks.c b/roms/seabios/src/stacks.c index beccc0f..1dbdfe9 100644 --- a/roms/seabios/src/stacks.c +++ b/roms/seabios/src/stacks.c @@ -1,11 +1,12 @@ // Code for manipulating stack locations. // -// Copyright (C) 2009-2010 Kevin O'Connor +// Copyright (C) 2009-2014 Kevin O'Connor // // This file may be distributed under the terms of the GNU LGPLv3 license. #include "biosvar.h" // GET_GLOBAL #include "bregs.h" // CR0_PE +#include "fw/paravirt.h" // PORT_SMI_CMD #include "hw/rtc.h" // rtc_use #include "list.h" // hlist_node #include "malloc.h" // free @@ -18,6 +19,369 @@ /**************************************************************** + * 16bit / 32bit calling + ****************************************************************/ + +struct { + u8 method; + u8 cmosindex; + u8 a20; + u16 ss, fs, gs; + struct descloc_s gdt; +} Call32Data VARLOW; + +#define C32_SLOPPY 1 +#define C32_SMM 2 + +int HaveSmmCall32 VARFSEG; + +// Backup state in preparation for call32_smm() +static void +call32_smm_prep(void) +{ + // Backup cmos index register and disable nmi + u8 cmosindex = inb(PORT_CMOS_INDEX); + outb(cmosindex | NMI_DISABLE_BIT, PORT_CMOS_INDEX); + inb(PORT_CMOS_DATA); + SET_LOW(Call32Data.cmosindex, cmosindex); + + // Backup ss + SET_LOW(Call32Data.ss, GET_SEG(SS)); + + SET_LOW(Call32Data.method, C32_SMM); +} + +// Restore state backed up during call32_smm() +static void +call32_smm_post(void) +{ + SET_LOW(Call32Data.method, 0); + SET_LOW(Call32Data.ss, 0); + + // Restore cmos index register + outb(GET_LOW(Call32Data.cmosindex), PORT_CMOS_INDEX); + inb(PORT_CMOS_DATA); +} + +#define ASM32_SWITCH16 " .pushsection .text.32fseg." UNIQSEC "\n .code16\n" +#define ASM32_BACK32 " .popsection\n .code32\n" +#define ASM16_SWITCH32 " .code32\n" +#define ASM16_BACK16 " .code16gcc\n" + +// Call a SeaBIOS C function in 32bit mode using smm trampoline +static u32 +call32_smm(void *func, u32 eax) +{ + ASSERT16(); + dprintf(9, "call32_smm %p %x\n", func, eax); + call32_smm_prep(); + u32 bkup_esp; + asm volatile( + // Backup esp / set esp to flat stack location + " movl %%esp, %0\n" + " movl %%ss, %%eax\n" + " shll $4, %%eax\n" + " addl %%eax, %%esp\n" + + // Transition to 32bit mode, call func, return to 16bit + " movl $" __stringify(CALL32SMM_CMDID) ", %%eax\n" + " movl $" __stringify(CALL32SMM_ENTERID) ", %%ecx\n" + " movl $(" __stringify(BUILD_BIOS_ADDR) " + 1f), %%ebx\n" + " outb %%al, $" __stringify(PORT_SMI_CMD) "\n" + " rep; nop\n" + " hlt\n" + + ASM16_SWITCH32 + "1:movl %1, %%eax\n" + " calll *%2\n" + " movl %%eax, %1\n" + + " movl $" __stringify(CALL32SMM_CMDID) ", %%eax\n" + " movl $" __stringify(CALL32SMM_RETURNID) ", %%ecx\n" + " movl $2f, %%ebx\n" + " outb %%al, $" __stringify(PORT_SMI_CMD) "\n" + " rep; nop\n" + " hlt\n" + + // Restore esp + ASM16_BACK16 + "2:movl %0, %%esp\n" + : "=&r" (bkup_esp), "+r" (eax) + : "r" (func) + : "eax", "ecx", "edx", "ebx", "cc", "memory"); + call32_smm_post(); + + dprintf(9, "call32_smm done %p %x\n", func, eax); + return eax; +} + +// 16bit handler code called from call16_smm() +u32 VISIBLE16 +call16_smm_helper(u32 eax, u32 edx, u32 (*func)(u32 eax, u32 edx)) +{ + if (!CONFIG_CALL32_SMM) + return eax; + call32_smm_post(); + u32 ret = func(eax, edx); + call32_smm_prep(); + return ret; +} + +static u32 +call16_smm(u32 eax, u32 edx, void *func) +{ + ASSERT32FLAT(); + if (!CONFIG_CALL32_SMM) + return eax; + func -= BUILD_BIOS_ADDR; + dprintf(9, "call16_smm %p %x %x\n", func, eax, edx); + u32 stackoffset = Call32Data.ss << 4; + asm volatile( + // Restore esp + " subl %0, %%esp\n" + + // Transition to 16bit mode, call func, return to 32bit + " movl $" __stringify(CALL32SMM_CMDID) ", %%eax\n" + " movl $" __stringify(CALL32SMM_RETURNID) ", %%ecx\n" + " movl $(1f - " __stringify(BUILD_BIOS_ADDR) "), %%ebx\n" + " outb %%al, $" __stringify(PORT_SMI_CMD) "\n" + " rep; nop\n" + " hlt\n" + + ASM32_SWITCH16 + "1:movl %1, %%eax\n" + " movl %3, %%ecx\n" + " calll _cfunc16_call16_smm_helper\n" + " movl %%eax, %1\n" + + " movl $" __stringify(CALL32SMM_CMDID) ", %%eax\n" + " movl $" __stringify(CALL32SMM_ENTERID) ", %%ecx\n" + " movl $2f, %%ebx\n" + " outb %%al, $" __stringify(PORT_SMI_CMD) "\n" + " rep; nop\n" + " hlt\n" + + // Set esp to flat stack location + ASM32_BACK32 + "2:addl %0, %%esp\n" + : "+r" (stackoffset), "+r" (eax), "+d" (edx) + : "r" (func) + : "eax", "ecx", "ebx", "cc", "memory"); + return eax; +} + +// Backup state in preparation for call32_sloppy() +static void +call32_sloppy_prep(void) +{ + // Backup cmos index register and disable nmi + u8 cmosindex = inb(PORT_CMOS_INDEX); + outb(cmosindex | NMI_DISABLE_BIT, PORT_CMOS_INDEX); + inb(PORT_CMOS_DATA); + SET_LOW(Call32Data.cmosindex, cmosindex); + + // Enable a20 and backup it's previous state + SET_LOW(Call32Data.a20, set_a20(1)); + + // Backup ss/fs/gs and gdt + SET_LOW(Call32Data.ss, GET_SEG(SS)); + SET_LOW(Call32Data.fs, GET_SEG(FS)); + SET_LOW(Call32Data.gs, GET_SEG(GS)); + struct descloc_s gdt; + sgdt(&gdt); + SET_LOW(Call32Data.gdt.length, gdt.length); + SET_LOW(Call32Data.gdt.addr, gdt.addr); + + SET_LOW(Call32Data.method, C32_SLOPPY); +} + +// Restore state backed up during call32_sloppy() +static void +call32_sloppy_post(void) +{ + SET_LOW(Call32Data.method, 0); + SET_LOW(Call32Data.ss, 0); + + // Restore gdt and fs/gs + struct descloc_s gdt; + gdt.length = GET_LOW(Call32Data.gdt.length); + gdt.addr = GET_LOW(Call32Data.gdt.addr); + lgdt(&gdt); + SET_SEG(FS, GET_LOW(Call32Data.fs)); + SET_SEG(GS, GET_LOW(Call32Data.gs)); + + // Restore a20 + set_a20(GET_LOW(Call32Data.a20)); + + // Restore cmos index register + outb(GET_LOW(Call32Data.cmosindex), PORT_CMOS_INDEX); + inb(PORT_CMOS_DATA); +} + +// Call a C function in 32bit mode. This clobbers the 16bit segment +// selector registers. +static u32 +call32_sloppy(void *func, u32 eax) +{ + ASSERT16(); + call32_sloppy_prep(); + u32 bkup_ss, bkup_esp; + asm volatile( + // Backup ss/esp / set esp to flat stack location + " movl %%ss, %0\n" + " movl %%esp, %1\n" + " shll $4, %0\n" + " addl %0, %%esp\n" + " shrl $4, %0\n" + + // Transition to 32bit mode, call func, return to 16bit + " movl $(" __stringify(BUILD_BIOS_ADDR) " + 1f), %%edx\n" + " jmp transition32\n" + ASM16_SWITCH32 + "1:calll *%3\n" + " movl $2f, %%edx\n" + " jmp transition16big\n" + + // Restore ds/ss/esp + ASM16_BACK16 + "2:movl %0, %%ds\n" + " movl %0, %%ss\n" + " movl %1, %%esp\n" + : "=&r" (bkup_ss), "=&r" (bkup_esp), "+a" (eax) + : "r" (func) + : "ecx", "edx", "cc", "memory"); + call32_sloppy_post(); + return eax; +} + +// 16bit handler code called from call16_sloppy() +u32 VISIBLE16 +call16_sloppy_helper(u32 eax, u32 edx, u32 (*func)(u32 eax, u32 edx)) +{ + call32_sloppy_post(); + u32 ret = func(eax, edx); + call32_sloppy_prep(); + return ret; +} + +// Jump back to 16bit mode while in 32bit mode from call32_sloppy() +static u32 +call16_sloppy(u32 eax, u32 edx, void *func) +{ + ASSERT32FLAT(); + if (getesp() > MAIN_STACK_MAX) + panic("call16_sloppy with invalid stack\n"); + func -= BUILD_BIOS_ADDR; + u32 stackseg = Call32Data.ss; + asm volatile( + // Transition to 16bit mode + " movl $(1f - " __stringify(BUILD_BIOS_ADDR) "), %%edx\n" + " jmp transition16big\n" + // Setup ss/esp and call func + ASM32_SWITCH16 + "1:movl %3, %%ecx\n" + " shll $4, %3\n" + " movw %%cx, %%ss\n" + " subl %3, %%esp\n" + " movw %%cx, %%ds\n" + " movl %2, %%edx\n" + " movl %1, %%ecx\n" + " calll _cfunc16_call16_sloppy_helper\n" + // Return to 32bit and restore esp + " movl $2f, %%edx\n" + " jmp transition32\n" + ASM32_BACK32 + "2:addl %3, %%esp\n" + : "+a" (eax) + : "r" (func), "r" (edx), "r" (stackseg) + : "edx", "ecx", "cc", "memory"); + return eax; +} + +// Call a 32bit SeaBIOS function from a 16bit SeaBIOS function. +u32 VISIBLE16 +call32(void *func, u32 eax, u32 errret) +{ + ASSERT16(); + if (CONFIG_CALL32_SMM && GET_GLOBAL(HaveSmmCall32)) + return call32_smm(func, eax); + u32 cr0 = getcr0(); + if (cr0 & CR0_PE) + // Called in 16bit protected mode?! + return errret; + return call32_sloppy(func, eax); +} + +// Call a 16bit SeaBIOS function from a 32bit SeaBIOS function. +static u32 +call16(u32 eax, u32 edx, void *func) +{ + ASSERT32FLAT(); + if (getesp() > BUILD_STACK_ADDR) + panic("call16 with invalid stack\n"); + func -= BUILD_BIOS_ADDR; + asm volatile( + // Transition to 16bit mode + " movl $(1f - " __stringify(BUILD_BIOS_ADDR) "), %%edx\n" + " jmp transition16\n" + // Call func + ASM32_SWITCH16 + "1:movl %2, %%edx\n" + " calll *%1\n" + // Return to 32bit + " movl $2f, %%edx\n" + " jmp transition32\n" + ASM32_BACK32 + "2:\n" + : "+a" (eax) + : "r" (func), "r" (edx) + : "edx", "ecx", "cc", "memory"); + return eax; +} + +// Call a 16bit SeaBIOS function in "big real" mode. +static u32 +call16big(u32 eax, u32 edx, void *func) +{ + ASSERT32FLAT(); + if (getesp() > BUILD_STACK_ADDR) + panic("call16big with invalid stack\n"); + func -= BUILD_BIOS_ADDR; + asm volatile( + // Transition to 16bit mode + " movl $(1f - " __stringify(BUILD_BIOS_ADDR) "), %%edx\n" + " jmp transition16big\n" + // Call func + ASM32_SWITCH16 + "1:movl %2, %%edx\n" + " calll *%1\n" + // Return to 32bit + " movl $2f, %%edx\n" + " jmp transition32\n" + ASM32_BACK32 + "2:\n" + : "+a" (eax) + : "r" (func), "r" (edx) + : "edx", "ecx", "cc", "memory"); + return eax; +} + +// Call a 16bit SeaBIOS function, restoring the mode from last call32(). +static u32 +call16_back(u32 eax, u32 edx, void *func) +{ + ASSERT32FLAT(); + if (CONFIG_CALL32_SMM && Call32Data.method == C32_SMM) + return call16_smm(eax, edx, func); + if (Call32Data.method == C32_SLOPPY) + return call16_sloppy(eax, edx, func); + if (in_post()) + return call16big(eax, edx, func); + return call16(eax, edx, func); +} + + +/**************************************************************** * Extra 16bit stack ****************************************************************/ @@ -26,7 +390,7 @@ u8 ExtraStack[BUILD_EXTRA_STACK_SIZE+1] VARLOW __aligned(8); u8 *StackPos VARLOW; // Test if currently on the extra stack -static inline int +int on_extra_stack(void) { return MODE16 && GET_SEG(SS) == SEG_LOW && getesp() > (u32)ExtraStack; @@ -69,7 +433,9 @@ stack_hop(u32 eax, u32 edx, void *func) u32 stack_hop_back(u32 eax, u32 edx, void *func) { - if (!on_extra_stack()) + if (!MODESEGMENT) + return call16_back(eax, edx, func); + if (!MODE16 || !on_extra_stack()) return ((u32 (*)(u32, u32))func)(eax, edx); ASSERT16(); u16 bkup_ss; @@ -100,95 +466,6 @@ stack_hop_back(u32 eax, u32 edx, void *func) /**************************************************************** - * 16bit / 32bit calling - ****************************************************************/ - -u16 StackSeg VARLOW; - -// Call a 32bit SeaBIOS function from a 16bit SeaBIOS function. -u32 VISIBLE16 -call32(void *func, u32 eax, u32 errret) -{ - ASSERT16(); - u32 cr0 = getcr0(); - if (cr0 & CR0_PE) - // Called in 16bit protected mode?! - return errret; - - // Backup cmos index register and disable nmi - u8 cmosindex = inb(PORT_CMOS_INDEX); - outb(cmosindex | NMI_DISABLE_BIT, PORT_CMOS_INDEX); - inb(PORT_CMOS_DATA); - - // Backup fs/gs and gdt - u16 fs = GET_SEG(FS), gs = GET_SEG(GS); - struct descloc_s gdt; - sgdt(&gdt); - - u16 oldstackseg = GET_LOW(StackSeg); - SET_LOW(StackSeg, GET_SEG(SS)); - u32 bkup_ss, bkup_esp; - asm volatile( - // Backup ss/esp / set esp to flat stack location - " movl %%ss, %0\n" - " movl %%esp, %1\n" - " shll $4, %0\n" - " addl %0, %%esp\n" - " shrl $4, %0\n" - - // Transition to 32bit mode, call func, return to 16bit - " movl $(" __stringify(BUILD_BIOS_ADDR) " + 1f), %%edx\n" - " jmp transition32\n" - " .code32\n" - "1:calll *%3\n" - " movl $2f, %%edx\n" - " jmp transition16big\n" - - // Restore ds/ss/esp - " .code16gcc\n" - "2:movl %0, %%ds\n" - " movl %0, %%ss\n" - " movl %1, %%esp\n" - : "=&r" (bkup_ss), "=&r" (bkup_esp), "+a" (eax) - : "r" (func) - : "ecx", "edx", "cc", "memory"); - - SET_LOW(StackSeg, oldstackseg); - - // Restore gdt and fs/gs - lgdt(&gdt); - SET_SEG(FS, fs); - SET_SEG(GS, gs); - - // Restore cmos index register - outb(cmosindex, PORT_CMOS_INDEX); - inb(PORT_CMOS_DATA); - return eax; -} - -// Call a 16bit SeaBIOS function from a 32bit SeaBIOS function. -static inline u32 -call16(u32 eax, u32 edx, void *func) -{ - ASSERT32FLAT(); - if (getesp() > MAIN_STACK_MAX) - panic("call16 with invalid stack\n"); - extern u32 __call16(u32 eax, u32 edx, void *func); - return __call16(eax, edx, func - BUILD_BIOS_ADDR); -} - -static inline u32 -call16big(u32 eax, u32 edx, void *func) -{ - ASSERT32FLAT(); - if (getesp() > MAIN_STACK_MAX) - panic("call16big with invalid stack\n"); - extern u32 __call16big(u32 eax, u32 edx, void *func); - return __call16big(eax, edx, func - BUILD_BIOS_ADDR); -} - - -/**************************************************************** * External 16bit interface calling ****************************************************************/ @@ -196,11 +473,12 @@ call16big(u32 eax, u32 edx, void *func) void VISIBLE16 _farcall16(struct bregs *callregs, u16 callregseg) { - ASSERT16(); - if (on_extra_stack()) { - stack_hop_back((u32)callregs, callregseg, _farcall16); + if (need_hop_back()) { + extern void _cfunc16__farcall16(void); + stack_hop_back((u32)callregs, callregseg, _cfunc16__farcall16); return; } + ASSERT16(); asm volatile( "calll __farcall16\n" : "+a" (callregs), "+m" (*callregs), "+d" (callregseg) @@ -208,34 +486,42 @@ _farcall16(struct bregs *callregs, u16 callregseg) : "ebx", "ecx", "esi", "edi", "cc", "memory"); } -inline void +void farcall16(struct bregs *callregs) { - if (MODE16) { - _farcall16(callregs, GET_SEG(SS)); - return; - } extern void _cfunc16__farcall16(void); - call16((u32)callregs - StackSeg * 16, StackSeg, _cfunc16__farcall16); + call16((u32)callregs, 0, _cfunc16__farcall16); } -inline void +void farcall16big(struct bregs *callregs) { extern void _cfunc16__farcall16(void); - call16big((u32)callregs - StackSeg * 16, StackSeg, _cfunc16__farcall16); + call16big((u32)callregs, 0, _cfunc16__farcall16); } // Invoke a 16bit software interrupt. -inline void +void __call16_int(struct bregs *callregs, u16 offset) { - if (MODESEGMENT) - callregs->code.seg = GET_SEG(CS); - else - callregs->code.seg = SEG_BIOS; callregs->code.offset = offset; - farcall16(callregs); + if (!MODESEGMENT) { + callregs->code.seg = SEG_BIOS; + _farcall16((void*)callregs - Call32Data.ss * 16, Call32Data.ss); + return; + } + callregs->code.seg = GET_SEG(CS); + _farcall16(callregs, GET_SEG(SS)); +} + +// Reset the machine +void +reset(void) +{ + extern void reset_vector(void) __noreturn; + if (!MODE16) + call16_back(0, 0, reset_vector); + reset_vector(); } @@ -374,8 +660,9 @@ fail: void VISIBLE16 check_irqs(void) { - if (on_extra_stack()) { - stack_hop_back(0, 0, check_irqs); + if (need_hop_back()) { + extern void _cfunc16_check_irqs(void); + stack_hop_back(0, 0, _cfunc16_check_irqs); return; } asm volatile("sti ; nop ; rep ; nop ; cli ; cld" : : :"memory"); @@ -385,19 +672,14 @@ check_irqs(void) void yield(void) { - if (MODESEGMENT) { + if (MODESEGMENT || !CONFIG_THREADS) { check_irqs(); return; } - extern void _cfunc16_check_irqs(void); - if (!CONFIG_THREADS) { - call16big(0, 0, _cfunc16_check_irqs); - return; - } struct thread_info *cur = getCurThread(); if (cur == &MainThread) // Permit irqs to fire - call16big(0, 0, _cfunc16_check_irqs); + check_irqs(); // Switch to the next thread switch_next(cur); @@ -406,8 +688,9 @@ yield(void) void VISIBLE16 wait_irq(void) { - if (on_extra_stack()) { - stack_hop_back(0, 0, wait_irq); + if (need_hop_back()) { + extern void _cfunc16_wait_irq(void); + stack_hop_back(0, 0, _cfunc16_wait_irq); return; } asm volatile("sti ; hlt ; cli ; cld": : :"memory"); @@ -417,17 +700,12 @@ wait_irq(void) void yield_toirq(void) { - if (MODESEGMENT) { - wait_irq(); - return; - } - if (have_threads()) { + if (!MODESEGMENT && have_threads()) { // Threads still active - do a yield instead. yield(); return; } - extern void _cfunc16_wait_irq(void); - call16big(0, 0, _cfunc16_wait_irq); + wait_irq(); } // Wait for all threads (other than the main thread) to complete. diff --git a/roms/seabios/src/stacks.h b/roms/seabios/src/stacks.h index d8584f2..82c4c3c 100644 --- a/roms/seabios/src/stacks.h +++ b/roms/seabios/src/stacks.h @@ -4,19 +4,26 @@ #include "types.h" // u32 +#define CALL32SMM_CMDID 0xb5 +#define CALL32SMM_ENTERID 0x1234 +#define CALL32SMM_RETURNID 0x5678 + // stacks.c +extern int HaveSmmCall32; +u32 call32(void *func, u32 eax, u32 errret); extern u8 ExtraStack[], *StackPos; u32 stack_hop(u32 eax, u32 edx, void *func); u32 stack_hop_back(u32 eax, u32 edx, void *func); -u32 call32(void *func, u32 eax, u32 errret); +int on_extra_stack(void); struct bregs; -inline void farcall16(struct bregs *callregs); -inline void farcall16big(struct bregs *callregs); -inline void __call16_int(struct bregs *callregs, u16 offset); +void farcall16(struct bregs *callregs); +void farcall16big(struct bregs *callregs); +void __call16_int(struct bregs *callregs, u16 offset); #define call16_int(nr, callregs) do { \ extern void irq_trampoline_ ##nr (); \ __call16_int((callregs), (u32)&irq_trampoline_ ##nr ); \ } while (0) +void reset(void); extern struct thread_info MainThread; struct thread_info *getCurThread(void); void yield(void); @@ -34,4 +41,13 @@ int wait_preempt(void); void check_preempt(void); u32 call32_params(void *func, u32 eax, u32 edx, u32 ecx, u32 errret); +// Inline functions + +// Check if a call to stack_hop_back is needed. +static inline int +need_hop_back(void) +{ + return !MODESEGMENT || on_extra_stack(); +} + #endif // stacks.h diff --git a/roms/seabios/src/std/LegacyBios.h b/roms/seabios/src/std/LegacyBios.h index cf0c3c5..5170c37 100644 --- a/roms/seabios/src/std/LegacyBios.h +++ b/roms/seabios/src/std/LegacyBios.h @@ -228,6 +228,26 @@ typedef struct { /// Maximum PCI bus number assigned. /// UINT8 LastPciBus; + + /// + /// Start address of UMB RAM + /// + UINT32 UmaAddress; + + /// + /// Size of UMB RAM + /// + UINT32 UmaSize; + + /// + /// Start address of persistent allocation in high (>1MiB) memory + /// + UINT32 HiPermanentMemoryAddress; + + /// + /// Size of persistent allocation in high (>1MiB) memory + /// + UINT32 HiPermanentMemorySize; } EFI_COMPATIBILITY16_TABLE; /// diff --git a/roms/seabios/src/std/acpi.h b/roms/seabios/src/std/acpi.h index fad6ac2..e0d9516 100644 --- a/roms/seabios/src/std/acpi.h +++ b/roms/seabios/src/std/acpi.h @@ -42,6 +42,31 @@ struct rsdp_descriptor { /* Root System Descriptor Pointer */ u8 asl_compiler_id [4]; /* ASL compiler vendor ID */ \ u32 asl_compiler_revision; /* ASL compiler revision number */ +/* + * Fixed ACPI Description Table Fixed Feature Flags + */ +#define ACPI_FADT_F_WBINVD (1 << 0) +#define ACPI_FADT_F_WBINVD_FLUSH (1 << 1) +#define ACPI_FADT_F_PROC_C1 (1 << 2) +#define ACPI_FADT_F_P_LVL2_UP (1 << 3) +#define ACPI_FADT_F_PWR_BUTTON (1 << 4) +#define ACPI_FADT_F_SLP_BUTTON (1 << 5) +#define ACPI_FADT_F_FIX_RTC (1 << 6) +#define ACPI_FADT_F_RTC_S4 (1 << 7) +#define ACPI_FADT_F_TMR_VAL_EXT (1 << 8) +#define ACPI_FADT_F_DCK_CAP (1 << 9) +#define ACPI_FADT_F_RESET_REG_SUP (1 << 10) +#define ACPI_FADT_F_SEALED_CASE (1 << 11) +#define ACPI_FADT_F_HEADLESS (1 << 12) +#define ACPI_FADT_F_CPU_SW_SLP (1 << 13) +#define ACPI_FADT_F_PCI_EXP_WAK (1 << 14) +#define ACPI_FADT_F_USE_PLATFORM_CLOCK (1 << 15) +#define ACPI_FADT_F_S4_RTC_STS_VALID (1 << 16) +#define ACPI_FADT_F_REMOTE_POWER_ON_CAPABLE (1 << 17) +#define ACPI_FADT_F_FORCE_APIC_CLUSTER_MODEL (1 << 18) +#define ACPI_FADT_F_FORCE_APIC_PHYSICAL_DESTINATION_MODE (1 << 19) +#define ACPI_FADT_F_HW_REDUCED_ACPI (1 << 20) +#define ACPI_FADT_F_LOW_POWER_S0_IDLE_CAPABLE (1 << 21) /* * ACPI 1.0 Fixed ACPI Description Table (FADT) diff --git a/roms/seabios/src/std/bda.h b/roms/seabios/src/std/bda.h index 948bdbf..c321266 100644 --- a/roms/seabios/src/std/bda.h +++ b/roms/seabios/src/std/bda.h @@ -95,12 +95,7 @@ struct bios_data_area_s { struct segoff_s video_savetable; u8 other_ac[4]; // 40:B0 - u8 other_b0[9]; - u8 vbe_flag; - u16 vbe_mode; - u8 other_bc[4]; - // 40:C0 - u8 other_c0[4*16]; + u8 other_b0[5*16]; } PACKED; // BDA floppy_recalibration_status bitdefs diff --git a/roms/seabios/src/std/disk.h b/roms/seabios/src/std/disk.h index b2576d9..6397108 100644 --- a/roms/seabios/src/std/disk.h +++ b/roms/seabios/src/std/disk.h @@ -129,7 +129,7 @@ struct packed_chs_s { u8 heads; u8 sptcyl; u8 cyllow; -}; +} PACKED; struct partition_s { u8 status; @@ -169,9 +169,7 @@ struct eltorito_s { u16 buffer_segment; u16 load_segment; u16 sector_count; - u8 cylinders; - u8 sectors; - u8 heads; -}; + struct packed_chs_s chs; +} PACKED; #endif // disk.h diff --git a/roms/seabios/src/std/vga.h b/roms/seabios/src/std/vga.h new file mode 100644 index 0000000..de9ec75 --- /dev/null +++ b/roms/seabios/src/std/vga.h @@ -0,0 +1,63 @@ +#ifndef __VGA_H +#define __VGA_H +// Standard structure definitions for vgabios video tables + +#include "types.h" // u8 + +// standard BIOS Video Parameter Table +struct video_param_s { + u8 twidth; + u8 theightm1; + u8 cheight; + u16 slength; + u8 sequ_regs[4]; + u8 miscreg; + u8 crtc_regs[25]; + u8 actl_regs[20]; + u8 grdc_regs[9]; +} PACKED; + +// Standard Video Save Pointer Table +struct video_save_pointer_s { + struct segoff_s videoparam; + struct segoff_s paramdynamicsave; + struct segoff_s textcharset; + struct segoff_s graphcharset; + struct segoff_s secsavepointer; + u8 reserved[8]; +} PACKED; + +// Data returned by int101B +struct video_func_static { + u32 modes; + u8 reserved_0x04[3]; + u8 scanlines; + u8 cblocks; + u8 active_cblocks; + u16 misc_flags; + u8 reserved_0x0c[2]; + u8 save_flags; + u8 reserved_0x0f; +} PACKED; + +struct video_func_info { + struct segoff_s static_functionality; + u8 bda_0x49[30]; + u8 bda_0x84[3]; + u8 dcc_index; + u8 dcc_alt; + u16 colors; + u8 pages; + u8 scan_lines; + u8 primary_char; + u8 secondar_char; + u8 misc; + u8 non_vga_mode; + u8 reserved_2f[2]; + u8 video_mem; + u8 save_flags; + u8 disp_info; + u8 reserved_34[12]; +} PACKED; + +#endif // vga.h diff --git a/roms/seabios/src/string.c b/roms/seabios/src/string.c index 8556fe9..2e4e437 100644 --- a/roms/seabios/src/string.c +++ b/roms/seabios/src/string.c @@ -42,6 +42,19 @@ strlen(const char *s) return p-s; } +int +memcmp_far(u16 s1seg, const void *s1, u16 s2seg, const void *s2, size_t n) +{ + while (n--) { + int d = GET_FARVAR(s1seg, *(u8*)s1) - GET_FARVAR(s2seg, *(u8*)s2); + if (d) + return d < 0 ? -1 : 1; + s1++; + s2++; + } + return 0; +} + // Compare two areas of memory. int memcmp(const void *s1, const void *s2, size_t n) diff --git a/roms/seabios/src/string.h b/roms/seabios/src/string.h index ca751a4..a557d6a 100644 --- a/roms/seabios/src/string.h +++ b/roms/seabios/src/string.h @@ -8,6 +8,7 @@ u8 checksum_far(u16 buf_seg, void *buf_far, u32 len); u8 checksum(void *buf, u32 len); size_t strlen(const char *s); +int memcmp_far(u16 s1seg, const void *s1, u16 s2seg, const void *s2, size_t n); int memcmp(const void *s1, const void *s2, size_t n); int strcmp(const char *s1, const char *s2); inline void memset_far(u16 d_seg, void *d_far, u8 c, size_t len); diff --git a/roms/seabios/src/system.c b/roms/seabios/src/system.c index 756dc31..60a6fce 100644 --- a/roms/seabios/src/system.c +++ b/roms/seabios/src/system.c @@ -8,28 +8,12 @@ #include "biosvar.h" // GET_GLOBAL #include "bregs.h" // struct bregs #include "hw/pic.h" // pic_reset -#include "hw/ps2port.h" // PORT_A20 #include "malloc.h" // LegacyRamSize #include "memmap.h" // E820_RAM #include "output.h" // debug_enter #include "string.h" // memcpy_far #include "util.h" // handle_1553 -#include "x86.h" // inb - -// Use PS2 System Control port A to set A20 enable -static inline u8 -set_a20(u8 cond) -{ - // get current setting first - u8 newval, oldval = inb(PORT_A20); - if (cond) - newval = oldval | A20_ENABLE_BIT; - else - newval = oldval & ~A20_ENABLE_BIT; - outb(newval, PORT_A20); - - return (oldval & A20_ENABLE_BIT) != 0; -} +#include "x86.h" // set_a20 static void handle_152400(struct bregs *regs) @@ -48,7 +32,7 @@ handle_152401(struct bregs *regs) static void handle_152402(struct bregs *regs) { - regs->al = (inb(PORT_A20) & A20_ENABLE_BIT) != 0; + regs->al = get_a20(); set_code_success(regs); } @@ -108,8 +92,7 @@ handle_1587(struct bregs *regs) // check for access rights of source & dest here // Initialize GDT descriptor - u32 si = regs->si; - u64 *gdt_far = (void*)si; + u64 *gdt_far = (void*)(regs->si + 0); u16 gdt_seg = regs->es; u32 loc = (u32)MAKE_FLATPTR(gdt_seg, gdt_far); SET_FARVAR(gdt_seg, gdt_far[1], GDT_DATA | GDT_LIMIT((6*sizeof(u64))-1) @@ -123,10 +106,11 @@ handle_1587(struct bregs *regs) loc = (u32)MAKE_FLATPTR(GET_SEG(SS), 0); SET_FARVAR(gdt_seg, gdt_far[5], GDT_DATA | lim | GDT_BASE(loc)); - u16 count = regs->cx; + SET_SEG(ES, gdt_seg); + u16 count = regs->cx, si = 0, di = 0; asm volatile( // Load new descriptor tables - " lgdtw %%es:(1<<3)(%%si)\n" + " lgdtw %%es:(1<<3)(%%eax)\n" " lidtw %%cs:pmode_IDT_info\n" // Enable protected mode @@ -143,13 +127,14 @@ handle_1587(struct bregs *regs) " movw $(3<<3), %%ax\n" // 3rd descriptor in table, TI=GDT, RPL=00 " movw %%ax, %%es\n" - // move CX words from DS:SI to ES:DI - " xorw %%si, %%si\n" - " xorw %%di, %%di\n" - " rep movsw\n" + // memcpy CX words using 32bit memcpy if applicable + " testw $1, %%cx\n" + " jnz 3f\n" + " shrw $1, %%cx\n" + " rep movsl %%ds:(%%si), %%es:(%%di)\n" // Restore DS and ES segment limits to 0xffff - " movw $(5<<3), %%ax\n" // 5th descriptor in table (SS) + "2:movw $(5<<3), %%ax\n" // 5th descriptor in table (SS) " movw %%ax, %%ds\n" " movw %%ax, %%es\n" @@ -159,16 +144,21 @@ handle_1587(struct bregs *regs) " movl %%eax, %%cr0\n" // far jump to flush CPU queue after transition to real mode - " ljmpw $" __stringify(SEG_BIOS) ", $2f\n" + " ljmpw $" __stringify(SEG_BIOS) ", $4f\n" + + // Slower 16bit copy method + "3:rep movsw %%ds:(%%si), %%es:(%%di)\n" + " jmp 2b\n" // restore IDT to normal real-mode defaults - "2:lidtw %%cs:rmode_IDT_info\n" + "4:lidtw %%cs:rmode_IDT_info\n" // Restore %ds (from %ss) " movw %%ss, %%ax\n" " movw %%ax, %%ds\n" - : "+c"(count), "+S"(si), "+m" (__segment_ES) - : : "eax", "di", "cc"); + : "+a" (gdt_far), "+c"(count), "+m" (__segment_ES) + : "S" (si), "D" (di) + : "cc"); set_a20(prev_a20_enable); diff --git a/roms/seabios/src/types.h b/roms/seabios/src/types.h index 3466b3a..097372c 100644 --- a/roms/seabios/src/types.h +++ b/roms/seabios/src/types.h @@ -62,14 +62,14 @@ extern void __force_link_error__only_in_16bit(void) __noreturn; # define VISIBLE32SEG // Designate a variable as (only) visible to 16bit code. # define VAR16 __section(".data16." UNIQSEC) -// Designate a variable at a specific 16bit address -# define VAR16FIXED(addr) __aligned(1) __VISIBLE __section(".fixedaddr." __stringify(addr)) // Designate a variable as (only) visible to 32bit segmented code. # define VAR32SEG __section(".discard.var32seg." UNIQSEC) // Designate a variable as visible and located in the e-segment. # define VARLOW __section(".discard.varlow." UNIQSEC) __VISIBLE __weak // Designate a variable as visible and located in the f-segment. # define VARFSEG __section(".discard.varfseg." UNIQSEC) __VISIBLE __weak +// Designate a variable at a specific address in the f-segment. +# define VARFSEGFIXED(addr) __section(".discard.varfixed." UNIQSEC) __VISIBLE __weak // Verify a variable is only accessable via 32bit "init" functions # define VARVERIFY32INIT __section(".discard.varinit." UNIQSEC) // Designate top-level assembler as 16bit only. @@ -86,10 +86,10 @@ extern void __force_link_error__only_in_16bit(void) __noreturn; # define VISIBLE32INIT # define VISIBLE32SEG __VISIBLE # define VAR16 __section(".discard.var16." UNIQSEC) -# define VAR16FIXED(addr) VAR16 __VISIBLE __weak # define VAR32SEG __section(".data32seg." UNIQSEC) # define VARLOW __section(".discard.varlow." UNIQSEC) __VISIBLE __weak # define VARFSEG __section(".discard.varfseg." UNIQSEC) __VISIBLE __weak +# define VARFSEGFIXED(addr) __section(".discard.varfixed." UNIQSEC) __VISIBLE __weak # define VARVERIFY32INIT __section(".discard.varinit." UNIQSEC) # define ASM16(code) # define ASM32FLAT(code) @@ -102,10 +102,10 @@ extern void __force_link_error__only_in_16bit(void) __noreturn; # define VISIBLE32INIT __section(".text.init." UNIQSEC) __VISIBLE # define VISIBLE32SEG # define VAR16 __section(".discard.var16." UNIQSEC) -# define VAR16FIXED(addr) VAR16 __VISIBLE __weak # define VAR32SEG __section(".discard.var32seg." UNIQSEC) # define VARLOW __section(".data.varlow." UNIQSEC) __VISIBLE __weak # define VARFSEG __section(".data.varfseg." UNIQSEC) __VISIBLE +# define VARFSEGFIXED(addr) __section(".fixedaddr." __stringify(addr)) __VISIBLE __aligned(1) # define VARVERIFY32INIT __section(".data.varinit." UNIQSEC) # define ASM16(code) # define ASM32FLAT(code) __ASM(code) diff --git a/roms/seabios/src/util.h b/roms/seabios/src/util.h index b54271b..09bb8a9 100644 --- a/roms/seabios/src/util.h +++ b/roms/seabios/src/util.h @@ -44,12 +44,11 @@ void disable_bootsplash(void); // cdrom.c extern u8 CDRom_locks[]; -extern struct cdemu_s CDEmu; +extern struct eltorito_s CDEmu; extern struct drive_s *cdemu_drive_gf; struct disk_op_s; int process_cdemu_op(struct disk_op_s *op); void cdrom_prepboot(void); -void cdemu_134b(struct bregs *regs); int cdrom_boot(struct drive_s *drive_g); // clock.c @@ -126,7 +125,6 @@ void smm_device_setup(void); void smm_setup(void); // fw/smp.c -extern u32 CountCPUs; extern u32 MaxCountCPUs; void wrmsr_smp(u32 index, u64 val); void smp_setup(void); @@ -148,6 +146,10 @@ void floppy_tick(void); void ramdisk_setup(void); int process_ramdisk_op(struct disk_op_s *op); +// hw/sdcard.c +int process_sdcard_op(struct disk_op_s *op); +void sdcard_setup(void); + // hw/timer.c void timer_setup(void); void pmtimer_setup(u16 ioport); @@ -178,6 +180,7 @@ void process_key(u8 key); // misc.c extern struct bios_config_table_s BIOS_CONFIG_TABLE __aligned(1); +extern struct floppy_dbt_s diskette_param_table __aligned(1); extern u8 BiosChecksum; int in_post(void); void mathcp_setup(void); @@ -218,9 +221,6 @@ void reloc_preinit(void *f, void *arg); // resume.c extern int HaveRunPost; -// romlayout.S -void reset_vector(void) __noreturn; - // serial.c void serial_setup(void); void lpt_setup(void); diff --git a/roms/seabios/src/x86.h b/roms/seabios/src/x86.h index 65f42b3..7798b1c 100644 --- a/roms/seabios/src/x86.h +++ b/roms/seabios/src/x86.h @@ -14,6 +14,10 @@ #define CR0_NW (1<<29) // Not Write-through #define CR0_PE (1<<0) // Protection enable +// PORT_A20 bitdefs +#define PORT_A20 0x0092 +#define A20_ENABLE_BIT 0x02 + #ifndef __ASSEMBLY__ #include "types.h" // u32 @@ -172,22 +176,31 @@ static inline void outsl(u16 port, u32 *data, u32 count) { } static inline void writel(void *addr, u32 val) { + barrier(); *(volatile u32 *)addr = val; } static inline void writew(void *addr, u16 val) { + barrier(); *(volatile u16 *)addr = val; } static inline void writeb(void *addr, u8 val) { + barrier(); *(volatile u8 *)addr = val; } static inline u32 readl(const void *addr) { - return *(volatile const u32 *)addr; + u32 val = *(volatile const u32 *)addr; + barrier(); + return val; } static inline u16 readw(const void *addr) { - return *(volatile const u16 *)addr; + u16 val = *(volatile const u16 *)addr; + barrier(); + return val; } static inline u8 readb(const void *addr) { - return *(volatile const u8 *)addr; + u8 val = *(volatile const u8 *)addr; + barrier(); + return val; } // GDT bits @@ -216,10 +229,19 @@ static inline void lgdt(struct descloc_s *desc) { asm("lgdtl %0" : : "m"(*desc) : "memory"); } +static inline u8 get_a20(void) { + return (inb(PORT_A20) & A20_ENABLE_BIT) != 0; +} + +static inline u8 set_a20(u8 cond) { + u8 val = inb(PORT_A20); + outb((val & ~A20_ENABLE_BIT) | (cond ? A20_ENABLE_BIT : 0), PORT_A20); + return (val & A20_ENABLE_BIT) != 0; +} + // x86.c void cpuid(u32 index, u32 *eax, u32 *ebx, u32 *ecx, u32 *edx); - #endif // !__ASSEMBLY__ #endif // x86.h diff --git a/roms/seabios/vgasrc/Kconfig b/roms/seabios/vgasrc/Kconfig index 951240c..400e8da 100644 --- a/roms/seabios/vgasrc/Kconfig +++ b/roms/seabios/vgasrc/Kconfig @@ -50,6 +50,7 @@ menu "VGA ROM" config VGA_COREBOOT depends on COREBOOT bool "coreboot linear framebuffer" + select VGA_EMULATE_TEXT help Build support for a vgabios wrapper around video devices initialized using coreboot native vga init. @@ -83,6 +84,11 @@ menu "VGA ROM" config VGA_STDVGA_PORTS bool + config VGA_EMULATE_TEXT + bool + help + Support emulating text mode features when only a + framebuffer is available. config VGA_ALLOCATE_EXTRA_STACK depends on BUILD_VGABIOS diff --git a/roms/seabios/vgasrc/bochsvga.c b/roms/seabios/vgasrc/bochsvga.c index 5313f54..ee6f437 100644 --- a/roms/seabios/vgasrc/bochsvga.c +++ b/roms/seabios/vgasrc/bochsvga.c @@ -151,10 +151,21 @@ static inline void dispi_write(u16 reg, u16 val) outw(val, VBE_DISPI_IOPORT_DATA); } +static u8 +bochsvga_dispi_enabled(void) +{ + if (!GET_GLOBAL(dispi_found)) + return 0; + u16 en = dispi_read(VBE_DISPI_INDEX_ENABLE); + if (!(en & VBE_DISPI_ENABLED)) + return 0; + return 1; +} + int bochsvga_get_window(struct vgamode_s *vmode_g, int window) { - if (!GET_GLOBAL(dispi_found)) + if (!bochsvga_dispi_enabled()) return stdvga_get_window(vmode_g, window); if (window != 0) return -1; @@ -164,7 +175,7 @@ bochsvga_get_window(struct vgamode_s *vmode_g, int window) int bochsvga_set_window(struct vgamode_s *vmode_g, int window, int val) { - if (!GET_GLOBAL(dispi_found)) + if (!bochsvga_dispi_enabled()) return stdvga_set_window(vmode_g, window, val); if (window != 0) return -1; @@ -177,7 +188,7 @@ bochsvga_set_window(struct vgamode_s *vmode_g, int window, int val) int bochsvga_get_linelength(struct vgamode_s *vmode_g) { - if (!GET_GLOBAL(dispi_found)) + if (!bochsvga_dispi_enabled()) return stdvga_get_linelength(vmode_g); return dispi_read(VBE_DISPI_INDEX_VIRT_WIDTH) * vga_bpp(vmode_g) / 8; } @@ -186,7 +197,7 @@ int bochsvga_set_linelength(struct vgamode_s *vmode_g, int val) { stdvga_set_linelength(vmode_g, val); - if (GET_GLOBAL(dispi_found)) { + if (bochsvga_dispi_enabled()) { int pixels = (val * 8) / vga_bpp(vmode_g); dispi_write(VBE_DISPI_INDEX_VIRT_WIDTH, pixels); } @@ -196,7 +207,7 @@ bochsvga_set_linelength(struct vgamode_s *vmode_g, int val) int bochsvga_get_displaystart(struct vgamode_s *vmode_g) { - if (!GET_GLOBAL(dispi_found)) + if (!bochsvga_dispi_enabled()) return stdvga_get_displaystart(vmode_g); int bpp = vga_bpp(vmode_g); int linelength = dispi_read(VBE_DISPI_INDEX_VIRT_WIDTH) * bpp / 8; @@ -209,7 +220,7 @@ int bochsvga_set_displaystart(struct vgamode_s *vmode_g, int val) { stdvga_set_displaystart(vmode_g, val); - if (GET_GLOBAL(dispi_found)) { + if (bochsvga_dispi_enabled()) { int bpp = vga_bpp(vmode_g); int linelength = dispi_read(VBE_DISPI_INDEX_VIRT_WIDTH) * bpp / 8; if (!linelength) @@ -223,7 +234,7 @@ bochsvga_set_displaystart(struct vgamode_s *vmode_g, int val) int bochsvga_get_dacformat(struct vgamode_s *vmode_g) { - if (!GET_GLOBAL(dispi_found)) + if (!bochsvga_dispi_enabled()) return stdvga_get_dacformat(vmode_g); u16 en = dispi_read(VBE_DISPI_INDEX_ENABLE); return (en & VBE_DISPI_8BIT_DAC) ? 8 : 6; @@ -232,7 +243,7 @@ bochsvga_get_dacformat(struct vgamode_s *vmode_g) int bochsvga_set_dacformat(struct vgamode_s *vmode_g, int val) { - if (!GET_GLOBAL(dispi_found)) + if (!bochsvga_dispi_enabled()) return stdvga_set_dacformat(vmode_g, val); u16 en = dispi_read(VBE_DISPI_INDEX_ENABLE); if (val == 6) diff --git a/roms/seabios/vgasrc/cbvga.c b/roms/seabios/vgasrc/cbvga.c index a9c6d3a..1cfb9d3 100644 --- a/roms/seabios/vgasrc/cbvga.c +++ b/roms/seabios/vgasrc/cbvga.c @@ -14,12 +14,15 @@ static int CBmode VAR16; static struct vgamode_s CBmodeinfo VAR16; +static struct vgamode_s CBemulinfo VAR16; static u32 CBlinelength VAR16; struct vgamode_s *cbvga_find_mode(int mode) { if (mode == GET_GLOBAL(CBmode)) return &CBmodeinfo; + if (mode == 0x03) + return &CBemulinfo; return NULL; } @@ -92,6 +95,8 @@ cbvga_save_restore(int cmd, u16 seg, void *data) int cbvga_set_mode(struct vgamode_s *vmode_g, int flags) { + u8 emul = vmode_g == &CBemulinfo || GET_GLOBAL(CBmode) == 0x03; + MASK_BDA_EXT(flags, BF_EMULATE_TEXT, emul ? BF_EMULATE_TEXT : 0); if (!(flags & MF_NOCLEARMEM)) { if (GET_GLOBAL(CBmodeinfo.memmodel) == MM_TEXT) { memset16_far(SEG_CTEXT, (void*)0, 0x0720, 80*25*2); @@ -181,9 +186,8 @@ cbvga_setup(void) SET_VGA(CBmodeinfo.depth, bpp); SET_VGA(CBmodeinfo.cwidth, 8); SET_VGA(CBmodeinfo.cheight, 16); - - // Setup BDA and clear screen. - vga_set_mode(GET_GLOBAL(CBmode), 0); + memcpy_far(get_global_seg(), &CBemulinfo + , get_global_seg(), &CBmodeinfo, sizeof(CBemulinfo)); return 0; } diff --git a/roms/seabios/vgasrc/stdvga.c b/roms/seabios/vgasrc/stdvga.c index 70cceed..00a0fc5 100644 --- a/roms/seabios/vgasrc/stdvga.c +++ b/roms/seabios/vgasrc/stdvga.c @@ -229,11 +229,11 @@ stdvga_vram_ratio(struct vgamode_s *vmode_g) } void -stdvga_set_cursor_shape(u8 start, u8 end) +stdvga_set_cursor_shape(u16 cursor_type) { u16 crtc_addr = stdvga_get_crtc(); - stdvga_crtc_write(crtc_addr, 0x0a, start); - stdvga_crtc_write(crtc_addr, 0x0b, end); + stdvga_crtc_write(crtc_addr, 0x0a, cursor_type >> 8); + stdvga_crtc_write(crtc_addr, 0x0b, cursor_type); } void diff --git a/roms/seabios/vgasrc/stdvga.h b/roms/seabios/vgasrc/stdvga.h index df09bab..39753b4 100644 --- a/roms/seabios/vgasrc/stdvga.h +++ b/roms/seabios/vgasrc/stdvga.h @@ -92,7 +92,7 @@ void stdvga_load_font(u16 seg, void *src_far, u16 count , u16 start, u8 destflags, u8 fontsize); u16 stdvga_get_crtc(void); int stdvga_vram_ratio(struct vgamode_s *vmode_g); -void stdvga_set_cursor_shape(u8 start, u8 end); +void stdvga_set_cursor_shape(u16 cursor_type); void stdvga_set_cursor_pos(int address); void stdvga_set_scan_lines(u8 lines); u16 stdvga_get_vde(void); diff --git a/roms/seabios/vgasrc/stdvgamodes.c b/roms/seabios/vgasrc/stdvgamodes.c index 8436729..c553514 100644 --- a/roms/seabios/vgasrc/stdvgamodes.c +++ b/roms/seabios/vgasrc/stdvgamodes.c @@ -9,7 +9,7 @@ #include "output.h" // warn_internalerror #include "stdvga.h" // stdvga_find_mode #include "string.h" // memcpy_far -#include "vgabios.h" // struct VideoParamTableEntry_s +#include "vgabios.h" // video_param_table /**************************************************************** @@ -363,7 +363,7 @@ stdvga_build_video_param(void) int mode = GET_GLOBAL(parammodes[i]); if (! mode) continue; - struct VideoParam_s *vparam_g = &video_param_table[i]; + struct video_param_s *vparam_g = &video_param_table[i]; struct vgamode_s *vmode_g = stdvga_find_mode(mode); if (!vmode_g) continue; @@ -397,6 +397,15 @@ stdvga_build_video_param(void) , get_global_seg(), GET_GLOBAL(stdmode_g->grdc_regs) , ARRAY_SIZE(vparam_g->grdc_regs)); } + + // Fill available legacy modes in video_func_static table + u32 modes = 0; + for (i = 0; i < ARRAY_SIZE(vga_modes); i++) { + u16 mode = vga_modes[i].mode; + if (mode <= 0x13) + modes |= 1<bx = GET_BDA(vbe_mode); + regs->bx = GET_BDA_EXT(vbe_mode); dprintf(1, "VBE current mode=%x\n", regs->bx); regs->ax = 0x004f; } @@ -247,7 +247,7 @@ vbe_104f05(struct bregs *regs) { if (regs->bh > 1 || regs->bl > 1) goto fail; - if (GET_BDA(vbe_mode) & MF_LINEARFB) { + if (GET_BDA_EXT(vbe_mode) & MF_LINEARFB) { regs->ah = VBE_RETURN_STATUS_INVALID; return; } @@ -382,10 +382,10 @@ vbe_104f10(struct bregs *regs) regs->bx = 0x0f30; break; case 0x01: - SET_BDA(vbe_flag, regs->bh); + MASK_BDA_EXT(flags, BF_PM_MASK, regs->bh & BF_PM_MASK); break; case 0x02: - regs->bh = GET_BDA(vbe_flag); + regs->bh = GET_BDA_EXT(flags) & BF_PM_MASK; break; default: regs->ax = 0x014f; diff --git a/roms/seabios/vgasrc/vgabios.c b/roms/seabios/vgasrc/vgabios.c index e87b7eb..f5abda6 100644 --- a/roms/seabios/vgasrc/vgabios.c +++ b/roms/seabios/vgasrc/vgabios.c @@ -50,37 +50,34 @@ calc_page_size(u8 memmodel, u16 width, u16 height) } } -static void -set_cursor_shape(u8 start, u8 end) -{ - start &= 0x3f; - end &= 0x1f; - - u16 curs = (start << 8) + end; - SET_BDA(cursor_type, curs); - - if (!CONFIG_VGA_STDVGA_PORTS) - return; - - u8 modeset_ctl = GET_BDA(modeset_ctl); +// Determine cursor shape (taking into account possible cursor scaling) +u16 +get_cursor_shape(void) +{ + u16 cursor_type = GET_BDA(cursor_type); + u8 emulate_cursor = (GET_BDA(video_ctl) & 1) == 0; + if (!emulate_cursor) + return cursor_type; + u8 start = (cursor_type >> 8) & 0x3f; + u8 end = cursor_type & 0x1f; u16 cheight = GET_BDA(char_height); - if ((modeset_ctl & 0x01) && (cheight > 8) && (end < 8) && (start < 0x20)) { - if (end != (start + 1)) - start = ((start + 1) * cheight / 8) - 1; - else - start = ((end + 1) * cheight / 8) - 2; - end = ((end + 1) * cheight / 8) - 1; - } - stdvga_set_cursor_shape(start, end); + if (cheight <= 8 || end >= 8 || start >= 0x20) + return cursor_type; + if (end != (start + 1)) + start = ((start + 1) * cheight / 8) - 1; + else + start = ((end + 1) * cheight / 8) - 2; + end = ((end + 1) * cheight / 8) - 1; + return (start << 8) | end; } -static u16 -get_cursor_shape(u8 page) +static void +set_cursor_shape(u16 cursor_type) { - if (page > 7) - return 0; - // FIXME should handle VGA 14/16 lines - return GET_BDA(cursor_type); + vgafb_set_swcursor(0); + SET_BDA(cursor_type, cursor_type); + if (CONFIG_VGA_STDVGA_PORTS) + stdvga_set_cursor_shape(get_cursor_shape()); } static void @@ -92,6 +89,8 @@ set_cursor_pos(struct cursorpos cp) if (page > 7) return; + vgafb_set_swcursor(0); + // Bios cursor pos SET_BDA(cursor_pos[page], (y << 8) | x); @@ -107,7 +106,7 @@ set_cursor_pos(struct cursorpos cp) stdvga_set_cursor_pos((int)text_address(cp)); } -static struct cursorpos +struct cursorpos get_cursor_pos(u8 page) { if (page == 0xff) @@ -117,7 +116,6 @@ get_cursor_pos(u8 page) struct cursorpos cp = { 0, 0, 0xfe }; return cp; } - // FIXME should handle VGA 14/16 lines u16 xy = GET_BDA(cursor_pos[page]); struct cursorpos cp = {xy, xy>>8, page}; return cp; @@ -134,6 +132,8 @@ set_active_page(u8 page) if (!vmode_g) return; + vgafb_set_swcursor(0); + // Calculate memory address of start of page struct cursorpos cp = {0, 0, page}; int address = (int)text_address(cp); @@ -153,16 +153,16 @@ static void set_scan_lines(u8 lines) { stdvga_set_scan_lines(lines); - if (lines == 8) - set_cursor_shape(0x06, 0x07); - else - set_cursor_shape(lines - 4, lines - 3); SET_BDA(char_height, lines); u16 vde = stdvga_get_vde(); u8 rows = vde / lines; SET_BDA(video_rows, rows - 1); u16 cols = GET_BDA(video_cols); SET_BDA(video_pagesize, calc_page_size(MM_TEXT, cols, rows)); + if (lines == 8) + set_cursor_shape(0x0607); + else + set_cursor_shape(((lines - 3) << 8) | (lines - 2)); } @@ -212,19 +212,15 @@ write_teletype(struct cursorpos *pcp, struct carattr ca) if (pcp->y > nbrows) { pcp->y--; - struct vgamode_s *vmode_g = get_current_mode(); - if (!vmode_g) - return; - struct cursorpos dest = {0, 0, pcp->page}; struct cursorpos src = {0, 1, pcp->page}; struct cursorpos size = {GET_BDA(video_cols), nbrows}; - vgafb_move_chars(vmode_g, dest, src, size); + vgafb_move_chars(dest, src, size); struct cursorpos clr = {0, nbrows, pcp->page}; struct carattr attr = {' ', 0, 0}; struct cursorpos clrsize = {GET_BDA(video_cols), 1}; - vgafb_clear_chars(vmode_g, clr, attr, clrsize); + vgafb_clear_chars(clr, attr, clrsize); } } @@ -252,7 +248,7 @@ bda_save_restore(int cmd, u16 seg, void *data) , sizeof(info->bda_0x49)); memcpy_far(seg, info->bda_0x84, SEG_BDA, (void*)0x84 , sizeof(info->bda_0x84)); - SET_FARVAR(seg, info->vbe_mode, GET_BDA(vbe_mode)); + SET_FARVAR(seg, info->vbe_mode, GET_BDA_EXT(vbe_mode)); SET_FARVAR(seg, info->font0, GET_IVT(0x1f)); SET_FARVAR(seg, info->font1, GET_IVT(0x43)); } @@ -261,7 +257,10 @@ bda_save_restore(int cmd, u16 seg, void *data) , sizeof(info->bda_0x49)); memcpy_far(SEG_BDA, (void*)0x84, seg, info->bda_0x84 , sizeof(info->bda_0x84)); - SET_BDA(vbe_mode, GET_FARVAR(seg, info->vbe_mode)); + u16 vbe_mode = GET_FARVAR(seg, info->vbe_mode); + SET_BDA_EXT(vbe_mode, vbe_mode); + struct vgamode_s *vmode_g = vgahw_find_mode(vbe_mode); + SET_BDA_EXT(vgamode_offset, (u32)vmode_g); SET_IVT(0x1f, GET_FARVAR(seg, info->font0)); SET_IVT(0x43, GET_FARVAR(seg, info->font1)); } @@ -276,7 +275,7 @@ bda_save_restore(int cmd, u16 seg, void *data) struct vgamode_s * get_current_mode(void) { - return vgahw_find_mode(GET_BDA(vbe_mode) & ~MF_VBEFLAGS); + return (void*)(GET_BDA_EXT(vgamode_offset)+0); } // Setup BDA after a mode switch. @@ -288,6 +287,8 @@ vga_set_mode(int mode, int flags) if (!vmode_g) return VBE_RETURN_STATUS_FAILED; + vgafb_set_swcursor(0); + int ret = vgahw_set_mode(vmode_g, flags); if (ret) return ret; @@ -301,7 +302,8 @@ vga_set_mode(int mode, int flags) SET_BDA(video_mode, mode); else SET_BDA(video_mode, 0xff); - SET_BDA(vbe_mode, mode | (flags & MF_VBEFLAGS)); + SET_BDA_EXT(vbe_mode, mode | (flags & MF_VBEFLAGS)); + SET_BDA_EXT(vgamode_offset, (u32)vmode_g); if (memmodel == MM_TEXT) { SET_BDA(video_cols, width); SET_BDA(video_rows, height-1); @@ -310,7 +312,7 @@ vga_set_mode(int mode, int flags) int cwidth = GET_GLOBAL(vmode_g->cwidth); SET_BDA(video_cols, width / cwidth); SET_BDA(video_rows, (height / cheight) - 1); - SET_BDA(cursor_type, 0x0000); + SET_BDA(cursor_type, vga_emulate_text() ? 0x0607 : 0x0000); } SET_BDA(video_pagesize, calc_page_size(memmodel, width, height)); SET_BDA(crtc_address, CONFIG_VGA_STDVGA_PORTS ? stdvga_get_crtc() : 0); @@ -324,15 +326,6 @@ vga_set_mode(int mode, int flags) SET_BDA(video_pagestart, 0x0000); SET_BDA(video_page, 0x00); - // FIXME We nearly have the good tables. to be reworked - SET_BDA(dcc_index, 0x08); // 8 is VGA should be ok for now - SET_BDA(video_savetable - , SEGOFF(get_global_seg(), (u32)&video_save_pointer_table)); - - // FIXME - SET_BDA(video_msr, 0x00); // Unavailable on vanilla vga, but... - SET_BDA(video_pal, 0x00); // Unavailable on vanilla vga, but... - // Set the ints 0x1F and 0x43 SET_IVT(0x1f, SEGOFF(get_global_seg(), (u32)&vgafont8[128 * 8])); @@ -379,7 +372,7 @@ handle_1000(struct bregs *regs) static void handle_1001(struct bregs *regs) { - set_cursor_shape(regs->ch, regs->cl); + set_cursor_shape(regs->cx); } static void @@ -392,7 +385,7 @@ handle_1002(struct bregs *regs) static void handle_1003(struct bregs *regs) { - regs->cx = get_cursor_shape(regs->bh); + regs->cx = GET_BDA(cursor_type); struct cursorpos cp = get_cursor_pos(regs->bh); regs->dl = cp.x; regs->dh = cp.y; @@ -426,10 +419,6 @@ verify_scroll(struct bregs *regs, int dir) if (wincols <= 0 || winrows <= 0) return; - struct vgamode_s *vmode_g = get_current_mode(); - if (!vmode_g) - return; - u8 page = GET_BDA(video_page); int clearlines = regs->al, movelines = winrows - clearlines; if (!clearlines || movelines <= 0) { @@ -437,7 +426,7 @@ verify_scroll(struct bregs *regs, int dir) struct cursorpos clr = {ulx, uly, page}; struct carattr attr = {' ', regs->bh, 1}; struct cursorpos clrsize = {wincols, winrows}; - vgafb_clear_chars(vmode_g, clr, attr, clrsize); + vgafb_clear_chars(clr, attr, clrsize); return; } @@ -446,23 +435,23 @@ verify_scroll(struct bregs *regs, int dir) struct cursorpos dest = {ulx, uly, page}; struct cursorpos src = {ulx, uly + clearlines, page}; struct cursorpos size = {wincols, movelines}; - vgafb_move_chars(vmode_g, dest, src, size); + vgafb_move_chars(dest, src, size); struct cursorpos clr = {ulx, uly + movelines, page}; struct carattr attr = {' ', regs->bh, 1}; struct cursorpos clrsize = {wincols, clearlines}; - vgafb_clear_chars(vmode_g, clr, attr, clrsize); + vgafb_clear_chars(clr, attr, clrsize); } else { // Scroll down struct cursorpos dest = {ulx, uly + clearlines, page}; struct cursorpos src = {ulx, uly, page}; struct cursorpos size = {wincols, movelines}; - vgafb_move_chars(vmode_g, dest, src, size); + vgafb_move_chars(dest, src, size); struct cursorpos clr = {ulx, uly, page}; struct carattr attr = {' ', regs->bh, 1}; struct cursorpos clrsize = {wincols, clearlines}; - vgafb_clear_chars(vmode_g, clr, attr, clrsize); + vgafb_clear_chars(clr, attr, clrsize); } } @@ -985,9 +974,7 @@ handle_101233(struct bregs *regs) static void handle_101234(struct bregs *regs) { - u8 v = (regs->al & 0x01) ^ 0x01; - u8 v2 = GET_BDA(modeset_ctl) & ~0x01; - SET_BDA(modeset_ctl, v | v2); + SET_BDA(video_ctl, (GET_BDA(video_ctl) & ~0x01) | (regs->al & 0x01)); regs->al = 0x12; } @@ -1096,51 +1083,23 @@ handle_101a(struct bregs *regs) } -static u8 static_functionality[0x10] VAR16 = { - /* 0 */ 0xff, // All modes supported #1 - /* 1 */ 0xe0, // All modes supported #2 - /* 2 */ 0x0f, // All modes supported #3 - /* 3 */ 0x00, 0x00, 0x00, 0x00, // reserved - /* 7 */ 0x07, // 200, 350, 400 scan lines - /* 8 */ 0x02, // mamimum number of visible charsets in text mode - /* 9 */ 0x08, // total number of charset blocks in text mode - /* a */ 0xe7, // Change to add new functions - /* b */ 0x0c, // Change to add new functions - /* c */ 0x00, // reserved - /* d */ 0x00, // reserved - /* e */ 0x00, // Change to add new functions - /* f */ 0x00 // reserved +struct video_func_static static_functionality VAR16 = { + .modes = 0x00, // Filled in by stdvga_build_video_param() + .scanlines = 0x07, // 200, 350, 400 scan lines + .cblocks = 0x02, // mamimum number of visible charsets in text mode + .active_cblocks = 0x08, // total number of charset blocks in text mode + .misc_flags = 0x0ce7, }; -struct funcInfo { - struct segoff_s static_functionality; - u8 bda_0x49[30]; - u8 bda_0x84[3]; - u8 dcc_index; - u8 dcc_alt; - u16 colors; - u8 pages; - u8 scan_lines; - u8 primary_char; - u8 secondar_char; - u8 misc; - u8 non_vga_mode; - u8 reserved_2f[2]; - u8 video_mem; - u8 save_flags; - u8 disp_info; - u8 reserved_34[12]; -} PACKED; - static void handle_101b(struct bregs *regs) { u16 seg = regs->es; - struct funcInfo *info = (void*)(regs->di+0); + struct video_func_info *info = (void*)(regs->di+0); memset_far(seg, info, 0, sizeof(*info)); // Address of static functionality table SET_FARVAR(seg, info->static_functionality - , SEGOFF(get_global_seg(), (u32)static_functionality)); + , SEGOFF(get_global_seg(), (u32)&static_functionality)); // Hard coded copy from BIOS area. Should it be cleaner ? memcpy_far(seg, info->bda_0x49, SEG_BDA, (void*)0x49 diff --git a/roms/seabios/vgasrc/vgabios.h b/roms/seabios/vgasrc/vgabios.h index 02cf3e3..fd796f2 100644 --- a/roms/seabios/vgasrc/vgabios.h +++ b/roms/seabios/vgasrc/vgabios.h @@ -1,23 +1,10 @@ #ifndef __VGABIOS_H #define __VGABIOS_H +#include "config.h" // CONFIG_VGA_EMULATE_TEXT #include "types.h" // u8 #include "farptr.h" // struct segoff_s - -// standard BIOS Video Parameter Table -struct VideoParam_s { - u8 twidth; - u8 theightm1; - u8 cheight; - u16 slength; - u8 sequ_regs[4]; - u8 miscreg; - u8 crtc_regs[25]; - u8 actl_regs[20]; - u8 grdc_regs[9]; -} PACKED; - -extern struct VideoParam_s video_param_table[29]; +#include "std/vga.h" // struct video_param_s // Save/Restore flags #define SR_HARDWARE 0x0001 @@ -75,6 +62,30 @@ struct gfx_op { #define GO_MEMSET 3 #define GO_MEMMOVE 4 +// Custom internal storage in BDA +#define VGA_CUSTOM_BDA 0xb9 + +struct vga_bda_s { + u8 flags; + u16 vbe_mode; + u16 vgamode_offset; +} PACKED; + +#define BF_PM_MASK 0x0f +#define BF_EMULATE_TEXT 0x10 +#define BF_SWCURSOR 0x20 + +#define GET_BDA_EXT(var) \ + GET_FARVAR(SEG_BDA, ((struct vga_bda_s *)VGA_CUSTOM_BDA)->var) +#define SET_BDA_EXT(var, val) \ + SET_FARVAR(SEG_BDA, ((struct vga_bda_s *)VGA_CUSTOM_BDA)->var, (val)) +#define MASK_BDA_EXT(var, off, on) \ + SET_BDA_EXT(var, (GET_BDA_EXT(var) & ~(off)) | (on)) + +static inline int vga_emulate_text(void) { + return CONFIG_VGA_EMULATE_TEXT && GET_BDA_EXT(flags) & BF_EMULATE_TEXT; +} + // Debug settings #define DEBUG_VGA_POST 1 #define DEBUG_VGA_10 3 @@ -87,7 +98,8 @@ extern u8 vgafont14alt[]; extern u8 vgafont16alt[]; // vgainit.c -extern struct VideoSavePointer_s video_save_pointer_table; +extern struct video_save_pointer_s video_save_pointer_table; +extern struct video_param_s video_param_table[29]; // vgabios.c extern int VgaBDF; @@ -101,22 +113,26 @@ struct cursorpos { }; int vga_bpp(struct vgamode_s *vmode_g); u16 calc_page_size(u8 memmodel, u16 width, u16 height); +u16 get_cursor_shape(void); +struct cursorpos get_cursor_pos(u8 page); int bda_save_restore(int cmd, u16 seg, void *data); struct vgamode_s *get_current_mode(void); int vga_set_mode(int mode, int flags); +extern struct video_func_static static_functionality; // vgafb.c void init_gfx_op(struct gfx_op *op, struct vgamode_s *vmode_g); void handle_gfx_op(struct gfx_op *op); void *text_address(struct cursorpos cp); -void vgafb_move_chars(struct vgamode_s *vmode_g, struct cursorpos dest +void vgafb_move_chars(struct cursorpos dest , struct cursorpos src, struct cursorpos movesize); -void vgafb_clear_chars(struct vgamode_s *vmode_g, struct cursorpos dest +void vgafb_clear_chars(struct cursorpos dest , struct carattr ca, struct cursorpos movesize); void vgafb_write_char(struct cursorpos cp, struct carattr ca); struct carattr vgafb_read_char(struct cursorpos cp); void vgafb_write_pixel(u8 color, u16 x, u16 y); u8 vgafb_read_pixel(u16 x, u16 y); +void vgafb_set_swcursor(int enable); // vbe.c extern u32 VBE_total_memory; diff --git a/roms/seabios/vgasrc/vgaentry.S b/roms/seabios/vgasrc/vgaentry.S index 11197f1..f9cf656 100644 --- a/roms/seabios/vgasrc/vgaentry.S +++ b/roms/seabios/vgasrc/vgaentry.S @@ -15,7 +15,7 @@ ****************************************************************/ .section .rom.header - .code16gcc + .code16 .global _rom_header, _rom_header_size, _rom_header_checksum _rom_header: .word 0xaa55 @@ -112,18 +112,10 @@ entry_10_extrastack: pushw %ds // Set %ds:%eax to space on ExtraStack pushl %eax movw %cs:ExtraStackSeg, %ds - movl $(CONFIG_VGA_EXTRA_STACK_SIZE-BREGS_size-8), %eax - popl BREGS_eax(%eax) // Backup registers - popw BREGS_ds(%eax) - movl %edi, BREGS_edi(%eax) - movl %esi, BREGS_esi(%eax) - movl %ebp, BREGS_ebp(%eax) - movl %ebx, BREGS_ebx(%eax) - movl %edx, BREGS_edx(%eax) - movl %ecx, BREGS_ecx(%eax) - movw %es, BREGS_es(%eax) - movl %esp, BREGS_size+0(%eax) - movw %ss, BREGS_size+4(%eax) + movl $(CONFIG_VGA_EXTRA_STACK_SIZE-PUSHBREGS_size-16), %eax + SAVEBREGS_POP_DSEAX // Save registers on extra stack + movl %esp, PUSHBREGS_size+8(%eax) + movw %ss, PUSHBREGS_size+12(%eax) popl BREGS_code(%eax) popw BREGS_flags(%eax) @@ -133,21 +125,41 @@ entry_10_extrastack: VGA_CALLL handle_10 movl %esp, %eax // Restore registers and return - movw BREGS_size+4(%eax), %ss - movl BREGS_size+0(%eax), %esp + movw PUSHBREGS_size+12(%eax), %ss + movl PUSHBREGS_size+8(%eax), %esp popl %edx popw %dx pushw BREGS_flags(%eax) pushl BREGS_code(%eax) - movl BREGS_edi(%eax), %edi - movl BREGS_esi(%eax), %esi - movl BREGS_ebp(%eax), %ebp - movl BREGS_ebx(%eax), %ebx - movl BREGS_edx(%eax), %edx - movl BREGS_ecx(%eax), %ecx - movw BREGS_es(%eax), %es - pushw BREGS_ds(%eax) - pushl BREGS_eax(%eax) - popl %eax - popw %ds + RESTOREBREGS_DSEAX iretw + + // Timer irq handling + DECLFUNC entry_timer_hook +entry_timer_hook: + ENTRY handle_timer_hook + ljmpw *%cs:Timer_Hook_Resume + + // Timer irq handling on extra stack + DECLFUNC entry_timer_hook_extrastack +entry_timer_hook_extrastack: + cli + cld + pushw %ds // Set %ds:%eax to space on ExtraStack + pushl %eax + movw %cs:ExtraStackSeg, %ds + movl $(CONFIG_VGA_EXTRA_STACK_SIZE-PUSHBREGS_size-8), %eax + SAVEBREGS_POP_DSEAX + movl %esp, PUSHBREGS_size(%eax) + movw %ss, PUSHBREGS_size+4(%eax) + + movw %ds, %dx // Setup %ss/%esp and call function + movw %dx, %ss + movl %eax, %esp + calll handle_timer_hook + + movl %esp, %eax // Restore registers and return + movw PUSHBREGS_size+4(%eax), %ss + movl PUSHBREGS_size(%eax), %esp + RESTOREBREGS_DSEAX + ljmpw *%cs:Timer_Hook_Resume diff --git a/roms/seabios/vgasrc/vgafb.c b/roms/seabios/vgasrc/vgafb.c index fdc525d..1a94fcf 100644 --- a/roms/seabios/vgasrc/vgafb.c +++ b/roms/seabios/vgasrc/vgafb.c @@ -236,9 +236,31 @@ get_color(int depth, u8 attr) int r = (attr&4) ? 2 : 0, g = (attr&2) ? 2 : 0, b = (attr&1) ? 2 : 0; if ((attr & 0xf) == 6) g = 1; - return ((((((1<> (gbits+bbits)) & ((1<> bbits) & ((1<x * bypp); switch (op->op) { default: - case GO_READ8: - // XXX - not implemented. + case GO_READ8: { + u8 data[64]; + memcpy_high(MAKE_FLATPTR(GET_SEG(SS), data), dest_far, bypp * 8); + int i; + for (i=0; i<8; i++) + op->pixels[i] = reverse_color(depth, *(u32*)&data[i*bypp]); break; + } case GO_WRITE8: { u8 data[64]; int i; @@ -340,7 +367,7 @@ gfx_move_chars(struct vgamode_s *vmode_g, struct cursorpos dest handle_gfx_op(&op); } -// Clear are of screen in graphics mode. +// Clear area of screen in graphics mode. static void gfx_clear_chars(struct vgamode_s *vmode_g, struct cursorpos dest , struct carattr ca, struct cursorpos clearsize) @@ -353,6 +380,8 @@ gfx_clear_chars(struct vgamode_s *vmode_g, struct cursorpos dest op.y = dest.y * cheight; op.ylen = clearsize.y * cheight; op.pixels[0] = ca.attr; + if (vga_emulate_text()) + op.pixels[0] = ca.attr >> 4; op.op = GO_MEMSET; handle_gfx_op(&op); } @@ -387,7 +416,25 @@ gfx_write_char(struct vgamode_s *vmode_g op.x = cp.x * 8; int cheight = GET_BDA(char_height); op.y = cp.y * cheight; - int usexor = ca.attr & 0x80 && GET_GLOBAL(vmode_g->depth) < 8; + u8 fgattr = ca.attr, bgattr = 0x00; + int usexor = 0; + if (vga_emulate_text()) { + if (ca.use_attr) { + bgattr = fgattr >> 4; + fgattr = fgattr & 0x0f; + } else { + // Read bottom right pixel of the cell to guess bg color + op.op = GO_READ8; + op.y += cheight-1; + handle_gfx_op(&op); + op.y -= cheight-1; + bgattr = op.pixels[7]; + fgattr = bgattr ^ 0x7; + } + } else if (fgattr & 0x80 && GET_GLOBAL(vmode_g->depth) < 8) { + usexor = 1; + fgattr &= 0x7f; + } int i; for (i = 0; i < cheight; i++, op.y++) { u8 fontline = GET_FARVAR(font.seg, *(u8*)(font.offset+i)); @@ -396,17 +443,91 @@ gfx_write_char(struct vgamode_s *vmode_g handle_gfx_op(&op); int j; for (j = 0; j < 8; j++) - op.pixels[j] ^= (fontline & (0x80>>j)) ? (ca.attr & 0x7f) : 0x00; + op.pixels[j] ^= (fontline & (0x80>>j)) ? fgattr : 0x00; } else { int j; for (j = 0; j < 8; j++) - op.pixels[j] = (fontline & (0x80>>j)) ? ca.attr : 0x00; + op.pixels[j] = (fontline & (0x80>>j)) ? fgattr : bgattr; } op.op = GO_WRITE8; handle_gfx_op(&op); } } +// Read a character from the screen in graphics mode. +static struct carattr +gfx_read_char(struct vgamode_s *vmode_g, struct cursorpos cp) +{ + u8 lines[16]; + int cheight = GET_BDA(char_height); + if (cp.x >= GET_BDA(video_cols) || cheight > ARRAY_SIZE(lines)) + goto fail; + + // Read cell from screen + struct gfx_op op; + init_gfx_op(&op, vmode_g); + op.op = GO_READ8; + op.x = cp.x * 8; + op.y = cp.y * cheight; + int car = 0; + u8 fgattr = 0x00, bgattr = 0x00; + if (vga_emulate_text()) { + // Read bottom right pixel of the cell to guess bg color + op.y += cheight-1; + handle_gfx_op(&op); + op.y -= cheight-1; + bgattr = op.pixels[7]; + fgattr = bgattr ^ 0x7; + // Report space character for blank cells (skip null character check) + car = 1; + } + u8 i, j; + for (i=0; i> j; + fgattr = op.pixels[j]; + } + lines[i] = line; + } + + // Determine font + for (; car<256; car++) { + struct segoff_s font = get_font_data(car); + if (memcmp_far(GET_SEG(SS), lines + , font.seg, (void*)(font.offset+0), cheight) == 0) + return (struct carattr){car, fgattr | (bgattr << 4), 0}; + } +fail: + return (struct carattr){0, 0, 0}; +} + +// Draw/undraw a cursor on the framebuffer by xor'ing the cursor cell +void +gfx_set_swcursor(struct vgamode_s *vmode_g, int enable, struct cursorpos cp) +{ + u16 cursor_type = get_cursor_shape(); + u8 start = cursor_type >> 8, end = cursor_type & 0xff; + struct gfx_op op; + init_gfx_op(&op, vmode_g); + op.x = cp.x * 8; + int cheight = GET_BDA(char_height); + op.y = cp.y * cheight + start; + + int i; + for (i = start; i < cheight && i <= end; i++, op.y++) { + op.op = GO_READ8; + handle_gfx_op(&op); + int j; + for (j = 0; j < 8; j++) + op.pixels[j] ^= 0x07; + op.op = GO_WRITE8; + handle_gfx_op(&op); + } +} + // Set the pixel at the given position. void vgafb_write_pixel(u8 color, u16 x, u16 y) @@ -414,6 +535,7 @@ vgafb_write_pixel(u8 color, u16 x, u16 y) struct vgamode_s *vmode_g = get_current_mode(); if (!vmode_g) return; + vgafb_set_swcursor(0); struct gfx_op op; init_gfx_op(&op, vmode_g); @@ -438,6 +560,7 @@ vgafb_read_pixel(u16 x, u16 y) struct vgamode_s *vmode_g = get_current_mode(); if (!vmode_g) return 0; + vgafb_set_swcursor(0); struct gfx_op op; init_gfx_op(&op, vmode_g); @@ -465,9 +588,14 @@ text_address(struct cursorpos cp) // Move characters on screen. void -vgafb_move_chars(struct vgamode_s *vmode_g, struct cursorpos dest +vgafb_move_chars(struct cursorpos dest , struct cursorpos src, struct cursorpos movesize) { + struct vgamode_s *vmode_g = get_current_mode(); + if (!vmode_g) + return; + vgafb_set_swcursor(0); + if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) { gfx_move_chars(vmode_g, dest, src, movesize); return; @@ -479,11 +607,16 @@ vgafb_move_chars(struct vgamode_s *vmode_g, struct cursorpos dest , movesize.x * 2, stride, movesize.y); } -// Clear are of screen. +// Clear area of screen. void -vgafb_clear_chars(struct vgamode_s *vmode_g, struct cursorpos dest +vgafb_clear_chars(struct cursorpos dest , struct carattr ca, struct cursorpos clearsize) { + struct vgamode_s *vmode_g = get_current_mode(); + if (!vmode_g) + return; + vgafb_set_swcursor(0); + if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) { gfx_clear_chars(vmode_g, dest, ca, clearsize); return; @@ -502,6 +635,7 @@ vgafb_write_char(struct cursorpos cp, struct carattr ca) struct vgamode_s *vmode_g = get_current_mode(); if (!vmode_g) return; + vgafb_set_swcursor(0); if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) { gfx_write_char(vmode_g, cp, ca); @@ -523,20 +657,46 @@ vgafb_read_char(struct cursorpos cp) { struct vgamode_s *vmode_g = get_current_mode(); if (!vmode_g) - goto fail; + return (struct carattr){0, 0, 0}; + vgafb_set_swcursor(0); - if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) { - // FIXME gfx mode - dprintf(1, "Read char in graphics mode\n"); - goto fail; - } + if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) + return gfx_read_char(vmode_g, cp); u16 *dest_far = text_address(cp); u16 v = GET_FARVAR(GET_GLOBAL(vmode_g->sstart), *dest_far); - struct carattr ca = {v, v>>8, 0}; - return ca; + return (struct carattr){v, v>>8, 0}; +} + +// Draw/undraw a cursor on the screen +void +vgafb_set_swcursor(int enable) +{ + if (!vga_emulate_text()) + return; + u8 flags = GET_BDA_EXT(flags); + if (!!(flags & BF_SWCURSOR) == enable) + // Already in requested mode. + return; + struct vgamode_s *vmode_g = get_current_mode(); + if (!vmode_g) + return; + struct cursorpos cp = get_cursor_pos(0xff); + if (cp.x >= GET_BDA(video_cols) || cp.y > GET_BDA(video_rows) + || GET_BDA(cursor_type) >= 0x2000) + // Cursor not visible + return; + + SET_BDA_EXT(flags, (flags & ~BF_SWCURSOR) | (enable ? BF_SWCURSOR : 0)); + + if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) { + gfx_set_swcursor(vmode_g, enable, cp); + return; + } -fail: ; - struct carattr ca2 = {0, 0, 0}; - return ca2; + // In text mode, swap foreground and background attributes for cursor + void *dest_far = text_address(cp) + 1; + u8 attr = GET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u8*)dest_far); + attr = (attr >> 4) | (attr << 4); + SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u8*)dest_far, attr); } diff --git a/roms/seabios/vgasrc/vgainit.c b/roms/seabios/vgasrc/vgainit.c index 9d6dd1e..8d12261 100644 --- a/roms/seabios/vgasrc/vgainit.c +++ b/roms/seabios/vgasrc/vgainit.c @@ -15,22 +15,12 @@ #include "std/pmm.h" // struct pmmheader #include "string.h" // checksum_far #include "util.h" // VERSION -#include "vgabios.h" // struct VideoSavePointer_s +#include "vgabios.h" // video_save_pointer_table #include "vgahw.h" // vgahw_setup -// Standard Video Save Pointer Table -struct VideoSavePointer_s { - struct segoff_s videoparam; - struct segoff_s paramdynamicsave; - struct segoff_s textcharset; - struct segoff_s graphcharset; - struct segoff_s secsavepointer; - u8 reserved[8]; -} PACKED; +struct video_save_pointer_s video_save_pointer_table VAR16; -struct VideoSavePointer_s video_save_pointer_table VAR16; - -struct VideoParam_s video_param_table[29] VAR16; +struct video_param_s video_param_table[29] VAR16; // Type of emulator platform - for dprintf with certain compile options. int PlatformRunningOn VAR16; @@ -98,6 +88,38 @@ allocate_extra_stack(void) /**************************************************************** + * Timer hook + ****************************************************************/ + +struct segoff_s Timer_Hook_Resume VAR16 VISIBLE16; + +void VISIBLE16 +handle_timer_hook(void) +{ + if (!vga_emulate_text()) + return; + vgafb_set_swcursor(GET_BDA(timer_counter) % 18 < 9); +} + +static void +hook_timer_irq(void) +{ + if (!CONFIG_VGA_EMULATE_TEXT) + return; + extern void entry_timer_hook(void); + extern void entry_timer_hook_extrastack(void); + struct segoff_s oldirq = GET_IVT(0x08); + struct segoff_s newirq = SEGOFF(get_global_seg(), (u32)entry_timer_hook); + if (CONFIG_VGA_ALLOCATE_EXTRA_STACK && GET_GLOBAL(ExtraStackSeg)) + newirq = SEGOFF(get_global_seg(), (u32)entry_timer_hook_extrastack); + dprintf(1, "Hooking hardware timer irq (old=%x new=%x)\n" + , oldirq.segoff, newirq.segoff); + SET_VGA(Timer_Hook_Resume, oldirq); + SET_IVT(0x08, newirq); +} + + +/**************************************************************** * VGA post ****************************************************************/ @@ -108,20 +130,16 @@ init_bios_area(void) // set 80x25 color (not clear from RBIL but usual) set_equipment_flags(0x30, 0x20); - // the default char height - SET_BDA(char_height, 0x10); - - // Clear the screen - SET_BDA(video_ctl, 0x60); - - // Set the basic screen we have - SET_BDA(video_switches, 0xf9); - // Set the basic modeset options SET_BDA(modeset_ctl, 0x51); - // Set the default MSR - SET_BDA(video_msr, 0x09); + SET_BDA(dcc_index, CONFIG_VGA_STDVGA_PORTS ? 0x08 : 0xff); + SET_BDA(video_savetable + , SEGOFF(get_global_seg(), (u32)&video_save_pointer_table)); + + // FIXME + SET_BDA(video_msr, 0x00); // Unavailable on vanilla vga, but... + SET_BDA(video_pal, 0x00); // Unavailable on vanilla vga, but... } int VgaBDF VAR16 = -1; @@ -164,6 +182,8 @@ vga_post(struct bregs *regs) allocate_extra_stack(); + hook_timer_irq(); + SET_VGA(HaveRunInit, 1); // Fixup checksum diff --git a/roms/seabios/vgasrc/vgalayout.lds.S b/roms/seabios/vgasrc/vgalayout.lds.S index 08a5f32..533734d 100644 --- a/roms/seabios/vgasrc/vgalayout.lds.S +++ b/roms/seabios/vgasrc/vgalayout.lds.S @@ -13,8 +13,7 @@ SECTIONS KEEP(*(.rom.header)) *(.text.*) _rodata = . ; - *(.rodata.__func__.*) - *(.rodata.str1.1) + *(.rodata*) *(.data16.*) } diff --git a/rules.mak b/rules.mak index f500fef..3a05627 100644 --- a/rules.mak +++ b/rules.mak @@ -326,7 +326,17 @@ define unnest-vars $(if $1,$(call fix-paths,$1/,,$2)) # Descend and include every subdir Makefile.objs - $(foreach v, $2, $(call unnest-var-recursive,$1,$2,$v)) + $(foreach v, $2, + $(call unnest-var-recursive,$1,$2,$v) + # Pass the .mo-cflags and .mo-libs along to its member objects + $(foreach o, $(filter %.mo,$($v)), + $(foreach p,$($o-objs), + $(if $($o-cflags), $(eval $p-cflags += $($o-cflags))) + $(if $($o-libs), $(eval $p-libs += $($o-libs)))))) + + # For all %.mo objects that are directly added into -y, just expand them + $(foreach v,$(filter %-y,$2), + $(eval $v := $(foreach o,$($v),$(if $($o-objs),$($o-objs),$o)))) $(foreach v,$(filter %-m,$2), # All .o found in *-m variables are single object modules, create .mo @@ -353,18 +363,9 @@ define unnest-vars # according to .mo-objs. Report error if not set $(if $($o-objs), $(eval $(o:%.mo=%$(DSOSUF)): module-common.o $($o-objs)), - $(error $o added in $v but $o-objs is not set)) - # Pass the .mo-cflags and .mo-libs along to member objects - $(foreach p,$($o-objs), - $(if $($o-cflags), $(eval $p-cflags += $($o-cflags))) - $(if $($o-libs), $(eval $p-libs += $($o-libs))))) + $(error $o added in $v but $o-objs is not set))) $(shell mkdir -p ./ $(sort $(dir $($v)))) # Include all the .d files $(eval -include $(addsuffix *.d, $(sort $(dir $($v))))) $(eval $v := $(filter-out %/,$($v)))) - - # For all %.mo objects that are directly added into -y, expand them to %.mo-objs - $(foreach v,$2, - $(eval $v := $(foreach o,$($v),$(if $($o-objs),$($o-objs),$o)))) - endef diff --git a/savevm.c b/savevm.c index 08ec678..3b0e222 100644 --- a/savevm.c +++ b/savevm.c @@ -572,14 +572,34 @@ static int vmstate_load(QEMUFile *f, SaveStateEntry *se, int version_id) return vmstate_load_state(f, se->vmsd, se->opaque, version_id); } -static void vmstate_save(QEMUFile *f, SaveStateEntry *se) +static void vmstate_save_old_style(QEMUFile *f, SaveStateEntry *se, QJSON *vmdesc) +{ + int64_t old_offset, size; + + old_offset = qemu_ftell_fast(f); + se->ops->save_state(f, se->opaque); + size = qemu_ftell_fast(f) - old_offset; + + if (vmdesc) { + json_prop_int(vmdesc, "size", size); + json_start_array(vmdesc, "fields"); + json_start_object(vmdesc, NULL); + json_prop_str(vmdesc, "name", "data"); + json_prop_int(vmdesc, "size", size); + json_prop_str(vmdesc, "type", "buffer"); + json_end_object(vmdesc); + json_end_array(vmdesc); + } +} + +static void vmstate_save(QEMUFile *f, SaveStateEntry *se, QJSON *vmdesc) { trace_vmstate_save(se->idstr, se->vmsd ? se->vmsd->name : "(old)"); - if (!se->vmsd) { /* Old style */ - se->ops->save_state(f, se->opaque); + if (!se->vmsd) { + vmstate_save_old_style(f, se, vmdesc); return; } - vmstate_save_state(f, se->vmsd, se->opaque); + vmstate_save_state(f, se->vmsd, se->opaque, vmdesc); } bool qemu_savevm_state_blocked(Error **errp) @@ -674,7 +694,7 @@ int qemu_savevm_state_iterate(QEMUFile *f) qemu_put_be32(f, se->section_id); ret = se->ops->save_live_iterate(f, se->opaque); - trace_savevm_section_end(se->idstr, se->section_id); + trace_savevm_section_end(se->idstr, se->section_id, ret); if (ret < 0) { qemu_file_set_error(f, ret); @@ -690,8 +710,16 @@ int qemu_savevm_state_iterate(QEMUFile *f) return ret; } +static bool should_send_vmdesc(void) +{ + MachineState *machine = MACHINE(qdev_get_machine()); + return !machine->suppress_vmdesc; +} + void qemu_savevm_state_complete(QEMUFile *f) { + QJSON *vmdesc; + int vmdesc_len; SaveStateEntry *se; int ret; @@ -714,13 +742,16 @@ void qemu_savevm_state_complete(QEMUFile *f) qemu_put_be32(f, se->section_id); ret = se->ops->save_live_complete(f, se->opaque); - trace_savevm_section_end(se->idstr, se->section_id); + trace_savevm_section_end(se->idstr, se->section_id, ret); if (ret < 0) { qemu_file_set_error(f, ret); return; } } + vmdesc = qjson_new(); + json_prop_int(vmdesc, "page_size", TARGET_PAGE_SIZE); + json_start_array(vmdesc, "devices"); QTAILQ_FOREACH(se, &savevm_handlers, entry) { int len; @@ -728,6 +759,11 @@ void qemu_savevm_state_complete(QEMUFile *f) continue; } trace_savevm_section_start(se->idstr, se->section_id); + + json_start_object(vmdesc, NULL); + json_prop_str(vmdesc, "name", se->idstr); + json_prop_int(vmdesc, "instance_id", se->instance_id); + /* Section type */ qemu_put_byte(f, QEMU_VM_SECTION_FULL); qemu_put_be32(f, se->section_id); @@ -740,11 +776,25 @@ void qemu_savevm_state_complete(QEMUFile *f) qemu_put_be32(f, se->instance_id); qemu_put_be32(f, se->version_id); - vmstate_save(f, se); - trace_savevm_section_end(se->idstr, se->section_id); + vmstate_save(f, se, vmdesc); + + json_end_object(vmdesc); + trace_savevm_section_end(se->idstr, se->section_id, 0); } qemu_put_byte(f, QEMU_VM_EOF); + + json_end_array(vmdesc); + qjson_finish(vmdesc); + vmdesc_len = strlen(qjson_get_str(vmdesc)); + + if (should_send_vmdesc()) { + qemu_put_byte(f, QEMU_VM_VMDESCRIPTION); + qemu_put_be32(f, vmdesc_len); + qemu_put_buffer(f, (uint8_t *)qjson_get_str(vmdesc), vmdesc_len); + } + object_unref(OBJECT(vmdesc)); + qemu_fflush(f); } @@ -779,7 +829,7 @@ void qemu_savevm_state_cancel(void) } } -static int qemu_savevm_state(QEMUFile *f) +static int qemu_savevm_state(QEMUFile *f, Error **errp) { int ret; MigrationParams params = { @@ -787,7 +837,7 @@ static int qemu_savevm_state(QEMUFile *f) .shared = 0 }; - if (qemu_savevm_state_blocked(NULL)) { + if (qemu_savevm_state_blocked(errp)) { return -EINVAL; } @@ -808,6 +858,7 @@ static int qemu_savevm_state(QEMUFile *f) } if (ret != 0) { qemu_savevm_state_cancel(); + error_setg_errno(errp, -ret, "Error while writing VM state"); } return ret; } @@ -843,7 +894,7 @@ static int qemu_save_device_state(QEMUFile *f) qemu_put_be32(f, se->instance_id); qemu_put_be32(f, se->version_id); - vmstate_save(f, se); + vmstate_save(f, se, NULL); } qemu_put_byte(f, QEMU_VM_EOF); @@ -883,25 +934,30 @@ int qemu_loadvm_state(QEMUFile *f) QLIST_HEAD(, LoadStateEntry) loadvm_handlers = QLIST_HEAD_INITIALIZER(loadvm_handlers); LoadStateEntry *le, *new_le; + Error *local_err = NULL; uint8_t section_type; unsigned int v; int ret; + int file_error_after_eof = -1; - if (qemu_savevm_state_blocked(NULL)) { + if (qemu_savevm_state_blocked(&local_err)) { + error_report_err(local_err); return -EINVAL; } v = qemu_get_be32(f); if (v != QEMU_VM_FILE_MAGIC) { + error_report("Not a migration stream"); return -EINVAL; } v = qemu_get_be32(f); if (v == QEMU_VM_FILE_VERSION_COMPAT) { - fprintf(stderr, "SaveVM v2 format is obsolete and don't work anymore\n"); + error_report("SaveVM v2 format is obsolete and don't work anymore"); return -ENOTSUP; } if (v != QEMU_VM_FILE_VERSION) { + error_report("Unsupported migration stream version"); return -ENOTSUP; } @@ -911,6 +967,7 @@ int qemu_loadvm_state(QEMUFile *f) char idstr[257]; int len; + trace_qemu_loadvm_state_section(section_type); switch (section_type) { case QEMU_VM_SECTION_START: case QEMU_VM_SECTION_FULL: @@ -922,18 +979,21 @@ int qemu_loadvm_state(QEMUFile *f) instance_id = qemu_get_be32(f); version_id = qemu_get_be32(f); + trace_qemu_loadvm_state_section_startfull(section_id, idstr, + instance_id, version_id); /* Find savevm section */ se = find_se(idstr, instance_id); if (se == NULL) { - fprintf(stderr, "Unknown savevm section or instance '%s' %d\n", idstr, instance_id); + error_report("Unknown savevm section or instance '%s' %d", + idstr, instance_id); ret = -EINVAL; goto out; } /* Validate version */ if (version_id > se->version_id) { - fprintf(stderr, "savevm: unsupported version %d for '%s' v%d\n", - version_id, idstr, se->version_id); + error_report("savevm: unsupported version %d for '%s' v%d", + version_id, idstr, se->version_id); ret = -EINVAL; goto out; } @@ -948,8 +1008,8 @@ int qemu_loadvm_state(QEMUFile *f) ret = vmstate_load(f, le->se, le->version_id); if (ret < 0) { - fprintf(stderr, "qemu: warning: error while loading state for instance 0x%x of device '%s'\n", - instance_id, idstr); + error_report("error while loading state for instance 0x%x of" + " device '%s'", instance_id, idstr); goto out; } break; @@ -957,31 +1017,50 @@ int qemu_loadvm_state(QEMUFile *f) case QEMU_VM_SECTION_END: section_id = qemu_get_be32(f); + trace_qemu_loadvm_state_section_partend(section_id); QLIST_FOREACH(le, &loadvm_handlers, entry) { if (le->section_id == section_id) { break; } } if (le == NULL) { - fprintf(stderr, "Unknown savevm section %d\n", section_id); + error_report("Unknown savevm section %d", section_id); ret = -EINVAL; goto out; } ret = vmstate_load(f, le->se, le->version_id); if (ret < 0) { - fprintf(stderr, "qemu: warning: error while loading state section id %d\n", - section_id); + error_report("error while loading state section id %d(%s)", + section_id, le->se->idstr); goto out; } break; default: - fprintf(stderr, "Unknown savevm section type %d\n", section_type); + error_report("Unknown savevm section type %d", section_type); ret = -EINVAL; goto out; } } + file_error_after_eof = qemu_file_get_error(f); + + /* + * Try to read in the VMDESC section as well, so that dumping tools that + * intercept our migration stream have the chance to see it. + */ + if (qemu_get_byte(f) == QEMU_VM_VMDESCRIPTION) { + uint32_t size = qemu_get_be32(f); + uint8_t *buf = g_malloc(0x1000); + + while (size > 0) { + uint32_t read_chunk = MIN(size, 0x1000); + qemu_get_buffer(f, buf, read_chunk); + size -= read_chunk; + } + g_free(buf); + } + cpu_synchronize_all_post_init(); ret = 0; @@ -993,7 +1072,8 @@ out: } if (ret == 0) { - ret = qemu_file_get_error(f); + /* We may not have a VMDESC section, so ignore relative errors */ + ret = file_error_after_eof; } return ret; @@ -1039,7 +1119,7 @@ static int del_existing_snapshots(Monitor *mon, const char *name) return 0; } -void do_savevm(Monitor *mon, const QDict *qdict) +void hmp_savevm(Monitor *mon, const QDict *qdict) { BlockDriverState *bs, *bs1; QEMUSnapshotInfo sn1, *sn = &sn1, old_sn1, *old_sn = &old_sn1; @@ -1050,6 +1130,7 @@ void do_savevm(Monitor *mon, const QDict *qdict) qemu_timeval tv; struct tm tm; const char *name = qdict_get_try_str(qdict, "name"); + Error *local_err = NULL; /* Verify if there is a device that doesn't support snapshots and is writable */ bs = NULL; @@ -1108,11 +1189,12 @@ void do_savevm(Monitor *mon, const QDict *qdict) monitor_printf(mon, "Could not open VM state file\n"); goto the_end; } - ret = qemu_savevm_state(f); + ret = qemu_savevm_state(f, &local_err); vm_state_size = qemu_ftell(f); qemu_fclose(f); if (ret < 0) { - monitor_printf(mon, "Error %d while writing VM\n", ret); + monitor_printf(mon, "%s\n", error_get_pretty(local_err)); + error_free(local_err); goto the_end; } @@ -1243,7 +1325,7 @@ int load_vmstate(const char *name) return 0; } -void do_delvm(Monitor *mon, const QDict *qdict) +void hmp_delvm(Monitor *mon, const QDict *qdict) { BlockDriverState *bs; Error *err; @@ -1271,7 +1353,7 @@ void do_delvm(Monitor *mon, const QDict *qdict) } } -void do_info_snapshots(Monitor *mon, const QDict *qdict) +void hmp_info_snapshots(Monitor *mon, const QDict *qdict) { BlockDriverState *bs, *bs1; QEMUSnapshotInfo *sn_tab, *sn, s, *sn_info = &s; diff --git a/scripts/analyze-migration.py b/scripts/analyze-migration.py new file mode 100755 index 0000000..0c8b22f --- /dev/null +++ b/scripts/analyze-migration.py @@ -0,0 +1,592 @@ +#!/usr/bin/env python +# +# Migration Stream Analyzer +# +# Copyright (c) 2015 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 . + +import numpy as np +import json +import os +import argparse +import collections +import pprint + +def mkdir_p(path): + try: + os.makedirs(path) + except OSError: + pass + +class MigrationFile(object): + def __init__(self, filename): + self.filename = filename + self.file = open(self.filename, "rb") + + def read64(self): + return np.asscalar(np.fromfile(self.file, count=1, dtype='>i8')[0]) + + def read32(self): + return np.asscalar(np.fromfile(self.file, count=1, dtype='>i4')[0]) + + def read16(self): + return np.asscalar(np.fromfile(self.file, count=1, dtype='>i2')[0]) + + def read8(self): + return np.asscalar(np.fromfile(self.file, count=1, dtype='>i1')[0]) + + def readstr(self, len = None): + if len is None: + len = self.read8() + if len == 0: + return "" + return np.fromfile(self.file, count=1, dtype=('S%d' % len))[0] + + def readvar(self, size = None): + if size is None: + size = self.read8() + if size == 0: + return "" + value = self.file.read(size) + if len(value) != size: + raise Exception("Unexpected end of %s at 0x%x" % (self.filename, self.file.tell())) + return value + + def tell(self): + return self.file.tell() + + # The VMSD description is at the end of the file, after EOF. Look for + # the last NULL byte, then for the beginning brace of JSON. + def read_migration_debug_json(self): + QEMU_VM_VMDESCRIPTION = 0x06 + + # Remember the offset in the file when we started + entrypos = self.file.tell() + + # Read the last 10MB + self.file.seek(0, os.SEEK_END) + endpos = self.file.tell() + self.file.seek(max(-endpos, -10 * 1024 * 1024), os.SEEK_END) + datapos = self.file.tell() + data = self.file.read() + # The full file read closed the file as well, reopen it + self.file = open(self.filename, "rb") + + # Find the last NULL byte, then the first brace after that. This should + # be the beginning of our JSON data. + nulpos = data.rfind("\0") + jsonpos = data.find("{", nulpos) + + # Check backwards from there and see whether we guessed right + self.file.seek(datapos + jsonpos - 5, 0) + if self.read8() != QEMU_VM_VMDESCRIPTION: + raise Exception("No Debug Migration device found") + + jsonlen = self.read32() + + # Seek back to where we were at the beginning + self.file.seek(entrypos, 0) + + return data[jsonpos:jsonpos + jsonlen] + + def close(self): + self.file.close() + +class RamSection(object): + RAM_SAVE_FLAG_COMPRESS = 0x02 + RAM_SAVE_FLAG_MEM_SIZE = 0x04 + RAM_SAVE_FLAG_PAGE = 0x08 + RAM_SAVE_FLAG_EOS = 0x10 + RAM_SAVE_FLAG_CONTINUE = 0x20 + RAM_SAVE_FLAG_XBZRLE = 0x40 + RAM_SAVE_FLAG_HOOK = 0x80 + + def __init__(self, file, version_id, ramargs, section_key): + if version_id != 4: + raise Exception("Unknown RAM version %d" % version_id) + + self.file = file + self.section_key = section_key + self.TARGET_PAGE_SIZE = ramargs['page_size'] + self.dump_memory = ramargs['dump_memory'] + self.write_memory = ramargs['write_memory'] + self.sizeinfo = collections.OrderedDict() + self.data = collections.OrderedDict() + self.data['section sizes'] = self.sizeinfo + self.name = '' + if self.write_memory: + self.files = { } + if self.dump_memory: + self.memory = collections.OrderedDict() + self.data['memory'] = self.memory + + def __repr__(self): + return self.data.__repr__() + + def __str__(self): + return self.data.__str__() + + def getDict(self): + return self.data + + def read(self): + # Read all RAM sections + while True: + addr = self.file.read64() + flags = addr & (self.TARGET_PAGE_SIZE - 1) + addr &= ~(self.TARGET_PAGE_SIZE - 1) + + if flags & self.RAM_SAVE_FLAG_MEM_SIZE: + while True: + namelen = self.file.read8() + # We assume that no RAM chunk is big enough to ever + # hit the first byte of the address, so when we see + # a zero here we know it has to be an address, not the + # length of the next block. + if namelen == 0: + self.file.file.seek(-1, 1) + break + self.name = self.file.readstr(len = namelen) + len = self.file.read64() + self.sizeinfo[self.name] = '0x%016x' % len + if self.write_memory: + print self.name + mkdir_p('./' + os.path.dirname(self.name)) + f = open('./' + self.name, "wb") + f.truncate(0) + f.truncate(len) + self.files[self.name] = f + flags &= ~self.RAM_SAVE_FLAG_MEM_SIZE + + if flags & self.RAM_SAVE_FLAG_COMPRESS: + if flags & self.RAM_SAVE_FLAG_CONTINUE: + flags &= ~self.RAM_SAVE_FLAG_CONTINUE + else: + self.name = self.file.readstr() + fill_char = self.file.read8() + # The page in question is filled with fill_char now + if self.write_memory and fill_char != 0: + self.files[self.name].seek(addr, os.SEEK_SET) + self.files[self.name].write(chr(fill_char) * self.TARGET_PAGE_SIZE) + if self.dump_memory: + self.memory['%s (0x%016x)' % (self.name, addr)] = 'Filled with 0x%02x' % fill_char + flags &= ~self.RAM_SAVE_FLAG_COMPRESS + elif flags & self.RAM_SAVE_FLAG_PAGE: + if flags & self.RAM_SAVE_FLAG_CONTINUE: + flags &= ~self.RAM_SAVE_FLAG_CONTINUE + else: + self.name = self.file.readstr() + + if self.write_memory or self.dump_memory: + data = self.file.readvar(size = self.TARGET_PAGE_SIZE) + else: # Just skip RAM data + self.file.file.seek(self.TARGET_PAGE_SIZE, 1) + + if self.write_memory: + self.files[self.name].seek(addr, os.SEEK_SET) + self.files[self.name].write(data) + if self.dump_memory: + hexdata = " ".join("{0:02x}".format(ord(c)) for c in data) + self.memory['%s (0x%016x)' % (self.name, addr)] = hexdata + + flags &= ~self.RAM_SAVE_FLAG_PAGE + elif flags & self.RAM_SAVE_FLAG_XBZRLE: + raise Exception("XBZRLE RAM compression is not supported yet") + elif flags & self.RAM_SAVE_FLAG_HOOK: + raise Exception("RAM hooks don't make sense with files") + + # End of RAM section + if flags & self.RAM_SAVE_FLAG_EOS: + break + + if flags != 0: + raise Exception("Unknown RAM flags: %x" % flags) + + def __del__(self): + if self.write_memory: + for key in self.files: + self.files[key].close() + + +class HTABSection(object): + HASH_PTE_SIZE_64 = 16 + + def __init__(self, file, version_id, device, section_key): + if version_id != 1: + raise Exception("Unknown HTAB version %d" % version_id) + + self.file = file + self.section_key = section_key + + def read(self): + + header = self.file.read32() + + if (header > 0): + # First section, just the hash shift + return + + # Read until end marker + while True: + index = self.file.read32() + n_valid = self.file.read16() + n_invalid = self.file.read16() + + if index == 0 and n_valid == 0 and n_invalid == 0: + break + + self.file.readvar(n_valid * self.HASH_PTE_SIZE_64) + + def getDict(self): + return "" + +class VMSDFieldGeneric(object): + def __init__(self, desc, file): + self.file = file + self.desc = desc + self.data = "" + + def __repr__(self): + return str(self.__str__()) + + def __str__(self): + return " ".join("{0:02x}".format(ord(c)) for c in self.data) + + def getDict(self): + return self.__str__() + + def read(self): + size = int(self.desc['size']) + self.data = self.file.readvar(size) + return self.data + +class VMSDFieldInt(VMSDFieldGeneric): + def __init__(self, desc, file): + super(VMSDFieldInt, self).__init__(desc, file) + self.size = int(desc['size']) + self.format = '0x%%0%dx' % (self.size * 2) + self.sdtype = '>i%d' % self.size + self.udtype = '>u%d' % self.size + + def __repr__(self): + if self.data < 0: + return ('%s (%d)' % ((self.format % self.udata), self.data)) + else: + return self.format % self.data + + def __str__(self): + return self.__repr__() + + def getDict(self): + return self.__str__() + + def read(self): + super(VMSDFieldInt, self).read() + self.sdata = np.fromstring(self.data, count=1, dtype=(self.sdtype))[0] + self.udata = np.fromstring(self.data, count=1, dtype=(self.udtype))[0] + self.data = self.sdata + return self.data + +class VMSDFieldUInt(VMSDFieldInt): + def __init__(self, desc, file): + super(VMSDFieldUInt, self).__init__(desc, file) + + def read(self): + super(VMSDFieldUInt, self).read() + self.data = self.udata + return self.data + +class VMSDFieldIntLE(VMSDFieldInt): + def __init__(self, desc, file): + super(VMSDFieldIntLE, self).__init__(desc, file) + self.dtype = ' ctx_cnt<$ctx_cnt>\n"; #print "pre<$pre_ctx>\nline<$line>\nctx<$ctx>\nnext<$lines[$ctx_ln - 1]>\n"; - if ($ctx !~ /{\s*/ && defined($lines[$ctx_ln -1]) && $lines[$ctx_ln - 1] =~ /^\+\s*{/) { + # The length of the "previous line" is checked against 80 because it + # includes the + at the beginning of the line (if the actual line has + # 79 or 80 characters, it is no longer possible to add a space and an + # opening brace there) + if ($#ctx == 0 && $ctx !~ /{\s*/ && + defined($lines[$ctx_ln - 1]) && $lines[$ctx_ln - 1] =~ /^\+\s*{/ && + defined($lines[$ctx_ln - 2]) && length($lines[$ctx_ln - 2]) < 80) { ERROR("that open brace { should be on the previous line\n" . "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n"); } @@ -2542,7 +2548,10 @@ sub process { substr($block, 0, length($cond), ''); - $seen++ if ($block =~ /^\s*{/); + my $spaced_block = $block; + $spaced_block =~ s/\n\+/ /g; + + $seen++ if ($spaced_block =~ /^\s*{/); print "APW: cond<$cond> block<$block> allowed<$allowed>\n" if $dbg_adv_apw; diff --git a/scripts/coverity-model.c b/scripts/coverity-model.c index 4c99a85..cdda259 100644 --- a/scripts/coverity-model.c +++ b/scripts/coverity-model.c @@ -40,6 +40,8 @@ typedef unsigned long long uint64_t; typedef long long int64_t; typedef _Bool bool; +typedef struct va_list_str *va_list; + /* exec.c */ typedef struct AddressSpace AddressSpace; @@ -90,7 +92,8 @@ static int get_keysym(const name2keysym_t *table, } } -/* glib memory allocation functions. +/* + * GLib memory allocation functions. * * Note that we ignore the fact that g_malloc of 0 bytes returns NULL, * and g_realloc of 0 bytes frees the pointer. @@ -107,60 +110,215 @@ static int get_keysym(const name2keysym_t *table, * we'll get a buffer overflow reported anyway. */ -void *malloc(size_t); -void *calloc(size_t, size_t); -void *realloc(void *, size_t); -void free(void *); +/* + * Allocation primitives, cannot return NULL + * See also Coverity's library/generic/libc/all/all.c + */ + +void *g_malloc_n(size_t nmemb, size_t size) +{ + size_t sz; + void *ptr; + + __coverity_negative_sink__(nmemb); + __coverity_negative_sink__(size); + sz = nmemb * size; + ptr = __coverity_alloc__(sz); + __coverity_mark_as_uninitialized_buffer__(ptr); + __coverity_mark_as_afm_allocated__(ptr, "g_free"); + return ptr; +} + +void *g_malloc0_n(size_t nmemb, size_t size) +{ + size_t sz; + void *ptr; + + __coverity_negative_sink__(nmemb); + __coverity_negative_sink__(size); + sz = nmemb * size; + ptr = __coverity_alloc__(sz); + __coverity_writeall0__(ptr); + __coverity_mark_as_afm_allocated__(ptr, "g_free"); + return ptr; +} -void * -g_malloc(size_t n_bytes) +void *g_realloc_n(void *ptr, size_t nmemb, size_t size) { - void *mem; - __coverity_negative_sink__(n_bytes); - mem = malloc(n_bytes == 0 ? 1 : n_bytes); - if (!mem) __coverity_panic__(); - return mem; + size_t sz; + + __coverity_negative_sink__(nmemb); + __coverity_negative_sink__(size); + sz = nmemb * size; + __coverity_escape__(ptr); + ptr = __coverity_alloc__(sz); + /* + * Memory beyond the old size isn't actually initialized. Can't + * model that. See Coverity's realloc() model + */ + __coverity_writeall__(ptr); + __coverity_mark_as_afm_allocated__(ptr, "g_free"); + return ptr; } -void * -g_malloc0(size_t n_bytes) +void g_free(void *ptr) +{ + __coverity_free__(ptr); + __coverity_mark_as_afm_freed__(ptr, "g_free"); +} + +/* + * Derive the g_try_FOO_n() from the g_FOO_n() by adding indeterminate + * out of memory conditions + */ + +void *g_try_malloc_n(size_t nmemb, size_t size) +{ + int nomem; + + if (nomem) { + return NULL; + } + return g_malloc_n(nmemb, size); +} + +void *g_try_malloc0_n(size_t nmemb, size_t size) +{ + int nomem; + + if (nomem) { + return NULL; + } + return g_malloc0_n(nmemb, size); +} + +void *g_try_realloc_n(void *ptr, size_t nmemb, size_t size) +{ + int nomem; + + if (nomem) { + return NULL; + } + return g_realloc_n(ptr, nmemb, size); +} + +/* Trivially derive the g_FOO() from the g_FOO_n() */ + +void *g_malloc(size_t size) { - void *mem; - __coverity_negative_sink__(n_bytes); - mem = calloc(1, n_bytes == 0 ? 1 : n_bytes); - if (!mem) __coverity_panic__(); - return mem; + return g_malloc_n(1, size); } -void g_free(void *mem) +void *g_malloc0(size_t size) { - free(mem); + return g_malloc0_n(1, size); } -void *g_realloc(void * mem, size_t n_bytes) +void *g_realloc(void *ptr, size_t size) { - __coverity_negative_sink__(n_bytes); - mem = realloc(mem, n_bytes == 0 ? 1 : n_bytes); - if (!mem) __coverity_panic__(); - return mem; + return g_realloc_n(ptr, 1, size); } -void *g_try_malloc(size_t n_bytes) +void *g_try_malloc(size_t size) { - __coverity_negative_sink__(n_bytes); - return malloc(n_bytes == 0 ? 1 : n_bytes); + return g_try_malloc_n(1, size); } -void *g_try_malloc0(size_t n_bytes) +void *g_try_malloc0(size_t size) { - __coverity_negative_sink__(n_bytes); - return calloc(1, n_bytes == 0 ? 1 : n_bytes); + return g_try_malloc0_n(1, size); } -void *g_try_realloc(void *mem, size_t n_bytes) +void *g_try_realloc(void *ptr, size_t size) { - __coverity_negative_sink__(n_bytes); - return realloc(mem, n_bytes == 0 ? 1 : n_bytes); + return g_try_realloc_n(ptr, 1, size); +} + +/* + * GLib string allocation functions + */ + +char *g_strdup(const char *s) +{ + char *dup; + size_t i; + + if (!s) { + return NULL; + } + + __coverity_string_null_sink__(s); + __coverity_string_size_sink__(s); + dup = __coverity_alloc_nosize__(); + __coverity_mark_as_afm_allocated__(dup, "g_free"); + for (i = 0; (dup[i] = s[i]); i++) ; + return dup; +} + +char *g_strndup(const char *s, size_t n) +{ + char *dup; + size_t i; + + __coverity_negative_sink__(n); + + if (!s) { + return NULL; + } + + dup = g_malloc(n + 1); + for (i = 0; i < n && (dup[i] = s[i]); i++) ; + dup[i] = 0; + return dup; +} + +char *g_strdup_printf(const char *format, ...) +{ + char ch, *s; + size_t len; + + __coverity_string_null_sink__(format); + __coverity_string_size_sink__(format); + + ch = *format; + + s = __coverity_alloc_nosize__(); + __coverity_writeall__(s); + __coverity_mark_as_afm_allocated__(s, "g_free"); + return s; +} + +char *g_strdup_vprintf(const char *format, va_list ap) +{ + char ch, *s; + size_t len; + + __coverity_string_null_sink__(format); + __coverity_string_size_sink__(format); + + ch = *format; + ch = *(char *)ap; + + s = __coverity_alloc_nosize__(); + __coverity_writeall__(s); + __coverity_mark_as_afm_allocated__(s, "g_free"); + + return len; +} + +char *g_strconcat(const char *s, ...) +{ + char *s; + + /* + * Can't model: last argument must be null, the others + * null-terminated strings + */ + + s = __coverity_alloc_nosize__(); + __coverity_writeall__(s); + __coverity_mark_as_afm_allocated__(s, "g_free"); + return s; } /* Other glib functions */ diff --git a/scripts/dump-guest-memory.py b/scripts/dump-guest-memory.py index 1ed8b67..dc8e44a 100644 --- a/scripts/dump-guest-memory.py +++ b/scripts/dump-guest-memory.py @@ -108,16 +108,16 @@ shape and this command should mostly work.""" assert (val["hi"] == 0) return val["lo"] - def qtailq_foreach(self, head, field_str): - var_p = head["tqh_first"] + def qlist_foreach(self, head, field_str): + var_p = head["lh_first"] while (var_p != 0): var = var_p.dereference() yield var - var_p = var[field_str]["tqe_next"] + var_p = var[field_str]["le_next"] def qemu_get_ram_block(self, ram_addr): ram_blocks = gdb.parse_and_eval("ram_list.blocks") - for block in self.qtailq_foreach(ram_blocks, "next"): + for block in self.qlist_foreach(ram_blocks, "next"): if (ram_addr - block["offset"] < block["length"]): return block raise gdb.GdbError("Bad ram offset %x" % ram_addr) diff --git a/scripts/get_maintainer.pl b/scripts/get_maintainer.pl index af68c6c..f39630e 100755 --- a/scripts/get_maintainer.pl +++ b/scripts/get_maintainer.pl @@ -23,7 +23,6 @@ my $email_usename = 1; my $email_maintainer = 1; my $email_list = 1; my $email_subscriber_list = 0; -my $email_git_penguin_chiefs = 0; my $email_git = 0; my $email_git_all_signature_types = 0; my $email_git_blame = 0; @@ -60,21 +59,6 @@ my $exit = 0; my %commit_author_hash; my %commit_signer_hash; -my @penguin_chief = (); -push(@penguin_chief, "Linus Torvalds:torvalds\@linux-foundation.org"); -#Andrew wants in on most everything - 2009/01/14 -#push(@penguin_chief, "Andrew Morton:akpm\@linux-foundation.org"); - -my @penguin_chief_names = (); -foreach my $chief (@penguin_chief) { - if ($chief =~ m/^(.*):(.*)/) { - my $chief_name = $1; - my $chief_addr = $2; - push(@penguin_chief_names, $chief_name); - } -} -my $penguin_chiefs = "\(" . join("|", @penguin_chief_names) . "\)"; - # Signature types of people who are either # a) responsible for the code in question, or # b) familiar enough with it to give relevant feedback @@ -187,7 +171,6 @@ if (!GetOptions( 'git-blame!' => \$email_git_blame, 'git-blame-signatures!' => \$email_git_blame_signatures, 'git-fallback!' => \$email_git_fallback, - 'git-chief-penguins!' => \$email_git_penguin_chiefs, 'git-min-signatures=i' => \$email_git_min_signatures, 'git-max-maintainers=i' => \$email_git_max_maintainers, 'git-min-percent=i' => \$email_git_min_percent, @@ -256,7 +239,7 @@ if ($sections) { if ($email && ($email_maintainer + $email_list + $email_subscriber_list + - $email_git + $email_git_penguin_chiefs + $email_git_blame) == 0) { + $email_git + $email_git_blame) == 0) { die "$P: Please select at least 1 email option\n"; } @@ -671,19 +654,6 @@ sub get_maintainers { } } - foreach my $chief (@penguin_chief) { - if ($chief =~ m/^(.*):(.*)/) { - my $email_address; - - $email_address = format_email($1, $2, $email_usename); - if ($email_git_penguin_chiefs) { - push(@email_to, [$email_address, 'chief penguin']); - } else { - @email_to = grep($_->[0] !~ /${email_address}/, @email_to); - } - } - } - foreach my $email (@file_emails) { my ($name, $address) = parse_email($email); @@ -740,7 +710,6 @@ MAINTAINER field selection options: --git-all-signature-types => include signers regardless of signature type or use only ${signature_pattern} signers (default: $email_git_all_signature_types) --git-fallback => use git when no exact MAINTAINERS pattern (default: $email_git_fallback) - --git-chief-penguins => include ${penguin_chiefs} --git-min-signatures => number of signatures required (default: $email_git_min_signatures) --git-max-maintainers => maximum maintainers to add (default: $email_git_max_maintainers) --git-min-percent => minimum percentage of commits required (default: $email_git_min_percent) @@ -1281,10 +1250,6 @@ sub vcs_find_signers { save_commits_by_author(@lines) if ($interactive); save_commits_by_signer(@lines) if ($interactive); - if (!$email_git_penguin_chiefs) { - @signatures = grep(!/${penguin_chiefs}/i, @signatures); - } - my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures); return ($commits, @$signers_ref); @@ -1296,10 +1261,6 @@ sub vcs_find_author { @lines = &{$VCS_cmds{"execute_cmd"}}($cmd); - if (!$email_git_penguin_chiefs) { - @lines = grep(!/${penguin_chiefs}/i, @lines); - } - return @lines if !@lines; my @authors = (); @@ -1925,10 +1886,6 @@ sub vcs_file_blame { @lines = &{$VCS_cmds{"execute_cmd"}}($cmd); - if (!$email_git_penguin_chiefs) { - @lines = grep(!/${penguin_chiefs}/i, @lines); - } - last if !@lines; my @authors = (); diff --git a/scripts/kvm/kvm_stat b/scripts/kvm/kvm_stat index 7b1437c..7e5d256 100755 --- a/scripts/kvm/kvm_stat +++ b/scripts/kvm/kvm_stat @@ -13,6 +13,7 @@ import curses import sys, os, time, optparse, ctypes +from ctypes import * class DebugfsProvider(object): def __init__(self): @@ -65,6 +66,8 @@ vmx_exit_reasons = { 49: 'EPT_MISCONFIG', 54: 'WBINVD', 55: 'XSETBV', + 56: 'APIC_WRITE', + 58: 'INVPCID', } svm_exit_reasons = { @@ -138,9 +141,49 @@ svm_exit_reasons = { 0x08a: 'MONITOR', 0x08b: 'MWAIT', 0x08c: 'MWAIT_COND', + 0x08d: 'XSETBV', 0x400: 'NPF', } +# EC definition of HSR (from arch/arm64/include/asm/kvm_arm.h) +aarch64_exit_reasons = { + 0x00: 'UNKNOWN', + 0x01: 'WFI', + 0x03: 'CP15_32', + 0x04: 'CP15_64', + 0x05: 'CP14_MR', + 0x06: 'CP14_LS', + 0x07: 'FP_ASIMD', + 0x08: 'CP10_ID', + 0x0C: 'CP14_64', + 0x0E: 'ILL_ISS', + 0x11: 'SVC32', + 0x12: 'HVC32', + 0x13: 'SMC32', + 0x15: 'SVC64', + 0x16: 'HVC64', + 0x17: 'SMC64', + 0x18: 'SYS64', + 0x20: 'IABT', + 0x21: 'IABT_HYP', + 0x22: 'PC_ALIGN', + 0x24: 'DABT', + 0x25: 'DABT_HYP', + 0x26: 'SP_ALIGN', + 0x28: 'FP_EXC32', + 0x2C: 'FP_EXC64', + 0x2F: 'SERROR', + 0x30: 'BREAKPT', + 0x31: 'BREAKPT_HYP', + 0x32: 'SOFTSTP', + 0x33: 'SOFTSTP_HYP', + 0x34: 'WATCHPT', + 0x35: 'WATCHPT_HYP', + 0x38: 'BKPT32', + 0x3A: 'VECTOR32', + 0x3C: 'BRK64', +} + # From include/uapi/linux/kvm.h, KVM_EXIT_xxx userspace_exit_reasons = { 0: 'UNKNOWN', @@ -167,6 +210,7 @@ userspace_exit_reasons = { 21: 'WATCHDOG', 22: 'S390_TSCH', 23: 'EPR', + 24: 'SYSTEM_EVENT', } x86_exit_reasons = { @@ -181,6 +225,7 @@ ioctl_numbers = { 'SET_FILTER' : 0x40082406, 'ENABLE' : 0x00002400, 'DISABLE' : 0x00002401, + 'RESET' : 0x00002403, } def x86_init(flag): @@ -204,10 +249,19 @@ def ppc_init(): } }) +def aarch64_init(): + globals().update({ + 'sc_perf_evt_open' : 241, + 'exit_reasons' : aarch64_exit_reasons, + }) + def detect_platform(): if os.uname()[4].startswith('ppc'): ppc_init() return + elif os.uname()[4].startswith('aarch64'): + aarch64_init() + return for line in file('/proc/cpuinfo').readlines(): if line.startswith('flags'): @@ -235,6 +289,9 @@ import struct, array libc = ctypes.CDLL('libc.so.6') syscall = libc.syscall +get_errno = libc.__errno_location +get_errno.restype = POINTER(c_int) + class perf_event_attr(ctypes.Structure): _fields_ = [('type', ctypes.c_uint32), ('size', ctypes.c_uint32), @@ -318,7 +375,8 @@ class Event(object): group_leader = group.events[0].fd fd = _perf_event_open(attr, -1, group.cpu, group_leader, 0) if fd == -1: - raise Exception('perf_event_open failed') + err = get_errno()[0] + raise Exception('perf_event_open failed, errno = ' + err.__str__()) if filter: import fcntl fcntl.ioctl(fd, ioctl_numbers['SET_FILTER'], filter) @@ -329,6 +387,9 @@ class Event(object): def disable(self): import fcntl fcntl.ioctl(self.fd, ioctl_numbers['DISABLE'], 0) + def reset(self): + import fcntl + fcntl.ioctl(self.fd, ioctl_numbers['RESET'], 0) class TracepointProvider(object): def __init__(self): @@ -388,6 +449,7 @@ class TracepointProvider(object): for group in self.group_leaders: for event in group.events: if event.name in fields: + event.reset() event.enable() else: event.disable() @@ -457,7 +519,10 @@ def tui(screen, stats): def refresh(sleeptime): screen.erase() screen.addstr(0, 0, 'kvm statistics') - row = 2 + screen.addstr(2, 1, 'Event') + screen.addstr(2, 1 + label_width + number_width - len('Total'), 'Total') + screen.addstr(2, 1 + label_width + number_width + 8 - len('Current'), 'Current') + row = 3 s = stats.get() def sortkey(x): if s[x][1]: diff --git a/scripts/kvm/kvm_stat.texi b/scripts/kvm/kvm_stat.texi new file mode 100644 index 0000000..6ce00d8 --- /dev/null +++ b/scripts/kvm/kvm_stat.texi @@ -0,0 +1,55 @@ +@example +@c man begin SYNOPSIS +usage: kvm_stat [OPTION]... +@c man end +@end example + +@c man begin DESCRIPTION + +kvm_stat prints counts of KVM kernel module trace events. These events signify +state transitions such as guest mode entry and exit. + +This tool is useful for observing guest behavior from the host perspective. +Often conclusions about performance or buggy behavior can be drawn from the +output. + +The set of KVM kernel module trace events may be specific to the kernel version +or architecture. It is best to check the KVM kernel module source code for the +meaning of events. + +Note that trace events are counted globally across all running guests. + +@c man end + +@c man begin OPTIONS +@table @option +@item -1, --once, --batch + run in batch mode for one second +@item -l, --log + run in logging mode (like vmstat) +@item -t, --tracepoints + retrieve statistics from tracepoints +@item -d, --debugfs + retrieve statistics from debugfs +@item -f, --fields=@var{fields} + fields to display (regex) +@item -h, --help + show help message +@end table + +@c man end + +@ignore + +@setfilename kvm_stat +@settitle Report KVM kernel module event counters. + +@c man begin AUTHOR +Stefan Hajnoczi +@c man end + +@c man begin SEEALSO +perf(1), trace-cmd(1) +@c man end + +@end ignore diff --git a/scripts/make_device_config.sh b/scripts/make_device_config.sh index 7242707..c1afb3f 100644 --- a/scripts/make_device_config.sh +++ b/scripts/make_device_config.sh @@ -1,10 +1,12 @@ #! /bin/sh -# Construct a target device config file from a default, pulling in any -# files from include directives. +# Writes a target device config file to stdout, from a default and from +# include directives therein. Also emits Makefile dependencies. +# +# Usage: make_device_config.sh SRC DEPFILE-NAME DEPFILE-TARGET > DEST -dest=$1.tmp -dep=`dirname $1`-`basename $1`.d -src=$2 +src=$1 +dep=$2 +target=$3 src_dir=`dirname $src` all_includes= @@ -22,7 +24,7 @@ while [ -n "$f" ] ; do [ $? = 0 ] || exit 1 all_includes="$all_includes $f" done -process_includes $src > $dest +process_includes $src -cat $src $all_includes | grep -v '^include' > $dest -echo "$1: $all_includes" > $dep +cat $src $all_includes | grep -v '^include' +echo "$target: $all_includes" > $dep diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py index d2f815b..db87218 100644 --- a/scripts/qapi-types.py +++ b/scripts/qapi-types.py @@ -99,6 +99,14 @@ struct %(name)s ret += generate_struct_fields(members) + # Make sure that all structs have at least one field; this avoids + # potential issues with attempting to malloc space for zero-length structs + # in C, and also incompatibility with C++ (where an empty struct is size 1). + if not base and not members: + ret += mcgen(''' + char qapi_dummy_field_for_empty_struct; +''') + if len(fieldname): fieldname = " " + fieldname ret += mcgen(''' @@ -115,16 +123,19 @@ const char *%(name)s_lookup[] = { name=name) i = 0 for value in values: + index = generate_enum_full_value(name, value) ret += mcgen(''' - "%(value)s", + [%(index)s] = "%(value)s", ''', - value=value) + index = index, value = value) + max_index = generate_enum_full_value(name, 'MAX') ret += mcgen(''' - NULL, + [%(max_index)s] = NULL, }; -''') +''', + max_index=max_index) return ret def generate_enum(name, values): diff --git a/scripts/qmp/qmp.py b/scripts/qmp/qmp.py index 20b6ec7..1d38e3e 100644 --- a/scripts/qmp/qmp.py +++ b/scripts/qmp/qmp.py @@ -21,6 +21,9 @@ class QMPConnectError(QMPError): class QMPCapabilitiesError(QMPError): pass +class QMPTimeoutError(QMPError): + pass + class QEMUMonitorProtocol: def __init__(self, address, server=False): """ @@ -72,6 +75,44 @@ class QEMUMonitorProtocol: error = socket.error + def __get_events(self, wait=False): + """ + Check for new events in the stream and cache them in __events. + + @param wait (bool): block until an event is available. + @param wait (float): If wait is a float, treat it as a timeout value. + + @raise QMPTimeoutError: If a timeout float is provided and the timeout + period elapses. + @raise QMPConnectError: If wait is True but no events could be retrieved + or if some other error occurred. + """ + + # Check for new events regardless and pull them into the cache: + self.__sock.setblocking(0) + try: + self.__json_read() + except socket.error, err: + if err[0] == errno.EAGAIN: + # No data available + pass + self.__sock.setblocking(1) + + # Wait for new events, if needed. + # if wait is 0.0, this means "no wait" and is also implicitly false. + if not self.__events and wait: + if isinstance(wait, float): + self.__sock.settimeout(wait) + try: + ret = self.__json_read(only_event=True) + except socket.timeout: + raise QMPTimeoutError("Timeout waiting for event") + except: + raise QMPConnectError("Error while reading from socket") + if ret is None: + raise QMPConnectError("Error while reading from socket") + self.__sock.settimeout(None) + def connect(self, negotiate=True): """ Connect to the QMP Monitor and perform capabilities negotiation. @@ -140,43 +181,37 @@ class QEMUMonitorProtocol: """ Get and delete the first available QMP event. - @param wait: block until an event is available (bool) + @param wait (bool): block until an event is available. + @param wait (float): If wait is a float, treat it as a timeout value. + + @raise QMPTimeoutError: If a timeout float is provided and the timeout + period elapses. + @raise QMPConnectError: If wait is True but no events could be retrieved + or if some other error occurred. + + @return The first available QMP event, or None. """ - self.__sock.setblocking(0) - try: - self.__json_read() - except socket.error, err: - if err[0] == errno.EAGAIN: - # No data available - pass - self.__sock.setblocking(1) - if not self.__events and wait: - self.__json_read(only_event=True) - event = self.__events[0] - del self.__events[0] - return event + self.__get_events(wait) + + if self.__events: + return self.__events.pop(0) + return None def get_events(self, wait=False): """ Get a list of available QMP events. - @param wait: block until an event is available (bool) - """ - self.__sock.setblocking(0) - try: - self.__json_read() - except socket.error, err: - if err[0] == errno.EAGAIN: - # No data available - pass - self.__sock.setblocking(1) - if not self.__events and wait: - ret = self.__json_read(only_event=True) - if ret == None: - # We are in blocking mode, if don't get anything, something - # went wrong - raise QMPConnectError("Error while reading from socket") + @param wait (bool): block until an event is available. + @param wait (float): If wait is a float, treat it as a timeout value. + @raise QMPTimeoutError: If a timeout float is provided and the timeout + period elapses. + @raise QMPConnectError: If wait is True but no events could be retrieved + or if some other error occurred. + + @return The list of available QMP events. + """ + self.__get_events(wait) return self.__events def clear_events(self): diff --git a/scripts/qmp/qom-tree b/scripts/qmp/qom-tree new file mode 100755 index 0000000..aea11d4 --- /dev/null +++ b/scripts/qmp/qom-tree @@ -0,0 +1,70 @@ +#!/usr/bin/python +## +# QEMU Object Model test tools +# +# Copyright IBM, Corp. 2011 +# Copyright (c) 2013 SUSE LINUX Products GmbH +# +# Authors: +# Anthony Liguori +# Andreas Faerber +# +# This work is licensed under the terms of the GNU GPL, version 2 or later. See +# the COPYING file in the top-level directory. +## + +import sys +import os +from qmp import QEMUMonitorProtocol + +cmd, args = sys.argv[0], sys.argv[1:] +socket_path = None +path = None +prop = None + +def usage(): + return '''environment variables: + QMP_SOCKET= +usage: + %s [-h] [-s ] [] +''' % cmd + +def usage_error(error_msg = "unspecified error"): + sys.stderr.write('%s\nERROR: %s\n' % (usage(), error_msg)) + exit(1) + +if len(args) > 0: + if args[0] == "-h": + print usage() + exit(0); + elif args[0] == "-s": + try: + socket_path = args[1] + except: + usage_error("missing argument: QMP socket path or address"); + args = args[2:] + +if not socket_path: + if os.environ.has_key('QMP_SOCKET'): + socket_path = os.environ['QMP_SOCKET'] + else: + usage_error("no QMP socket path or address given"); + +srv = QEMUMonitorProtocol(socket_path) +srv.connect() + +def list_node(path): + print '%s' % path + items = srv.command('qom-list', path=path) + for item in items: + if not item['type'].startswith('child<'): + try: + print ' %s: %s (%s)' % (item['name'], srv.command('qom-get', path=path, property=item['name']), item['type']) + except: + print ' %s: (%s)' % (item['name'], item['type']) + print '' + for item in items: + if item['type'].startswith('child<'): + list_node(path + '/' + item['name']) + +list_node('/machine') diff --git a/scripts/qtest.py b/scripts/qtest.py new file mode 100644 index 0000000..a971445 --- /dev/null +++ b/scripts/qtest.py @@ -0,0 +1,71 @@ +# QEMU qtest library +# +# Copyright (C) 2015 Red Hat Inc. +# +# Authors: +# Fam Zheng +# +# This work is licensed under the terms of the GNU GPL, version 2. See +# the COPYING file in the top-level directory. +# +# Based on qmp.py. +# + +import errno +import socket + +class QEMUQtestProtocol(object): + def __init__(self, address, server=False): + """ + Create a QEMUQtestProtocol object. + + @param address: QEMU address, can be either a unix socket path (string) + or a tuple in the form ( address, port ) for a TCP + connection + @param server: server mode, listens on the socket (bool) + @raise socket.error on socket connection errors + @note No connection is established, this is done by the connect() or + accept() methods + """ + self._address = address + self._sock = self._get_sock() + if server: + self._sock.bind(self._address) + self._sock.listen(1) + + def _get_sock(self): + if isinstance(self._address, tuple): + family = socket.AF_INET + else: + family = socket.AF_UNIX + return socket.socket(family, socket.SOCK_STREAM) + + def connect(self): + """ + Connect to the qtest socket. + + @raise socket.error on socket connection errors + """ + self._sock.connect(self._address) + + def accept(self): + """ + Await connection from QEMU. + + @raise socket.error on socket connection errors + """ + self._sock, _ = self._sock.accept() + + def cmd(self, qtest_cmd): + """ + Send a qtest command on the wire. + + @param qtest_cmd: qtest command text to be sent + """ + self._sock.sendall(qtest_cmd + "\n") + + def close(self): + self._sock.close() + + def settimeout(self, timeout): + self._sock.settimeout(timeout) diff --git a/scripts/tracetool/backend/stderr.py b/scripts/tracetool/backend/stderr.py index 2a1e906..ca58054 100644 --- a/scripts/tracetool/backend/stderr.py +++ b/scripts/tracetool/backend/stderr.py @@ -21,6 +21,9 @@ PUBLIC = True def generate_h_begin(events): out('#include ', + '#include ', + '#include ', + '#include ', '#include "trace/control.h"', '') @@ -31,7 +34,12 @@ def generate_h(event): argnames = ", " + argnames out(' if (trace_event_get_state(%(event_id)s)) {', - ' fprintf(stderr, "%(name)s " %(fmt)s "\\n" %(argnames)s);', + ' struct timeval _now;', + ' gettimeofday(&_now, NULL);', + ' fprintf(stderr, "%%d@%%zd.%%06zd:%(name)s " %(fmt)s "\\n",', + ' getpid(),', + ' (size_t)_now.tv_sec, (size_t)_now.tv_usec', + ' %(argnames)s);', ' }', event_id="TRACE_" + event.name.upper(), name=event.name, diff --git a/scripts/tracetool/format/d.py b/scripts/tracetool/format/d.py index 46eebb1..c77d5b7 100644 --- a/scripts/tracetool/format/d.py +++ b/scripts/tracetool/format/d.py @@ -16,6 +16,19 @@ __email__ = "stefanha@linux.vnet.ibm.com" from tracetool import out +# Reserved keywords from +# https://wikis.oracle.com/display/DTrace/Types,+Operators+and+Expressions +RESERVED_WORDS = ( + 'auto', 'goto', 'sizeof', 'break', 'if', 'static', 'case', 'import', + 'string', 'char', 'inline', 'stringof', 'const', 'int', 'struct', + 'continue', 'long', 'switch', 'counter', 'offsetof', 'this', + 'default', 'probe', 'translator', 'do', 'provider', 'typedef', + 'double', 'register', 'union', 'else', 'restrict', 'unsigned', + 'enum', 'return', 'void', 'extern', 'self', 'volatile', 'float', + 'short', 'while', 'for', 'signed', 'xlate', +) + + def generate(events, backend): events = [e for e in events if "disable" not in e.properties] @@ -25,18 +38,17 @@ def generate(events, backend): 'provider qemu {') for e in events: - args = str(e.args) - - # DTrace provider syntax expects foo() for empty - # params, not foo(void) - if args == 'void': - args = '' + args = [] + for type_, name in e.args: + if name in RESERVED_WORDS: + name += '_' + args.append(type_ + ' ' + name) # Define prototype for probe arguments out('', 'probe %(name)s(%(args)s);', name=e.name, - args=args) + args=','.join(args)) out('', '};') diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index c8e026d..f208ec9 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -28,6 +28,37 @@ if [ -z "$output" ]; then output="$PWD" fi +cp_virtio() { + from=$1 + to=$2 + virtio=$(find "$from" -name '*virtio*h') + if [ "$virtio" ]; then + rm -rf "$to" + mkdir -p "$to" + for f in $virtio; do + if + grep '#include' "$f" | grep -v -e 'linux/virtio' \ + -e 'linux/types' \ + -e 'linux/if_ether' \ + > /dev/null + then + echo "Unexpected #include in input file $f". + exit 2 + fi + + header=$(basename "$f"); + sed -e 's/__u\([0-9][0-9]*\)/uint\1_t/g' \ + -e 's/__le\([0-9][0-9]*\)/uint\1_t/g' \ + -e 's/__be\([0-9][0-9]*\)/uint\1_t/g' \ + -e 's/]*\)>/"standard-headers\/linux\/\1"/' \ + -e 's/__bitwise__//' \ + -e 's/__attribute__((packed))/QEMU_PACKED/' \ + -e 's/__inline__/inline/' \ + "$f" > "$to/$header"; + done + fi +} + # This will pick up non-directories too (eg "Kconfig") but we will # ignore them in the next loop. ARCHLIST=$(cd "$linux/arch" && echo *) @@ -57,11 +88,13 @@ for arch in $ARCHLIST; do if [ $arch = powerpc ]; then cp "$tmpdir/include/asm/epapr_hcalls.h" "$output/linux-headers/asm-powerpc/" fi + + cp_virtio "$tmpdir/include/asm" "$output/include/standard-headers/asm-$arch" done rm -rf "$output/linux-headers/linux" mkdir -p "$output/linux-headers/linux" -for header in kvm.h kvm_para.h vfio.h vhost.h virtio_config.h virtio_ring.h \ +for header in kvm.h kvm_para.h vfio.h vhost.h \ psci.h; do cp "$tmpdir/include/linux/$header" "$output/linux-headers/linux" done @@ -76,4 +109,21 @@ else cp "$linux/COPYING" "$output/linux-headers" fi +cat <$output/linux-headers/linux/virtio_config.h +#include "standard-headers/linux/virtio_config.h" +EOF +cat <$output/linux-headers/linux/virtio_ring.h +#include "standard-headers/linux/virtio_ring.h" +EOF + +cp_virtio "$tmpdir/include/linux/" "$output/include/standard-headers/linux" + +cat <$output/include/standard-headers/linux/types.h +#include +#include "qemu/compiler.h" +EOF +cat <$output/include/standard-headers/linux/if_ether.h +#define ETH_ALEN 6 +EOF + rm -rf "$tmpdir" diff --git a/scripts/vmstate-static-checker.py b/scripts/vmstate-static-checker.py index f7ce3fc..b6c0bbe 100755 --- a/scripts/vmstate-static-checker.py +++ b/scripts/vmstate-static-checker.py @@ -53,6 +53,8 @@ def check_fields_match(name, s_field, d_field): 'parent_obj.parent_obj.parent_obj', 'port.br.dev.exp.aer_log', 'parent_obj.parent_obj.parent_obj.exp.aer_log'], + 'cirrus_vga': ['hw_cursor_x', 'vga.hw_cursor_x', + 'hw_cursor_y', 'vga.hw_cursor_y'], 'lsiscsi': ['dev', 'parent_obj'], 'mch': ['d', 'parent_obj'], 'pci_bridge': ['bridge.dev', 'parent_obj', 'bridge.dev.shpc', 'shpc'], diff --git a/softmmu_template.h b/softmmu_template.h index 6b4e615..0e3dd35 100644 --- a/softmmu_template.h +++ b/softmmu_template.h @@ -149,7 +149,7 @@ static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env, { uint64_t val; CPUState *cpu = ENV_GET_CPU(env); - MemoryRegion *mr = iotlb_to_region(cpu->as, physaddr); + MemoryRegion *mr = iotlb_to_region(cpu, physaddr); physaddr = (physaddr & TARGET_PAGE_MASK) + addr; cpu->mem_io_pc = retaddr; @@ -369,7 +369,7 @@ static inline void glue(io_write, SUFFIX)(CPUArchState *env, uintptr_t retaddr) { CPUState *cpu = ENV_GET_CPU(env); - MemoryRegion *mr = iotlb_to_region(cpu->as, physaddr); + MemoryRegion *mr = iotlb_to_region(cpu, physaddr); physaddr = (physaddr & TARGET_PAGE_MASK) + addr; if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu_can_do_io(cpu)) { diff --git a/spice-qemu-char.c b/spice-qemu-char.c index 8106e06..a4f4e57 100644 --- a/spice-qemu-char.c +++ b/spice-qemu-char.c @@ -3,7 +3,6 @@ #include "ui/qemu-spice.h" #include "sysemu/char.h" #include -#include #include #include "qemu/osdep.h" @@ -159,7 +158,7 @@ static gboolean spice_char_source_dispatch(GSource *source, return func(NULL, G_IO_OUT, user_data); } -GSourceFuncs SpiceCharSourceFuncs = { +static GSourceFuncs SpiceCharSourceFuncs = { .prepare = spice_char_source_prepare, .check = spice_char_source_check, .dispatch = spice_char_source_dispatch, diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs index 5e347d0..8beff4c 100644 --- a/stubs/Makefile.objs +++ b/stubs/Makefile.objs @@ -24,7 +24,6 @@ stub-obj-y += mon-printf.o stub-obj-y += mon-set-error.o stub-obj-y += monitor-init.o stub-obj-y += notify-event.o -stub-obj-y += pci-drive-hot-add.o stub-obj-$(CONFIG_SPICE) += qemu-chr-open-spice.o stub-obj-y += qtest.o stub-obj-y += reset.o diff --git a/stubs/pci-drive-hot-add.c b/stubs/pci-drive-hot-add.c deleted file mode 100644 index 1d98145..0000000 --- a/stubs/pci-drive-hot-add.c +++ /dev/null @@ -1,10 +0,0 @@ -#include -#include -#include - -int pci_drive_hot_add(Monitor *mon, const QDict *qdict, DriveInfo *dinfo) -{ - /* On non-x86 we don't do PCI hotplug */ - monitor_printf(mon, "Can't hot-add drive to type %d\n", dinfo->type); - return -1; -} diff --git a/stubs/qmp_pc_dimm_device_list.c b/stubs/qmp_pc_dimm_device_list.c index 5cb220c..b584bd8 100644 --- a/stubs/qmp_pc_dimm_device_list.c +++ b/stubs/qmp_pc_dimm_device_list.c @@ -5,3 +5,8 @@ int qmp_pc_dimm_device_list(Object *obj, void *opaque) { return 0; } + +ram_addr_t get_current_ram_size(void) +{ + return ram_size; +} diff --git a/stubs/qtest.c b/stubs/qtest.c index e671ed8..dc17594 100644 --- a/stubs/qtest.c +++ b/stubs/qtest.c @@ -8,7 +8,7 @@ * See the COPYING file in the top-level directory. */ -#include "qemu-common.h" +#include "sysemu/qtest.h" /* Needed for qtest_allowed() */ bool qtest_allowed; diff --git a/target-alpha/cpu.h b/target-alpha/cpu.h index d9b861f..9538f19 100644 --- a/target-alpha/cpu.h +++ b/target-alpha/cpu.h @@ -32,8 +32,6 @@ #include "fpu/softfloat.h" -#define TARGET_HAS_ICE 1 - #define ELF_MACHINE EM_ALPHA #define ICACHE_LINE_SIZE 32 @@ -431,14 +429,7 @@ void alpha_translate_init(void); AlphaCPU *cpu_alpha_init(const char *cpu_model); -static inline CPUAlphaState *cpu_init(const char *cpu_model) -{ - AlphaCPU *cpu = cpu_alpha_init(cpu_model); - if (cpu == NULL) { - return NULL; - } - return &cpu->env; -} +#define cpu_init(cpu_model) CPU(cpu_alpha_init(cpu_model)) void alpha_cpu_list(FILE *f, fprintf_function cpu_fprintf); int cpu_alpha_exec(CPUAlphaState *s); diff --git a/target-alpha/translate.c b/target-alpha/translate.c index 76658a0..efeeb05 100644 --- a/target-alpha/translate.c +++ b/target-alpha/translate.c @@ -388,7 +388,7 @@ static ExitStatus gen_store_conditional(DisasContext *ctx, int ra, int rb, /* ??? In system mode we are never multi-threaded, so CAS can be implemented via a non-atomic load-compare-store sequence. */ { - int lab_fail, lab_done; + TCGLabel *lab_fail, *lab_done; TCGv val; lab_fail = gen_new_label(); @@ -465,7 +465,7 @@ static ExitStatus gen_bcond_internal(DisasContext *ctx, TCGCond cond, TCGv cmp, int32_t disp) { uint64_t dest = ctx->pc + (disp << 2); - int lab_true = gen_new_label(); + TCGLabel *lab_true = gen_new_label(); if (use_goto_tb(ctx, dest)) { tcg_gen_brcondi_i64(cond, cmp, 0, lab_true); @@ -1285,7 +1285,7 @@ static int cpu_pr_data(int pr) return 0; } -static ExitStatus gen_mfpr(TCGv va, int regno) +static ExitStatus gen_mfpr(DisasContext *ctx, TCGv va, int regno) { int data = cpu_pr_data(regno); @@ -1295,7 +1295,7 @@ static ExitStatus gen_mfpr(TCGv va, int regno) if (regno == 249) { helper = gen_helper_get_vmtime; } - if (use_icount) { + if (ctx->tb->cflags & CF_USE_ICOUNT) { gen_io_start(); helper(va); gen_io_end(); @@ -2283,7 +2283,7 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) case 0xC000: /* RPCC */ va = dest_gpr(ctx, ra); - if (use_icount) { + if (ctx->tb->cflags & CF_USE_ICOUNT) { gen_io_start(); gen_helper_load_pcc(va, cpu_env); gen_io_end(); @@ -2317,7 +2317,7 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) #ifndef CONFIG_USER_ONLY REQUIRE_TB_FLAG(TB_FLAGS_PAL_MODE); va = dest_gpr(ctx, ra); - ret = gen_mfpr(va, insn & 0xffff); + ret = gen_mfpr(ctx, va, insn & 0xffff); break; #else goto invalid_opc; @@ -2790,7 +2790,6 @@ static inline void gen_intermediate_code_internal(AlphaCPU *cpu, target_ulong pc_start; target_ulong pc_mask; uint32_t insn; - uint16_t *gen_opc_end; CPUBreakpoint *bp; int j, lj = -1; ExitStatus ret; @@ -2798,7 +2797,6 @@ static inline void gen_intermediate_code_internal(AlphaCPU *cpu, int max_insns; pc_start = tb->pc; - gen_opc_end = tcg_ctx.gen_opc_buf + OPC_MAX_SIZE; ctx.tb = tb; ctx.pc = pc_start; @@ -2828,7 +2826,7 @@ static inline void gen_intermediate_code_internal(AlphaCPU *cpu, pc_mask = ~TARGET_PAGE_MASK; } - gen_tb_start(); + gen_tb_start(tb); do { if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) { QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { @@ -2839,11 +2837,12 @@ static inline void gen_intermediate_code_internal(AlphaCPU *cpu, } } if (search_pc) { - j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + j = tcg_op_buf_count(); if (lj < j) { lj++; - while (lj < j) + while (lj < j) { tcg_ctx.gen_opc_instr_start[lj++] = 0; + } } tcg_ctx.gen_opc_pc[lj] = ctx.pc; tcg_ctx.gen_opc_instr_start[lj] = 1; @@ -2881,7 +2880,7 @@ static inline void gen_intermediate_code_internal(AlphaCPU *cpu, or exhaust instruction count, stop generation. */ if (ret == NO_EXIT && ((ctx.pc & pc_mask) == 0 - || tcg_ctx.gen_opc_ptr >= gen_opc_end + || tcg_op_buf_full() || num_insns >= max_insns || singlestep || ctx.singlestep_enabled)) { @@ -2912,12 +2911,13 @@ static inline void gen_intermediate_code_internal(AlphaCPU *cpu, } gen_tb_end(tb, num_insns); - *tcg_ctx.gen_opc_ptr = INDEX_op_end; + if (search_pc) { - j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + j = tcg_op_buf_count(); lj++; - while (lj <= j) + while (lj <= j) { tcg_ctx.gen_opc_instr_start[lj++] = 0; + } } else { tb->size = ctx.pc - pc_start; tb->icount = num_insns; diff --git a/target-arm/arm-semi.c b/target-arm/arm-semi.c index ebb5235..a8b83e6 100644 --- a/target-arm/arm-semi.c +++ b/target-arm/arm-semi.c @@ -58,6 +58,10 @@ #define TARGET_SYS_HEAPINFO 0x16 #define TARGET_SYS_EXIT 0x18 +/* ADP_Stopped_ApplicationExit is used for exit(0), + * anything else is implemented as exit(1) */ +#define ADP_Stopped_ApplicationExit (0x20026) + #ifndef O_BINARY #define O_BINARY 0 #endif @@ -551,8 +555,11 @@ uint32_t do_arm_semihosting(CPUARMState *env) return 0; } case TARGET_SYS_EXIT: - gdb_exit(env, 0); - exit(0); + /* ARM specifies only Stopped_ApplicationExit as normal + * exit, everything else is considered an error */ + ret = (args == ADP_Stopped_ApplicationExit) ? 0 : 1; + gdb_exit(env, ret); + exit(ret); default: fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr); cpu_dump_state(cs, stderr, fprintf, 0); diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h index dcfda7d..ed5a644 100644 --- a/target-arm/cpu-qom.h +++ b/target-arm/cpu-qom.h @@ -100,6 +100,8 @@ typedef struct ARMCPU { bool start_powered_off; /* CPU currently in PSCI powered-off state */ bool powered_off; + /* CPU has security extension */ + bool has_el3; /* PSCI conduit used to invoke PSCI methods * 0 - disabled, 1 - smc, 2 - hvc diff --git a/target-arm/cpu.c b/target-arm/cpu.c index 5ce7350..986f04c 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -109,11 +109,18 @@ static void arm_cpu_reset(CPUState *s) #if defined(CONFIG_USER_ONLY) env->pstate = PSTATE_MODE_EL0t; /* Userspace expects access to DC ZVA, CTL_EL0 and the cache ops */ - env->cp15.c1_sys |= SCTLR_UCT | SCTLR_UCI | SCTLR_DZE; + env->cp15.sctlr_el[1] |= SCTLR_UCT | SCTLR_UCI | SCTLR_DZE; /* and to the FP/Neon instructions */ env->cp15.c1_coproc = deposit64(env->cp15.c1_coproc, 20, 2, 3); #else - env->pstate = PSTATE_MODE_EL1h; + /* Reset into the highest available EL */ + if (arm_feature(env, ARM_FEATURE_EL3)) { + env->pstate = PSTATE_MODE_EL3h; + } else if (arm_feature(env, ARM_FEATURE_EL2)) { + env->pstate = PSTATE_MODE_EL2h; + } else { + env->pstate = PSTATE_MODE_EL1h; + } env->pc = cpu->rvbar; #endif } else { @@ -167,7 +174,11 @@ static void arm_cpu_reset(CPUState *s) env->thumb = initial_pc & 1; } - if (env->cp15.c1_sys & SCTLR_V) { + /* AArch32 has a hard highvec setting of 0xFFFF0000. If we are currently + * executing as AArch32 then check if highvecs are enabled and + * adjust the PC accordingly. + */ + if (A32_BANKED_CURRENT_REG_GET(env, sctlr) & SCTLR_V) { env->regs[15] = 0xFFFF0000; } @@ -316,6 +327,29 @@ static void arm_cpu_kvm_set_irq(void *opaque, int irq, int level) kvm_set_irq(kvm_state, kvm_irq, level ? 1 : 0); #endif } + +static bool arm_cpu_is_big_endian(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + int cur_el; + + cpu_synchronize_state(cs); + + /* In 32bit guest endianness is determined by looking at CPSR's E bit */ + if (!is_a64(env)) { + return (env->uncached_cpsr & CPSR_E) ? 1 : 0; + } + + cur_el = arm_current_el(env); + + if (cur_el == 0) { + return (env->cp15.sctlr_el[1] & SCTLR_E0E) != 0; + } + + return (env->cp15.sctlr_el[cur_el] & SCTLR_EE) != 0; +} + #endif static inline void set_feature(CPUARMState *env, int feature) @@ -323,6 +357,11 @@ static inline void set_feature(CPUARMState *env, int feature) env->features |= 1ULL << feature; } +static inline void unset_feature(CPUARMState *env, int feature) +{ + env->features &= ~(1ULL << feature); +} + static void arm_cpu_initfn(Object *obj) { CPUState *cs = CPU(obj); @@ -379,6 +418,9 @@ static Property arm_cpu_reset_hivecs_property = static Property arm_cpu_rvbar_property = DEFINE_PROP_UINT64("rvbar", ARMCPU, rvbar, 0); +static Property arm_cpu_has_el3_property = + DEFINE_PROP_BOOL("has_el3", ARMCPU, has_el3, true); + static void arm_cpu_post_init(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -398,6 +440,14 @@ static void arm_cpu_post_init(Object *obj) qdev_property_add_static(DEVICE(obj), &arm_cpu_rvbar_property, &error_abort); } + + if (arm_feature(&cpu->env, ARM_FEATURE_EL3)) { + /* Add the has_el3 state CPU property only if EL3 is allowed. This will + * prevent "has_el3" from existing on CPUs which cannot support EL3. + */ + qdev_property_add_static(DEVICE(obj), &arm_cpu_has_el3_property, + &error_abort); + } } static void arm_cpu_finalizefn(Object *obj) @@ -467,6 +517,18 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) cpu->reset_sctlr |= (1 << 13); } + if (!cpu->has_el3) { + /* If the has_el3 CPU property is disabled then we need to disable the + * feature. + */ + unset_feature(env, ARM_FEATURE_EL3); + + /* Disable the security extension feature bits in the processor feature + * register as well. This is id_pfr1[7:4]. + */ + cpu->id_pfr1 &= ~0xf0; + } + register_cp_regs_for_features(cpu); arm_cpu_register_gdb_regs_for_features(cpu); @@ -482,13 +544,16 @@ static ObjectClass *arm_cpu_class_by_name(const char *cpu_model) { ObjectClass *oc; char *typename; + char **cpuname; if (!cpu_model) { return NULL; } - typename = g_strdup_printf("%s-" TYPE_ARM_CPU, cpu_model); + cpuname = g_strsplit(cpu_model, ",", 1); + typename = g_strdup_printf("%s-" TYPE_ARM_CPU, cpuname[0]); oc = object_class_by_name(typename); + g_strfreev(cpuname); g_free(typename); if (!oc || !object_class_dynamic_cast(oc, TYPE_ARM_CPU) || object_class_is_abstract(oc)) { @@ -548,7 +613,7 @@ static void arm1026_initfn(Object *obj) ARMCPRegInfo ifar = { .name = "IFAR", .cp = 15, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 1, .access = PL1_RW, - .fieldoffset = offsetofhigh32(CPUARMState, cp15.far_el[1]), + .fieldoffset = offsetof(CPUARMState, cp15.ifar_ns), .resetvalue = 0 }; define_one_arm_cp_reg(cpu, &ifar); @@ -636,6 +701,7 @@ static void arm1176_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); set_feature(&cpu->env, ARM_FEATURE_CACHE_DIRTY_REG); set_feature(&cpu->env, ARM_FEATURE_CACHE_BLOCK_OPS); + set_feature(&cpu->env, ARM_FEATURE_EL3); cpu->midr = 0x410fb767; cpu->reset_fpsid = 0x410120b5; cpu->mvfr0 = 0x11111111; @@ -724,6 +790,7 @@ static void cortex_a8_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); + set_feature(&cpu->env, ARM_FEATURE_EL3); cpu->midr = 0x410fc080; cpu->reset_fpsid = 0x410330c0; cpu->mvfr0 = 0x11110222; @@ -791,6 +858,7 @@ static void cortex_a9_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_VFP_FP16); set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); + set_feature(&cpu->env, ARM_FEATURE_EL3); /* Note that A9 supports the MP extensions even for * A9UP and single-core A9MP (which are both different * and valid configurations; we don't model A9UP). @@ -858,6 +926,7 @@ static void cortex_a15_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); set_feature(&cpu->env, ARM_FEATURE_LPAE); + set_feature(&cpu->env, ARM_FEATURE_EL3); cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A15; cpu->midr = 0x412fc0f1; cpu->reset_fpsid = 0x410430f0; @@ -1153,6 +1222,7 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data) cc->do_interrupt = arm_cpu_do_interrupt; cc->get_phys_page_debug = arm_cpu_get_phys_page_debug; cc->vmsd = &vmstate_arm_cpu; + cc->virtio_is_big_endian = arm_cpu_is_big_endian; #endif cc->gdb_num_core_regs = 26; cc->gdb_core_xml_file = "arm-core.xml"; diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 7f80090..083211c 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -32,6 +32,8 @@ # define ELF_MACHINE EM_ARM #endif +#define TARGET_IS_BIENDIAN 1 + #define CPUArchState struct CPUARMState #include "qemu-common.h" @@ -39,8 +41,6 @@ #include "fpu/softfloat.h" -#define TARGET_HAS_ICE 1 - #define EXCP_UDEF 1 /* undefined instruction */ #define EXCP_SWI 2 /* software interrupt */ #define EXCP_PREFETCH_ABORT 3 @@ -100,7 +100,7 @@ typedef uint32_t ARMReadCPFunc(void *opaque, int cp_info, struct arm_boot_info; -#define NB_MMU_MODES 4 +#define NB_MMU_MODES 7 /* We currently assume float and double are IEEE single and double precision respectively. @@ -120,6 +120,12 @@ typedef struct ARMGenericTimer { #define GTIMER_VIRT 1 #define NUM_GTIMERS 2 +typedef struct { + uint64_t raw_tcr; + uint32_t mask; + uint32_t base_mask; +} TCR; + typedef struct CPUARMState { /* Regs for current mode. */ uint32_t regs[16]; @@ -177,28 +183,111 @@ typedef struct CPUARMState { /* System control coprocessor (cp15) */ struct { uint32_t c0_cpuid; - uint64_t c0_cssel; /* Cache size selection. */ - uint64_t c1_sys; /* System control register. */ + union { /* Cache size selection */ + struct { + uint64_t _unused_csselr0; + uint64_t csselr_ns; + uint64_t _unused_csselr1; + uint64_t csselr_s; + }; + uint64_t csselr_el[4]; + }; + union { /* System control register. */ + struct { + uint64_t _unused_sctlr; + uint64_t sctlr_ns; + uint64_t hsctlr; + uint64_t sctlr_s; + }; + uint64_t sctlr_el[4]; + }; uint64_t c1_coproc; /* Coprocessor access register. */ uint32_t c1_xscaleauxcr; /* XScale auxiliary control register. */ - uint64_t ttbr0_el1; /* MMU translation table base 0. */ - uint64_t ttbr1_el1; /* MMU translation table base 1. */ - uint64_t c2_control; /* MMU translation table base control. */ - uint32_t c2_mask; /* MMU translation table base selection mask. */ - uint32_t c2_base_mask; /* MMU translation table base 0 mask. */ + uint64_t sder; /* Secure debug enable register. */ + uint32_t nsacr; /* Non-secure access control register. */ + union { /* MMU translation table base 0. */ + struct { + uint64_t _unused_ttbr0_0; + uint64_t ttbr0_ns; + uint64_t _unused_ttbr0_1; + uint64_t ttbr0_s; + }; + uint64_t ttbr0_el[4]; + }; + union { /* MMU translation table base 1. */ + struct { + uint64_t _unused_ttbr1_0; + uint64_t ttbr1_ns; + uint64_t _unused_ttbr1_1; + uint64_t ttbr1_s; + }; + uint64_t ttbr1_el[4]; + }; + /* MMU translation table base control. */ + TCR tcr_el[4]; uint32_t c2_data; /* MPU data cachable bits. */ uint32_t c2_insn; /* MPU instruction cachable bits. */ - uint32_t c3; /* MMU domain access control register - MPU write buffer control. */ + union { /* MMU domain access control register + * MPU write buffer control. + */ + struct { + uint64_t dacr_ns; + uint64_t dacr_s; + }; + struct { + uint64_t dacr32_el2; + }; + }; uint32_t pmsav5_data_ap; /* PMSAv5 MPU data access permissions */ uint32_t pmsav5_insn_ap; /* PMSAv5 MPU insn access permissions */ uint64_t hcr_el2; /* Hypervisor configuration register */ uint64_t scr_el3; /* Secure configuration register. */ - uint32_t ifsr_el2; /* Fault status registers. */ - uint64_t esr_el[4]; + union { /* Fault status registers. */ + struct { + uint64_t ifsr_ns; + uint64_t ifsr_s; + }; + struct { + uint64_t ifsr32_el2; + }; + }; + union { + struct { + uint64_t _unused_dfsr; + uint64_t dfsr_ns; + uint64_t hsr; + uint64_t dfsr_s; + }; + uint64_t esr_el[4]; + }; uint32_t c6_region[8]; /* MPU base/size registers. */ - uint64_t far_el[4]; /* Fault address registers. */ - uint64_t par_el1; /* Translation result. */ + union { /* Fault address registers. */ + struct { + uint64_t _unused_far0; +#ifdef HOST_WORDS_BIGENDIAN + uint32_t ifar_ns; + uint32_t dfar_ns; + uint32_t ifar_s; + uint32_t dfar_s; +#else + uint32_t dfar_ns; + uint32_t ifar_ns; + uint32_t dfar_s; + uint32_t ifar_s; +#endif + uint64_t _unused_far3; + }; + uint64_t far_el[4]; + }; + union { /* Translation result. */ + struct { + uint64_t _unused_par_0; + uint64_t par_ns; + uint64_t _unused_par_1; + uint64_t par_s; + }; + uint64_t par_el[4]; + }; uint32_t c9_insn; /* Cache lockdown registers. */ uint32_t c9_data; uint64_t c9_pmcr; /* performance monitor control register */ @@ -207,13 +296,67 @@ typedef struct CPUARMState { uint32_t c9_pmxevtyper; /* perf monitor event type */ uint32_t c9_pmuserenr; /* perf monitor user enable */ uint32_t c9_pminten; /* perf monitor interrupt enables */ - uint64_t mair_el1; - uint64_t vbar_el[4]; /* vector base address register */ - uint32_t c13_fcse; /* FCSE PID. */ - uint64_t contextidr_el1; /* Context ID. */ - uint64_t tpidr_el0; /* User RW Thread register. */ - uint64_t tpidrro_el0; /* User RO Thread register. */ - uint64_t tpidr_el1; /* Privileged Thread register. */ + union { /* Memory attribute redirection */ + struct { +#ifdef HOST_WORDS_BIGENDIAN + uint64_t _unused_mair_0; + uint32_t mair1_ns; + uint32_t mair0_ns; + uint64_t _unused_mair_1; + uint32_t mair1_s; + uint32_t mair0_s; +#else + uint64_t _unused_mair_0; + uint32_t mair0_ns; + uint32_t mair1_ns; + uint64_t _unused_mair_1; + uint32_t mair0_s; + uint32_t mair1_s; +#endif + }; + uint64_t mair_el[4]; + }; + union { /* vector base address register */ + struct { + uint64_t _unused_vbar; + uint64_t vbar_ns; + uint64_t hvbar; + uint64_t vbar_s; + }; + uint64_t vbar_el[4]; + }; + uint32_t mvbar; /* (monitor) vector base address register */ + struct { /* FCSE PID. */ + uint32_t fcseidr_ns; + uint32_t fcseidr_s; + }; + union { /* Context ID. */ + struct { + uint64_t _unused_contextidr_0; + uint64_t contextidr_ns; + uint64_t _unused_contextidr_1; + uint64_t contextidr_s; + }; + uint64_t contextidr_el[4]; + }; + union { /* User RW Thread register. */ + struct { + uint64_t tpidrurw_ns; + uint64_t tpidrprw_ns; + uint64_t htpidr; + uint64_t _tpidr_el3; + }; + uint64_t tpidr_el[4]; + }; + /* The secure banks of these registers don't map anywhere */ + uint64_t tpidrurw_s; + uint64_t tpidrprw_s; + uint64_t tpidruro_s; + + union { /* User RO Thread register. */ + uint64_t tpidruro_ns; + uint64_t tpidrro_el[1]; + }; uint64_t c14_cntfrq; /* Counter Frequency register */ uint64_t c14_cntkctl; /* Timer Control register */ ARMGenericTimer c14_timer[NUM_GTIMERS]; @@ -352,6 +495,8 @@ typedef struct CPUARMState { ARMCPU *cpu_arm_init(const char *cpu_model); int cpu_arm_exec(CPUARMState *s); uint32_t do_arm_semihosting(CPUARMState *env); +void aarch64_sync_32_to_64(CPUARMState *env); +void aarch64_sync_64_to_32(CPUARMState *env); static inline bool is_a64(CPUARMState *env) { @@ -817,6 +962,49 @@ static inline bool arm_el_is_aa64(CPUARMState *env, int el) return arm_feature(env, ARM_FEATURE_AARCH64); } +/* Function for determing whether guest cp register reads and writes should + * access the secure or non-secure bank of a cp register. When EL3 is + * operating in AArch32 state, the NS-bit determines whether the secure + * instance of a cp register should be used. When EL3 is AArch64 (or if + * it doesn't exist at all) then there is no register banking, and all + * accesses are to the non-secure version. + */ +static inline bool access_secure_reg(CPUARMState *env) +{ + bool ret = (arm_feature(env, ARM_FEATURE_EL3) && + !arm_el_is_aa64(env, 3) && + !(env->cp15.scr_el3 & SCR_NS)); + + return ret; +} + +/* Macros for accessing a specified CP register bank */ +#define A32_BANKED_REG_GET(_env, _regname, _secure) \ + ((_secure) ? (_env)->cp15._regname##_s : (_env)->cp15._regname##_ns) + +#define A32_BANKED_REG_SET(_env, _regname, _secure, _val) \ + do { \ + if (_secure) { \ + (_env)->cp15._regname##_s = (_val); \ + } else { \ + (_env)->cp15._regname##_ns = (_val); \ + } \ + } while (0) + +/* Macros for automatically accessing a specific CP register bank depending on + * the current secure state of the system. These macros are not intended for + * supporting instruction translation reads/writes as these are dependent + * solely on the SCR.NS bit and not the mode. + */ +#define A32_BANKED_CURRENT_REG_GET(_env, _regname) \ + A32_BANKED_REG_GET((_env), _regname, \ + ((!arm_el_is_aa64((_env), 3) && arm_is_secure(_env)))) + +#define A32_BANKED_CURRENT_REG_SET(_env, _regname, _val) \ + A32_BANKED_REG_SET((_env), _regname, \ + ((!arm_el_is_aa64((_env), 3) && arm_is_secure(_env))), \ + (_val)) + void arm_cpu_list(FILE *f, fprintf_function cpu_fprintf); unsigned int arm_excp_target_el(CPUState *cs, unsigned int excp_idx); @@ -836,6 +1024,7 @@ void armv7m_nvic_complete_irq(void *opaque, int irq); * Crn, Crm, opc1, opc2 fields * 32 or 64 bit register (ie is it accessed via MRC/MCR * or via MRRC/MCRR?) + * non-secure/secure bank (AArch32 only) * We allow 4 bits for opc1 because MRRC/MCRR have a 4 bit field. * (In this case crn and opc2 should be zero.) * For AArch64, there is no 32/64 bit size distinction; @@ -853,9 +1042,16 @@ void armv7m_nvic_complete_irq(void *opaque, int irq); #define CP_REG_AA64_SHIFT 28 #define CP_REG_AA64_MASK (1 << CP_REG_AA64_SHIFT) -#define ENCODE_CP_REG(cp, is64, crn, crm, opc1, opc2) \ - (((cp) << 16) | ((is64) << 15) | ((crn) << 11) | \ - ((crm) << 7) | ((opc1) << 3) | (opc2)) +/* To enable banking of coprocessor registers depending on ns-bit we + * add a bit to distinguish between secure and non-secure cpregs in the + * hashtable. + */ +#define CP_REG_NS_SHIFT 29 +#define CP_REG_NS_MASK (1 << CP_REG_NS_SHIFT) + +#define ENCODE_CP_REG(cp, is64, ns, crn, crm, opc1, opc2) \ + ((ns) << CP_REG_NS_SHIFT | ((cp) << 16) | ((is64) << 15) | \ + ((crn) << 11) | ((crm) << 7) | ((opc1) << 3) | (opc2)) #define ENCODE_AA64_CP_REG(cp, crn, crm, op0, op1, op2) \ (CP_REG_AA64_MASK | \ @@ -874,8 +1070,15 @@ static inline uint32_t kvm_to_cpreg_id(uint64_t kvmid) uint32_t cpregid = kvmid; if ((kvmid & CP_REG_ARCH_MASK) == CP_REG_ARM64) { cpregid |= CP_REG_AA64_MASK; - } else if ((kvmid & CP_REG_SIZE_MASK) == CP_REG_SIZE_U64) { - cpregid |= (1 << 15); + } else { + if ((kvmid & CP_REG_SIZE_MASK) == CP_REG_SIZE_U64) { + cpregid |= (1 << 15); + } + + /* KVM is always non-secure so add the NS flag on AArch32 register + * entries. + */ + cpregid |= 1 << CP_REG_NS_SHIFT; } return cpregid; } @@ -911,8 +1114,14 @@ static inline uint64_t cpreg_to_kvm_id(uint32_t cpregid) * a register definition to override a previous definition for the * same (cp, is64, crn, crm, opc1, opc2) tuple: either the new or the * old must have the OVERRIDE bit set. - * NO_MIGRATE indicates that this register should be ignored for migration; - * (eg because any state is accessed via some other coprocessor register). + * ALIAS indicates that this register is an alias view of some underlying + * state which is also visible via another register, and that the other + * register is handling migration; registers marked ALIAS will not be migrated + * but may have their state set by syncing of register state from KVM. + * NO_RAW indicates that this register has no underlying state and does not + * support raw access for state saving/loading; it will not be used for either + * migration or KVM state synchronization. (Typically this is for "registers" + * which are actually used as instructions for cache maintenance and so on.) * IO indicates that this register does I/O and therefore its accesses * need to be surrounded by gen_io_start()/gen_io_end(). In particular, * registers which implement clocks or timers require this. @@ -922,8 +1131,9 @@ static inline uint64_t cpreg_to_kvm_id(uint32_t cpregid) #define ARM_CP_64BIT 4 #define ARM_CP_SUPPRESS_TB_END 8 #define ARM_CP_OVERRIDE 16 -#define ARM_CP_NO_MIGRATE 32 +#define ARM_CP_ALIAS 32 #define ARM_CP_IO 64 +#define ARM_CP_NO_RAW 128 #define ARM_CP_NOP (ARM_CP_SPECIAL | (1 << 8)) #define ARM_CP_WFI (ARM_CP_SPECIAL | (2 << 8)) #define ARM_CP_NZCV (ARM_CP_SPECIAL | (3 << 8)) @@ -933,7 +1143,7 @@ static inline uint64_t cpreg_to_kvm_id(uint32_t cpregid) /* Used only as a terminator for ARMCPRegInfo lists */ #define ARM_CP_SENTINEL 0xffff /* Mask of only the flag bits in a type field */ -#define ARM_CP_FLAG_MASK 0x7f +#define ARM_CP_FLAG_MASK 0xff /* Valid values for ARMCPRegInfo state field, indicating which of * the AArch32 and AArch64 execution states this register is visible in. @@ -950,6 +1160,21 @@ enum { ARM_CP_STATE_BOTH = 2, }; +/* ARM CP register secure state flags. These flags identify security state + * attributes for a given CP register entry. + * The existence of both or neither secure and non-secure flags indicates that + * the register has both a secure and non-secure hash entry. A single one of + * these flags causes the register to only be hashed for the specified + * security state. + * Although definitions may have any combination of the S/NS bits, each + * registered entry will only have one to identify whether the entry is secure + * or non-secure. + */ +enum { + ARM_CP_SECSTATE_S = (1 << 0), /* bit[0]: Secure state register */ + ARM_CP_SECSTATE_NS = (1 << 1), /* bit[1]: Non-secure state register */ +}; + /* Return true if cptype is a valid type field. This is used to try to * catch errors where the sentinel has been accidentally left off the end * of a list of registers. @@ -997,6 +1222,10 @@ static inline bool cptype_valid(int cptype) */ static inline int arm_current_el(CPUARMState *env) { + if (arm_feature(env, ARM_FEATURE_M)) { + return !((env->v7m.exception == 0) && (env->v7m.control & 1)); + } + if (is_a64(env)) { return extract32(env->pstate, 2, 2); } @@ -1084,6 +1313,8 @@ struct ARMCPRegInfo { int type; /* Access rights: PL*_[RW] */ int access; + /* Security state: ARM_CP_SECSTATE_* bits/values */ + int secure; /* The opaque pointer passed to define_arm_cp_regs_with_opaque() when * this register was defined: can be used to hand data through to the * register read/write functions, since they are passed the ARMCPRegInfo*. @@ -1093,12 +1324,27 @@ struct ARMCPRegInfo { * fieldoffset is non-zero, the reset value of the register. */ uint64_t resetvalue; - /* Offset of the field in CPUARMState for this register. This is not - * needed if either: + /* Offset of the field in CPUARMState for this register. + * + * This is not needed if either: * 1. type is ARM_CP_CONST or one of the ARM_CP_SPECIALs * 2. both readfn and writefn are specified */ ptrdiff_t fieldoffset; /* offsetof(CPUARMState, field) */ + + /* Offsets of the secure and non-secure fields in CPUARMState for the + * register if it is banked. These fields are only used during the static + * registration of a register. During hashing the bank associated + * with a given security state is copied to fieldoffset which is used from + * there on out. + * + * It is expected that register definitions use either fieldoffset or + * bank_fieldoffsets in the definition but not both. It is also expected + * that both bank offsets are set when defining a banked register. This + * use indicates that a register is banked. + */ + ptrdiff_t bank_fieldoffsets[2]; + /* Function for making any access checks for this register in addition to * those specified by the 'access' permissions bits. If NULL, no extra * checks required. The access check is performed at runtime, not at @@ -1247,27 +1493,50 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx) CPUARMState *env = cs->env_ptr; unsigned int cur_el = arm_current_el(env); unsigned int target_el = arm_excp_target_el(cs, excp_idx); - /* FIXME: Use actual secure state. */ - bool secure = false; - /* If in EL1/0, Physical IRQ routing to EL2 only happens from NS state. */ - bool irq_can_hyp = !secure && cur_el < 2 && target_el == 2; - - /* Don't take exceptions if they target a lower EL. */ + bool secure = arm_is_secure(env); + uint32_t scr; + uint32_t hcr; + bool pstate_unmasked; + int8_t unmasked = 0; + + /* Don't take exceptions if they target a lower EL. + * This check should catch any exceptions that would not be taken but left + * pending. + */ if (cur_el > target_el) { return false; } switch (excp_idx) { case EXCP_FIQ: - if (irq_can_hyp && (env->cp15.hcr_el2 & HCR_FMO)) { - return true; - } - return !(env->daif & PSTATE_F); + /* If FIQs are routed to EL3 or EL2 then there are cases where we + * override the CPSR.F in determining if the exception is masked or + * not. If neither of these are set then we fall back to the CPSR.F + * setting otherwise we further assess the state below. + */ + hcr = (env->cp15.hcr_el2 & HCR_FMO); + scr = (env->cp15.scr_el3 & SCR_FIQ); + + /* When EL3 is 32-bit, the SCR.FW bit controls whether the CPSR.F bit + * masks FIQ interrupts when taken in non-secure state. If SCR.FW is + * set then FIQs can be masked by CPSR.F when non-secure but only + * when FIQs are only routed to EL3. + */ + scr &= !((env->cp15.scr_el3 & SCR_FW) && !hcr); + pstate_unmasked = !(env->daif & PSTATE_F); + break; + case EXCP_IRQ: - if (irq_can_hyp && (env->cp15.hcr_el2 & HCR_IMO)) { - return true; - } - return !(env->daif & PSTATE_I); + /* When EL3 execution state is 32-bit, if HCR.IMO is set then we may + * override the CPSR.I masking when in non-secure state. The SCR.IRQ + * setting has already been taken into consideration when setting the + * target EL, so it does not have a further affect here. + */ + hcr = (env->cp15.hcr_el2 & HCR_IMO); + scr = false; + pstate_unmasked = !(env->daif & PSTATE_I); + break; + case EXCP_VFIQ: if (secure || !(env->cp15.hcr_el2 & HCR_FMO)) { /* VFIQs are only taken when hypervized and non-secure. */ @@ -1283,29 +1552,114 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx) default: g_assert_not_reached(); } -} -static inline CPUARMState *cpu_init(const char *cpu_model) -{ - ARMCPU *cpu = cpu_arm_init(cpu_model); - if (cpu) { - return &cpu->env; + /* Use the target EL, current execution state and SCR/HCR settings to + * determine whether the corresponding CPSR bit is used to mask the + * interrupt. + */ + if ((target_el > cur_el) && (target_el != 1)) { + if (arm_el_is_aa64(env, 3) || ((scr || hcr) && (!secure))) { + unmasked = 1; + } } - return NULL; + + /* The PSTATE bits only mask the interrupt if we have not overriden the + * ability above. + */ + return unmasked || pstate_unmasked; } +#define cpu_init(cpu_model) CPU(cpu_arm_init(cpu_model)) + #define cpu_exec cpu_arm_exec #define cpu_gen_code cpu_arm_gen_code #define cpu_signal_handler cpu_arm_signal_handler #define cpu_list arm_cpu_list -/* MMU modes definitions */ -#define MMU_MODE0_SUFFIX _user -#define MMU_MODE1_SUFFIX _kernel +/* ARM has the following "translation regimes" (as the ARM ARM calls them): + * + * If EL3 is 64-bit: + * + NonSecure EL1 & 0 stage 1 + * + NonSecure EL1 & 0 stage 2 + * + NonSecure EL2 + * + Secure EL1 & EL0 + * + Secure EL3 + * If EL3 is 32-bit: + * + NonSecure PL1 & 0 stage 1 + * + NonSecure PL1 & 0 stage 2 + * + NonSecure PL2 + * + Secure PL0 & PL1 + * (reminder: for 32 bit EL3, Secure PL1 is *EL3*, not EL1.) + * + * For QEMU, an mmu_idx is not quite the same as a translation regime because: + * 1. we need to split the "EL1 & 0" regimes into two mmu_idxes, because they + * may differ in access permissions even if the VA->PA map is the same + * 2. we want to cache in our TLB the full VA->IPA->PA lookup for a stage 1+2 + * translation, which means that we have one mmu_idx that deals with two + * concatenated translation regimes [this sort of combined s1+2 TLB is + * architecturally permitted] + * 3. we don't need to allocate an mmu_idx to translations that we won't be + * handling via the TLB. The only way to do a stage 1 translation without + * the immediate stage 2 translation is via the ATS or AT system insns, + * which can be slow-pathed and always do a page table walk. + * 4. we can also safely fold together the "32 bit EL3" and "64 bit EL3" + * translation regimes, because they map reasonably well to each other + * and they can't both be active at the same time. + * This gives us the following list of mmu_idx values: + * + * NS EL0 (aka NS PL0) stage 1+2 + * NS EL1 (aka NS PL1) stage 1+2 + * NS EL2 (aka NS PL2) + * S EL3 (aka S PL1) + * S EL0 (aka S PL0) + * S EL1 (not used if EL3 is 32 bit) + * NS EL0+1 stage 2 + * + * (The last of these is an mmu_idx because we want to be able to use the TLB + * for the accesses done as part of a stage 1 page table walk, rather than + * having to walk the stage 2 page table over and over.) + * + * Our enumeration includes at the end some entries which are not "true" + * mmu_idx values in that they don't have corresponding TLBs and are only + * valid for doing slow path page table walks. + * + * The constant names here are patterned after the general style of the names + * of the AT/ATS operations. + * The values used are carefully arranged to make mmu_idx => EL lookup easy. + */ +typedef enum ARMMMUIdx { + ARMMMUIdx_S12NSE0 = 0, + ARMMMUIdx_S12NSE1 = 1, + ARMMMUIdx_S1E2 = 2, + ARMMMUIdx_S1E3 = 3, + ARMMMUIdx_S1SE0 = 4, + ARMMMUIdx_S1SE1 = 5, + ARMMMUIdx_S2NS = 6, + /* Indexes below here don't have TLBs and are used only for AT system + * instructions or for the first stage of an S12 page table walk. + */ + ARMMMUIdx_S1NSE0 = 7, + ARMMMUIdx_S1NSE1 = 8, +} ARMMMUIdx; + #define MMU_USER_IDX 0 -static inline int cpu_mmu_index (CPUARMState *env) + +/* Return the exception level we're running at if this is our mmu_idx */ +static inline int arm_mmu_idx_to_el(ARMMMUIdx mmu_idx) { - return arm_current_el(env); + assert(mmu_idx < ARMMMUIdx_S2NS); + return mmu_idx & 3; +} + +/* Determine the current mmu_idx to use for normal loads/stores */ +static inline int cpu_mmu_index(CPUARMState *env) +{ + int el = arm_current_el(env); + + if (el < 2 && arm_is_secure_below_el3(env)) { + return ARMMMUIdx_S1SE0 + el; + } + return el; } /* Return the Exception Level targeted by debug exceptions; @@ -1372,9 +1726,13 @@ static inline bool arm_singlestep_active(CPUARMState *env) /* Bit usage in the TB flags field: bit 31 indicates whether we are * in 32 or 64 bit mode. The meaning of the other bits depends on that. + * We put flags which are shared between 32 and 64 bit mode at the top + * of the word, and flags which apply to only one mode at the bottom. */ #define ARM_TBFLAG_AARCH64_STATE_SHIFT 31 #define ARM_TBFLAG_AARCH64_STATE_MASK (1U << ARM_TBFLAG_AARCH64_STATE_SHIFT) +#define ARM_TBFLAG_MMUIDX_SHIFT 28 +#define ARM_TBFLAG_MMUIDX_MASK (0x7 << ARM_TBFLAG_MMUIDX_SHIFT) /* Bit usage when in AArch32 state: */ #define ARM_TBFLAG_THUMB_SHIFT 0 @@ -1383,8 +1741,6 @@ static inline bool arm_singlestep_active(CPUARMState *env) #define ARM_TBFLAG_VECLEN_MASK (0x7 << ARM_TBFLAG_VECLEN_SHIFT) #define ARM_TBFLAG_VECSTRIDE_SHIFT 4 #define ARM_TBFLAG_VECSTRIDE_MASK (0x3 << ARM_TBFLAG_VECSTRIDE_SHIFT) -#define ARM_TBFLAG_PRIV_SHIFT 6 -#define ARM_TBFLAG_PRIV_MASK (1 << ARM_TBFLAG_PRIV_SHIFT) #define ARM_TBFLAG_VFPEN_SHIFT 7 #define ARM_TBFLAG_VFPEN_MASK (1 << ARM_TBFLAG_VFPEN_SHIFT) #define ARM_TBFLAG_CONDEXEC_SHIFT 8 @@ -1402,10 +1758,14 @@ static inline bool arm_singlestep_active(CPUARMState *env) */ #define ARM_TBFLAG_XSCALE_CPAR_SHIFT 20 #define ARM_TBFLAG_XSCALE_CPAR_MASK (3 << ARM_TBFLAG_XSCALE_CPAR_SHIFT) +/* Indicates whether cp register reads and writes by guest code should access + * the secure or nonsecure bank of banked registers; note that this is not + * the same thing as the current security state of the processor! + */ +#define ARM_TBFLAG_NS_SHIFT 22 +#define ARM_TBFLAG_NS_MASK (1 << ARM_TBFLAG_NS_SHIFT) /* Bit usage when in AArch64 state */ -#define ARM_TBFLAG_AA64_EL_SHIFT 0 -#define ARM_TBFLAG_AA64_EL_MASK (0x3 << ARM_TBFLAG_AA64_EL_SHIFT) #define ARM_TBFLAG_AA64_FPEN_SHIFT 2 #define ARM_TBFLAG_AA64_FPEN_MASK (1 << ARM_TBFLAG_AA64_FPEN_SHIFT) #define ARM_TBFLAG_AA64_SS_ACTIVE_SHIFT 3 @@ -1416,14 +1776,14 @@ static inline bool arm_singlestep_active(CPUARMState *env) /* some convenience accessor macros */ #define ARM_TBFLAG_AARCH64_STATE(F) \ (((F) & ARM_TBFLAG_AARCH64_STATE_MASK) >> ARM_TBFLAG_AARCH64_STATE_SHIFT) +#define ARM_TBFLAG_MMUIDX(F) \ + (((F) & ARM_TBFLAG_MMUIDX_MASK) >> ARM_TBFLAG_MMUIDX_SHIFT) #define ARM_TBFLAG_THUMB(F) \ (((F) & ARM_TBFLAG_THUMB_MASK) >> ARM_TBFLAG_THUMB_SHIFT) #define ARM_TBFLAG_VECLEN(F) \ (((F) & ARM_TBFLAG_VECLEN_MASK) >> ARM_TBFLAG_VECLEN_SHIFT) #define ARM_TBFLAG_VECSTRIDE(F) \ (((F) & ARM_TBFLAG_VECSTRIDE_MASK) >> ARM_TBFLAG_VECSTRIDE_SHIFT) -#define ARM_TBFLAG_PRIV(F) \ - (((F) & ARM_TBFLAG_PRIV_MASK) >> ARM_TBFLAG_PRIV_SHIFT) #define ARM_TBFLAG_VFPEN(F) \ (((F) & ARM_TBFLAG_VFPEN_MASK) >> ARM_TBFLAG_VFPEN_SHIFT) #define ARM_TBFLAG_CONDEXEC(F) \ @@ -1438,14 +1798,14 @@ static inline bool arm_singlestep_active(CPUARMState *env) (((F) & ARM_TBFLAG_PSTATE_SS_MASK) >> ARM_TBFLAG_PSTATE_SS_SHIFT) #define ARM_TBFLAG_XSCALE_CPAR(F) \ (((F) & ARM_TBFLAG_XSCALE_CPAR_MASK) >> ARM_TBFLAG_XSCALE_CPAR_SHIFT) -#define ARM_TBFLAG_AA64_EL(F) \ - (((F) & ARM_TBFLAG_AA64_EL_MASK) >> ARM_TBFLAG_AA64_EL_SHIFT) #define ARM_TBFLAG_AA64_FPEN(F) \ (((F) & ARM_TBFLAG_AA64_FPEN_MASK) >> ARM_TBFLAG_AA64_FPEN_SHIFT) #define ARM_TBFLAG_AA64_SS_ACTIVE(F) \ (((F) & ARM_TBFLAG_AA64_SS_ACTIVE_MASK) >> ARM_TBFLAG_AA64_SS_ACTIVE_SHIFT) #define ARM_TBFLAG_AA64_PSTATE_SS(F) \ (((F) & ARM_TBFLAG_AA64_PSTATE_SS_MASK) >> ARM_TBFLAG_AA64_PSTATE_SS_SHIFT) +#define ARM_TBFLAG_NS(F) \ + (((F) & ARM_TBFLAG_NS_MASK) >> ARM_TBFLAG_NS_SHIFT) static inline void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, target_ulong *cs_base, int *flags) @@ -1461,8 +1821,7 @@ static inline void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, if (is_a64(env)) { *pc = env->pc; - *flags = ARM_TBFLAG_AARCH64_STATE_MASK - | (arm_current_el(env) << ARM_TBFLAG_AA64_EL_SHIFT); + *flags = ARM_TBFLAG_AARCH64_STATE_MASK; if (fpen == 3 || (fpen == 1 && arm_current_el(env) != 0)) { *flags |= ARM_TBFLAG_AA64_FPEN_MASK; } @@ -1480,20 +1839,14 @@ static inline void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, } } } else { - int privmode; *pc = env->regs[15]; *flags = (env->thumb << ARM_TBFLAG_THUMB_SHIFT) | (env->vfp.vec_len << ARM_TBFLAG_VECLEN_SHIFT) | (env->vfp.vec_stride << ARM_TBFLAG_VECSTRIDE_SHIFT) | (env->condexec_bits << ARM_TBFLAG_CONDEXEC_SHIFT) | (env->bswap_code << ARM_TBFLAG_BSWAP_CODE_SHIFT); - if (arm_feature(env, ARM_FEATURE_M)) { - privmode = !((env->v7m.exception == 0) && (env->v7m.control & 1)); - } else { - privmode = (env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR; - } - if (privmode) { - *flags |= ARM_TBFLAG_PRIV_MASK; + if (!(access_secure_reg(env))) { + *flags |= ARM_TBFLAG_NS_MASK; } if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30) || arm_el_is_aa64(env, 1)) { @@ -1519,6 +1872,8 @@ static inline void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, << ARM_TBFLAG_XSCALE_CPAR_SHIFT); } + *flags |= (cpu_mmu_index(env) << ARM_TBFLAG_MMUIDX_SHIFT); + *cs_base = 0; } diff --git a/target-arm/cpu64.c b/target-arm/cpu64.c index bb778b3..270bc2f 100644 --- a/target-arm/cpu64.c +++ b/target-arm/cpu64.c @@ -32,6 +32,11 @@ static inline void set_feature(CPUARMState *env, int feature) env->features |= 1ULL << feature; } +static inline void unset_feature(CPUARMState *env, int feature) +{ + env->features &= ~(1ULL << feature); +} + #ifndef CONFIG_USER_ONLY static uint64_t a57_l2ctlr_read(CPUARMState *env, const ARMCPRegInfo *ri) { @@ -91,6 +96,7 @@ static void aarch64_a57_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + cpu->dtb_compatible = "arm,cortex-a57"; set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_VFP4); set_feature(&cpu->env, ARM_FEATURE_NEON); @@ -170,8 +176,42 @@ static const ARMCPUInfo aarch64_cpus[] = { { .name = NULL } }; +static bool aarch64_cpu_get_aarch64(Object *obj, Error **errp) +{ + ARMCPU *cpu = ARM_CPU(obj); + + return arm_feature(&cpu->env, ARM_FEATURE_AARCH64); +} + +static void aarch64_cpu_set_aarch64(Object *obj, bool value, Error **errp) +{ + ARMCPU *cpu = ARM_CPU(obj); + + /* At this time, this property is only allowed if KVM is enabled. This + * restriction allows us to avoid fixing up functionality that assumes a + * uniform execution state like do_interrupt. + */ + if (!kvm_enabled()) { + error_setg(errp, "'aarch64' feature cannot be disabled " + "unless KVM is enabled"); + return; + } + + if (value == false) { + unset_feature(&cpu->env, ARM_FEATURE_AARCH64); + } else { + set_feature(&cpu->env, ARM_FEATURE_AARCH64); + } +} + static void aarch64_cpu_initfn(Object *obj) { + object_property_add_bool(obj, "aarch64", aarch64_cpu_get_aarch64, + aarch64_cpu_set_aarch64, NULL); + object_property_set_description(obj, "aarch64", + "Set on/off to enable/disable aarch64 " + "execution state ", + NULL); } static void aarch64_cpu_finalizefn(Object *obj) diff --git a/target-arm/crypto_helper.c b/target-arm/crypto_helper.c index dd60d0b..1fe975d 100644 --- a/target-arm/crypto_helper.c +++ b/target-arm/crypto_helper.c @@ -22,6 +22,14 @@ union CRYPTO_STATE { uint64_t l[2]; }; +#ifdef HOST_WORDS_BIGENDIAN +#define CR_ST_BYTE(state, i) (state.bytes[(15 - (i)) ^ 8]) +#define CR_ST_WORD(state, i) (state.words[(3 - (i)) ^ 2]) +#else +#define CR_ST_BYTE(state, i) (state.bytes[i]) +#define CR_ST_WORD(state, i) (state.words[i]) +#endif + void HELPER(crypto_aese)(CPUARMState *env, uint32_t rd, uint32_t rm, uint32_t decrypt) { @@ -46,7 +54,7 @@ void HELPER(crypto_aese)(CPUARMState *env, uint32_t rd, uint32_t rm, /* combine ShiftRows operation and sbox substitution */ for (i = 0; i < 16; i++) { - st.bytes[i] = sbox[decrypt][rk.bytes[shift[decrypt][i]]]; + CR_ST_BYTE(st, i) = sbox[decrypt][CR_ST_BYTE(rk, shift[decrypt][i])]; } env->vfp.regs[rd] = make_float64(st.l[0]); @@ -198,11 +206,11 @@ void HELPER(crypto_aesmc)(CPUARMState *env, uint32_t rd, uint32_t rm, assert(decrypt < 2); for (i = 0; i < 16; i += 4) { - st.words[i >> 2] = cpu_to_le32( - mc[decrypt][st.bytes[i]] ^ - rol32(mc[decrypt][st.bytes[i + 1]], 8) ^ - rol32(mc[decrypt][st.bytes[i + 2]], 16) ^ - rol32(mc[decrypt][st.bytes[i + 3]], 24)); + CR_ST_WORD(st, i >> 2) = + mc[decrypt][CR_ST_BYTE(st, i)] ^ + rol32(mc[decrypt][CR_ST_BYTE(st, i + 1)], 8) ^ + rol32(mc[decrypt][CR_ST_BYTE(st, i + 2)], 16) ^ + rol32(mc[decrypt][CR_ST_BYTE(st, i + 3)], 24); } env->vfp.regs[rd] = make_float64(st.l[0]); @@ -255,24 +263,25 @@ void HELPER(crypto_sha1_3reg)(CPUARMState *env, uint32_t rd, uint32_t rn, switch (op) { case 0: /* sha1c */ - t = cho(d.words[1], d.words[2], d.words[3]); + t = cho(CR_ST_WORD(d, 1), CR_ST_WORD(d, 2), CR_ST_WORD(d, 3)); break; case 1: /* sha1p */ - t = par(d.words[1], d.words[2], d.words[3]); + t = par(CR_ST_WORD(d, 1), CR_ST_WORD(d, 2), CR_ST_WORD(d, 3)); break; case 2: /* sha1m */ - t = maj(d.words[1], d.words[2], d.words[3]); + t = maj(CR_ST_WORD(d, 1), CR_ST_WORD(d, 2), CR_ST_WORD(d, 3)); break; default: g_assert_not_reached(); } - t += rol32(d.words[0], 5) + n.words[0] + m.words[i]; - - n.words[0] = d.words[3]; - d.words[3] = d.words[2]; - d.words[2] = ror32(d.words[1], 2); - d.words[1] = d.words[0]; - d.words[0] = t; + t += rol32(CR_ST_WORD(d, 0), 5) + CR_ST_WORD(n, 0) + + CR_ST_WORD(m, i); + + CR_ST_WORD(n, 0) = CR_ST_WORD(d, 3); + CR_ST_WORD(d, 3) = CR_ST_WORD(d, 2); + CR_ST_WORD(d, 2) = ror32(CR_ST_WORD(d, 1), 2); + CR_ST_WORD(d, 1) = CR_ST_WORD(d, 0); + CR_ST_WORD(d, 0) = t; } } env->vfp.regs[rd] = make_float64(d.l[0]); @@ -286,8 +295,8 @@ void HELPER(crypto_sha1h)(CPUARMState *env, uint32_t rd, uint32_t rm) float64_val(env->vfp.regs[rm + 1]) } }; - m.words[0] = ror32(m.words[0], 2); - m.words[1] = m.words[2] = m.words[3] = 0; + CR_ST_WORD(m, 0) = ror32(CR_ST_WORD(m, 0), 2); + CR_ST_WORD(m, 1) = CR_ST_WORD(m, 2) = CR_ST_WORD(m, 3) = 0; env->vfp.regs[rd] = make_float64(m.l[0]); env->vfp.regs[rd + 1] = make_float64(m.l[1]); @@ -304,10 +313,10 @@ void HELPER(crypto_sha1su1)(CPUARMState *env, uint32_t rd, uint32_t rm) float64_val(env->vfp.regs[rm + 1]) } }; - d.words[0] = rol32(d.words[0] ^ m.words[1], 1); - d.words[1] = rol32(d.words[1] ^ m.words[2], 1); - d.words[2] = rol32(d.words[2] ^ m.words[3], 1); - d.words[3] = rol32(d.words[3] ^ d.words[0], 1); + CR_ST_WORD(d, 0) = rol32(CR_ST_WORD(d, 0) ^ CR_ST_WORD(m, 1), 1); + CR_ST_WORD(d, 1) = rol32(CR_ST_WORD(d, 1) ^ CR_ST_WORD(m, 2), 1); + CR_ST_WORD(d, 2) = rol32(CR_ST_WORD(d, 2) ^ CR_ST_WORD(m, 3), 1); + CR_ST_WORD(d, 3) = rol32(CR_ST_WORD(d, 3) ^ CR_ST_WORD(d, 0), 1); env->vfp.regs[rd] = make_float64(d.l[0]); env->vfp.regs[rd + 1] = make_float64(d.l[1]); @@ -356,20 +365,22 @@ void HELPER(crypto_sha256h)(CPUARMState *env, uint32_t rd, uint32_t rn, int i; for (i = 0; i < 4; i++) { - uint32_t t = cho(n.words[0], n.words[1], n.words[2]) + n.words[3] - + S1(n.words[0]) + m.words[i]; - - n.words[3] = n.words[2]; - n.words[2] = n.words[1]; - n.words[1] = n.words[0]; - n.words[0] = d.words[3] + t; - - t += maj(d.words[0], d.words[1], d.words[2]) + S0(d.words[0]); - - d.words[3] = d.words[2]; - d.words[2] = d.words[1]; - d.words[1] = d.words[0]; - d.words[0] = t; + uint32_t t = cho(CR_ST_WORD(n, 0), CR_ST_WORD(n, 1), CR_ST_WORD(n, 2)) + + CR_ST_WORD(n, 3) + S1(CR_ST_WORD(n, 0)) + + CR_ST_WORD(m, i); + + CR_ST_WORD(n, 3) = CR_ST_WORD(n, 2); + CR_ST_WORD(n, 2) = CR_ST_WORD(n, 1); + CR_ST_WORD(n, 1) = CR_ST_WORD(n, 0); + CR_ST_WORD(n, 0) = CR_ST_WORD(d, 3) + t; + + t += maj(CR_ST_WORD(d, 0), CR_ST_WORD(d, 1), CR_ST_WORD(d, 2)) + + S0(CR_ST_WORD(d, 0)); + + CR_ST_WORD(d, 3) = CR_ST_WORD(d, 2); + CR_ST_WORD(d, 2) = CR_ST_WORD(d, 1); + CR_ST_WORD(d, 1) = CR_ST_WORD(d, 0); + CR_ST_WORD(d, 0) = t; } env->vfp.regs[rd] = make_float64(d.l[0]); @@ -394,13 +405,14 @@ void HELPER(crypto_sha256h2)(CPUARMState *env, uint32_t rd, uint32_t rn, int i; for (i = 0; i < 4; i++) { - uint32_t t = cho(d.words[0], d.words[1], d.words[2]) + d.words[3] - + S1(d.words[0]) + m.words[i]; - - d.words[3] = d.words[2]; - d.words[2] = d.words[1]; - d.words[1] = d.words[0]; - d.words[0] = n.words[3 - i] + t; + uint32_t t = cho(CR_ST_WORD(d, 0), CR_ST_WORD(d, 1), CR_ST_WORD(d, 2)) + + CR_ST_WORD(d, 3) + S1(CR_ST_WORD(d, 0)) + + CR_ST_WORD(m, i); + + CR_ST_WORD(d, 3) = CR_ST_WORD(d, 2); + CR_ST_WORD(d, 2) = CR_ST_WORD(d, 1); + CR_ST_WORD(d, 1) = CR_ST_WORD(d, 0); + CR_ST_WORD(d, 0) = CR_ST_WORD(n, 3 - i) + t; } env->vfp.regs[rd] = make_float64(d.l[0]); @@ -418,10 +430,10 @@ void HELPER(crypto_sha256su0)(CPUARMState *env, uint32_t rd, uint32_t rm) float64_val(env->vfp.regs[rm + 1]) } }; - d.words[0] += s0(d.words[1]); - d.words[1] += s0(d.words[2]); - d.words[2] += s0(d.words[3]); - d.words[3] += s0(m.words[0]); + CR_ST_WORD(d, 0) += s0(CR_ST_WORD(d, 1)); + CR_ST_WORD(d, 1) += s0(CR_ST_WORD(d, 2)); + CR_ST_WORD(d, 2) += s0(CR_ST_WORD(d, 3)); + CR_ST_WORD(d, 3) += s0(CR_ST_WORD(m, 0)); env->vfp.regs[rd] = make_float64(d.l[0]); env->vfp.regs[rd + 1] = make_float64(d.l[1]); @@ -443,10 +455,10 @@ void HELPER(crypto_sha256su1)(CPUARMState *env, uint32_t rd, uint32_t rn, float64_val(env->vfp.regs[rm + 1]) } }; - d.words[0] += s1(m.words[2]) + n.words[1]; - d.words[1] += s1(m.words[3]) + n.words[2]; - d.words[2] += s1(d.words[0]) + n.words[3]; - d.words[3] += s1(d.words[1]) + m.words[0]; + CR_ST_WORD(d, 0) += s1(CR_ST_WORD(m, 2)) + CR_ST_WORD(n, 1); + CR_ST_WORD(d, 1) += s1(CR_ST_WORD(m, 3)) + CR_ST_WORD(n, 2); + CR_ST_WORD(d, 2) += s1(CR_ST_WORD(d, 0)) + CR_ST_WORD(n, 3); + CR_ST_WORD(d, 3) += s1(CR_ST_WORD(d, 1)) + CR_ST_WORD(m, 0); env->vfp.regs[rd] = make_float64(d.l[0]); env->vfp.regs[rd + 1] = make_float64(d.l[1]); diff --git a/target-arm/helper-a64.c b/target-arm/helper-a64.c index 81066ca..861f6fa 100644 --- a/target-arm/helper-a64.c +++ b/target-arm/helper-a64.c @@ -135,6 +135,9 @@ float32 HELPER(vfp_mulxs)(float32 a, float32 b, void *fpstp) { float_status *fpst = fpstp; + a = float32_squash_input_denormal(a, fpst); + b = float32_squash_input_denormal(b, fpst); + if ((float32_is_zero(a) && float32_is_infinity(b)) || (float32_is_infinity(a) && float32_is_zero(b))) { /* 2.0 with the sign bit set to sign(A) XOR sign(B) */ @@ -148,6 +151,9 @@ float64 HELPER(vfp_mulxd)(float64 a, float64 b, void *fpstp) { float_status *fpst = fpstp; + a = float64_squash_input_denormal(a, fpst); + b = float64_squash_input_denormal(b, fpst); + if ((float64_is_zero(a) && float64_is_infinity(b)) || (float64_is_infinity(a) && float64_is_zero(b))) { /* 2.0 with the sign bit set to sign(A) XOR sign(B) */ @@ -223,6 +229,9 @@ float32 HELPER(recpsf_f32)(float32 a, float32 b, void *fpstp) { float_status *fpst = fpstp; + a = float32_squash_input_denormal(a, fpst); + b = float32_squash_input_denormal(b, fpst); + a = float32_chs(a); if ((float32_is_infinity(a) && float32_is_zero(b)) || (float32_is_infinity(b) && float32_is_zero(a))) { @@ -235,6 +244,9 @@ float64 HELPER(recpsf_f64)(float64 a, float64 b, void *fpstp) { float_status *fpst = fpstp; + a = float64_squash_input_denormal(a, fpst); + b = float64_squash_input_denormal(b, fpst); + a = float64_chs(a); if ((float64_is_infinity(a) && float64_is_zero(b)) || (float64_is_infinity(b) && float64_is_zero(a))) { @@ -247,6 +259,9 @@ float32 HELPER(rsqrtsf_f32)(float32 a, float32 b, void *fpstp) { float_status *fpst = fpstp; + a = float32_squash_input_denormal(a, fpst); + b = float32_squash_input_denormal(b, fpst); + a = float32_chs(a); if ((float32_is_infinity(a) && float32_is_zero(b)) || (float32_is_infinity(b) && float32_is_zero(a))) { @@ -259,6 +274,9 @@ float64 HELPER(rsqrtsf_f64)(float64 a, float64 b, void *fpstp) { float_status *fpst = fpstp; + a = float64_squash_input_denormal(a, fpst); + b = float64_squash_input_denormal(b, fpst); + a = float64_chs(a); if ((float64_is_infinity(a) && float64_is_zero(b)) || (float64_is_infinity(b) && float64_is_zero(a))) { @@ -448,7 +466,6 @@ void aarch64_cpu_do_interrupt(CPUState *cs) unsigned int new_el = arm_excp_target_el(cs, cs->exception_index); target_ulong addr = env->cp15.vbar_el[new_el]; unsigned int new_mode = aarch64_pstate_mode(new_el, true); - int i; if (arm_current_el(env) < new_el) { if (env->aarch64) { @@ -506,15 +523,13 @@ void aarch64_cpu_do_interrupt(CPUState *cs) aarch64_save_sp(env, arm_current_el(env)); env->elr_el[new_el] = env->pc; } else { - env->banked_spsr[0] = cpsr_read(env); + env->banked_spsr[aarch64_banked_spsr_index(new_el)] = cpsr_read(env); if (!env->thumb) { env->cp15.esr_el[new_el] |= 1 << 25; } env->elr_el[new_el] = env->regs[15]; - for (i = 0; i < 15; i++) { - env->xregs[i] = env->regs[i]; - } + aarch64_sync_32_to_64(env); env->condexec_bits = 0; } diff --git a/target-arm/helper.c b/target-arm/helper.c index b74d348..d77c6de 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -13,7 +13,7 @@ #ifndef CONFIG_USER_ONLY static inline int get_phys_addr(CPUARMState *env, target_ulong address, - int access_type, int is_user, + int access_type, ARMMMUIdx mmu_idx, hwaddr *phys_ptr, int *prot, target_ulong *page_size); @@ -119,6 +119,7 @@ static int aarch64_fpu_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg) static uint64_t raw_read(CPUARMState *env, const ARMCPRegInfo *ri) { + assert(ri->fieldoffset); if (cpreg_field_is_64bit(ri)) { return CPREG_FIELD64(env, ri); } else { @@ -129,6 +130,7 @@ static uint64_t raw_read(CPUARMState *env, const ARMCPRegInfo *ri) static void raw_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { + assert(ri->fieldoffset); if (cpreg_field_is_64bit(ri)) { CPREG_FIELD64(env, ri) = value; } else { @@ -136,6 +138,11 @@ static void raw_write(CPUARMState *env, const ARMCPRegInfo *ri, } } +static void *raw_ptr(CPUARMState *env, const ARMCPRegInfo *ri) +{ + return (char *)env + ri->fieldoffset; +} + static uint64_t read_raw_cp_reg(CPUARMState *env, const ARMCPRegInfo *ri) { /* Raw read of a coprocessor register (as needed for migration, etc). */ @@ -169,6 +176,27 @@ static void write_raw_cp_reg(CPUARMState *env, const ARMCPRegInfo *ri, } } +static bool raw_accessors_invalid(const ARMCPRegInfo *ri) +{ + /* Return true if the regdef would cause an assertion if you called + * read_raw_cp_reg() or write_raw_cp_reg() on it (ie if it is a + * program bug for it not to have the NO_RAW flag). + * NB that returning false here doesn't necessarily mean that calling + * read/write_raw_cp_reg() is safe, because we can't distinguish "has + * read/write access functions which are safe for raw use" from "has + * read/write access functions which have side effects but has forgotten + * to provide raw access functions". + * The tests here line up with the conditions in read/write_raw_cp_reg() + * and assertions in raw_read()/raw_write(). + */ + if ((ri->type & ARM_CP_CONST) || + ri->fieldoffset || + ((ri->raw_writefn || ri->writefn) && (ri->raw_readfn || ri->readfn))) { + return false; + } + return true; +} + bool write_cpustate_to_list(ARMCPU *cpu) { /* Write the coprocessor state from cpu->env to the (index,value) list. */ @@ -184,7 +212,7 @@ bool write_cpustate_to_list(ARMCPU *cpu) ok = false; continue; } - if (ri->type & ARM_CP_NO_MIGRATE) { + if (ri->type & ARM_CP_NO_RAW) { continue; } cpu->cpreg_values[i] = read_raw_cp_reg(&cpu->env, ri); @@ -207,7 +235,7 @@ bool write_list_to_cpustate(ARMCPU *cpu) ok = false; continue; } - if (ri->type & ARM_CP_NO_MIGRATE) { + if (ri->type & ARM_CP_NO_RAW) { continue; } /* Write value and confirm it reads back as written @@ -231,7 +259,7 @@ static void add_cpreg_to_list(gpointer key, gpointer opaque) regidx = *(uint32_t *)key; ri = get_arm_cp_reginfo(cpu->cp_regs, regidx); - if (!(ri->type & ARM_CP_NO_MIGRATE)) { + if (!(ri->type & (ARM_CP_NO_RAW|ARM_CP_ALIAS))) { cpu->cpreg_indexes[cpu->cpreg_array_len] = cpreg_to_kvm_id(regidx); /* The value array need not be initialized at this point */ cpu->cpreg_array_len++; @@ -247,7 +275,7 @@ static void count_cpreg(gpointer key, gpointer opaque) regidx = *(uint32_t *)key; ri = get_arm_cp_reginfo(cpu->cp_regs, regidx); - if (!(ri->type & ARM_CP_NO_MIGRATE)) { + if (!(ri->type & (ARM_CP_NO_RAW|ARM_CP_ALIAS))) { cpu->cpreg_array_len++; } } @@ -419,13 +447,36 @@ static void tlbimvaa_is_write(CPUARMState *env, const ARMCPRegInfo *ri, } static const ARMCPRegInfo cp_reginfo[] = { - { .name = "FCSEIDR", .cp = 15, .crn = 13, .crm = 0, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c13_fcse), + /* Define the secure and non-secure FCSE identifier CP registers + * separately because there is no secure bank in V8 (no _EL3). This allows + * the secure register to be properly reset and migrated. There is also no + * v8 EL1 version of the register so the non-secure instance stands alone. + */ + { .name = "FCSEIDR(NS)", + .cp = 15, .opc1 = 0, .crn = 13, .crm = 0, .opc2 = 0, + .access = PL1_RW, .secure = ARM_CP_SECSTATE_NS, + .fieldoffset = offsetof(CPUARMState, cp15.fcseidr_ns), .resetvalue = 0, .writefn = fcse_write, .raw_writefn = raw_write, }, - { .name = "CONTEXTIDR", .state = ARM_CP_STATE_BOTH, + { .name = "FCSEIDR(S)", + .cp = 15, .opc1 = 0, .crn = 13, .crm = 0, .opc2 = 0, + .access = PL1_RW, .secure = ARM_CP_SECSTATE_S, + .fieldoffset = offsetof(CPUARMState, cp15.fcseidr_s), + .resetvalue = 0, .writefn = fcse_write, .raw_writefn = raw_write, }, + /* Define the secure and non-secure context identifier CP registers + * separately because there is no secure bank in V8 (no _EL3). This allows + * the secure register to be properly reset and migrated. In the + * non-secure case, the 32-bit register will have reset and migration + * disabled during registration as it is handled by the 64-bit instance. + */ + { .name = "CONTEXTIDR_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 13, .crm = 0, .opc2 = 1, - .access = PL1_RW, - .fieldoffset = offsetof(CPUARMState, cp15.contextidr_el1), + .access = PL1_RW, .secure = ARM_CP_SECSTATE_NS, + .fieldoffset = offsetof(CPUARMState, cp15.contextidr_el[1]), + .resetvalue = 0, .writefn = contextidr_write, .raw_writefn = raw_write, }, + { .name = "CONTEXTIDR(S)", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 0, .crn = 13, .crm = 0, .opc2 = 1, + .access = PL1_RW, .secure = ARM_CP_SECSTATE_S, + .fieldoffset = offsetof(CPUARMState, cp15.contextidr_s), .resetvalue = 0, .writefn = contextidr_write, .raw_writefn = raw_write, }, REGINFO_SENTINEL }; @@ -435,10 +486,12 @@ static const ARMCPRegInfo not_v8_cp_reginfo[] = { * definitions that don't use CP_ANY wildcards (mostly in v8_cp_reginfo[]). */ /* MMU Domain access control / MPU write buffer control */ - { .name = "DACR", .cp = 15, - .crn = 3, .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY, - .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c3), - .resetvalue = 0, .writefn = dacr_write, .raw_writefn = raw_write, }, + { .name = "DACR", + .cp = 15, .opc1 = CP_ANY, .crn = 3, .crm = CP_ANY, .opc2 = CP_ANY, + .access = PL1_RW, .resetvalue = 0, + .writefn = dacr_write, .raw_writefn = raw_write, + .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.dacr_s), + offsetoflow32(CPUARMState, cp15.dacr_ns) } }, /* ??? This covers not just the impdef TLB lockdown registers but also * some v7VMSA registers relating to TEX remap, so it is overly broad. */ @@ -478,7 +531,7 @@ static const ARMCPRegInfo not_v7_cp_reginfo[] = { .resetvalue = 0 }, /* v6 doesn't have the cache ID registers but Linux reads them anyway */ { .name = "DUMMY", .cp = 15, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = CP_ANY, - .access = PL1_R, .type = ARM_CP_CONST | ARM_CP_NO_MIGRATE, + .access = PL1_R, .type = ARM_CP_CONST | ARM_CP_NO_RAW, .resetvalue = 0 }, /* We don't implement pre-v7 debug but most CPUs had at least a DBGDIDR; * implementing it as RAZ means the "debug architecture version" bits @@ -492,16 +545,16 @@ static const ARMCPRegInfo not_v7_cp_reginfo[] = { */ { .name = "TLBIALL", .cp = 15, .crn = 8, .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = 0, .access = PL1_W, .writefn = tlbiall_write, - .type = ARM_CP_NO_MIGRATE }, + .type = ARM_CP_NO_RAW }, { .name = "TLBIMVA", .cp = 15, .crn = 8, .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = 1, .access = PL1_W, .writefn = tlbimva_write, - .type = ARM_CP_NO_MIGRATE }, + .type = ARM_CP_NO_RAW }, { .name = "TLBIASID", .cp = 15, .crn = 8, .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = 2, .access = PL1_W, .writefn = tlbiasid_write, - .type = ARM_CP_NO_MIGRATE }, + .type = ARM_CP_NO_RAW }, { .name = "TLBIMVAA", .cp = 15, .crn = 8, .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = 3, .access = PL1_W, .writefn = tlbimvaa_write, - .type = ARM_CP_NO_MIGRATE }, + .type = ARM_CP_NO_RAW }, REGINFO_SENTINEL }; @@ -552,7 +605,8 @@ static const ARMCPRegInfo v6_cp_reginfo[] = { .access = PL0_W, .type = ARM_CP_NOP }, { .name = "IFAR", .cp = 15, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 2, .access = PL1_RW, - .fieldoffset = offsetofhigh32(CPUARMState, cp15.far_el[1]), + .bank_fieldoffsets = { offsetof(CPUARMState, cp15.ifar_s), + offsetof(CPUARMState, cp15.ifar_ns) }, .resetvalue = 0, }, /* Watchpoint Fault Address Register : should actually only be present * for 1136, 1176, 11MPCore. @@ -776,7 +830,14 @@ static void scr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) static uint64_t ccsidr_read(CPUARMState *env, const ARMCPRegInfo *ri) { ARMCPU *cpu = arm_env_get_cpu(env); - return cpu->ccsidr[env->cp15.c0_cssel]; + + /* Acquire the CSSELR index from the bank corresponding to the CCSIDR + * bank + */ + uint32_t index = A32_BANKED_REG_GET(env, csselr, + ri->secure & ARM_CP_SECSTATE_S); + + return cpu->ccsidr[index]; } static void csselr_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -816,7 +877,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { * or PL0_RO as appropriate and then check PMUSERENR in the helper fn. */ { .name = "PMCNTENSET", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 1, - .access = PL0_RW, .type = ARM_CP_NO_MIGRATE, + .access = PL0_RW, .type = ARM_CP_ALIAS, .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcnten), .writefn = pmcntenset_write, .accessfn = pmreg_access, @@ -831,11 +892,11 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcnten), .accessfn = pmreg_access, .writefn = pmcntenclr_write, - .type = ARM_CP_NO_MIGRATE }, + .type = ARM_CP_ALIAS }, { .name = "PMCNTENCLR_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 2, .access = PL0_RW, .accessfn = pmreg_access, - .type = ARM_CP_NO_MIGRATE, + .type = ARM_CP_ALIAS, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten), .writefn = pmcntenclr_write }, { .name = "PMOVSR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 3, @@ -890,24 +951,23 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .resetvalue = 0, .writefn = pmintenset_write, .raw_writefn = raw_write }, { .name = "PMINTENCLR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 2, - .access = PL1_RW, .type = ARM_CP_NO_MIGRATE, + .access = PL1_RW, .type = ARM_CP_ALIAS, .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), .resetvalue = 0, .writefn = pmintenclr_write, }, { .name = "VBAR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 12, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .writefn = vbar_write, - .fieldoffset = offsetof(CPUARMState, cp15.vbar_el[1]), + .bank_fieldoffsets = { offsetof(CPUARMState, cp15.vbar_s), + offsetof(CPUARMState, cp15.vbar_ns) }, .resetvalue = 0 }, - { .name = "SCR", .cp = 15, .crn = 1, .crm = 1, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .fieldoffset = offsetoflow32(CPUARMState, cp15.scr_el3), - .resetvalue = 0, .writefn = scr_write }, { .name = "CCSIDR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 0, - .access = PL1_R, .readfn = ccsidr_read, .type = ARM_CP_NO_MIGRATE }, + .access = PL1_R, .readfn = ccsidr_read, .type = ARM_CP_NO_RAW }, { .name = "CSSELR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 0, .crm = 0, .opc1 = 2, .opc2 = 0, - .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c0_cssel), - .writefn = csselr_write, .resetvalue = 0 }, + .access = PL1_RW, .writefn = csselr_write, .resetvalue = 0, + .bank_fieldoffsets = { offsetof(CPUARMState, cp15.csselr_s), + offsetof(CPUARMState, cp15.csselr_ns) } }, /* Auxiliary ID register: this actually has an IMPDEF value but for now * just RAZ for all cores: */ @@ -928,61 +988,67 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { */ { .name = "MAIR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 10, .crm = 2, .opc2 = 0, - .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.mair_el1), + .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.mair_el[1]), .resetvalue = 0 }, /* For non-long-descriptor page tables these are PRRR and NMRR; * regardless they still act as reads-as-written for QEMU. * The override is necessary because of the overly-broad TLB_LOCKDOWN * definition. */ + /* MAIR0/1 are defined separately from their 64-bit counterpart which + * allows them to assign the correct fieldoffset based on the endianness + * handled in the field definitions. + */ { .name = "MAIR0", .state = ARM_CP_STATE_AA32, .type = ARM_CP_OVERRIDE, .cp = 15, .opc1 = 0, .crn = 10, .crm = 2, .opc2 = 0, .access = PL1_RW, - .fieldoffset = offsetoflow32(CPUARMState, cp15.mair_el1), + .bank_fieldoffsets = { offsetof(CPUARMState, cp15.mair0_s), + offsetof(CPUARMState, cp15.mair0_ns) }, .resetfn = arm_cp_reset_ignore }, { .name = "MAIR1", .state = ARM_CP_STATE_AA32, .type = ARM_CP_OVERRIDE, .cp = 15, .opc1 = 0, .crn = 10, .crm = 2, .opc2 = 1, .access = PL1_RW, - .fieldoffset = offsetofhigh32(CPUARMState, cp15.mair_el1), + .bank_fieldoffsets = { offsetof(CPUARMState, cp15.mair1_s), + offsetof(CPUARMState, cp15.mair1_ns) }, .resetfn = arm_cp_reset_ignore }, { .name = "ISR_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 1, .opc2 = 0, - .type = ARM_CP_NO_MIGRATE, .access = PL1_R, .readfn = isr_read }, + .type = ARM_CP_NO_RAW, .access = PL1_R, .readfn = isr_read }, /* 32 bit ITLB invalidates */ { .name = "ITLBIALL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 0, - .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbiall_write }, + .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbiall_write }, { .name = "ITLBIMVA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 1, - .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbimva_write }, + .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbimva_write }, { .name = "ITLBIASID", .cp = 15, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 2, - .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbiasid_write }, + .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbiasid_write }, /* 32 bit DTLB invalidates */ { .name = "DTLBIALL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 0, - .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbiall_write }, + .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbiall_write }, { .name = "DTLBIMVA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 1, - .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbimva_write }, + .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbimva_write }, { .name = "DTLBIASID", .cp = 15, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 2, - .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbiasid_write }, + .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbiasid_write }, /* 32 bit TLB invalidates */ { .name = "TLBIALL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 0, - .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbiall_write }, + .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbiall_write }, { .name = "TLBIMVA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 1, - .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbimva_write }, + .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbimva_write }, { .name = "TLBIASID", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 2, - .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbiasid_write }, + .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbiasid_write }, { .name = "TLBIMVAA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 3, - .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbimvaa_write }, + .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbimvaa_write }, REGINFO_SENTINEL }; static const ARMCPRegInfo v7mp_cp_reginfo[] = { /* 32 bit TLB invalidates, Inner Shareable */ { .name = "TLBIALLIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 0, - .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbiall_is_write }, + .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbiall_is_write }, { .name = "TLBIMVAIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 1, - .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbimva_is_write }, + .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbimva_is_write }, { .name = "TLBIASIDIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 2, - .type = ARM_CP_NO_MIGRATE, .access = PL1_W, + .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbiasid_is_write }, { .name = "TLBIMVAAIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 3, - .type = ARM_CP_NO_MIGRATE, .access = PL1_W, + .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbimvaa_is_write }, REGINFO_SENTINEL }; @@ -1017,23 +1083,31 @@ static const ARMCPRegInfo v6k_cp_reginfo[] = { { .name = "TPIDR_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .opc2 = 2, .crn = 13, .crm = 0, .access = PL0_RW, - .fieldoffset = offsetof(CPUARMState, cp15.tpidr_el0), .resetvalue = 0 }, + .fieldoffset = offsetof(CPUARMState, cp15.tpidr_el[0]), .resetvalue = 0 }, { .name = "TPIDRURW", .cp = 15, .crn = 13, .crm = 0, .opc1 = 0, .opc2 = 2, .access = PL0_RW, - .fieldoffset = offsetoflow32(CPUARMState, cp15.tpidr_el0), + .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.tpidrurw_s), + offsetoflow32(CPUARMState, cp15.tpidrurw_ns) }, .resetfn = arm_cp_reset_ignore }, { .name = "TPIDRRO_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .opc2 = 3, .crn = 13, .crm = 0, .access = PL0_R|PL1_W, - .fieldoffset = offsetof(CPUARMState, cp15.tpidrro_el0), .resetvalue = 0 }, + .fieldoffset = offsetof(CPUARMState, cp15.tpidrro_el[0]), + .resetvalue = 0}, { .name = "TPIDRURO", .cp = 15, .crn = 13, .crm = 0, .opc1 = 0, .opc2 = 3, .access = PL0_R|PL1_W, - .fieldoffset = offsetoflow32(CPUARMState, cp15.tpidrro_el0), + .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.tpidruro_s), + offsetoflow32(CPUARMState, cp15.tpidruro_ns) }, .resetfn = arm_cp_reset_ignore }, - { .name = "TPIDR_EL1", .state = ARM_CP_STATE_BOTH, + { .name = "TPIDR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .opc2 = 4, .crn = 13, .crm = 0, .access = PL1_RW, - .fieldoffset = offsetof(CPUARMState, cp15.tpidr_el1), .resetvalue = 0 }, + .fieldoffset = offsetof(CPUARMState, cp15.tpidr_el[1]), .resetvalue = 0 }, + { .name = "TPIDRPRW", .opc1 = 0, .cp = 15, .crn = 13, .crm = 0, .opc2 = 4, + .access = PL1_RW, + .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.tpidrprw_s), + offsetoflow32(CPUARMState, cp15.tpidrprw_ns) }, + .resetvalue = 0 }, REGINFO_SENTINEL }; @@ -1217,7 +1291,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { * Our reset value matches the fixed frequency we implement the timer at. */ { .name = "CNTFRQ", .cp = 15, .crn = 14, .crm = 0, .opc1 = 0, .opc2 = 0, - .type = ARM_CP_NO_MIGRATE, + .type = ARM_CP_ALIAS, .access = PL1_RW | PL0_R, .accessfn = gt_cntfrq_access, .fieldoffset = offsetoflow32(CPUARMState, cp15.c14_cntfrq), .resetfn = arm_cp_reset_ignore, @@ -1237,7 +1311,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { }, /* per-timer control */ { .name = "CNTP_CTL", .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 1, - .type = ARM_CP_IO | ARM_CP_NO_MIGRATE, .access = PL1_RW | PL0_R, + .type = ARM_CP_IO | ARM_CP_ALIAS, .access = PL1_RW | PL0_R, .accessfn = gt_ptimer_access, .fieldoffset = offsetoflow32(CPUARMState, cp15.c14_timer[GTIMER_PHYS].ctl), @@ -1253,7 +1327,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { .writefn = gt_ctl_write, .raw_writefn = raw_write, }, { .name = "CNTV_CTL", .cp = 15, .crn = 14, .crm = 3, .opc1 = 0, .opc2 = 1, - .type = ARM_CP_IO | ARM_CP_NO_MIGRATE, .access = PL1_RW | PL0_R, + .type = ARM_CP_IO | ARM_CP_ALIAS, .access = PL1_RW | PL0_R, .accessfn = gt_vtimer_access, .fieldoffset = offsetoflow32(CPUARMState, cp15.c14_timer[GTIMER_VIRT].ctl), @@ -1270,52 +1344,52 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { }, /* TimerValue views: a 32 bit downcounting view of the underlying state */ { .name = "CNTP_TVAL", .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 0, - .type = ARM_CP_NO_MIGRATE | ARM_CP_IO, .access = PL1_RW | PL0_R, + .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL1_RW | PL0_R, .accessfn = gt_ptimer_access, .readfn = gt_tval_read, .writefn = gt_tval_write, }, { .name = "CNTP_TVAL_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 2, .opc2 = 0, - .type = ARM_CP_NO_MIGRATE | ARM_CP_IO, .access = PL1_RW | PL0_R, + .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL1_RW | PL0_R, .readfn = gt_tval_read, .writefn = gt_tval_write, }, { .name = "CNTV_TVAL", .cp = 15, .crn = 14, .crm = 3, .opc1 = 0, .opc2 = 0, - .type = ARM_CP_NO_MIGRATE | ARM_CP_IO, .access = PL1_RW | PL0_R, + .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL1_RW | PL0_R, .accessfn = gt_vtimer_access, .readfn = gt_tval_read, .writefn = gt_tval_write, }, { .name = "CNTV_TVAL_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 3, .opc2 = 0, - .type = ARM_CP_NO_MIGRATE | ARM_CP_IO, .access = PL1_RW | PL0_R, + .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL1_RW | PL0_R, .readfn = gt_tval_read, .writefn = gt_tval_write, }, /* The counter itself */ { .name = "CNTPCT", .cp = 15, .crm = 14, .opc1 = 0, - .access = PL0_R, .type = ARM_CP_64BIT | ARM_CP_NO_MIGRATE | ARM_CP_IO, + .access = PL0_R, .type = ARM_CP_64BIT | ARM_CP_NO_RAW | ARM_CP_IO, .accessfn = gt_pct_access, .readfn = gt_cnt_read, .resetfn = arm_cp_reset_ignore, }, { .name = "CNTPCT_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 0, .opc2 = 1, - .access = PL0_R, .type = ARM_CP_NO_MIGRATE | ARM_CP_IO, + .access = PL0_R, .type = ARM_CP_NO_RAW | ARM_CP_IO, .accessfn = gt_pct_access, .readfn = gt_cnt_read, .resetfn = gt_cnt_reset, }, { .name = "CNTVCT", .cp = 15, .crm = 14, .opc1 = 1, - .access = PL0_R, .type = ARM_CP_64BIT | ARM_CP_NO_MIGRATE | ARM_CP_IO, + .access = PL0_R, .type = ARM_CP_64BIT | ARM_CP_NO_RAW | ARM_CP_IO, .accessfn = gt_vct_access, .readfn = gt_cnt_read, .resetfn = arm_cp_reset_ignore, }, { .name = "CNTVCT_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 0, .opc2 = 2, - .access = PL0_R, .type = ARM_CP_NO_MIGRATE | ARM_CP_IO, + .access = PL0_R, .type = ARM_CP_NO_RAW | ARM_CP_IO, .accessfn = gt_vct_access, .readfn = gt_cnt_read, .resetfn = gt_cnt_reset, }, /* Comparison value, indicating when the timer goes off */ { .name = "CNTP_CVAL", .cp = 15, .crm = 14, .opc1 = 2, .access = PL1_RW | PL0_R, - .type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_NO_MIGRATE, + .type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_ALIAS, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval), .accessfn = gt_ptimer_access, .resetfn = arm_cp_reset_ignore, .writefn = gt_cval_write, .raw_writefn = raw_write, @@ -1330,7 +1404,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { }, { .name = "CNTV_CVAL", .cp = 15, .crm = 14, .opc1 = 3, .access = PL1_RW | PL0_R, - .type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_NO_MIGRATE, + .type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_ALIAS, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval), .accessfn = gt_vtimer_access, .resetfn = arm_cp_reset_ignore, .writefn = gt_cval_write, .raw_writefn = raw_write, @@ -1377,29 +1451,30 @@ static CPAccessResult ats_access(CPUARMState *env, const ARMCPRegInfo *ri) /* Other states are only available with TrustZone; in * a non-TZ implementation these registers don't exist * at all, which is an Uncategorized trap. This underdecoding - * is safe because the reginfo is NO_MIGRATE. + * is safe because the reginfo is NO_RAW. */ return CP_ACCESS_TRAP_UNCATEGORIZED; } return CP_ACCESS_OK; } -static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) +static uint64_t do_ats_write(CPUARMState *env, uint64_t value, + int access_type, ARMMMUIdx mmu_idx) { hwaddr phys_addr; target_ulong page_size; int prot; - int ret, is_user = ri->opc2 & 2; - int access_type = ri->opc2 & 1; + int ret; + uint64_t par64; - ret = get_phys_addr(env, value, access_type, is_user, + ret = get_phys_addr(env, value, access_type, mmu_idx, &phys_addr, &prot, &page_size); if (extended_addresses_enabled(env)) { /* ret is a DFSR/IFSR value for the long descriptor * translation table format, but with WnR always clear. * Convert it to a 64-bit PAR. */ - uint64_t par64 = (1 << 11); /* LPAE bit always set */ + par64 = (1 << 11); /* LPAE bit always set */ if (ret == 0) { par64 |= phys_addr & ~0xfffULL; /* We don't set the ATTR or SH fields in the PAR. */ @@ -1411,7 +1486,6 @@ static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) * fault. */ } - env->cp15.par_el1 = par64; } else { /* ret is a DFSR/IFSR value for the short descriptor * translation table format (with WnR always clear). @@ -1421,28 +1495,126 @@ static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) /* We do not set any attribute bits in the PAR */ if (page_size == (1 << 24) && arm_feature(env, ARM_FEATURE_V7)) { - env->cp15.par_el1 = (phys_addr & 0xff000000) | 1 << 1; + par64 = (phys_addr & 0xff000000) | (1 << 1); } else { - env->cp15.par_el1 = phys_addr & 0xfffff000; + par64 = phys_addr & 0xfffff000; } } else { - env->cp15.par_el1 = ((ret & (1 << 10)) >> 5) | - ((ret & (1 << 12)) >> 6) | - ((ret & 0xf) << 1) | 1; + par64 = ((ret & (1 << 10)) >> 5) | ((ret & (1 << 12)) >> 6) | + ((ret & 0xf) << 1) | 1; + } + } + return par64; +} + +static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) +{ + int access_type = ri->opc2 & 1; + uint64_t par64; + ARMMMUIdx mmu_idx; + int el = arm_current_el(env); + bool secure = arm_is_secure_below_el3(env); + + switch (ri->opc2 & 6) { + case 0: + /* stage 1 current state PL1: ATS1CPR, ATS1CPW */ + switch (el) { + case 3: + mmu_idx = ARMMMUIdx_S1E3; + break; + case 2: + mmu_idx = ARMMMUIdx_S1NSE1; + break; + case 1: + mmu_idx = secure ? ARMMMUIdx_S1SE1 : ARMMMUIdx_S1NSE1; + break; + default: + g_assert_not_reached(); + } + break; + case 2: + /* stage 1 current state PL0: ATS1CUR, ATS1CUW */ + switch (el) { + case 3: + mmu_idx = ARMMMUIdx_S1SE0; + break; + case 2: + mmu_idx = ARMMMUIdx_S1NSE0; + break; + case 1: + mmu_idx = secure ? ARMMMUIdx_S1SE0 : ARMMMUIdx_S1NSE0; + break; + default: + g_assert_not_reached(); + } + break; + case 4: + /* stage 1+2 NonSecure PL1: ATS12NSOPR, ATS12NSOPW */ + mmu_idx = ARMMMUIdx_S12NSE1; + break; + case 6: + /* stage 1+2 NonSecure PL0: ATS12NSOUR, ATS12NSOUW */ + mmu_idx = ARMMMUIdx_S12NSE0; + break; + default: + g_assert_not_reached(); + } + + par64 = do_ats_write(env, value, access_type, mmu_idx); + + A32_BANKED_CURRENT_REG_SET(env, par, par64); +} + +static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + int access_type = ri->opc2 & 1; + ARMMMUIdx mmu_idx; + int secure = arm_is_secure_below_el3(env); + + switch (ri->opc2 & 6) { + case 0: + switch (ri->opc1) { + case 0: /* AT S1E1R, AT S1E1W */ + mmu_idx = secure ? ARMMMUIdx_S1SE1 : ARMMMUIdx_S1NSE1; + break; + case 4: /* AT S1E2R, AT S1E2W */ + mmu_idx = ARMMMUIdx_S1E2; + break; + case 6: /* AT S1E3R, AT S1E3W */ + mmu_idx = ARMMMUIdx_S1E3; + break; + default: + g_assert_not_reached(); } + break; + case 2: /* AT S1E0R, AT S1E0W */ + mmu_idx = secure ? ARMMMUIdx_S1SE0 : ARMMMUIdx_S1NSE0; + break; + case 4: /* AT S12E1R, AT S12E1W */ + mmu_idx = ARMMMUIdx_S12NSE1; + break; + case 6: /* AT S12E0R, AT S12E0W */ + mmu_idx = ARMMMUIdx_S12NSE0; + break; + default: + g_assert_not_reached(); } + + env->cp15.par_el[1] = do_ats_write(env, value, access_type, mmu_idx); } #endif static const ARMCPRegInfo vapa_cp_reginfo[] = { { .name = "PAR", .cp = 15, .crn = 7, .crm = 4, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .resetvalue = 0, - .fieldoffset = offsetoflow32(CPUARMState, cp15.par_el1), + .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.par_s), + offsetoflow32(CPUARMState, cp15.par_ns) }, .writefn = par_write }, #ifndef CONFIG_USER_ONLY { .name = "ATS", .cp = 15, .crn = 7, .crm = 8, .opc1 = 0, .opc2 = CP_ANY, .access = PL1_W, .accessfn = ats_access, - .writefn = ats_write, .type = ARM_CP_NO_MIGRATE }, + .writefn = ats_write, .type = ARM_CP_NO_RAW }, #endif REGINFO_SENTINEL }; @@ -1501,12 +1673,12 @@ static uint64_t pmsav5_insn_ap_read(CPUARMState *env, const ARMCPRegInfo *ri) static const ARMCPRegInfo pmsav5_cp_reginfo[] = { { .name = "DATA_AP", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_NO_MIGRATE, + .access = PL1_RW, .type = ARM_CP_ALIAS, .fieldoffset = offsetof(CPUARMState, cp15.pmsav5_data_ap), .resetvalue = 0, .readfn = pmsav5_data_ap_read, .writefn = pmsav5_data_ap_write, }, { .name = "INSN_AP", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 1, - .access = PL1_RW, .type = ARM_CP_NO_MIGRATE, + .access = PL1_RW, .type = ARM_CP_ALIAS, .fieldoffset = offsetof(CPUARMState, cp15.pmsav5_insn_ap), .resetvalue = 0, .readfn = pmsav5_insn_ap_read, .writefn = pmsav5_insn_ap_write, }, @@ -1555,6 +1727,7 @@ static const ARMCPRegInfo pmsav5_cp_reginfo[] = { static void vmsa_ttbcr_raw_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { + TCR *tcr = raw_ptr(env, ri); int maskshift = extract32(value, 0, 3); if (!arm_feature(env, ARM_FEATURE_V8)) { @@ -1573,14 +1746,15 @@ static void vmsa_ttbcr_raw_write(CPUARMState *env, const ARMCPRegInfo *ri, } } - /* Note that we always calculate c2_mask and c2_base_mask, but + /* Update the masks corresponding to the the TCR bank being written + * Note that we always calculate mask and base_mask, but * they are only used for short-descriptor tables (ie if EAE is 0); - * for long-descriptor tables the TTBCR fields are used differently - * and the c2_mask and c2_base_mask values are meaningless. + * for long-descriptor tables the TCR fields are used differently + * and the mask and base_mask values are meaningless. */ - raw_write(env, ri, value); - env->cp15.c2_mask = ~(((uint32_t)0xffffffffu) >> maskshift); - env->cp15.c2_base_mask = ~((uint32_t)0x3fffu >> maskshift); + tcr->raw_tcr = value; + tcr->mask = ~(((uint32_t)0xffffffffu) >> maskshift); + tcr->base_mask = ~((uint32_t)0x3fffu >> maskshift); } static void vmsa_ttbcr_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -1599,19 +1773,25 @@ static void vmsa_ttbcr_write(CPUARMState *env, const ARMCPRegInfo *ri, static void vmsa_ttbcr_reset(CPUARMState *env, const ARMCPRegInfo *ri) { - env->cp15.c2_base_mask = 0xffffc000u; - raw_write(env, ri, 0); - env->cp15.c2_mask = 0; + TCR *tcr = raw_ptr(env, ri); + + /* Reset both the TCR as well as the masks corresponding to the bank of + * the TCR being reset. + */ + tcr->raw_tcr = 0; + tcr->mask = 0; + tcr->base_mask = 0xffffc000u; } static void vmsa_tcr_el1_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { ARMCPU *cpu = arm_env_get_cpu(env); + TCR *tcr = raw_ptr(env, ri); /* For AArch64 the A1 bit could result in a change of ASID, so TLB flush. */ tlb_flush(CPU(cpu), 1); - raw_write(env, ri, value); + tcr->raw_tcr = value; } static void vmsa_ttbr_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -1630,38 +1810,46 @@ static void vmsa_ttbr_write(CPUARMState *env, const ARMCPRegInfo *ri, static const ARMCPRegInfo vmsa_cp_reginfo[] = { { .name = "DFSR", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_NO_MIGRATE, - .fieldoffset = offsetoflow32(CPUARMState, cp15.esr_el[1]), + .access = PL1_RW, .type = ARM_CP_ALIAS, + .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.dfsr_s), + offsetoflow32(CPUARMState, cp15.dfsr_ns) }, .resetfn = arm_cp_reset_ignore, }, { .name = "IFSR", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 1, - .access = PL1_RW, - .fieldoffset = offsetof(CPUARMState, cp15.ifsr_el2), .resetvalue = 0, }, + .access = PL1_RW, .resetvalue = 0, + .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.ifsr_s), + offsetoflow32(CPUARMState, cp15.ifsr_ns) } }, { .name = "ESR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .crn = 5, .crm = 2, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.esr_el[1]), .resetvalue = 0, }, { .name = "TTBR0_EL1", .state = ARM_CP_STATE_BOTH, - .opc0 = 3, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.ttbr0_el1), - .writefn = vmsa_ttbr_write, .resetvalue = 0 }, + .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 0, .opc2 = 0, + .access = PL1_RW, .writefn = vmsa_ttbr_write, .resetvalue = 0, + .bank_fieldoffsets = { offsetof(CPUARMState, cp15.ttbr0_s), + offsetof(CPUARMState, cp15.ttbr0_ns) } }, { .name = "TTBR1_EL1", .state = ARM_CP_STATE_BOTH, - .opc0 = 3, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 1, - .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.ttbr1_el1), - .writefn = vmsa_ttbr_write, .resetvalue = 0 }, + .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 0, .opc2 = 1, + .access = PL1_RW, .writefn = vmsa_ttbr_write, .resetvalue = 0, + .bank_fieldoffsets = { offsetof(CPUARMState, cp15.ttbr1_s), + offsetof(CPUARMState, cp15.ttbr1_ns) } }, { .name = "TCR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 2, .access = PL1_RW, .writefn = vmsa_tcr_el1_write, .resetfn = vmsa_ttbcr_reset, .raw_writefn = raw_write, - .fieldoffset = offsetof(CPUARMState, cp15.c2_control) }, + .fieldoffset = offsetof(CPUARMState, cp15.tcr_el[1]) }, { .name = "TTBCR", .cp = 15, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 2, - .access = PL1_RW, .type = ARM_CP_NO_MIGRATE, .writefn = vmsa_ttbcr_write, + .access = PL1_RW, .type = ARM_CP_ALIAS, .writefn = vmsa_ttbcr_write, .resetfn = arm_cp_reset_ignore, .raw_writefn = vmsa_ttbcr_raw_write, - .fieldoffset = offsetoflow32(CPUARMState, cp15.c2_control) }, - /* 64-bit FAR; this entry also gives us the AArch32 DFAR */ - { .name = "FAR_EL1", .state = ARM_CP_STATE_BOTH, + .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.tcr_el[3]), + offsetoflow32(CPUARMState, cp15.tcr_el[1])} }, + { .name = "FAR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.far_el[1]), .resetvalue = 0, }, + { .name = "DFAR", .cp = 15, .opc1 = 0, .crn = 6, .crm = 0, .opc2 = 0, + .access = PL1_RW, .resetvalue = 0, + .bank_fieldoffsets = { offsetof(CPUARMState, cp15.dfar_s), + offsetof(CPUARMState, cp15.dfar_ns) } }, REGINFO_SENTINEL }; @@ -1720,7 +1908,7 @@ static const ARMCPRegInfo omap_cp_reginfo[] = { .writefn = omap_threadid_write }, { .name = "TI925T_STATUS", .cp = 15, .crn = 15, .crm = 8, .opc1 = 0, .opc2 = 0, .access = PL1_RW, - .type = ARM_CP_NO_MIGRATE, + .type = ARM_CP_NO_RAW, .readfn = arm_cp_read_zero, .writefn = omap_wfi_write, }, /* TODO: Peripheral port remap register: * On OMAP2 mcr p15, 0, rn, c15, c2, 4 sets up the interrupt controller @@ -1729,7 +1917,7 @@ static const ARMCPRegInfo omap_cp_reginfo[] = { */ { .name = "OMAP_CACHEMAINT", .cp = 15, .crn = 7, .crm = CP_ANY, .opc1 = 0, .opc2 = CP_ANY, .access = PL1_W, - .type = ARM_CP_OVERRIDE | ARM_CP_NO_MIGRATE, + .type = ARM_CP_OVERRIDE | ARM_CP_NO_RAW, .writefn = omap_cachemaint_write }, { .name = "C9", .cp = 15, .crn = 9, .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_RW, @@ -1779,7 +1967,7 @@ static const ARMCPRegInfo dummy_c15_cp_reginfo[] = { { .name = "C15_IMPDEF", .cp = 15, .crn = 15, .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_RW, - .type = ARM_CP_CONST | ARM_CP_NO_MIGRATE | ARM_CP_OVERRIDE, + .type = ARM_CP_CONST | ARM_CP_NO_RAW | ARM_CP_OVERRIDE, .resetvalue = 0 }, REGINFO_SENTINEL }; @@ -1787,7 +1975,7 @@ static const ARMCPRegInfo dummy_c15_cp_reginfo[] = { static const ARMCPRegInfo cache_dirty_status_cp_reginfo[] = { /* Cache status: RAZ because we have no cache so it's always clean */ { .name = "CDSR", .cp = 15, .crn = 7, .crm = 10, .opc1 = 0, .opc2 = 6, - .access = PL1_R, .type = ARM_CP_CONST | ARM_CP_NO_MIGRATE, + .access = PL1_R, .type = ARM_CP_CONST | ARM_CP_NO_RAW, .resetvalue = 0 }, REGINFO_SENTINEL }; @@ -1795,7 +1983,7 @@ static const ARMCPRegInfo cache_dirty_status_cp_reginfo[] = { static const ARMCPRegInfo cache_block_ops_cp_reginfo[] = { /* We never have a a block transfer operation in progress */ { .name = "BXSR", .cp = 15, .crn = 7, .crm = 12, .opc1 = 0, .opc2 = 4, - .access = PL0_R, .type = ARM_CP_CONST | ARM_CP_NO_MIGRATE, + .access = PL0_R, .type = ARM_CP_CONST | ARM_CP_NO_RAW, .resetvalue = 0 }, /* The cache ops themselves: these all NOP for QEMU */ { .name = "IICR", .cp = 15, .crm = 5, .opc1 = 0, @@ -1818,10 +2006,10 @@ static const ARMCPRegInfo cache_test_clean_cp_reginfo[] = { * to indicate that there are no dirty cache lines. */ { .name = "TC_DCACHE", .cp = 15, .crn = 7, .crm = 10, .opc1 = 0, .opc2 = 3, - .access = PL0_R, .type = ARM_CP_CONST | ARM_CP_NO_MIGRATE, + .access = PL0_R, .type = ARM_CP_CONST | ARM_CP_NO_RAW, .resetvalue = (1 << 30) }, { .name = "TCI_DCACHE", .cp = 15, .crn = 7, .crm = 14, .opc1 = 0, .opc2 = 3, - .access = PL0_R, .type = ARM_CP_CONST | ARM_CP_NO_MIGRATE, + .access = PL0_R, .type = ARM_CP_CONST | ARM_CP_NO_RAW, .resetvalue = (1 << 30) }, REGINFO_SENTINEL }; @@ -1831,7 +2019,7 @@ static const ARMCPRegInfo strongarm_cp_reginfo[] = { { .name = "C9_READBUFFER", .cp = 15, .crn = 9, .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_RW, .resetvalue = 0, - .type = ARM_CP_CONST | ARM_CP_OVERRIDE | ARM_CP_NO_MIGRATE }, + .type = ARM_CP_CONST | ARM_CP_OVERRIDE | ARM_CP_NO_RAW }, REGINFO_SENTINEL }; @@ -1857,7 +2045,7 @@ static uint64_t mpidr_read(CPUARMState *env, const ARMCPRegInfo *ri) static const ARMCPRegInfo mpidr_cp_reginfo[] = { { .name = "MPIDR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 5, - .access = PL1_R, .readfn = mpidr_read, .type = ARM_CP_NO_MIGRATE }, + .access = PL1_R, .readfn = mpidr_read, .type = ARM_CP_NO_RAW }, REGINFO_SENTINEL }; @@ -1874,15 +2062,18 @@ static const ARMCPRegInfo lpae_cp_reginfo[] = { .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_OVERRIDE, .resetvalue = 0 }, { .name = "PAR", .cp = 15, .crm = 7, .opc1 = 0, - .access = PL1_RW, .type = ARM_CP_64BIT, - .fieldoffset = offsetof(CPUARMState, cp15.par_el1), .resetvalue = 0 }, + .access = PL1_RW, .type = ARM_CP_64BIT, .resetvalue = 0, + .bank_fieldoffsets = { offsetof(CPUARMState, cp15.par_s), + offsetof(CPUARMState, cp15.par_ns)} }, { .name = "TTBR0", .cp = 15, .crm = 2, .opc1 = 0, - .access = PL1_RW, .type = ARM_CP_64BIT | ARM_CP_NO_MIGRATE, - .fieldoffset = offsetof(CPUARMState, cp15.ttbr0_el1), + .access = PL1_RW, .type = ARM_CP_64BIT | ARM_CP_ALIAS, + .bank_fieldoffsets = { offsetof(CPUARMState, cp15.ttbr0_s), + offsetof(CPUARMState, cp15.ttbr0_ns) }, .writefn = vmsa_ttbr_write, .resetfn = arm_cp_reset_ignore }, { .name = "TTBR1", .cp = 15, .crm = 2, .opc1 = 1, - .access = PL1_RW, .type = ARM_CP_64BIT | ARM_CP_NO_MIGRATE, - .fieldoffset = offsetof(CPUARMState, cp15.ttbr1_el1), + .access = PL1_RW, .type = ARM_CP_64BIT | ARM_CP_ALIAS, + .bank_fieldoffsets = { offsetof(CPUARMState, cp15.ttbr1_s), + offsetof(CPUARMState, cp15.ttbr1_ns) }, .writefn = vmsa_ttbr_write, .resetfn = arm_cp_reset_ignore }, REGINFO_SENTINEL }; @@ -1911,7 +2102,7 @@ static void aa64_fpsr_write(CPUARMState *env, const ARMCPRegInfo *ri, static CPAccessResult aa64_daif_access(CPUARMState *env, const ARMCPRegInfo *ri) { - if (arm_current_el(env) == 0 && !(env->cp15.c1_sys & SCTLR_UMA)) { + if (arm_current_el(env) == 0 && !(env->cp15.sctlr_el[1] & SCTLR_UMA)) { return CP_ACCESS_TRAP; } return CP_ACCESS_OK; @@ -1929,7 +2120,7 @@ static CPAccessResult aa64_cacheop_access(CPUARMState *env, /* Cache invalidate/clean: NOP, but EL0 must UNDEF unless * SCTLR_EL1.UCI is set. */ - if (arm_current_el(env) == 0 && !(env->cp15.c1_sys & SCTLR_UCI)) { + if (arm_current_el(env) == 0 && !(env->cp15.sctlr_el[1] & SCTLR_UCI)) { return CP_ACCESS_TRAP; } return CP_ACCESS_OK; @@ -2006,7 +2197,7 @@ static CPAccessResult aa64_zva_access(CPUARMState *env, const ARMCPRegInfo *ri) /* We don't implement EL2, so the only control on DC ZVA is the * bit in the SCTLR which can prohibit access for EL0. */ - if (arm_current_el(env) == 0 && !(env->cp15.c1_sys & SCTLR_DZE)) { + if (arm_current_el(env) == 0 && !(env->cp15.sctlr_el[1] & SCTLR_DZE)) { return CP_ACCESS_TRAP; } return CP_ACCESS_OK; @@ -2045,6 +2236,24 @@ static void spsel_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t val) update_spsel(env, val); } +static void sctlr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + ARMCPU *cpu = arm_env_get_cpu(env); + + if (raw_read(env, ri) == value) { + /* Skip the TLB flush if nothing actually changed; Linux likes + * to do a lot of pointless SCTLR writes. + */ + return; + } + + raw_write(env, ri, value); + /* ??? Lots of these bits are not implemented. */ + /* This may enable/disable the MMU, so do a TLB flush. */ + tlb_flush(CPU(cpu), 1); +} + static const ARMCPRegInfo v8_cp_reginfo[] = { /* Minimal set of EL0-visible registers. This will need to be expanded * significantly for system emulation of AArch64 CPUs. @@ -2054,7 +2263,7 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .access = PL0_RW, .type = ARM_CP_NZCV }, { .name = "DAIF", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .opc2 = 1, .crn = 4, .crm = 2, - .type = ARM_CP_NO_MIGRATE, + .type = ARM_CP_NO_RAW, .access = PL0_RW, .accessfn = aa64_daif_access, .fieldoffset = offsetof(CPUARMState, daif), .writefn = aa64_daif_write, .resetfn = arm_cp_reset_ignore }, @@ -2066,7 +2275,7 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .access = PL0_RW, .readfn = aa64_fpsr_read, .writefn = aa64_fpsr_write }, { .name = "DCZID_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .opc2 = 7, .crn = 0, .crm = 0, - .access = PL0_R, .type = ARM_CP_NO_MIGRATE, + .access = PL0_R, .type = ARM_CP_NO_RAW, .readfn = aa64_dczid_read }, { .name = "DC_ZVA", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 4, .opc2 = 1, @@ -2117,77 +2326,77 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { /* TLBI operations */ { .name = "TLBI_VMALLE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 0, - .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = tlbiall_is_write }, { .name = "TLBI_VAE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 1, - .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_va_is_write }, { .name = "TLBI_ASIDE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 2, - .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_asid_is_write }, { .name = "TLBI_VAAE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 3, - .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_vaa_is_write }, { .name = "TLBI_VALE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 5, - .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_va_is_write }, { .name = "TLBI_VAALE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 7, - .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_vaa_is_write }, { .name = "TLBI_VMALLE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 0, - .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = tlbiall_write }, { .name = "TLBI_VAE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 1, - .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_va_write }, { .name = "TLBI_ASIDE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 2, - .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_asid_write }, { .name = "TLBI_VAAE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 3, - .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_vaa_write }, { .name = "TLBI_VALE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 5, - .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_va_write }, { .name = "TLBI_VAALE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 7, - .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_vaa_write }, #ifndef CONFIG_USER_ONLY /* 64 bit address translation operations */ { .name = "AT_S1E1R", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 0, - .access = PL1_W, .type = ARM_CP_NO_MIGRATE, .writefn = ats_write }, + .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = ats_write64 }, { .name = "AT_S1E1W", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 1, - .access = PL1_W, .type = ARM_CP_NO_MIGRATE, .writefn = ats_write }, + .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = ats_write64 }, { .name = "AT_S1E0R", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 2, - .access = PL1_W, .type = ARM_CP_NO_MIGRATE, .writefn = ats_write }, + .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = ats_write64 }, { .name = "AT_S1E0W", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 3, - .access = PL1_W, .type = ARM_CP_NO_MIGRATE, .writefn = ats_write }, + .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = ats_write64 }, #endif /* TLB invalidate last level of translation table walk */ { .name = "TLBIMVALIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 5, - .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbimva_is_write }, + .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbimva_is_write }, { .name = "TLBIMVAALIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 7, - .type = ARM_CP_NO_MIGRATE, .access = PL1_W, + .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbimvaa_is_write }, { .name = "TLBIMVAL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 5, - .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbimva_write }, + .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbimva_write }, { .name = "TLBIMVAAL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 7, - .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbimvaa_write }, + .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbimvaa_write }, /* 32 bit cache operations */ { .name = "ICIALLUIS", .cp = 15, .opc1 = 0, .crn = 7, .crm = 1, .opc2 = 0, .type = ARM_CP_NOP, .access = PL1_W }, @@ -2216,19 +2425,20 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { { .name = "DCCISW", .cp = 15, .opc1 = 0, .crn = 7, .crm = 14, .opc2 = 2, .type = ARM_CP_NOP, .access = PL1_W }, /* MMU Domain access control / MPU write buffer control */ - { .name = "DACR", .cp = 15, - .opc1 = 0, .crn = 3, .crm = 0, .opc2 = 0, - .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c3), - .resetvalue = 0, .writefn = dacr_write, .raw_writefn = raw_write, }, + { .name = "DACR", .cp = 15, .opc1 = 0, .crn = 3, .crm = 0, .opc2 = 0, + .access = PL1_RW, .resetvalue = 0, + .writefn = dacr_write, .raw_writefn = raw_write, + .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.dacr_s), + offsetoflow32(CPUARMState, cp15.dacr_ns) } }, { .name = "ELR_EL1", .state = ARM_CP_STATE_AA64, - .type = ARM_CP_NO_MIGRATE, + .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 0, .crn = 4, .crm = 0, .opc2 = 1, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, elr_el[1]) }, { .name = "SPSR_EL1", .state = ARM_CP_STATE_AA64, - .type = ARM_CP_NO_MIGRATE, + .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 0, .crn = 4, .crm = 0, .opc2 = 0, - .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, banked_spsr[0]) }, + .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, banked_spsr[1]) }, /* We rely on the access checks not allowing the guest to write to the * state field when SPSel indicates that it's being used as the stack * pointer. @@ -2236,11 +2446,15 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { { .name = "SP_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 4, .crm = 1, .opc2 = 0, .access = PL1_RW, .accessfn = sp_el0_access, - .type = ARM_CP_NO_MIGRATE, + .type = ARM_CP_ALIAS, .fieldoffset = offsetof(CPUARMState, sp_el[0]) }, + { .name = "SP_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 1, .opc2 = 0, + .access = PL2_RW, .type = ARM_CP_ALIAS, + .fieldoffset = offsetof(CPUARMState, sp_el[1]) }, { .name = "SPSel", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 4, .crm = 2, .opc2 = 0, - .type = ARM_CP_NO_MIGRATE, + .type = ARM_CP_NO_RAW, .access = PL1_RW, .readfn = spsel_read, .writefn = spsel_write }, REGINFO_SENTINEL }; @@ -2252,7 +2466,7 @@ static const ARMCPRegInfo v8_el3_no_el2_cp_reginfo[] = { .access = PL2_RW, .readfn = arm_cp_read_zero, .writefn = arm_cp_write_ignore }, { .name = "HCR_EL2", .state = ARM_CP_STATE_AA64, - .type = ARM_CP_NO_MIGRATE, + .type = ARM_CP_NO_RAW, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 0, .access = PL2_RW, .readfn = arm_cp_read_zero, .writefn = arm_cp_write_ignore }, @@ -2289,20 +2503,29 @@ static const ARMCPRegInfo v8_el2_cp_reginfo[] = { .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.hcr_el2), .writefn = hcr_write }, + { .name = "DACR32_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 3, .crm = 0, .opc2 = 0, + .access = PL2_RW, .resetvalue = 0, + .writefn = dacr_write, .raw_writefn = raw_write, + .fieldoffset = offsetof(CPUARMState, cp15.dacr32_el2) }, { .name = "ELR_EL2", .state = ARM_CP_STATE_AA64, - .type = ARM_CP_NO_MIGRATE, + .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 0, .opc2 = 1, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, elr_el[2]) }, { .name = "ESR_EL2", .state = ARM_CP_STATE_AA64, - .type = ARM_CP_NO_MIGRATE, + .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 2, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.esr_el[2]) }, + { .name = "IFSR32_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 0, .opc2 = 1, + .access = PL2_RW, .resetvalue = 0, + .fieldoffset = offsetof(CPUARMState, cp15.ifsr32_el2) }, { .name = "FAR_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.far_el[2]) }, { .name = "SPSR_EL2", .state = ARM_CP_STATE_AA64, - .type = ARM_CP_NO_MIGRATE, + .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 0, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, banked_spsr[6]) }, { .name = "VBAR_EL2", .state = ARM_CP_STATE_AA64, @@ -2310,24 +2533,64 @@ static const ARMCPRegInfo v8_el2_cp_reginfo[] = { .access = PL2_RW, .writefn = vbar_write, .fieldoffset = offsetof(CPUARMState, cp15.vbar_el[2]), .resetvalue = 0 }, + { .name = "SP_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 4, .crm = 1, .opc2 = 0, + .access = PL3_RW, .type = ARM_CP_ALIAS, + .fieldoffset = offsetof(CPUARMState, sp_el[2]) }, REGINFO_SENTINEL }; -static const ARMCPRegInfo v8_el3_cp_reginfo[] = { +static const ARMCPRegInfo el3_cp_reginfo[] = { + { .name = "SCR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 1, .crm = 1, .opc2 = 0, + .access = PL3_RW, .fieldoffset = offsetof(CPUARMState, cp15.scr_el3), + .resetvalue = 0, .writefn = scr_write }, + { .name = "SCR", .type = ARM_CP_ALIAS, + .cp = 15, .opc1 = 0, .crn = 1, .crm = 1, .opc2 = 0, + .access = PL3_RW, .fieldoffset = offsetoflow32(CPUARMState, cp15.scr_el3), + .resetfn = arm_cp_reset_ignore, .writefn = scr_write }, + { .name = "SDER32_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 1, .crm = 1, .opc2 = 1, + .access = PL3_RW, .resetvalue = 0, + .fieldoffset = offsetof(CPUARMState, cp15.sder) }, + { .name = "SDER", + .cp = 15, .opc1 = 0, .crn = 1, .crm = 1, .opc2 = 1, + .access = PL3_RW, .resetvalue = 0, + .fieldoffset = offsetoflow32(CPUARMState, cp15.sder) }, + /* TODO: Implement NSACR trapping of secure EL1 accesses to EL3 */ + { .name = "NSACR", .cp = 15, .opc1 = 0, .crn = 1, .crm = 1, .opc2 = 2, + .access = PL3_W | PL1_R, .resetvalue = 0, + .fieldoffset = offsetof(CPUARMState, cp15.nsacr) }, + { .name = "MVBAR", .cp = 15, .opc1 = 0, .crn = 12, .crm = 0, .opc2 = 1, + .access = PL3_RW, .writefn = vbar_write, .resetvalue = 0, + .fieldoffset = offsetof(CPUARMState, cp15.mvbar) }, + { .name = "SCTLR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 1, .crm = 0, .opc2 = 0, + .access = PL3_RW, .raw_writefn = raw_write, .writefn = sctlr_write, + .fieldoffset = offsetof(CPUARMState, cp15.sctlr_el[3]) }, + { .name = "TTBR0_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 2, .crm = 0, .opc2 = 0, + .access = PL3_RW, .writefn = vmsa_ttbr_write, .resetvalue = 0, + .fieldoffset = offsetof(CPUARMState, cp15.ttbr0_el[3]) }, + { .name = "TCR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 2, .crm = 0, .opc2 = 2, + .access = PL3_RW, .writefn = vmsa_tcr_el1_write, + .resetfn = vmsa_ttbcr_reset, .raw_writefn = raw_write, + .fieldoffset = offsetof(CPUARMState, cp15.tcr_el[3]) }, { .name = "ELR_EL3", .state = ARM_CP_STATE_AA64, - .type = ARM_CP_NO_MIGRATE, + .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 6, .crn = 4, .crm = 0, .opc2 = 1, .access = PL3_RW, .fieldoffset = offsetof(CPUARMState, elr_el[3]) }, { .name = "ESR_EL3", .state = ARM_CP_STATE_AA64, - .type = ARM_CP_NO_MIGRATE, + .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 6, .crn = 5, .crm = 2, .opc2 = 0, .access = PL3_RW, .fieldoffset = offsetof(CPUARMState, cp15.esr_el[3]) }, { .name = "FAR_EL3", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 6, .crn = 6, .crm = 0, .opc2 = 0, .access = PL3_RW, .fieldoffset = offsetof(CPUARMState, cp15.far_el[3]) }, { .name = "SPSR_EL3", .state = ARM_CP_STATE_AA64, - .type = ARM_CP_NO_MIGRATE, + .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 6, .crn = 4, .crm = 0, .opc2 = 0, .access = PL3_RW, .fieldoffset = offsetof(CPUARMState, banked_spsr[7]) }, { .name = "VBAR_EL3", .state = ARM_CP_STATE_AA64, @@ -2335,38 +2598,15 @@ static const ARMCPRegInfo v8_el3_cp_reginfo[] = { .access = PL3_RW, .writefn = vbar_write, .fieldoffset = offsetof(CPUARMState, cp15.vbar_el[3]), .resetvalue = 0 }, - { .name = "SCR_EL3", .state = ARM_CP_STATE_AA64, - .type = ARM_CP_NO_MIGRATE, - .opc0 = 3, .opc1 = 6, .crn = 1, .crm = 1, .opc2 = 0, - .access = PL3_RW, .fieldoffset = offsetof(CPUARMState, cp15.scr_el3), - .writefn = scr_write }, REGINFO_SENTINEL }; -static void sctlr_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - ARMCPU *cpu = arm_env_get_cpu(env); - - if (raw_read(env, ri) == value) { - /* Skip the TLB flush if nothing actually changed; Linux likes - * to do a lot of pointless SCTLR writes. - */ - return; - } - - raw_write(env, ri, value); - /* ??? Lots of these bits are not implemented. */ - /* This may enable/disable the MMU, so do a TLB flush. */ - tlb_flush(CPU(cpu), 1); -} - static CPAccessResult ctr_el0_access(CPUARMState *env, const ARMCPRegInfo *ri) { /* Only accessible in EL0 if SCTLR.UCT is set (and only in AArch64, * but the AArch32 CTR has its own reginfo struct) */ - if (arm_current_el(env) == 0 && !(env->cp15.c1_sys & SCTLR_UCT)) { + if (arm_current_el(env) == 0 && !(env->cp15.sctlr_el[1] & SCTLR_UCT)) { return CP_ACCESS_TRAP; } return CP_ACCESS_OK; @@ -2397,7 +2637,7 @@ static const ARMCPRegInfo debug_cp_reginfo[] = { */ { .name = "MDCCSR_EL0", .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 0, - .type = ARM_CP_NO_MIGRATE, + .type = ARM_CP_ALIAS, .access = PL1_R, .fieldoffset = offsetof(CPUARMState, cp15.mdscr_el1), .resetfn = arm_cp_reset_ignore }, @@ -2850,7 +3090,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) ARMCPRegInfo pmcr = { .name = "PMCR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 0, .access = PL0_RW, - .type = ARM_CP_IO | ARM_CP_NO_MIGRATE, + .type = ARM_CP_IO | ARM_CP_ALIAS, .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcr), .accessfn = pmreg_access, .writefn = pmcr_write, .raw_writefn = raw_write, @@ -2940,17 +3180,30 @@ void register_cp_regs_for_features(ARMCPU *cpu) .resetvalue = cpu->mvfr2 }, REGINFO_SENTINEL }; - ARMCPRegInfo rvbar = { - .name = "RVBAR_EL1", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 0, .opc2 = 2, - .type = ARM_CP_CONST, .access = PL1_R, .resetvalue = cpu->rvbar - }; - define_one_arm_cp_reg(cpu, &rvbar); + /* RVBAR_EL1 is only implemented if EL1 is the highest EL */ + if (!arm_feature(env, ARM_FEATURE_EL3) && + !arm_feature(env, ARM_FEATURE_EL2)) { + ARMCPRegInfo rvbar = { + .name = "RVBAR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 0, .opc2 = 1, + .type = ARM_CP_CONST, .access = PL1_R, .resetvalue = cpu->rvbar + }; + define_one_arm_cp_reg(cpu, &rvbar); + } define_arm_cp_regs(cpu, v8_idregs); define_arm_cp_regs(cpu, v8_cp_reginfo); } if (arm_feature(env, ARM_FEATURE_EL2)) { define_arm_cp_regs(cpu, v8_el2_cp_reginfo); + /* RVBAR_EL2 is only implemented if EL2 is the highest EL */ + if (!arm_feature(env, ARM_FEATURE_EL3)) { + ARMCPRegInfo rvbar = { + .name = "RVBAR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 0, .opc2 = 1, + .type = ARM_CP_CONST, .access = PL2_R, .resetvalue = cpu->rvbar + }; + define_one_arm_cp_reg(cpu, &rvbar); + } } else { /* If EL2 is missing but higher ELs are enabled, we need to * register the no_el2 reginfos. @@ -2960,7 +3213,13 @@ void register_cp_regs_for_features(ARMCPU *cpu) } } if (arm_feature(env, ARM_FEATURE_EL3)) { - define_arm_cp_regs(cpu, v8_el3_cp_reginfo); + define_arm_cp_regs(cpu, el3_cp_reginfo); + ARMCPRegInfo rvbar = { + .name = "RVBAR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 12, .crm = 0, .opc2 = 1, + .type = ARM_CP_CONST, .access = PL3_R, .resetvalue = cpu->rvbar + }; + define_one_arm_cp_reg(cpu, &rvbar); } if (arm_feature(env, ARM_FEATURE_MPU)) { /* These are the MPU registers prior to PMSAv6. Any new @@ -3160,8 +3419,10 @@ void register_cp_regs_for_features(ARMCPU *cpu) { ARMCPRegInfo sctlr = { .name = "SCTLR", .state = ARM_CP_STATE_BOTH, - .opc0 = 3, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c1_sys), + .opc0 = 3, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 0, + .access = PL1_RW, + .bank_fieldoffsets = { offsetof(CPUARMState, cp15.sctlr_s), + offsetof(CPUARMState, cp15.sctlr_ns) }, .writefn = sctlr_write, .resetvalue = cpu->reset_sctlr, .raw_writefn = raw_write, }; @@ -3287,7 +3548,7 @@ CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp) } static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, - void *opaque, int state, + void *opaque, int state, int secstate, int crm, int opc1, int opc2) { /* Private utility function for define_one_arm_cp_reg_with_opaque(): @@ -3296,22 +3557,59 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, uint32_t *key = g_new(uint32_t, 1); ARMCPRegInfo *r2 = g_memdup(r, sizeof(ARMCPRegInfo)); int is64 = (r->type & ARM_CP_64BIT) ? 1 : 0; - if (r->state == ARM_CP_STATE_BOTH && state == ARM_CP_STATE_AA32) { - /* The AArch32 view of a shared register sees the lower 32 bits - * of a 64 bit backing field. It is not migratable as the AArch64 - * view handles that. AArch64 also handles reset. - * We assume it is a cp15 register if the .cp field is left unset. + int ns = (secstate & ARM_CP_SECSTATE_NS) ? 1 : 0; + + /* Reset the secure state to the specific incoming state. This is + * necessary as the register may have been defined with both states. + */ + r2->secure = secstate; + + if (r->bank_fieldoffsets[0] && r->bank_fieldoffsets[1]) { + /* Register is banked (using both entries in array). + * Overwriting fieldoffset as the array is only used to define + * banked registers but later only fieldoffset is used. */ - if (r2->cp == 0) { - r2->cp = 15; + r2->fieldoffset = r->bank_fieldoffsets[ns]; + } + + if (state == ARM_CP_STATE_AA32) { + if (r->bank_fieldoffsets[0] && r->bank_fieldoffsets[1]) { + /* If the register is banked then we don't need to migrate or + * reset the 32-bit instance in certain cases: + * + * 1) If the register has both 32-bit and 64-bit instances then we + * can count on the 64-bit instance taking care of the + * non-secure bank. + * 2) If ARMv8 is enabled then we can count on a 64-bit version + * taking care of the secure bank. This requires that separate + * 32 and 64-bit definitions are provided. + */ + if ((r->state == ARM_CP_STATE_BOTH && ns) || + (arm_feature(&cpu->env, ARM_FEATURE_V8) && !ns)) { + r2->type |= ARM_CP_ALIAS; + r2->resetfn = arm_cp_reset_ignore; + } + } else if ((secstate != r->secure) && !ns) { + /* The register is not banked so we only want to allow migration of + * the non-secure instance. + */ + r2->type |= ARM_CP_ALIAS; + r2->resetfn = arm_cp_reset_ignore; } - r2->type |= ARM_CP_NO_MIGRATE; - r2->resetfn = arm_cp_reset_ignore; + + if (r->state == ARM_CP_STATE_BOTH) { + /* We assume it is a cp15 register if the .cp field is left unset. + */ + if (r2->cp == 0) { + r2->cp = 15; + } + #ifdef HOST_WORDS_BIGENDIAN - if (r2->fieldoffset) { - r2->fieldoffset += sizeof(uint32_t); - } + if (r2->fieldoffset) { + r2->fieldoffset += sizeof(uint32_t); + } #endif + } } if (state == ARM_CP_STATE_AA64) { /* To allow abbreviation of ARMCPRegInfo @@ -3327,7 +3625,7 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, *key = ENCODE_AA64_CP_REG(r2->cp, r2->crn, crm, r2->opc0, opc1, opc2); } else { - *key = ENCODE_CP_REG(r2->cp, is64, r2->crn, crm, opc1, opc2); + *key = ENCODE_CP_REG(r2->cp, is64, ns, r2->crn, crm, opc1, opc2); } if (opaque) { r2->opaque = opaque; @@ -3344,15 +3642,25 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, r2->opc2 = opc2; /* By convention, for wildcarded registers only the first * entry is used for migration; the others are marked as - * NO_MIGRATE so we don't try to transfer the register + * ALIAS so we don't try to transfer the register * multiple times. Special registers (ie NOP/WFI) are - * never migratable. + * never migratable and not even raw-accessible. */ - if ((r->type & ARM_CP_SPECIAL) || - ((r->crm == CP_ANY) && crm != 0) || + if ((r->type & ARM_CP_SPECIAL)) { + r2->type |= ARM_CP_NO_RAW; + } + if (((r->crm == CP_ANY) && crm != 0) || ((r->opc1 == CP_ANY) && opc1 != 0) || ((r->opc2 == CP_ANY) && opc2 != 0)) { - r2->type |= ARM_CP_NO_MIGRATE; + r2->type |= ARM_CP_ALIAS; + } + + /* Check that raw accesses are either forbidden or handled. Note that + * we can't assert this earlier because the setup of fieldoffset for + * banked registers has to be done first. + */ + if (!(r2->type & ARM_CP_NO_RAW)) { + assert(!raw_accessors_invalid(r2)); } /* Overriding of an existing definition must be explicitly @@ -3460,10 +3768,14 @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, */ if (!(r->type & (ARM_CP_SPECIAL|ARM_CP_CONST))) { if (r->access & PL3_R) { - assert(r->fieldoffset || r->readfn); + assert((r->fieldoffset || + (r->bank_fieldoffsets[0] && r->bank_fieldoffsets[1])) || + r->readfn); } if (r->access & PL3_W) { - assert(r->fieldoffset || r->writefn); + assert((r->fieldoffset || + (r->bank_fieldoffsets[0] && r->bank_fieldoffsets[1])) || + r->writefn); } } /* Bad type field probably means missing sentinel at end of reg list */ @@ -3476,8 +3788,32 @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, if (r->state != state && r->state != ARM_CP_STATE_BOTH) { continue; } - add_cpreg_to_hashtable(cpu, r, opaque, state, - crm, opc1, opc2); + if (state == ARM_CP_STATE_AA32) { + /* Under AArch32 CP registers can be common + * (same for secure and non-secure world) or banked. + */ + switch (r->secure) { + case ARM_CP_SECSTATE_S: + case ARM_CP_SECSTATE_NS: + add_cpreg_to_hashtable(cpu, r, opaque, state, + r->secure, crm, opc1, opc2); + break; + default: + add_cpreg_to_hashtable(cpu, r, opaque, state, + ARM_CP_SECSTATE_S, + crm, opc1, opc2); + add_cpreg_to_hashtable(cpu, r, opaque, state, + ARM_CP_SECSTATE_NS, + crm, opc1, opc2); + break; + } + } else { + /* AArch64 registers get mapped to non-secure instance + * of AArch32 */ + add_cpreg_to_hashtable(cpu, r, opaque, state, + ARM_CP_SECSTATE_NS, + crm, opc1, opc2); + } } } } @@ -3551,6 +3887,8 @@ uint32_t cpsr_read(CPUARMState *env) void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) { + uint32_t changed_daif; + if (mask & CPSR_NZCV) { env->ZF = (~val) & CPSR_Z; env->NF = val; @@ -3573,6 +3911,58 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) env->GE = (val >> 16) & 0xf; } + /* In a V7 implementation that includes the security extensions but does + * not include Virtualization Extensions the SCR.FW and SCR.AW bits control + * whether non-secure software is allowed to change the CPSR_F and CPSR_A + * bits respectively. + * + * In a V8 implementation, it is permitted for privileged software to + * change the CPSR A/F bits regardless of the SCR.AW/FW bits. + */ + if (!arm_feature(env, ARM_FEATURE_V8) && + arm_feature(env, ARM_FEATURE_EL3) && + !arm_feature(env, ARM_FEATURE_EL2) && + !arm_is_secure(env)) { + + changed_daif = (env->daif ^ val) & mask; + + if (changed_daif & CPSR_A) { + /* Check to see if we are allowed to change the masking of async + * abort exceptions from a non-secure state. + */ + if (!(env->cp15.scr_el3 & SCR_AW)) { + qemu_log_mask(LOG_GUEST_ERROR, + "Ignoring attempt to switch CPSR_A flag from " + "non-secure world with SCR.AW bit clear\n"); + mask &= ~CPSR_A; + } + } + + if (changed_daif & CPSR_F) { + /* Check to see if we are allowed to change the masking of FIQ + * exceptions from a non-secure state. + */ + if (!(env->cp15.scr_el3 & SCR_FW)) { + qemu_log_mask(LOG_GUEST_ERROR, + "Ignoring attempt to switch CPSR_F flag from " + "non-secure world with SCR.FW bit clear\n"); + mask &= ~CPSR_F; + } + + /* Check whether non-maskable FIQ (NMFI) support is enabled. + * If this bit is set software is not allowed to mask + * FIQs, but is allowed to set CPSR_F to 0. + */ + if ((A32_BANKED_CURRENT_REG_GET(env, sctlr) & SCTLR_NMFI) && + (val & CPSR_F)) { + qemu_log_mask(LOG_GUEST_ERROR, + "Ignoring attempt to enable CPSR_F flag " + "(non-maskable FIQ [NMFI] support enabled)\n"); + mask &= ~CPSR_F; + } + } + } + env->daif &= ~(CPSR_AIF & mask); env->daif |= val & CPSR_AIF & mask; @@ -3706,6 +4096,11 @@ unsigned int arm_excp_target_el(CPUState *cs, unsigned int excp_idx) return 1; } +void aarch64_sync_64_to_32(CPUARMState *env) +{ + g_assert_not_reached(); +} + #else /* Map CPU modes onto saved register banks. */ @@ -3761,6 +4156,101 @@ void switch_mode(CPUARMState *env, int mode) env->spsr = env->banked_spsr[i]; } +/* Physical Interrupt Target EL Lookup Table + * + * [ From ARM ARM section G1.13.4 (Table G1-15) ] + * + * The below multi-dimensional table is used for looking up the target + * exception level given numerous condition criteria. Specifically, the + * target EL is based on SCR and HCR routing controls as well as the + * currently executing EL and secure state. + * + * Dimensions: + * target_el_table[2][2][2][2][2][4] + * | | | | | +--- Current EL + * | | | | +------ Non-secure(0)/Secure(1) + * | | | +--------- HCR mask override + * | | +------------ SCR exec state control + * | +--------------- SCR mask override + * +------------------ 32-bit(0)/64-bit(1) EL3 + * + * The table values are as such: + * 0-3 = EL0-EL3 + * -1 = Cannot occur + * + * The ARM ARM target EL table includes entries indicating that an "exception + * is not taken". The two cases where this is applicable are: + * 1) An exception is taken from EL3 but the SCR does not have the exception + * routed to EL3. + * 2) An exception is taken from EL2 but the HCR does not have the exception + * routed to EL2. + * In these two cases, the below table contain a target of EL1. This value is + * returned as it is expected that the consumer of the table data will check + * for "target EL >= current EL" to ensure the exception is not taken. + * + * SCR HCR + * 64 EA AMO From + * BIT IRQ IMO Non-secure Secure + * EL3 FIQ RW FMO EL0 EL1 EL2 EL3 EL0 EL1 EL2 EL3 + */ +const int8_t target_el_table[2][2][2][2][2][4] = { + {{{{/* 0 0 0 0 */{ 1, 1, 2, -1 },{ 3, -1, -1, 3 },}, + {/* 0 0 0 1 */{ 2, 2, 2, -1 },{ 3, -1, -1, 3 },},}, + {{/* 0 0 1 0 */{ 1, 1, 2, -1 },{ 3, -1, -1, 3 },}, + {/* 0 0 1 1 */{ 2, 2, 2, -1 },{ 3, -1, -1, 3 },},},}, + {{{/* 0 1 0 0 */{ 3, 3, 3, -1 },{ 3, -1, -1, 3 },}, + {/* 0 1 0 1 */{ 3, 3, 3, -1 },{ 3, -1, -1, 3 },},}, + {{/* 0 1 1 0 */{ 3, 3, 3, -1 },{ 3, -1, -1, 3 },}, + {/* 0 1 1 1 */{ 3, 3, 3, -1 },{ 3, -1, -1, 3 },},},},}, + {{{{/* 1 0 0 0 */{ 1, 1, 2, -1 },{ 1, 1, -1, 1 },}, + {/* 1 0 0 1 */{ 2, 2, 2, -1 },{ 1, 1, -1, 1 },},}, + {{/* 1 0 1 0 */{ 1, 1, 1, -1 },{ 1, 1, -1, 1 },}, + {/* 1 0 1 1 */{ 2, 2, 2, -1 },{ 1, 1, -1, 1 },},},}, + {{{/* 1 1 0 0 */{ 3, 3, 3, -1 },{ 3, 3, -1, 3 },}, + {/* 1 1 0 1 */{ 3, 3, 3, -1 },{ 3, 3, -1, 3 },},}, + {{/* 1 1 1 0 */{ 3, 3, 3, -1 },{ 3, 3, -1, 3 },}, + {/* 1 1 1 1 */{ 3, 3, 3, -1 },{ 3, 3, -1, 3 },},},},}, +}; + +/* + * Determine the target EL for physical exceptions + */ +static inline uint32_t arm_phys_excp_target_el(CPUState *cs, uint32_t excp_idx, + uint32_t cur_el, bool secure) +{ + CPUARMState *env = cs->env_ptr; + int rw = ((env->cp15.scr_el3 & SCR_RW) == SCR_RW); + int scr; + int hcr; + int target_el; + int is64 = arm_el_is_aa64(env, 3); + + switch (excp_idx) { + case EXCP_IRQ: + scr = ((env->cp15.scr_el3 & SCR_IRQ) == SCR_IRQ); + hcr = ((env->cp15.hcr_el2 & HCR_IMO) == HCR_IMO); + break; + case EXCP_FIQ: + scr = ((env->cp15.scr_el3 & SCR_FIQ) == SCR_FIQ); + hcr = ((env->cp15.hcr_el2 & HCR_FMO) == HCR_FMO); + break; + default: + scr = ((env->cp15.scr_el3 & SCR_EA) == SCR_EA); + hcr = ((env->cp15.hcr_el2 & HCR_AMO) == HCR_AMO); + break; + }; + + /* If HCR.TGE is set then HCR is treated as being 1 */ + hcr |= ((env->cp15.hcr_el2 & HCR_TGE) == HCR_TGE); + + /* Perform a table-lookup for the target EL given the current state */ + target_el = target_el_table[is64][scr][rw][hcr][secure][cur_el]; + + assert(target_el > 0); + + return target_el; +} + /* * Determine the target EL for a given exception type. */ @@ -3770,13 +4260,7 @@ unsigned int arm_excp_target_el(CPUState *cs, unsigned int excp_idx) CPUARMState *env = &cpu->env; unsigned int cur_el = arm_current_el(env); unsigned int target_el; - /* FIXME: Use actual secure state. */ - bool secure = false; - - if (!env->aarch64) { - /* TODO: Add EL2 and 3 exception handling for AArch32. */ - return 1; - } + bool secure = arm_is_secure(env); switch (excp_idx) { case EXCP_HVC: @@ -3788,19 +4272,8 @@ unsigned int arm_excp_target_el(CPUState *cs, unsigned int excp_idx) break; case EXCP_FIQ: case EXCP_IRQ: - { - const uint64_t hcr_mask = excp_idx == EXCP_FIQ ? HCR_FMO : HCR_IMO; - const uint32_t scr_mask = excp_idx == EXCP_FIQ ? SCR_FIQ : SCR_IRQ; - - target_el = 1; - if (!secure && (env->cp15.hcr_el2 & hcr_mask)) { - target_el = 2; - } - if (env->cp15.scr_el3 & scr_mask) { - target_el = 3; - } + target_el = arm_phys_excp_target_el(cs, excp_idx, cur_el, secure); break; - } case EXCP_VIRQ: case EXCP_VFIQ: target_el = 1; @@ -3861,6 +4334,16 @@ static void do_v7m_exception_exit(CPUARMState *env) env->regs[12] = v7m_pop(env); env->regs[14] = v7m_pop(env); env->regs[15] = v7m_pop(env); + if (env->regs[15] & 1) { + qemu_log_mask(LOG_GUEST_ERROR, + "M profile return from interrupt with misaligned " + "PC is UNPREDICTABLE\n"); + /* Actual hardware seems to ignore the lsbit, and there are several + * RTOSes out there which incorrectly assume the r15 in the stack + * frame should be a Thumb-style "lsbit indicates ARM/Thumb" value. + */ + env->regs[15] &= ~1U; + } xpsr = v7m_pop(env); xpsr_write(env, xpsr, 0xfffffdff); /* Undo stack alignment. */ @@ -3957,6 +4440,212 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs) env->thumb = addr & 1; } +/* Function used to synchronize QEMU's AArch64 register set with AArch32 + * register set. This is necessary when switching between AArch32 and AArch64 + * execution state. + */ +void aarch64_sync_32_to_64(CPUARMState *env) +{ + int i; + uint32_t mode = env->uncached_cpsr & CPSR_M; + + /* We can blanket copy R[0:7] to X[0:7] */ + for (i = 0; i < 8; i++) { + env->xregs[i] = env->regs[i]; + } + + /* Unless we are in FIQ mode, x8-x12 come from the user registers r8-r12. + * Otherwise, they come from the banked user regs. + */ + if (mode == ARM_CPU_MODE_FIQ) { + for (i = 8; i < 13; i++) { + env->xregs[i] = env->usr_regs[i - 8]; + } + } else { + for (i = 8; i < 13; i++) { + env->xregs[i] = env->regs[i]; + } + } + + /* Registers x13-x23 are the various mode SP and FP registers. Registers + * r13 and r14 are only copied if we are in that mode, otherwise we copy + * from the mode banked register. + */ + if (mode == ARM_CPU_MODE_USR || mode == ARM_CPU_MODE_SYS) { + env->xregs[13] = env->regs[13]; + env->xregs[14] = env->regs[14]; + } else { + env->xregs[13] = env->banked_r13[bank_number(ARM_CPU_MODE_USR)]; + /* HYP is an exception in that it is copied from r14 */ + if (mode == ARM_CPU_MODE_HYP) { + env->xregs[14] = env->regs[14]; + } else { + env->xregs[14] = env->banked_r14[bank_number(ARM_CPU_MODE_USR)]; + } + } + + if (mode == ARM_CPU_MODE_HYP) { + env->xregs[15] = env->regs[13]; + } else { + env->xregs[15] = env->banked_r13[bank_number(ARM_CPU_MODE_HYP)]; + } + + if (mode == ARM_CPU_MODE_IRQ) { + env->xregs[16] = env->regs[13]; + env->xregs[17] = env->regs[14]; + } else { + env->xregs[16] = env->banked_r13[bank_number(ARM_CPU_MODE_IRQ)]; + env->xregs[17] = env->banked_r14[bank_number(ARM_CPU_MODE_IRQ)]; + } + + if (mode == ARM_CPU_MODE_SVC) { + env->xregs[18] = env->regs[13]; + env->xregs[19] = env->regs[14]; + } else { + env->xregs[18] = env->banked_r13[bank_number(ARM_CPU_MODE_SVC)]; + env->xregs[19] = env->banked_r14[bank_number(ARM_CPU_MODE_SVC)]; + } + + if (mode == ARM_CPU_MODE_ABT) { + env->xregs[20] = env->regs[13]; + env->xregs[21] = env->regs[14]; + } else { + env->xregs[20] = env->banked_r13[bank_number(ARM_CPU_MODE_ABT)]; + env->xregs[21] = env->banked_r14[bank_number(ARM_CPU_MODE_ABT)]; + } + + if (mode == ARM_CPU_MODE_UND) { + env->xregs[22] = env->regs[13]; + env->xregs[23] = env->regs[14]; + } else { + env->xregs[22] = env->banked_r13[bank_number(ARM_CPU_MODE_UND)]; + env->xregs[23] = env->banked_r14[bank_number(ARM_CPU_MODE_UND)]; + } + + /* Registers x24-x30 are mapped to r8-r14 in FIQ mode. If we are in FIQ + * mode, then we can copy from r8-r14. Otherwise, we copy from the + * FIQ bank for r8-r14. + */ + if (mode == ARM_CPU_MODE_FIQ) { + for (i = 24; i < 31; i++) { + env->xregs[i] = env->regs[i - 16]; /* X[24:30] <- R[8:14] */ + } + } else { + for (i = 24; i < 29; i++) { + env->xregs[i] = env->fiq_regs[i - 24]; + } + env->xregs[29] = env->banked_r13[bank_number(ARM_CPU_MODE_FIQ)]; + env->xregs[30] = env->banked_r14[bank_number(ARM_CPU_MODE_FIQ)]; + } + + env->pc = env->regs[15]; +} + +/* Function used to synchronize QEMU's AArch32 register set with AArch64 + * register set. This is necessary when switching between AArch32 and AArch64 + * execution state. + */ +void aarch64_sync_64_to_32(CPUARMState *env) +{ + int i; + uint32_t mode = env->uncached_cpsr & CPSR_M; + + /* We can blanket copy X[0:7] to R[0:7] */ + for (i = 0; i < 8; i++) { + env->regs[i] = env->xregs[i]; + } + + /* Unless we are in FIQ mode, r8-r12 come from the user registers x8-x12. + * Otherwise, we copy x8-x12 into the banked user regs. + */ + if (mode == ARM_CPU_MODE_FIQ) { + for (i = 8; i < 13; i++) { + env->usr_regs[i - 8] = env->xregs[i]; + } + } else { + for (i = 8; i < 13; i++) { + env->regs[i] = env->xregs[i]; + } + } + + /* Registers r13 & r14 depend on the current mode. + * If we are in a given mode, we copy the corresponding x registers to r13 + * and r14. Otherwise, we copy the x register to the banked r13 and r14 + * for the mode. + */ + if (mode == ARM_CPU_MODE_USR || mode == ARM_CPU_MODE_SYS) { + env->regs[13] = env->xregs[13]; + env->regs[14] = env->xregs[14]; + } else { + env->banked_r13[bank_number(ARM_CPU_MODE_USR)] = env->xregs[13]; + + /* HYP is an exception in that it does not have its own banked r14 but + * shares the USR r14 + */ + if (mode == ARM_CPU_MODE_HYP) { + env->regs[14] = env->xregs[14]; + } else { + env->banked_r14[bank_number(ARM_CPU_MODE_USR)] = env->xregs[14]; + } + } + + if (mode == ARM_CPU_MODE_HYP) { + env->regs[13] = env->xregs[15]; + } else { + env->banked_r13[bank_number(ARM_CPU_MODE_HYP)] = env->xregs[15]; + } + + if (mode == ARM_CPU_MODE_IRQ) { + env->regs[13] = env->xregs[16]; + env->regs[14] = env->xregs[17]; + } else { + env->banked_r13[bank_number(ARM_CPU_MODE_IRQ)] = env->xregs[16]; + env->banked_r14[bank_number(ARM_CPU_MODE_IRQ)] = env->xregs[17]; + } + + if (mode == ARM_CPU_MODE_SVC) { + env->regs[13] = env->xregs[18]; + env->regs[14] = env->xregs[19]; + } else { + env->banked_r13[bank_number(ARM_CPU_MODE_SVC)] = env->xregs[18]; + env->banked_r14[bank_number(ARM_CPU_MODE_SVC)] = env->xregs[19]; + } + + if (mode == ARM_CPU_MODE_ABT) { + env->regs[13] = env->xregs[20]; + env->regs[14] = env->xregs[21]; + } else { + env->banked_r13[bank_number(ARM_CPU_MODE_ABT)] = env->xregs[20]; + env->banked_r14[bank_number(ARM_CPU_MODE_ABT)] = env->xregs[21]; + } + + if (mode == ARM_CPU_MODE_UND) { + env->regs[13] = env->xregs[22]; + env->regs[14] = env->xregs[23]; + } else { + env->banked_r13[bank_number(ARM_CPU_MODE_UND)] = env->xregs[22]; + env->banked_r14[bank_number(ARM_CPU_MODE_UND)] = env->xregs[23]; + } + + /* Registers x24-x30 are mapped to r8-r14 in FIQ mode. If we are in FIQ + * mode, then we can copy to r8-r14. Otherwise, we copy to the + * FIQ bank for r8-r14. + */ + if (mode == ARM_CPU_MODE_FIQ) { + for (i = 24; i < 31; i++) { + env->regs[i - 16] = env->xregs[i]; /* X[24:30] -> R[8:14] */ + } + } else { + for (i = 24; i < 29; i++) { + env->fiq_regs[i - 24] = env->xregs[i]; + } + env->banked_r13[bank_number(ARM_CPU_MODE_FIQ)] = env->xregs[29]; + env->banked_r14[bank_number(ARM_CPU_MODE_FIQ)] = env->xregs[30]; + } + + env->regs[15] = env->pc; +} + /* Handle a CPU exception. */ void arm_cpu_do_interrupt(CPUState *cs) { @@ -4055,22 +4744,20 @@ void arm_cpu_do_interrupt(CPUState *cs) env->exception.fsr = 2; /* Fall through to prefetch abort. */ case EXCP_PREFETCH_ABORT: - env->cp15.ifsr_el2 = env->exception.fsr; - env->cp15.far_el[1] = deposit64(env->cp15.far_el[1], 32, 32, - env->exception.vaddress); + A32_BANKED_CURRENT_REG_SET(env, ifsr, env->exception.fsr); + A32_BANKED_CURRENT_REG_SET(env, ifar, env->exception.vaddress); qemu_log_mask(CPU_LOG_INT, "...with IFSR 0x%x IFAR 0x%x\n", - env->cp15.ifsr_el2, (uint32_t)env->exception.vaddress); + env->exception.fsr, (uint32_t)env->exception.vaddress); new_mode = ARM_CPU_MODE_ABT; addr = 0x0c; mask = CPSR_A | CPSR_I; offset = 4; break; case EXCP_DATA_ABORT: - env->cp15.esr_el[1] = env->exception.fsr; - env->cp15.far_el[1] = deposit64(env->cp15.far_el[1], 0, 32, - env->exception.vaddress); + A32_BANKED_CURRENT_REG_SET(env, dfsr, env->exception.fsr); + A32_BANKED_CURRENT_REG_SET(env, dfar, env->exception.vaddress); qemu_log_mask(CPU_LOG_INT, "...with DFSR 0x%x DFAR 0x%x\n", - (uint32_t)env->cp15.esr_el[1], + env->exception.fsr, (uint32_t)env->exception.vaddress); new_mode = ARM_CPU_MODE_ABT; addr = 0x10; @@ -4083,12 +4770,21 @@ void arm_cpu_do_interrupt(CPUState *cs) /* Disable IRQ and imprecise data aborts. */ mask = CPSR_A | CPSR_I; offset = 4; + if (env->cp15.scr_el3 & SCR_IRQ) { + /* IRQ routed to monitor mode */ + new_mode = ARM_CPU_MODE_MON; + mask |= CPSR_F; + } break; case EXCP_FIQ: new_mode = ARM_CPU_MODE_FIQ; addr = 0x1c; /* Disable FIQ, IRQ and imprecise data aborts. */ mask = CPSR_A | CPSR_I | CPSR_F; + if (env->cp15.scr_el3 & SCR_FIQ) { + /* FIQ routed to monitor mode */ + new_mode = ARM_CPU_MODE_MON; + } offset = 4; break; case EXCP_SMC: @@ -4101,19 +4797,19 @@ void arm_cpu_do_interrupt(CPUState *cs) cpu_abort(cs, "Unhandled exception 0x%x\n", cs->exception_index); return; /* Never happens. Keep compiler happy. */ } - /* High vectors. */ - if (env->cp15.c1_sys & SCTLR_V) { - /* when enabled, base address cannot be remapped. */ + + if (new_mode == ARM_CPU_MODE_MON) { + addr += env->cp15.mvbar; + } else if (A32_BANKED_CURRENT_REG_GET(env, sctlr) & SCTLR_V) { + /* High vectors. When enabled, base address cannot be remapped. */ addr += 0xffff0000; } else { /* ARM v7 architectures provide a vector base address register to remap * the interrupt vector table. - * This register is only followed in non-monitor mode, and has a secure - * and un-secure copy. Since the cpu is always in a un-secure operation - * and is never in monitor mode this feature is always active. + * This register is only followed in non-monitor mode, and is banked. * Note: only bits 31:5 are valid. */ - addr += env->cp15.vbar_el[1]; + addr += A32_BANKED_CURRENT_REG_GET(env, vbar); } if ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_MON) { @@ -4134,91 +4830,280 @@ void arm_cpu_do_interrupt(CPUState *cs) /* this is a lie, as the was no c1_sys on V4T/V5, but who cares * and we should just guard the thumb mode on V4 */ if (arm_feature(env, ARM_FEATURE_V4T)) { - env->thumb = (env->cp15.c1_sys & SCTLR_TE) != 0; + env->thumb = (A32_BANKED_CURRENT_REG_GET(env, sctlr) & SCTLR_TE) != 0; } env->regs[14] = env->regs[15] + offset; env->regs[15] = addr; cs->interrupt_request |= CPU_INTERRUPT_EXITTB; } -/* Check section/page access permissions. - Returns the page protection flags, or zero if the access is not - permitted. */ -static inline int check_ap(CPUARMState *env, int ap, int domain_prot, - int access_type, int is_user) -{ - int prot_ro; - - if (domain_prot == 3) { - return PAGE_READ | PAGE_WRITE; - } - - if (access_type == 1) - prot_ro = 0; - else - prot_ro = PAGE_READ; - - switch (ap) { - case 0: - if (arm_feature(env, ARM_FEATURE_V7)) { - return 0; - } - if (access_type == 1) - return 0; - switch (env->cp15.c1_sys & (SCTLR_S | SCTLR_R)) { - case SCTLR_S: - return is_user ? 0 : PAGE_READ; - case SCTLR_R: - return PAGE_READ; - default: - return 0; - } - case 1: - return is_user ? 0 : PAGE_READ | PAGE_WRITE; - case 2: - if (is_user) - return prot_ro; - else - return PAGE_READ | PAGE_WRITE; - case 3: - return PAGE_READ | PAGE_WRITE; - case 4: /* Reserved. */ - return 0; - case 5: - return is_user ? 0 : prot_ro; - case 6: - return prot_ro; - case 7: - if (!arm_feature (env, ARM_FEATURE_V6K)) - return 0; - return prot_ro; - default: - abort(); - } -} - -static bool get_level1_table_address(CPUARMState *env, uint32_t *table, - uint32_t address) -{ - if (address & env->cp15.c2_mask) { - if ((env->cp15.c2_control & TTBCR_PD1)) { + +/* Return the exception level which controls this address translation regime */ +static inline uint32_t regime_el(CPUARMState *env, ARMMMUIdx mmu_idx) +{ + switch (mmu_idx) { + case ARMMMUIdx_S2NS: + case ARMMMUIdx_S1E2: + return 2; + case ARMMMUIdx_S1E3: + return 3; + case ARMMMUIdx_S1SE0: + return arm_el_is_aa64(env, 3) ? 1 : 3; + case ARMMMUIdx_S1SE1: + case ARMMMUIdx_S1NSE0: + case ARMMMUIdx_S1NSE1: + return 1; + default: + g_assert_not_reached(); + } +} + +/* Return the SCTLR value which controls this address translation regime */ +static inline uint32_t regime_sctlr(CPUARMState *env, ARMMMUIdx mmu_idx) +{ + return env->cp15.sctlr_el[regime_el(env, mmu_idx)]; +} + +/* Return true if the specified stage of address translation is disabled */ +static inline bool regime_translation_disabled(CPUARMState *env, + ARMMMUIdx mmu_idx) +{ + if (mmu_idx == ARMMMUIdx_S2NS) { + return (env->cp15.hcr_el2 & HCR_VM) == 0; + } + return (regime_sctlr(env, mmu_idx) & SCTLR_M) == 0; +} + +/* Return the TCR controlling this translation regime */ +static inline TCR *regime_tcr(CPUARMState *env, ARMMMUIdx mmu_idx) +{ + if (mmu_idx == ARMMMUIdx_S2NS) { + /* TODO: return VTCR_EL2 */ + g_assert_not_reached(); + } + return &env->cp15.tcr_el[regime_el(env, mmu_idx)]; +} + +/* Return true if the translation regime is using LPAE format page tables */ +static inline bool regime_using_lpae_format(CPUARMState *env, + ARMMMUIdx mmu_idx) +{ + int el = regime_el(env, mmu_idx); + if (el == 2 || arm_el_is_aa64(env, el)) { + return true; + } + if (arm_feature(env, ARM_FEATURE_LPAE) + && (regime_tcr(env, mmu_idx)->raw_tcr & TTBCR_EAE)) { + return true; + } + return false; +} + +static inline bool regime_is_user(CPUARMState *env, ARMMMUIdx mmu_idx) +{ + switch (mmu_idx) { + case ARMMMUIdx_S1SE0: + case ARMMMUIdx_S1NSE0: + return true; + default: + return false; + case ARMMMUIdx_S12NSE0: + case ARMMMUIdx_S12NSE1: + g_assert_not_reached(); + } +} + +/* Translate section/page access permissions to page + * R/W protection flags + * + * @env: CPUARMState + * @mmu_idx: MMU index indicating required translation regime + * @ap: The 3-bit access permissions (AP[2:0]) + * @domain_prot: The 2-bit domain access permissions + */ +static inline int ap_to_rw_prot(CPUARMState *env, ARMMMUIdx mmu_idx, + int ap, int domain_prot) +{ + bool is_user = regime_is_user(env, mmu_idx); + + if (domain_prot == 3) { + return PAGE_READ | PAGE_WRITE; + } + + switch (ap) { + case 0: + if (arm_feature(env, ARM_FEATURE_V7)) { + return 0; + } + switch (regime_sctlr(env, mmu_idx) & (SCTLR_S | SCTLR_R)) { + case SCTLR_S: + return is_user ? 0 : PAGE_READ; + case SCTLR_R: + return PAGE_READ; + default: + return 0; + } + case 1: + return is_user ? 0 : PAGE_READ | PAGE_WRITE; + case 2: + if (is_user) { + return PAGE_READ; + } else { + return PAGE_READ | PAGE_WRITE; + } + case 3: + return PAGE_READ | PAGE_WRITE; + case 4: /* Reserved. */ + return 0; + case 5: + return is_user ? 0 : PAGE_READ; + case 6: + return PAGE_READ; + case 7: + if (!arm_feature(env, ARM_FEATURE_V6K)) { + return 0; + } + return PAGE_READ; + default: + g_assert_not_reached(); + } +} + +/* Translate section/page access permissions to page + * R/W protection flags. + * + * @ap: The 2-bit simple AP (AP[2:1]) + * @is_user: TRUE if accessing from PL0 + */ +static inline int simple_ap_to_rw_prot_is_user(int ap, bool is_user) +{ + switch (ap) { + case 0: + return is_user ? 0 : PAGE_READ | PAGE_WRITE; + case 1: + return PAGE_READ | PAGE_WRITE; + case 2: + return is_user ? 0 : PAGE_READ; + case 3: + return PAGE_READ; + default: + g_assert_not_reached(); + } +} + +static inline int +simple_ap_to_rw_prot(CPUARMState *env, ARMMMUIdx mmu_idx, int ap) +{ + return simple_ap_to_rw_prot_is_user(ap, regime_is_user(env, mmu_idx)); +} + +/* Translate section/page access permissions to protection flags + * + * @env: CPUARMState + * @mmu_idx: MMU index indicating required translation regime + * @is_aa64: TRUE if AArch64 + * @ap: The 2-bit simple AP (AP[2:1]) + * @ns: NS (non-secure) bit + * @xn: XN (execute-never) bit + * @pxn: PXN (privileged execute-never) bit + */ +static int get_S1prot(CPUARMState *env, ARMMMUIdx mmu_idx, bool is_aa64, + int ap, int ns, int xn, int pxn) +{ + bool is_user = regime_is_user(env, mmu_idx); + int prot_rw, user_rw; + bool have_wxn; + int wxn = 0; + + assert(mmu_idx != ARMMMUIdx_S2NS); + + user_rw = simple_ap_to_rw_prot_is_user(ap, true); + if (is_user) { + prot_rw = user_rw; + } else { + prot_rw = simple_ap_to_rw_prot_is_user(ap, false); + } + + if (ns && arm_is_secure(env) && (env->cp15.scr_el3 & SCR_SIF)) { + return prot_rw; + } + + /* TODO have_wxn should be replaced with + * ARM_FEATURE_V8 || (ARM_FEATURE_V7 && ARM_FEATURE_EL2) + * when ARM_FEATURE_EL2 starts getting set. For now we assume all LPAE + * compatible processors have EL2, which is required for [U]WXN. + */ + have_wxn = arm_feature(env, ARM_FEATURE_LPAE); + + if (have_wxn) { + wxn = regime_sctlr(env, mmu_idx) & SCTLR_WXN; + } + + if (is_aa64) { + switch (regime_el(env, mmu_idx)) { + case 1: + if (!is_user) { + xn = pxn || (user_rw & PAGE_WRITE); + } + break; + case 2: + case 3: + break; + } + } else if (arm_feature(env, ARM_FEATURE_V7)) { + switch (regime_el(env, mmu_idx)) { + case 1: + case 3: + if (is_user) { + xn = xn || !(user_rw & PAGE_READ); + } else { + int uwxn = 0; + if (have_wxn) { + uwxn = regime_sctlr(env, mmu_idx) & SCTLR_UWXN; + } + xn = xn || !(prot_rw & PAGE_READ) || pxn || + (uwxn && (user_rw & PAGE_WRITE)); + } + break; + case 2: + break; + } + } else { + xn = wxn = 0; + } + + if (xn || (wxn && (prot_rw & PAGE_WRITE))) { + return prot_rw; + } + return prot_rw | PAGE_EXEC; +} + +static bool get_level1_table_address(CPUARMState *env, ARMMMUIdx mmu_idx, + uint32_t *table, uint32_t address) +{ + /* Note that we can only get here for an AArch32 PL0/PL1 lookup */ + int el = regime_el(env, mmu_idx); + TCR *tcr = regime_tcr(env, mmu_idx); + + if (address & tcr->mask) { + if (tcr->raw_tcr & TTBCR_PD1) { /* Translation table walk disabled for TTBR1 */ return false; } - *table = env->cp15.ttbr1_el1 & 0xffffc000; + *table = env->cp15.ttbr1_el[el] & 0xffffc000; } else { - if ((env->cp15.c2_control & TTBCR_PD0)) { + if (tcr->raw_tcr & TTBCR_PD0) { /* Translation table walk disabled for TTBR0 */ return false; } - *table = env->cp15.ttbr0_el1 & env->cp15.c2_base_mask; + *table = env->cp15.ttbr0_el[el] & tcr->base_mask; } *table |= (address >> 18) & 0x3ffc; return true; } static int get_phys_addr_v5(CPUARMState *env, uint32_t address, int access_type, - int is_user, hwaddr *phys_ptr, + ARMMMUIdx mmu_idx, hwaddr *phys_ptr, int *prot, target_ulong *page_size) { CPUState *cs = CPU(arm_env_get_cpu(env)); @@ -4230,10 +5115,11 @@ static int get_phys_addr_v5(CPUARMState *env, uint32_t address, int access_type, int domain = 0; int domain_prot; hwaddr phys_addr; + uint32_t dacr; /* Pagetable walk. */ /* Lookup l1 descriptor. */ - if (!get_level1_table_address(env, &table, address)) { + if (!get_level1_table_address(env, mmu_idx, &table, address)) { /* Section translation fault if page walk is disabled by PD0 or PD1 */ code = 5; goto do_fault; @@ -4241,7 +5127,12 @@ static int get_phys_addr_v5(CPUARMState *env, uint32_t address, int access_type, desc = ldl_phys(cs->as, table); type = (desc & 3); domain = (desc >> 5) & 0x0f; - domain_prot = (env->cp15.c3 >> (domain * 2)) & 3; + if (regime_el(env, mmu_idx) == 1) { + dacr = env->cp15.dacr_ns; + } else { + dacr = env->cp15.dacr_s; + } + domain_prot = (dacr >> (domain * 2)) & 3; if (type == 0) { /* Section translation fault. */ code = 5; @@ -4262,13 +5153,13 @@ static int get_phys_addr_v5(CPUARMState *env, uint32_t address, int access_type, *page_size = 1024 * 1024; } else { /* Lookup l2 entry. */ - if (type == 1) { - /* Coarse pagetable. */ - table = (desc & 0xfffffc00) | ((address >> 10) & 0x3fc); - } else { - /* Fine pagetable. */ - table = (desc & 0xfffff000) | ((address >> 8) & 0xffc); - } + if (type == 1) { + /* Coarse pagetable. */ + table = (desc & 0xfffffc00) | ((address >> 10) & 0x3fc); + } else { + /* Fine pagetable. */ + table = (desc & 0xfffff000) | ((address >> 8) & 0xffc); + } desc = ldl_phys(cs->as, table); switch (desc & 3) { case 0: /* Page translation fault. */ @@ -4285,17 +5176,17 @@ static int get_phys_addr_v5(CPUARMState *env, uint32_t address, int access_type, *page_size = 0x1000; break; case 3: /* 1k page. */ - if (type == 1) { - if (arm_feature(env, ARM_FEATURE_XSCALE)) { - phys_addr = (desc & 0xfffff000) | (address & 0xfff); - } else { - /* Page translation fault. */ - code = 7; - goto do_fault; - } - } else { - phys_addr = (desc & 0xfffffc00) | (address & 0x3ff); - } + if (type == 1) { + if (arm_feature(env, ARM_FEATURE_XSCALE)) { + phys_addr = (desc & 0xfffff000) | (address & 0xfff); + } else { + /* Page translation fault. */ + code = 7; + goto do_fault; + } + } else { + phys_addr = (desc & 0xfffffc00) | (address & 0x3ff); + } ap = (desc >> 4) & 3; *page_size = 0x400; break; @@ -4305,12 +5196,12 @@ static int get_phys_addr_v5(CPUARMState *env, uint32_t address, int access_type, } code = 15; } - *prot = check_ap(env, ap, domain_prot, access_type, is_user); - if (!*prot) { + *prot = ap_to_rw_prot(env, mmu_idx, ap, domain_prot); + *prot |= *prot ? PAGE_EXEC : 0; + if (!(*prot & (1 << access_type))) { /* Access permission fault. */ goto do_fault; } - *prot |= PAGE_EXEC; *phys_ptr = phys_addr; return 0; do_fault: @@ -4318,7 +5209,7 @@ do_fault: } static int get_phys_addr_v6(CPUARMState *env, uint32_t address, int access_type, - int is_user, hwaddr *phys_ptr, + ARMMMUIdx mmu_idx, hwaddr *phys_ptr, int *prot, target_ulong *page_size) { CPUState *cs = CPU(arm_env_get_cpu(env)); @@ -4332,10 +5223,11 @@ static int get_phys_addr_v6(CPUARMState *env, uint32_t address, int access_type, int domain = 0; int domain_prot; hwaddr phys_addr; + uint32_t dacr; /* Pagetable walk. */ /* Lookup l1 descriptor. */ - if (!get_level1_table_address(env, &table, address)) { + if (!get_level1_table_address(env, mmu_idx, &table, address)) { /* Section translation fault if page walk is disabled by PD0 or PD1 */ code = 5; goto do_fault; @@ -4353,7 +5245,12 @@ static int get_phys_addr_v6(CPUARMState *env, uint32_t address, int access_type, /* Page or Section. */ domain = (desc >> 5) & 0x0f; } - domain_prot = (env->cp15.c3 >> (domain * 2)) & 3; + if (regime_el(env, mmu_idx) == 1) { + dacr = env->cp15.dacr_ns; + } else { + dacr = env->cp15.dacr_s; + } + domain_prot = (dacr >> (domain * 2)) & 3; if (domain_prot == 0 || domain_prot == 2) { if (type != 1) { code = 9; /* Section domain fault. */ @@ -4407,26 +5304,31 @@ static int get_phys_addr_v6(CPUARMState *env, uint32_t address, int access_type, if (domain_prot == 3) { *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; } else { - if (pxn && !is_user) { + if (pxn && !regime_is_user(env, mmu_idx)) { xn = 1; } if (xn && access_type == 2) goto do_fault; - /* The simplified model uses AP[0] as an access control bit. */ - if ((env->cp15.c1_sys & SCTLR_AFE) && (ap & 1) == 0) { - /* Access flag fault. */ - code = (code == 15) ? 6 : 3; - goto do_fault; + if (arm_feature(env, ARM_FEATURE_V6K) && + (regime_sctlr(env, mmu_idx) & SCTLR_AFE)) { + /* The simplified model uses AP[0] as an access control bit. */ + if ((ap & 1) == 0) { + /* Access flag fault. */ + code = (code == 15) ? 6 : 3; + goto do_fault; + } + *prot = simple_ap_to_rw_prot(env, mmu_idx, ap >> 1); + } else { + *prot = ap_to_rw_prot(env, mmu_idx, ap, domain_prot); + } + if (*prot && !xn) { + *prot |= PAGE_EXEC; } - *prot = check_ap(env, ap, domain_prot, access_type, is_user); - if (!*prot) { + if (!(*prot & (1 << access_type))) { /* Access permission fault. */ goto do_fault; } - if (!xn) { - *prot |= PAGE_EXEC; - } } *phys_ptr = phys_addr; return 0; @@ -4444,7 +5346,7 @@ typedef enum { } MMUFaultType; static int get_phys_addr_lpae(CPUARMState *env, target_ulong address, - int access_type, int is_user, + int access_type, ARMMMUIdx mmu_idx, hwaddr *phys_ptr, int *prot, target_ulong *page_size_ptr) { @@ -4464,13 +5366,22 @@ static int get_phys_addr_lpae(CPUARMState *env, target_ulong address, int32_t granule_sz = 9; int32_t va_size = 32; int32_t tbi = 0; - - if (arm_el_is_aa64(env, 1)) { + TCR *tcr = regime_tcr(env, mmu_idx); + int ap, ns, xn, pxn; + + /* TODO: + * This code assumes we're either a 64-bit EL1 or a 32-bit PL1; + * it doesn't handle the different format TCR for TCR_EL2, TCR_EL3, + * and VTCR_EL2, or the fact that those regimes don't have a split + * TTBR0/TTBR1. Attribute and permission bit handling should also + * be checked when adding support for those page table walks. + */ + if (arm_el_is_aa64(env, regime_el(env, mmu_idx))) { va_size = 64; if (extract64(address, 55, 1)) - tbi = extract64(env->cp15.c2_control, 38, 1); + tbi = extract64(tcr->raw_tcr, 38, 1); else - tbi = extract64(env->cp15.c2_control, 37, 1); + tbi = extract64(tcr->raw_tcr, 37, 1); tbi *= 8; } @@ -4479,13 +5390,13 @@ static int get_phys_addr_lpae(CPUARMState *env, target_ulong address, * This is a Non-secure PL0/1 stage 1 translation, so controlled by * TTBCR/TTBR0/TTBR1 in accordance with ARM ARM DDI0406C table B-32: */ - uint32_t t0sz = extract32(env->cp15.c2_control, 0, 6); - if (arm_el_is_aa64(env, 1)) { + uint32_t t0sz = extract32(tcr->raw_tcr, 0, 6); + if (va_size == 64) { t0sz = MIN(t0sz, 39); t0sz = MAX(t0sz, 16); } - uint32_t t1sz = extract32(env->cp15.c2_control, 16, 6); - if (arm_el_is_aa64(env, 1)) { + uint32_t t1sz = extract32(tcr->raw_tcr, 16, 6); + if (va_size == 64) { t1sz = MIN(t1sz, 39); t1sz = MAX(t1sz, 16); } @@ -4515,11 +5426,11 @@ static int get_phys_addr_lpae(CPUARMState *env, target_ulong address, * we will always flush the TLB any time the ASID is changed). */ if (ttbr_select == 0) { - ttbr = env->cp15.ttbr0_el1; - epd = extract32(env->cp15.c2_control, 7, 1); + ttbr = A32_BANKED_CURRENT_REG_GET(env, ttbr0); + epd = extract32(tcr->raw_tcr, 7, 1); tsz = t0sz; - tg = extract32(env->cp15.c2_control, 14, 2); + tg = extract32(tcr->raw_tcr, 14, 2); if (tg == 1) { /* 64KB pages */ granule_sz = 13; } @@ -4527,11 +5438,11 @@ static int get_phys_addr_lpae(CPUARMState *env, target_ulong address, granule_sz = 11; } } else { - ttbr = env->cp15.ttbr1_el1; - epd = extract32(env->cp15.c2_control, 23, 1); + ttbr = A32_BANKED_CURRENT_REG_GET(env, ttbr1); + epd = extract32(tcr->raw_tcr, 23, 1); tsz = t1sz; - tg = extract32(env->cp15.c2_control, 30, 2); + tg = extract32(tcr->raw_tcr, 30, 2); if (tg == 3) { /* 64KB pages */ granule_sz = 13; } @@ -4540,6 +5451,10 @@ static int get_phys_addr_lpae(CPUARMState *env, target_ulong address, } } + /* Here we should have set up all the parameters for the translation: + * va_size, ttbr, epd, tsz, granule_sz, tbi + */ + if (epd) { /* Translation table walk disabled => Translation fault on TLB miss */ goto do_fault; @@ -4613,7 +5528,7 @@ static int get_phys_addr_lpae(CPUARMState *env, target_ulong address, if (extract32(tableattrs, 2, 1)) { attrs &= ~(1 << 4); } - /* Since we're always in the Non-secure state, NSTable is ignored. */ + attrs |= extract32(tableattrs, 4, 1) << 3; /* NS */ break; } /* Here descaddr is the final physical address, and attributes @@ -4624,30 +5539,18 @@ static int get_phys_addr_lpae(CPUARMState *env, target_ulong address, /* Access flag */ goto do_fault; } + + ap = extract32(attrs, 4, 2); + ns = extract32(attrs, 3, 1); + xn = extract32(attrs, 12, 1); + pxn = extract32(attrs, 11, 1); + + *prot = get_S1prot(env, mmu_idx, va_size == 64, ap, ns, xn, pxn); + fault_type = permission_fault; - if (is_user && !(attrs & (1 << 4))) { - /* Unprivileged access not enabled */ + if (!(*prot & (1 << access_type))) { goto do_fault; } - *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; - if ((arm_feature(env, ARM_FEATURE_V8) && is_user && (attrs & (1 << 12))) || - (!arm_feature(env, ARM_FEATURE_V8) && (attrs & (1 << 12))) || - (!is_user && (attrs & (1 << 11)))) { - /* XN/UXN or PXN. Since we only implement EL0/EL1 we unconditionally - * treat XN/UXN as UXN for v8. - */ - if (access_type == 2) { - goto do_fault; - } - *prot &= ~PAGE_EXEC; - } - if (attrs & (1 << 5)) { - /* Write access forbidden */ - if (access_type == 1) { - goto do_fault; - } - *prot &= ~PAGE_WRITE; - } *phys_ptr = descaddr; *page_size_ptr = page_size; @@ -4659,27 +5562,31 @@ do_fault: } static int get_phys_addr_mpu(CPUARMState *env, uint32_t address, - int access_type, int is_user, + int access_type, ARMMMUIdx mmu_idx, hwaddr *phys_ptr, int *prot) { int n; uint32_t mask; uint32_t base; + bool is_user = regime_is_user(env, mmu_idx); *phys_ptr = address; for (n = 7; n >= 0; n--) { - base = env->cp15.c6_region[n]; - if ((base & 1) == 0) - continue; - mask = 1 << ((base >> 1) & 0x1f); - /* Keep this shift separate from the above to avoid an - (undefined) << 32. */ - mask = (mask << 1) - 1; - if (((base ^ address) & ~mask) == 0) - break; - } - if (n < 0) - return 2; + base = env->cp15.c6_region[n]; + if ((base & 1) == 0) { + continue; + } + mask = 1 << ((base >> 1) & 0x1f); + /* Keep this shift separate from the above to avoid an + (undefined) << 32. */ + mask = (mask << 1) - 1; + if (((base ^ address) & ~mask) == 0) { + break; + } + } + if (n < 0) { + return 2; + } if (access_type == 2) { mask = env->cp15.pmsav5_insn_ap; @@ -4689,31 +5596,34 @@ static int get_phys_addr_mpu(CPUARMState *env, uint32_t address, mask = (mask >> (n * 4)) & 0xf; switch (mask) { case 0: - return 1; + return 1; case 1: - if (is_user) - return 1; - *prot = PAGE_READ | PAGE_WRITE; - break; + if (is_user) { + return 1; + } + *prot = PAGE_READ | PAGE_WRITE; + break; case 2: - *prot = PAGE_READ; - if (!is_user) - *prot |= PAGE_WRITE; - break; + *prot = PAGE_READ; + if (!is_user) { + *prot |= PAGE_WRITE; + } + break; case 3: - *prot = PAGE_READ | PAGE_WRITE; - break; + *prot = PAGE_READ | PAGE_WRITE; + break; case 5: - if (is_user) - return 1; - *prot = PAGE_READ; - break; + if (is_user) { + return 1; + } + *prot = PAGE_READ; + break; case 6: - *prot = PAGE_READ; - break; + *prot = PAGE_READ; + break; default: - /* Bad permission. */ - return 1; + /* Bad permission. */ + return 1; } *prot |= PAGE_EXEC; return 0; @@ -4737,38 +5647,60 @@ static int get_phys_addr_mpu(CPUARMState *env, uint32_t address, * @env: CPUARMState * @address: virtual address to get physical address for * @access_type: 0 for read, 1 for write, 2 for execute - * @is_user: 0 for privileged access, 1 for user + * @mmu_idx: MMU index indicating required translation regime * @phys_ptr: set to the physical address corresponding to the virtual address * @prot: set to the permissions for the page containing phys_ptr * @page_size: set to the size of the page containing phys_ptr */ static inline int get_phys_addr(CPUARMState *env, target_ulong address, - int access_type, int is_user, + int access_type, ARMMMUIdx mmu_idx, hwaddr *phys_ptr, int *prot, target_ulong *page_size) { - /* Fast Context Switch Extension. */ - if (address < 0x02000000) - address += env->cp15.c13_fcse; + if (mmu_idx == ARMMMUIdx_S12NSE0 || mmu_idx == ARMMMUIdx_S12NSE1) { + /* TODO: when we support EL2 we should here call ourselves recursively + * to do the stage 1 and then stage 2 translations. The ldl_phys + * calls for stage 1 will also need changing. + * For non-EL2 CPUs a stage1+stage2 translation is just stage 1. + */ + assert(!arm_feature(env, ARM_FEATURE_EL2)); + mmu_idx += ARMMMUIdx_S1NSE0; + } + + /* Fast Context Switch Extension. This doesn't exist at all in v8. + * In v7 and earlier it affects all stage 1 translations. + */ + if (address < 0x02000000 && mmu_idx != ARMMMUIdx_S2NS + && !arm_feature(env, ARM_FEATURE_V8)) { + if (regime_el(env, mmu_idx) == 3) { + address += env->cp15.fcseidr_s; + } else { + address += env->cp15.fcseidr_ns; + } + } - if ((env->cp15.c1_sys & SCTLR_M) == 0) { + if (regime_translation_disabled(env, mmu_idx)) { /* MMU/MPU disabled. */ *phys_ptr = address; *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; *page_size = TARGET_PAGE_SIZE; return 0; - } else if (arm_feature(env, ARM_FEATURE_MPU)) { + } + + if (arm_feature(env, ARM_FEATURE_MPU)) { *page_size = TARGET_PAGE_SIZE; - return get_phys_addr_mpu(env, address, access_type, is_user, phys_ptr, - prot); - } else if (extended_addresses_enabled(env)) { - return get_phys_addr_lpae(env, address, access_type, is_user, phys_ptr, + return get_phys_addr_mpu(env, address, access_type, mmu_idx, phys_ptr, + prot); + } + + if (regime_using_lpae_format(env, mmu_idx)) { + return get_phys_addr_lpae(env, address, access_type, mmu_idx, phys_ptr, prot, page_size); - } else if (env->cp15.c1_sys & SCTLR_XP) { - return get_phys_addr_v6(env, address, access_type, is_user, phys_ptr, + } else if (regime_sctlr(env, mmu_idx) & SCTLR_XP) { + return get_phys_addr_v6(env, address, access_type, mmu_idx, phys_ptr, prot, page_size); } else { - return get_phys_addr_v5(env, address, access_type, is_user, phys_ptr, + return get_phys_addr_v5(env, address, access_type, mmu_idx, phys_ptr, prot, page_size); } } @@ -4781,12 +5713,11 @@ int arm_cpu_handle_mmu_fault(CPUState *cs, vaddr address, hwaddr phys_addr; target_ulong page_size; int prot; - int ret, is_user; + int ret; uint32_t syn; bool same_el = (arm_current_el(env) != 0); - is_user = mmu_idx == MMU_USER_IDX; - ret = get_phys_addr(env, address, access_type, is_user, &phys_addr, &prot, + ret = get_phys_addr(env, address, access_type, mmu_idx, &phys_addr, &prot, &page_size); if (ret == 0) { /* Map a single [sub]page. */ @@ -4822,12 +5753,14 @@ int arm_cpu_handle_mmu_fault(CPUState *cs, vaddr address, hwaddr arm_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; hwaddr phys_addr; target_ulong page_size; int prot; int ret; - ret = get_phys_addr(&cpu->env, addr, 0, 0, &phys_addr, &prot, &page_size); + ret = get_phys_addr(env, addr, 0, cpu_mmu_index(env), &phys_addr, + &prot, &page_size); if (ret != 0) { return -1; @@ -5908,7 +6841,7 @@ float64 HELPER(recpe_f64)(float64 input, void *fpstp) } else { return float64_set_sign(float64_maxnorm, float64_is_neg(f64)); } - } else if (f64_exp >= 1023 && fpst->flush_to_zero) { + } else if (f64_exp >= 2045 && fpst->flush_to_zero) { float_raise(float_flag_underflow, fpst); return float64_set_sign(float64_zero, float64_is_neg(f64)); } diff --git a/target-arm/internals.h b/target-arm/internals.h index 2dff4ff..2cc3017 100644 --- a/target-arm/internals.h +++ b/target-arm/internals.h @@ -82,11 +82,14 @@ static inline void arm_log_exception(int idx) /* * For AArch64, map a given EL to an index in the banked_spsr array. + * Note that this mapping and the AArch32 mapping defined in bank_number() + * must agree such that the AArch64<->AArch32 SPSRs have the architecturally + * mandated mapping between each other. */ static inline unsigned int aarch64_banked_spsr_index(unsigned int el) { static const unsigned int map[4] = { - [1] = 0, /* EL1. */ + [1] = 1, /* EL1. */ [2] = 6, /* EL2. */ [3] = 7, /* EL3. */ }; @@ -153,9 +156,9 @@ static inline void update_spsel(CPUARMState *env, uint32_t imm) */ static inline bool extended_addresses_enabled(CPUARMState *env) { - return arm_el_is_aa64(env, 1) - || ((arm_feature(env, ARM_FEATURE_LPAE) - && (env->cp15.c2_control & TTBCR_EAE))); + TCR *tcr = &env->cp15.tcr_el[arm_is_secure(env) ? 3 : 1]; + return arm_el_is_aa64(env, 1) || + (arm_feature(env, ARM_FEATURE_LPAE) && (tcr->raw_tcr & TTBCR_EAE)); } /* Valid Syndrome Register EC field values */ diff --git a/target-arm/kvm.c b/target-arm/kvm.c index 319784d..fdd9ba3 100644 --- a/target-arm/kvm.c +++ b/target-arm/kvm.c @@ -21,12 +21,15 @@ #include "sysemu/kvm.h" #include "kvm_arm.h" #include "cpu.h" +#include "internals.h" #include "hw/arm/arm.h" const KVMCapabilityInfo kvm_arch_required_capabilities[] = { KVM_CAP_LAST_INFO }; +static bool cap_has_mp_state; + int kvm_arm_vcpu_init(CPUState *cs) { ARMCPU *cpu = ARM_CPU(cs); @@ -149,13 +152,15 @@ static const TypeInfo host_arm_cpu_type_info = { .class_size = sizeof(ARMHostCPUClass), }; -int kvm_arch_init(KVMState *s) +int kvm_arch_init(MachineState *ms, KVMState *s) { /* For ARM interrupt delivery is always asynchronous, * whether we are using an in-kernel VGIC or not. */ kvm_async_interrupts_allowed = true; + cap_has_mp_state = kvm_check_extension(s, KVM_CAP_MP_STATE); + type_register_static(&host_arm_cpu_type_info); return 0; @@ -279,6 +284,94 @@ void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid, uint64_t group, memory_region_ref(kd->mr); } +static int compare_u64(const void *a, const void *b) +{ + if (*(uint64_t *)a > *(uint64_t *)b) { + return 1; + } + if (*(uint64_t *)a < *(uint64_t *)b) { + return -1; + } + return 0; +} + +/* Initialize the CPUState's cpreg list according to the kernel's + * definition of what CPU registers it knows about (and throw away + * the previous TCG-created cpreg list). + */ +int kvm_arm_init_cpreg_list(ARMCPU *cpu) +{ + struct kvm_reg_list rl; + struct kvm_reg_list *rlp; + int i, ret, arraylen; + CPUState *cs = CPU(cpu); + + rl.n = 0; + ret = kvm_vcpu_ioctl(cs, KVM_GET_REG_LIST, &rl); + if (ret != -E2BIG) { + return ret; + } + rlp = g_malloc(sizeof(struct kvm_reg_list) + rl.n * sizeof(uint64_t)); + rlp->n = rl.n; + ret = kvm_vcpu_ioctl(cs, KVM_GET_REG_LIST, rlp); + if (ret) { + goto out; + } + /* Sort the list we get back from the kernel, since cpreg_tuples + * must be in strictly ascending order. + */ + qsort(&rlp->reg, rlp->n, sizeof(rlp->reg[0]), compare_u64); + + for (i = 0, arraylen = 0; i < rlp->n; i++) { + if (!kvm_arm_reg_syncs_via_cpreg_list(rlp->reg[i])) { + continue; + } + switch (rlp->reg[i] & KVM_REG_SIZE_MASK) { + case KVM_REG_SIZE_U32: + case KVM_REG_SIZE_U64: + break; + default: + fprintf(stderr, "Can't handle size of register in kernel list\n"); + ret = -EINVAL; + goto out; + } + + arraylen++; + } + + cpu->cpreg_indexes = g_renew(uint64_t, cpu->cpreg_indexes, arraylen); + cpu->cpreg_values = g_renew(uint64_t, cpu->cpreg_values, arraylen); + cpu->cpreg_vmstate_indexes = g_renew(uint64_t, cpu->cpreg_vmstate_indexes, + arraylen); + cpu->cpreg_vmstate_values = g_renew(uint64_t, cpu->cpreg_vmstate_values, + arraylen); + cpu->cpreg_array_len = arraylen; + cpu->cpreg_vmstate_array_len = arraylen; + + for (i = 0, arraylen = 0; i < rlp->n; i++) { + uint64_t regidx = rlp->reg[i]; + if (!kvm_arm_reg_syncs_via_cpreg_list(regidx)) { + continue; + } + cpu->cpreg_indexes[arraylen] = regidx; + arraylen++; + } + assert(cpu->cpreg_array_len == arraylen); + + if (!write_kvmstate_to_list(cpu)) { + /* Shouldn't happen unless kernel is inconsistent about + * what registers exist. + */ + fprintf(stderr, "Initial read of kernel register state failed\n"); + ret = -EINVAL; + goto out; + } + +out: + g_free(rlp); + return ret; +} + bool write_kvmstate_to_list(ARMCPU *cpu) { CPUState *cs = CPU(cpu); @@ -351,6 +444,64 @@ bool write_list_to_kvmstate(ARMCPU *cpu) return ok; } +void kvm_arm_reset_vcpu(ARMCPU *cpu) +{ + int ret; + + /* Re-init VCPU so that all registers are set to + * their respective reset values. + */ + ret = kvm_arm_vcpu_init(CPU(cpu)); + if (ret < 0) { + fprintf(stderr, "kvm_arm_vcpu_init failed: %s\n", strerror(-ret)); + abort(); + } + if (!write_kvmstate_to_list(cpu)) { + fprintf(stderr, "write_kvmstate_to_list failed\n"); + abort(); + } +} + +/* + * Update KVM's MP_STATE based on what QEMU thinks it is + */ +int kvm_arm_sync_mpstate_to_kvm(ARMCPU *cpu) +{ + if (cap_has_mp_state) { + struct kvm_mp_state mp_state = { + .mp_state = + cpu->powered_off ? KVM_MP_STATE_STOPPED : KVM_MP_STATE_RUNNABLE + }; + int ret = kvm_vcpu_ioctl(CPU(cpu), KVM_SET_MP_STATE, &mp_state); + if (ret) { + fprintf(stderr, "%s: failed to set MP_STATE %d/%s\n", + __func__, ret, strerror(-ret)); + return -1; + } + } + + return 0; +} + +/* + * Sync the KVM MP_STATE into QEMU + */ +int kvm_arm_sync_mpstate_to_qemu(ARMCPU *cpu) +{ + if (cap_has_mp_state) { + struct kvm_mp_state mp_state; + int ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_MP_STATE, &mp_state); + if (ret) { + fprintf(stderr, "%s: failed to get MP_STATE %d/%s\n", + __func__, ret, strerror(-ret)); + abort(); + } + cpu->powered_off = (mp_state.mp_state == KVM_MP_STATE_STOPPED); + } + + return 0; +} + void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run) { } @@ -441,3 +592,9 @@ int kvm_arch_irqchip_create(KVMState *s) return 0; } + +int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, + uint64_t address, uint32_t data) +{ + return 0; +} diff --git a/target-arm/kvm32.c b/target-arm/kvm32.c index 5ec4eb1..49b6bab 100644 --- a/target-arm/kvm32.c +++ b/target-arm/kvm32.c @@ -51,17 +51,17 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc) struct kvm_one_reg idregs[] = { { .id = KVM_REG_ARM | KVM_REG_SIZE_U32 - | ENCODE_CP_REG(15, 0, 0, 0, 0, 0), + | ENCODE_CP_REG(15, 0, 0, 0, 0, 0, 0), .addr = (uintptr_t)&midr, }, { .id = KVM_REG_ARM | KVM_REG_SIZE_U32 - | ENCODE_CP_REG(15, 0, 0, 1, 0, 0), + | ENCODE_CP_REG(15, 0, 0, 0, 1, 0, 0), .addr = (uintptr_t)&id_pfr0, }, { .id = KVM_REG_ARM | KVM_REG_SIZE_U32 - | ENCODE_CP_REG(15, 0, 0, 2, 0, 0), + | ENCODE_CP_REG(15, 0, 0, 0, 2, 0, 0), .addr = (uintptr_t)&id_isar0, }, { @@ -138,7 +138,7 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc) return true; } -static bool reg_syncs_via_tuple_list(uint64_t regidx) +bool kvm_arm_reg_syncs_via_cpreg_list(uint64_t regidx) { /* Return true if the regidx is a register we should synchronize * via the cpreg_tuples array (ie is not a core reg we sync by @@ -153,24 +153,11 @@ static bool reg_syncs_via_tuple_list(uint64_t regidx) } } -static int compare_u64(const void *a, const void *b) -{ - if (*(uint64_t *)a > *(uint64_t *)b) { - return 1; - } - if (*(uint64_t *)a < *(uint64_t *)b) { - return -1; - } - return 0; -} - int kvm_arch_init_vcpu(CPUState *cs) { - int i, ret, arraylen; + int ret; uint64_t v; struct kvm_one_reg r; - struct kvm_reg_list rl; - struct kvm_reg_list *rlp; ARMCPU *cpu = ARM_CPU(cs); if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE) { @@ -206,73 +193,7 @@ int kvm_arch_init_vcpu(CPUState *cs) return -EINVAL; } - /* Populate the cpreg list based on the kernel's idea - * of what registers exist (and throw away the TCG-created list). - */ - rl.n = 0; - ret = kvm_vcpu_ioctl(cs, KVM_GET_REG_LIST, &rl); - if (ret != -E2BIG) { - return ret; - } - rlp = g_malloc(sizeof(struct kvm_reg_list) + rl.n * sizeof(uint64_t)); - rlp->n = rl.n; - ret = kvm_vcpu_ioctl(cs, KVM_GET_REG_LIST, rlp); - if (ret) { - goto out; - } - /* Sort the list we get back from the kernel, since cpreg_tuples - * must be in strictly ascending order. - */ - qsort(&rlp->reg, rlp->n, sizeof(rlp->reg[0]), compare_u64); - - for (i = 0, arraylen = 0; i < rlp->n; i++) { - if (!reg_syncs_via_tuple_list(rlp->reg[i])) { - continue; - } - switch (rlp->reg[i] & KVM_REG_SIZE_MASK) { - case KVM_REG_SIZE_U32: - case KVM_REG_SIZE_U64: - break; - default: - fprintf(stderr, "Can't handle size of register in kernel list\n"); - ret = -EINVAL; - goto out; - } - - arraylen++; - } - - cpu->cpreg_indexes = g_renew(uint64_t, cpu->cpreg_indexes, arraylen); - cpu->cpreg_values = g_renew(uint64_t, cpu->cpreg_values, arraylen); - cpu->cpreg_vmstate_indexes = g_renew(uint64_t, cpu->cpreg_vmstate_indexes, - arraylen); - cpu->cpreg_vmstate_values = g_renew(uint64_t, cpu->cpreg_vmstate_values, - arraylen); - cpu->cpreg_array_len = arraylen; - cpu->cpreg_vmstate_array_len = arraylen; - - for (i = 0, arraylen = 0; i < rlp->n; i++) { - uint64_t regidx = rlp->reg[i]; - if (!reg_syncs_via_tuple_list(regidx)) { - continue; - } - cpu->cpreg_indexes[arraylen] = regidx; - arraylen++; - } - assert(cpu->cpreg_array_len == arraylen); - - if (!write_kvmstate_to_list(cpu)) { - /* Shouldn't happen unless kernel is inconsistent about - * what registers exist. - */ - fprintf(stderr, "Initial read of kernel register state failed\n"); - ret = -EINVAL; - goto out; - } - -out: - g_free(rlp); - return ret; + return kvm_arm_init_cpreg_list(cpu); } typedef struct Reg { @@ -435,6 +356,8 @@ int kvm_arch_put_registers(CPUState *cs, int level) return EINVAL; } + kvm_arm_sync_mpstate_to_kvm(cpu); + return ret; } @@ -506,14 +429,7 @@ int kvm_arch_get_registers(CPUState *cs) */ write_list_to_cpustate(cpu); - return 0; -} + kvm_arm_sync_mpstate_to_qemu(cpu); -void kvm_arm_reset_vcpu(ARMCPU *cpu) -{ - /* Re-init VCPU so that all registers are set to - * their respective reset values. - */ - kvm_arm_vcpu_init(CPU(cpu)); - write_kvmstate_to_list(cpu); + return 0; } diff --git a/target-arm/kvm64.c b/target-arm/kvm64.c index c615286..93c1ca8 100644 --- a/target-arm/kvm64.c +++ b/target-arm/kvm64.c @@ -15,6 +15,7 @@ #include +#include "config-host.h" #include "qemu-common.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" @@ -82,7 +83,7 @@ int kvm_arch_init_vcpu(CPUState *cs) ARMCPU *cpu = ARM_CPU(cs); if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE || - !arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { + !object_dynamic_cast(OBJECT(cpu), TYPE_AARCH64_CPU)) { fprintf(stderr, "KVM is not supported for this guest CPU type\n"); return -EINVAL; } @@ -96,6 +97,9 @@ int kvm_arch_init_vcpu(CPUState *cs) cpu->psci_version = 2; cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_PSCI_0_2; } + if (!arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { + cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_EL1_32BIT; + } /* Do KVM_ARM_VCPU_INIT ioctl */ ret = kvm_arm_vcpu_init(cs); @@ -103,24 +107,51 @@ int kvm_arch_init_vcpu(CPUState *cs) return ret; } - /* TODO : support for save/restore/reset of system regs via tuple list */ + return kvm_arm_init_cpreg_list(cpu); +} - return 0; +bool kvm_arm_reg_syncs_via_cpreg_list(uint64_t regidx) +{ + /* Return true if the regidx is a register we should synchronize + * via the cpreg_tuples array (ie is not a core reg we sync by + * hand in kvm_arch_get/put_registers()) + */ + switch (regidx & KVM_REG_ARM_COPROC_MASK) { + case KVM_REG_ARM_CORE: + return false; + default: + return true; + } } #define AARCH64_CORE_REG(x) (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \ KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(x)) +#define AARCH64_SIMD_CORE_REG(x) (KVM_REG_ARM64 | KVM_REG_SIZE_U128 | \ + KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(x)) + +#define AARCH64_SIMD_CTRL_REG(x) (KVM_REG_ARM64 | KVM_REG_SIZE_U32 | \ + KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(x)) + int kvm_arch_put_registers(CPUState *cs, int level) { struct kvm_one_reg reg; + uint32_t fpr; uint64_t val; int i; int ret; + unsigned int el; ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; + /* If we are in AArch32 mode then we need to copy the AArch32 regs to the + * AArch64 registers before pushing them out to 64-bit KVM. + */ + if (!is_a64(env)) { + aarch64_sync_32_to_64(env); + } + for (i = 0; i < 31; i++) { reg.id = AARCH64_CORE_REG(regs.regs[i]); reg.addr = (uintptr_t) &env->xregs[i]; @@ -150,7 +181,11 @@ int kvm_arch_put_registers(CPUState *cs, int level) } /* Note that KVM thinks pstate is 64 bit but we use a uint32_t */ - val = pstate_read(env); + if (is_a64(env)) { + val = pstate_read(env); + } else { + val = cpsr_read(env); + } reg.id = AARCH64_CORE_REG(regs.pstate); reg.addr = (uintptr_t) &val; ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); @@ -172,19 +207,70 @@ int kvm_arch_put_registers(CPUState *cs, int level) return ret; } + /* Saved Program State Registers + * + * Before we restore from the banked_spsr[] array we need to + * ensure that any modifications to env->spsr are correctly + * reflected in the banks. + */ + el = arm_current_el(env); + if (el > 0 && !is_a64(env)) { + i = bank_number(env->uncached_cpsr & CPSR_M); + env->banked_spsr[i] = env->spsr; + } + + /* KVM 0-4 map to QEMU banks 1-5 */ for (i = 0; i < KVM_NR_SPSR; i++) { reg.id = AARCH64_CORE_REG(spsr[i]); - reg.addr = (uintptr_t) &env->banked_spsr[i - 1]; + reg.addr = (uintptr_t) &env->banked_spsr[i + 1]; ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); if (ret) { return ret; } } - /* TODO: - * FP state - * system registers + /* Advanced SIMD and FP registers + * We map Qn = regs[2n+1]:regs[2n] */ + for (i = 0; i < 32; i++) { + int rd = i << 1; + uint64_t fp_val[2]; +#ifdef HOST_WORDS_BIGENDIAN + fp_val[0] = env->vfp.regs[rd + 1]; + fp_val[1] = env->vfp.regs[rd]; +#else + fp_val[1] = env->vfp.regs[rd + 1]; + fp_val[0] = env->vfp.regs[rd]; +#endif + reg.id = AARCH64_SIMD_CORE_REG(fp_regs.vregs[i]); + reg.addr = (uintptr_t)(&fp_val); + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret) { + return ret; + } + } + + reg.addr = (uintptr_t)(&fpr); + fpr = vfp_get_fpsr(env); + reg.id = AARCH64_SIMD_CTRL_REG(fp_regs.fpsr); + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret) { + return ret; + } + + fpr = vfp_get_fpcr(env); + reg.id = AARCH64_SIMD_CTRL_REG(fp_regs.fpcr); + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret) { + return ret; + } + + if (!write_list_to_kvmstate(cpu)) { + return EINVAL; + } + + kvm_arm_sync_mpstate_to_kvm(cpu); + return ret; } @@ -192,6 +278,8 @@ int kvm_arch_get_registers(CPUState *cs) { struct kvm_one_reg reg; uint64_t val; + uint32_t fpr; + unsigned int el; int i; int ret; @@ -227,7 +315,14 @@ int kvm_arch_get_registers(CPUState *cs) if (ret) { return ret; } - pstate_write(env, val); + + env->aarch64 = ((val & PSTATE_nRW) == 0); + if (is_a64(env)) { + pstate_write(env, val); + } else { + env->uncached_cpsr = val & CPSR_M; + cpsr_write(env, val, 0xffffffff); + } /* KVM puts SP_EL0 in regs.sp and SP_EL1 in regs.sp_el1. On the * QEMU side we keep the current SP in xregs[31] as well. @@ -241,6 +336,15 @@ int kvm_arch_get_registers(CPUState *cs) return ret; } + /* If we are in AArch32 mode then we need to sync the AArch32 regs with the + * incoming AArch64 regs received from 64-bit KVM. + * We must perform this after all of the registers have been acquired from + * the kernel. + */ + if (!is_a64(env)) { + aarch64_sync_64_to_32(env); + } + reg.id = AARCH64_CORE_REG(elr_el1); reg.addr = (uintptr_t) &env->elr_el[1]; ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); @@ -248,23 +352,72 @@ int kvm_arch_get_registers(CPUState *cs) return ret; } + /* Fetch the SPSR registers + * + * KVM SPSRs 0-4 map to QEMU banks 1-5 + */ for (i = 0; i < KVM_NR_SPSR; i++) { reg.id = AARCH64_CORE_REG(spsr[i]); - reg.addr = (uintptr_t) &env->banked_spsr[i - 1]; + reg.addr = (uintptr_t) &env->banked_spsr[i + 1]; ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); if (ret) { return ret; } } - /* TODO: other registers */ - return ret; -} + el = arm_current_el(env); + if (el > 0 && !is_a64(env)) { + i = bank_number(env->uncached_cpsr & CPSR_M); + env->spsr = env->banked_spsr[i]; + } -void kvm_arm_reset_vcpu(ARMCPU *cpu) -{ - /* Re-init VCPU so that all registers are set to - * their respective reset values. + /* Advanced SIMD and FP registers + * We map Qn = regs[2n+1]:regs[2n] + */ + for (i = 0; i < 32; i++) { + uint64_t fp_val[2]; + reg.id = AARCH64_SIMD_CORE_REG(fp_regs.vregs[i]); + reg.addr = (uintptr_t)(&fp_val); + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (ret) { + return ret; + } else { + int rd = i << 1; +#ifdef HOST_WORDS_BIGENDIAN + env->vfp.regs[rd + 1] = fp_val[0]; + env->vfp.regs[rd] = fp_val[1]; +#else + env->vfp.regs[rd + 1] = fp_val[1]; + env->vfp.regs[rd] = fp_val[0]; +#endif + } + } + + reg.addr = (uintptr_t)(&fpr); + reg.id = AARCH64_SIMD_CTRL_REG(fp_regs.fpsr); + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (ret) { + return ret; + } + vfp_set_fpsr(env, fpr); + + reg.id = AARCH64_SIMD_CTRL_REG(fp_regs.fpcr); + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (ret) { + return ret; + } + vfp_set_fpcr(env, fpr); + + if (!write_kvmstate_to_list(cpu)) { + return EINVAL; + } + /* Note that it's OK to have registers which aren't in CPUState, + * so we can ignore a failure return here. */ - kvm_arm_vcpu_init(CPU(cpu)); + write_list_to_cpustate(cpu); + + kvm_arm_sync_mpstate_to_qemu(cpu); + + /* TODO: other registers */ + return ret; } diff --git a/target-arm/kvm_arm.h b/target-arm/kvm_arm.h index af93105..5abd591 100644 --- a/target-arm/kvm_arm.h +++ b/target-arm/kvm_arm.h @@ -47,6 +47,28 @@ void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid, uint64_t group, uint64_t attr, int dev_fd); /** + * kvm_arm_init_cpreg_list: + * @cs: CPUState + * + * Initialize the CPUState's cpreg list according to the kernel's + * definition of what CPU registers it knows about (and throw away + * the previous TCG-created cpreg list). + * + * Returns: 0 if success, else < 0 error code + */ +int kvm_arm_init_cpreg_list(ARMCPU *cpu); + +/** + * kvm_arm_reg_syncs_via_cpreg_list + * regidx: KVM register index + * + * Return true if this KVM register should be synchronized via the + * cpreg list of arbitrary system registers, false if it is synchronized + * by hand using code in kvm_arch_get/put_registers(). + */ +bool kvm_arm_reg_syncs_via_cpreg_list(uint64_t regidx); + +/** * write_list_to_kvmstate: * @cpu: ARMCPU * @@ -140,6 +162,23 @@ typedef struct ARMHostCPUClass { */ bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc); + +/** + * kvm_arm_sync_mpstate_to_kvm + * @cpu: ARMCPU + * + * If supported set the KVM MP_STATE based on QEMU's model. + */ +int kvm_arm_sync_mpstate_to_kvm(ARMCPU *cpu); + +/** + * kvm_arm_sync_mpstate_to_qemu + * @cpu: ARMCPU + * + * If supported get the MP_STATE from KVM and store in QEMU's model. + */ +int kvm_arm_sync_mpstate_to_qemu(ARMCPU *cpu); + #endif #endif diff --git a/target-arm/machine.c b/target-arm/machine.c index 6437690..9446e5a 100644 --- a/target-arm/machine.c +++ b/target-arm/machine.c @@ -127,6 +127,13 @@ static int get_cpsr(QEMUFile *f, void *opaque, size_t size) CPUARMState *env = &cpu->env; uint32_t val = qemu_get_be32(f); + env->aarch64 = ((val & PSTATE_nRW) == 0); + + if (is_a64(env)) { + pstate_write(env, val); + return 0; + } + /* Avoid mode switch when restoring CPSR */ env->uncached_cpsr = val & CPSR_M; cpsr_write(env, val, 0xffffffff); @@ -137,8 +144,15 @@ static void put_cpsr(QEMUFile *f, void *opaque, size_t size) { ARMCPU *cpu = opaque; CPUARMState *env = &cpu->env; + uint32_t val; + + if (is_a64(env)) { + val = pstate_read(env); + } else { + val = cpsr_read(env); + } - qemu_put_be32(f, cpsr_read(env)); + qemu_put_be32(f, val); } static const VMStateInfo vmstate_cpsr = { @@ -222,12 +236,14 @@ static int cpu_post_load(void *opaque, int version_id) const VMStateDescription vmstate_arm_cpu = { .name = "cpu", - .version_id = 21, - .minimum_version_id = 21, + .version_id = 22, + .minimum_version_id = 22, .pre_save = cpu_pre_save, .post_load = cpu_post_load, .fields = (VMStateField[]) { VMSTATE_UINT32_ARRAY(env.regs, ARMCPU, 16), + VMSTATE_UINT64_ARRAY(env.xregs, ARMCPU, 32), + VMSTATE_UINT64(env.pc, ARMCPU), { .name = "cpsr", .version_id = 0, @@ -261,8 +277,8 @@ const VMStateDescription vmstate_arm_cpu = { VMSTATE_UINT32(env.exception.syndrome, ARMCPU), VMSTATE_UINT32(env.exception.fsr, ARMCPU), VMSTATE_UINT64(env.exception.vaddress, ARMCPU), - VMSTATE_TIMER(gt_timer[GTIMER_PHYS], ARMCPU), - VMSTATE_TIMER(gt_timer[GTIMER_VIRT], ARMCPU), + VMSTATE_TIMER_PTR(gt_timer[GTIMER_PHYS], ARMCPU), + VMSTATE_TIMER_PTR(gt_timer[GTIMER_VIRT], ARMCPU), VMSTATE_BOOL(powered_off, ARMCPU), VMSTATE_END_OF_LIST() }, diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c index 62012c3..7713022 100644 --- a/target-arm/op_helper.c +++ b/target-arm/op_helper.c @@ -361,7 +361,7 @@ void HELPER(msr_i_pstate)(CPUARMState *env, uint32_t op, uint32_t imm) * Note that SPSel is never OK from EL0; we rely on handle_msr_i() * to catch that case at translate time. */ - if (arm_current_el(env) == 0 && !(env->cp15.c1_sys & SCTLR_UMA)) { + if (arm_current_el(env) == 0 && !(env->cp15.sctlr_el[1] & SCTLR_UMA)) { raise_exception(env, EXCP_UDEF); } @@ -465,7 +465,7 @@ void HELPER(exception_return)(CPUARMState *env) int cur_el = arm_current_el(env); unsigned int spsr_idx = aarch64_banked_spsr_index(cur_el); uint32_t spsr = env->banked_spsr[spsr_idx]; - int new_el, i; + int new_el; aarch64_save_sp(env, cur_el); @@ -491,9 +491,7 @@ void HELPER(exception_return)(CPUARMState *env) if (!arm_singlestep_active(env)) { env->uncached_cpsr &= ~PSTATE_SS; } - for (i = 0; i < 15; i++) { - env->regs[i] = env->xregs[i]; - } + aarch64_sync_64_to_32(env); env->regs[15] = env->elr_el[1] & ~0x1; } else { @@ -575,7 +573,7 @@ static bool linked_bp_matches(ARMCPU *cpu, int lbn) * short descriptor format (in which case it holds both PROCID and ASID), * since we don't implement the optional v7 context ID masking. */ - contextidr = extract64(env->cp15.contextidr_el1, 0, 32); + contextidr = extract64(env->cp15.contextidr_el[1], 0, 32); switch (bt) { case 3: /* linked context ID match */ diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index 80d2c07..0b192a1 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -123,6 +123,23 @@ void a64_translate_init(void) #endif } +static inline ARMMMUIdx get_a64_user_mem_index(DisasContext *s) +{ + /* Return the mmu_idx to use for A64 "unprivileged load/store" insns: + * if EL1, access as if EL0; otherwise access at current EL + */ + switch (s->mmu_idx) { + case ARMMMUIdx_S12NSE1: + return ARMMMUIdx_S12NSE0; + case ARMMMUIdx_S1SE1: + return ARMMMUIdx_S1SE0; + case ARMMMUIdx_S2NS: + g_assert_not_reached(); + default: + return s->mmu_idx; + } +} + void aarch64_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, int flags) { @@ -1060,7 +1077,7 @@ static void disas_uncond_b_imm(DisasContext *s, uint32_t insn) { uint64_t addr = s->pc + sextract32(insn, 0, 26) * 4 - 4; - if (insn & (1 << 31)) { + if (insn & (1U << 31)) { /* C5.6.26 BL Branch with link */ tcg_gen_movi_i64(cpu_reg(s, 30), s->pc); } @@ -1079,7 +1096,7 @@ static void disas_comp_b_imm(DisasContext *s, uint32_t insn) { unsigned int sf, op, rt; uint64_t addr; - int label_match; + TCGLabel *label_match; TCGv_i64 tcg_cmp; sf = extract32(insn, 31, 1); @@ -1108,7 +1125,7 @@ static void disas_test_b_imm(DisasContext *s, uint32_t insn) { unsigned int bit_pos, op, rt; uint64_t addr; - int label_match; + TCGLabel *label_match; TCGv_i64 tcg_cmp; bit_pos = (extract32(insn, 31, 1) << 5) | extract32(insn, 19, 5); @@ -1147,7 +1164,7 @@ static void disas_cond_b_imm(DisasContext *s, uint32_t insn) if (cond < 0x0e) { /* genuinely conditional branches */ - int label_match = gen_new_label(); + TCGLabel *label_match = gen_new_label(); arm_gen_test_cc(cond, label_match); gen_goto_tb(s, 0, s->pc); gen_set_label(label_match); @@ -1254,7 +1271,7 @@ static void gen_get_nzcv(TCGv_i64 tcg_rt) TCGv_i32 nzcv = tcg_temp_new_i32(); /* build bit 31, N */ - tcg_gen_andi_i32(nzcv, cpu_NF, (1 << 31)); + tcg_gen_andi_i32(nzcv, cpu_NF, (1U << 31)); /* build bit 30, Z */ tcg_gen_setcondi_i32(TCG_COND_EQ, tmp, cpu_ZF, 0); tcg_gen_deposit_i32(nzcv, nzcv, tmp, 30, 1); @@ -1279,7 +1296,7 @@ static void gen_set_nzcv(TCGv_i64 tcg_rt) tcg_gen_trunc_i64_i32(nzcv, tcg_rt); /* bit 31, N */ - tcg_gen_andi_i32(cpu_NF, nzcv, (1 << 31)); + tcg_gen_andi_i32(cpu_NF, nzcv, (1U << 31)); /* bit 30, Z */ tcg_gen_andi_i32(cpu_ZF, nzcv, (1 << 30)); tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_ZF, cpu_ZF, 0); @@ -1372,7 +1389,7 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread, break; } - if (use_icount && (ri->type & ARM_CP_IO)) { + if ((s->tb->cflags & CF_USE_ICOUNT) && (ri->type & ARM_CP_IO)) { gen_io_start(); } @@ -1403,7 +1420,7 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread, } } - if (use_icount && (ri->type & ARM_CP_IO)) { + if ((s->tb->cflags & CF_USE_ICOUNT) && (ri->type & ARM_CP_IO)) { /* I/O operations must end the TB here (whether read or write) */ gen_io_end(); s->is_jmp = DISAS_UPDATE; @@ -1694,8 +1711,8 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, * } * env->exclusive_addr = -1; */ - int fail_label = gen_new_label(); - int done_label = gen_new_label(); + TCGLabel *fail_label = gen_new_label(); + TCGLabel *done_label = gen_new_label(); TCGv_i64 addr = tcg_temp_local_new_i64(); TCGv_i64 tmp; @@ -1900,7 +1917,7 @@ static void disas_ldst_pair(DisasContext *s, uint32_t insn) int rt = extract32(insn, 0, 5); int rn = extract32(insn, 5, 5); int rt2 = extract32(insn, 10, 5); - int64_t offset = sextract32(insn, 15, 7); + uint64_t offset = sextract64(insn, 15, 7); int index = extract32(insn, 23, 2); bool is_vector = extract32(insn, 26, 1); bool is_load = extract32(insn, 22, 1); @@ -2107,7 +2124,7 @@ static void disas_ldst_reg_imm9(DisasContext *s, uint32_t insn) } } else { TCGv_i64 tcg_rt = cpu_reg(s, rt); - int memidx = is_unpriv ? 1 : get_mem_index(s); + int memidx = is_unpriv ? get_a64_user_mem_index(s) : get_mem_index(s); if (is_store) { do_gpr_st_memidx(s, tcg_rt, tcg_addr, size, memidx); @@ -2645,11 +2662,12 @@ static void disas_pc_rel_adr(DisasContext *s, uint32_t insn) { unsigned int page, rd; uint64_t base; - int64_t offset; + uint64_t offset; page = extract32(insn, 31, 1); /* SignExtend(immhi:immlo) -> offset */ - offset = ((int64_t)sextract32(insn, 5, 19) << 2) | extract32(insn, 29, 2); + offset = sextract64(insn, 5, 19); + offset = offset << 2 | extract32(insn, 29, 2); rd = extract32(insn, 0, 5); base = s->pc - 4; @@ -2803,7 +2821,10 @@ static bool logic_imm_decode_wmask(uint64_t *result, unsigned int immn, * by r within the element (which is e bits wide)... */ mask = bitmask64(s + 1); - mask = (mask >> r) | (mask << (e - r)); + if (r) { + mask = (mask >> r) | (mask << (e - r)); + mask &= bitmask64(e); + } /* ...then replicate the element over the whole 64 bit value */ mask = bitfield_replicate(mask, e); *result = mask; @@ -3516,7 +3537,7 @@ static void disas_adc_sbc(DisasContext *s, uint32_t insn) static void disas_cc(DisasContext *s, uint32_t insn) { unsigned int sf, op, y, cond, rn, nzcv, is_imm; - int label_continue = -1; + TCGLabel *label_continue = NULL; TCGv_i64 tcg_tmp, tcg_y, tcg_rn; if (!extract32(insn, 29, 1)) { @@ -3536,7 +3557,7 @@ static void disas_cc(DisasContext *s, uint32_t insn) nzcv = extract32(insn, 0, 4); if (cond < 0x0e) { /* not always */ - int label_match = gen_new_label(); + TCGLabel *label_match = gen_new_label(); label_continue = gen_new_label(); arm_gen_test_cc(cond, label_match); /* nomatch: */ @@ -3609,8 +3630,8 @@ static void disas_cond_select(DisasContext *s, uint32_t insn) /* OPTME: we could use movcond here, at the cost of duplicating * a lot of the arm_gen_test_cc() logic. */ - int label_match = gen_new_label(); - int label_continue = gen_new_label(); + TCGLabel *label_match = gen_new_label(); + TCGLabel *label_continue = gen_new_label(); arm_gen_test_cc(cond, label_match); /* nomatch: */ @@ -4083,7 +4104,7 @@ static void disas_fp_ccomp(DisasContext *s, uint32_t insn) { unsigned int mos, type, rm, cond, rn, op, nzcv; TCGv_i64 tcg_flags; - int label_continue = -1; + TCGLabel *label_continue = NULL; mos = extract32(insn, 29, 3); type = extract32(insn, 22, 2); /* 0 = single, 1 = double */ @@ -4103,7 +4124,7 @@ static void disas_fp_ccomp(DisasContext *s, uint32_t insn) } if (cond < 0x0e) { /* not always */ - int label_match = gen_new_label(); + TCGLabel *label_match = gen_new_label(); label_continue = gen_new_label(); arm_gen_test_cc(cond, label_match); /* nomatch: */ @@ -4144,7 +4165,7 @@ static void gen_mov_fp2fp(DisasContext *s, int type, int dst, int src) static void disas_fp_csel(DisasContext *s, uint32_t insn) { unsigned int mos, type, rm, cond, rn, rd; - int label_continue = -1; + TCGLabel *label_continue = NULL; mos = extract32(insn, 29, 3); type = extract32(insn, 22, 2); /* 0 = single, 1 = double */ @@ -4163,7 +4184,7 @@ static void disas_fp_csel(DisasContext *s, uint32_t insn) } if (cond < 0x0e) { /* not always */ - int label_match = gen_new_label(); + TCGLabel *label_match = gen_new_label(); label_continue = gen_new_label(); arm_gen_test_cc(cond, label_match); /* nomatch: */ @@ -10899,7 +10920,6 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu, CPUARMState *env = &cpu->env; DisasContext dc1, *dc = &dc1; CPUBreakpoint *bp; - uint16_t *gen_opc_end; int j, lj; target_ulong pc_start; target_ulong next_page_start; @@ -10910,8 +10930,6 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu, dc->tb = tb; - gen_opc_end = tcg_ctx.gen_opc_buf + OPC_MAX_SIZE; - dc->is_jmp = DISAS_NEXT; dc->pc = pc_start; dc->singlestep_enabled = cs->singlestep_enabled; @@ -10922,14 +10940,15 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu, dc->bswap_code = 0; dc->condexec_mask = 0; dc->condexec_cond = 0; + dc->mmu_idx = ARM_TBFLAG_MMUIDX(tb->flags); + dc->current_el = arm_mmu_idx_to_el(dc->mmu_idx); #if !defined(CONFIG_USER_ONLY) - dc->user = (ARM_TBFLAG_AA64_EL(tb->flags) == 0); + dc->user = (dc->current_el == 0); #endif dc->cpacr_fpen = ARM_TBFLAG_AA64_FPEN(tb->flags); dc->vec_len = 0; dc->vec_stride = 0; dc->cp_regs = cpu->cp_regs; - dc->current_el = arm_current_el(env); dc->features = env->features; /* Single step state. The code-generation logic here is: @@ -10962,7 +10981,7 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu, max_insns = CF_COUNT_MASK; } - gen_tb_start(); + gen_tb_start(tb); tcg_clear_temp_count(); @@ -10980,7 +10999,7 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu, } if (search_pc) { - j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + j = tcg_op_buf_count(); if (lj < j) { lj++; while (lj < j) { @@ -11030,7 +11049,7 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu, * ensures prefetch aborts occur at the right place. */ num_insns++; - } while (!dc->is_jmp && tcg_ctx.gen_opc_ptr < gen_opc_end && + } while (!dc->is_jmp && !tcg_op_buf_full() && !cs->singlestep_enabled && !singlestep && !dc->ss_active && @@ -11090,7 +11109,6 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu, done_generating: gen_tb_end(tb, num_insns); - *tcg_ctx.gen_opc_ptr = INDEX_op_end; #ifdef DEBUG_DISAS if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { @@ -11102,7 +11120,7 @@ done_generating: } #endif if (search_pc) { - j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + j = tcg_op_buf_count(); lj++; while (lj <= j) { tcg_ctx.gen_opc_instr_start[lj++] = 0; diff --git a/target-arm/translate.c b/target-arm/translate.c index af51568..f8f72be 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -113,6 +113,28 @@ void arm_translate_init(void) a64_translate_init(); } +static inline ARMMMUIdx get_a32_user_mem_index(DisasContext *s) +{ + /* Return the mmu_idx to use for A32/T32 "unprivileged load/store" + * insns: + * if PL2, UNPREDICTABLE (we choose to implement as if PL0) + * otherwise, access as if at PL0. + */ + switch (s->mmu_idx) { + case ARMMMUIdx_S1E2: /* this one is UNPREDICTABLE */ + case ARMMMUIdx_S12NSE0: + case ARMMMUIdx_S12NSE1: + return ARMMMUIdx_S12NSE0; + case ARMMMUIdx_S1E3: + case ARMMMUIdx_S1SE0: + case ARMMMUIdx_S1SE1: + return ARMMMUIdx_S1SE0; + case ARMMMUIdx_S2NS: + default: + g_assert_not_reached(); + } +} + static inline TCGv_i32 load_cpu_offset(int offset) { TCGv_i32 tmp = tcg_temp_new_i32(); @@ -714,10 +736,10 @@ static void gen_thumb2_parallel_addsub(int op1, int op2, TCGv_i32 a, TCGv_i32 b) * generate a conditional branch based on ARM condition code cc. * This is common between ARM and Aarch64 targets. */ -void arm_gen_test_cc(int cc, int label) +void arm_gen_test_cc(int cc, TCGLabel *label) { TCGv_i32 tmp; - int inv; + TCGLabel *inv; switch (cc) { case 0: /* eq: Z */ @@ -7091,7 +7113,7 @@ static int disas_coproc_insn(DisasContext *s, uint32_t insn) rt = (insn >> 12) & 0xf; ri = get_arm_cp_reginfo(s->cp_regs, - ENCODE_CP_REG(cpnum, is64, crn, crm, opc1, opc2)); + ENCODE_CP_REG(cpnum, is64, s->ns, crn, crm, opc1, opc2)); if (ri) { /* Check access permissions */ if (!cp_access_ok(s->current_el, ri, isread)) { @@ -7170,7 +7192,7 @@ static int disas_coproc_insn(DisasContext *s, uint32_t insn) break; } - if (use_icount && (ri->type & ARM_CP_IO)) { + if ((s->tb->cflags & CF_USE_ICOUNT) && (ri->type & ARM_CP_IO)) { gen_io_start(); } @@ -7261,7 +7283,7 @@ static int disas_coproc_insn(DisasContext *s, uint32_t insn) } } - if (use_icount && (ri->type & ARM_CP_IO)) { + if ((s->tb->cflags & CF_USE_ICOUNT) && (ri->type & ARM_CP_IO)) { /* I/O operations must end the TB here (whether read or write) */ gen_io_end(); gen_lookup_tb(s); @@ -7281,12 +7303,16 @@ static int disas_coproc_insn(DisasContext *s, uint32_t insn) */ if (is64) { qemu_log_mask(LOG_UNIMP, "%s access to unsupported AArch32 " - "64 bit system register cp:%d opc1: %d crm:%d\n", - isread ? "read" : "write", cpnum, opc1, crm); + "64 bit system register cp:%d opc1: %d crm:%d " + "(%s)\n", + isread ? "read" : "write", cpnum, opc1, crm, + s->ns ? "non-secure" : "secure"); } else { qemu_log_mask(LOG_UNIMP, "%s access to unsupported AArch32 " - "system register cp:%d opc1:%d crn:%d crm:%d opc2:%d\n", - isread ? "read" : "write", cpnum, opc1, crn, crm, opc2); + "system register cp:%d opc1:%d crn:%d crm:%d opc2:%d " + "(%s)\n", + isread ? "read" : "write", cpnum, opc1, crn, crm, opc2, + s->ns ? "non-secure" : "secure"); } return 1; @@ -7414,8 +7440,8 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, { TCGv_i32 tmp; TCGv_i64 val64, extaddr; - int done_label; - int fail_label; + TCGLabel *done_label; + TCGLabel *fail_label; /* if (env->exclusive_addr == addr && env->exclusive_val == [addr]) { [addr] = {Rt}; @@ -8397,34 +8423,30 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) } } else { int address_offset; - int load; + bool load = insn & (1 << 20); + bool doubleword = false; /* Misc load/store */ rn = (insn >> 16) & 0xf; rd = (insn >> 12) & 0xf; + + if (!load && (sh & 2)) { + /* doubleword */ + ARCH(5TE); + if (rd & 1) { + /* UNPREDICTABLE; we choose to UNDEF */ + goto illegal_op; + } + load = (sh & 1) == 0; + doubleword = true; + } + addr = load_reg(s, rn); if (insn & (1 << 24)) gen_add_datah_offset(s, insn, 0, addr); address_offset = 0; - if (insn & (1 << 20)) { - /* load */ - tmp = tcg_temp_new_i32(); - switch(sh) { - case 1: - gen_aa32_ld16u(tmp, addr, get_mem_index(s)); - break; - case 2: - gen_aa32_ld8s(tmp, addr, get_mem_index(s)); - break; - default: - case 3: - gen_aa32_ld16s(tmp, addr, get_mem_index(s)); - break; - } - load = 1; - } else if (sh & 2) { - ARCH(5TE); - /* doubleword */ - if (sh & 1) { + + if (doubleword) { + if (!load) { /* store */ tmp = load_reg(s, rd); gen_aa32_st32(tmp, addr, get_mem_index(s)); @@ -8433,7 +8455,6 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) tmp = load_reg(s, rd + 1); gen_aa32_st32(tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); - load = 0; } else { /* load */ tmp = tcg_temp_new_i32(); @@ -8443,15 +8464,28 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) tmp = tcg_temp_new_i32(); gen_aa32_ld32u(tmp, addr, get_mem_index(s)); rd++; - load = 1; } address_offset = -4; + } else if (load) { + /* load */ + tmp = tcg_temp_new_i32(); + switch (sh) { + case 1: + gen_aa32_ld16u(tmp, addr, get_mem_index(s)); + break; + case 2: + gen_aa32_ld8s(tmp, addr, get_mem_index(s)); + break; + default: + case 3: + gen_aa32_ld16s(tmp, addr, get_mem_index(s)); + break; + } } else { /* store */ tmp = load_reg(s, rd); gen_aa32_st16(tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); - load = 0; } /* Perform base writeback before the loaded value to ensure correct behavior with overlapping index registers. @@ -8735,6 +8769,10 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) ARCH(6T2); shift = (insn >> 7) & 0x1f; i = (insn >> 16) & 0x1f; + if (i < shift) { + /* UNPREDICTABLE; we choose to UNDEF */ + goto illegal_op; + } i = i + 1 - shift; if (rm == 15) { tmp = tcg_temp_new_i32(); @@ -8789,7 +8827,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) tmp2 = load_reg(s, rn); if ((insn & 0x01200000) == 0x00200000) { /* ldrt/strt */ - i = MMU_USER_IDX; + i = get_a32_user_mem_index(s); } else { i = get_mem_index(s); } @@ -8829,17 +8867,23 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) case 0x08: case 0x09: { - int j, n, user, loaded_base; + int j, n, loaded_base; + bool exc_return = false; + bool is_load = extract32(insn, 20, 1); + bool user = false; TCGv_i32 loaded_var; /* load/store multiple words */ /* XXX: store correct base if write back */ - user = 0; if (insn & (1 << 22)) { + /* LDM (user), LDM (exception return) and STM (user) */ if (IS_USER(s)) goto illegal_op; /* only usable in supervisor mode */ - if ((insn & (1 << 15)) == 0) - user = 1; + if (is_load && extract32(insn, 15, 1)) { + exc_return = true; + } else { + user = true; + } } rn = (insn >> 16) & 0xf; addr = load_reg(s, rn); @@ -8873,7 +8917,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) j = 0; for(i=0;i<16;i++) { if (insn & (1 << i)) { - if (insn & (1 << 20)) { + if (is_load) { /* load */ tmp = tcg_temp_new_i32(); gen_aa32_ld32u(tmp, addr, get_mem_index(s)); @@ -8938,7 +8982,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) if (loaded_base) { store_reg(s, rn, loaded_var); } - if ((insn & (1 << 22)) && !user) { + if (exc_return) { /* Restore CPSR from SPSR. */ tmp = load_cpu_field(spsr); gen_set_cpsr(tmp, CPSR_ERET_MASK); @@ -10169,7 +10213,7 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw break; case 0xe: /* User privilege. */ tcg_gen_addi_i32(addr, addr, imm); - memidx = MMU_USER_IDX; + memidx = get_a32_user_mem_index(s); break; case 0x9: /* Post-decrement. */ imm = -imm; @@ -10995,7 +11039,6 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, CPUARMState *env = &cpu->env; DisasContext dc1, *dc = &dc1; CPUBreakpoint *bp; - uint16_t *gen_opc_end; int j, lj; target_ulong pc_start; target_ulong next_page_start; @@ -11016,8 +11059,6 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, dc->tb = tb; - gen_opc_end = tcg_ctx.gen_opc_buf + OPC_MAX_SIZE; - dc->is_jmp = DISAS_NEXT; dc->pc = pc_start; dc->singlestep_enabled = cs->singlestep_enabled; @@ -11028,16 +11069,18 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, dc->bswap_code = ARM_TBFLAG_BSWAP_CODE(tb->flags); dc->condexec_mask = (ARM_TBFLAG_CONDEXEC(tb->flags) & 0xf) << 1; dc->condexec_cond = ARM_TBFLAG_CONDEXEC(tb->flags) >> 4; + dc->mmu_idx = ARM_TBFLAG_MMUIDX(tb->flags); + dc->current_el = arm_mmu_idx_to_el(dc->mmu_idx); #if !defined(CONFIG_USER_ONLY) - dc->user = (ARM_TBFLAG_PRIV(tb->flags) == 0); + dc->user = (dc->current_el == 0); #endif + dc->ns = ARM_TBFLAG_NS(tb->flags); dc->cpacr_fpen = ARM_TBFLAG_CPACR_FPEN(tb->flags); dc->vfp_enabled = ARM_TBFLAG_VFPEN(tb->flags); dc->vec_len = ARM_TBFLAG_VECLEN(tb->flags); dc->vec_stride = ARM_TBFLAG_VECSTRIDE(tb->flags); dc->c15_cpar = ARM_TBFLAG_XSCALE_CPAR(tb->flags); dc->cp_regs = cpu->cp_regs; - dc->current_el = arm_current_el(env); dc->features = env->features; /* Single step state. The code-generation logic here is: @@ -11075,7 +11118,7 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, if (max_insns == 0) max_insns = CF_COUNT_MASK; - gen_tb_start(); + gen_tb_start(tb); tcg_clear_temp_count(); @@ -11150,7 +11193,7 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, } } if (search_pc) { - j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + j = tcg_op_buf_count(); if (lj < j) { lj++; while (lj < j) @@ -11216,7 +11259,7 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, * Also stop translation when a page boundary is reached. This * ensures prefetch aborts occur at the right place. */ num_insns ++; - } while (!dc->is_jmp && tcg_ctx.gen_opc_ptr < gen_opc_end && + } while (!dc->is_jmp && !tcg_op_buf_full() && !cs->singlestep_enabled && !singlestep && !dc->ss_active && @@ -11325,7 +11368,6 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, done_generating: gen_tb_end(tb, num_insns); - *tcg_ctx.gen_opc_ptr = INDEX_op_end; #ifdef DEBUG_DISAS if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { @@ -11337,7 +11379,7 @@ done_generating: } #endif if (search_pc) { - j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + j = tcg_op_buf_count(); lj++; while (lj <= j) tcg_ctx.gen_opc_instr_start[lj++] = 0; diff --git a/target-arm/translate.h b/target-arm/translate.h index 41a9071..9829576 100644 --- a/target-arm/translate.h +++ b/target-arm/translate.h @@ -9,7 +9,7 @@ typedef struct DisasContext { /* Nonzero if this instruction has been conditionally skipped. */ int condjmp; /* The label that will be jumped to when the instruction is skipped. */ - int condlabel; + TCGLabel *condlabel; /* Thumb-2 conditional execution bits. */ int condexec_mask; int condexec_cond; @@ -20,6 +20,8 @@ typedef struct DisasContext { #if !defined(CONFIG_USER_ONLY) int user; #endif + ARMMMUIdx mmu_idx; /* MMU index to use for normal loads/stores */ + bool ns; /* Use non-secure CPREG bank on access */ bool cpacr_fpen; /* FP enabled via CPACR.FPEN */ bool vfp_enabled; /* FP enabled via FPSCR.EN */ int vec_len; @@ -68,7 +70,7 @@ static inline int arm_dc_feature(DisasContext *dc, int feature) static inline int get_mem_index(DisasContext *s) { - return s->current_el; + return s->mmu_idx; } /* target-specific extra values for is_jmp */ @@ -117,6 +119,6 @@ static inline void aarch64_cpu_dump_state(CPUState *cs, FILE *f, } #endif -void arm_gen_test_cc(int cc, int label); +void arm_gen_test_cc(int cc, TCGLabel *label); #endif /* TARGET_ARM_TRANSLATE_H */ diff --git a/target-cris/cpu.h b/target-cris/cpu.h index b88c147..677b38c 100644 --- a/target-cris/cpu.h +++ b/target-cris/cpu.h @@ -29,8 +29,6 @@ #include "exec/cpu-defs.h" -#define TARGET_HAS_ICE 1 - #define ELF_MACHINE EM_CRIS #define EXCP_NMI 1 @@ -223,14 +221,7 @@ enum { #define TARGET_PHYS_ADDR_SPACE_BITS 32 #define TARGET_VIRT_ADDR_SPACE_BITS 32 -static inline CPUCRISState *cpu_init(const char *cpu_model) -{ - CRISCPU *cpu = cpu_cris_init(cpu_model); - if (cpu == NULL) { - return NULL; - } - return &cpu->env; -} +#define cpu_init(cpu_model) CPU(cpu_cris_init(cpu_model)) #define cpu_exec cpu_cris_exec #define cpu_gen_code cpu_cris_gen_code diff --git a/target-cris/helper.c b/target-cris/helper.c index e901c3a..df6c9fd 100644 --- a/target-cris/helper.c +++ b/target-cris/helper.c @@ -84,8 +84,8 @@ int cris_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, int r = -1; target_ulong phy; - D(printf("%s addr=%" VADDR_PRIx " pc=%x rw=%x\n", - __func__, address, env->pc, rw)); + qemu_log_mask(CPU_LOG_MMU, "%s addr=%" VADDR_PRIx " pc=%x rw=%x\n", + __func__, address, env->pc, rw); miss = cris_mmu_translate(&res, env, address & TARGET_PAGE_MASK, rw, mmu_idx, 0); if (miss) { @@ -112,9 +112,10 @@ int cris_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, r = 0; } if (r > 0) { - D_LOG("%s returns %d irqreq=%x addr=%" VADDR_PRIx " phy=%x vec=%x" - " pc=%x\n", __func__, r, cs->interrupt_request, address, res.phy, - res.bf_vec, env->pc); + qemu_log_mask(CPU_LOG_MMU, + "%s returns %d irqreq=%x addr=%" VADDR_PRIx " phy=%x vec=%x" + " pc=%x\n", __func__, r, cs->interrupt_request, address, + res.phy, res.bf_vec, env->pc); } return r; } diff --git a/target-cris/opcode-cris.h b/target-cris/opcode-cris.h index 779d4aa..e7ebb98 100644 --- a/target-cris/opcode-cris.h +++ b/target-cris/opcode-cris.h @@ -108,16 +108,6 @@ struct cris_support_reg }; extern const struct cris_support_reg cris_support_regs[]; -struct cris_cond15 -{ - /* The name of the condition. */ - const char *const name; - - /* What CPU version this condition name applies to. */ - enum cris_insn_version_usage applicable_version; -}; -extern const struct cris_cond15 cris_conds15[]; - /* Opcode-dependent constants. */ #define AUTOINCR_BIT (0x04) diff --git a/target-cris/translate.c b/target-cris/translate.c index 76406af..687c88b 100644 --- a/target-cris/translate.c +++ b/target-cris/translate.c @@ -311,9 +311,7 @@ static void t_gen_asr(TCGv d, TCGv a, TCGv b) static void t_gen_cris_dstep(TCGv d, TCGv a, TCGv b) { - int l1; - - l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); /* * d <<= 1 @@ -509,9 +507,7 @@ static inline void t_gen_swapr(TCGv d, TCGv s) static void t_gen_cc_jmp(TCGv pc_true, TCGv pc_false) { - int l1; - - l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); /* Conditional jmp. */ tcg_gen_mov_tl(env_pc, pc_false); @@ -774,8 +770,7 @@ static void cris_alu_op_exec(DisasContext *dc, int op, break; case CC_OP_BOUND: { - int l1; - l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); tcg_gen_mov_tl(dst, a); tcg_gen_brcond_tl(TCG_COND_LEU, a, b, l1); tcg_gen_mov_tl(dst, b); @@ -1488,10 +1483,8 @@ static int dec_scc_r(CPUCRISState *env, DisasContext *dc) cc_name(cond), dc->op1); if (cond != CC_A) { - int l1; - + TCGLabel *l1 = gen_new_label(); gen_tst_cc(dc, cpu_R[dc->op1], cond); - l1 = gen_new_label(); tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_R[dc->op1], 0, l1); tcg_gen_movi_tl(cpu_R[dc->op1], 1); gen_set_label(l1); @@ -3040,9 +3033,7 @@ static unsigned int crisv32_decoder(CPUCRISState *env, DisasContext *dc) #if !defined(CONFIG_USER_ONLY) /* Single-stepping ? */ if (dc->tb_flags & S_FLAG) { - int l1; - - l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); tcg_gen_brcondi_tl(TCG_COND_NE, cpu_PR[PR_SPC], dc->pc, l1); /* We treat SPC as a break with an odd trap vector. */ cris_evaluate_flags(dc); @@ -3116,7 +3107,6 @@ gen_intermediate_code_internal(CRISCPU *cpu, TranslationBlock *tb, { CPUState *cs = CPU(cpu); CPUCRISState *env = &cpu->env; - uint16_t *gen_opc_end; uint32_t pc_start; unsigned int insn_len; int j, lj; @@ -3142,8 +3132,6 @@ gen_intermediate_code_internal(CRISCPU *cpu, TranslationBlock *tb, dc->cpu = cpu; dc->tb = tb; - gen_opc_end = tcg_ctx.gen_opc_buf + OPC_MAX_SIZE; - dc->is_jmp = DISAS_NEXT; dc->ppc = pc_start; dc->pc = pc_start; @@ -3202,12 +3190,12 @@ gen_intermediate_code_internal(CRISCPU *cpu, TranslationBlock *tb, max_insns = CF_COUNT_MASK; } - gen_tb_start(); + gen_tb_start(tb); do { check_breakpoint(env, dc); if (search_pc) { - j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + j = tcg_op_buf_count(); if (lj < j) { lj++; while (lj < j) { @@ -3259,9 +3247,7 @@ gen_intermediate_code_internal(CRISCPU *cpu, TranslationBlock *tb, } if (dc->jmp == JMP_DIRECT_CC) { - int l1; - - l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); cris_evaluate_flags(dc); /* Conditional jmp. */ @@ -3291,7 +3277,7 @@ gen_intermediate_code_internal(CRISCPU *cpu, TranslationBlock *tb, break; } } while (!dc->is_jmp && !dc->cpustate_changed - && tcg_ctx.gen_opc_ptr < gen_opc_end + && !tcg_op_buf_full() && !singlestep && (dc->pc < next_page_start) && num_insns < max_insns); @@ -3344,9 +3330,9 @@ gen_intermediate_code_internal(CRISCPU *cpu, TranslationBlock *tb, } } gen_tb_end(tb, num_insns); - *tcg_ctx.gen_opc_ptr = INDEX_op_end; + if (search_pc) { - j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + j = tcg_op_buf_count(); lj++; while (lj <= j) { tcg_ctx.gen_opc_instr_start[lj++] = 0; @@ -3361,8 +3347,8 @@ gen_intermediate_code_internal(CRISCPU *cpu, TranslationBlock *tb, if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { log_target_disas(env, pc_start, dc->pc - pc_start, env->pregs[PR_VR]); - qemu_log("\nisize=%d osize=%td\n", - dc->pc - pc_start, tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf); + qemu_log("\nisize=%d osize=%d\n", + dc->pc - pc_start, tcg_op_buf_count()); } #endif #endif diff --git a/target-cris/translate_v10.c b/target-cris/translate_v10.c index efb3639..b742c4c 100644 --- a/target-cris/translate_v10.c +++ b/target-cris/translate_v10.c @@ -65,7 +65,7 @@ static inline void cris_illegal_insn(DisasContext *dc) static void gen_store_v10_conditional(DisasContext *dc, TCGv addr, TCGv val, unsigned int size, int mem_index) { - int l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); TCGv taddr = tcg_temp_local_new(); TCGv tval = tcg_temp_local_new(); TCGv t1 = tcg_temp_local_new(); @@ -537,10 +537,8 @@ static void dec10_reg_scc(DisasContext *dc) if (cond != CC_A) { - int l1; - + TCGLabel *l1 = gen_new_label(); gen_tst_cc (dc, cpu_R[dc->src], cond); - l1 = gen_new_label(); tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_R[dc->src], 0, l1); tcg_gen_movi_tl(cpu_R[dc->src], 1); gen_set_label(l1); diff --git a/target-i386/arch_dump.c b/target-i386/arch_dump.c index 0bbed23..eccd803 100644 --- a/target-i386/arch_dump.c +++ b/target-i386/arch_dump.c @@ -78,9 +78,7 @@ static int x86_64_write_elf64_note(WriteCoreDumpFunction f, descsz = sizeof(x86_64_elf_prstatus); note_size = ((sizeof(Elf64_Nhdr) + 3) / 4 + (name_size + 3) / 4 + (descsz + 3) / 4) * 4; - note = g_malloc(note_size); - - memset(note, 0, note_size); + note = g_malloc0(note_size); note->n_namesz = cpu_to_le32(name_size); note->n_descsz = cpu_to_le32(descsz); note->n_type = cpu_to_le32(NT_PRSTATUS); @@ -159,9 +157,7 @@ static int x86_write_elf64_note(WriteCoreDumpFunction f, CPUX86State *env, descsz = sizeof(x86_elf_prstatus); note_size = ((sizeof(Elf64_Nhdr) + 3) / 4 + (name_size + 3) / 4 + (descsz + 3) / 4) * 4; - note = g_malloc(note_size); - - memset(note, 0, note_size); + note = g_malloc0(note_size); note->n_namesz = cpu_to_le32(name_size); note->n_descsz = cpu_to_le32(descsz); note->n_type = cpu_to_le32(NT_PRSTATUS); @@ -216,9 +212,7 @@ int x86_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, descsz = sizeof(x86_elf_prstatus); note_size = ((sizeof(Elf32_Nhdr) + 3) / 4 + (name_size + 3) / 4 + (descsz + 3) / 4) * 4; - note = g_malloc(note_size); - - memset(note, 0, note_size); + note = g_malloc0(note_size); note->n_namesz = cpu_to_le32(name_size); note->n_descsz = cpu_to_le32(descsz); note->n_type = cpu_to_le32(NT_PRSTATUS); @@ -345,9 +339,7 @@ static inline int cpu_write_qemu_note(WriteCoreDumpFunction f, } note_size = ((note_head_size + 3) / 4 + (name_size + 3) / 4 + (descsz + 3) / 4) * 4; - note = g_malloc(note_size); - - memset(note, 0, note_size); + note = g_malloc0(note_size); if (type == 0) { note32 = note; note32->n_namesz = cpu_to_le32(name_size); diff --git a/target-i386/cpu-qom.h b/target-i386/cpu-qom.h index b557b61..31a0c1e 100644 --- a/target-i386/cpu-qom.h +++ b/target-i386/cpu-qom.h @@ -93,6 +93,7 @@ typedef struct X86CPU { bool expose_kvm; bool migratable; bool host_features; + int64_t apic_id; /* if true the CPUID code directly forward host cache leaves to the guest */ bool cache_info_passthrough; diff --git a/target-i386/cpu.c b/target-i386/cpu.c index e9df33e..03b33cf 100644 --- a/target-i386/cpu.c +++ b/target-i386/cpu.c @@ -25,7 +25,6 @@ #include "sysemu/kvm.h" #include "sysemu/cpus.h" #include "kvm_i386.h" -#include "topology.h" #include "qemu/option.h" #include "qemu/config-file.h" @@ -274,6 +273,17 @@ static const char *cpuid_apm_edx_feature_name[] = { NULL, NULL, NULL, NULL, }; +static const char *cpuid_xsave_feature_name[] = { + "xsaveopt", "xsavec", "xgetbv1", "xsaves", + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, +}; + #define I486_FEATURES (CPUID_FP87 | CPUID_VME | CPUID_PSE) #define PENTIUM_FEATURES (I486_FEATURES | CPUID_DE | CPUID_TSC | \ CPUID_MSR | CPUID_MCE | CPUID_CX8 | CPUID_MMX | CPUID_APIC) @@ -391,6 +401,13 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { .tcg_features = TCG_APM_FEATURES, .unmigratable_flags = CPUID_APM_INVTSC, }, + [FEAT_XSAVE] = { + .feat_names = cpuid_xsave_feature_name, + .cpuid_eax = 0xd, + .cpuid_needs_ecx = true, .cpuid_ecx = 1, + .cpuid_reg = R_EAX, + .tcg_features = 0, + }, }; typedef struct X86RegisterInfo32 { @@ -742,9 +759,9 @@ static X86CPUDefinition builtin_x86_defs[] = { .family = 15, .model = 6, .stepping = 1, - /* Missing: CPUID_VME, CPUID_HT */ + /* Missing: CPUID_HT */ .features[FEAT_1_EDX] = - PPRO_FEATURES | + PPRO_FEATURES | CPUID_VME | CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | CPUID_PSE36, /* Missing: CPUID_EXT_POPCNT, CPUID_EXT_MONITOR */ @@ -784,7 +801,7 @@ static X86CPUDefinition builtin_x86_defs[] = { .model = 6, .stepping = 1, .features[FEAT_1_EDX] = - PPRO_FEATURES | + PPRO_FEATURES | CPUID_VME | CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | CPUID_PSE36, .features[FEAT_1_ECX] = CPUID_EXT_SSE3, @@ -910,7 +927,7 @@ static X86CPUDefinition builtin_x86_defs[] = { .model = 15, .stepping = 3, .features[FEAT_1_EDX] = - CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | + CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | @@ -932,7 +949,7 @@ static X86CPUDefinition builtin_x86_defs[] = { .model = 23, .stepping = 3, .features[FEAT_1_EDX] = - CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | + CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | @@ -955,7 +972,7 @@ static X86CPUDefinition builtin_x86_defs[] = { .model = 26, .stepping = 3, .features[FEAT_1_EDX] = - CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | + CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | @@ -978,7 +995,7 @@ static X86CPUDefinition builtin_x86_defs[] = { .model = 44, .stepping = 1, .features[FEAT_1_EDX] = - CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | + CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | @@ -1002,7 +1019,7 @@ static X86CPUDefinition builtin_x86_defs[] = { .model = 42, .stepping = 1, .features[FEAT_1_EDX] = - CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | + CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | @@ -1018,10 +1035,77 @@ static X86CPUDefinition builtin_x86_defs[] = { CPUID_EXT2_SYSCALL, .features[FEAT_8000_0001_ECX] = CPUID_EXT3_LAHF_LM, + .features[FEAT_XSAVE] = + CPUID_XSAVE_XSAVEOPT, .xlevel = 0x8000000A, .model_id = "Intel Xeon E312xx (Sandy Bridge)", }, { + .name = "IvyBridge", + .level = 0xd, + .vendor = CPUID_VENDOR_INTEL, + .family = 6, + .model = 58, + .stepping = 9, + .features[FEAT_1_EDX] = + CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | + CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | + CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | + CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | + CPUID_DE | CPUID_FP87, + .features[FEAT_1_ECX] = + CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES | + CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_POPCNT | + CPUID_EXT_X2APIC | CPUID_EXT_SSE42 | CPUID_EXT_SSE41 | + CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | CPUID_EXT_PCLMULQDQ | + CPUID_EXT_SSE3 | CPUID_EXT_F16C | CPUID_EXT_RDRAND, + .features[FEAT_7_0_EBX] = + CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_SMEP | + CPUID_7_0_EBX_ERMS, + .features[FEAT_8000_0001_EDX] = + CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX | + CPUID_EXT2_SYSCALL, + .features[FEAT_8000_0001_ECX] = + CPUID_EXT3_LAHF_LM, + .features[FEAT_XSAVE] = + CPUID_XSAVE_XSAVEOPT, + .xlevel = 0x8000000A, + .model_id = "Intel Xeon E3-12xx v2 (Ivy Bridge)", + }, + { + .name = "Haswell-noTSX", + .level = 0xd, + .vendor = CPUID_VENDOR_INTEL, + .family = 6, + .model = 60, + .stepping = 1, + .features[FEAT_1_EDX] = + CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | + CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | + CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | + CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | + CPUID_DE | CPUID_FP87, + .features[FEAT_1_ECX] = + CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES | + CPUID_EXT_POPCNT | CPUID_EXT_X2APIC | CPUID_EXT_SSE42 | + CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | + CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3 | + CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_FMA | CPUID_EXT_MOVBE | + CPUID_EXT_PCID | CPUID_EXT_F16C | CPUID_EXT_RDRAND, + .features[FEAT_8000_0001_EDX] = + CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX | + CPUID_EXT2_SYSCALL, + .features[FEAT_8000_0001_ECX] = + CPUID_EXT3_LAHF_LM, + .features[FEAT_7_0_EBX] = + CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | + CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP | + CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID, + .features[FEAT_XSAVE] = + CPUID_XSAVE_XSAVEOPT, + .xlevel = 0x8000000A, + .model_id = "Intel Core Processor (Haswell, no TSX)", + }, { .name = "Haswell", .level = 0xd, .vendor = CPUID_VENDOR_INTEL, @@ -1029,7 +1113,7 @@ static X86CPUDefinition builtin_x86_defs[] = { .model = 60, .stepping = 1, .features[FEAT_1_EDX] = - CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | + CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | @@ -1040,7 +1124,7 @@ static X86CPUDefinition builtin_x86_defs[] = { CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3 | CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_FMA | CPUID_EXT_MOVBE | - CPUID_EXT_PCID, + CPUID_EXT_PCID | CPUID_EXT_F16C | CPUID_EXT_RDRAND, .features[FEAT_8000_0001_EDX] = CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX | CPUID_EXT2_SYSCALL, @@ -1051,10 +1135,48 @@ static X86CPUDefinition builtin_x86_defs[] = { CPUID_7_0_EBX_HLE | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID | CPUID_7_0_EBX_RTM, + .features[FEAT_XSAVE] = + CPUID_XSAVE_XSAVEOPT, .xlevel = 0x8000000A, .model_id = "Intel Core Processor (Haswell)", }, { + .name = "Broadwell-noTSX", + .level = 0xd, + .vendor = CPUID_VENDOR_INTEL, + .family = 6, + .model = 61, + .stepping = 2, + .features[FEAT_1_EDX] = + CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | + CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | + CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | + CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | + CPUID_DE | CPUID_FP87, + .features[FEAT_1_ECX] = + CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES | + CPUID_EXT_POPCNT | CPUID_EXT_X2APIC | CPUID_EXT_SSE42 | + CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | + CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3 | + CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_FMA | CPUID_EXT_MOVBE | + CPUID_EXT_PCID | CPUID_EXT_F16C | CPUID_EXT_RDRAND, + .features[FEAT_8000_0001_EDX] = + CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX | + CPUID_EXT2_SYSCALL, + .features[FEAT_8000_0001_ECX] = + CPUID_EXT3_LAHF_LM | CPUID_EXT3_3DNOWPREFETCH, + .features[FEAT_7_0_EBX] = + CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | + CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP | + CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID | + CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX | + CPUID_7_0_EBX_SMAP, + .features[FEAT_XSAVE] = + CPUID_XSAVE_XSAVEOPT, + .xlevel = 0x8000000A, + .model_id = "Intel Core Processor (Broadwell, no TSX)", + }, + { .name = "Broadwell", .level = 0xd, .vendor = CPUID_VENDOR_INTEL, @@ -1062,7 +1184,7 @@ static X86CPUDefinition builtin_x86_defs[] = { .model = 61, .stepping = 2, .features[FEAT_1_EDX] = - CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | + CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | @@ -1073,7 +1195,7 @@ static X86CPUDefinition builtin_x86_defs[] = { CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3 | CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_FMA | CPUID_EXT_MOVBE | - CPUID_EXT_PCID, + CPUID_EXT_PCID | CPUID_EXT_F16C | CPUID_EXT_RDRAND, .features[FEAT_8000_0001_EDX] = CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX | CPUID_EXT2_SYSCALL, @@ -1085,6 +1207,8 @@ static X86CPUDefinition builtin_x86_defs[] = { CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID | CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX | CPUID_7_0_EBX_SMAP, + .features[FEAT_XSAVE] = + CPUID_XSAVE_XSAVEOPT, .xlevel = 0x8000000A, .model_id = "Intel Core Processor (Broadwell)", }, @@ -1096,7 +1220,7 @@ static X86CPUDefinition builtin_x86_defs[] = { .model = 6, .stepping = 1, .features[FEAT_1_EDX] = - CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | + CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | @@ -1121,7 +1245,7 @@ static X86CPUDefinition builtin_x86_defs[] = { .model = 6, .stepping = 1, .features[FEAT_1_EDX] = - CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | + CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | @@ -1149,7 +1273,7 @@ static X86CPUDefinition builtin_x86_defs[] = { .model = 6, .stepping = 1, .features[FEAT_1_EDX] = - CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | + CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | @@ -1179,7 +1303,7 @@ static X86CPUDefinition builtin_x86_defs[] = { .model = 1, .stepping = 2, .features[FEAT_1_EDX] = - CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | + CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | @@ -1202,6 +1326,7 @@ static X86CPUDefinition builtin_x86_defs[] = { CPUID_EXT3_3DNOWPREFETCH | CPUID_EXT3_MISALIGNSSE | CPUID_EXT3_SSE4A | CPUID_EXT3_ABM | CPUID_EXT3_SVM | CPUID_EXT3_LAHF_LM, + /* no xsaveopt! */ .xlevel = 0x8000001A, .model_id = "AMD Opteron 62xx class CPU", }, @@ -1213,7 +1338,7 @@ static X86CPUDefinition builtin_x86_defs[] = { .model = 2, .stepping = 0, .features[FEAT_1_EDX] = - CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | + CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | @@ -1236,6 +1361,7 @@ static X86CPUDefinition builtin_x86_defs[] = { CPUID_EXT3_3DNOWPREFETCH | CPUID_EXT3_MISALIGNSSE | CPUID_EXT3_SSE4A | CPUID_EXT3_ABM | CPUID_EXT3_SVM | CPUID_EXT3_LAHF_LM, + /* no xsaveopt! */ .xlevel = 0x8000001A, .model_id = "AMD Opteron 63xx class CPU", }, @@ -1530,7 +1656,7 @@ static char *x86_cpuid_get_vendor(Object *obj, Error **errp) CPUX86State *env = &cpu->env; char *value; - value = (char *)g_malloc(CPUID_VENDOR_SZ + 1); + value = g_malloc(CPUID_VENDOR_SZ + 1); x86_cpu_vendor_words2str(value, env->cpuid_vendor1, env->cpuid_vendor2, env->cpuid_vendor3); return value; @@ -1633,7 +1759,7 @@ static void x86_cpuid_get_apic_id(Object *obj, Visitor *v, void *opaque, const char *name, Error **errp) { X86CPU *cpu = X86_CPU(obj); - int64_t value = cpu->env.cpuid_apic_id; + int64_t value = cpu->apic_id; visit_type_int(v, &value, name, errp); } @@ -1666,11 +1792,11 @@ static void x86_cpuid_set_apic_id(Object *obj, Visitor *v, void *opaque, return; } - if ((value != cpu->env.cpuid_apic_id) && cpu_exists(value)) { + if ((value != cpu->apic_id) && cpu_exists(value)) { error_setg(errp, "CPU with APIC ID %" PRIi64 " exists", value); return; } - cpu->env.cpuid_apic_id = value; + cpu->apic_id = value; } /* Generic getter for "feature-words" and "filtered-features" properties */ @@ -1854,34 +1980,19 @@ static void x86_cpu_parse_featurestr(CPUState *cs, char *features, } } -/* generate a composite string into buf of all cpuid names in featureset - * selected by fbits. indicate truncation at bufsize in the event of overflow. - * if flags, suppress names undefined in featureset. +/* Print all cpuid feature names in featureset */ -static void listflags(char *buf, int bufsize, uint32_t fbits, - const char **featureset, uint32_t flags) -{ - const char **p = &featureset[31]; - char *q, *b, bit; - int nc; - - b = 4 <= bufsize ? buf + (bufsize -= 3) - 1 : NULL; - *buf = '\0'; - for (q = buf, bit = 31; fbits && bufsize; --p, fbits &= ~(1 << bit), --bit) - if (fbits & 1 << bit && (*p || !flags)) { - if (*p) - nc = snprintf(q, bufsize, "%s%s", q == buf ? "" : " ", *p); - else - nc = snprintf(q, bufsize, "%s[%d]", q == buf ? "" : " ", bit); - if (bufsize <= nc) { - if (b) { - memcpy(b, "...", sizeof("...")); - } - return; - } - q += nc; - bufsize -= nc; +static void listflags(FILE *f, fprintf_function print, const char **featureset) +{ + int bit; + bool first = true; + + for (bit = 0; bit < 32; bit++) { + if (featureset[bit]) { + print(f, "%s%s", first ? "" : " ", featureset[bit]); + first = false; } + } } /* generate CPU information. */ @@ -1906,8 +2017,9 @@ void x86_cpu_list(FILE *f, fprintf_function cpu_fprintf) for (i = 0; i < ARRAY_SIZE(feature_word_info); i++) { FeatureWordInfo *fw = &feature_word_info[i]; - listflags(buf, sizeof(buf), (uint32_t)~0, fw->feat_names, 1); - (*cpu_fprintf)(f, " %s\n", buf); + (*cpu_fprintf)(f, " "); + listflags(f, cpu_fprintf, fw->feat_names); + (*cpu_fprintf)(f, "\n"); } } @@ -2034,8 +2146,7 @@ static void x86_cpu_load_def(X86CPU *cpu, X86CPUDefinition *def, Error **errp) } -X86CPU *cpu_x86_create(const char *cpu_model, DeviceState *icc_bridge, - Error **errp) +X86CPU *cpu_x86_create(const char *cpu_model, Error **errp) { X86CPU *cpu = NULL; X86CPUClass *xcc; @@ -2066,15 +2177,6 @@ X86CPU *cpu_x86_create(const char *cpu_model, DeviceState *icc_bridge, cpu = X86_CPU(object_new(object_class_get_name(oc))); -#ifndef CONFIG_USER_ONLY - if (icc_bridge == NULL) { - error_setg(&error, "Invalid icc-bridge value"); - goto out; - } - qdev_set_parent_bus(DEVICE(cpu), qdev_get_child_bus(icc_bridge, "icc")); - object_unref(OBJECT(cpu)); -#endif - x86_cpu_parse_featurestr(CPU(cpu), features, &error); if (error) { goto out; @@ -2097,7 +2199,7 @@ X86CPU *cpu_x86_init(const char *cpu_model) Error *error = NULL; X86CPU *cpu; - cpu = cpu_x86_create(cpu_model, NULL, &error); + cpu = cpu_x86_create(cpu_model, &error); if (error) { goto out; } @@ -2106,8 +2208,7 @@ X86CPU *cpu_x86_init(const char *cpu_model) out: if (error) { - error_report("%s", error_get_pretty(error)); - error_free(error); + error_report_err(error); if (cpu != NULL) { object_unref(OBJECT(cpu)); cpu = NULL; @@ -2171,14 +2272,6 @@ void x86_cpudef_setup(void) } } -static void get_cpuid_vendor(CPUX86State *env, uint32_t *ebx, - uint32_t *ecx, uint32_t *edx) -{ - *ebx = env->cpuid_vendor1; - *edx = env->cpuid_vendor2; - *ecx = env->cpuid_vendor3; -} - void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) @@ -2212,11 +2305,14 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, switch(index) { case 0: *eax = env->cpuid_level; - get_cpuid_vendor(env, ebx, ecx, edx); + *ebx = env->cpuid_vendor1; + *edx = env->cpuid_vendor2; + *ecx = env->cpuid_vendor3; break; case 1: *eax = env->cpuid_version; - *ebx = (env->cpuid_apic_id << 24) | 8 << 8; /* CLFLUSH size in quad words, Linux wants it. */ + *ebx = (cpu->apic_id << 24) | + 8 << 8; /* CLFLUSH size in quad words, Linux wants it. */ *ecx = env->features[FEAT_1_ECX]; *edx = env->features[FEAT_1_EDX]; if (cs->nr_cores * cs->nr_threads > 1) { @@ -2377,7 +2473,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *eax |= kvm_mask & (XSTATE_FP | XSTATE_SSE); *ebx = *ecx; } else if (count == 1) { - *eax = kvm_arch_get_supported_cpuid(s, 0xd, 1, R_EAX); + *eax = env->features[FEAT_XSAVE]; } else if (count < ARRAY_SIZE(ext_save_areas)) { const ExtSaveArea *esa = &ext_save_areas[count]; if ((env->features[esa->feature] & esa->bits) == esa->bits && @@ -2405,11 +2501,9 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, * So dont set it here for Intel to make Linux guests happy. */ if (cs->nr_cores * cs->nr_threads > 1) { - uint32_t tebx, tecx, tedx; - get_cpuid_vendor(env, &tebx, &tecx, &tedx); - if (tebx != CPUID_VENDOR_INTEL_1 || - tedx != CPUID_VENDOR_INTEL_2 || - tecx != CPUID_VENDOR_INTEL_3) { + if (env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1 || + env->cpuid_vendor2 != CPUID_VENDOR_INTEL_2 || + env->cpuid_vendor3 != CPUID_VENDOR_INTEL_3) { *ecx |= 1 << 1; /* CmpLegacy bit */ } } @@ -2620,9 +2714,7 @@ static void x86_cpu_reset(CPUState *s) #if !defined(CONFIG_USER_ONLY) /* We hard-wire the BSP to the first CPU. */ - if (s->cpu_index == 0) { - apic_designate_bsp(cpu->apic_state); - } + apic_designate_bsp(cpu->apic_state, s->cpu_index == 0); s->halted = !cpu_is_bsp(cpu); @@ -2665,7 +2757,6 @@ static void mce_init(X86CPU *cpu) #ifndef CONFIG_USER_ONLY static void x86_cpu_apic_create(X86CPU *cpu, Error **errp) { - CPUX86State *env = &cpu->env; DeviceState *dev = DEVICE(cpu); APICCommonState *apic; const char *apic_type = "apic"; @@ -2684,7 +2775,7 @@ static void x86_cpu_apic_create(X86CPU *cpu, Error **errp) object_property_add_child(OBJECT(cpu), "apic", OBJECT(cpu->apic_state), NULL); - qdev_prop_set_uint8(cpu->apic_state, "id", env->cpuid_apic_id); + qdev_prop_set_uint8(cpu->apic_state, "id", cpu->apic_id); /* TODO: convert to link<> */ apic = APIC_COMMON(cpu->apic_state); apic->cpu = cpu; @@ -2695,12 +2786,8 @@ static void x86_cpu_apic_realize(X86CPU *cpu, Error **errp) if (cpu->apic_state == NULL) { return; } - - if (qdev_init(cpu->apic_state)) { - error_setg(errp, "APIC device '%s' could not be initialized", - object_get_typename(OBJECT(cpu->apic_state))); - return; - } + object_property_set_bool(OBJECT(cpu->apic_state), true, "realized", + errp); } #else static void x86_cpu_apic_realize(X86CPU *cpu, Error **errp) @@ -2724,6 +2811,11 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) Error *local_err = NULL; static bool ht_warned; + if (cpu->apic_id < 0) { + error_setg(errp, "apic-id property was not initialized properly"); + return; + } + if (env->features[FEAT_7_0_EBX] && env->cpuid_level < 7) { env->cpuid_level = 7; } @@ -2788,39 +2880,6 @@ out: } } -/* 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. - */ -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) { - 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 x86_cpu_initfn(Object *obj) { CPUState *cs = CPU(obj); @@ -2867,7 +2926,11 @@ static void x86_cpu_initfn(Object *obj) NULL, NULL, (void *)cpu->filtered_features, NULL); cpu->hyperv_spinlock_attempts = HYPERV_SPINLOCK_NEVER_RETRY; - env->cpuid_apic_id = x86_cpu_apic_id_from_index(cs->cpu_index); + +#ifndef CONFIG_USER_ONLY + /* Any code creating new X86CPU objects have to set apic-id explicitly */ + cpu->apic_id = -1; +#endif x86_cpu_load_def(cpu, xcc->cpu_def, &error_abort); @@ -2881,9 +2944,8 @@ static void x86_cpu_initfn(Object *obj) static int64_t x86_cpu_get_arch_id(CPUState *cs) { X86CPU *cpu = X86_CPU(cs); - CPUX86State *env = &cpu->env; - return env->cpuid_apic_id; + return cpu->apic_id; } static bool x86_cpu_get_paging_enabled(const CPUState *cs) diff --git a/target-i386/cpu.h b/target-i386/cpu.h index 015f5b5..4ee12ca 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -28,14 +28,13 @@ #define TARGET_LONG_BITS 32 #endif -/* target supports implicit self modifying code */ -#define TARGET_HAS_SMC +/* Maximum instruction code size */ +#define TARGET_MAX_INSN_SIZE 16 + /* support for self modifying code even if the modified instruction is close to the modifying instruction */ #define TARGET_HAS_PRECISE_SMC -#define TARGET_HAS_ICE 1 - #ifdef TARGET_X86_64 #define ELF_MACHINE EM_X86_64 #define ELF_MACHINE_UNAME "x86_64" @@ -389,6 +388,7 @@ #define MSR_VM_HSAVE_PA 0xc0010117 #define MSR_IA32_BNDCFGS 0x00000d90 +#define MSR_IA32_XSS 0x00000da0 #define XSTATE_FP (1ULL << 0) #define XSTATE_SSE (1ULL << 1) @@ -411,6 +411,7 @@ typedef enum FeatureWord { FEAT_C000_0001_EDX, /* CPUID[C000_0001].EDX */ FEAT_KVM, /* CPUID[4000_0001].EAX (KVM_CPUID_FEATURES) */ FEAT_SVM, /* CPUID[8000_000A].EDX */ + FEAT_XSAVE, /* CPUID[EAX=0xd,ECX=1].EAX */ FEATURE_WORDS, } FeatureWord; @@ -571,6 +572,11 @@ typedef uint32_t FeatureWordArray[FEATURE_WORDS]; #define CPUID_7_0_EBX_AVX512ER (1U << 27) /* AVX-512 Exponential and Reciprocal */ #define CPUID_7_0_EBX_AVX512CD (1U << 28) /* AVX-512 Conflict Detection */ +#define CPUID_XSAVE_XSAVEOPT (1U << 0) +#define CPUID_XSAVE_XSAVEC (1U << 1) +#define CPUID_XSAVE_XGETBV1 (1U << 2) +#define CPUID_XSAVE_XSAVES (1U << 3) + /* CPUID[0x80000007].EDX flags: */ #define CPUID_APM_INVTSC (1U << 8) @@ -705,31 +711,13 @@ typedef struct SegmentCache { } SegmentCache; typedef union { - uint8_t _b[16]; - uint16_t _w[8]; - uint32_t _l[4]; - uint64_t _q[2]; - float32 _s[4]; - float64 _d[2]; -} XMMReg; - -typedef union { - uint8_t _b[32]; - uint16_t _w[16]; - uint32_t _l[8]; - uint64_t _q[4]; - float32 _s[8]; - float64 _d[4]; -} YMMReg; - -typedef union { uint8_t _b[64]; uint16_t _w[32]; uint32_t _l[16]; uint64_t _q[8]; float32 _s[16]; float64 _d[8]; -} ZMMReg; +} XMMReg; /* really zmm */ typedef union { uint8_t _b[8]; @@ -750,46 +738,18 @@ typedef struct BNDCSReg { } BNDCSReg; #ifdef HOST_WORDS_BIGENDIAN -#define ZMM_B(n) _b[63 - (n)] -#define ZMM_W(n) _w[31 - (n)] -#define ZMM_L(n) _l[15 - (n)] -#define ZMM_S(n) _s[15 - (n)] -#define ZMM_Q(n) _q[7 - (n)] -#define ZMM_D(n) _d[7 - (n)] - -#define YMM_B(n) _b[31 - (n)] -#define YMM_W(n) _w[15 - (n)] -#define YMM_L(n) _l[7 - (n)] -#define YMM_S(n) _s[7 - (n)] -#define YMM_Q(n) _q[3 - (n)] -#define YMM_D(n) _d[3 - (n)] - -#define XMM_B(n) _b[15 - (n)] -#define XMM_W(n) _w[7 - (n)] -#define XMM_L(n) _l[3 - (n)] -#define XMM_S(n) _s[3 - (n)] -#define XMM_Q(n) _q[1 - (n)] -#define XMM_D(n) _d[1 - (n)] +#define XMM_B(n) _b[63 - (n)] +#define XMM_W(n) _w[31 - (n)] +#define XMM_L(n) _l[15 - (n)] +#define XMM_S(n) _s[15 - (n)] +#define XMM_Q(n) _q[7 - (n)] +#define XMM_D(n) _d[7 - (n)] #define MMX_B(n) _b[7 - (n)] #define MMX_W(n) _w[3 - (n)] #define MMX_L(n) _l[1 - (n)] #define MMX_S(n) _s[1 - (n)] #else -#define ZMM_B(n) _b[n] -#define ZMM_W(n) _w[n] -#define ZMM_L(n) _l[n] -#define ZMM_S(n) _s[n] -#define ZMM_Q(n) _q[n] -#define ZMM_D(n) _d[n] - -#define YMM_B(n) _b[n] -#define YMM_W(n) _w[n] -#define YMM_L(n) _l[n] -#define YMM_S(n) _s[n] -#define YMM_Q(n) _q[n] -#define YMM_D(n) _d[n] - #define XMM_B(n) _b[n] #define XMM_W(n) _w[n] #define XMM_L(n) _l[n] @@ -888,17 +848,11 @@ typedef struct CPUX86State { float_status mmx_status; /* for 3DNow! float ops */ float_status sse_status; uint32_t mxcsr; - XMMReg xmm_regs[CPU_NB_REGS]; + XMMReg xmm_regs[CPU_NB_REGS == 8 ? 8 : 32]; XMMReg xmm_t0; MMXReg mmx_t0; - XMMReg ymmh_regs[CPU_NB_REGS]; - uint64_t opmask_regs[NB_OPMASK_REGS]; - YMMReg zmmh_regs[CPU_NB_REGS]; -#ifdef TARGET_X86_64 - ZMMReg hi16_zmm_regs[CPU_NB_REGS]; -#endif /* sysenter registers */ uint32_t sysenter_cs; @@ -988,7 +942,6 @@ typedef struct CPUX86State { uint32_t cpuid_version; FeatureWordArray features; uint32_t cpuid_model[12]; - uint32_t cpuid_apic_id; /* MTRRs */ uint64_t mtrr_fixed[11]; @@ -1019,6 +972,7 @@ typedef struct CPUX86State { uint64_t xstate_bv; uint64_t xcr0; + uint64_t xss; TPRAccess tpr_access_type; } CPUX86State; @@ -1026,8 +980,7 @@ typedef struct CPUX86State { #include "cpu-qom.h" X86CPU *cpu_x86_init(const char *cpu_model); -X86CPU *cpu_x86_create(const char *cpu_model, DeviceState *icc_bridge, - Error **errp); +X86CPU *cpu_x86_create(const char *cpu_model, Error **errp); int cpu_x86_exec(CPUX86State *s); void x86_cpu_list(FILE *f, fprintf_function cpu_fprintf); void x86_cpudef_setup(void); @@ -1214,14 +1167,7 @@ uint64_t cpu_get_tsc(CPUX86State *env); # define PHYS_ADDR_MASK 0xfffffffffLL # endif -static inline CPUX86State *cpu_init(const char *cpu_model) -{ - X86CPU *cpu = cpu_x86_init(cpu_model); - if (cpu == NULL) { - return NULL; - } - return &cpu->env; -} +#define cpu_init(cpu_model) CPU(cpu_x86_init(cpu_model)) #define cpu_exec cpu_x86_exec #define cpu_gen_code cpu_x86_gen_code @@ -1372,7 +1318,6 @@ void x86_cpu_compat_kvm_no_autodisable(FeatureWord w, uint32_t features); /* Return name of 32-bit register, from a R_* constant */ const char *get_register_name_32(unsigned int reg); -uint32_t x86_cpu_apic_id_from_index(unsigned int cpu_index); void enable_compat_apic_id_mode(void); #define APIC_DEFAULT_ADDRESS 0xfee00000 diff --git a/target-i386/fpu_helper.c b/target-i386/fpu_helper.c index 1d4eee3..30d34d5 100644 --- a/target-i386/fpu_helper.c +++ b/target-i386/fpu_helper.c @@ -251,16 +251,34 @@ int32_t helper_fist_ST0(CPUX86State *env) int32_t helper_fistl_ST0(CPUX86State *env) { int32_t val; + signed char old_exp_flags; + + old_exp_flags = get_float_exception_flags(&env->fp_status); + set_float_exception_flags(0, &env->fp_status); val = floatx80_to_int32(ST0, &env->fp_status); + if (get_float_exception_flags(&env->fp_status) & float_flag_invalid) { + val = 0x80000000; + } + set_float_exception_flags(get_float_exception_flags(&env->fp_status) + | old_exp_flags, &env->fp_status); return val; } int64_t helper_fistll_ST0(CPUX86State *env) { int64_t val; + signed char old_exp_flags; - val = floatx80_to_int64(ST0, &env->fp_status); + old_exp_flags = get_float_exception_flags(&env->fp_status); + set_float_exception_flags(0, &env->fp_status); + + val = floatx80_to_int32(ST0, &env->fp_status); + if (get_float_exception_flags(&env->fp_status) & float_flag_invalid) { + val = 0x8000000000000000ULL; + } + set_float_exception_flags(get_float_exception_flags(&env->fp_status) + | old_exp_flags, &env->fp_status); return val; } @@ -621,7 +639,7 @@ void helper_fbld_ST0(CPUX86State *env, target_ulong ptr) } tmp = int64_to_floatx80(val, &env->fp_status); if (cpu_ldub_data(env, ptr + 9) & 0x80) { - floatx80_chs(tmp); + tmp = floatx80_chs(tmp); } fpush(env); ST0 = tmp; diff --git a/target-i386/helper.c b/target-i386/helper.c index 345bda1..4f1ddf7 100644 --- a/target-i386/helper.c +++ b/target-i386/helper.c @@ -25,8 +25,6 @@ #include "monitor/monitor.h" #endif -//#define DEBUG_MMU - static void cpu_x86_version(CPUX86State *env, int *family, int *model) { int cpuver = env->cpuid_version; @@ -388,9 +386,7 @@ void x86_cpu_set_a20(X86CPU *cpu, int a20_state) if (a20_state != ((env->a20_mask >> 20) & 1)) { CPUState *cs = CPU(cpu); -#if defined(DEBUG_MMU) - printf("A20 update: a20=%d\n", a20_state); -#endif + qemu_log_mask(CPU_LOG_MMU, "A20 update: a20=%d\n", a20_state); /* if the cpu is currently executing code, we must unlink it and all the potentially executing TB */ cpu_interrupt(cs, CPU_INTERRUPT_EXITTB); @@ -407,9 +403,7 @@ void cpu_x86_update_cr0(CPUX86State *env, uint32_t new_cr0) X86CPU *cpu = x86_env_get_cpu(env); int pe_state; -#if defined(DEBUG_MMU) - printf("CR0 update: CR0=0x%08x\n", new_cr0); -#endif + qemu_log_mask(CPU_LOG_MMU, "CR0 update: CR0=0x%08x\n", new_cr0); if ((new_cr0 & (CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK)) != (env->cr[0] & (CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK))) { tlb_flush(CPU(cpu), 1); @@ -452,9 +446,8 @@ void cpu_x86_update_cr3(CPUX86State *env, target_ulong new_cr3) env->cr[3] = new_cr3; if (env->cr[0] & CR0_PG_MASK) { -#if defined(DEBUG_MMU) - printf("CR3 update: CR3=" TARGET_FMT_lx "\n", new_cr3); -#endif + qemu_log_mask(CPU_LOG_MMU, + "CR3 update: CR3=" TARGET_FMT_lx "\n", new_cr3); tlb_flush(CPU(cpu), 0); } } diff --git a/target-i386/kvm.c b/target-i386/kvm.c index ccf36e8..41d09e5 100644 --- a/target-i386/kvm.c +++ b/target-i386/kvm.c @@ -80,6 +80,7 @@ static bool has_msr_hv_hypercall; static bool has_msr_hv_vapic; static bool has_msr_hv_tsc; static bool has_msr_mtrr; +static bool has_msr_xss; static bool has_msr_architectural_pmu; static uint32_t num_architectural_pmu_counters; @@ -95,7 +96,7 @@ static struct kvm_cpuid2 *try_get_cpuid(KVMState *s, int max) int r, size; size = sizeof(*cpuid) + max * sizeof(*cpuid->entries); - cpuid = (struct kvm_cpuid2 *)g_malloc0(size); + cpuid = g_malloc0(size); cpuid->nent = max; r = kvm_ioctl(s, KVM_GET_SUPPORTED_CPUID, cpuid); if (r == 0 && cpuid->nent >= max) { @@ -277,7 +278,7 @@ static void kvm_hwpoison_page_add(ram_addr_t ram_addr) return; } } - page = g_malloc(sizeof(HWPoisonPage)); + page = g_new(HWPoisonPage, 1); page->ram_addr = ram_addr; QLIST_INSERT_HEAD(&hwpoison_page_list, page, list); } @@ -429,7 +430,7 @@ static void cpu_update_state(void *opaque, int running, RunState state) unsigned long kvm_arch_vcpu_id(CPUState *cs) { X86CPU *cpu = X86_CPU(cs); - return cpu->env.cpuid_apic_id; + return cpu->apic_id; } #ifndef KVM_CPUID_SIGNATURE_NEXT @@ -826,6 +827,10 @@ static int kvm_get_supported_msrs(KVMState *s) has_msr_bndcfgs = true; continue; } + if (kvm_msr_list->indices[i] == MSR_IA32_XSS) { + has_msr_xss = true; + continue; + } } } @@ -835,7 +840,7 @@ static int kvm_get_supported_msrs(KVMState *s) return ret; } -int kvm_arch_init(KVMState *s) +int kvm_arch_init(MachineState *ms, KVMState *s) { uint64_t identity_base = 0xfffbc000; uint64_t shadow_mem; @@ -885,8 +890,7 @@ int kvm_arch_init(KVMState *s) } qemu_register_reset(kvm_unpoison_all, NULL); - shadow_mem = qemu_opt_get_size(qemu_get_machine_opts(), - "kvm_shadow_mem", -1); + shadow_mem = machine_kvm_shadow_mem(ms); if (shadow_mem != -1) { shadow_mem /= 4096; ret = kvm_vm_ioctl(s, KVM_SET_NR_MMU_PAGES, shadow_mem); @@ -1014,7 +1018,10 @@ static int kvm_put_fpu(X86CPU *cpu) fpu.ftwx |= (!env->fptags[i]) << i; } memcpy(fpu.fpr, env->fpregs, sizeof env->fpregs); - memcpy(fpu.xmm, env->xmm_regs, sizeof env->xmm_regs); + for (i = 0; i < CPU_NB_REGS; i++) { + stq_p(&fpu.xmm[i][0], env->xmm_regs[i].XMM_Q(0)); + stq_p(&fpu.xmm[i][8], env->xmm_regs[i].XMM_Q(1)); + } fpu.mxcsr = env->mxcsr; return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_FPU, &fpu); @@ -1040,6 +1047,7 @@ static int kvm_put_xsave(X86CPU *cpu) CPUX86State *env = &cpu->env; struct kvm_xsave* xsave = env->kvm_xsave_buf; uint16_t cwd, swd, twd; + uint8_t *xmm, *ymmh, *zmmh; int i, r; if (!kvm_has_xsave()) { @@ -1060,23 +1068,32 @@ static int kvm_put_xsave(X86CPU *cpu) memcpy(&xsave->region[XSAVE_CWD_RDP], &env->fpdp, sizeof(env->fpdp)); memcpy(&xsave->region[XSAVE_ST_SPACE], env->fpregs, sizeof env->fpregs); - memcpy(&xsave->region[XSAVE_XMM_SPACE], env->xmm_regs, - sizeof env->xmm_regs); xsave->region[XSAVE_MXCSR] = env->mxcsr; *(uint64_t *)&xsave->region[XSAVE_XSTATE_BV] = env->xstate_bv; - memcpy(&xsave->region[XSAVE_YMMH_SPACE], env->ymmh_regs, - sizeof env->ymmh_regs); memcpy(&xsave->region[XSAVE_BNDREGS], env->bnd_regs, sizeof env->bnd_regs); memcpy(&xsave->region[XSAVE_BNDCSR], &env->bndcs_regs, sizeof(env->bndcs_regs)); memcpy(&xsave->region[XSAVE_OPMASK], env->opmask_regs, sizeof env->opmask_regs); - memcpy(&xsave->region[XSAVE_ZMM_Hi256], env->zmmh_regs, - sizeof env->zmmh_regs); + + xmm = (uint8_t *)&xsave->region[XSAVE_XMM_SPACE]; + ymmh = (uint8_t *)&xsave->region[XSAVE_YMMH_SPACE]; + zmmh = (uint8_t *)&xsave->region[XSAVE_ZMM_Hi256]; + for (i = 0; i < CPU_NB_REGS; i++, xmm += 16, ymmh += 16, zmmh += 32) { + stq_p(xmm, env->xmm_regs[i].XMM_Q(0)); + stq_p(xmm+8, env->xmm_regs[i].XMM_Q(1)); + stq_p(ymmh, env->xmm_regs[i].XMM_Q(2)); + stq_p(ymmh+8, env->xmm_regs[i].XMM_Q(3)); + stq_p(zmmh, env->xmm_regs[i].XMM_Q(4)); + stq_p(zmmh+8, env->xmm_regs[i].XMM_Q(5)); + stq_p(zmmh+16, env->xmm_regs[i].XMM_Q(6)); + stq_p(zmmh+24, env->xmm_regs[i].XMM_Q(7)); + } + #ifdef TARGET_X86_64 - memcpy(&xsave->region[XSAVE_Hi16_ZMM], env->hi16_zmm_regs, - sizeof env->hi16_zmm_regs); + memcpy(&xsave->region[XSAVE_Hi16_ZMM], &env->xmm_regs[16], + 16 * sizeof env->xmm_regs[16]); #endif r = kvm_vcpu_ioctl(CPU(cpu), KVM_SET_XSAVE, xsave); return r; @@ -1085,7 +1102,7 @@ static int kvm_put_xsave(X86CPU *cpu) static int kvm_put_xcrs(X86CPU *cpu) { CPUX86State *env = &cpu->env; - struct kvm_xcrs xcrs; + struct kvm_xcrs xcrs = {}; if (!kvm_has_xcrs()) { return 0; @@ -1152,6 +1169,7 @@ static void kvm_msr_entry_set(struct kvm_msr_entry *entry, uint32_t index, uint64_t value) { entry->index = index; + entry->reserved = 0; entry->data = value; } @@ -1170,7 +1188,9 @@ static int kvm_put_tscdeadline_msr(X86CPU *cpu) kvm_msr_entry_set(&msrs[0], MSR_IA32_TSCDEADLINE, env->tsc_deadline); - msr_data.info.nmsrs = 1; + msr_data.info = (struct kvm_msrs) { + .nmsrs = 1, + }; return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_MSRS, &msr_data); } @@ -1190,7 +1210,11 @@ static int kvm_put_msr_feature_control(X86CPU *cpu) kvm_msr_entry_set(&msr_data.entry, MSR_IA32_FEATURE_CONTROL, cpu->env.msr_ia32_feature_control); - msr_data.info.nmsrs = 1; + + msr_data.info = (struct kvm_msrs) { + .nmsrs = 1, + }; + return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_MSRS, &msr_data); } @@ -1224,6 +1248,9 @@ static int kvm_put_msrs(X86CPU *cpu, int level) if (has_msr_bndcfgs) { kvm_msr_entry_set(&msrs[n++], MSR_IA32_BNDCFGS, env->msr_bndcfgs); } + if (has_msr_xss) { + kvm_msr_entry_set(&msrs[n++], MSR_IA32_XSS, env->xss); + } #ifdef TARGET_X86_64 if (lm_capable_kernel) { kvm_msr_entry_set(&msrs[n++], MSR_CSTAR, env->cstar); @@ -1339,7 +1366,9 @@ static int kvm_put_msrs(X86CPU *cpu, int level) } } - msr_data.info.nmsrs = n; + msr_data.info = (struct kvm_msrs) { + .nmsrs = n, + }; return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_MSRS, &msr_data); @@ -1367,7 +1396,10 @@ static int kvm_get_fpu(X86CPU *cpu) env->fptags[i] = !((fpu.ftwx >> i) & 1); } memcpy(env->fpregs, fpu.fpr, sizeof env->fpregs); - memcpy(env->xmm_regs, fpu.xmm, sizeof env->xmm_regs); + for (i = 0; i < CPU_NB_REGS; i++) { + env->xmm_regs[i].XMM_Q(0) = ldq_p(&fpu.xmm[i][0]); + env->xmm_regs[i].XMM_Q(1) = ldq_p(&fpu.xmm[i][8]); + } env->mxcsr = fpu.mxcsr; return 0; @@ -1378,6 +1410,7 @@ static int kvm_get_xsave(X86CPU *cpu) CPUX86State *env = &cpu->env; struct kvm_xsave* xsave = env->kvm_xsave_buf; int ret, i; + const uint8_t *xmm, *ymmh, *zmmh; uint16_t cwd, swd, twd; if (!kvm_has_xsave()) { @@ -1404,22 +1437,31 @@ static int kvm_get_xsave(X86CPU *cpu) env->mxcsr = xsave->region[XSAVE_MXCSR]; memcpy(env->fpregs, &xsave->region[XSAVE_ST_SPACE], sizeof env->fpregs); - memcpy(env->xmm_regs, &xsave->region[XSAVE_XMM_SPACE], - sizeof env->xmm_regs); env->xstate_bv = *(uint64_t *)&xsave->region[XSAVE_XSTATE_BV]; - memcpy(env->ymmh_regs, &xsave->region[XSAVE_YMMH_SPACE], - sizeof env->ymmh_regs); memcpy(env->bnd_regs, &xsave->region[XSAVE_BNDREGS], sizeof env->bnd_regs); memcpy(&env->bndcs_regs, &xsave->region[XSAVE_BNDCSR], sizeof(env->bndcs_regs)); memcpy(env->opmask_regs, &xsave->region[XSAVE_OPMASK], sizeof env->opmask_regs); - memcpy(env->zmmh_regs, &xsave->region[XSAVE_ZMM_Hi256], - sizeof env->zmmh_regs); + + xmm = (const uint8_t *)&xsave->region[XSAVE_XMM_SPACE]; + ymmh = (const uint8_t *)&xsave->region[XSAVE_YMMH_SPACE]; + zmmh = (const uint8_t *)&xsave->region[XSAVE_ZMM_Hi256]; + for (i = 0; i < CPU_NB_REGS; i++, xmm += 16, ymmh += 16, zmmh += 32) { + env->xmm_regs[i].XMM_Q(0) = ldq_p(xmm); + env->xmm_regs[i].XMM_Q(1) = ldq_p(xmm+8); + env->xmm_regs[i].XMM_Q(2) = ldq_p(ymmh); + env->xmm_regs[i].XMM_Q(3) = ldq_p(ymmh+8); + env->xmm_regs[i].XMM_Q(4) = ldq_p(zmmh); + env->xmm_regs[i].XMM_Q(5) = ldq_p(zmmh+8); + env->xmm_regs[i].XMM_Q(6) = ldq_p(zmmh+16); + env->xmm_regs[i].XMM_Q(7) = ldq_p(zmmh+24); + } + #ifdef TARGET_X86_64 - memcpy(env->hi16_zmm_regs, &xsave->region[XSAVE_Hi16_ZMM], - sizeof env->hi16_zmm_regs); + memcpy(&env->xmm_regs[16], &xsave->region[XSAVE_Hi16_ZMM], + 16 * sizeof env->xmm_regs[16]); #endif return 0; } @@ -1570,6 +1612,10 @@ static int kvm_get_msrs(X86CPU *cpu) if (has_msr_bndcfgs) { msrs[n++].index = MSR_IA32_BNDCFGS; } + if (has_msr_xss) { + msrs[n++].index = MSR_IA32_XSS; + } + if (!env->tsc_valid) { msrs[n++].index = MSR_IA32_TSC; @@ -1646,7 +1692,10 @@ static int kvm_get_msrs(X86CPU *cpu) } } - msr_data.info.nmsrs = n; + msr_data.info = (struct kvm_msrs) { + .nmsrs = n, + }; + ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_MSRS, &msr_data); if (ret < 0) { return ret; @@ -1717,6 +1766,9 @@ static int kvm_get_msrs(X86CPU *cpu) case MSR_IA32_BNDCFGS: env->msr_bndcfgs = msrs[i].data; break; + case MSR_IA32_XSS: + env->xss = msrs[i].data; + break; default: if (msrs[i].index >= MSR_MC0_CTL && msrs[i].index < MSR_MC0_CTL + (env->mcg_cap & 0xff) * 4) { @@ -1872,7 +1924,7 @@ static int kvm_put_apic(X86CPU *cpu) static int kvm_put_vcpu_events(X86CPU *cpu, int level) { CPUX86State *env = &cpu->env; - struct kvm_vcpu_events events; + struct kvm_vcpu_events events = {}; if (!kvm_has_vcpu_events()) { return 0; @@ -2563,7 +2615,6 @@ void kvm_arch_init_irq_routing(KVMState *s) * irqchip, so we can use irqfds, and on x86 we know * we can use msi via irqfd and GSI routing. */ - kvm_irqfds_allowed = true; kvm_msi_via_irqfd_allowed = true; kvm_gsi_routing_allowed = true; } @@ -2707,3 +2758,9 @@ int kvm_device_msix_deassign(KVMState *s, uint32_t dev_id) return kvm_deassign_irq_internal(s, dev_id, KVM_DEV_IRQ_GUEST_MSIX | KVM_DEV_IRQ_HOST_MSIX); } + +int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, + uint64_t address, uint32_t data) +{ + return 0; +} diff --git a/target-i386/machine.c b/target-i386/machine.c index 1c13b14..cd1ddd2 100644 --- a/target-i386/machine.c +++ b/target-i386/machine.c @@ -42,39 +42,42 @@ static const VMStateDescription vmstate_xmm_reg = { } }; -#define VMSTATE_XMM_REGS(_field, _state, _n) \ - VMSTATE_STRUCT_ARRAY(_field, _state, _n, 0, vmstate_xmm_reg, XMMReg) +#define VMSTATE_XMM_REGS(_field, _state, _start) \ + VMSTATE_STRUCT_SUB_ARRAY(_field, _state, _start, CPU_NB_REGS, 0, \ + vmstate_xmm_reg, XMMReg) -/* YMMH format is the same as XMM */ +/* YMMH format is the same as XMM, but for bits 128-255 */ static const VMStateDescription vmstate_ymmh_reg = { .name = "ymmh_reg", .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { - VMSTATE_UINT64(XMM_Q(0), XMMReg), - VMSTATE_UINT64(XMM_Q(1), XMMReg), + VMSTATE_UINT64(XMM_Q(2), XMMReg), + VMSTATE_UINT64(XMM_Q(3), XMMReg), VMSTATE_END_OF_LIST() } }; -#define VMSTATE_YMMH_REGS_VARS(_field, _state, _n, _v) \ - VMSTATE_STRUCT_ARRAY(_field, _state, _n, _v, vmstate_ymmh_reg, XMMReg) +#define VMSTATE_YMMH_REGS_VARS(_field, _state, _start, _v) \ + VMSTATE_STRUCT_SUB_ARRAY(_field, _state, _start, CPU_NB_REGS, _v, \ + vmstate_ymmh_reg, XMMReg) static const VMStateDescription vmstate_zmmh_reg = { .name = "zmmh_reg", .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { - VMSTATE_UINT64(YMM_Q(0), YMMReg), - VMSTATE_UINT64(YMM_Q(1), YMMReg), - VMSTATE_UINT64(YMM_Q(2), YMMReg), - VMSTATE_UINT64(YMM_Q(3), YMMReg), + VMSTATE_UINT64(XMM_Q(4), XMMReg), + VMSTATE_UINT64(XMM_Q(5), XMMReg), + VMSTATE_UINT64(XMM_Q(6), XMMReg), + VMSTATE_UINT64(XMM_Q(7), XMMReg), VMSTATE_END_OF_LIST() } }; -#define VMSTATE_ZMMH_REGS_VARS(_field, _state, _n) \ - VMSTATE_STRUCT_ARRAY(_field, _state, _n, 0, vmstate_zmmh_reg, YMMReg) +#define VMSTATE_ZMMH_REGS_VARS(_field, _state, _start) \ + VMSTATE_STRUCT_SUB_ARRAY(_field, _state, _start, CPU_NB_REGS, 0, \ + vmstate_zmmh_reg, XMMReg) #ifdef TARGET_X86_64 static const VMStateDescription vmstate_hi16_zmm_reg = { @@ -82,20 +85,21 @@ static const VMStateDescription vmstate_hi16_zmm_reg = { .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { - VMSTATE_UINT64(ZMM_Q(0), ZMMReg), - VMSTATE_UINT64(ZMM_Q(1), ZMMReg), - VMSTATE_UINT64(ZMM_Q(2), ZMMReg), - VMSTATE_UINT64(ZMM_Q(3), ZMMReg), - VMSTATE_UINT64(ZMM_Q(4), ZMMReg), - VMSTATE_UINT64(ZMM_Q(5), ZMMReg), - VMSTATE_UINT64(ZMM_Q(6), ZMMReg), - VMSTATE_UINT64(ZMM_Q(7), ZMMReg), + VMSTATE_UINT64(XMM_Q(0), XMMReg), + VMSTATE_UINT64(XMM_Q(1), XMMReg), + VMSTATE_UINT64(XMM_Q(2), XMMReg), + VMSTATE_UINT64(XMM_Q(3), XMMReg), + VMSTATE_UINT64(XMM_Q(4), XMMReg), + VMSTATE_UINT64(XMM_Q(5), XMMReg), + VMSTATE_UINT64(XMM_Q(6), XMMReg), + VMSTATE_UINT64(XMM_Q(7), XMMReg), VMSTATE_END_OF_LIST() } }; -#define VMSTATE_Hi16_ZMM_REGS_VARS(_field, _state, _n) \ - VMSTATE_STRUCT_ARRAY(_field, _state, _n, 0, vmstate_hi16_zmm_reg, ZMMReg) +#define VMSTATE_Hi16_ZMM_REGS_VARS(_field, _state, _start) \ + VMSTATE_STRUCT_SUB_ARRAY(_field, _state, _start, CPU_NB_REGS, 0, \ + vmstate_hi16_zmm_reg, XMMReg) #endif static const VMStateDescription vmstate_bnd_regs = { @@ -654,17 +658,16 @@ static bool avx512_needed(void *opaque) } for (i = 0; i < CPU_NB_REGS; i++) { -#define ENV_ZMMH(reg, field) (env->zmmh_regs[reg].YMM_Q(field)) - if (ENV_ZMMH(i, 0) || ENV_ZMMH(i, 1) || - ENV_ZMMH(i, 2) || ENV_ZMMH(i, 3)) { +#define ENV_XMM(reg, field) (env->xmm_regs[reg].XMM_Q(field)) + if (ENV_XMM(i, 4) || ENV_XMM(i, 6) || + ENV_XMM(i, 5) || ENV_XMM(i, 7)) { return true; } #ifdef TARGET_X86_64 -#define ENV_Hi16_ZMM(reg, field) (env->hi16_zmm_regs[reg].ZMM_Q(field)) - if (ENV_Hi16_ZMM(i, 0) || ENV_Hi16_ZMM(i, 1) || - ENV_Hi16_ZMM(i, 2) || ENV_Hi16_ZMM(i, 3) || - ENV_Hi16_ZMM(i, 4) || ENV_Hi16_ZMM(i, 5) || - ENV_Hi16_ZMM(i, 6) || ENV_Hi16_ZMM(i, 7)) { + if (ENV_XMM(i+16, 0) || ENV_XMM(i+16, 1) || + ENV_XMM(i+16, 2) || ENV_XMM(i+16, 3) || + ENV_XMM(i+16, 4) || ENV_XMM(i+16, 5) || + ENV_XMM(i+16, 6) || ENV_XMM(i+16, 7)) { return true; } #endif @@ -679,14 +682,32 @@ static const VMStateDescription vmstate_avx512 = { .minimum_version_id = 1, .fields = (VMStateField[]) { VMSTATE_UINT64_ARRAY(env.opmask_regs, X86CPU, NB_OPMASK_REGS), - VMSTATE_ZMMH_REGS_VARS(env.zmmh_regs, X86CPU, CPU_NB_REGS), + VMSTATE_ZMMH_REGS_VARS(env.xmm_regs, X86CPU, 0), #ifdef TARGET_X86_64 - VMSTATE_Hi16_ZMM_REGS_VARS(env.hi16_zmm_regs, X86CPU, CPU_NB_REGS), + VMSTATE_Hi16_ZMM_REGS_VARS(env.xmm_regs, X86CPU, 16), #endif VMSTATE_END_OF_LIST() } }; +static bool xss_needed(void *opaque) +{ + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; + + return env->xss != 0; +} + +static const VMStateDescription vmstate_xss = { + .name = "cpu/xss", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT64(env.xss, X86CPU), + VMSTATE_END_OF_LIST() + } +}; + VMStateDescription vmstate_x86_cpu = { .name = "cpu", .version_id = 12, @@ -732,7 +753,7 @@ VMStateDescription vmstate_x86_cpu = { VMSTATE_INT32(env.a20_mask, X86CPU), /* XMM */ VMSTATE_UINT32(env.mxcsr, X86CPU), - VMSTATE_XMM_REGS(env.xmm_regs, X86CPU, CPU_NB_REGS), + VMSTATE_XMM_REGS(env.xmm_regs, X86CPU, 0), #ifdef TARGET_X86_64 VMSTATE_UINT64(env.efer, X86CPU), @@ -785,7 +806,7 @@ VMStateDescription vmstate_x86_cpu = { /* XSAVE related fields */ VMSTATE_UINT64_V(env.xcr0, X86CPU, 12), VMSTATE_UINT64_V(env.xstate_bv, X86CPU, 12), - VMSTATE_YMMH_REGS_VARS(env.ymmh_regs, X86CPU, CPU_NB_REGS, 12), + VMSTATE_YMMH_REGS_VARS(env.xmm_regs, X86CPU, 0, 12), VMSTATE_END_OF_LIST() /* The above list is not sorted /wrt version numbers, watch out! */ }, @@ -832,6 +853,9 @@ VMStateDescription vmstate_x86_cpu = { }, { .vmsd = &vmstate_avx512, .needed = avx512_needed, + }, { + .vmsd = &vmstate_xss, + .needed = xss_needed, } , { /* empty */ } diff --git a/target-i386/ops_sse.h b/target-i386/ops_sse.h index 886e0a8..0765073 100644 --- a/target-i386/ops_sse.h +++ b/target-i386/ops_sse.h @@ -2228,7 +2228,7 @@ void glue(helper_aesdeclast, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) Reg rk = *s; for (i = 0; i < 16; i++) { - d->B(i) = rk.B(i) ^ (AES_Td4[st.B(AES_ishifts[i])] & 0xff); + d->B(i) = rk.B(i) ^ (AES_isbox[st.B(AES_ishifts[i])]); } } @@ -2253,7 +2253,7 @@ void glue(helper_aesenclast, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) Reg rk = *s; for (i = 0; i < 16; i++) { - d->B(i) = rk.B(i) ^ (AES_Te4[st.B(AES_shifts[i])] & 0xff); + d->B(i) = rk.B(i) ^ (AES_sbox[st.B(AES_shifts[i])]); } } @@ -2264,10 +2264,10 @@ void glue(helper_aesimc, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) Reg tmp = *s; for (i = 0 ; i < 4 ; i++) { - d->L(i) = bswap32(AES_Td0[AES_Te4[tmp.B(4*i+0)] & 0xff] ^ - AES_Td1[AES_Te4[tmp.B(4*i+1)] & 0xff] ^ - AES_Td2[AES_Te4[tmp.B(4*i+2)] & 0xff] ^ - AES_Td3[AES_Te4[tmp.B(4*i+3)] & 0xff]); + d->L(i) = bswap32(AES_imc[tmp.B(4*i+0)][0] ^ + AES_imc[tmp.B(4*i+1)][1] ^ + AES_imc[tmp.B(4*i+2)][2] ^ + AES_imc[tmp.B(4*i+3)][3]); } } @@ -2278,8 +2278,8 @@ void glue(helper_aeskeygenassist, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, Reg tmp = *s; for (i = 0 ; i < 4 ; i++) { - d->B(i) = AES_Te4[tmp.B(i + 4)] & 0xff; - d->B(i + 8) = AES_Te4[tmp.B(i + 12)] & 0xff; + d->B(i) = AES_sbox[tmp.B(i + 4)]; + d->B(i + 8) = AES_sbox[tmp.B(i + 12)]; } d->L(1) = (d->L(0) << 24 | d->L(0) >> 8) ^ ctrl; d->L(3) = (d->L(2) << 24 | d->L(2) >> 8) ^ ctrl; diff --git a/target-i386/seg_helper.c b/target-i386/seg_helper.c index c98eeb4..2bc757a 100644 --- a/target-i386/seg_helper.c +++ b/target-i386/seg_helper.c @@ -34,7 +34,21 @@ # define LOG_PCALL_STATE(cpu) do { } while (0) #endif -#ifndef CONFIG_USER_ONLY +#ifdef CONFIG_USER_ONLY +#define MEMSUFFIX _kernel +#define DATA_SIZE 1 +#include "exec/cpu_ldst_useronly_template.h" + +#define DATA_SIZE 2 +#include "exec/cpu_ldst_useronly_template.h" + +#define DATA_SIZE 4 +#include "exec/cpu_ldst_useronly_template.h" + +#define DATA_SIZE 8 +#include "exec/cpu_ldst_useronly_template.h" +#undef MEMSUFFIX +#else #define CPU_MMU_INDEX (cpu_mmu_index_kernel(env)) #define MEMSUFFIX _kernel #define DATA_SIZE 1 @@ -1029,7 +1043,7 @@ void helper_sysret(CPUX86State *env, int dflag) DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK); env->eip = (uint32_t)env->regs[R_ECX]; } - cpu_x86_load_seg_cache(env, R_SS, selector + 8, + cpu_x86_load_seg_cache(env, R_SS, (selector + 8) | 3, 0, 0xffffffff, DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | (3 << DESC_DPL_SHIFT) | @@ -1042,7 +1056,7 @@ void helper_sysret(CPUX86State *env, int dflag) DESC_S_MASK | (3 << DESC_DPL_SHIFT) | DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK); env->eip = (uint32_t)env->regs[R_ECX]; - cpu_x86_load_seg_cache(env, R_SS, selector + 8, + cpu_x86_load_seg_cache(env, R_SS, (selector + 8) | 3, 0, 0xffffffff, DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | (3 << DESC_DPL_SHIFT) | diff --git a/target-i386/smm_helper.c b/target-i386/smm_helper.c index 58051d3..c62f468 100644 --- a/target-i386/smm_helper.c +++ b/target-i386/smm_helper.c @@ -101,7 +101,7 @@ void do_smm_enter(X86CPU *cpu) stl_phys(cs->as, sm_state + 0x7f60, env->dr[7]); stl_phys(cs->as, sm_state + 0x7f48, env->cr[4]); - stl_phys(cs->as, sm_state + 0x7f50, env->cr[3]); + stq_phys(cs->as, sm_state + 0x7f50, env->cr[3]); stl_phys(cs->as, sm_state + 0x7f58, env->cr[0]); stl_phys(cs->as, sm_state + 0x7efc, SMM_REVISION_ID); @@ -236,7 +236,7 @@ void helper_rsm(CPUX86State *env) env->dr[7] = ldl_phys(cs->as, sm_state + 0x7f60); cpu_x86_update_cr4(env, ldl_phys(cs->as, sm_state + 0x7f48)); - cpu_x86_update_cr3(env, ldl_phys(cs->as, sm_state + 0x7f50)); + cpu_x86_update_cr3(env, ldq_phys(cs->as, sm_state + 0x7f50)); cpu_x86_update_cr0(env, ldl_phys(cs->as, sm_state + 0x7f58)); for (i = 0; i < 6; i++) { diff --git a/target-i386/translate.c b/target-i386/translate.c index 782f7d2..305ce50 100644 --- a/target-i386/translate.c +++ b/target-i386/translate.c @@ -115,6 +115,7 @@ typedef struct DisasContext { int tf; /* TF cpu flag */ int singlestep_enabled; /* "hardware" single step enabled */ int jmp_opt; /* use direct block chaining for direct jumps */ + int repz_opt; /* optimize jumps within repz instructions */ int mem_index; /* select memory access functions */ uint64_t flags; /* all execution flags */ struct TranslationBlock *tb; @@ -612,14 +613,14 @@ static void gen_exts(TCGMemOp ot, TCGv reg) gen_ext_tl(reg, reg, ot, true); } -static inline void gen_op_jnz_ecx(TCGMemOp size, int label1) +static inline void gen_op_jnz_ecx(TCGMemOp size, TCGLabel *label1) { tcg_gen_mov_tl(cpu_tmp0, cpu_regs[R_ECX]); gen_extu(size, cpu_tmp0); tcg_gen_brcondi_tl(TCG_COND_NE, cpu_tmp0, 0, label1); } -static inline void gen_op_jz_ecx(TCGMemOp size, int label1) +static inline void gen_op_jz_ecx(TCGMemOp size, TCGLabel *label1) { tcg_gen_mov_tl(cpu_tmp0, cpu_regs[R_ECX]); gen_extu(size, cpu_tmp0); @@ -1077,7 +1078,7 @@ static inline void gen_compute_eflags_c(DisasContext *s, TCGv reg) /* generate a conditional jump to label 'l1' according to jump opcode value 'b'. In the fast case, T0 is guaranted not to be used. */ -static inline void gen_jcc1_noeob(DisasContext *s, int b, int l1) +static inline void gen_jcc1_noeob(DisasContext *s, int b, TCGLabel *l1) { CCPrepare cc = gen_prepare_cc(s, b, cpu_T[0]); @@ -1095,7 +1096,7 @@ static inline void gen_jcc1_noeob(DisasContext *s, int b, int l1) /* Generate a conditional jump to label 'l1' according to jump opcode value 'b'. In the fast case, T0 is guaranted not to be used. A translation block must end soon. */ -static inline void gen_jcc1(DisasContext *s, int b, int l1) +static inline void gen_jcc1(DisasContext *s, int b, TCGLabel *l1) { CCPrepare cc = gen_prepare_cc(s, b, cpu_T[0]); @@ -1114,12 +1115,10 @@ static inline void gen_jcc1(DisasContext *s, int b, int l1) /* XXX: does not work with gdbstub "ice" single step - not a serious problem */ -static int gen_jz_ecx_string(DisasContext *s, target_ulong next_eip) +static TCGLabel *gen_jz_ecx_string(DisasContext *s, target_ulong next_eip) { - int l1, l2; - - l1 = gen_new_label(); - l2 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); gen_op_jnz_ecx(s->aflag, l1); gen_set_label(l2); gen_jmp_tb(s, next_eip, 1); @@ -1167,8 +1166,9 @@ static inline void gen_cmps(DisasContext *s, TCGMemOp ot) static inline void gen_ins(DisasContext *s, TCGMemOp ot) { - if (use_icount) + if (s->tb->cflags & CF_USE_ICOUNT) { gen_io_start(); + } gen_string_movl_A0_EDI(s); /* Note: we must do this dummy write first to be restartable in case of page fault. */ @@ -1180,14 +1180,16 @@ static inline void gen_ins(DisasContext *s, TCGMemOp ot) gen_op_st_v(s, ot, cpu_T[0], cpu_A0); gen_op_movl_T0_Dshift(ot); gen_op_add_reg_T0(s->aflag, R_EDI); - if (use_icount) + if (s->tb->cflags & CF_USE_ICOUNT) { gen_io_end(); + } } static inline void gen_outs(DisasContext *s, TCGMemOp ot) { - if (use_icount) + if (s->tb->cflags & CF_USE_ICOUNT) { gen_io_start(); + } gen_string_movl_A0_ESI(s); gen_op_ld_v(s, ot, cpu_T[0], cpu_A0); @@ -1198,8 +1200,9 @@ static inline void gen_outs(DisasContext *s, TCGMemOp ot) gen_op_movl_T0_Dshift(ot); gen_op_add_reg_T0(s->aflag, R_ESI); - if (use_icount) + if (s->tb->cflags & CF_USE_ICOUNT) { gen_io_end(); + } } /* same method as Valgrind : we generate jumps to current or next @@ -1208,14 +1211,14 @@ static inline void gen_outs(DisasContext *s, TCGMemOp ot) static inline void gen_repz_ ## op(DisasContext *s, TCGMemOp ot, \ target_ulong cur_eip, target_ulong next_eip) \ { \ - int l2;\ + TCGLabel *l2; \ gen_update_cc_op(s); \ l2 = gen_jz_ecx_string(s, next_eip); \ gen_ ## op(s, ot); \ gen_op_add_reg_im(s->aflag, R_ECX, -1); \ /* a loop would cause two single step exceptions if ECX = 1 \ before rep string_insn */ \ - if (!s->jmp_opt) \ + if (s->repz_opt) \ gen_op_jz_ecx(s->aflag, l2); \ gen_jmp(s, cur_eip); \ } @@ -1226,14 +1229,14 @@ static inline void gen_repz_ ## op(DisasContext *s, TCGMemOp ot, \ target_ulong next_eip, \ int nz) \ { \ - int l2;\ + TCGLabel *l2; \ gen_update_cc_op(s); \ l2 = gen_jz_ecx_string(s, next_eip); \ gen_ ## op(s, ot); \ gen_op_add_reg_im(s->aflag, R_ECX, -1); \ gen_update_cc_op(s); \ gen_jcc1(s, (JCC_Z << 1) | (nz ^ 1), l2); \ - if (!s->jmp_opt) \ + if (s->repz_opt) \ gen_op_jz_ecx(s->aflag, l2); \ gen_jmp(s, cur_eip); \ } @@ -2222,7 +2225,7 @@ static inline void gen_goto_tb(DisasContext *s, int tb_num, target_ulong eip) static inline void gen_jcc(DisasContext *s, int b, target_ulong val, target_ulong next_eip) { - int l1, l2; + TCGLabel *l1, *l2; if (s->jmp_opt) { l1 = gen_new_label(); @@ -2616,10 +2619,10 @@ static inline void gen_sto_env_A0(DisasContext *s, int offset) static inline void gen_op_movo(int d_offset, int s_offset) { - tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, s_offset); - tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, d_offset); - tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, s_offset + 8); - tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, d_offset + 8); + tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, s_offset + offsetof(XMMReg, XMM_Q(0))); + tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, d_offset + offsetof(XMMReg, XMM_Q(0))); + tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, s_offset + offsetof(XMMReg, XMM_Q(1))); + tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, d_offset + offsetof(XMMReg, XMM_Q(1))); } static inline void gen_op_movq(int d_offset, int s_offset) @@ -3069,7 +3072,8 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, goto illegal_op; gen_lea_modrm(env, s, modrm); if (b1 & 1) { - gen_stq_env_A0(s, offsetof(CPUX86State, xmm_regs[reg])); + gen_stq_env_A0(s, offsetof(CPUX86State, + xmm_regs[reg].XMM_Q(0))); } else { tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, xmm_regs[reg].XMM_L(0))); @@ -5146,7 +5150,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, case 0x1b0: case 0x1b1: /* cmpxchg Ev, Gv */ { - int label1, label2; + TCGLabel *label1, *label2; TCGv t0, t1, t2, a0; ot = mo_b_d(b, dflag); @@ -6190,7 +6194,8 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, case 0x10 ... 0x13: /* fcmovxx */ case 0x18 ... 0x1b: { - int op1, l1; + int op1; + TCGLabel *l1; static const uint8_t fcmov_cc[8] = { (JCC_B << 1), (JCC_Z << 1), @@ -6277,7 +6282,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_repz_ins(s, ot, pc_start - s->cs_base, s->pc - s->cs_base); } else { gen_ins(s, ot); - if (use_icount) { + if (s->tb->cflags & CF_USE_ICOUNT) { gen_jmp(s, s->pc - s->cs_base); } } @@ -6292,7 +6297,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_repz_outs(s, ot, pc_start - s->cs_base, s->pc - s->cs_base); } else { gen_outs(s, ot); - if (use_icount) { + if (s->tb->cflags & CF_USE_ICOUNT) { gen_jmp(s, s->pc - s->cs_base); } } @@ -6308,12 +6313,13 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, tcg_gen_movi_tl(cpu_T[0], val); gen_check_io(s, ot, pc_start - s->cs_base, SVM_IOIO_TYPE_MASK | svm_is_rep(prefixes)); - if (use_icount) + if (s->tb->cflags & CF_USE_ICOUNT) { gen_io_start(); + } tcg_gen_movi_i32(cpu_tmp2_i32, val); gen_helper_in_func(ot, cpu_T[1], cpu_tmp2_i32); gen_op_mov_reg_v(ot, R_EAX, cpu_T[1]); - if (use_icount) { + if (s->tb->cflags & CF_USE_ICOUNT) { gen_io_end(); gen_jmp(s, s->pc - s->cs_base); } @@ -6327,12 +6333,13 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, svm_is_rep(prefixes)); gen_op_mov_v_reg(ot, cpu_T[1], R_EAX); - if (use_icount) + if (s->tb->cflags & CF_USE_ICOUNT) { gen_io_start(); + } tcg_gen_movi_i32(cpu_tmp2_i32, val); tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_T[1]); gen_helper_out_func(ot, cpu_tmp2_i32, cpu_tmp3_i32); - if (use_icount) { + if (s->tb->cflags & CF_USE_ICOUNT) { gen_io_end(); gen_jmp(s, s->pc - s->cs_base); } @@ -6343,12 +6350,13 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, tcg_gen_ext16u_tl(cpu_T[0], cpu_regs[R_EDX]); gen_check_io(s, ot, pc_start - s->cs_base, SVM_IOIO_TYPE_MASK | svm_is_rep(prefixes)); - if (use_icount) + if (s->tb->cflags & CF_USE_ICOUNT) { gen_io_start(); + } tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); gen_helper_in_func(ot, cpu_T[1], cpu_tmp2_i32); gen_op_mov_reg_v(ot, R_EAX, cpu_T[1]); - if (use_icount) { + if (s->tb->cflags & CF_USE_ICOUNT) { gen_io_end(); gen_jmp(s, s->pc - s->cs_base); } @@ -6361,12 +6369,13 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, svm_is_rep(prefixes)); gen_op_mov_v_reg(ot, cpu_T[1], R_EAX); - if (use_icount) + if (s->tb->cflags & CF_USE_ICOUNT) { gen_io_start(); + } tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_T[1]); gen_helper_out_func(ot, cpu_tmp2_i32, cpu_tmp3_i32); - if (use_icount) { + if (s->tb->cflags & CF_USE_ICOUNT) { gen_io_end(); gen_jmp(s, s->pc - s->cs_base); } @@ -7007,7 +7016,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, case 0xe2: /* loop */ case 0xe3: /* jecxz */ { - int l1, l2, l3; + TCGLabel *l1, *l2, *l3; tval = (int8_t)insn_get(env, s, MO_8); next_eip = s->pc - s->cs_base; @@ -7064,10 +7073,11 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, case 0x131: /* rdtsc */ gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); - if (use_icount) + if (s->tb->cflags & CF_USE_ICOUNT) { gen_io_start(); + } gen_helper_rdtsc(cpu_env); - if (use_icount) { + if (s->tb->cflags & CF_USE_ICOUNT) { gen_io_end(); gen_jmp(s, s->pc - s->cs_base); } @@ -7450,10 +7460,11 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, goto illegal_op; gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); - if (use_icount) + if (s->tb->cflags & CF_USE_ICOUNT) { gen_io_start(); + } gen_helper_rdtscp(cpu_env); - if (use_icount) { + if (s->tb->cflags & CF_USE_ICOUNT) { gen_io_end(); gen_jmp(s, s->pc - s->cs_base); } @@ -7503,7 +7514,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } else #endif { - int label1; + TCGLabel *label1; TCGv t0, t1, t2, a0; if (!s->pe || s->vm86) @@ -7552,7 +7563,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, case 0x102: /* lar */ case 0x103: /* lsl */ { - int label1; + TCGLabel *label1; TCGv t0; if (!s->pe || s->vm86) goto illegal_op; @@ -7901,7 +7912,6 @@ static inline void gen_intermediate_code_internal(X86CPU *cpu, CPUX86State *env = &cpu->env; DisasContext dc1, *dc = &dc1; target_ulong pc_ptr; - uint16_t *gen_opc_end; CPUBreakpoint *bp; int j, lj; uint64_t flags; @@ -7951,6 +7961,17 @@ static inline void gen_intermediate_code_internal(X86CPU *cpu, || (flags & HF_SOFTMMU_MASK) #endif ); + /* Do not optimize repz jumps at all in icount mode, because + rep movsS instructions are execured with different paths + in !repz_opt and repz_opt modes. The first one was used + always except single step mode. And this setting + disables jumps optimization and control paths become + equivalent in run and single step modes. + Now there will be no jump optimization for repz in + record/replay modes and there will always be an + additional step for ecx=0 when icount is enabled. + */ + dc->repz_opt = !dc->jmp_opt && !(tb->cflags & CF_USE_ICOUNT); #if 0 /* check addseg logic */ if (!dc->addseg && (dc->vm86 || !dc->pe || !dc->code32)) @@ -7970,8 +7991,6 @@ static inline void gen_intermediate_code_internal(X86CPU *cpu, cpu_ptr1 = tcg_temp_new_ptr(); cpu_cc_srcT = tcg_temp_local_new(); - gen_opc_end = tcg_ctx.gen_opc_buf + OPC_MAX_SIZE; - dc->is_jmp = DISAS_NEXT; pc_ptr = pc_start; lj = -1; @@ -7980,7 +7999,7 @@ static inline void gen_intermediate_code_internal(X86CPU *cpu, if (max_insns == 0) max_insns = CF_COUNT_MASK; - gen_tb_start(); + gen_tb_start(tb); for(;;) { if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) { QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { @@ -7992,7 +8011,7 @@ static inline void gen_intermediate_code_internal(X86CPU *cpu, } } if (search_pc) { - j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + j = tcg_op_buf_count(); if (lj < j) { lj++; while (lj < j) @@ -8022,8 +8041,22 @@ static inline void gen_intermediate_code_internal(X86CPU *cpu, gen_eob(dc); break; } + /* Do not cross the boundary of the pages in icount mode, + it can cause an exception. Do it only when boundary is + crossed by the first instruction in the block. + If current instruction already crossed the bound - it's ok, + because an exception hasn't stopped this code. + */ + if ((tb->cflags & CF_USE_ICOUNT) + && ((pc_ptr & TARGET_PAGE_MASK) + != ((pc_ptr + TARGET_MAX_INSN_SIZE - 1) & TARGET_PAGE_MASK) + || (pc_ptr & ~TARGET_PAGE_MASK) == 0)) { + gen_jmp_im(pc_ptr - dc->cs_base); + gen_eob(dc); + break; + } /* if too long translation, stop generation too */ - if (tcg_ctx.gen_opc_ptr >= gen_opc_end || + if (tcg_op_buf_full() || (pc_ptr - pc_start) >= (TARGET_PAGE_SIZE - 32) || num_insns >= max_insns) { gen_jmp_im(pc_ptr - dc->cs_base); @@ -8040,10 +8073,10 @@ static inline void gen_intermediate_code_internal(X86CPU *cpu, gen_io_end(); done_generating: gen_tb_end(tb, num_insns); - *tcg_ctx.gen_opc_ptr = INDEX_op_end; + /* we don't forget to fill the last values */ if (search_pc) { - j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + j = tcg_op_buf_count(); lj++; while (lj <= j) tcg_ctx.gen_opc_instr_start[lj++] = 0; diff --git a/target-lm32/cpu.h b/target-lm32/cpu.h index 0dab6e8..11ae68d 100644 --- a/target-lm32/cpu.h +++ b/target-lm32/cpu.h @@ -30,8 +30,6 @@ struct CPULM32State; typedef struct CPULM32State CPULM32State; -#define TARGET_HAS_ICE 1 - #define ELF_MACHINE EM_LATTICEMICO32 #define NB_MMU_MODES 1 @@ -219,14 +217,7 @@ void lm32_watchpoint_insert(CPULM32State *env, int index, target_ulong address, void lm32_watchpoint_remove(CPULM32State *env, int index); bool lm32_cpu_do_semihosting(CPUState *cs); -static inline CPULM32State *cpu_init(const char *cpu_model) -{ - LM32CPU *cpu = cpu_lm32_init(cpu_model); - if (cpu == NULL) { - return NULL; - } - return &cpu->env; -} +#define cpu_init(cpu_model) CPU(cpu_lm32_init(cpu_model)) #define cpu_list lm32_cpu_list #define cpu_exec cpu_lm32_exec diff --git a/target-lm32/translate.c b/target-lm32/translate.c index 8454e8b..81a204f 100644 --- a/target-lm32/translate.c +++ b/target-lm32/translate.c @@ -219,7 +219,7 @@ static void dec_b(DisasContext *dc) /* restore IE.IE in case of an eret */ if (dc->r0 == R_EA) { TCGv t0 = tcg_temp_new(); - int l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); tcg_gen_andi_tl(t0, cpu_ie, IE_EIE); tcg_gen_ori_tl(cpu_ie, cpu_ie, IE_IE); tcg_gen_brcondi_tl(TCG_COND_EQ, t0, IE_EIE, l1); @@ -228,7 +228,7 @@ static void dec_b(DisasContext *dc) tcg_temp_free(t0); } else if (dc->r0 == R_BA) { TCGv t0 = tcg_temp_new(); - int l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); tcg_gen_andi_tl(t0, cpu_ie, IE_BIE); tcg_gen_ori_tl(cpu_ie, cpu_ie, IE_IE); tcg_gen_brcondi_tl(TCG_COND_EQ, t0, IE_BIE, l1); @@ -252,9 +252,7 @@ static void dec_bi(DisasContext *dc) static inline void gen_cond_branch(DisasContext *dc, int cond) { - int l1; - - l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); tcg_gen_brcond_tl(cond, cpu_R[dc->r0], cpu_R[dc->r1], l1); gen_goto_tb(dc, 0, dc->pc + 4); gen_set_label(l1); @@ -428,7 +426,7 @@ static void dec_cmpne(DisasContext *dc) static void dec_divu(DisasContext *dc) { - int l1; + TCGLabel *l1; LOG_DIS("divu r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); @@ -508,7 +506,7 @@ static void dec_lw(DisasContext *dc) static void dec_modu(DisasContext *dc) { - int l1; + TCGLabel *l1; LOG_DIS("modu r%d, r%d, %d\n", dc->r2, dc->r0, dc->r1); @@ -769,8 +767,8 @@ static void dec_sr(DisasContext *dc) } tcg_gen_sari_tl(cpu_R[dc->r1], cpu_R[dc->r0], dc->imm5); } else { - int l1 = gen_new_label(); - int l2 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); TCGv t0 = tcg_temp_local_new(); tcg_gen_andi_tl(t0, cpu_R[dc->r1], 0x1f); @@ -805,8 +803,8 @@ static void dec_sru(DisasContext *dc) } tcg_gen_shri_tl(cpu_R[dc->r1], cpu_R[dc->r0], dc->imm5); } else { - int l1 = gen_new_label(); - int l2 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); TCGv t0 = tcg_temp_local_new(); tcg_gen_andi_tl(t0, cpu_R[dc->r1], 0x1f); @@ -865,24 +863,24 @@ static void dec_wcsr(DisasContext *dc) break; case CSR_IM: /* mark as an io operation because it could cause an interrupt */ - if (use_icount) { + if (dc->tb->cflags & CF_USE_ICOUNT) { gen_io_start(); } gen_helper_wcsr_im(cpu_env, cpu_R[dc->r1]); tcg_gen_movi_tl(cpu_pc, dc->pc + 4); - if (use_icount) { + if (dc->tb->cflags & CF_USE_ICOUNT) { gen_io_end(); } dc->is_jmp = DISAS_UPDATE; break; case CSR_IP: /* mark as an io operation because it could cause an interrupt */ - if (use_icount) { + if (dc->tb->cflags & CF_USE_ICOUNT) { gen_io_start(); } gen_helper_wcsr_ip(cpu_env, cpu_R[dc->r1]); tcg_gen_movi_tl(cpu_pc, dc->pc + 4); - if (use_icount) { + if (dc->tb->cflags & CF_USE_ICOUNT) { gen_io_end(); } dc->is_jmp = DISAS_UPDATE; @@ -1062,7 +1060,6 @@ void gen_intermediate_code_internal(LM32CPU *cpu, CPUState *cs = CPU(cpu); CPULM32State *env = &cpu->env; struct DisasContext ctx, *dc = &ctx; - uint16_t *gen_opc_end; uint32_t pc_start; int j, lj; uint32_t next_page_start; @@ -1075,8 +1072,6 @@ void gen_intermediate_code_internal(LM32CPU *cpu, dc->num_watchpoints = cpu->num_watchpoints; dc->tb = tb; - gen_opc_end = tcg_ctx.gen_opc_buf + OPC_MAX_SIZE; - dc->is_jmp = DISAS_NEXT; dc->pc = pc_start; dc->singlestep_enabled = cs->singlestep_enabled; @@ -1095,12 +1090,12 @@ void gen_intermediate_code_internal(LM32CPU *cpu, max_insns = CF_COUNT_MASK; } - gen_tb_start(); + gen_tb_start(tb); do { check_breakpoint(env, dc); if (search_pc) { - j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + j = tcg_op_buf_count(); if (lj < j) { lj++; while (lj < j) { @@ -1124,7 +1119,7 @@ void gen_intermediate_code_internal(LM32CPU *cpu, num_insns++; } while (!dc->is_jmp - && tcg_ctx.gen_opc_ptr < gen_opc_end + && !tcg_op_buf_full() && !cs->singlestep_enabled && !singlestep && (dc->pc < next_page_start) @@ -1158,9 +1153,9 @@ void gen_intermediate_code_internal(LM32CPU *cpu, } gen_tb_end(tb, num_insns); - *tcg_ctx.gen_opc_ptr = INDEX_op_end; + if (search_pc) { - j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + j = tcg_op_buf_count(); lj++; while (lj <= j) { tcg_ctx.gen_opc_instr_start[lj++] = 0; @@ -1174,9 +1169,8 @@ void gen_intermediate_code_internal(LM32CPU *cpu, if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { qemu_log("\n"); log_target_disas(env, pc_start, dc->pc - pc_start, 0); - qemu_log("\nisize=%d osize=%td\n", - dc->pc - pc_start, tcg_ctx.gen_opc_ptr - - tcg_ctx.gen_opc_buf); + qemu_log("\nisize=%d osize=%d\n", + dc->pc - pc_start, tcg_op_buf_count()); } #endif } diff --git a/target-m68k/cpu.h b/target-m68k/cpu.h index f67bbcc..5f165da 100644 --- a/target-m68k/cpu.h +++ b/target-m68k/cpu.h @@ -32,8 +32,6 @@ #define MAX_QREGS 32 -#define TARGET_HAS_ICE 1 - #define ELF_MACHINE EM_68K #define EXCP_ACCESS 2 /* Access (MMU) error. */ @@ -214,14 +212,7 @@ void register_m68k_insns (CPUM68KState *env); #define TARGET_PHYS_ADDR_SPACE_BITS 32 #define TARGET_VIRT_ADDR_SPACE_BITS 32 -static inline CPUM68KState *cpu_init(const char *cpu_model) -{ - M68kCPU *cpu = cpu_m68k_init(cpu_model); - if (cpu == NULL) { - return NULL; - } - return &cpu->env; -} +#define cpu_init(cpu_model) CPU(cpu_m68k_init(cpu_model)) #define cpu_exec cpu_m68k_exec #define cpu_gen_code cpu_m68k_gen_code diff --git a/target-m68k/translate.c b/target-m68k/translate.c index efd4cfc..4959b97 100644 --- a/target-m68k/translate.c +++ b/target-m68k/translate.c @@ -679,7 +679,7 @@ static TCGv gen_ea(CPUM68KState *env, DisasContext *s, uint16_t insn, } /* This generates a conditional branch, clobbering all temporaries. */ -static void gen_jmpcc(DisasContext *s, int cond, int l1) +static void gen_jmpcc(DisasContext *s, int cond, TCGLabel *l1) { TCGv tmp; @@ -784,7 +784,7 @@ static void gen_jmpcc(DisasContext *s, int cond, int l1) DISAS_INSN(scc) { - int l1; + TCGLabel *l1; int cond; TCGv reg; @@ -1658,7 +1658,7 @@ DISAS_INSN(branch) int32_t offset; uint32_t base; int op; - int l1; + TCGLabel *l1; base = s->pc; op = (insn >> 8) & 0xf; @@ -2395,7 +2395,7 @@ DISAS_INSN(fbcc) uint32_t offset; uint32_t addr; TCGv flag; - int l1; + TCGLabel *l1; addr = s->pc; offset = cpu_ldsw_code(env, s->pc); @@ -2980,7 +2980,6 @@ gen_intermediate_code_internal(M68kCPU *cpu, TranslationBlock *tb, CPUState *cs = CPU(cpu); CPUM68KState *env = &cpu->env; DisasContext dc1, *dc = &dc1; - uint16_t *gen_opc_end; CPUBreakpoint *bp; int j, lj; target_ulong pc_start; @@ -2993,8 +2992,6 @@ gen_intermediate_code_internal(M68kCPU *cpu, TranslationBlock *tb, dc->tb = tb; - gen_opc_end = tcg_ctx.gen_opc_buf + OPC_MAX_SIZE; - dc->env = env; dc->is_jmp = DISAS_NEXT; dc->pc = pc_start; @@ -3010,7 +3007,7 @@ gen_intermediate_code_internal(M68kCPU *cpu, TranslationBlock *tb, if (max_insns == 0) max_insns = CF_COUNT_MASK; - gen_tb_start(); + gen_tb_start(tb); do { pc_offset = dc->pc - pc_start; gen_throws_exception = NULL; @@ -3026,7 +3023,7 @@ gen_intermediate_code_internal(M68kCPU *cpu, TranslationBlock *tb, break; } if (search_pc) { - j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + j = tcg_op_buf_count(); if (lj < j) { lj++; while (lj < j) @@ -3041,7 +3038,7 @@ gen_intermediate_code_internal(M68kCPU *cpu, TranslationBlock *tb, dc->insn_pc = dc->pc; disas_m68k_insn(env, dc); num_insns++; - } while (!dc->is_jmp && tcg_ctx.gen_opc_ptr < gen_opc_end && + } while (!dc->is_jmp && !tcg_op_buf_full() && !cs->singlestep_enabled && !singlestep && (pc_offset) < (TARGET_PAGE_SIZE - 32) && @@ -3075,7 +3072,6 @@ gen_intermediate_code_internal(M68kCPU *cpu, TranslationBlock *tb, } } gen_tb_end(tb, num_insns); - *tcg_ctx.gen_opc_ptr = INDEX_op_end; #ifdef DEBUG_DISAS if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { @@ -3086,7 +3082,7 @@ gen_intermediate_code_internal(M68kCPU *cpu, TranslationBlock *tb, } #endif if (search_pc) { - j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + j = tcg_op_buf_count(); lj++; while (lj <= j) tcg_ctx.gen_opc_instr_start[lj++] = 0; diff --git a/target-microblaze/cpu.h b/target-microblaze/cpu.h index 6ccd060..7d06227 100644 --- a/target-microblaze/cpu.h +++ b/target-microblaze/cpu.h @@ -34,8 +34,6 @@ typedef struct CPUMBState CPUMBState; #include "mmu.h" #endif -#define TARGET_HAS_ICE 1 - #define ELF_MACHINE EM_MICROBLAZE #define EXCP_NMI 1 @@ -299,14 +297,7 @@ enum { #define TARGET_PHYS_ADDR_SPACE_BITS 32 #define TARGET_VIRT_ADDR_SPACE_BITS 32 -static inline CPUMBState *cpu_init(const char *cpu_model) -{ - MicroBlazeCPU *cpu = cpu_mb_init(cpu_model); - if (cpu == NULL) { - return NULL; - } - return &cpu->env; -} +#define cpu_init(cpu_model) CPU(cpu_mb_init(cpu_model)) #define cpu_exec cpu_mb_exec #define cpu_gen_code cpu_mb_gen_code diff --git a/target-microblaze/helper.c b/target-microblaze/helper.c index 59466c9..32896f4 100644 --- a/target-microblaze/helper.c +++ b/target-microblaze/helper.c @@ -22,7 +22,6 @@ #include "qemu/host-utils.h" #define D(x) -#define DMMU(x) #if defined(CONFIG_USER_ONLY) @@ -75,13 +74,14 @@ int mb_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, vaddr = address & TARGET_PAGE_MASK; paddr = lu.paddr + vaddr - lu.vaddr; - DMMU(qemu_log("MMU map mmu=%d v=%x p=%x prot=%x\n", - mmu_idx, vaddr, paddr, lu.prot)); + qemu_log_mask(CPU_LOG_MMU, "MMU map mmu=%d v=%x p=%x prot=%x\n", + mmu_idx, vaddr, paddr, lu.prot); tlb_set_page(cs, vaddr, paddr, lu.prot, mmu_idx, TARGET_PAGE_SIZE); r = 0; } else { env->sregs[SR_EAR] = address; - DMMU(qemu_log("mmu=%d miss v=%x\n", mmu_idx, address)); + qemu_log_mask(CPU_LOG_MMU, "mmu=%d miss v=%" VADDR_PRIx "\n", + mmu_idx, address); switch (lu.err) { case ERR_PROT: diff --git a/target-microblaze/translate.c b/target-microblaze/translate.c index fd2b771..4068946 100644 --- a/target-microblaze/translate.c +++ b/target-microblaze/translate.c @@ -313,7 +313,7 @@ static void dec_sub(DisasContext *dc) static void dec_pattern(DisasContext *dc) { unsigned int mode; - int l1; + TCGLabel *l1; if ((dc->tb_flags & MSR_EE_FLAG) && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) @@ -1038,7 +1038,7 @@ static void dec_load(DisasContext *dc) static void dec_store(DisasContext *dc) { TCGv t, *addr, swx_addr; - int swx_skip = 0; + TCGLabel *swx_skip = NULL; unsigned int size, rev = 0, ex = 0; TCGMemOp mop; @@ -1192,9 +1192,7 @@ static inline void eval_cc(DisasContext *dc, unsigned int cc, static void eval_cond_jmp(DisasContext *dc, TCGv pc_true, TCGv pc_false) { - int l1; - - l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); /* Conditional jmp. */ tcg_gen_mov_tl(cpu_SR[SR_PC], pc_false); tcg_gen_brcondi_tl(TCG_COND_EQ, env_btaken, 0, l1); @@ -1673,7 +1671,6 @@ gen_intermediate_code_internal(MicroBlazeCPU *cpu, TranslationBlock *tb, { CPUState *cs = CPU(cpu); CPUMBState *env = &cpu->env; - uint16_t *gen_opc_end; uint32_t pc_start; int j, lj; struct DisasContext ctx; @@ -1688,8 +1685,6 @@ gen_intermediate_code_internal(MicroBlazeCPU *cpu, TranslationBlock *tb, dc->tb = tb; org_flags = dc->synced_flags = dc->tb_flags = tb->flags; - gen_opc_end = tcg_ctx.gen_opc_buf + OPC_MAX_SIZE; - dc->is_jmp = DISAS_NEXT; dc->jmp = 0; dc->delayed_branch = !!(dc->tb_flags & D_FLAG); @@ -1720,7 +1715,7 @@ gen_intermediate_code_internal(MicroBlazeCPU *cpu, TranslationBlock *tb, if (max_insns == 0) max_insns = CF_COUNT_MASK; - gen_tb_start(); + gen_tb_start(tb); do { #if SIM_COMPAT @@ -1732,7 +1727,7 @@ gen_intermediate_code_internal(MicroBlazeCPU *cpu, TranslationBlock *tb, check_breakpoint(env, dc); if (search_pc) { - j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + j = tcg_op_buf_count(); if (lj < j) { lj++; while (lj < j) @@ -1776,10 +1771,8 @@ gen_intermediate_code_internal(MicroBlazeCPU *cpu, TranslationBlock *tb, gen_goto_tb(dc, 0, dc->jmp_pc); dc->is_jmp = DISAS_TB_JUMP; } else if (dc->jmp == JMP_DIRECT_CC) { - int l1; - + TCGLabel *l1 = gen_new_label(); t_sync_flags(dc); - l1 = gen_new_label(); /* Conditional jmp. */ tcg_gen_brcondi_tl(TCG_COND_NE, env_btaken, 0, l1); gen_goto_tb(dc, 1, dc->pc); @@ -1795,10 +1788,10 @@ gen_intermediate_code_internal(MicroBlazeCPU *cpu, TranslationBlock *tb, break; } } while (!dc->is_jmp && !dc->cpustate_changed - && tcg_ctx.gen_opc_ptr < gen_opc_end - && !singlestep - && (dc->pc < next_page_start) - && num_insns < max_insns); + && !tcg_op_buf_full() + && !singlestep + && (dc->pc < next_page_start) + && num_insns < max_insns); npc = dc->pc; if (dc->jmp == JMP_DIRECT || dc->jmp == JMP_DIRECT_CC) { @@ -1846,9 +1839,9 @@ gen_intermediate_code_internal(MicroBlazeCPU *cpu, TranslationBlock *tb, } } gen_tb_end(tb, num_insns); - *tcg_ctx.gen_opc_ptr = INDEX_op_end; + if (search_pc) { - j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + j = tcg_op_buf_count(); lj++; while (lj <= j) tcg_ctx.gen_opc_instr_start[lj++] = 0; @@ -1864,9 +1857,8 @@ gen_intermediate_code_internal(MicroBlazeCPU *cpu, TranslationBlock *tb, #if DISAS_GNU log_target_disas(env, pc_start, dc->pc - pc_start, 0); #endif - qemu_log("\nisize=%d osize=%td\n", - dc->pc - pc_start, tcg_ctx.gen_opc_ptr - - tcg_ctx.gen_opc_buf); + qemu_log("\nisize=%d osize=%d\n", + dc->pc - pc_start, tcg_op_buf_count()); } #endif #endif diff --git a/target-mips/cpu-qom.h b/target-mips/cpu-qom.h index 2ffc1bf..4d6f9de 100644 --- a/target-mips/cpu-qom.h +++ b/target-mips/cpu-qom.h @@ -74,6 +74,10 @@ static inline MIPSCPU *mips_env_get_cpu(CPUMIPSState *env) #define ENV_OFFSET offsetof(MIPSCPU, env) +#ifndef CONFIG_USER_ONLY +extern const struct VMStateDescription vmstate_mips_cpu; +#endif + void mips_cpu_do_interrupt(CPUState *cpu); bool mips_cpu_exec_interrupt(CPUState *cpu, int int_req); void mips_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, diff --git a/target-mips/cpu.c b/target-mips/cpu.c index 98dc94e..958c999 100644 --- a/target-mips/cpu.c +++ b/target-mips/cpu.c @@ -148,6 +148,7 @@ static void mips_cpu_class_init(ObjectClass *c, void *data) cc->do_unassigned_access = mips_cpu_unassigned_access; cc->do_unaligned_access = mips_cpu_do_unaligned_access; cc->get_phys_page_debug = mips_cpu_get_phys_page_debug; + cc->vmsd = &vmstate_mips_cpu; #endif cc->gdb_num_core_regs = 73; diff --git a/target-mips/cpu.h b/target-mips/cpu.h index c01bbda..f9d2b4c 100644 --- a/target-mips/cpu.h +++ b/target-mips/cpu.h @@ -4,7 +4,6 @@ //#define DEBUG_OP #define ALIGNED_ONLY -#define TARGET_HAS_ICE 1 #define ELF_MACHINE EM_MIPS @@ -446,8 +445,8 @@ struct CPUMIPSState { #define CP0C3_MT 2 #define CP0C3_SM 1 #define CP0C3_TL 0 - uint32_t CP0_Config4; - uint32_t CP0_Config4_rw_bitmask; + int32_t CP0_Config4; + int32_t CP0_Config4_rw_bitmask; #define CP0C4_M 31 #define CP0C4_IE 29 #define CP0C4_KScrExist 16 @@ -456,8 +455,8 @@ struct CPUMIPSState { #define CP0C4_FTLBWays 4 #define CP0C4_FTLBSets 0 #define CP0C4_MMUSizeExt 0 - uint32_t CP0_Config5; - uint32_t CP0_Config5_rw_bitmask; + int32_t CP0_Config5; + int32_t CP0_Config5_rw_bitmask; #define CP0C5_M 31 #define CP0C5_K 30 #define CP0C5_CV 29 @@ -615,8 +614,6 @@ void mips_cpu_list (FILE *f, fprintf_function cpu_fprintf); extern void cpu_wrdsp(uint32_t rs, uint32_t mask_num, CPUMIPSState *env); extern uint32_t cpu_rddsp(uint32_t mask_num, CPUMIPSState *env); -#define CPU_SAVE_VERSION 5 - /* MMU modes definitions. We carefully match the indices with our hflags layout. */ #define MMU_MODE0_SUFFIX _kernel @@ -740,14 +737,7 @@ void mips_tcg_init(void); MIPSCPU *cpu_mips_init(const char *cpu_model); int cpu_mips_signal_handler(int host_signum, void *pinfo, void *puc); -static inline CPUMIPSState *cpu_init(const char *cpu_model) -{ - MIPSCPU *cpu = cpu_mips_init(cpu_model); - if (cpu == NULL) { - return NULL; - } - return &cpu->env; -} +#define cpu_init(cpu_model) CPU(cpu_mips_init(cpu_model)) /* TODO QOM'ify CPU reset and remove */ void cpu_state_reset(CPUMIPSState *s); @@ -777,6 +767,35 @@ target_ulong exception_resume_pc (CPUMIPSState *env); extern unsigned int ieee_rm[]; int ieee_ex_to_mips(int xcpt); +static inline void restore_rounding_mode(CPUMIPSState *env) +{ + set_float_rounding_mode(ieee_rm[env->active_fpu.fcr31 & 3], + &env->active_fpu.fp_status); +} + +static inline void restore_flush_mode(CPUMIPSState *env) +{ + set_flush_to_zero((env->active_fpu.fcr31 & (1 << 24)) != 0, + &env->active_fpu.fp_status); +} + +static inline void restore_fp_status(CPUMIPSState *env) +{ + restore_rounding_mode(env); + restore_flush_mode(env); +} + +static inline void restore_msa_fp_status(CPUMIPSState *env) +{ + float_status *status = &env->active_tc.msa_fp_status; + int rounding_mode = (env->active_tc.msacsr & MSACSR_RM_MASK) >> MSACSR_RM; + bool flush_to_zero = (env->active_tc.msacsr & MSACSR_FS_MASK) != 0; + + set_float_rounding_mode(ieee_rm[rounding_mode], status); + set_flush_to_zero(flush_to_zero, status); + set_flush_inputs_to_zero(flush_to_zero, status); +} + static inline void cpu_get_tb_cpu_state(CPUMIPSState *env, target_ulong *pc, target_ulong *cs_base, int *flags) { @@ -831,16 +850,19 @@ static inline void compute_hflags(CPUMIPSState *env) env->hflags |= (env->CP0_Status >> CP0St_KSU) & MIPS_HFLAG_KSU; } #if defined(TARGET_MIPS64) - if (((env->hflags & MIPS_HFLAG_KSU) != MIPS_HFLAG_UM) || - (env->CP0_Status & (1 << CP0St_PX)) || - (env->CP0_Status & (1 << CP0St_UX))) { + if ((env->insn_flags & ISA_MIPS3) && + (((env->hflags & MIPS_HFLAG_KSU) != MIPS_HFLAG_UM) || + (env->CP0_Status & (1 << CP0St_PX)) || + (env->CP0_Status & (1 << CP0St_UX)))) { env->hflags |= MIPS_HFLAG_64; } - if (((env->hflags & MIPS_HFLAG_KSU) == MIPS_HFLAG_UM) && - !(env->CP0_Status & (1 << CP0St_UX))) { + if (!(env->insn_flags & ISA_MIPS3)) { + env->hflags |= MIPS_HFLAG_AWRAP; + } else if (((env->hflags & MIPS_HFLAG_KSU) == MIPS_HFLAG_UM) && + !(env->CP0_Status & (1 << CP0St_UX))) { env->hflags |= MIPS_HFLAG_AWRAP; - } else if (env->insn_flags & ISA_MIPS32R6) { + } else if (env->insn_flags & ISA_MIPS64R6) { /* Address wrapping for Supervisor and Kernel is specified in R6 */ if ((((env->hflags & MIPS_HFLAG_KSU) == MIPS_HFLAG_SM) && !(env->CP0_Status & (1 << CP0St_SX))) || @@ -904,4 +926,93 @@ static inline void compute_hflags(CPUMIPSState *env) } } +#ifndef CONFIG_USER_ONLY +/* Called for updates to CP0_Status. */ +static inline void sync_c0_status(CPUMIPSState *env, CPUMIPSState *cpu, int tc) +{ + int32_t tcstatus, *tcst; + uint32_t v = cpu->CP0_Status; + uint32_t cu, mx, asid, ksu; + uint32_t mask = ((1 << CP0TCSt_TCU3) + | (1 << CP0TCSt_TCU2) + | (1 << CP0TCSt_TCU1) + | (1 << CP0TCSt_TCU0) + | (1 << CP0TCSt_TMX) + | (3 << CP0TCSt_TKSU) + | (0xff << CP0TCSt_TASID)); + + cu = (v >> CP0St_CU0) & 0xf; + mx = (v >> CP0St_MX) & 0x1; + ksu = (v >> CP0St_KSU) & 0x3; + asid = env->CP0_EntryHi & 0xff; + + tcstatus = cu << CP0TCSt_TCU0; + tcstatus |= mx << CP0TCSt_TMX; + tcstatus |= ksu << CP0TCSt_TKSU; + tcstatus |= asid; + + if (tc == cpu->current_tc) { + tcst = &cpu->active_tc.CP0_TCStatus; + } else { + tcst = &cpu->tcs[tc].CP0_TCStatus; + } + + *tcst &= ~mask; + *tcst |= tcstatus; + compute_hflags(cpu); +} + +static inline void cpu_mips_store_status(CPUMIPSState *env, target_ulong val) +{ + uint32_t mask = env->CP0_Status_rw_bitmask; + + if (env->insn_flags & ISA_MIPS32R6) { + bool has_supervisor = extract32(mask, CP0St_KSU, 2) == 0x3; + + if (has_supervisor && extract32(val, CP0St_KSU, 2) == 0x3) { + mask &= ~(3 << CP0St_KSU); + } + mask &= ~(((1 << CP0St_SR) | (1 << CP0St_NMI)) & val); + } + + env->CP0_Status = (env->CP0_Status & ~mask) | (val & mask); + if (env->CP0_Config3 & (1 << CP0C3_MT)) { + sync_c0_status(env, env, env->current_tc); + } else { + compute_hflags(env); + } +} + +static inline void cpu_mips_store_cause(CPUMIPSState *env, target_ulong val) +{ + uint32_t mask = 0x00C00300; + uint32_t old = env->CP0_Cause; + int i; + + if (env->insn_flags & ISA_MIPS32R2) { + mask |= 1 << CP0Ca_DC; + } + if (env->insn_flags & ISA_MIPS32R6) { + mask &= ~((1 << CP0Ca_WP) & val); + } + + env->CP0_Cause = (env->CP0_Cause & ~mask) | (val & mask); + + if ((old ^ env->CP0_Cause) & (1 << CP0Ca_DC)) { + if (env->CP0_Cause & (1 << CP0Ca_DC)) { + cpu_mips_stop_count(env); + } else { + cpu_mips_start_count(env); + } + } + + /* Set/reset software interrupts */ + for (i = 0 ; i < 2 ; i++) { + if ((old ^ env->CP0_Cause) & (1 << (CP0Ca_IP + i))) { + cpu_mips_soft_irq(env, i, env->CP0_Cause & (1 << (CP0Ca_IP + i))); + } + } +} +#endif + #endif /* !defined (__MIPS_CPU_H__) */ diff --git a/target-mips/dsp_helper.c b/target-mips/dsp_helper.c index 349f2a0..46528de 100644 --- a/target-mips/dsp_helper.c +++ b/target-mips/dsp_helper.c @@ -3678,7 +3678,7 @@ void cpu_wrdsp(uint32_t rs, uint32_t mask_num, CPUMIPSState *env) void helper_wrdsp(target_ulong rs, target_ulong mask_num, CPUMIPSState *env) { - return cpu_wrdsp(rs, mask_num, env); + cpu_wrdsp(rs, mask_num, env); } uint32_t cpu_rddsp(uint32_t mask_num, CPUMIPSState *env) diff --git a/target-mips/gdbstub.c b/target-mips/gdbstub.c index f65fec2..9845d88 100644 --- a/target-mips/gdbstub.c +++ b/target-mips/gdbstub.c @@ -29,8 +29,13 @@ int mips_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) if (n < 32) { return gdb_get_regl(mem_buf, env->active_tc.gpr[n]); } - if (env->CP0_Config1 & (1 << CP0C1_FP)) { - if (n >= 38 && n < 70) { + if (env->CP0_Config1 & (1 << CP0C1_FP) && n >= 38 && n < 72) { + switch (n) { + case 70: + return gdb_get_regl(mem_buf, (int32_t)env->active_fpu.fcr31); + case 71: + return gdb_get_regl(mem_buf, (int32_t)env->active_fpu.fcr0); + default: if (env->CP0_Status & (1 << CP0St_FR)) { return gdb_get_regl(mem_buf, env->active_fpu.fpr[n - 38].d); @@ -39,12 +44,6 @@ int mips_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) env->active_fpu.fpr[n - 38].w[FP_ENDIAN_IDX]); } } - switch (n) { - case 70: - return gdb_get_regl(mem_buf, (int32_t)env->active_fpu.fcr31); - case 71: - return gdb_get_regl(mem_buf, (int32_t)env->active_fpu.fcr0); - } } switch (n) { case 32: @@ -64,8 +63,10 @@ int mips_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) return gdb_get_regl(mem_buf, 0); /* fp */ case 89: return gdb_get_regl(mem_buf, (int32_t)env->CP0_PRid); - } - if (n >= 73 && n <= 88) { + default: + if (n > 89) { + return 0; + } /* 16 embedded regs. */ return gdb_get_regl(mem_buf, 0); } @@ -73,10 +74,6 @@ int mips_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) return 0; } -#define RESTORE_ROUNDING_MODE \ - set_float_rounding_mode(ieee_rm[env->active_fpu.fcr31 & 3], \ - &env->active_fpu.fp_status) - int mips_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) { MIPSCPU *cpu = MIPS_CPU(cs); @@ -89,30 +86,33 @@ int mips_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) env->active_tc.gpr[n] = tmp; return sizeof(target_ulong); } - if (env->CP0_Config1 & (1 << CP0C1_FP) - && n >= 38 && n < 73) { - if (n < 70) { - if (env->CP0_Status & (1 << CP0St_FR)) { - env->active_fpu.fpr[n - 38].d = tmp; - } else { - env->active_fpu.fpr[n - 38].w[FP_ENDIAN_IDX] = tmp; - } - } + if (env->CP0_Config1 & (1 << CP0C1_FP) && n >= 38 && n < 72) { switch (n) { case 70: env->active_fpu.fcr31 = tmp & 0xFF83FFFF; /* set rounding mode */ - RESTORE_ROUNDING_MODE; + restore_rounding_mode(env); + /* set flush-to-zero mode */ + restore_flush_mode(env); break; case 71: - env->active_fpu.fcr0 = tmp; + /* FIR is read-only. Ignore writes. */ + break; + default: + if (env->CP0_Status & (1 << CP0St_FR)) { + env->active_fpu.fpr[n - 38].d = tmp; + } else { + env->active_fpu.fpr[n - 38].w[FP_ENDIAN_IDX] = tmp; + } break; } return sizeof(target_ulong); } switch (n) { case 32: - env->CP0_Status = tmp; +#ifndef CONFIG_USER_ONLY + cpu_mips_store_status(env, tmp); +#endif break; case 33: env->active_tc.LO[0] = tmp; @@ -124,7 +124,9 @@ int mips_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) env->CP0_BadVAddr = tmp; break; case 36: - env->CP0_Cause = tmp; +#ifndef CONFIG_USER_ONLY + cpu_mips_store_cause(env, tmp); +#endif break; case 37: env->active_tc.PC = tmp & ~(target_ulong)1; diff --git a/target-mips/helper.c b/target-mips/helper.c index 3a93c20..8e3204a 100644 --- a/target-mips/helper.c +++ b/target-mips/helper.c @@ -341,7 +341,8 @@ int mips_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, #if 0 log_cpu_state(cs, 0); #endif - qemu_log("%s pc " TARGET_FMT_lx " ad %" VADDR_PRIx " rw %d mmu_idx %d\n", + qemu_log_mask(CPU_LOG_MMU, + "%s pc " TARGET_FMT_lx " ad %" VADDR_PRIx " rw %d mmu_idx %d\n", __func__, env->active_tc.PC, address, rw, mmu_idx); /* data access */ @@ -351,7 +352,8 @@ int mips_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, access_type = ACCESS_INT; ret = get_physical_address(env, &physical, &prot, address, rw, access_type); - qemu_log("%s address=%" VADDR_PRIx " ret %d physical " TARGET_FMT_plx + qemu_log_mask(CPU_LOG_MMU, + "%s address=%" VADDR_PRIx " ret %d physical " TARGET_FMT_plx " prot %d\n", __func__, address, ret, physical, prot); if (ret == TLBRET_MATCH) { @@ -388,7 +390,6 @@ hwaddr cpu_mips_translate_address(CPUMIPSState *env, target_ulong address, int r return physical; } } -#endif static const char * const excp_names[EXCP_LAST + 1] = { [EXCP_RESET] = "reset", @@ -429,6 +430,7 @@ static const char * const excp_names[EXCP_LAST + 1] = { [EXCP_MSADIS] = "MSA disabled", [EXCP_MSAFPE] = "MSA floating point", }; +#endif target_ulong exception_resume_pc (CPUMIPSState *env) { @@ -527,7 +529,10 @@ void mips_cpu_do_interrupt(CPUState *cs) env->CP0_DEPC = exception_resume_pc(env); env->hflags &= ~MIPS_HFLAG_BMASK; enter_debug_mode: - env->hflags |= MIPS_HFLAG_DM | MIPS_HFLAG_64 | MIPS_HFLAG_CP0; + if (env->insn_flags & ISA_MIPS3) { + env->hflags |= MIPS_HFLAG_64; + } + env->hflags |= MIPS_HFLAG_DM | MIPS_HFLAG_CP0; env->hflags &= ~(MIPS_HFLAG_KSU); /* EJTAG probe trap enable is not implemented... */ if (!(env->CP0_Status & (1 << CP0St_EXL))) @@ -548,7 +553,10 @@ void mips_cpu_do_interrupt(CPUState *cs) env->CP0_ErrorEPC = exception_resume_pc(env); env->hflags &= ~MIPS_HFLAG_BMASK; env->CP0_Status |= (1 << CP0St_ERL) | (1 << CP0St_BEV); - env->hflags |= MIPS_HFLAG_64 | MIPS_HFLAG_CP0; + if (env->insn_flags & ISA_MIPS3) { + env->hflags |= MIPS_HFLAG_64; + } + env->hflags |= MIPS_HFLAG_CP0; env->hflags &= ~(MIPS_HFLAG_KSU); if (!(env->CP0_Status & (1 << CP0St_EXL))) env->CP0_Cause &= ~(1U << CP0Ca_BD); @@ -726,7 +734,10 @@ void mips_cpu_do_interrupt(CPUState *cs) env->CP0_Cause &= ~(1U << CP0Ca_BD); } env->CP0_Status |= (1 << CP0St_EXL); - env->hflags |= MIPS_HFLAG_64 | MIPS_HFLAG_CP0; + if (env->insn_flags & ISA_MIPS3) { + env->hflags |= MIPS_HFLAG_64; + } + env->hflags |= MIPS_HFLAG_CP0; env->hflags &= ~(MIPS_HFLAG_KSU); } env->hflags &= ~MIPS_HFLAG_BMASK; diff --git a/target-mips/helper.h b/target-mips/helper.h index 9d02758..3bd0b02 100644 --- a/target-mips/helper.h +++ b/target-mips/helper.h @@ -137,6 +137,7 @@ DEF_HELPER_2(mtc0_ebase, void, env, tl) DEF_HELPER_2(mttc0_ebase, void, env, tl) DEF_HELPER_2(mtc0_config0, void, env, tl) DEF_HELPER_2(mtc0_config2, void, env, tl) +DEF_HELPER_2(mtc0_config3, void, env, tl) DEF_HELPER_2(mtc0_config4, void, env, tl) DEF_HELPER_2(mtc0_config5, void, env, tl) DEF_HELPER_2(mtc0_lladdr, void, env, tl) diff --git a/target-mips/kvm.c b/target-mips/kvm.c index 97fd51a..d5388ca 100644 --- a/target-mips/kvm.c +++ b/target-mips/kvm.c @@ -40,7 +40,7 @@ unsigned long kvm_arch_vcpu_id(CPUState *cs) return cs->cpu_index; } -int kvm_arch_init(KVMState *s) +int kvm_arch_init(MachineState *ms, KVMState *s) { /* MIPS has 128 signals */ kvm_set_sigmask_len(s, 16); @@ -240,10 +240,9 @@ int kvm_mips_set_ipi_interrupt(MIPSCPU *cpu, int irq, int level) static inline int kvm_mips_put_one_reg(CPUState *cs, uint64_t reg_id, int32_t *addr) { - uint64_t val64 = *addr; struct kvm_one_reg cp0reg = { .id = reg_id, - .addr = (uintptr_t)&val64 + .addr = (uintptr_t)addr }; return kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &cp0reg); @@ -275,18 +274,12 @@ static inline int kvm_mips_put_one_reg64(CPUState *cs, uint64_t reg_id, static inline int kvm_mips_get_one_reg(CPUState *cs, uint64_t reg_id, int32_t *addr) { - int ret; - uint64_t val64 = 0; struct kvm_one_reg cp0reg = { .id = reg_id, - .addr = (uintptr_t)&val64 + .addr = (uintptr_t)addr }; - ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &cp0reg); - if (ret >= 0) { - *addr = val64; - } - return ret; + return kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &cp0reg); } static inline int kvm_mips_get_one_ulreg(CPUState *cs, uint64 reg_id, @@ -439,7 +432,7 @@ static void kvm_mips_update_state(void *opaque, int running, RunState state) } } else { /* Set clock restore time to now */ - count_resume = get_clock(); + count_resume = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); ret = kvm_mips_put_one_reg64(cs, KVM_REG_MIPS_COUNT_RESUME, &count_resume); if (ret < 0) { @@ -640,12 +633,12 @@ int kvm_arch_put_registers(CPUState *cs, int level) /* Set the registers based on QEMU's view of things */ for (i = 0; i < 32; i++) { - regs.gpr[i] = env->active_tc.gpr[i]; + regs.gpr[i] = (int64_t)(target_long)env->active_tc.gpr[i]; } - regs.hi = env->active_tc.HI[0]; - regs.lo = env->active_tc.LO[0]; - regs.pc = env->active_tc.PC; + regs.hi = (int64_t)(target_long)env->active_tc.HI[0]; + regs.lo = (int64_t)(target_long)env->active_tc.LO[0]; + regs.pc = (int64_t)(target_long)env->active_tc.PC; ret = kvm_vcpu_ioctl(cs, KVM_SET_REGS, ®s); @@ -688,3 +681,9 @@ int kvm_arch_get_registers(CPUState *cs) return ret; } + +int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, + uint64_t address, uint32_t data) +{ + return 0; +} diff --git a/target-mips/machine.c b/target-mips/machine.c index 0ba7d73..7d1fa32 100644 --- a/target-mips/machine.c +++ b/target-mips/machine.c @@ -1,339 +1,296 @@ #include "hw/hw.h" -#include "hw/boards.h" #include "cpu.h" -static void save_tc(QEMUFile *f, TCState *tc) +static int cpu_post_load(void *opaque, int version_id) { - int i; + MIPSCPU *cpu = opaque; + CPUMIPSState *env = &cpu->env; + + restore_fp_status(env); + restore_msa_fp_status(env); + compute_hflags(env); - /* Save active TC */ - for(i = 0; i < 32; i++) - qemu_put_betls(f, &tc->gpr[i]); - qemu_put_betls(f, &tc->PC); - for(i = 0; i < MIPS_DSP_ACC; i++) - qemu_put_betls(f, &tc->HI[i]); - for(i = 0; i < MIPS_DSP_ACC; i++) - qemu_put_betls(f, &tc->LO[i]); - for(i = 0; i < MIPS_DSP_ACC; i++) - qemu_put_betls(f, &tc->ACX[i]); - qemu_put_betls(f, &tc->DSPControl); - qemu_put_sbe32s(f, &tc->CP0_TCStatus); - qemu_put_sbe32s(f, &tc->CP0_TCBind); - qemu_put_betls(f, &tc->CP0_TCHalt); - qemu_put_betls(f, &tc->CP0_TCContext); - qemu_put_betls(f, &tc->CP0_TCSchedule); - qemu_put_betls(f, &tc->CP0_TCScheFBack); - qemu_put_sbe32s(f, &tc->CP0_Debug_tcstatus); - qemu_put_betls(f, &tc->CP0_UserLocal); + return 0; } -static void save_fpu(QEMUFile *f, CPUMIPSFPUContext *fpu) +/* FPU state */ + +static int get_fpr(QEMUFile *f, void *pv, size_t size) { int i; - - for(i = 0; i < 32; i++) - qemu_put_be64s(f, &fpu->fpr[i].d); - qemu_put_s8s(f, &fpu->fp_status.float_detect_tininess); - qemu_put_s8s(f, &fpu->fp_status.float_rounding_mode); - qemu_put_s8s(f, &fpu->fp_status.float_exception_flags); - qemu_put_be32s(f, &fpu->fcr0); - qemu_put_be32s(f, &fpu->fcr31); + fpr_t *v = pv; + /* Restore entire MSA vector register */ + for (i = 0; i < MSA_WRLEN/64; i++) { + qemu_get_sbe64s(f, &v->wr.d[i]); + } + return 0; } -void cpu_save(QEMUFile *f, void *opaque) +static void put_fpr(QEMUFile *f, void *pv, size_t size) { - CPUMIPSState *env = opaque; int i; - - /* Save active TC */ - save_tc(f, &env->active_tc); - - /* Save active FPU */ - save_fpu(f, &env->active_fpu); - - /* Save MVP */ - qemu_put_sbe32s(f, &env->mvp->CP0_MVPControl); - qemu_put_sbe32s(f, &env->mvp->CP0_MVPConf0); - qemu_put_sbe32s(f, &env->mvp->CP0_MVPConf1); - - /* Save TLB */ - qemu_put_be32s(f, &env->tlb->nb_tlb); - qemu_put_be32s(f, &env->tlb->tlb_in_use); - for(i = 0; i < MIPS_TLB_MAX; i++) { - uint16_t flags = ((env->tlb->mmu.r4k.tlb[i].EHINV << 15) | - (env->tlb->mmu.r4k.tlb[i].RI1 << 14) | - (env->tlb->mmu.r4k.tlb[i].RI0 << 13) | - (env->tlb->mmu.r4k.tlb[i].XI1 << 12) | - (env->tlb->mmu.r4k.tlb[i].XI0 << 11) | - (env->tlb->mmu.r4k.tlb[i].G << 10) | - (env->tlb->mmu.r4k.tlb[i].C0 << 7) | - (env->tlb->mmu.r4k.tlb[i].C1 << 4) | - (env->tlb->mmu.r4k.tlb[i].V0 << 3) | - (env->tlb->mmu.r4k.tlb[i].V1 << 2) | - (env->tlb->mmu.r4k.tlb[i].D0 << 1) | - (env->tlb->mmu.r4k.tlb[i].D1 << 0)); - uint8_t asid; - - qemu_put_betls(f, &env->tlb->mmu.r4k.tlb[i].VPN); - qemu_put_be32s(f, &env->tlb->mmu.r4k.tlb[i].PageMask); - asid = env->tlb->mmu.r4k.tlb[i].ASID; - qemu_put_8s(f, &asid); - qemu_put_be16s(f, &flags); - qemu_put_betls(f, &env->tlb->mmu.r4k.tlb[i].PFN[0]); - qemu_put_betls(f, &env->tlb->mmu.r4k.tlb[i].PFN[1]); + fpr_t *v = pv; + /* Save entire MSA vector register */ + for (i = 0; i < MSA_WRLEN/64; i++) { + qemu_put_sbe64s(f, &v->wr.d[i]); } +} + +const VMStateInfo vmstate_info_fpr = { + .name = "fpr", + .get = get_fpr, + .put = put_fpr, +}; + +#define VMSTATE_FPR_ARRAY_V(_f, _s, _n, _v) \ + VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_fpr, fpr_t) - /* Save CPU metastate */ - qemu_put_be32s(f, &env->current_tc); - qemu_put_be32s(f, &env->current_fpu); - qemu_put_sbe32s(f, &env->error_code); - qemu_put_be32s(f, &env->hflags); - qemu_put_betls(f, &env->btarget); - i = env->bcond; - qemu_put_sbe32s(f, &i); - - /* Save remaining CP1 registers */ - qemu_put_sbe32s(f, &env->CP0_Index); - qemu_put_sbe32s(f, &env->CP0_Random); - qemu_put_sbe32s(f, &env->CP0_VPEControl); - qemu_put_sbe32s(f, &env->CP0_VPEConf0); - qemu_put_sbe32s(f, &env->CP0_VPEConf1); - qemu_put_betls(f, &env->CP0_YQMask); - qemu_put_betls(f, &env->CP0_VPESchedule); - qemu_put_betls(f, &env->CP0_VPEScheFBack); - qemu_put_sbe32s(f, &env->CP0_VPEOpt); - qemu_put_betls(f, &env->CP0_EntryLo0); - qemu_put_betls(f, &env->CP0_EntryLo1); - qemu_put_betls(f, &env->CP0_Context); - qemu_put_sbe32s(f, &env->CP0_PageMask); - qemu_put_sbe32s(f, &env->CP0_PageGrain); - qemu_put_sbe32s(f, &env->CP0_Wired); - qemu_put_sbe32s(f, &env->CP0_SRSConf0); - qemu_put_sbe32s(f, &env->CP0_SRSConf1); - qemu_put_sbe32s(f, &env->CP0_SRSConf2); - qemu_put_sbe32s(f, &env->CP0_SRSConf3); - qemu_put_sbe32s(f, &env->CP0_SRSConf4); - qemu_put_sbe32s(f, &env->CP0_HWREna); - qemu_put_betls(f, &env->CP0_BadVAddr); - qemu_put_be32s(f, &env->CP0_BadInstr); - qemu_put_be32s(f, &env->CP0_BadInstrP); - qemu_put_sbe32s(f, &env->CP0_Count); - qemu_put_betls(f, &env->CP0_EntryHi); - qemu_put_sbe32s(f, &env->CP0_Compare); - qemu_put_sbe32s(f, &env->CP0_Status); - qemu_put_sbe32s(f, &env->CP0_IntCtl); - qemu_put_sbe32s(f, &env->CP0_SRSCtl); - qemu_put_sbe32s(f, &env->CP0_SRSMap); - qemu_put_sbe32s(f, &env->CP0_Cause); - qemu_put_betls(f, &env->CP0_EPC); - qemu_put_sbe32s(f, &env->CP0_PRid); - qemu_put_sbe32s(f, &env->CP0_EBase); - qemu_put_sbe32s(f, &env->CP0_Config0); - qemu_put_sbe32s(f, &env->CP0_Config1); - qemu_put_sbe32s(f, &env->CP0_Config2); - qemu_put_sbe32s(f, &env->CP0_Config3); - qemu_put_sbe32s(f, &env->CP0_Config6); - qemu_put_sbe32s(f, &env->CP0_Config7); - qemu_put_betls(f, &env->lladdr); - for(i = 0; i < 8; i++) - qemu_put_betls(f, &env->CP0_WatchLo[i]); - for(i = 0; i < 8; i++) - qemu_put_sbe32s(f, &env->CP0_WatchHi[i]); - qemu_put_betls(f, &env->CP0_XContext); - qemu_put_sbe32s(f, &env->CP0_Framemask); - qemu_put_sbe32s(f, &env->CP0_Debug); - qemu_put_betls(f, &env->CP0_DEPC); - qemu_put_sbe32s(f, &env->CP0_Performance0); - qemu_put_sbe32s(f, &env->CP0_TagLo); - qemu_put_sbe32s(f, &env->CP0_DataLo); - qemu_put_sbe32s(f, &env->CP0_TagHi); - qemu_put_sbe32s(f, &env->CP0_DataHi); - qemu_put_betls(f, &env->CP0_ErrorEPC); - qemu_put_sbe32s(f, &env->CP0_DESAVE); - for (i = 0; i < MIPS_KSCRATCH_NUM; i++) { - qemu_put_betls(f, &env->CP0_KScratch[i]); +#define VMSTATE_FPR_ARRAY(_f, _s, _n) \ + VMSTATE_FPR_ARRAY_V(_f, _s, _n, 0) + +static VMStateField vmstate_fpu_fields[] = { + VMSTATE_FPR_ARRAY(fpr, CPUMIPSFPUContext, 32), + VMSTATE_UINT32(fcr0, CPUMIPSFPUContext), + VMSTATE_UINT32(fcr31, CPUMIPSFPUContext), + VMSTATE_END_OF_LIST() +}; + +const VMStateDescription vmstate_fpu = { + .name = "cpu/fpu", + .version_id = 1, + .minimum_version_id = 1, + .fields = vmstate_fpu_fields +}; + +const VMStateDescription vmstate_inactive_fpu = { + .name = "cpu/inactive_fpu", + .version_id = 1, + .minimum_version_id = 1, + .fields = vmstate_fpu_fields +}; + +/* TC state */ + +static VMStateField vmstate_tc_fields[] = { + VMSTATE_UINTTL_ARRAY(gpr, TCState, 32), + VMSTATE_UINTTL(PC, TCState), + VMSTATE_UINTTL_ARRAY(HI, TCState, MIPS_DSP_ACC), + VMSTATE_UINTTL_ARRAY(LO, TCState, MIPS_DSP_ACC), + VMSTATE_UINTTL_ARRAY(ACX, TCState, MIPS_DSP_ACC), + VMSTATE_UINTTL(DSPControl, TCState), + VMSTATE_INT32(CP0_TCStatus, TCState), + VMSTATE_INT32(CP0_TCBind, TCState), + VMSTATE_UINTTL(CP0_TCHalt, TCState), + VMSTATE_UINTTL(CP0_TCContext, TCState), + VMSTATE_UINTTL(CP0_TCSchedule, TCState), + VMSTATE_UINTTL(CP0_TCScheFBack, TCState), + VMSTATE_INT32(CP0_Debug_tcstatus, TCState), + VMSTATE_UINTTL(CP0_UserLocal, TCState), + VMSTATE_INT32(msacsr, TCState), + VMSTATE_END_OF_LIST() +}; + +const VMStateDescription vmstate_tc = { + .name = "cpu/tc", + .version_id = 1, + .minimum_version_id = 1, + .fields = vmstate_tc_fields +}; + +const VMStateDescription vmstate_inactive_tc = { + .name = "cpu/inactive_tc", + .version_id = 1, + .minimum_version_id = 1, + .fields = vmstate_tc_fields +}; + +/* MVP state */ + +const VMStateDescription vmstate_mvp = { + .name = "cpu/mvp", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_INT32(CP0_MVPControl, CPUMIPSMVPContext), + VMSTATE_INT32(CP0_MVPConf0, CPUMIPSMVPContext), + VMSTATE_INT32(CP0_MVPConf1, CPUMIPSMVPContext), + VMSTATE_END_OF_LIST() } +}; - /* Save inactive TC state */ - for (i = 0; i < MIPS_SHADOW_SET_MAX; i++) - save_tc(f, &env->tcs[i]); - for (i = 0; i < MIPS_FPU_MAX; i++) - save_fpu(f, &env->fpus[i]); -} +/* TLB state */ -static void load_tc(QEMUFile *f, TCState *tc, int version_id) +static int get_tlb(QEMUFile *f, void *pv, size_t size) { - int i; + r4k_tlb_t *v = pv; + uint16_t flags; - /* Save active TC */ - for(i = 0; i < 32; i++) - qemu_get_betls(f, &tc->gpr[i]); - qemu_get_betls(f, &tc->PC); - for(i = 0; i < MIPS_DSP_ACC; i++) - qemu_get_betls(f, &tc->HI[i]); - for(i = 0; i < MIPS_DSP_ACC; i++) - qemu_get_betls(f, &tc->LO[i]); - for(i = 0; i < MIPS_DSP_ACC; i++) - qemu_get_betls(f, &tc->ACX[i]); - qemu_get_betls(f, &tc->DSPControl); - qemu_get_sbe32s(f, &tc->CP0_TCStatus); - qemu_get_sbe32s(f, &tc->CP0_TCBind); - qemu_get_betls(f, &tc->CP0_TCHalt); - qemu_get_betls(f, &tc->CP0_TCContext); - qemu_get_betls(f, &tc->CP0_TCSchedule); - qemu_get_betls(f, &tc->CP0_TCScheFBack); - qemu_get_sbe32s(f, &tc->CP0_Debug_tcstatus); - if (version_id >= 4) { - qemu_get_betls(f, &tc->CP0_UserLocal); - } + qemu_get_betls(f, &v->VPN); + qemu_get_be32s(f, &v->PageMask); + qemu_get_8s(f, &v->ASID); + qemu_get_be16s(f, &flags); + v->G = (flags >> 10) & 1; + v->C0 = (flags >> 7) & 3; + v->C1 = (flags >> 4) & 3; + v->V0 = (flags >> 3) & 1; + v->V1 = (flags >> 2) & 1; + v->D0 = (flags >> 1) & 1; + v->D1 = (flags >> 0) & 1; + v->EHINV = (flags >> 15) & 1; + v->RI1 = (flags >> 14) & 1; + v->RI0 = (flags >> 13) & 1; + v->XI1 = (flags >> 12) & 1; + v->XI0 = (flags >> 11) & 1; + qemu_get_betls(f, &v->PFN[0]); + qemu_get_betls(f, &v->PFN[1]); + + return 0; } -static void load_fpu(QEMUFile *f, CPUMIPSFPUContext *fpu) +static void put_tlb(QEMUFile *f, void *pv, size_t size) { - int i; + r4k_tlb_t *v = pv; - for(i = 0; i < 32; i++) - qemu_get_be64s(f, &fpu->fpr[i].d); - qemu_get_s8s(f, &fpu->fp_status.float_detect_tininess); - qemu_get_s8s(f, &fpu->fp_status.float_rounding_mode); - qemu_get_s8s(f, &fpu->fp_status.float_exception_flags); - qemu_get_be32s(f, &fpu->fcr0); - qemu_get_be32s(f, &fpu->fcr31); + uint16_t flags = ((v->EHINV << 15) | + (v->RI1 << 14) | + (v->RI0 << 13) | + (v->XI1 << 12) | + (v->XI0 << 11) | + (v->G << 10) | + (v->C0 << 7) | + (v->C1 << 4) | + (v->V0 << 3) | + (v->V1 << 2) | + (v->D0 << 1) | + (v->D1 << 0)); + + qemu_put_betls(f, &v->VPN); + qemu_put_be32s(f, &v->PageMask); + qemu_put_8s(f, &v->ASID); + qemu_put_be16s(f, &flags); + qemu_put_betls(f, &v->PFN[0]); + qemu_put_betls(f, &v->PFN[1]); } -int cpu_load(QEMUFile *f, void *opaque, int version_id) -{ - CPUMIPSState *env = opaque; - MIPSCPU *cpu = mips_env_get_cpu(env); - int i; +const VMStateInfo vmstate_info_tlb = { + .name = "tlb_entry", + .get = get_tlb, + .put = put_tlb, +}; - if (version_id < 3) { - return -EINVAL; - } +#define VMSTATE_TLB_ARRAY_V(_f, _s, _n, _v) \ + VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_tlb, r4k_tlb_t) - /* Load active TC */ - load_tc(f, &env->active_tc, version_id); - - /* Load active FPU */ - load_fpu(f, &env->active_fpu); - - /* Load MVP */ - qemu_get_sbe32s(f, &env->mvp->CP0_MVPControl); - qemu_get_sbe32s(f, &env->mvp->CP0_MVPConf0); - qemu_get_sbe32s(f, &env->mvp->CP0_MVPConf1); - - /* Load TLB */ - qemu_get_be32s(f, &env->tlb->nb_tlb); - qemu_get_be32s(f, &env->tlb->tlb_in_use); - for(i = 0; i < MIPS_TLB_MAX; i++) { - uint16_t flags; - uint8_t asid; - - qemu_get_betls(f, &env->tlb->mmu.r4k.tlb[i].VPN); - qemu_get_be32s(f, &env->tlb->mmu.r4k.tlb[i].PageMask); - qemu_get_8s(f, &asid); - env->tlb->mmu.r4k.tlb[i].ASID = asid; - qemu_get_be16s(f, &flags); - env->tlb->mmu.r4k.tlb[i].G = (flags >> 10) & 1; - env->tlb->mmu.r4k.tlb[i].C0 = (flags >> 7) & 3; - env->tlb->mmu.r4k.tlb[i].C1 = (flags >> 4) & 3; - env->tlb->mmu.r4k.tlb[i].V0 = (flags >> 3) & 1; - env->tlb->mmu.r4k.tlb[i].V1 = (flags >> 2) & 1; - env->tlb->mmu.r4k.tlb[i].D0 = (flags >> 1) & 1; - env->tlb->mmu.r4k.tlb[i].D1 = (flags >> 0) & 1; - if (version_id >= 5) { - env->tlb->mmu.r4k.tlb[i].EHINV = (flags >> 15) & 1; - env->tlb->mmu.r4k.tlb[i].RI1 = (flags >> 14) & 1; - env->tlb->mmu.r4k.tlb[i].RI0 = (flags >> 13) & 1; - env->tlb->mmu.r4k.tlb[i].XI1 = (flags >> 12) & 1; - env->tlb->mmu.r4k.tlb[i].XI0 = (flags >> 11) & 1; - } - qemu_get_betls(f, &env->tlb->mmu.r4k.tlb[i].PFN[0]); - qemu_get_betls(f, &env->tlb->mmu.r4k.tlb[i].PFN[1]); - } +#define VMSTATE_TLB_ARRAY(_f, _s, _n) \ + VMSTATE_TLB_ARRAY_V(_f, _s, _n, 0) - /* Load CPU metastate */ - qemu_get_be32s(f, &env->current_tc); - qemu_get_be32s(f, &env->current_fpu); - qemu_get_sbe32s(f, &env->error_code); - qemu_get_be32s(f, &env->hflags); - qemu_get_betls(f, &env->btarget); - qemu_get_sbe32s(f, &i); - env->bcond = i; - - /* Load remaining CP1 registers */ - qemu_get_sbe32s(f, &env->CP0_Index); - qemu_get_sbe32s(f, &env->CP0_Random); - qemu_get_sbe32s(f, &env->CP0_VPEControl); - qemu_get_sbe32s(f, &env->CP0_VPEConf0); - qemu_get_sbe32s(f, &env->CP0_VPEConf1); - qemu_get_betls(f, &env->CP0_YQMask); - qemu_get_betls(f, &env->CP0_VPESchedule); - qemu_get_betls(f, &env->CP0_VPEScheFBack); - qemu_get_sbe32s(f, &env->CP0_VPEOpt); - qemu_get_betls(f, &env->CP0_EntryLo0); - qemu_get_betls(f, &env->CP0_EntryLo1); - qemu_get_betls(f, &env->CP0_Context); - qemu_get_sbe32s(f, &env->CP0_PageMask); - qemu_get_sbe32s(f, &env->CP0_PageGrain); - qemu_get_sbe32s(f, &env->CP0_Wired); - qemu_get_sbe32s(f, &env->CP0_SRSConf0); - qemu_get_sbe32s(f, &env->CP0_SRSConf1); - qemu_get_sbe32s(f, &env->CP0_SRSConf2); - qemu_get_sbe32s(f, &env->CP0_SRSConf3); - qemu_get_sbe32s(f, &env->CP0_SRSConf4); - qemu_get_sbe32s(f, &env->CP0_HWREna); - qemu_get_betls(f, &env->CP0_BadVAddr); - qemu_get_sbe32s(f, &env->CP0_Count); - qemu_get_betls(f, &env->CP0_EntryHi); - qemu_get_sbe32s(f, &env->CP0_Compare); - qemu_get_sbe32s(f, &env->CP0_Status); - qemu_get_sbe32s(f, &env->CP0_IntCtl); - qemu_get_sbe32s(f, &env->CP0_SRSCtl); - qemu_get_sbe32s(f, &env->CP0_SRSMap); - qemu_get_sbe32s(f, &env->CP0_Cause); - qemu_get_betls(f, &env->CP0_EPC); - qemu_get_sbe32s(f, &env->CP0_PRid); - qemu_get_sbe32s(f, &env->CP0_EBase); - qemu_get_sbe32s(f, &env->CP0_Config0); - qemu_get_sbe32s(f, &env->CP0_Config1); - qemu_get_sbe32s(f, &env->CP0_Config2); - qemu_get_sbe32s(f, &env->CP0_Config3); - qemu_get_sbe32s(f, &env->CP0_Config6); - qemu_get_sbe32s(f, &env->CP0_Config7); - qemu_get_betls(f, &env->lladdr); - for(i = 0; i < 8; i++) - qemu_get_betls(f, &env->CP0_WatchLo[i]); - for(i = 0; i < 8; i++) - qemu_get_sbe32s(f, &env->CP0_WatchHi[i]); - qemu_get_betls(f, &env->CP0_XContext); - qemu_get_sbe32s(f, &env->CP0_Framemask); - qemu_get_sbe32s(f, &env->CP0_Debug); - qemu_get_betls(f, &env->CP0_DEPC); - qemu_get_sbe32s(f, &env->CP0_Performance0); - qemu_get_sbe32s(f, &env->CP0_TagLo); - qemu_get_sbe32s(f, &env->CP0_DataLo); - qemu_get_sbe32s(f, &env->CP0_TagHi); - qemu_get_sbe32s(f, &env->CP0_DataHi); - qemu_get_betls(f, &env->CP0_ErrorEPC); - qemu_get_sbe32s(f, &env->CP0_DESAVE); - if (version_id >= 5) { - qemu_get_be32s(f, &env->CP0_BadInstr); - qemu_get_be32s(f, &env->CP0_BadInstrP); - for (i = 0; i < MIPS_KSCRATCH_NUM; i++) { - qemu_get_betls(f, &env->CP0_KScratch[i]); - } +const VMStateDescription vmstate_tlb = { + .name = "cpu/tlb", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(nb_tlb, CPUMIPSTLBContext), + VMSTATE_UINT32(tlb_in_use, CPUMIPSTLBContext), + VMSTATE_TLB_ARRAY(mmu.r4k.tlb, CPUMIPSTLBContext, MIPS_TLB_MAX), + VMSTATE_END_OF_LIST() } +}; - /* Load inactive TC state */ - for (i = 0; i < MIPS_SHADOW_SET_MAX; i++) { - load_tc(f, &env->tcs[i], version_id); - } - for (i = 0; i < MIPS_FPU_MAX; i++) - load_fpu(f, &env->fpus[i]); +/* MIPS CPU state */ - /* XXX: ensure compatibility for halted bit ? */ - tlb_flush(CPU(cpu), 1); - return 0; -} +const VMStateDescription vmstate_mips_cpu = { + .name = "cpu", + .version_id = 6, + .minimum_version_id = 6, + .post_load = cpu_post_load, + .fields = (VMStateField[]) { + /* Active TC */ + VMSTATE_STRUCT(env.active_tc, MIPSCPU, 1, vmstate_tc, TCState), + + /* Active FPU */ + VMSTATE_STRUCT(env.active_fpu, MIPSCPU, 1, vmstate_fpu, + CPUMIPSFPUContext), + + /* MVP */ + VMSTATE_STRUCT_POINTER(env.mvp, MIPSCPU, vmstate_mvp, + CPUMIPSMVPContext), + + /* TLB */ + VMSTATE_STRUCT_POINTER(env.tlb, MIPSCPU, vmstate_tlb, + CPUMIPSTLBContext), + + /* CPU metastate */ + VMSTATE_UINT32(env.current_tc, MIPSCPU), + VMSTATE_UINT32(env.current_fpu, MIPSCPU), + VMSTATE_INT32(env.error_code, MIPSCPU), + VMSTATE_UINTTL(env.btarget, MIPSCPU), + VMSTATE_UINTTL(env.bcond, MIPSCPU), + + /* Remaining CP0 registers */ + VMSTATE_INT32(env.CP0_Index, MIPSCPU), + VMSTATE_INT32(env.CP0_Random, MIPSCPU), + VMSTATE_INT32(env.CP0_VPEControl, MIPSCPU), + VMSTATE_INT32(env.CP0_VPEConf0, MIPSCPU), + VMSTATE_INT32(env.CP0_VPEConf1, MIPSCPU), + VMSTATE_UINTTL(env.CP0_YQMask, MIPSCPU), + VMSTATE_UINTTL(env.CP0_VPESchedule, MIPSCPU), + VMSTATE_UINTTL(env.CP0_VPEScheFBack, MIPSCPU), + VMSTATE_INT32(env.CP0_VPEOpt, MIPSCPU), + VMSTATE_UINTTL(env.CP0_EntryLo0, MIPSCPU), + VMSTATE_UINTTL(env.CP0_EntryLo1, MIPSCPU), + VMSTATE_UINTTL(env.CP0_Context, MIPSCPU), + VMSTATE_INT32(env.CP0_PageMask, MIPSCPU), + VMSTATE_INT32(env.CP0_PageGrain, MIPSCPU), + VMSTATE_INT32(env.CP0_Wired, MIPSCPU), + VMSTATE_INT32(env.CP0_SRSConf0, MIPSCPU), + VMSTATE_INT32(env.CP0_SRSConf1, MIPSCPU), + VMSTATE_INT32(env.CP0_SRSConf2, MIPSCPU), + VMSTATE_INT32(env.CP0_SRSConf3, MIPSCPU), + VMSTATE_INT32(env.CP0_SRSConf4, MIPSCPU), + VMSTATE_INT32(env.CP0_HWREna, MIPSCPU), + VMSTATE_UINTTL(env.CP0_BadVAddr, MIPSCPU), + VMSTATE_UINT32(env.CP0_BadInstr, MIPSCPU), + VMSTATE_UINT32(env.CP0_BadInstrP, MIPSCPU), + VMSTATE_INT32(env.CP0_Count, MIPSCPU), + VMSTATE_UINTTL(env.CP0_EntryHi, MIPSCPU), + VMSTATE_INT32(env.CP0_Compare, MIPSCPU), + VMSTATE_INT32(env.CP0_Status, MIPSCPU), + VMSTATE_INT32(env.CP0_IntCtl, MIPSCPU), + VMSTATE_INT32(env.CP0_SRSCtl, MIPSCPU), + VMSTATE_INT32(env.CP0_SRSMap, MIPSCPU), + VMSTATE_INT32(env.CP0_Cause, MIPSCPU), + VMSTATE_UINTTL(env.CP0_EPC, MIPSCPU), + VMSTATE_INT32(env.CP0_PRid, MIPSCPU), + VMSTATE_INT32(env.CP0_EBase, MIPSCPU), + VMSTATE_INT32(env.CP0_Config0, MIPSCPU), + VMSTATE_INT32(env.CP0_Config1, MIPSCPU), + VMSTATE_INT32(env.CP0_Config2, MIPSCPU), + VMSTATE_INT32(env.CP0_Config3, MIPSCPU), + VMSTATE_INT32(env.CP0_Config6, MIPSCPU), + VMSTATE_INT32(env.CP0_Config7, MIPSCPU), + VMSTATE_UINTTL(env.lladdr, MIPSCPU), + VMSTATE_UINTTL_ARRAY(env.CP0_WatchLo, MIPSCPU, 8), + VMSTATE_INT32_ARRAY(env.CP0_WatchHi, MIPSCPU, 8), + VMSTATE_UINTTL(env.CP0_XContext, MIPSCPU), + VMSTATE_INT32(env.CP0_Framemask, MIPSCPU), + VMSTATE_INT32(env.CP0_Debug, MIPSCPU), + VMSTATE_UINTTL(env.CP0_DEPC, MIPSCPU), + VMSTATE_INT32(env.CP0_Performance0, MIPSCPU), + VMSTATE_INT32(env.CP0_TagLo, MIPSCPU), + VMSTATE_INT32(env.CP0_DataLo, MIPSCPU), + VMSTATE_INT32(env.CP0_TagHi, MIPSCPU), + VMSTATE_INT32(env.CP0_DataHi, MIPSCPU), + VMSTATE_UINTTL(env.CP0_ErrorEPC, MIPSCPU), + VMSTATE_INT32(env.CP0_DESAVE, MIPSCPU), + VMSTATE_UINTTL_ARRAY(env.CP0_KScratch, MIPSCPU, MIPS_KSCRATCH_NUM), + + /* Inactive TC */ + VMSTATE_STRUCT_ARRAY(env.tcs, MIPSCPU, MIPS_SHADOW_SET_MAX, 1, + vmstate_inactive_tc, TCState), + VMSTATE_STRUCT_ARRAY(env.fpus, MIPSCPU, MIPS_FPU_MAX, 1, + vmstate_inactive_fpu, CPUMIPSFPUContext), + + VMSTATE_END_OF_LIST() + }, +}; diff --git a/target-mips/msa_helper.c b/target-mips/msa_helper.c index b08f37f..26ffdc7 100644 --- a/target-mips/msa_helper.c +++ b/target-mips/msa_helper.c @@ -1348,17 +1348,7 @@ void helper_msa_ctcmsa(CPUMIPSState *env, target_ulong elm, uint32_t cd) break; case 1: env->active_tc.msacsr = (int32_t)elm & MSACSR_MASK; - /* set float_status rounding mode */ - set_float_rounding_mode( - ieee_rm[(env->active_tc.msacsr & MSACSR_RM_MASK) >> MSACSR_RM], - &env->active_tc.msa_fp_status); - /* set float_status flush modes */ - set_flush_to_zero( - (env->active_tc.msacsr & MSACSR_FS_MASK) != 0 ? 1 : 0, - &env->active_tc.msa_fp_status); - set_flush_inputs_to_zero( - (env->active_tc.msacsr & MSACSR_FS_MASK) != 0 ? 1 : 0, - &env->active_tc.msa_fp_status); + restore_msa_fp_status(env); /* check exception */ if ((GET_FP_ENABLE(env->active_tc.msacsr) | FP_UNIMPLEMENTED) & GET_FP_CAUSE(env->active_tc.msacsr)) { @@ -1614,69 +1604,71 @@ static inline int get_enabled_exceptions(const CPUMIPSState *env, int c) return c & enable; } -static inline float16 float16_from_float32(int32 a, flag ieee STATUS_PARAM) +static inline float16 float16_from_float32(int32 a, flag ieee, + float_status *status) { float16 f_val; - f_val = float32_to_float16((float32)a, ieee STATUS_VAR); + f_val = float32_to_float16((float32)a, ieee, status); f_val = float16_maybe_silence_nan(f_val); return a < 0 ? (f_val | (1 << 15)) : f_val; } -static inline float32 float32_from_float64(int64 a STATUS_PARAM) +static inline float32 float32_from_float64(int64 a, float_status *status) { float32 f_val; - f_val = float64_to_float32((float64)a STATUS_VAR); + f_val = float64_to_float32((float64)a, status); f_val = float32_maybe_silence_nan(f_val); return a < 0 ? (f_val | (1 << 31)) : f_val; } -static inline float32 float32_from_float16(int16_t a, flag ieee STATUS_PARAM) +static inline float32 float32_from_float16(int16_t a, flag ieee, + float_status *status) { float32 f_val; - f_val = float16_to_float32((float16)a, ieee STATUS_VAR); + f_val = float16_to_float32((float16)a, ieee, status); f_val = float32_maybe_silence_nan(f_val); return a < 0 ? (f_val | (1 << 31)) : f_val; } -static inline float64 float64_from_float32(int32 a STATUS_PARAM) +static inline float64 float64_from_float32(int32 a, float_status *status) { float64 f_val; - f_val = float32_to_float64((float64)a STATUS_VAR); + f_val = float32_to_float64((float64)a, status); f_val = float64_maybe_silence_nan(f_val); return a < 0 ? (f_val | (1ULL << 63)) : f_val; } -static inline float32 float32_from_q16(int16_t a STATUS_PARAM) +static inline float32 float32_from_q16(int16_t a, float_status *status) { float32 f_val; /* conversion as integer and scaling */ - f_val = int32_to_float32(a STATUS_VAR); - f_val = float32_scalbn(f_val, -15 STATUS_VAR); + f_val = int32_to_float32(a, status); + f_val = float32_scalbn(f_val, -15, status); return f_val; } -static inline float64 float64_from_q32(int32 a STATUS_PARAM) +static inline float64 float64_from_q32(int32 a, float_status *status) { float64 f_val; /* conversion as integer and scaling */ - f_val = int32_to_float64(a STATUS_VAR); - f_val = float64_scalbn(f_val, -31 STATUS_VAR); + f_val = int32_to_float64(a, status); + f_val = float64_scalbn(f_val, -31, status); return f_val; } -static inline int16_t float32_to_q16(float32 a STATUS_PARAM) +static inline int16_t float32_to_q16(float32 a, float_status *status) { int32 q_val; int32 q_min = 0xffff8000; @@ -1685,50 +1677,50 @@ static inline int16_t float32_to_q16(float32 a STATUS_PARAM) int ieee_ex; if (float32_is_any_nan(a)) { - float_raise(float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return 0; } /* scaling */ - a = float32_scalbn(a, 15 STATUS_VAR); + a = float32_scalbn(a, 15, status); ieee_ex = get_float_exception_flags(status); set_float_exception_flags(ieee_ex & (~float_flag_underflow) - STATUS_VAR); + , status); if (ieee_ex & float_flag_overflow) { - float_raise(float_flag_inexact STATUS_VAR); + float_raise(float_flag_inexact, status); return (int32)a < 0 ? q_min : q_max; } /* conversion to int */ - q_val = float32_to_int32(a STATUS_VAR); + q_val = float32_to_int32(a, status); ieee_ex = get_float_exception_flags(status); set_float_exception_flags(ieee_ex & (~float_flag_underflow) - STATUS_VAR); + , status); if (ieee_ex & float_flag_invalid) { set_float_exception_flags(ieee_ex & (~float_flag_invalid) - STATUS_VAR); - float_raise(float_flag_overflow | float_flag_inexact STATUS_VAR); + , status); + float_raise(float_flag_overflow | float_flag_inexact, status); return (int32)a < 0 ? q_min : q_max; } if (q_val < q_min) { - float_raise(float_flag_overflow | float_flag_inexact STATUS_VAR); + float_raise(float_flag_overflow | float_flag_inexact, status); return (int16_t)q_min; } if (q_max < q_val) { - float_raise(float_flag_overflow | float_flag_inexact STATUS_VAR); + float_raise(float_flag_overflow | float_flag_inexact, status); return (int16_t)q_max; } return (int16_t)q_val; } -static inline int32 float64_to_q32(float64 a STATUS_PARAM) +static inline int32 float64_to_q32(float64 a, float_status *status) { int64 q_val; int64 q_min = 0xffffffff80000000LL; @@ -1737,43 +1729,43 @@ static inline int32 float64_to_q32(float64 a STATUS_PARAM) int ieee_ex; if (float64_is_any_nan(a)) { - float_raise(float_flag_invalid STATUS_VAR); + float_raise(float_flag_invalid, status); return 0; } /* scaling */ - a = float64_scalbn(a, 31 STATUS_VAR); + a = float64_scalbn(a, 31, status); ieee_ex = get_float_exception_flags(status); set_float_exception_flags(ieee_ex & (~float_flag_underflow) - STATUS_VAR); + , status); if (ieee_ex & float_flag_overflow) { - float_raise(float_flag_inexact STATUS_VAR); + float_raise(float_flag_inexact, status); return (int64)a < 0 ? q_min : q_max; } /* conversion to integer */ - q_val = float64_to_int64(a STATUS_VAR); + q_val = float64_to_int64(a, status); ieee_ex = get_float_exception_flags(status); set_float_exception_flags(ieee_ex & (~float_flag_underflow) - STATUS_VAR); + , status); if (ieee_ex & float_flag_invalid) { set_float_exception_flags(ieee_ex & (~float_flag_invalid) - STATUS_VAR); - float_raise(float_flag_overflow | float_flag_inexact STATUS_VAR); + , status); + float_raise(float_flag_overflow | float_flag_inexact, status); return (int64)a < 0 ? q_min : q_max; } if (q_val < q_min) { - float_raise(float_flag_overflow | float_flag_inexact STATUS_VAR); + float_raise(float_flag_overflow | float_flag_inexact, status); return (int32)q_min; } if (q_max < q_val) { - float_raise(float_flag_overflow | float_flag_inexact STATUS_VAR); + float_raise(float_flag_overflow | float_flag_inexact, status); return (int32)q_max; } @@ -1782,15 +1774,14 @@ static inline int32 float64_to_q32(float64 a STATUS_PARAM) #define MSA_FLOAT_COND(DEST, OP, ARG1, ARG2, BITS, QUIET) \ do { \ + float_status *status = &env->active_tc.msa_fp_status; \ int c; \ int64_t cond; \ - set_float_exception_flags(0, &env->active_tc.msa_fp_status); \ + set_float_exception_flags(0, status); \ if (!QUIET) { \ - cond = float ## BITS ## _ ## OP(ARG1, ARG2, \ - &env->active_tc.msa_fp_status); \ + cond = float ## BITS ## _ ## OP(ARG1, ARG2, status); \ } else { \ - cond = float ## BITS ## _ ## OP ## _quiet(ARG1, ARG2, \ - &env->active_tc.msa_fp_status); \ + cond = float ## BITS ## _ ## OP ## _quiet(ARG1, ARG2, status); \ } \ DEST = cond ? M_MAX_UINT(BITS) : 0; \ c = update_msacsr(env, CLEAR_IS_INEXACT, 0); \ @@ -2375,11 +2366,11 @@ void helper_msa_fsne_df(CPUMIPSState *env, uint32_t df, uint32_t wd, #define MSA_FLOAT_BINOP(DEST, OP, ARG1, ARG2, BITS) \ do { \ + float_status *status = &env->active_tc.msa_fp_status; \ int c; \ \ - set_float_exception_flags(0, &env->active_tc.msa_fp_status); \ - DEST = float ## BITS ## _ ## OP(ARG1, ARG2, \ - &env->active_tc.msa_fp_status); \ + set_float_exception_flags(0, status); \ + DEST = float ## BITS ## _ ## OP(ARG1, ARG2, status); \ c = update_msacsr(env, 0, IS_DENORMAL(DEST, BITS)); \ \ if (get_enabled_exceptions(env, c)) { \ @@ -2511,11 +2502,11 @@ void helper_msa_fdiv_df(CPUMIPSState *env, uint32_t df, uint32_t wd, #define MSA_FLOAT_MULADD(DEST, ARG1, ARG2, ARG3, NEGATE, BITS) \ do { \ + float_status *status = &env->active_tc.msa_fp_status; \ int c; \ \ - set_float_exception_flags(0, &env->active_tc.msa_fp_status); \ - DEST = float ## BITS ## _muladd(ARG2, ARG3, ARG1, NEGATE, \ - &env->active_tc.msa_fp_status); \ + set_float_exception_flags(0, status); \ + DEST = float ## BITS ## _muladd(ARG2, ARG3, ARG1, NEGATE, status); \ c = update_msacsr(env, 0, IS_DENORMAL(DEST, BITS)); \ \ if (get_enabled_exceptions(env, c)) { \ @@ -2630,10 +2621,11 @@ void helper_msa_fexp2_df(CPUMIPSState *env, uint32_t df, uint32_t wd, #define MSA_FLOAT_UNOP(DEST, OP, ARG, BITS) \ do { \ + float_status *status = &env->active_tc.msa_fp_status; \ int c; \ \ - set_float_exception_flags(0, &env->active_tc.msa_fp_status); \ - DEST = float ## BITS ## _ ## OP(ARG, &env->active_tc.msa_fp_status);\ + set_float_exception_flags(0, status); \ + DEST = float ## BITS ## _ ## OP(ARG, status); \ c = update_msacsr(env, 0, IS_DENORMAL(DEST, BITS)); \ \ if (get_enabled_exceptions(env, c)) { \ @@ -2678,10 +2670,11 @@ void helper_msa_fexdo_df(CPUMIPSState *env, uint32_t df, uint32_t wd, #define MSA_FLOAT_UNOP_XD(DEST, OP, ARG, BITS, XBITS) \ do { \ + float_status *status = &env->active_tc.msa_fp_status; \ int c; \ \ - set_float_exception_flags(0, &env->active_tc.msa_fp_status); \ - DEST = float ## BITS ## _ ## OP(ARG, &env->active_tc.msa_fp_status);\ + set_float_exception_flags(0, status); \ + DEST = float ## BITS ## _ ## OP(ARG, status); \ c = update_msacsr(env, CLEAR_FS_UNDERFLOW, 0); \ \ if (get_enabled_exceptions(env, c)) { \ @@ -2728,11 +2721,11 @@ void helper_msa_ftq_df(CPUMIPSState *env, uint32_t df, uint32_t wd, #define MSA_FLOAT_MAXOP(DEST, OP, ARG1, ARG2, BITS) \ do { \ + float_status *status = &env->active_tc.msa_fp_status; \ int c; \ \ - set_float_exception_flags(0, &env->active_tc.msa_fp_status); \ - DEST = float ## BITS ## _ ## OP(ARG1, ARG2, \ - &env->active_tc.msa_fp_status); \ + set_float_exception_flags(0, status); \ + DEST = float ## BITS ## _ ## OP(ARG1, ARG2, status); \ c = update_msacsr(env, 0, 0); \ \ if (get_enabled_exceptions(env, c)) { \ @@ -2924,10 +2917,11 @@ void helper_msa_fclass_df(CPUMIPSState *env, uint32_t df, #define MSA_FLOAT_UNOP0(DEST, OP, ARG, BITS) \ do { \ + float_status *status = &env->active_tc.msa_fp_status; \ int c; \ \ - set_float_exception_flags(0, &env->active_tc.msa_fp_status); \ - DEST = float ## BITS ## _ ## OP(ARG, &env->active_tc.msa_fp_status);\ + set_float_exception_flags(0, status); \ + DEST = float ## BITS ## _ ## OP(ARG, status); \ c = update_msacsr(env, CLEAR_FS_UNDERFLOW, 0); \ \ if (get_enabled_exceptions(env, c)) { \ @@ -3029,11 +3023,11 @@ void helper_msa_fsqrt_df(CPUMIPSState *env, uint32_t df, uint32_t wd, #define MSA_FLOAT_RECIPROCAL(DEST, ARG, BITS) \ do { \ + float_status *status = &env->active_tc.msa_fp_status; \ int c; \ \ - set_float_exception_flags(0, &env->active_tc.msa_fp_status); \ - DEST = float ## BITS ## _ ## div(FLOAT_ONE ## BITS, ARG, \ - &env->active_tc.msa_fp_status); \ + set_float_exception_flags(0, status); \ + DEST = float ## BITS ## _ ## div(FLOAT_ONE ## BITS, ARG, status); \ c = update_msacsr(env, float ## BITS ## _is_infinity(ARG) || \ float ## BITS ## _is_quiet_nan(DEST) ? \ 0 : RECIPROCAL_INEXACT, \ @@ -3138,23 +3132,20 @@ void helper_msa_frint_df(CPUMIPSState *env, uint32_t df, uint32_t wd, #define MSA_FLOAT_LOGB(DEST, ARG, BITS) \ do { \ + float_status *status = &env->active_tc.msa_fp_status; \ int c; \ \ - set_float_exception_flags(0, &env->active_tc.msa_fp_status); \ - set_float_rounding_mode(float_round_down, \ - &env->active_tc.msa_fp_status); \ - DEST = float ## BITS ## _ ## log2(ARG, \ - &env->active_tc.msa_fp_status); \ - DEST = float ## BITS ## _ ## round_to_int(DEST, \ - &env->active_tc.msa_fp_status); \ + set_float_exception_flags(0, status); \ + set_float_rounding_mode(float_round_down, status); \ + DEST = float ## BITS ## _ ## log2(ARG, status); \ + DEST = float ## BITS ## _ ## round_to_int(DEST, status); \ set_float_rounding_mode(ieee_rm[(env->active_tc.msacsr & \ MSACSR_RM_MASK) >> MSACSR_RM], \ - &env->active_tc.msa_fp_status); \ + status); \ \ - set_float_exception_flags( \ - get_float_exception_flags(&env->active_tc.msa_fp_status) \ - & (~float_flag_inexact), \ - &env->active_tc.msa_fp_status); \ + set_float_exception_flags(get_float_exception_flags(status) & \ + (~float_flag_inexact), \ + status); \ \ c = update_msacsr(env, 0, IS_DENORMAL(DEST, BITS)); \ \ diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c index 638c9f9..73a8e45 100644 --- a/target-mips/op_helper.c +++ b/target-mips/op_helper.c @@ -74,7 +74,7 @@ void helper_raise_exception(CPUMIPSState *env, uint32_t exception) static inline type do_##name(CPUMIPSState *env, target_ulong addr, \ int mem_idx) \ { \ - return (type) insn##_raw(addr); \ + return (type) cpu_##insn##_data(env, addr); \ } #else #define HELPER_LD(name, insn, type) \ @@ -101,7 +101,7 @@ HELPER_LD(ld, ldq, int64_t) static inline void do_##name(CPUMIPSState *env, target_ulong addr, \ type val, int mem_idx) \ { \ - insn##_raw(addr, val); \ + cpu_##insn##_data(env, addr, val); \ } #else #define HELPER_ST(name, insn, type) \ @@ -304,16 +304,20 @@ static inline hwaddr do_translate_address(CPUMIPSState *env, } } -#define HELPER_LD_ATOMIC(name, insn) \ +#define HELPER_LD_ATOMIC(name, insn, almask) \ target_ulong helper_##name(CPUMIPSState *env, target_ulong arg, int mem_idx) \ { \ + if (arg & almask) { \ + env->CP0_BadVAddr = arg; \ + helper_raise_exception(env, EXCP_AdEL); \ + } \ env->lladdr = do_translate_address(env, arg, 0); \ env->llval = do_##insn(env, arg, mem_idx); \ return env->llval; \ } -HELPER_LD_ATOMIC(ll, lw) +HELPER_LD_ATOMIC(ll, lw, 0x3) #ifdef TARGET_MIPS64 -HELPER_LD_ATOMIC(lld, ld) +HELPER_LD_ATOMIC(lld, ld, 0x7) #endif #undef HELPER_LD_ATOMIC @@ -625,40 +629,9 @@ static CPUMIPSState *mips_cpu_map_tc(CPUMIPSState *env, int *tc) These helper call synchronizes the regs for a given cpu. */ -/* Called for updates to CP0_Status. */ -static void sync_c0_status(CPUMIPSState *env, CPUMIPSState *cpu, int tc) -{ - int32_t tcstatus, *tcst; - uint32_t v = cpu->CP0_Status; - uint32_t cu, mx, asid, ksu; - uint32_t mask = ((1 << CP0TCSt_TCU3) - | (1 << CP0TCSt_TCU2) - | (1 << CP0TCSt_TCU1) - | (1 << CP0TCSt_TCU0) - | (1 << CP0TCSt_TMX) - | (3 << CP0TCSt_TKSU) - | (0xff << CP0TCSt_TASID)); - - cu = (v >> CP0St_CU0) & 0xf; - mx = (v >> CP0St_MX) & 0x1; - ksu = (v >> CP0St_KSU) & 0x3; - asid = env->CP0_EntryHi & 0xff; - - tcstatus = cu << CP0TCSt_TCU0; - tcstatus |= mx << CP0TCSt_TMX; - tcstatus |= ksu << CP0TCSt_TKSU; - tcstatus |= asid; - - if (tc == cpu->current_tc) { - tcst = &cpu->active_tc.CP0_TCStatus; - } else { - tcst = &cpu->tcs[tc].CP0_TCStatus; - } - - *tcst &= ~mask; - *tcst |= tcstatus; - compute_hflags(cpu); -} +/* Called for updates to CP0_Status. Defined in "cpu.h" for gdbstub.c. */ +/* static inline void sync_c0_status(CPUMIPSState *env, CPUMIPSState *cpu, + int tc); */ /* Called for updates to CP0_TCStatus. */ static void sync_c0_tcstatus(CPUMIPSState *cpu, int tc, @@ -1420,23 +1393,10 @@ void helper_mtc0_status(CPUMIPSState *env, target_ulong arg1) { MIPSCPU *cpu = mips_env_get_cpu(env); uint32_t val, old; - uint32_t mask = env->CP0_Status_rw_bitmask; - if (env->insn_flags & ISA_MIPS32R6) { - if (extract32(env->CP0_Status, CP0St_KSU, 2) == 0x3) { - mask &= ~(3 << CP0St_KSU); - } - mask &= ~(0x00180000 & arg1); - } - - val = arg1 & mask; old = env->CP0_Status; - env->CP0_Status = (env->CP0_Status & ~mask) | val; - if (env->CP0_Config3 & (1 << CP0C3_MT)) { - sync_c0_status(env, env, env->current_tc); - } else { - compute_hflags(env); - } + cpu_mips_store_status(env, arg1); + val = env->CP0_Status; if (qemu_loglevel_mask(CPU_LOG_EXEC)) { qemu_log("Status %08x (%08x) => %08x (%08x) Cause %08x", @@ -1457,9 +1417,10 @@ void helper_mtc0_status(CPUMIPSState *env, target_ulong arg1) void helper_mttc0_status(CPUMIPSState *env, target_ulong arg1) { int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + uint32_t mask = env->CP0_Status_rw_bitmask & ~0xf1000018; CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - other->CP0_Status = arg1 & ~0xf1000018; + other->CP0_Status = (other->CP0_Status & ~mask) | (arg1 & mask); sync_c0_status(env, other, other_tc); } @@ -1475,40 +1436,9 @@ void helper_mtc0_srsctl(CPUMIPSState *env, target_ulong arg1) env->CP0_SRSCtl = (env->CP0_SRSCtl & ~mask) | (arg1 & mask); } -static void mtc0_cause(CPUMIPSState *cpu, target_ulong arg1) -{ - uint32_t mask = 0x00C00300; - uint32_t old = cpu->CP0_Cause; - int i; - - if (cpu->insn_flags & ISA_MIPS32R2) { - mask |= 1 << CP0Ca_DC; - } - if (cpu->insn_flags & ISA_MIPS32R6) { - mask &= ~((1 << CP0Ca_WP) & arg1); - } - - cpu->CP0_Cause = (cpu->CP0_Cause & ~mask) | (arg1 & mask); - - if ((old ^ cpu->CP0_Cause) & (1 << CP0Ca_DC)) { - if (cpu->CP0_Cause & (1 << CP0Ca_DC)) { - cpu_mips_stop_count(cpu); - } else { - cpu_mips_start_count(cpu); - } - } - - /* Set/reset software interrupts */ - for (i = 0 ; i < 2 ; i++) { - if ((old ^ cpu->CP0_Cause) & (1 << (CP0Ca_IP + i))) { - cpu_mips_soft_irq(cpu, i, cpu->CP0_Cause & (1 << (CP0Ca_IP + i))); - } - } -} - void helper_mtc0_cause(CPUMIPSState *env, target_ulong arg1) { - mtc0_cause(env, arg1); + cpu_mips_store_cause(env, arg1); } void helper_mttc0_cause(CPUMIPSState *env, target_ulong arg1) @@ -1516,7 +1446,7 @@ void helper_mttc0_cause(CPUMIPSState *env, target_ulong arg1) int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - mtc0_cause(other, arg1); + cpu_mips_store_cause(other, arg1); } target_ulong helper_mftc0_epc(CPUMIPSState *env) @@ -1578,6 +1508,14 @@ void helper_mtc0_config2(CPUMIPSState *env, target_ulong arg1) env->CP0_Config2 = (env->CP0_Config2 & 0x8FFF0FFF); } +void helper_mtc0_config3(CPUMIPSState *env, target_ulong arg1) +{ + if (env->insn_flags & ASE_MICROMIPS) { + env->CP0_Config3 = (env->CP0_Config3 & ~(1 << CP0C3_ISA_ON_EXC)) | + (arg1 & (1 << CP0C3_ISA_ON_EXC)); + } +} + void helper_mtc0_config4(CPUMIPSState *env, target_ulong arg1) { env->CP0_Config4 = (env->CP0_Config4 & (~env->CP0_Config4_rw_bitmask)) | @@ -2346,18 +2284,6 @@ unsigned int ieee_rm[] = { float_round_down }; -static inline void restore_rounding_mode(CPUMIPSState *env) -{ - set_float_rounding_mode(ieee_rm[env->active_fpu.fcr31 & 3], - &env->active_fpu.fp_status); -} - -static inline void restore_flush_mode(CPUMIPSState *env) -{ - set_flush_to_zero((env->active_fpu.fcr31 & (1 << 24)) != 0, - &env->active_fpu.fp_status); -} - target_ulong helper_cfc1(CPUMIPSState *env, uint32_t reg) { target_ulong arg1 = 0; @@ -2659,11 +2585,11 @@ uint32_t helper_float_cvtw_s(CPUMIPSState *env, uint32_t fst0) uint32_t wt2; wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status); - update_fcr31(env, GETPC()); if (get_float_exception_flags(&env->active_fpu.fp_status) & (float_flag_invalid | float_flag_overflow)) { wt2 = FP_TO_INT32_OVERFLOW; } + update_fcr31(env, GETPC()); return wt2; } @@ -2935,110 +2861,6 @@ FLOAT_UNOP(abs) FLOAT_UNOP(chs) #undef FLOAT_UNOP -#define FLOAT_FMADDSUB(name, bits, muladd_arg) \ -uint ## bits ## _t helper_float_ ## name (CPUMIPSState *env, \ - uint ## bits ## _t fs, \ - uint ## bits ## _t ft, \ - uint ## bits ## _t fd) \ -{ \ - uint ## bits ## _t fdret; \ - \ - fdret = float ## bits ## _muladd(fs, ft, fd, muladd_arg, \ - &env->active_fpu.fp_status); \ - update_fcr31(env, GETPC()); \ - return fdret; \ -} - -FLOAT_FMADDSUB(maddf_s, 32, 0) -FLOAT_FMADDSUB(maddf_d, 64, 0) -FLOAT_FMADDSUB(msubf_s, 32, float_muladd_negate_product) -FLOAT_FMADDSUB(msubf_d, 64, float_muladd_negate_product) -#undef FLOAT_FMADDSUB - -#define FLOAT_MINMAX(name, bits, minmaxfunc) \ -uint ## bits ## _t helper_float_ ## name (CPUMIPSState *env, \ - uint ## bits ## _t fs, \ - uint ## bits ## _t ft) \ -{ \ - uint ## bits ## _t fdret; \ - \ - fdret = float ## bits ## _ ## minmaxfunc(fs, ft, \ - &env->active_fpu.fp_status); \ - update_fcr31(env, GETPC()); \ - return fdret; \ -} - -FLOAT_MINMAX(max_s, 32, maxnum) -FLOAT_MINMAX(max_d, 64, maxnum) -FLOAT_MINMAX(maxa_s, 32, maxnummag) -FLOAT_MINMAX(maxa_d, 64, maxnummag) - -FLOAT_MINMAX(min_s, 32, minnum) -FLOAT_MINMAX(min_d, 64, minnum) -FLOAT_MINMAX(mina_s, 32, minnummag) -FLOAT_MINMAX(mina_d, 64, minnummag) -#undef FLOAT_MINMAX - -#define FLOAT_RINT(name, bits) \ -uint ## bits ## _t helper_float_ ## name (CPUMIPSState *env, \ - uint ## bits ## _t fs) \ -{ \ - uint ## bits ## _t fdret; \ - \ - fdret = float ## bits ## _round_to_int(fs, &env->active_fpu.fp_status); \ - update_fcr31(env, GETPC()); \ - return fdret; \ -} - -FLOAT_RINT(rint_s, 32) -FLOAT_RINT(rint_d, 64) -#undef FLOAT_RINT - -#define FLOAT_CLASS_SIGNALING_NAN 0x001 -#define FLOAT_CLASS_QUIET_NAN 0x002 -#define FLOAT_CLASS_NEGATIVE_INFINITY 0x004 -#define FLOAT_CLASS_NEGATIVE_NORMAL 0x008 -#define FLOAT_CLASS_NEGATIVE_SUBNORMAL 0x010 -#define FLOAT_CLASS_NEGATIVE_ZERO 0x020 -#define FLOAT_CLASS_POSITIVE_INFINITY 0x040 -#define FLOAT_CLASS_POSITIVE_NORMAL 0x080 -#define FLOAT_CLASS_POSITIVE_SUBNORMAL 0x100 -#define FLOAT_CLASS_POSITIVE_ZERO 0x200 - -#define FLOAT_CLASS(name, bits) \ -uint ## bits ## _t helper_float_ ## name (uint ## bits ## _t arg) \ -{ \ - if (float ## bits ## _is_signaling_nan(arg)) { \ - return FLOAT_CLASS_SIGNALING_NAN; \ - } else if (float ## bits ## _is_quiet_nan(arg)) { \ - return FLOAT_CLASS_QUIET_NAN; \ - } else if (float ## bits ## _is_neg(arg)) { \ - if (float ## bits ## _is_infinity(arg)) { \ - return FLOAT_CLASS_NEGATIVE_INFINITY; \ - } else if (float ## bits ## _is_zero(arg)) { \ - return FLOAT_CLASS_NEGATIVE_ZERO; \ - } else if (float ## bits ## _is_zero_or_denormal(arg)) { \ - return FLOAT_CLASS_NEGATIVE_SUBNORMAL; \ - } else { \ - return FLOAT_CLASS_NEGATIVE_NORMAL; \ - } \ - } else { \ - if (float ## bits ## _is_infinity(arg)) { \ - return FLOAT_CLASS_POSITIVE_INFINITY; \ - } else if (float ## bits ## _is_zero(arg)) { \ - return FLOAT_CLASS_POSITIVE_ZERO; \ - } else if (float ## bits ## _is_zero_or_denormal(arg)) { \ - return FLOAT_CLASS_POSITIVE_SUBNORMAL; \ - } else { \ - return FLOAT_CLASS_POSITIVE_NORMAL; \ - } \ - } \ -} - -FLOAT_CLASS(class_s, 32) -FLOAT_CLASS(class_d, 64) -#undef FLOAT_CLASS - /* MIPS specific unary operations */ uint64_t helper_float_recip_d(CPUMIPSState *env, uint64_t fdt0) { @@ -3140,7 +2962,65 @@ uint64_t helper_float_rsqrt1_ps(CPUMIPSState *env, uint64_t fdt0) return ((uint64_t)fsth2 << 32) | fst2; } -#define FLOAT_OP(name, p) void helper_float_##name##_##p(CPUMIPSState *env) +#define FLOAT_RINT(name, bits) \ +uint ## bits ## _t helper_float_ ## name (CPUMIPSState *env, \ + uint ## bits ## _t fs) \ +{ \ + uint ## bits ## _t fdret; \ + \ + fdret = float ## bits ## _round_to_int(fs, &env->active_fpu.fp_status); \ + update_fcr31(env, GETPC()); \ + return fdret; \ +} + +FLOAT_RINT(rint_s, 32) +FLOAT_RINT(rint_d, 64) +#undef FLOAT_RINT + +#define FLOAT_CLASS_SIGNALING_NAN 0x001 +#define FLOAT_CLASS_QUIET_NAN 0x002 +#define FLOAT_CLASS_NEGATIVE_INFINITY 0x004 +#define FLOAT_CLASS_NEGATIVE_NORMAL 0x008 +#define FLOAT_CLASS_NEGATIVE_SUBNORMAL 0x010 +#define FLOAT_CLASS_NEGATIVE_ZERO 0x020 +#define FLOAT_CLASS_POSITIVE_INFINITY 0x040 +#define FLOAT_CLASS_POSITIVE_NORMAL 0x080 +#define FLOAT_CLASS_POSITIVE_SUBNORMAL 0x100 +#define FLOAT_CLASS_POSITIVE_ZERO 0x200 + +#define FLOAT_CLASS(name, bits) \ +uint ## bits ## _t helper_float_ ## name (uint ## bits ## _t arg) \ +{ \ + if (float ## bits ## _is_signaling_nan(arg)) { \ + return FLOAT_CLASS_SIGNALING_NAN; \ + } else if (float ## bits ## _is_quiet_nan(arg)) { \ + return FLOAT_CLASS_QUIET_NAN; \ + } else if (float ## bits ## _is_neg(arg)) { \ + if (float ## bits ## _is_infinity(arg)) { \ + return FLOAT_CLASS_NEGATIVE_INFINITY; \ + } else if (float ## bits ## _is_zero(arg)) { \ + return FLOAT_CLASS_NEGATIVE_ZERO; \ + } else if (float ## bits ## _is_zero_or_denormal(arg)) { \ + return FLOAT_CLASS_NEGATIVE_SUBNORMAL; \ + } else { \ + return FLOAT_CLASS_NEGATIVE_NORMAL; \ + } \ + } else { \ + if (float ## bits ## _is_infinity(arg)) { \ + return FLOAT_CLASS_POSITIVE_INFINITY; \ + } else if (float ## bits ## _is_zero(arg)) { \ + return FLOAT_CLASS_POSITIVE_ZERO; \ + } else if (float ## bits ## _is_zero_or_denormal(arg)) { \ + return FLOAT_CLASS_POSITIVE_SUBNORMAL; \ + } else { \ + return FLOAT_CLASS_POSITIVE_NORMAL; \ + } \ + } \ +} + +FLOAT_CLASS(class_s, 32) +FLOAT_CLASS(class_d, 64) +#undef FLOAT_CLASS /* binary operations */ #define FLOAT_BINOP(name) \ @@ -3187,61 +3067,6 @@ FLOAT_BINOP(mul) FLOAT_BINOP(div) #undef FLOAT_BINOP -#define UNFUSED_FMA(prefix, a, b, c, flags) \ -{ \ - a = prefix##_mul(a, b, &env->active_fpu.fp_status); \ - if ((flags) & float_muladd_negate_c) { \ - a = prefix##_sub(a, c, &env->active_fpu.fp_status); \ - } else { \ - a = prefix##_add(a, c, &env->active_fpu.fp_status); \ - } \ - if ((flags) & float_muladd_negate_result) { \ - a = prefix##_chs(a); \ - } \ -} - -/* FMA based operations */ -#define FLOAT_FMA(name, type) \ -uint64_t helper_float_ ## name ## _d(CPUMIPSState *env, \ - uint64_t fdt0, uint64_t fdt1, \ - uint64_t fdt2) \ -{ \ - UNFUSED_FMA(float64, fdt0, fdt1, fdt2, type); \ - update_fcr31(env, GETPC()); \ - return fdt0; \ -} \ - \ -uint32_t helper_float_ ## name ## _s(CPUMIPSState *env, \ - uint32_t fst0, uint32_t fst1, \ - uint32_t fst2) \ -{ \ - UNFUSED_FMA(float32, fst0, fst1, fst2, type); \ - update_fcr31(env, GETPC()); \ - return fst0; \ -} \ - \ -uint64_t helper_float_ ## name ## _ps(CPUMIPSState *env, \ - uint64_t fdt0, uint64_t fdt1, \ - uint64_t fdt2) \ -{ \ - uint32_t fst0 = fdt0 & 0XFFFFFFFF; \ - uint32_t fsth0 = fdt0 >> 32; \ - uint32_t fst1 = fdt1 & 0XFFFFFFFF; \ - uint32_t fsth1 = fdt1 >> 32; \ - uint32_t fst2 = fdt2 & 0XFFFFFFFF; \ - uint32_t fsth2 = fdt2 >> 32; \ - \ - UNFUSED_FMA(float32, fst0, fst1, fst2, type); \ - UNFUSED_FMA(float32, fsth0, fsth1, fsth2, type); \ - update_fcr31(env, GETPC()); \ - return ((uint64_t)fsth0 << 32) | fst0; \ -} -FLOAT_FMA(madd, 0) -FLOAT_FMA(msub, float_muladd_negate_c) -FLOAT_FMA(nmadd, float_muladd_negate_result) -FLOAT_FMA(nmsub, float_muladd_negate_result | float_muladd_negate_c) -#undef FLOAT_FMA - /* MIPS specific binary operations */ uint64_t helper_float_recip2_d(CPUMIPSState *env, uint64_t fdt0, uint64_t fdt2) { @@ -3339,6 +3164,106 @@ uint64_t helper_float_mulr_ps(CPUMIPSState *env, uint64_t fdt0, uint64_t fdt1) return ((uint64_t)fsth2 << 32) | fst2; } +#define FLOAT_MINMAX(name, bits, minmaxfunc) \ +uint ## bits ## _t helper_float_ ## name (CPUMIPSState *env, \ + uint ## bits ## _t fs, \ + uint ## bits ## _t ft) \ +{ \ + uint ## bits ## _t fdret; \ + \ + fdret = float ## bits ## _ ## minmaxfunc(fs, ft, \ + &env->active_fpu.fp_status); \ + update_fcr31(env, GETPC()); \ + return fdret; \ +} + +FLOAT_MINMAX(max_s, 32, maxnum) +FLOAT_MINMAX(max_d, 64, maxnum) +FLOAT_MINMAX(maxa_s, 32, maxnummag) +FLOAT_MINMAX(maxa_d, 64, maxnummag) + +FLOAT_MINMAX(min_s, 32, minnum) +FLOAT_MINMAX(min_d, 64, minnum) +FLOAT_MINMAX(mina_s, 32, minnummag) +FLOAT_MINMAX(mina_d, 64, minnummag) +#undef FLOAT_MINMAX + +/* ternary operations */ +#define UNFUSED_FMA(prefix, a, b, c, flags) \ +{ \ + a = prefix##_mul(a, b, &env->active_fpu.fp_status); \ + if ((flags) & float_muladd_negate_c) { \ + a = prefix##_sub(a, c, &env->active_fpu.fp_status); \ + } else { \ + a = prefix##_add(a, c, &env->active_fpu.fp_status); \ + } \ + if ((flags) & float_muladd_negate_result) { \ + a = prefix##_chs(a); \ + } \ +} + +/* FMA based operations */ +#define FLOAT_FMA(name, type) \ +uint64_t helper_float_ ## name ## _d(CPUMIPSState *env, \ + uint64_t fdt0, uint64_t fdt1, \ + uint64_t fdt2) \ +{ \ + UNFUSED_FMA(float64, fdt0, fdt1, fdt2, type); \ + update_fcr31(env, GETPC()); \ + return fdt0; \ +} \ + \ +uint32_t helper_float_ ## name ## _s(CPUMIPSState *env, \ + uint32_t fst0, uint32_t fst1, \ + uint32_t fst2) \ +{ \ + UNFUSED_FMA(float32, fst0, fst1, fst2, type); \ + update_fcr31(env, GETPC()); \ + return fst0; \ +} \ + \ +uint64_t helper_float_ ## name ## _ps(CPUMIPSState *env, \ + uint64_t fdt0, uint64_t fdt1, \ + uint64_t fdt2) \ +{ \ + uint32_t fst0 = fdt0 & 0XFFFFFFFF; \ + uint32_t fsth0 = fdt0 >> 32; \ + uint32_t fst1 = fdt1 & 0XFFFFFFFF; \ + uint32_t fsth1 = fdt1 >> 32; \ + uint32_t fst2 = fdt2 & 0XFFFFFFFF; \ + uint32_t fsth2 = fdt2 >> 32; \ + \ + UNFUSED_FMA(float32, fst0, fst1, fst2, type); \ + UNFUSED_FMA(float32, fsth0, fsth1, fsth2, type); \ + update_fcr31(env, GETPC()); \ + return ((uint64_t)fsth0 << 32) | fst0; \ +} +FLOAT_FMA(madd, 0) +FLOAT_FMA(msub, float_muladd_negate_c) +FLOAT_FMA(nmadd, float_muladd_negate_result) +FLOAT_FMA(nmsub, float_muladd_negate_result | float_muladd_negate_c) +#undef FLOAT_FMA + +#define FLOAT_FMADDSUB(name, bits, muladd_arg) \ +uint ## bits ## _t helper_float_ ## name (CPUMIPSState *env, \ + uint ## bits ## _t fs, \ + uint ## bits ## _t ft, \ + uint ## bits ## _t fd) \ +{ \ + uint ## bits ## _t fdret; \ + \ + fdret = float ## bits ## _muladd(fs, ft, fd, muladd_arg, \ + &env->active_fpu.fp_status); \ + update_fcr31(env, GETPC()); \ + return fdret; \ +} + +FLOAT_FMADDSUB(maddf_s, 32, 0) +FLOAT_FMADDSUB(maddf_d, 64, 0) +FLOAT_FMADDSUB(msubf_s, 32, float_muladd_negate_product) +FLOAT_FMADDSUB(msubf_d, 64, float_muladd_negate_product) +#undef FLOAT_FMADDSUB + /* compare operations */ #define FOP_COND_D(op, cond) \ void helper_cmp_d_ ## op(CPUMIPSState *env, uint64_t fdt0, \ diff --git a/target-mips/translate.c b/target-mips/translate.c index f0b8e6f..fd063a2 100644 --- a/target-mips/translate.c +++ b/target-mips/translate.c @@ -1882,10 +1882,8 @@ static inline void gen_r6_cmp_ ## fmt(DisasContext * ctx, int n, \ { \ TCGv_i ## bits fp0 = tcg_temp_new_i ## bits(); \ TCGv_i ## bits fp1 = tcg_temp_new_i ## bits(); \ - switch (ifmt) { \ - case FMT_D: \ + if (ifmt == FMT_D) { \ check_cp1_registers(ctx, fs | ft | fd); \ - break; \ } \ gen_ldcmp_fpr ## bits(ctx, fp0, fs); \ gen_ldcmp_fpr ## bits(ctx, fp1, ft); \ @@ -2000,8 +1998,8 @@ OP_LD_ATOMIC(lld,ld64); static inline void op_st_##insn(TCGv arg1, TCGv arg2, int rt, DisasContext *ctx) \ { \ TCGv t0 = tcg_temp_new(); \ - int l1 = gen_new_label(); \ - int l2 = gen_new_label(); \ + TCGLabel *l1 = gen_new_label(); \ + TCGLabel *l2 = gen_new_label(); \ \ tcg_gen_andi_tl(t0, arg2, almask); \ tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0, l1); \ @@ -2398,7 +2396,14 @@ static void gen_cop1_ldst(DisasContext *ctx, uint32_t op, int rt, { if (ctx->CP0_Config1 & (1 << CP0C1_FP)) { check_cp1_enabled(ctx); - gen_flt_ldst(ctx, op, rt, rs, imm); + switch (op) { + case OPC_LDC1: + case OPC_SDC1: + check_insn(ctx, ISA_MIPS2); + /* Fallthrough */ + default: + gen_flt_ldst(ctx, op, rt, rs, imm); + } } else { generate_exception_err(ctx, EXCP_CpU, 1); } @@ -2423,7 +2428,7 @@ static void gen_arith_imm(DisasContext *ctx, uint32_t opc, TCGv t0 = tcg_temp_local_new(); TCGv t1 = tcg_temp_new(); TCGv t2 = tcg_temp_new(); - int l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); gen_load_gpr(t1, rs); tcg_gen_addi_tl(t0, t1, uimm); @@ -2459,7 +2464,7 @@ static void gen_arith_imm(DisasContext *ctx, uint32_t opc, TCGv t0 = tcg_temp_local_new(); TCGv t1 = tcg_temp_new(); TCGv t2 = tcg_temp_new(); - int l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); gen_load_gpr(t1, rs); tcg_gen_addi_tl(t0, t1, uimm); @@ -2689,7 +2694,7 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, TCGv t0 = tcg_temp_local_new(); TCGv t1 = tcg_temp_new(); TCGv t2 = tcg_temp_new(); - int l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); gen_load_gpr(t1, rs); gen_load_gpr(t2, rt); @@ -2727,7 +2732,7 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, TCGv t0 = tcg_temp_local_new(); TCGv t1 = tcg_temp_new(); TCGv t2 = tcg_temp_new(); - int l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); gen_load_gpr(t1, rs); gen_load_gpr(t2, rt); @@ -2767,7 +2772,7 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, TCGv t0 = tcg_temp_local_new(); TCGv t1 = tcg_temp_new(); TCGv t2 = tcg_temp_new(); - int l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); gen_load_gpr(t1, rs); gen_load_gpr(t2, rt); @@ -2803,7 +2808,7 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, TCGv t0 = tcg_temp_local_new(); TCGv t1 = tcg_temp_new(); TCGv t2 = tcg_temp_new(); - int l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); gen_load_gpr(t1, rs); gen_load_gpr(t2, rt); @@ -3841,9 +3846,9 @@ static void gen_loongson_integer(DisasContext *ctx, uint32_t opc, case OPC_DIV_G_2E: case OPC_DIV_G_2F: { - int l1 = gen_new_label(); - int l2 = gen_new_label(); - int l3 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); + TCGLabel *l3 = gen_new_label(); tcg_gen_ext32s_tl(t0, t0); tcg_gen_ext32s_tl(t1, t1); tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); @@ -3864,8 +3869,8 @@ static void gen_loongson_integer(DisasContext *ctx, uint32_t opc, case OPC_DIVU_G_2E: case OPC_DIVU_G_2F: { - int l1 = gen_new_label(); - int l2 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); tcg_gen_ext32u_tl(t0, t0); tcg_gen_ext32u_tl(t1, t1); tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); @@ -3881,9 +3886,9 @@ static void gen_loongson_integer(DisasContext *ctx, uint32_t opc, case OPC_MOD_G_2E: case OPC_MOD_G_2F: { - int l1 = gen_new_label(); - int l2 = gen_new_label(); - int l3 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); + TCGLabel *l3 = gen_new_label(); tcg_gen_ext32u_tl(t0, t0); tcg_gen_ext32u_tl(t1, t1); tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, l1); @@ -3902,8 +3907,8 @@ static void gen_loongson_integer(DisasContext *ctx, uint32_t opc, case OPC_MODU_G_2E: case OPC_MODU_G_2F: { - int l1 = gen_new_label(); - int l2 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); tcg_gen_ext32u_tl(t0, t0); tcg_gen_ext32u_tl(t1, t1); tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); @@ -3930,9 +3935,9 @@ static void gen_loongson_integer(DisasContext *ctx, uint32_t opc, case OPC_DDIV_G_2E: case OPC_DDIV_G_2F: { - int l1 = gen_new_label(); - int l2 = gen_new_label(); - int l3 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); + TCGLabel *l3 = gen_new_label(); tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); tcg_gen_movi_tl(cpu_gpr[rd], 0); tcg_gen_br(l3); @@ -3950,8 +3955,8 @@ static void gen_loongson_integer(DisasContext *ctx, uint32_t opc, case OPC_DDIVU_G_2E: case OPC_DDIVU_G_2F: { - int l1 = gen_new_label(); - int l2 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); tcg_gen_movi_tl(cpu_gpr[rd], 0); tcg_gen_br(l2); @@ -3964,9 +3969,9 @@ static void gen_loongson_integer(DisasContext *ctx, uint32_t opc, case OPC_DMOD_G_2E: case OPC_DMOD_G_2F: { - int l1 = gen_new_label(); - int l2 = gen_new_label(); - int l3 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); + TCGLabel *l3 = gen_new_label(); tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, l1); tcg_gen_brcondi_tl(TCG_COND_NE, t0, -1LL << 63, l2); tcg_gen_brcondi_tl(TCG_COND_NE, t1, -1LL, l2); @@ -3982,8 +3987,8 @@ static void gen_loongson_integer(DisasContext *ctx, uint32_t opc, case OPC_DMODU_G_2E: case OPC_DMODU_G_2F: { - int l1 = gen_new_label(); - int l2 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); tcg_gen_movi_tl(cpu_gpr[rd], 0); tcg_gen_br(l2); @@ -4199,7 +4204,7 @@ static void gen_loongson_multimedia(DisasContext *ctx, int rd, int rs, int rt) case OPC_DADD_CP2: { TCGv_i64 t2 = tcg_temp_new_i64(); - int lab = gen_new_label(); + TCGLabel *lab = gen_new_label(); tcg_gen_mov_i64(t2, t0); tcg_gen_add_i64(t0, t1, t2); @@ -4222,7 +4227,7 @@ static void gen_loongson_multimedia(DisasContext *ctx, int rd, int rs, int rt) case OPC_DSUB_CP2: { TCGv_i64 t2 = tcg_temp_new_i64(); - int lab = gen_new_label(); + TCGLabel *lab = gen_new_label(); tcg_gen_mov_i64(t2, t0); tcg_gen_sub_i64(t0, t1, t2); @@ -4333,7 +4338,7 @@ static void gen_trap (DisasContext *ctx, uint32_t opc, break; } } else { - int l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); switch (opc) { case OPC_TEQ: @@ -4942,7 +4947,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) #if defined(TARGET_MIPS64) if (ctx->rxi) { TCGv tmp = tcg_temp_new(); - tcg_gen_andi_tl(tmp, arg, (3ull << 62)); + tcg_gen_andi_tl(tmp, arg, (3ull << CP0EnLo_XI)); tcg_gen_shri_tl(tmp, tmp, 32); tcg_gen_or_tl(arg, arg, tmp); tcg_temp_free(tmp); @@ -4997,7 +5002,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) #if defined(TARGET_MIPS64) if (ctx->rxi) { TCGv tmp = tcg_temp_new(); - tcg_gen_andi_tl(tmp, arg, (3ull << 62)); + tcg_gen_andi_tl(tmp, arg, (3ull << CP0EnLo_XI)); tcg_gen_shri_tl(tmp, tmp, 32); tcg_gen_or_tl(arg, arg, tmp); tcg_temp_free(tmp); @@ -5118,10 +5123,11 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case 0: /* Mark as an IO operation because we read the time. */ - if (use_icount) + if (ctx->tb->cflags & CF_USE_ICOUNT) { gen_io_start(); + } gen_helper_mfc0_count(arg, cpu_env); - if (use_icount) { + if (ctx->tb->cflags & CF_USE_ICOUNT) { gen_io_end(); } /* Break the TB to be able to take timer interrupts immediately @@ -5494,8 +5500,9 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) if (sel != 0) check_insn(ctx, ISA_MIPS32); - if (use_icount) + if (ctx->tb->cflags & CF_USE_ICOUNT) { gen_io_start(); + } switch (reg) { case 0: @@ -5846,8 +5853,10 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) ctx->bstate = BS_STOP; break; case 3: - /* ignored, read only */ + gen_helper_mtc0_config3(cpu_env, arg); rn = "Config3"; + /* Stop translation as we may have switched the execution mode */ + ctx->bstate = BS_STOP; break; case 4: gen_helper_mtc0_config4(cpu_env, arg); @@ -6111,7 +6120,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) (void)rn; /* avoid a compiler warning */ LOG_DISAS("mtc0 %s (reg %d sel %d)\n", rn, reg, sel); /* For simplicity assume that all writes can cause interrupts. */ - if (use_icount) { + if (ctx->tb->cflags & CF_USE_ICOUNT) { gen_io_end(); ctx->bstate = BS_STOP; } @@ -6362,10 +6371,11 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case 0: /* Mark as an IO operation because we read the time. */ - if (use_icount) + if (ctx->tb->cflags & CF_USE_ICOUNT) { gen_io_start(); + } gen_helper_mfc0_count(arg, cpu_env); - if (use_icount) { + if (ctx->tb->cflags & CF_USE_ICOUNT) { gen_io_end(); } /* Break the TB to be able to take timer interrupts immediately @@ -6731,8 +6741,9 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) if (sel != 0) check_insn(ctx, ISA_MIPS64); - if (use_icount) + if (ctx->tb->cflags & CF_USE_ICOUNT) { gen_io_start(); + } switch (reg) { case 0: @@ -7038,11 +7049,11 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) save_cpu_state(ctx, 1); /* Mark as an IO operation because we may trigger a software interrupt. */ - if (use_icount) { + if (ctx->tb->cflags & CF_USE_ICOUNT) { gen_io_start(); } gen_helper_mtc0_cause(cpu_env, arg); - if (use_icount) { + if (ctx->tb->cflags & CF_USE_ICOUNT) { gen_io_end(); } /* Stop translation as we may have triggered an intetrupt */ @@ -7097,8 +7108,10 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) ctx->bstate = BS_STOP; break; case 3: - /* ignored */ + gen_helper_mtc0_config3(cpu_env, arg); rn = "Config3"; + /* Stop translation as we may have switched the execution mode */ + ctx->bstate = BS_STOP; break; case 4: /* currently ignored */ @@ -7349,7 +7362,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) (void)rn; /* avoid a compiler warning */ LOG_DISAS("dmtc0 %s (reg %d sel %d)\n", rn, reg, sel); /* For simplicity assume that all writes can cause interrupts. */ - if (use_icount) { + if (ctx->tb->cflags & CF_USE_ICOUNT) { gen_io_end(); ctx->bstate = BS_STOP; } @@ -8417,7 +8430,7 @@ static void gen_cp1 (DisasContext *ctx, uint32_t opc, int rt, int fs) static void gen_movci (DisasContext *ctx, int rd, int rs, int cc, int tf) { - int l1; + TCGLabel *l1; TCGCond cond; TCGv_i32 t0; @@ -8448,7 +8461,7 @@ static inline void gen_movcf_s (int fs, int fd, int cc, int tf) { int cond; TCGv_i32 t0 = tcg_temp_new_i32(); - int l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); if (tf) cond = TCG_COND_EQ; @@ -8468,7 +8481,7 @@ static inline void gen_movcf_d (DisasContext *ctx, int fs, int fd, int cc, int t int cond; TCGv_i32 t0 = tcg_temp_new_i32(); TCGv_i64 fp0; - int l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); if (tf) cond = TCG_COND_EQ; @@ -8490,8 +8503,8 @@ static inline void gen_movcf_ps(DisasContext *ctx, int fs, int fd, { int cond; TCGv_i32 t0 = tcg_temp_new_i32(); - int l1 = gen_new_label(); - int l2 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); if (tf) cond = TCG_COND_EQ; @@ -8856,7 +8869,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, case OPC_MOVZ_S: check_insn_opc_removed(ctx, ISA_MIPS32R6); { - int l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); TCGv_i32 fp0; if (ft != 0) { @@ -8873,7 +8886,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, case OPC_MOVN_S: check_insn_opc_removed(ctx, ISA_MIPS32R6); { - int l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); TCGv_i32 fp0; if (ft != 0) { @@ -9401,7 +9414,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, case OPC_MOVZ_D: check_insn_opc_removed(ctx, ISA_MIPS32R6); { - int l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); TCGv_i64 fp0; if (ft != 0) { @@ -9418,7 +9431,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, case OPC_MOVN_D: check_insn_opc_removed(ctx, ISA_MIPS32R6); { - int l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); TCGv_i64 fp0; if (ft != 0) { @@ -9839,7 +9852,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, case OPC_MOVZ_PS: check_cp1_64bitmode(ctx); { - int l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); TCGv_i64 fp0; if (ft != 0) @@ -9855,7 +9868,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, case OPC_MOVN_PS: check_cp1_64bitmode(ctx); { - int l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); TCGv_i64 fp0; if (ft != 0) { @@ -10199,8 +10212,8 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, TCGv t0 = tcg_temp_local_new(); TCGv_i32 fp = tcg_temp_new_i32(); TCGv_i32 fph = tcg_temp_new_i32(); - int l1 = gen_new_label(); - int l2 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); gen_load_gpr(t0, fr); tcg_gen_andi_tl(t0, t0, 0x7); @@ -10518,14 +10531,25 @@ static void gen_rdhwr(DisasContext *ctx, int rt, int rd) tcg_temp_free(t0); } +static inline void clear_branch_hflags(DisasContext *ctx) +{ + ctx->hflags &= ~MIPS_HFLAG_BMASK; + if (ctx->bstate == BS_NONE) { + save_cpu_state(ctx, 0); + } else { + /* it is not safe to save ctx->hflags as hflags may be changed + in execution time by the instruction in delay / forbidden slot. */ + tcg_gen_andi_i32(hflags, hflags, ~MIPS_HFLAG_BMASK); + } +} + static void gen_branch(DisasContext *ctx, int insn_bytes) { if (ctx->hflags & MIPS_HFLAG_BMASK) { int proc_hflags = ctx->hflags & MIPS_HFLAG_BMASK; /* Branches completion */ - ctx->hflags &= ~MIPS_HFLAG_BMASK; + clear_branch_hflags(ctx); ctx->bstate = BS_BRANCH; - save_cpu_state(ctx, 0); /* FIXME: Need to clear can_do_io. */ switch (proc_hflags & MIPS_HFLAG_BMASK_BASE) { case MIPS_HFLAG_FBNSLOT: @@ -10549,7 +10573,7 @@ static void gen_branch(DisasContext *ctx, int insn_bytes) /* Conditional branch */ MIPS_DEBUG("conditional branch"); { - int l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); tcg_gen_brcondi_tl(TCG_COND_NE, bcond, 0, l1); gen_goto_tb(ctx, 1, ctx->pc + insn_bytes); @@ -10583,8 +10607,8 @@ static void gen_branch(DisasContext *ctx, int insn_bytes) tcg_gen_exit_tb(0); break; default: - MIPS_DEBUG("unknown branch"); - break; + fprintf(stderr, "unknown branch 0x%x\n", proc_hflags); + abort(); } } } @@ -10717,6 +10741,7 @@ static void gen_mips16_save (DisasContext *ctx, { TCGv t0 = tcg_temp_new(); TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); int args, astatic; switch (aregs) { @@ -10775,7 +10800,8 @@ static void gen_mips16_save (DisasContext *ctx, gen_load_gpr(t0, 29); #define DECR_AND_STORE(reg) do { \ - tcg_gen_subi_tl(t0, t0, 4); \ + tcg_gen_movi_tl(t2, -4); \ + gen_op_addr_add(ctx, t0, t0, t2); \ gen_load_gpr(t1, reg); \ tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL); \ } while (0) @@ -10859,9 +10885,11 @@ static void gen_mips16_save (DisasContext *ctx, } #undef DECR_AND_STORE - tcg_gen_subi_tl(cpu_gpr[29], cpu_gpr[29], framesize); + tcg_gen_movi_tl(t2, -framesize); + gen_op_addr_add(ctx, cpu_gpr[29], cpu_gpr[29], t2); tcg_temp_free(t0); tcg_temp_free(t1); + tcg_temp_free(t2); } static void gen_mips16_restore (DisasContext *ctx, @@ -10872,11 +10900,14 @@ static void gen_mips16_restore (DisasContext *ctx, int astatic; TCGv t0 = tcg_temp_new(); TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); - tcg_gen_addi_tl(t0, cpu_gpr[29], framesize); + tcg_gen_movi_tl(t2, framesize); + gen_op_addr_add(ctx, t0, cpu_gpr[29], t2); #define DECR_AND_LOAD(reg) do { \ - tcg_gen_subi_tl(t0, t0, 4); \ + tcg_gen_movi_tl(t2, -4); \ + gen_op_addr_add(ctx, t0, t0, t2); \ tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TESL); \ gen_store_gpr(t1, reg); \ } while (0) @@ -10960,9 +10991,11 @@ static void gen_mips16_restore (DisasContext *ctx, } #undef DECR_AND_LOAD - tcg_gen_addi_tl(cpu_gpr[29], cpu_gpr[29], framesize); + tcg_gen_movi_tl(t2, framesize); + gen_op_addr_add(ctx, cpu_gpr[29], cpu_gpr[29], t2); tcg_temp_free(t0); tcg_temp_free(t1); + tcg_temp_free(t2); } static void gen_addiupc (DisasContext *ctx, int rx, int imm, @@ -10993,26 +11026,32 @@ static void decode_i64_mips16 (DisasContext *ctx, { switch (funct) { case I64_LDSP: + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); offset = extended ? offset : offset << 3; gen_ld(ctx, OPC_LD, ry, 29, offset); break; case I64_SDSP: + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); offset = extended ? offset : offset << 3; gen_st(ctx, OPC_SD, ry, 29, offset); break; case I64_SDRASP: + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); offset = extended ? offset : (ctx->opcode & 0xff) << 3; gen_st(ctx, OPC_SD, 31, 29, offset); break; case I64_DADJSP: + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); offset = extended ? offset : ((int8_t)ctx->opcode) << 3; gen_arith_imm(ctx, OPC_DADDIU, 29, 29, offset); break; case I64_LDPC: + check_insn(ctx, ISA_MIPS3); + check_mips_64(ctx); if (extended && (ctx->hflags & MIPS_HFLAG_BMASK)) { generate_exception(ctx, EXCP_RI); } else { @@ -11021,16 +11060,19 @@ static void decode_i64_mips16 (DisasContext *ctx, } break; case I64_DADDIU5: + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); offset = extended ? offset : ((int8_t)(offset << 3)) >> 3; gen_arith_imm(ctx, OPC_DADDIU, ry, ry, offset); break; case I64_DADDIUPC: + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); offset = extended ? offset : offset << 2; gen_addiupc(ctx, ry, offset, 1, extended); break; case I64_DADDIUSP: + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); offset = extended ? offset : offset << 2; gen_arith_imm(ctx, OPC_DADDIU, ry, 29, offset); @@ -11099,7 +11141,8 @@ static int decode_extended_mips16_opc (CPUMIPSState *env, DisasContext *ctx) break; #if defined(TARGET_MIPS64) case M16_OPC_LD: - check_mips_64(ctx); + check_insn(ctx, ISA_MIPS3); + check_mips_64(ctx); gen_ld(ctx, OPC_LD, ry, rx, offset); break; #endif @@ -11143,6 +11186,7 @@ static int decode_extended_mips16_opc (CPUMIPSState *env, DisasContext *ctx) gen_arith_imm(ctx, OPC_ADDIU, 29, 29, imm); break; case I8_SVRS: + check_insn(ctx, ISA_MIPS32); { int xsregs = (ctx->opcode >> 24) & 0x7; int aregs = (ctx->opcode >> 16) & 0xf; @@ -11176,6 +11220,8 @@ static int decode_extended_mips16_opc (CPUMIPSState *env, DisasContext *ctx) break; #if defined(TARGET_MIPS64) case M16_OPC_SD: + check_insn(ctx, ISA_MIPS3); + check_mips_64(ctx); gen_st(ctx, OPC_SD, ry, rx, offset); break; #endif @@ -11202,6 +11248,8 @@ static int decode_extended_mips16_opc (CPUMIPSState *env, DisasContext *ctx) break; #if defined(TARGET_MIPS64) case M16_OPC_LWU: + check_insn(ctx, ISA_MIPS3); + check_mips_64(ctx); gen_ld(ctx, OPC_LWU, ry, rx, offset); break; #endif @@ -11291,6 +11339,7 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx) break; case 0x1: #if defined(TARGET_MIPS64) + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); gen_shift_imm(ctx, OPC_DSLL, rx, ry, sa); #else @@ -11307,6 +11356,7 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx) break; #if defined(TARGET_MIPS64) case M16_OPC_LD: + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); gen_ld(ctx, OPC_LD, ry, rx, offset << 3); break; @@ -11317,6 +11367,7 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx) if ((ctx->opcode >> 4) & 1) { #if defined(TARGET_MIPS64) + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); gen_arith_imm(ctx, OPC_DADDIU, ry, rx, imm); #else @@ -11368,6 +11419,7 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx) ((int8_t)ctx->opcode) << 3); break; case I8_SVRS: + check_insn(ctx, ISA_MIPS32); { int do_ra = ctx->opcode & (1 << 6); int do_s0 = ctx->opcode & (1 << 5); @@ -11423,6 +11475,7 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx) break; #if defined(TARGET_MIPS64) case M16_OPC_SD: + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); gen_st(ctx, OPC_SD, ry, rx, offset << 3); break; @@ -11450,6 +11503,7 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx) break; #if defined (TARGET_MIPS64) case M16_OPC_LWU: + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); gen_ld(ctx, OPC_LWU, ry, rx, offset << 2); break; @@ -11481,10 +11535,12 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx) #if defined(TARGET_MIPS64) case RRR_DADDU: mips32_op = OPC_DADDU; + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); break; case RRR_DSUBU: mips32_op = OPC_DSUBU; + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); break; #endif @@ -11506,6 +11562,10 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx) int link = (ctx->opcode >> 6) & 0x1; int ra = (ctx->opcode >> 5) & 0x1; + if (nd) { + check_insn(ctx, ISA_MIPS32); + } + if (link) { op = OPC_JALR; } else { @@ -11547,6 +11607,7 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx) break; #if defined (TARGET_MIPS64) case RR_DSRL: + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); gen_shift_imm(ctx, OPC_DSRL, ry, ry, sa); break; @@ -11573,6 +11634,7 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx) gen_HILO(ctx, OPC_MFHI, 0, rx); break; case RR_CNVT: + check_insn(ctx, ISA_MIPS32); switch (cnvt_op) { case RR_RY_CNVT_ZEB: tcg_gen_ext8u_tl(cpu_gpr[rx], cpu_gpr[rx]); @@ -11588,10 +11650,12 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx) break; #if defined (TARGET_MIPS64) case RR_RY_CNVT_ZEW: + check_insn(ctx, ISA_MIPS64); check_mips_64(ctx); tcg_gen_ext32u_tl(cpu_gpr[rx], cpu_gpr[rx]); break; case RR_RY_CNVT_SEW: + check_insn(ctx, ISA_MIPS64); check_mips_64(ctx); tcg_gen_ext32s_tl(cpu_gpr[rx], cpu_gpr[rx]); break; @@ -11606,18 +11670,22 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx) break; #if defined (TARGET_MIPS64) case RR_DSRA: + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); gen_shift_imm(ctx, OPC_DSRA, ry, ry, sa); break; case RR_DSLLV: + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); gen_shift(ctx, OPC_DSLLV, ry, rx, ry); break; case RR_DSRLV: + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); gen_shift(ctx, OPC_DSRLV, ry, rx, ry); break; case RR_DSRAV: + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); gen_shift(ctx, OPC_DSRAV, ry, rx, ry); break; @@ -11636,18 +11704,22 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx) break; #if defined (TARGET_MIPS64) case RR_DMULT: + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); gen_muldiv(ctx, OPC_DMULT, 0, rx, ry); break; case RR_DMULTU: + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); gen_muldiv(ctx, OPC_DMULTU, 0, rx, ry); break; case RR_DDIV: + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); gen_muldiv(ctx, OPC_DDIV, 0, rx, ry); break; case RR_DDIVU: + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); gen_muldiv(ctx, OPC_DDIVU, 0, rx, ry); break; @@ -13212,20 +13284,26 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, /* COP2: Not implemented. */ generate_exception_err(ctx, EXCP_CpU, 2); break; - case LWP: - case SWP: #ifdef TARGET_MIPS64 case LDP: case SDP: + check_insn(ctx, ISA_MIPS3); + check_mips_64(ctx); + /* Fallthrough */ #endif + case LWP: + case SWP: gen_ldst_pair(ctx, minor, rt, rs, SIMM(ctx->opcode, 0, 12)); break; - case LWM32: - case SWM32: #ifdef TARGET_MIPS64 case LDM: case SDM: + check_insn(ctx, ISA_MIPS3); + check_mips_64(ctx); + /* Fallthrough */ #endif + case LWM32: + case SWM32: gen_ldst_multiple(ctx, minor, rt, rs, SIMM(ctx->opcode, 0, 12)); break; default: @@ -13586,7 +13664,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, target. */ break; case LUI: - gen_logic_imm(ctx, OPC_LUI, rs, -1, imm); + gen_logic_imm(ctx, OPC_LUI, rs, 0, imm); break; case SYNCI: /* Break the TB to be able to sync copied instructions @@ -13649,21 +13727,33 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, goto do_st_lr; #if defined(TARGET_MIPS64) case LDL: + check_insn(ctx, ISA_MIPS3); + check_mips_64(ctx); mips32_op = OPC_LDL; goto do_ld_lr; case SDL: + check_insn(ctx, ISA_MIPS3); + check_mips_64(ctx); mips32_op = OPC_SDL; goto do_st_lr; case LDR: + check_insn(ctx, ISA_MIPS3); + check_mips_64(ctx); mips32_op = OPC_LDR; goto do_ld_lr; case SDR: + check_insn(ctx, ISA_MIPS3); + check_mips_64(ctx); mips32_op = OPC_SDR; goto do_st_lr; case LWU: + check_insn(ctx, ISA_MIPS3); + check_mips_64(ctx); mips32_op = OPC_LWU; goto do_ld_lr; case LLD: + check_insn(ctx, ISA_MIPS3); + check_mips_64(ctx); mips32_op = OPC_LLD; goto do_ld_lr; #endif @@ -13681,6 +13771,8 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, break; #if defined(TARGET_MIPS64) case SCD: + check_insn(ctx, ISA_MIPS3); + check_mips_64(ctx); gen_st_cond(ctx, OPC_SCD, rt, rs, SIMM(ctx->opcode, 0, 12)); break; #endif @@ -13790,9 +13882,13 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, goto do_ld; #ifdef TARGET_MIPS64 case LD32: + check_insn(ctx, ISA_MIPS3); + check_mips_64(ctx); mips32_op = OPC_LD; goto do_ld; case SD32: + check_insn(ctx, ISA_MIPS3); + check_mips_64(ctx); mips32_op = OPC_SD; goto do_st; #endif @@ -13936,8 +14032,8 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx) rs = rs_rt_enc[enc_rs]; rt = rs_rt_enc[enc_rt]; - gen_arith_imm(ctx, OPC_ADDIU, rd, rs, 0); - gen_arith_imm(ctx, OPC_ADDIU, re, rt, 0); + gen_arith(ctx, OPC_ADDU, rd, rs, 0); + gen_arith(ctx, OPC_ADDU, re, rt, 0); } break; case LBU16: @@ -14018,7 +14114,7 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx) int rd = uMIPS_RD5(ctx->opcode); int rs = uMIPS_RS5(ctx->opcode); - gen_arith_imm(ctx, OPC_ADDIU, rd, rs, 0); + gen_arith(ctx, OPC_ADDU, rd, rs, 0); } break; case ANDI16: @@ -15913,7 +16009,7 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc, gen_branch(ctx, 4); } else { /* Conditional compact branch */ - int fs = gen_new_label(); + TCGLabel *fs = gen_new_label(); save_cpu_state(ctx, 0); switch (opc) { @@ -16322,6 +16418,7 @@ static void decode_opc_special(CPUMIPSState *env, DisasContext *ctx) break; case OPC_TGE ... OPC_TEQ: /* Traps */ case OPC_TNE: + check_insn(ctx, ISA_MIPS2); gen_trap(ctx, op1, rs, rt, -1); break; case OPC_LSA: /* OPC_PMON */ @@ -16346,6 +16443,7 @@ static void decode_opc_special(CPUMIPSState *env, DisasContext *ctx) generate_exception(ctx, EXCP_BREAK); break; case OPC_SYNC: + check_insn(ctx, ISA_MIPS2); /* Treat as NOP. */ break; @@ -18316,12 +18414,14 @@ static void gen_msa(CPUMIPSState *env, DisasContext *ctx) case OPC_LD_H: case OPC_LD_W: case OPC_LD_D: + save_cpu_state(ctx, 1); gen_helper_msa_ld_df(cpu_env, tdf, twd, trs, ts10); break; case OPC_ST_B: case OPC_ST_H: case OPC_ST_W: case OPC_ST_D: + save_cpu_state(ctx, 1); gen_helper_msa_st_df(cpu_env, tdf, twd, trs, ts10); break; } @@ -18340,7 +18440,7 @@ static void gen_msa(CPUMIPSState *env, DisasContext *ctx) } -static void decode_opc (CPUMIPSState *env, DisasContext *ctx) +static void decode_opc(CPUMIPSState *env, DisasContext *ctx) { int32_t offset; int rs, rt, rd, sa; @@ -18351,12 +18451,13 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx) if (ctx->pc & 0x3) { env->CP0_BadVAddr = ctx->pc; generate_exception_err(ctx, EXCP_AdEL, EXCP_INST_NOTAVAIL); + ctx->bstate = BS_STOP; return; } /* Handle blikely not taken case */ if ((ctx->hflags & MIPS_HFLAG_BMASK_BASE) == MIPS_HFLAG_BL) { - int l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); MIPS_DEBUG("blikely condition (" TARGET_FMT_lx ")", ctx->pc + 4); tcg_gen_brcondi_tl(TCG_COND_NE, bcond, 0, l1); @@ -18392,7 +18493,9 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx) case OPC_BGEZL: case OPC_BLTZALL: case OPC_BGEZALL: + check_insn(ctx, ISA_MIPS2); check_insn_opc_removed(ctx, ISA_MIPS32R6); + /* Fallthrough */ case OPC_BLTZ: case OPC_BGEZ: gen_compute_branch(ctx, op1, 4, rs, -1, imm << 2, 4); @@ -18412,6 +18515,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx) break; case OPC_TGEI ... OPC_TEQI: /* REGIMM traps */ case OPC_TNEI: + check_insn(ctx, ISA_MIPS2); check_insn_opc_removed(ctx, ISA_MIPS32R6); gen_trap(ctx, op1, rs, -1, imm); break; @@ -18506,7 +18610,8 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx) save_cpu_state(ctx, 1); gen_helper_di(t0, cpu_env); gen_store_gpr(t0, rt); - /* Stop translation as we may have switched the execution mode */ + /* Stop translation as we may have switched + the execution mode. */ ctx->bstate = BS_STOP; break; case OPC_EI: @@ -18514,7 +18619,8 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx) save_cpu_state(ctx, 1); gen_helper_ei(t0, cpu_env); gen_store_gpr(t0, rt); - /* Stop translation as we may have switched the execution mode */ + /* Stop translation as we may have switched + the execution mode. */ ctx->bstate = BS_STOP; break; default: /* Invalid */ @@ -18616,15 +18722,20 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx) break; case OPC_BEQL: case OPC_BNEL: + check_insn(ctx, ISA_MIPS2); check_insn_opc_removed(ctx, ISA_MIPS32R6); + /* Fallthrough */ case OPC_BEQ: case OPC_BNE: gen_compute_branch(ctx, op, 4, rs, rt, imm << 2, 4); break; - case OPC_LWL: /* Load and stores */ + case OPC_LL: /* Load and stores */ + check_insn(ctx, ISA_MIPS2); + /* Fallthrough */ + case OPC_LWL: case OPC_LWR: - case OPC_LL: check_insn_opc_removed(ctx, ISA_MIPS32R6); + /* Fallthrough */ case OPC_LB ... OPC_LH: case OPC_LW ... OPC_LHU: gen_ld(ctx, op, rt, rs, imm); @@ -18632,11 +18743,13 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx) case OPC_SWL: case OPC_SWR: check_insn_opc_removed(ctx, ISA_MIPS32R6); + /* fall through */ case OPC_SB ... OPC_SH: case OPC_SW: gen_st(ctx, op, rt, rs, imm); break; case OPC_SC: + check_insn(ctx, ISA_MIPS2); check_insn_opc_removed(ctx, ISA_MIPS32R6); gen_st_cond(ctx, op, rt, rs, imm); break; @@ -18680,6 +18793,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx) case OPC_DMTC1: check_cp1_enabled(ctx); check_insn(ctx, ISA_MIPS3); + check_mips_64(ctx); gen_cp1(ctx, op1, rt, rd); break; #endif @@ -18718,6 +18832,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx) case OPC_PS_FMT: check_cp1_enabled(ctx); check_insn_opc_removed(ctx, ISA_MIPS32R6); + /* fall through */ case OPC_S_FMT: case OPC_D_FMT: check_cp1_enabled(ctx); @@ -18780,8 +18895,9 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx) gen_r6_cmp_d(ctx, ctx->opcode & 0x1f, rt, rd, sa); break; default: - gen_farith(ctx, ctx->opcode & FOP(0x3f, 0x1f), rt, rd, sa, - (imm >> 8) & 0x7); + gen_farith(ctx, ctx->opcode & FOP(0x3f, 0x1f), + rt, rd, sa, (imm >> 8) & 0x7); + break; } } else { @@ -18852,18 +18968,24 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx) check_cp1_enabled(ctx); op1 = MASK_CP3(ctx->opcode); switch (op1) { + case OPC_LUXC1: + case OPC_SUXC1: + check_insn(ctx, ISA_MIPS5 | ISA_MIPS32R2); + /* Fallthrough */ case OPC_LWXC1: case OPC_LDXC1: - case OPC_LUXC1: case OPC_SWXC1: case OPC_SDXC1: - case OPC_SUXC1: + check_insn(ctx, ISA_MIPS4 | ISA_MIPS32R2); gen_flt3_ldst(ctx, op1, sa, rd, rs, rt); break; case OPC_PREFX: + check_insn(ctx, ISA_MIPS4 | ISA_MIPS32R2); /* Treat as NOP. */ break; case OPC_ALNV_PS: + check_insn(ctx, ISA_MIPS5 | ISA_MIPS32R2); + /* Fallthrough */ case OPC_MADD_S: case OPC_MADD_D: case OPC_MADD_PS: @@ -18876,6 +18998,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx) case OPC_NMSUB_S: case OPC_NMSUB_D: case OPC_NMSUB_PS: + check_insn(ctx, ISA_MIPS4 | ISA_MIPS32R2); gen_flt3_arith(ctx, op1, sa, rs, rd, rt); break; default: @@ -18893,6 +19016,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx) case OPC_LDL ... OPC_LDR: case OPC_LLD: check_insn_opc_removed(ctx, ISA_MIPS32R6); + /* fall through */ case OPC_LWU: case OPC_LD: check_insn(ctx, ISA_MIPS3); @@ -18901,6 +19025,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx) break; case OPC_SDL ... OPC_SDR: check_insn_opc_removed(ctx, ISA_MIPS32R6); + /* fall through */ case OPC_SD: check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); @@ -18984,7 +19109,7 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, CPUMIPSState *env = &cpu->env; DisasContext ctx; target_ulong pc_start; - uint16_t *gen_opc_end; + target_ulong next_page_start; CPUBreakpoint *bp; int j, lj = -1; int num_insns; @@ -18996,7 +19121,7 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, qemu_log("search pc %d\n", search_pc); pc_start = tb->pc; - gen_opc_end = tcg_ctx.gen_opc_buf + OPC_MAX_SIZE; + next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; ctx.pc = pc_start; ctx.saved_pc = -1; ctx.singlestep_enabled = cs->singlestep_enabled; @@ -19011,7 +19136,7 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, ctx.bp = (env->CP0_Config3 >> CP0C3_BP) & 1; /* Restore delay slot state from the tb context. */ ctx.hflags = (uint32_t)tb->flags; /* FIXME: maybe use 64 bits here? */ - ctx.ulri = env->CP0_Config3 & (1 << CP0C3_ULRI); + ctx.ulri = (env->CP0_Config3 >> CP0C3_ULRI) & 1; restore_cpu_state(env, &ctx); #ifdef CONFIG_USER_ONLY ctx.mem_idx = MIPS_HFLAG_UM; @@ -19023,7 +19148,7 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, if (max_insns == 0) max_insns = CF_COUNT_MASK; LOG_DISAS("\ntb %p idx %d hflags %04x\n", tb, ctx.mem_idx, ctx.hflags); - gen_tb_start(); + gen_tb_start(tb); while (ctx.bstate == BS_NONE) { if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) { QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { @@ -19040,7 +19165,7 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, } if (search_pc) { - j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + j = tcg_op_buf_count(); if (lj < j) { lj++; while (lj < j) @@ -19095,10 +19220,11 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, break; } - if ((ctx.pc & (TARGET_PAGE_SIZE - 1)) == 0) + if (ctx.pc >= next_page_start) { break; + } - if (tcg_ctx.gen_opc_ptr >= gen_opc_end) { + if (tcg_op_buf_full()) { break; } @@ -19133,9 +19259,9 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, } done_generating: gen_tb_end(tb, num_insns); - *tcg_ctx.gen_opc_ptr = INDEX_op_end; + if (search_pc) { - j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + j = tcg_op_buf_count(); lj++; while (lj <= j) tcg_ctx.gen_opc_instr_start[lj++] = 0; @@ -19261,6 +19387,10 @@ void mips_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, env->CP0_Status, env->CP0_Cause, env->CP0_EPC); cpu_fprintf(f, " Config0 0x%08x Config1 0x%08x LLAddr 0x" TARGET_FMT_lx "\n", env->CP0_Config0, env->CP0_Config1, env->lladdr); + cpu_fprintf(f, " Config2 0x%08x Config3 0x%08x\n", + env->CP0_Config2, env->CP0_Config3); + cpu_fprintf(f, " Config4 0x%08x Config5 0x%08x\n", + env->CP0_Config4, env->CP0_Config5); if (env->hflags & MIPS_HFLAG_FPU) fpu_dump_state(env, f, cpu_fprintf, flags); #if defined(TARGET_MIPS64) && defined(MIPS_DEBUG_SIGN_EXTENSIONS) @@ -19436,7 +19566,8 @@ void cpu_state_reset(CPUMIPSState *env) if (env->hflags & MIPS_HFLAG_BMASK) { /* If the exception was raised from a delay slot, come back to the jump. */ - env->CP0_ErrorEPC = env->active_tc.PC - 4; + env->CP0_ErrorEPC = (env->active_tc.PC + - (env->hflags & MIPS_HFLAG_B16 ? 2 : 4)); } else { env->CP0_ErrorEPC = env->active_tc.PC; } @@ -19507,6 +19638,8 @@ void cpu_state_reset(CPUMIPSState *env) } compute_hflags(env); + restore_rounding_mode(env); + restore_flush_mode(env); cs->exception_index = EXCP_NONE; } diff --git a/target-mips/translate_init.c b/target-mips/translate_init.c index 148b394..85a65e7 100644 --- a/target-mips/translate_init.c +++ b/target-mips/translate_init.c @@ -334,7 +334,7 @@ static const mips_def_t mips_defs[] = (1 << CP0C1_CA), .CP0_Config2 = MIPS_CONFIG2, .CP0_Config3 = MIPS_CONFIG3 | (1 << CP0C3_DSP2P) | (1 << CP0C3_DSPP) | - (0 << CP0C3_VInt), + (1 << CP0C3_VInt), .CP0_LLAddr_rw_bitmask = 0, .CP0_LLAddr_shift = 4, .SYNCI_Step = 32, @@ -348,6 +348,47 @@ static const mips_def_t mips_defs[] = .mmu_type = MMU_TYPE_R4000, }, { + .name = "M14K", + .CP0_PRid = 0x00019b00, + /* Config1 implemented, fixed mapping MMU, + no virtual icache, uncached coherency. */ + .CP0_Config0 = MIPS_CONFIG0 | (0x2 << CP0C0_KU) | (0x2 << CP0C0_K23) | + (0x1 << CP0C0_AR) | (MMU_TYPE_FMT << CP0C0_MT), + .CP0_Config1 = MIPS_CONFIG1, + .CP0_Config2 = MIPS_CONFIG2, + .CP0_Config3 = MIPS_CONFIG3 | (0x2 << CP0C3_ISA) | (1 << CP0C3_VInt), + .CP0_LLAddr_rw_bitmask = 0, + .CP0_LLAddr_shift = 4, + .SYNCI_Step = 32, + .CCRes = 2, + .CP0_Status_rw_bitmask = 0x1258FF17, + .SEGBITS = 32, + .PABITS = 32, + .insn_flags = CPU_MIPS32R2 | ASE_MICROMIPS, + .mmu_type = MMU_TYPE_FMT, + }, + { + .name = "M14Kc", + /* This is the TLB-based MMU core. */ + .CP0_PRid = 0x00019c00, + .CP0_Config0 = MIPS_CONFIG0 | (0x1 << CP0C0_AR) | + (MMU_TYPE_R4000 << CP0C0_MT), + .CP0_Config1 = MIPS_CONFIG1 | (15 << CP0C1_MMU) | + (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) | + (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA), + .CP0_Config2 = MIPS_CONFIG2, + .CP0_Config3 = MIPS_CONFIG3 | (0x2 << CP0C3_ISA) | (0 << CP0C3_VInt), + .CP0_LLAddr_rw_bitmask = 0, + .CP0_LLAddr_shift = 4, + .SYNCI_Step = 32, + .CCRes = 2, + .CP0_Status_rw_bitmask = 0x1278FF17, + .SEGBITS = 32, + .PABITS = 32, + .insn_flags = CPU_MIPS32R2 | ASE_MICROMIPS, + .mmu_type = MMU_TYPE_R4000, + }, + { /* A generic CPU providing MIPS32 Release 5 features. FIXME: Eventually this should be replaced by a real CPU model. */ .name = "mips32r5-generic", @@ -433,7 +474,7 @@ static const mips_def_t mips_defs[] = .CP0_LLAddr_shift = 4, .SYNCI_Step = 32, .CCRes = 2, - .CP0_Status_rw_bitmask = 0x32F8FFFF, + .CP0_Status_rw_bitmask = 0x12F8FFFF, .SEGBITS = 42, .PABITS = 36, .insn_flags = CPU_MIPS64, @@ -520,6 +561,51 @@ static const mips_def_t mips_defs[] = .mmu_type = MMU_TYPE_R4000, }, { + .name = "5KEc", + .CP0_PRid = 0x00018900, + .CP0_Config0 = MIPS_CONFIG0 | (0x1 << CP0C0_AR) | (0x2 << CP0C0_AT) | + (MMU_TYPE_R4000 << CP0C0_MT), + .CP0_Config1 = MIPS_CONFIG1 | (31 << CP0C1_MMU) | + (1 << CP0C1_IS) | (4 << CP0C1_IL) | (1 << CP0C1_IA) | + (1 << CP0C1_DS) | (4 << CP0C1_DL) | (1 << CP0C1_DA) | + (1 << CP0C1_PC) | (1 << CP0C1_WR) | (1 << CP0C1_EP), + .CP0_Config2 = MIPS_CONFIG2, + .CP0_Config3 = MIPS_CONFIG3, + .CP0_LLAddr_rw_bitmask = 0, + .CP0_LLAddr_shift = 4, + .SYNCI_Step = 32, + .CCRes = 2, + .CP0_Status_rw_bitmask = 0x12F8FFFF, + .SEGBITS = 42, + .PABITS = 36, + .insn_flags = CPU_MIPS64R2, + .mmu_type = MMU_TYPE_R4000, + }, + { + .name = "5KEf", + .CP0_PRid = 0x00018900, + .CP0_Config0 = MIPS_CONFIG0 | (0x1 << CP0C0_AR) | (0x2 << CP0C0_AT) | + (MMU_TYPE_R4000 << CP0C0_MT), + .CP0_Config1 = MIPS_CONFIG1 | (1 << CP0C1_FP) | (31 << CP0C1_MMU) | + (1 << CP0C1_IS) | (4 << CP0C1_IL) | (1 << CP0C1_IA) | + (1 << CP0C1_DS) | (4 << CP0C1_DL) | (1 << CP0C1_DA) | + (1 << CP0C1_PC) | (1 << CP0C1_WR) | (1 << CP0C1_EP), + .CP0_Config2 = MIPS_CONFIG2, + .CP0_Config3 = MIPS_CONFIG3, + .CP0_LLAddr_rw_bitmask = 0, + .CP0_LLAddr_shift = 4, + .SYNCI_Step = 32, + .CCRes = 2, + .CP0_Status_rw_bitmask = 0x36F8FFFF, + .CP1_fcr0 = (1 << FCR0_F64) | (1 << FCR0_L) | (1 << FCR0_W) | + (1 << FCR0_D) | (1 << FCR0_S) | + (0x89 << FCR0_PRID) | (0x0 << FCR0_REV), + .SEGBITS = 42, + .PABITS = 36, + .insn_flags = CPU_MIPS64R2, + .mmu_type = MMU_TYPE_R4000, + }, + { /* A generic CPU supporting MIPS64 Release 6 ISA. FIXME: Support IEEE 754-2008 FP and misaligned memory accesses. Eventually this should be replaced by a real CPU model. */ @@ -559,10 +645,11 @@ static const mips_def_t mips_defs[] = { .name = "Loongson-2E", .CP0_PRid = 0x6302, - /*64KB I-cache and d-cache. 4 way with 32 bit cache line size*/ - .CP0_Config0 = (0x1<<17) | (0x1<<16) | (0x1<<11) | (0x1<<8) | (0x1<<5) | - (0x1<<4) | (0x1<<1), - /* Note: Config1 is only used internally, Loongson-2E has only Config0. */ + /* 64KB I-cache and d-cache. 4 way with 32 bit cache line size. */ + .CP0_Config0 = (0x1<<17) | (0x1<<16) | (0x1<<11) | (0x1<<8) | + (0x1<<5) | (0x1<<4) | (0x1<<1), + /* Note: Config1 is only used internally, + Loongson-2E has only Config0. */ .CP0_Config1 = (1 << CP0C1_FP) | (47 << CP0C1_MMU), .SYNCI_Step = 16, .CCRes = 2, @@ -574,21 +661,22 @@ static const mips_def_t mips_defs[] = .mmu_type = MMU_TYPE_R4000, }, { - .name = "Loongson-2F", - .CP0_PRid = 0x6303, - /*64KB I-cache and d-cache. 4 way with 32 bit cache line size*/ - .CP0_Config0 = (0x1<<17) | (0x1<<16) | (0x1<<11) | (0x1<<8) | (0x1<<5) | - (0x1<<4) | (0x1<<1), - /* Note: Config1 is only used internally, Loongson-2F has only Config0. */ - .CP0_Config1 = (1 << CP0C1_FP) | (47 << CP0C1_MMU), - .SYNCI_Step = 16, - .CCRes = 2, - .CP0_Status_rw_bitmask = 0xF5D0FF1F, /*bit5:7 not writable*/ - .CP1_fcr0 = (0x5 << FCR0_PRID) | (0x1 << FCR0_REV), - .SEGBITS = 40, - .PABITS = 40, - .insn_flags = CPU_LOONGSON2F, - .mmu_type = MMU_TYPE_R4000, + .name = "Loongson-2F", + .CP0_PRid = 0x6303, + /* 64KB I-cache and d-cache. 4 way with 32 bit cache line size. */ + .CP0_Config0 = (0x1<<17) | (0x1<<16) | (0x1<<11) | (0x1<<8) | + (0x1<<5) | (0x1<<4) | (0x1<<1), + /* Note: Config1 is only used internally, + Loongson-2F has only Config0. */ + .CP0_Config1 = (1 << CP0C1_FP) | (47 << CP0C1_MMU), + .SYNCI_Step = 16, + .CCRes = 2, + .CP0_Status_rw_bitmask = 0xF5D0FF1F, /* Bits 7:5 not writable. */ + .CP1_fcr0 = (0x5 << FCR0_PRID) | (0x1 << FCR0_REV), + .SEGBITS = 40, + .PABITS = 40, + .insn_flags = CPU_LOONGSON2F, + .mmu_type = MMU_TYPE_R4000, }, { /* A generic CPU providing MIPS64 ASE DSP 2 features. @@ -747,6 +835,8 @@ static void msa_reset(CPUMIPSState *env) - round to nearest / ties to even (RM bits are 0) */ env->active_tc.msacsr = 0; + restore_msa_fp_status(env); + /* tininess detected after rounding.*/ set_float_detect_tininess(float_tininess_after_rounding, &env->active_tc.msa_fp_status); @@ -754,14 +844,6 @@ static void msa_reset(CPUMIPSState *env) /* clear float_status exception flags */ set_float_exception_flags(0, &env->active_tc.msa_fp_status); - /* set float_status rounding mode */ - set_float_rounding_mode(float_round_nearest_even, - &env->active_tc.msa_fp_status); - - /* set float_status flush modes */ - set_flush_to_zero(0, &env->active_tc.msa_fp_status); - set_flush_inputs_to_zero(0, &env->active_tc.msa_fp_status); - /* clear float_status nan mode */ set_default_nan_mode(0, &env->active_tc.msa_fp_status); } diff --git a/target-moxie/cpu.h b/target-moxie/cpu.h index c5b12a5..c2733a2 100644 --- a/target-moxie/cpu.h +++ b/target-moxie/cpu.h @@ -26,8 +26,6 @@ #define CPUArchState struct CPUMoxieState -#define TARGET_HAS_ICE 1 - #define ELF_MACHINE 0xFEED /* EM_MOXIE */ #define MOXIE_EX_DIV0 0 @@ -123,14 +121,7 @@ void moxie_translate_init(void); int cpu_moxie_signal_handler(int host_signum, void *pinfo, void *puc); -static inline CPUMoxieState *cpu_init(const char *cpu_model) -{ - MoxieCPU *cpu = cpu_moxie_init(cpu_model); - if (cpu == NULL) { - return NULL; - } - return &cpu->env; -} +#define cpu_init(cpu_model) CPU(cpu_moxie_init(cpu_model)) #define cpu_exec cpu_moxie_exec #define cpu_gen_code cpu_moxie_gen_code diff --git a/target-moxie/machine.c b/target-moxie/machine.c index da1a857..b9316f0 100644 --- a/target-moxie/machine.c +++ b/target-moxie/machine.c @@ -1,5 +1,6 @@ #include "hw/hw.h" #include "hw/boards.h" +#include "machine.h" const VMStateDescription vmstate_moxie_cpu = { .name = "cpu", diff --git a/target-moxie/mmu.h b/target-moxie/mmu.h index e01ffc2..abc7929 100644 --- a/target-moxie/mmu.h +++ b/target-moxie/mmu.h @@ -6,11 +6,11 @@ typedef struct { uint32_t phy; uint32_t pfn; - int g:1; - int v:1; - int k:1; - int w:1; - int e:1; + unsigned g:1; + unsigned v:1; + unsigned k:1; + unsigned w:1; + unsigned e:1; int cause_op; } MoxieMMUResult; diff --git a/target-moxie/translate.c b/target-moxie/translate.c index 4541b9b..e3e9139 100644 --- a/target-moxie/translate.c +++ b/target-moxie/translate.c @@ -169,7 +169,7 @@ static int decode_opc(MoxieCPU *cpu, DisasContext *ctx) #define BRANCH(cond) \ do { \ - int l1 = gen_new_label(); \ + TCGLabel *l1 = gen_new_label(); \ tcg_gen_brcond_i32(cond, cc_a, cc_b, l1); \ gen_goto_tb(env, ctx, 1, ctx->pc+2); \ gen_set_label(l1); \ @@ -827,14 +827,12 @@ gen_intermediate_code_internal(MoxieCPU *cpu, TranslationBlock *tb, CPUState *cs = CPU(cpu); DisasContext ctx; target_ulong pc_start; - uint16_t *gen_opc_end; CPUBreakpoint *bp; int j, lj = -1; CPUMoxieState *env = &cpu->env; int num_insns; pc_start = tb->pc; - gen_opc_end = tcg_ctx.gen_opc_buf + OPC_MAX_SIZE; ctx.pc = pc_start; ctx.saved_pc = -1; ctx.tb = tb; @@ -843,7 +841,7 @@ gen_intermediate_code_internal(MoxieCPU *cpu, TranslationBlock *tb, ctx.bstate = BS_NONE; num_insns = 0; - gen_tb_start(); + gen_tb_start(tb); do { if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) { QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { @@ -857,7 +855,7 @@ gen_intermediate_code_internal(MoxieCPU *cpu, TranslationBlock *tb, } if (search_pc) { - j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + j = tcg_op_buf_count(); if (lj < j) { lj++; while (lj < j) { @@ -879,7 +877,7 @@ gen_intermediate_code_internal(MoxieCPU *cpu, TranslationBlock *tb, if ((ctx.pc & (TARGET_PAGE_SIZE - 1)) == 0) { break; } - } while (ctx.bstate == BS_NONE && tcg_ctx.gen_opc_ptr < gen_opc_end); + } while (ctx.bstate == BS_NONE && !tcg_op_buf_full()); if (cs->singlestep_enabled) { tcg_gen_movi_tl(cpu_pc, ctx.pc); @@ -900,9 +898,9 @@ gen_intermediate_code_internal(MoxieCPU *cpu, TranslationBlock *tb, } done_generating: gen_tb_end(tb, num_insns); - *tcg_ctx.gen_opc_ptr = INDEX_op_end; + if (search_pc) { - j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + j = tcg_op_buf_count(); lj++; while (lj <= j) { tcg_ctx.gen_opc_instr_start[lj++] = 0; diff --git a/target-openrisc/cpu.h b/target-openrisc/cpu.h index 69b96c6..b25324b 100644 --- a/target-openrisc/cpu.h +++ b/target-openrisc/cpu.h @@ -389,14 +389,7 @@ int cpu_openrisc_get_phys_data(OpenRISCCPU *cpu, int *prot, target_ulong address, int rw); #endif -static inline CPUOpenRISCState *cpu_init(const char *cpu_model) -{ - OpenRISCCPU *cpu = cpu_openrisc_init(cpu_model); - if (cpu) { - return &cpu->env; - } - return NULL; -} +#define cpu_init(cpu_model) CPU(cpu_openrisc_init(cpu_model)) #include "exec/cpu-all.h" diff --git a/target-openrisc/translate.c b/target-openrisc/translate.c index 407bd97..dc76789 100644 --- a/target-openrisc/translate.c +++ b/target-openrisc/translate.c @@ -118,9 +118,7 @@ void openrisc_translate_init(void) /* Writeback SR_F translation space to execution space. */ static inline void wb_SR_F(void) { - int label; - - label = gen_new_label(); + TCGLabel *label = gen_new_label(); tcg_gen_andi_tl(cpu_sr, cpu_sr, ~SR_F); tcg_gen_brcondi_tl(TCG_COND_EQ, env_btaken, 0, label); tcg_gen_ori_tl(cpu_sr, cpu_sr, SR_F); @@ -226,7 +224,7 @@ static void gen_jump(DisasContext *dc, uint32_t imm, uint32_t reg, uint32_t op0) case 0x03: /* l.bnf */ case 0x04: /* l.bf */ { - int lab = gen_new_label(); + TCGLabel *lab = gen_new_label(); TCGv sr_f = tcg_temp_new(); tcg_gen_movi_tl(jmp_pc, dc->pc+8); tcg_gen_andi_tl(sr_f, cpu_sr, SR_F); @@ -272,7 +270,7 @@ static void dec_calc(DisasContext *dc, uint32_t insn) case 0x00: /* l.add */ LOG_DIS("l.add r%d, r%d, r%d\n", rd, ra, rb); { - int lab = gen_new_label(); + TCGLabel *lab = gen_new_label(); TCGv_i64 ta = tcg_temp_new_i64(); TCGv_i64 tb = tcg_temp_new_i64(); TCGv_i64 td = tcg_temp_local_new_i64(); @@ -311,7 +309,7 @@ static void dec_calc(DisasContext *dc, uint32_t insn) case 0x00: LOG_DIS("l.addc r%d, r%d, r%d\n", rd, ra, rb); { - int lab = gen_new_label(); + TCGLabel *lab = gen_new_label(); TCGv_i64 ta = tcg_temp_new_i64(); TCGv_i64 tb = tcg_temp_new_i64(); TCGv_i64 tcy = tcg_temp_local_new_i64(); @@ -358,7 +356,7 @@ static void dec_calc(DisasContext *dc, uint32_t insn) case 0x00: LOG_DIS("l.sub r%d, r%d, r%d\n", rd, ra, rb); { - int lab = gen_new_label(); + TCGLabel *lab = gen_new_label(); TCGv_i64 ta = tcg_temp_new_i64(); TCGv_i64 tb = tcg_temp_new_i64(); TCGv_i64 td = tcg_temp_local_new_i64(); @@ -450,10 +448,10 @@ static void dec_calc(DisasContext *dc, uint32_t insn) case 0x03: /* l.div */ LOG_DIS("l.div r%d, r%d, r%d\n", rd, ra, rb); { - int lab0 = gen_new_label(); - int lab1 = gen_new_label(); - int lab2 = gen_new_label(); - int lab3 = gen_new_label(); + TCGLabel *lab0 = gen_new_label(); + TCGLabel *lab1 = gen_new_label(); + TCGLabel *lab2 = gen_new_label(); + TCGLabel *lab3 = gen_new_label(); TCGv_i32 sr_ove = tcg_temp_local_new_i32(); if (rb == 0) { tcg_gen_ori_tl(cpu_sr, cpu_sr, (SR_OV | SR_CY)); @@ -492,9 +490,9 @@ static void dec_calc(DisasContext *dc, uint32_t insn) case 0x03: /* l.divu */ LOG_DIS("l.divu r%d, r%d, r%d\n", rd, ra, rb); { - int lab0 = gen_new_label(); - int lab1 = gen_new_label(); - int lab2 = gen_new_label(); + TCGLabel *lab0 = gen_new_label(); + TCGLabel *lab1 = gen_new_label(); + TCGLabel *lab2 = gen_new_label(); TCGv_i32 sr_ove = tcg_temp_local_new_i32(); if (rb == 0) { tcg_gen_ori_tl(cpu_sr, cpu_sr, (SR_OV | SR_CY)); @@ -533,7 +531,7 @@ static void dec_calc(DisasContext *dc, uint32_t insn) TCGv_i64 trb = tcg_temp_local_new_i64(); TCGv_i64 high = tcg_temp_new_i64(); TCGv_i32 sr_ove = tcg_temp_local_new_i32(); - int lab = gen_new_label(); + TCGLabel *lab = gen_new_label(); /* Calculate each result. */ tcg_gen_extu_i32_i64(tra, cpu_R[ra]); tcg_gen_extu_i32_i64(trb, cpu_R[rb]); @@ -568,7 +566,7 @@ static void dec_calc(DisasContext *dc, uint32_t insn) case 0x00: /* l.cmov */ LOG_DIS("l.cmov r%d, r%d, r%d\n", rd, ra, rb); { - int lab = gen_new_label(); + TCGLabel *lab = gen_new_label(); TCGv res = tcg_temp_local_new(); TCGv sr_f = tcg_temp_new(); tcg_gen_andi_tl(sr_f, cpu_sr, SR_F); @@ -893,7 +891,7 @@ static void dec_misc(DisasContext *dc, uint32_t insn) if (I16 == 0) { tcg_gen_mov_tl(cpu_R[rd], cpu_R[ra]); } else { - int lab = gen_new_label(); + TCGLabel *lab = gen_new_label(); TCGv_i64 ta = tcg_temp_new_i64(); TCGv_i64 td = tcg_temp_local_new_i64(); TCGv_i32 res = tcg_temp_local_new_i32(); @@ -923,7 +921,7 @@ static void dec_misc(DisasContext *dc, uint32_t insn) case 0x28: /* l.addic */ LOG_DIS("l.addic r%d, r%d, %d\n", rd, ra, I16); { - int lab = gen_new_label(); + TCGLabel *lab = gen_new_label(); TCGv_i64 ta = tcg_temp_new_i64(); TCGv_i64 td = tcg_temp_local_new_i64(); TCGv_i64 tcy = tcg_temp_local_new_i64(); @@ -1320,7 +1318,7 @@ static void dec_sys(DisasContext *dc, uint32_t insn) #ifdef OPENRISC_DISAS uint32_t K16; #endif - op0 = extract32(insn, 16, 8); + op0 = extract32(insn, 16, 10); #ifdef OPENRISC_DISAS K16 = extract32(insn, 0, 16); #endif @@ -1642,7 +1640,6 @@ static inline void gen_intermediate_code_internal(OpenRISCCPU *cpu, { CPUState *cs = CPU(cpu); struct DisasContext ctx, *dc = &ctx; - uint16_t *gen_opc_end; uint32_t pc_start; int j, k; uint32_t next_page_start; @@ -1652,7 +1649,6 @@ static inline void gen_intermediate_code_internal(OpenRISCCPU *cpu, pc_start = tb->pc; dc->tb = tb; - gen_opc_end = tcg_ctx.gen_opc_buf + OPC_MAX_SIZE; dc->is_jmp = DISAS_NEXT; dc->ppc = pc_start; dc->pc = pc_start; @@ -1675,12 +1671,12 @@ static inline void gen_intermediate_code_internal(OpenRISCCPU *cpu, max_insns = CF_COUNT_MASK; } - gen_tb_start(); + gen_tb_start(tb); do { check_breakpoint(cpu, dc); if (search_pc) { - j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + j = tcg_op_buf_count(); if (k < j) { k++; while (k < j) { @@ -1721,7 +1717,7 @@ static inline void gen_intermediate_code_internal(OpenRISCCPU *cpu, } } } while (!dc->is_jmp - && tcg_ctx.gen_opc_ptr < gen_opc_end + && !tcg_op_buf_full() && !cs->singlestep_enabled && !singlestep && (dc->pc < next_page_start) @@ -1759,9 +1755,9 @@ static inline void gen_intermediate_code_internal(OpenRISCCPU *cpu, } gen_tb_end(tb, num_insns); - *tcg_ctx.gen_opc_ptr = INDEX_op_end; + if (search_pc) { - j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + j = tcg_op_buf_count(); k++; while (k <= j) { tcg_ctx.gen_opc_instr_start[k++] = 0; @@ -1775,9 +1771,8 @@ static inline void gen_intermediate_code_internal(OpenRISCCPU *cpu, if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { qemu_log("\n"); log_target_disas(&cpu->env, pc_start, dc->pc - pc_start, 0); - qemu_log("\nisize=%d osize=%td\n", - dc->pc - pc_start, tcg_ctx.gen_opc_ptr - - tcg_ctx.gen_opc_buf); + qemu_log("\nisize=%d osize=%d\n", + dc->pc - pc_start, tcg_op_buf_count()); } #endif } diff --git a/target-ppc/cpu-models.c b/target-ppc/cpu-models.c index 3f18996..4d5ab4b 100644 --- a/target-ppc/cpu-models.c +++ b/target-ppc/cpu-models.c @@ -1124,8 +1124,6 @@ POWERPC_DEF("POWER5", CPU_POWERPC_POWER5, POWER5, "POWER5") #endif - POWERPC_DEF("POWER5+", CPU_POWERPC_POWER5P, POWER5P, - "POWER5+") POWERPC_DEF("POWER5+_v2.1", CPU_POWERPC_POWER5P_v21, POWER5P, "POWER5+ v2.1") #if defined(TODO) @@ -1144,8 +1142,8 @@ "POWER8E v1.0") POWERPC_DEF("POWER8_v1.0", CPU_POWERPC_POWER8_v10, POWER8, "POWER8 v1.0") - POWERPC_DEF("970", CPU_POWERPC_970, 970, - "PowerPC 970") + POWERPC_DEF("970_v2.2", CPU_POWERPC_970_v22, 970, + "PowerPC 970 v2.2") POWERPC_DEF("970fx_v1.0", CPU_POWERPC_970FX_v10, 970, "PowerPC 970FX v1.0 (G5)") POWERPC_DEF("970fx_v2.0", CPU_POWERPC_970FX_v20, 970, @@ -1387,11 +1385,13 @@ PowerPCCPUAlias ppc_cpu_aliases[] = { { "Dino", "POWER3" }, { "POWER3+", "631" }, { "POWER5gr", "POWER5" }, - { "POWER5gs", "POWER5+" }, + { "POWER5+", "POWER5+_v2.1" }, + { "POWER5gs", "POWER5+_v2.1" }, { "POWER7", "POWER7_v2.3" }, { "POWER7+", "POWER7+_v2.1" }, { "POWER8E", "POWER8E_v1.0" }, { "POWER8", "POWER8_v1.0" }, + { "970", "970_v2.2" }, { "970fx", "970fx_v3.1" }, { "970mp", "970mp_v1.1" }, { "Apache", "RS64" }, diff --git a/target-ppc/cpu-models.h b/target-ppc/cpu-models.h index 290a759..9d80e72 100644 --- a/target-ppc/cpu-models.h +++ b/target-ppc/cpu-models.h @@ -547,7 +547,6 @@ enum { CPU_POWERPC_POWER4P = 0x00380000, /* XXX: missing 0x003A0201 */ CPU_POWERPC_POWER5 = 0x003A0203, - CPU_POWERPC_POWER5P = 0x003B0000, CPU_POWERPC_POWER5P_v21 = 0x003B0201, CPU_POWERPC_POWER6 = 0x003E0000, CPU_POWERPC_POWER6_5 = 0x0F000001, /* POWER6 in POWER5 mode */ @@ -561,7 +560,7 @@ enum { CPU_POWERPC_POWER8E_v10 = 0x004B0100, CPU_POWERPC_POWER8_BASE = 0x004D0000, CPU_POWERPC_POWER8_v10 = 0x004D0100, - CPU_POWERPC_970 = 0x00390202, + CPU_POWERPC_970_v22 = 0x00390202, CPU_POWERPC_970FX_v10 = 0x00391100, CPU_POWERPC_970FX_v20 = 0x003C0200, CPU_POWERPC_970FX_v21 = 0x003C0201, diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 068fcb2..f15815f 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -45,6 +45,7 @@ # define TARGET_VIRT_ADDR_SPACE_BITS 64 #endif +#define TARGET_PAGE_BITS_64K 16 #define TARGET_PAGE_BITS_16M 24 #else /* defined (TARGET_PPC64) */ @@ -79,8 +80,6 @@ #include "fpu/softfloat.h" -#define TARGET_HAS_ICE 1 - #if defined (TARGET_PPC64) #define ELF_MACHINE EM_PPC64 #else @@ -320,6 +319,7 @@ typedef struct opc_handler_t opc_handler_t; /*****************************************************************************/ /* Types used to describe some PowerPC registers */ typedef struct CPUPPCState CPUPPCState; +typedef struct DisasContext DisasContext; typedef struct ppc_tb_t ppc_tb_t; typedef struct ppc_spr_t ppc_spr_t; typedef struct ppc_dcr_t ppc_dcr_t; @@ -328,13 +328,13 @@ typedef union ppc_tlb_t ppc_tlb_t; /* SPR access micro-ops generations callbacks */ struct ppc_spr_t { - void (*uea_read)(void *opaque, int gpr_num, int spr_num); - void (*uea_write)(void *opaque, int spr_num, int gpr_num); + void (*uea_read)(DisasContext *ctx, int gpr_num, int spr_num); + void (*uea_write)(DisasContext *ctx, int spr_num, int gpr_num); #if !defined(CONFIG_USER_ONLY) - void (*oea_read)(void *opaque, int gpr_num, int spr_num); - void (*oea_write)(void *opaque, int spr_num, int gpr_num); - void (*hea_read)(void *opaque, int gpr_num, int spr_num); - void (*hea_write)(void *opaque, int spr_num, int gpr_num); + void (*oea_read)(DisasContext *ctx, int gpr_num, int spr_num); + void (*oea_write)(DisasContext *ctx, int spr_num, int gpr_num); + void (*hea_read)(DisasContext *ctx, int gpr_num, int spr_num); + void (*hea_write)(DisasContext *ctx, int spr_num, int gpr_num); #endif const char *name; target_ulong default_value; @@ -558,6 +558,26 @@ struct ppc_slb_t { #define ESR_VLEMI (1 << (63 - 58)) /* VLE operation */ #define ESR_MIF (1 << (63 - 62)) /* Misaligned instruction (VLE) */ +/* Transaction EXception And Summary Register bits */ +#define TEXASR_FAILURE_PERSISTENT (63 - 7) +#define TEXASR_DISALLOWED (63 - 8) +#define TEXASR_NESTING_OVERFLOW (63 - 9) +#define TEXASR_FOOTPRINT_OVERFLOW (63 - 10) +#define TEXASR_SELF_INDUCED_CONFLICT (63 - 11) +#define TEXASR_NON_TRANSACTIONAL_CONFLICT (63 - 12) +#define TEXASR_TRANSACTION_CONFLICT (63 - 13) +#define TEXASR_TRANSLATION_INVALIDATION_CONFLICT (63 - 14) +#define TEXASR_IMPLEMENTATION_SPECIFIC (63 - 15) +#define TEXASR_INSTRUCTION_FETCH_CONFLICT (63 - 16) +#define TEXASR_ABORT (63 - 31) +#define TEXASR_SUSPENDED (63 - 32) +#define TEXASR_PRIVILEGE_HV (63 - 34) +#define TEXASR_PRIVILEGE_PR (63 - 35) +#define TEXASR_FAILURE_SUMMARY (63 - 36) +#define TEXASR_TFIAR_EXACT (63 - 37) +#define TEXASR_ROT (63 - 38) +#define TEXASR_TRANSACTION_LEVEL (63 - 52) /* 12 bits */ + enum { POWERPC_FLAG_NONE = 0x00000000, /* Flag for MSR bit 25 signification (VRE/SPE) */ @@ -584,6 +604,8 @@ enum { POWERPC_FLAG_CFAR = 0x00040000, /* Has VSX */ POWERPC_FLAG_VSX = 0x00080000, + /* Has Transaction Memory (ISA 2.07) */ + POWERPC_FLAG_TM = 0x00100000, }; /*****************************************************************************/ @@ -1216,14 +1238,7 @@ static inline uint64_t ppc_dump_gpr(CPUPPCState *env, int gprn) int ppc_dcr_read (ppc_dcr_t *dcr_env, int dcrn, uint32_t *valp); int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val); -static inline CPUPPCState *cpu_init(const char *cpu_model) -{ - PowerPCCPU *cpu = cpu_ppc_init(cpu_model); - if (cpu == NULL) { - return NULL; - } - return &cpu->env; -} +#define cpu_init(cpu_model) CPU(cpu_ppc_init(cpu_model)) #define cpu_exec cpu_ppc_exec #define cpu_gen_code cpu_ppc_gen_code @@ -1602,6 +1617,7 @@ static inline int cpu_mmu_index (CPUPPCState *env) #define SPR_MPC_MD_DBRAM1 (0x32A) #define SPR_RCPU_L2U_RA3 (0x32B) #define SPR_TAR (0x32F) +#define SPR_VTB (0x351) #define SPR_440_INV0 (0x370) #define SPR_440_INV1 (0x371) #define SPR_440_INV2 (0x372) @@ -2010,6 +2026,8 @@ enum { PPC2_ISA207S = 0x0000000000008000ULL, /* Double precision floating point conversion for signed integer 64 */ PPC2_FP_CVT_S64 = 0x0000000000010000ULL, + /* Transactional Memory (ISA 2.07, Book II) */ + PPC2_TM = 0x0000000000020000ULL, #define PPC_TCG_INSNS2 (PPC2_BOOKE206 | PPC2_VSX | PPC2_PRCNTL | PPC2_DBRX | \ PPC2_ISA205 | PPC2_VSX207 | PPC2_PERM_ISA206 | \ @@ -2017,7 +2035,7 @@ enum { PPC2_FP_CVT_ISA206 | PPC2_FP_TST_ISA206 | \ PPC2_BCTAR_ISA207 | PPC2_LSQ_ISA207 | \ PPC2_ALTIVEC_207 | PPC2_ISA207S | PPC2_DFP | \ - PPC2_FP_CVT_S64) + PPC2_FP_CVT_S64 | PPC2_TM) }; /*****************************************************************************/ diff --git a/target-ppc/fpu_helper.c b/target-ppc/fpu_helper.c index 7f74466..6cceffc 100644 --- a/target-ppc/fpu_helper.c +++ b/target-ppc/fpu_helper.c @@ -19,6 +19,9 @@ #include "cpu.h" #include "exec/helper-proto.h" +#define float64_snan_to_qnan(x) ((x) | 0x0008000000000000ULL) +#define float32_snan_to_qnan(x) ((x) | 0x00400000) + /*****************************************************************************/ /* Floating point operations helpers */ uint64_t helper_float32_to_float64(CPUPPCState *env, uint32_t arg) @@ -60,59 +63,55 @@ static inline int ppc_float64_get_unbiased_exp(float64 f) return ((f >> 52) & 0x7FF) - 1023; } -uint32_t helper_compute_fprf(CPUPPCState *env, uint64_t arg, uint32_t set_fprf) +void helper_compute_fprf(CPUPPCState *env, uint64_t arg) { CPU_DoubleU farg; int isneg; - int ret; + int fprf; farg.ll = arg; isneg = float64_is_neg(farg.d); if (unlikely(float64_is_any_nan(farg.d))) { if (float64_is_signaling_nan(farg.d)) { /* Signaling NaN: flags are undefined */ - ret = 0x00; + fprf = 0x00; } else { /* Quiet NaN */ - ret = 0x11; + fprf = 0x11; } } else if (unlikely(float64_is_infinity(farg.d))) { /* +/- infinity */ if (isneg) { - ret = 0x09; + fprf = 0x09; } else { - ret = 0x05; + fprf = 0x05; } } else { if (float64_is_zero(farg.d)) { /* +/- zero */ if (isneg) { - ret = 0x12; + fprf = 0x12; } else { - ret = 0x02; + fprf = 0x02; } } else { if (isden(farg.d)) { /* Denormalized numbers */ - ret = 0x10; + fprf = 0x10; } else { /* Normalized numbers */ - ret = 0x00; + fprf = 0x00; } if (isneg) { - ret |= 0x08; + fprf |= 0x08; } else { - ret |= 0x04; + fprf |= 0x04; } } } - if (set_fprf) { - /* We update FPSCR_FPRF */ - env->fpscr &= ~(0x1F << FPSCR_FPRF); - env->fpscr |= ret << FPSCR_FPRF; - } - /* We just need fpcc to update Rc1 */ - return ret & 0xF; + /* We update FPSCR_FPRF */ + env->fpscr &= ~(0x1F << FPSCR_FPRF); + env->fpscr |= fprf << FPSCR_FPRF; } /* Floating-point invalid operations exception */ @@ -920,14 +919,16 @@ uint64_t helper_fsqrt(CPUPPCState *env, uint64_t arg) farg.ll = arg; - if (unlikely(float64_is_neg(farg.d) && !float64_is_zero(farg.d))) { - /* Square root of a negative nonzero number */ - farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT, 1); - } else { + if (unlikely(float64_is_any_nan(farg.d))) { if (unlikely(float64_is_signaling_nan(farg.d))) { - /* sNaN square root */ + /* sNaN reciprocal square root */ fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + farg.ll = float64_snan_to_qnan(farg.ll); } + } else if (unlikely(float64_is_neg(farg.d) && !float64_is_zero(farg.d))) { + /* Square root of a negative nonzero number */ + farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT, 1); + } else { farg.d = float64_sqrt(farg.d, &env->fp_status); } return farg.ll; @@ -974,17 +975,20 @@ uint64_t helper_frsqrte(CPUPPCState *env, uint64_t arg) farg.ll = arg; - if (unlikely(float64_is_neg(farg.d) && !float64_is_zero(farg.d))) { - /* Reciprocal square root of a negative nonzero number */ - farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT, 1); - } else { + if (unlikely(float64_is_any_nan(farg.d))) { if (unlikely(float64_is_signaling_nan(farg.d))) { /* sNaN reciprocal square root */ fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + farg.ll = float64_snan_to_qnan(farg.ll); } + } else if (unlikely(float64_is_neg(farg.d) && !float64_is_zero(farg.d))) { + /* Reciprocal square root of a negative nonzero number */ + farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT, 1); + } else { farg.d = float64_sqrt(farg.d, &env->fp_status); farg.d = float64_div(float64_one, farg.d, &env->fp_status); } + return farg.ll; } @@ -1845,7 +1849,7 @@ void helper_##name(CPUPPCState *env, uint32_t opcode) \ } \ \ if (sfprf) { \ - helper_compute_fprf(env, xt.fld, sfprf); \ + helper_compute_fprf(env, xt.fld); \ } \ } \ putVSR(xT(opcode), &xt, env); \ @@ -1900,7 +1904,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ } \ \ if (sfprf) { \ - helper_compute_fprf(env, xt.fld, sfprf); \ + helper_compute_fprf(env, xt.fld); \ } \ } \ \ @@ -1954,7 +1958,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ } \ \ if (sfprf) { \ - helper_compute_fprf(env, xt.fld, sfprf); \ + helper_compute_fprf(env, xt.fld); \ } \ } \ \ @@ -1995,7 +1999,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ } \ \ if (sfprf) { \ - helper_compute_fprf(env, xt.fld, sfprf); \ + helper_compute_fprf(env, xt.fld); \ } \ } \ \ @@ -2044,7 +2048,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ } \ \ if (sfprf) { \ - helper_compute_fprf(env, xt.fld, sfprf); \ + helper_compute_fprf(env, xt.fld); \ } \ } \ \ @@ -2094,7 +2098,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ } \ \ if (sfprf) { \ - helper_compute_fprf(env, xt.fld, sfprf); \ + helper_compute_fprf(env, xt.fld); \ } \ } \ \ @@ -2294,7 +2298,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ } \ \ if (sfprf) { \ - helper_compute_fprf(env, xt_out.fld, sfprf); \ + helper_compute_fprf(env, xt_out.fld); \ } \ } \ putVSR(xT(opcode), &xt_out, env); \ @@ -2382,9 +2386,6 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ VSX_SCALAR_CMP(xscmpodp, 1) VSX_SCALAR_CMP(xscmpudp, 0) -#define float64_snan_to_qnan(x) ((x) | 0x0008000000000000ULL) -#define float32_snan_to_qnan(x) ((x) | 0x00400000) - /* VSX_MAX_MIN - VSX floating point maximum/minimum * name - instruction mnemonic * op - operation (max or min) @@ -2504,7 +2505,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ } \ if (sfprf) { \ helper_compute_fprf(env, ttp##_to_float64(xt.tfld, \ - &env->fp_status), sfprf); \ + &env->fp_status)); \ } \ } \ \ @@ -2614,7 +2615,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ xt.tfld = helper_frsp(env, xt.tfld); \ } \ if (sfprf) { \ - helper_compute_fprf(env, xt.tfld, sfprf); \ + helper_compute_fprf(env, xt.tfld); \ } \ } \ \ @@ -2669,7 +2670,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ xt.fld = tp##_round_to_int(xb.fld, &env->fp_status); \ } \ if (sfprf) { \ - helper_compute_fprf(env, xt.fld, sfprf); \ + helper_compute_fprf(env, xt.fld); \ } \ } \ \ @@ -2709,7 +2710,7 @@ uint64_t helper_xsrsp(CPUPPCState *env, uint64_t xb) uint64_t xt = helper_frsp(env, xb); - helper_compute_fprf(env, xt, 1); + helper_compute_fprf(env, xt); helper_float_check_status(env); return xt; } diff --git a/target-ppc/helper.h b/target-ppc/helper.h index 210fd97..869be15 100644 --- a/target-ppc/helper.h +++ b/target-ppc/helper.h @@ -52,7 +52,7 @@ DEF_HELPER_FLAGS_2(brinc, TCG_CALL_NO_RWG_SE, tl, tl, tl) DEF_HELPER_1(float_check_status, void, env) DEF_HELPER_1(reset_fpstatus, void, env) -DEF_HELPER_3(compute_fprf, i32, env, i64, i32) +DEF_HELPER_2(compute_fprf, void, env, i64) DEF_HELPER_3(store_fpscr, void, env, i64, i32) DEF_HELPER_2(fpscr_clrbit, void, env, i32) DEF_HELPER_2(fpscr_setbit, void, env, i32) @@ -665,3 +665,5 @@ DEF_HELPER_4(dscri, void, env, fprp, fprp, i32) DEF_HELPER_4(dscriq, void, env, fprp, fprp, i32) DEF_HELPER_4(dscli, void, env, fprp, fprp, i32) DEF_HELPER_4(dscliq, void, env, fprp, fprp, i32) + +DEF_HELPER_1(tbegin, void, env) diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c index 6843fa0..84ae447 100644 --- a/target-ppc/kvm.c +++ b/target-ppc/kvm.c @@ -39,6 +39,7 @@ #include "sysemu/watchdog.h" #include "trace.h" #include "exec/gdbstub.h" +#include "sysemu/hostmem.h" //#define DEBUG_KVM @@ -95,7 +96,7 @@ static void kvm_kick_cpu(void *opaque) static int kvm_ppc_register_host_cpu_type(void); -int kvm_arch_init(KVMState *s) +int kvm_arch_init(MachineState *ms, KVMState *s) { cap_interrupt_unset = kvm_check_extension(s, KVM_CAP_PPC_UNSET_IRQ); cap_interrupt_level = kvm_check_extension(s, KVM_CAP_PPC_IRQ_LEVEL); @@ -302,16 +303,11 @@ static void kvm_get_smmu_info(PowerPCCPU *cpu, struct kvm_ppc_smmu_info *info) kvm_get_fallback_smmu_info(cpu, info); } -static long getrampagesize(void) +static long gethugepagesize(const char *mem_path) { struct statfs fs; int ret; - if (!mem_path) { - /* guest RAM is backed by normal anonymous pages */ - return getpagesize(); - } - do { ret = statfs(mem_path, &fs); } while (ret != 0 && errno == EINTR); @@ -333,6 +329,55 @@ static long getrampagesize(void) return fs.f_bsize; } +static int find_max_supported_pagesize(Object *obj, void *opaque) +{ + char *mem_path; + long *hpsize_min = opaque; + + if (object_dynamic_cast(obj, TYPE_MEMORY_BACKEND)) { + mem_path = object_property_get_str(obj, "mem-path", NULL); + if (mem_path) { + long hpsize = gethugepagesize(mem_path); + if (hpsize < *hpsize_min) { + *hpsize_min = hpsize; + } + } else { + *hpsize_min = getpagesize(); + } + } + + return 0; +} + +static long getrampagesize(void) +{ + long hpsize = LONG_MAX; + Object *memdev_root; + + if (mem_path) { + return gethugepagesize(mem_path); + } + + /* it's possible we have memory-backend objects with + * hugepage-backed RAM. these may get mapped into system + * address space via -numa parameters or memory hotplug + * hooks. we want to take these into account, but we + * also want to make sure these supported hugepage + * sizes are applicable across the entire range of memory + * we may boot from, so we take the min across all + * backends, and assume normal pages in cases where a + * backend isn't backed by hugepages. + */ + memdev_root = object_resolve_path("/objects", NULL); + if (!memdev_root) { + return getpagesize(); + } + + object_child_foreach(memdev_root, find_max_supported_pagesize, &hpsize); + + return (hpsize == LONG_MAX) ? getpagesize() : hpsize; +} + static bool kvm_valid_page_size(uint32_t flags, long rampgsize, uint32_t shift) { if (!(flags & KVM_PPC_PAGE_SIZES_REAL)) { @@ -2246,8 +2291,23 @@ int kvmppc_save_htab(QEMUFile *f, int fd, size_t bufsize, int64_t max_ns) strerror(errno)); return rc; } else if (rc) { - /* Kernel already retuns data in BE format for the file */ - qemu_put_buffer(f, buf, rc); + uint8_t *buffer = buf; + ssize_t n = rc; + while (n) { + struct kvm_get_htab_header *head = + (struct kvm_get_htab_header *) buffer; + size_t chunksize = sizeof(*head) + + HASH_PTE_SIZE_64 * head->n_valid; + + qemu_put_be32(f, head->index); + qemu_put_be16(f, head->n_valid); + qemu_put_be16(f, head->n_invalid); + qemu_put_buffer(f, (void *)(head + 1), + HASH_PTE_SIZE_64 * head->n_valid); + + buffer += chunksize; + n -= chunksize; + } } } while ((rc != 0) && ((max_ns < 0) @@ -2264,7 +2324,6 @@ int kvmppc_load_htab_chunk(QEMUFile *f, int fd, uint32_t index, ssize_t rc; buf = alloca(chunksize); - /* This is KVM on ppc, so this is all big-endian */ buf->index = index; buf->n_valid = n_valid; buf->n_invalid = n_invalid; @@ -2388,3 +2447,9 @@ out_close: error_out: return; } + +int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, + uint64_t address, uint32_t data) +{ + return 0; +} diff --git a/target-ppc/machine.c b/target-ppc/machine.c index c801b82..d875211 100644 --- a/target-ppc/machine.c +++ b/target-ppc/machine.c @@ -159,6 +159,7 @@ static int cpu_post_load(void *opaque, int version_id) PowerPCCPU *cpu = opaque; CPUPPCState *env = &cpu->env; int i; + target_ulong msr; /* * We always ignore the source PVR. The user or management @@ -190,7 +191,12 @@ static int cpu_post_load(void *opaque, int version_id) /* Restore htab_base and htab_mask variables */ ppc_store_sdr1(env, env->spr[SPR_SDR1]); } - hreg_compute_hflags(env); + + /* Invalidate all msr bits except MSR_TGPR/MSR_HVB before restoring */ + msr = env->msr; + env->msr ^= ~((1ULL << MSR_TGPR) | MSR_HVB); + ppc_store_msr(env, msr); + hreg_compute_mem_idx(env); return 0; diff --git a/target-ppc/mem_helper.c b/target-ppc/mem_helper.c index 50344b8..6d37dae 100644 --- a/target-ppc/mem_helper.c +++ b/target-ppc/mem_helper.c @@ -269,3 +269,25 @@ STVE(stvewx, cpu_stl_data, bswap32, u32) #undef HI_IDX #undef LO_IDX + +void helper_tbegin(CPUPPCState *env) +{ + /* As a degenerate implementation, always fail tbegin. The reason + * given is "Nesting overflow". The "persistent" bit is set, + * providing a hint to the error handler to not retry. The TFIAR + * captures the address of the failure, which is this tbegin + * instruction. Instruction execution will continue with the + * next instruction in memory, which is precisely what we want. + */ + + env->spr[SPR_TEXASR] = + (1ULL << TEXASR_FAILURE_PERSISTENT) | + (1ULL << TEXASR_NESTING_OVERFLOW) | + (msr_hv << TEXASR_PRIVILEGE_HV) | + (msr_pr << TEXASR_PRIVILEGE_PR) | + (1ULL << TEXASR_FAILURE_SUMMARY) | + (1ULL << TEXASR_TFIAR_EXACT); + env->spr[SPR_TFIAR] = env->nip | (msr_hv << 1) | msr_pr; + env->spr[SPR_TFHAR] = env->nip + 4; + env->crf[0] = 0xB; /* 0b1010 = transaction failure */ +} diff --git a/target-ppc/misc_helper.c b/target-ppc/misc_helper.c index a577b3a..6b12ca8 100644 --- a/target-ppc/misc_helper.c +++ b/target-ppc/misc_helper.c @@ -77,8 +77,13 @@ void helper_msr_facility_check(CPUPPCState *env, uint32_t bit, void helper_store_sdr1(CPUPPCState *env, target_ulong val) { + PowerPCCPU *cpu = ppc_env_get_cpu(env); + if (!env->external_htab) { - ppc_store_sdr1(env, val); + if (env->spr[SPR_SDR1] != val) { + ppc_store_sdr1(env, val); + tlb_flush(CPU(cpu), 1); + } } } diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index 0a13a81..dfee358 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -28,10 +28,8 @@ //#define DEBUG_BAT #ifdef DEBUG_MMU -# define LOG_MMU(...) qemu_log(__VA_ARGS__) # define LOG_MMU_STATE(cpu) log_cpu_state((cpu), 0) #else -# define LOG_MMU(...) do { } while (0) # define LOG_MMU_STATE(cpu) do { } while (0) #endif @@ -225,7 +223,7 @@ static int ppc_hash32_direct_store(CPUPPCState *env, target_ulong sr, CPUState *cs = CPU(ppc_env_get_cpu(env)); int key = !!(msr_pr ? (sr & SR32_KP) : (sr & SR32_KS)); - LOG_MMU("direct store...\n"); + qemu_log_mask(CPU_LOG_MMU, "direct store...\n"); if ((sr & 0x1FF00000) >> 20 == 0x07f) { /* Memory-forced I/O controller interface access */ @@ -348,12 +346,13 @@ static hwaddr ppc_hash32_htab_lookup(CPUPPCState *env, ptem = (vsid << 7) | (pgidx >> 10); /* Page address translation */ - LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx + qemu_log_mask(CPU_LOG_MMU, "htab_base " TARGET_FMT_plx + " htab_mask " TARGET_FMT_plx " hash " TARGET_FMT_plx "\n", env->htab_base, env->htab_mask, hash); /* Primary PTEG lookup */ - LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx + qemu_log_mask(CPU_LOG_MMU, "0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx " vsid=%" PRIx32 " ptem=%" PRIx32 " hash=" TARGET_FMT_plx "\n", env->htab_base, env->htab_mask, vsid, ptem, hash); @@ -361,7 +360,7 @@ static hwaddr ppc_hash32_htab_lookup(CPUPPCState *env, pte_offset = ppc_hash32_pteg_search(env, pteg_off, 0, ptem, pte); if (pte_offset == -1) { /* Secondary PTEG lookup */ - LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx + qemu_log_mask(CPU_LOG_MMU, "1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx " vsid=%" PRIx32 " api=%" PRIx32 " hash=" TARGET_FMT_plx "\n", env->htab_base, env->htab_mask, vsid, ptem, ~hash); @@ -476,7 +475,8 @@ int ppc_hash32_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, int rwx, return 1; } - LOG_MMU("found PTE at offset %08" HWADDR_PRIx "\n", pte_offset); + qemu_log_mask(CPU_LOG_MMU, + "found PTE at offset %08" HWADDR_PRIx "\n", pte_offset); /* 7. Check access permissions */ @@ -484,7 +484,7 @@ int ppc_hash32_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, int rwx, if (need_prot[rwx] & ~prot) { /* Access right violation */ - LOG_MMU("PTE access rejected\n"); + qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); if (rwx == 2) { cs->exception_index = POWERPC_EXCP_ISI; env->error_code = 0x08000000; @@ -501,7 +501,7 @@ int ppc_hash32_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, int rwx, return 1; } - LOG_MMU("PTE access granted !\n"); + qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n"); /* 8. Update PTE referenced and changed bits if necessary */ diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index c72198a..7df6ede 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -27,10 +27,8 @@ //#define DEBUG_SLB #ifdef DEBUG_MMU -# define LOG_MMU(...) qemu_log(__VA_ARGS__) # define LOG_MMU_STATE(cpu) log_cpu_state((cpu), 0) #else -# define LOG_MMU(...) do { } while (0) # define LOG_MMU_STATE(cpu) do { } while (0) #endif @@ -352,7 +350,7 @@ uint64_t ppc_hash64_start_access(PowerPCCPU *cpu, target_ulong pte_index) void ppc_hash64_stop_access(uint64_t token) { if (kvmppc_kern_htab) { - return kvmppc_hash64_free_pteg(token); + kvmppc_hash64_free_pteg(token); } } @@ -390,6 +388,24 @@ static hwaddr ppc_hash64_pteg_search(CPUPPCState *env, hwaddr hash, return -1; } +static uint64_t ppc_hash64_page_shift(ppc_slb_t *slb) +{ + uint64_t epnshift; + + /* Page size according to the SLB, which we use to generate the + * EPN for hash table lookup.. When we implement more recent MMU + * extensions this might be different from the actual page size + * encoded in the PTE */ + if ((slb->vsid & SLB_VSID_LLP_MASK) == SLB_VSID_4K) { + epnshift = TARGET_PAGE_BITS; + } else if ((slb->vsid & SLB_VSID_LLP_MASK) == SLB_VSID_64K) { + epnshift = TARGET_PAGE_BITS_64K; + } else { + epnshift = TARGET_PAGE_BITS_16M; + } + return epnshift; +} + static hwaddr ppc_hash64_htab_lookup(CPUPPCState *env, ppc_slb_t *slb, target_ulong eaddr, ppc_hash_pte64_t *pte) @@ -398,12 +414,7 @@ static hwaddr ppc_hash64_htab_lookup(CPUPPCState *env, hwaddr hash; uint64_t vsid, epnshift, epnmask, epn, ptem; - /* Page size according to the SLB, which we use to generate the - * EPN for hash table lookup.. When we implement more recent MMU - * extensions this might be different from the actual page size - * encoded in the PTE */ - epnshift = (slb->vsid & SLB_VSID_L) - ? TARGET_PAGE_BITS_16M : TARGET_PAGE_BITS; + epnshift = ppc_hash64_page_shift(slb); epnmask = ~((1ULL << epnshift) - 1); if (slb->vsid & SLB_VSID_B) { @@ -420,12 +431,14 @@ static hwaddr ppc_hash64_htab_lookup(CPUPPCState *env, ptem = (slb->vsid & SLB_VSID_PTEM) | ((epn >> 16) & HPTE64_V_AVPN); /* Page address translation */ - LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx + qemu_log_mask(CPU_LOG_MMU, + "htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx " hash " TARGET_FMT_plx "\n", env->htab_base, env->htab_mask, hash); /* Primary PTEG lookup */ - LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx + qemu_log_mask(CPU_LOG_MMU, + "0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx " hash=" TARGET_FMT_plx "\n", env->htab_base, env->htab_mask, vsid, ptem, hash); @@ -433,7 +446,8 @@ static hwaddr ppc_hash64_htab_lookup(CPUPPCState *env, if (pte_offset == -1) { /* Secondary PTEG lookup */ - LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx + qemu_log_mask(CPU_LOG_MMU, + "1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx " hash=" TARGET_FMT_plx "\n", env->htab_base, env->htab_mask, vsid, ptem, ~hash); @@ -447,12 +461,14 @@ static hwaddr ppc_hash64_htab_lookup(CPUPPCState *env, static hwaddr ppc_hash64_pte_raddr(ppc_slb_t *slb, ppc_hash_pte64_t pte, target_ulong eaddr) { + hwaddr mask; + int target_page_bits; hwaddr rpn = pte.pte1 & HPTE64_R_RPN; - /* FIXME: Add support for SLLP extended page sizes */ - int target_page_bits = (slb->vsid & SLB_VSID_L) - ? TARGET_PAGE_BITS_16M : TARGET_PAGE_BITS; - hwaddr mask = (1ULL << target_page_bits) - 1; - + /* + * We support 4K, 64K and 16M now + */ + target_page_bits = ppc_hash64_page_shift(slb); + mask = (1ULL << target_page_bits) - 1; return (rpn & ~mask) | (eaddr & mask); } @@ -522,7 +538,8 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, } return 1; } - LOG_MMU("found PTE at offset %08" HWADDR_PRIx "\n", pte_offset); + qemu_log_mask(CPU_LOG_MMU, + "found PTE at offset %08" HWADDR_PRIx "\n", pte_offset); /* 5. Check access permissions */ @@ -532,7 +549,7 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, if ((need_prot[rwx] & ~prot) != 0) { /* Access right violation */ - LOG_MMU("PTE access rejected\n"); + qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); if (rwx == 2) { cs->exception_index = POWERPC_EXCP_ISI; env->error_code = 0x08000000; @@ -556,7 +573,7 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, return 1; } - LOG_MMU("PTE access granted !\n"); + qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n"); /* 6. Update PTE referenced and changed bits if necessary */ @@ -615,7 +632,8 @@ void ppc_hash64_store_hpte(CPUPPCState *env, CPUState *cs = CPU(ppc_env_get_cpu(env)); if (kvmppc_kern_htab) { - return kvmppc_hash64_write_pte(env, pte_index, pte0, pte1); + kvmppc_hash64_write_pte(env, pte_index, pte0, pte1); + return; } pte_index *= HASH_PTE_SIZE_64; diff --git a/target-ppc/mmu-hash64.h b/target-ppc/mmu-hash64.h index 49e385d..291750f 100644 --- a/target-ppc/mmu-hash64.h +++ b/target-ppc/mmu-hash64.h @@ -37,6 +37,9 @@ void ppc_hash64_store_hpte(CPUPPCState *env, target_ulong index, #define SLB_VSID_C 0x0000000000000080ULL /* class */ #define SLB_VSID_LP 0x0000000000000030ULL #define SLB_VSID_ATTR 0x0000000000000FFFULL +#define SLB_VSID_LLP_MASK (SLB_VSID_L | SLB_VSID_LP) +#define SLB_VSID_4K 0x0000000000000000ULL +#define SLB_VSID_64K 0x0000000000000110ULL /* * Hash page table definitions diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c index 4a34a73..527c6ad 100644 --- a/target-ppc/mmu_helper.c +++ b/target-ppc/mmu_helper.c @@ -32,10 +32,8 @@ //#define FLUSH_ALL_TLBS #ifdef DEBUG_MMU -# define LOG_MMU(...) qemu_log(__VA_ARGS__) # define LOG_MMU_STATE(cpu) log_cpu_state((cpu), 0) #else -# define LOG_MMU(...) do { } while (0) # define LOG_MMU_STATE(cpu) do { } while (0) #endif @@ -176,10 +174,10 @@ static inline int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, ret = check_prot(ctx->prot, rw, type); if (ret == 0) { /* Access granted */ - LOG_MMU("PTE access granted !\n"); + qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n"); } else { /* Access right violation */ - LOG_MMU("PTE access rejected\n"); + qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); } } } @@ -480,8 +478,9 @@ static inline int get_segment_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, ctx->nx = sr & 0x10000000 ? 1 : 0; vsid = sr & 0x00FFFFFF; target_page_bits = TARGET_PAGE_BITS; - LOG_MMU("Check segment v=" TARGET_FMT_lx " %d " TARGET_FMT_lx " nip=" - TARGET_FMT_lx " lr=" TARGET_FMT_lx + qemu_log_mask(CPU_LOG_MMU, + "Check segment v=" TARGET_FMT_lx " %d " TARGET_FMT_lx + " nip=" TARGET_FMT_lx " lr=" TARGET_FMT_lx " ir=%d dr=%d pr=%d %d t=%d\n", eaddr, (int)(eaddr >> 28), sr, env->nip, env->lr, (int)msr_ir, (int)msr_dr, pr != 0 ? 1 : 0, rw, type); @@ -489,14 +488,16 @@ static inline int get_segment_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, hash = vsid ^ pgidx; ctx->ptem = (vsid << 7) | (pgidx >> 10); - LOG_MMU("pte segment: key=%d ds %d nx %d vsid " TARGET_FMT_lx "\n", + qemu_log_mask(CPU_LOG_MMU, + "pte segment: key=%d ds %d nx %d vsid " TARGET_FMT_lx "\n", ctx->key, ds, ctx->nx, vsid); ret = -1; if (!ds) { /* Check if instruction fetch is allowed, if needed */ if (type != ACCESS_CODE || ctx->nx == 0) { /* Page address translation */ - LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx + qemu_log_mask(CPU_LOG_MMU, "htab_base " TARGET_FMT_plx + " htab_mask " TARGET_FMT_plx " hash " TARGET_FMT_plx "\n", env->htab_base, env->htab_mask, hash); ctx->hash[0] = hash; @@ -527,13 +528,13 @@ static inline int get_segment_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, } #endif } else { - LOG_MMU("No access allowed\n"); + qemu_log_mask(CPU_LOG_MMU, "No access allowed\n"); ret = -3; } } else { target_ulong sr; - LOG_MMU("direct store...\n"); + qemu_log_mask(CPU_LOG_MMU, "direct store...\n"); /* Direct-store segment : absolutely *BUGGY* for now */ /* Direct-store implies a 32-bit MMU. @@ -2035,31 +2036,26 @@ void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr) /* Special registers manipulation */ void ppc_store_sdr1(CPUPPCState *env, target_ulong value) { - PowerPCCPU *cpu = ppc_env_get_cpu(env); - - LOG_MMU("%s: " TARGET_FMT_lx "\n", __func__, value); + qemu_log_mask(CPU_LOG_MMU, "%s: " TARGET_FMT_lx "\n", __func__, value); assert(!env->external_htab); - if (env->spr[SPR_SDR1] != value) { - env->spr[SPR_SDR1] = value; + env->spr[SPR_SDR1] = value; #if defined(TARGET_PPC64) - if (env->mmu_model & POWERPC_MMU_64) { - target_ulong htabsize = value & SDR_64_HTABSIZE; + if (env->mmu_model & POWERPC_MMU_64) { + target_ulong htabsize = value & SDR_64_HTABSIZE; - if (htabsize > 28) { - fprintf(stderr, "Invalid HTABSIZE 0x" TARGET_FMT_lx - " stored in SDR1\n", htabsize); - htabsize = 28; - } - env->htab_mask = (1ULL << (htabsize + 18 - 7)) - 1; - env->htab_base = value & SDR_64_HTABORG; - } else -#endif /* defined(TARGET_PPC64) */ - { - /* FIXME: Should check for valid HTABMASK values */ - env->htab_mask = ((value & SDR_32_HTABMASK) << 16) | 0xFFFF; - env->htab_base = value & SDR_32_HTABORG; + if (htabsize > 28) { + fprintf(stderr, "Invalid HTABSIZE 0x" TARGET_FMT_lx + " stored in SDR1\n", htabsize); + htabsize = 28; } - tlb_flush(CPU(cpu), 1); + env->htab_mask = (1ULL << (htabsize + 18 - 7)) - 1; + env->htab_base = value & SDR_64_HTABORG; + } else +#endif /* defined(TARGET_PPC64) */ + { + /* FIXME: Should check for valid HTABMASK values */ + env->htab_mask = ((value & SDR_32_HTABMASK) << 16) | 0xFFFF; + env->htab_base = value & SDR_32_HTABORG; } } @@ -2079,7 +2075,8 @@ void helper_store_sr(CPUPPCState *env, target_ulong srnum, target_ulong value) { PowerPCCPU *cpu = ppc_env_get_cpu(env); - LOG_MMU("%s: reg=%d " TARGET_FMT_lx " " TARGET_FMT_lx "\n", __func__, + qemu_log_mask(CPU_LOG_MMU, + "%s: reg=%d " TARGET_FMT_lx " " TARGET_FMT_lx "\n", __func__, (int)srnum, value, env->sr[srnum]); #if defined(TARGET_PPC64) if (env->mmu_model & POWERPC_MMU_64) { diff --git a/target-ppc/translate.c b/target-ppc/translate.c index d381632..8f255ea 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -183,7 +183,7 @@ void ppc_translate_init(void) } /* internal defines */ -typedef struct DisasContext { +struct DisasContext { struct TranslationBlock *tb; target_ulong nip; uint32_t opcode; @@ -203,11 +203,12 @@ typedef struct DisasContext { int altivec_enabled; int vsx_enabled; int spe_enabled; + int tm_enabled; ppc_spr_t *spr_cb; /* Needed to check rights for mfspr/mtspr */ int singlestep_enabled; uint64_t insns_flags; uint64_t insns_flags2; -} DisasContext; +}; /* Return true iff byteswap is needed in a scalar memop */ static inline bool need_byteswap(const DisasContext *ctx) @@ -250,26 +251,10 @@ static inline void gen_reset_fpstatus(void) gen_helper_reset_fpstatus(cpu_env); } -static inline void gen_compute_fprf(TCGv_i64 arg, int set_fprf, int set_rc) +static inline void gen_compute_fprf(TCGv_i64 arg) { - TCGv_i32 t0 = tcg_temp_new_i32(); - - if (set_fprf != 0) { - /* This case might be optimized later */ - tcg_gen_movi_i32(t0, 1); - gen_helper_compute_fprf(t0, cpu_env, arg, t0); - if (unlikely(set_rc)) { - tcg_gen_mov_i32(cpu_crf[1], t0); - } - gen_helper_float_check_status(cpu_env); - } else if (unlikely(set_rc)) { - /* We always need to compute fpcc */ - tcg_gen_movi_i32(t0, 0); - gen_helper_compute_fprf(t0, cpu_env, arg, t0); - tcg_gen_mov_i32(cpu_crf[1], t0); - } - - tcg_temp_free_i32(t0); + gen_helper_compute_fprf(cpu_env, arg); + gen_helper_float_check_status(cpu_env); } static inline void gen_set_access_type(DisasContext *ctx, int access_type) @@ -346,11 +331,13 @@ static inline void gen_stop_exception(DisasContext *ctx) ctx->exception = POWERPC_EXCP_STOP; } +#ifndef CONFIG_USER_ONLY /* No need to update nip here, as execution flow will change */ static inline void gen_sync_exception(DisasContext *ctx) { ctx->exception = POWERPC_EXCP_SYNC; } +#endif #define GEN_HANDLER(name, opc1, opc2, opc3, inval, type) \ GEN_OPCODE(name, opc1, opc2, opc3, inval, type, PPC_NONE) @@ -452,7 +439,10 @@ EXTRACT_HELPER(ME, 1, 5); EXTRACT_HELPER(TO, 21, 5); EXTRACT_HELPER(CRM, 12, 8); + +#ifndef CONFIG_USER_ONLY EXTRACT_HELPER(SR, 16, 4); +#endif /* mtfsf/mtfsfi */ EXTRACT_HELPER(FPBF, 23, 3); @@ -763,7 +753,7 @@ static void gen_cmpli(DisasContext *ctx) /* isel (PowerPC 2.03 specification) */ static void gen_isel(DisasContext *ctx) { - int l1, l2; + TCGLabel *l1, *l2; uint32_t bi = rC(ctx->opcode); uint32_t mask; TCGv_i32 t0; @@ -954,8 +944,8 @@ static void gen_addis(DisasContext *ctx) static inline void gen_op_arith_divw(DisasContext *ctx, TCGv ret, TCGv arg1, TCGv arg2, int sign, int compute_ov) { - int l1 = gen_new_label(); - int l2 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); TCGv_i32 t0 = tcg_temp_local_new_i32(); TCGv_i32 t1 = tcg_temp_local_new_i32(); @@ -963,7 +953,7 @@ static inline void gen_op_arith_divw(DisasContext *ctx, TCGv ret, TCGv arg1, tcg_gen_trunc_tl_i32(t1, arg2); tcg_gen_brcondi_i32(TCG_COND_EQ, t1, 0, l1); if (sign) { - int l3 = gen_new_label(); + TCGLabel *l3 = gen_new_label(); tcg_gen_brcondi_i32(TCG_COND_NE, t1, -1, l3); tcg_gen_brcondi_i32(TCG_COND_EQ, t0, INT32_MIN, l1); gen_set_label(l3); @@ -1029,12 +1019,12 @@ GEN_DIVE(divweo, divwe, 1); static inline void gen_op_arith_divd(DisasContext *ctx, TCGv ret, TCGv arg1, TCGv arg2, int sign, int compute_ov) { - int l1 = gen_new_label(); - int l2 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); tcg_gen_brcondi_i64(TCG_COND_EQ, arg2, 0, l1); if (sign) { - int l3 = gen_new_label(); + TCGLabel *l3 = gen_new_label(); tcg_gen_brcondi_i64(TCG_COND_NE, arg2, -1, l3); tcg_gen_brcondi_i64(TCG_COND_EQ, arg1, INT64_MIN, l1); gen_set_label(l3); @@ -2077,6 +2067,21 @@ static void gen_srd(DisasContext *ctx) } #endif +#if defined(TARGET_PPC64) +static void gen_set_cr1_from_fpscr(DisasContext *ctx) +{ + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(tmp, cpu_fpscr); + tcg_gen_shri_i32(cpu_crf[1], tmp, 28); + tcg_temp_free_i32(tmp); +} +#else +static void gen_set_cr1_from_fpscr(DisasContext *ctx) +{ + tcg_gen_shri_tl(cpu_crf[1], cpu_fpscr, 28); +} +#endif + /*** Floating-Point arithmetic ***/ #define _GEN_FLOAT_ACB(name, op, op1, op2, isfloat, set_fprf, type) \ static void gen_f##name(DisasContext *ctx) \ @@ -2095,8 +2100,12 @@ static void gen_f##name(DisasContext *ctx) \ gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_env, \ cpu_fpr[rD(ctx->opcode)]); \ } \ - gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], set_fprf, \ - Rc(ctx->opcode) != 0); \ + if (set_fprf) { \ + gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]); \ + } \ + if (unlikely(Rc(ctx->opcode) != 0)) { \ + gen_set_cr1_from_fpscr(ctx); \ + } \ } #define GEN_FLOAT_ACB(name, op2, set_fprf, type) \ @@ -2120,8 +2129,12 @@ static void gen_f##name(DisasContext *ctx) \ gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_env, \ cpu_fpr[rD(ctx->opcode)]); \ } \ - gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], \ - set_fprf, Rc(ctx->opcode) != 0); \ + if (set_fprf) { \ + gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]); \ + } \ + if (unlikely(Rc(ctx->opcode) != 0)) { \ + gen_set_cr1_from_fpscr(ctx); \ + } \ } #define GEN_FLOAT_AB(name, op2, inval, set_fprf, type) \ _GEN_FLOAT_AB(name, name, 0x3F, op2, inval, 0, set_fprf, type); \ @@ -2144,8 +2157,12 @@ static void gen_f##name(DisasContext *ctx) \ gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_env, \ cpu_fpr[rD(ctx->opcode)]); \ } \ - gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], \ - set_fprf, Rc(ctx->opcode) != 0); \ + if (set_fprf) { \ + gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]); \ + } \ + if (unlikely(Rc(ctx->opcode) != 0)) { \ + gen_set_cr1_from_fpscr(ctx); \ + } \ } #define GEN_FLOAT_AC(name, op2, inval, set_fprf, type) \ _GEN_FLOAT_AC(name, name, 0x3F, op2, inval, 0, set_fprf, type); \ @@ -2163,8 +2180,12 @@ static void gen_f##name(DisasContext *ctx) \ gen_reset_fpstatus(); \ gen_helper_f##name(cpu_fpr[rD(ctx->opcode)], cpu_env, \ cpu_fpr[rB(ctx->opcode)]); \ - gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], \ - set_fprf, Rc(ctx->opcode) != 0); \ + if (set_fprf) { \ + gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]); \ + } \ + if (unlikely(Rc(ctx->opcode) != 0)) { \ + gen_set_cr1_from_fpscr(ctx); \ + } \ } #define GEN_FLOAT_BS(name, op1, op2, set_fprf, type) \ @@ -2179,8 +2200,12 @@ static void gen_f##name(DisasContext *ctx) \ gen_reset_fpstatus(); \ gen_helper_f##name(cpu_fpr[rD(ctx->opcode)], cpu_env, \ cpu_fpr[rB(ctx->opcode)]); \ - gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], \ - set_fprf, Rc(ctx->opcode) != 0); \ + if (set_fprf) { \ + gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]); \ + } \ + if (unlikely(Rc(ctx->opcode) != 0)) { \ + gen_set_cr1_from_fpscr(ctx); \ + } \ } /* fadd - fadds */ @@ -2213,7 +2238,10 @@ static void gen_frsqrtes(DisasContext *ctx) cpu_fpr[rB(ctx->opcode)]); gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_env, cpu_fpr[rD(ctx->opcode)]); - gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], 1, Rc(ctx->opcode) != 0); + gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]); + if (unlikely(Rc(ctx->opcode) != 0)) { + gen_set_cr1_from_fpscr(ctx); + } } /* fsel */ @@ -2234,7 +2262,10 @@ static void gen_fsqrt(DisasContext *ctx) gen_reset_fpstatus(); gen_helper_fsqrt(cpu_fpr[rD(ctx->opcode)], cpu_env, cpu_fpr[rB(ctx->opcode)]); - gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], 1, Rc(ctx->opcode) != 0); + gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]); + if (unlikely(Rc(ctx->opcode) != 0)) { + gen_set_cr1_from_fpscr(ctx); + } } static void gen_fsqrts(DisasContext *ctx) @@ -2250,7 +2281,10 @@ static void gen_fsqrts(DisasContext *ctx) cpu_fpr[rB(ctx->opcode)]); gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_env, cpu_fpr[rD(ctx->opcode)]); - gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], 1, Rc(ctx->opcode) != 0); + gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]); + if (unlikely(Rc(ctx->opcode) != 0)) { + gen_set_cr1_from_fpscr(ctx); + } } /*** Floating-Point multiply-and-add ***/ @@ -2370,7 +2404,9 @@ static void gen_fabs(DisasContext *ctx) } tcg_gen_andi_i64(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rB(ctx->opcode)], ~(1ULL << 63)); - gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], 0, Rc(ctx->opcode) != 0); + if (unlikely(Rc(ctx->opcode))) { + gen_set_cr1_from_fpscr(ctx); + } } /* fmr - fmr. */ @@ -2382,7 +2418,9 @@ static void gen_fmr(DisasContext *ctx) return; } tcg_gen_mov_i64(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rB(ctx->opcode)]); - gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], 0, Rc(ctx->opcode) != 0); + if (unlikely(Rc(ctx->opcode))) { + gen_set_cr1_from_fpscr(ctx); + } } /* fnabs */ @@ -2395,7 +2433,9 @@ static void gen_fnabs(DisasContext *ctx) } tcg_gen_ori_i64(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rB(ctx->opcode)], 1ULL << 63); - gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], 0, Rc(ctx->opcode) != 0); + if (unlikely(Rc(ctx->opcode))) { + gen_set_cr1_from_fpscr(ctx); + } } /* fneg */ @@ -2408,7 +2448,9 @@ static void gen_fneg(DisasContext *ctx) } tcg_gen_xori_i64(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rB(ctx->opcode)], 1ULL << 63); - gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], 0, Rc(ctx->opcode) != 0); + if (unlikely(Rc(ctx->opcode))) { + gen_set_cr1_from_fpscr(ctx); + } } /* fcpsgn: PowerPC 2.05 specification */ @@ -2421,7 +2463,9 @@ static void gen_fcpsgn(DisasContext *ctx) } tcg_gen_deposit_i64(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rA(ctx->opcode)], cpu_fpr[rB(ctx->opcode)], 0, 63); - gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], 0, Rc(ctx->opcode) != 0); + if (unlikely(Rc(ctx->opcode))) { + gen_set_cr1_from_fpscr(ctx); + } } static void gen_fmrgew(DisasContext *ctx) @@ -2479,7 +2523,9 @@ static void gen_mffs(DisasContext *ctx) } gen_reset_fpstatus(); tcg_gen_extu_tl_i64(cpu_fpr[rD(ctx->opcode)], cpu_fpscr); - gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], 0, Rc(ctx->opcode) != 0); + if (unlikely(Rc(ctx->opcode))) { + gen_set_cr1_from_fpscr(ctx); + } } /* mtfsb0 */ @@ -2669,7 +2715,7 @@ static inline void gen_addr_add(DisasContext *ctx, TCGv ret, TCGv arg1, static inline void gen_check_align(DisasContext *ctx, TCGv EA, int mask) { - int l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); TCGv t0 = tcg_temp_new(); TCGv_i32 t1, t2; /* NIP cannot be restored if the memory exception comes from an helper */ @@ -3302,7 +3348,7 @@ static void gen_conditional_store(DisasContext *ctx, TCGv EA, static void gen_conditional_store(DisasContext *ctx, TCGv EA, int reg, int size) { - int l1; + TCGLabel *l1; tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); l1 = gen_new_label(); @@ -3833,7 +3879,7 @@ static void gen_b(DisasContext *ctx) static inline void gen_bcond(DisasContext *ctx, int type) { uint32_t bo = BO(ctx->opcode); - int l1; + TCGLabel *l1; TCGv target; ctx->exception = POWERPC_EXCP_BRANCH; @@ -4206,7 +4252,7 @@ static void gen_mfmsr(DisasContext *ctx) #endif } -static void spr_noaccess(void *opaque, int gprn, int sprn) +static void spr_noaccess(DisasContext *ctx, int gprn, int sprn) { #if 0 sprn = ((sprn >> 5) & 0x1F) | ((sprn & 0x1F) << 5); @@ -4218,7 +4264,7 @@ static void spr_noaccess(void *opaque, int gprn, int sprn) /* mfspr */ static inline void gen_op_mfspr(DisasContext *ctx) { - void (*read_cb)(void *opaque, int gprn, int sprn); + void (*read_cb)(DisasContext *ctx, int gprn, int sprn); uint32_t sprn = SPR(ctx->opcode); #if !defined(CONFIG_USER_ONLY) @@ -4369,7 +4415,7 @@ static void gen_mtmsr(DisasContext *ctx) /* mtspr */ static void gen_mtspr(DisasContext *ctx) { - void (*write_cb)(void *opaque, int sprn, int gprn); + void (*write_cb)(DisasContext *ctx, int sprn, int gprn); uint32_t sprn = SPR(ctx->opcode); #if !defined(CONFIG_USER_ONLY) @@ -4876,8 +4922,8 @@ static void gen_ecowx(DisasContext *ctx) /* abs - abs. */ static void gen_abs(DisasContext *ctx) { - int l1 = gen_new_label(); - int l2 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); tcg_gen_brcondi_tl(TCG_COND_GE, cpu_gpr[rA(ctx->opcode)], 0, l1); tcg_gen_neg_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); tcg_gen_br(l2); @@ -4891,9 +4937,9 @@ static void gen_abs(DisasContext *ctx) /* abso - abso. */ static void gen_abso(DisasContext *ctx) { - int l1 = gen_new_label(); - int l2 = gen_new_label(); - int l3 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); + TCGLabel *l3 = gen_new_label(); /* Start with XER OV disabled, the most likely case */ tcg_gen_movi_tl(cpu_ov, 0); tcg_gen_brcondi_tl(TCG_COND_GE, cpu_gpr[rA(ctx->opcode)], 0, l2); @@ -4959,8 +5005,8 @@ static void gen_divso(DisasContext *ctx) /* doz - doz. */ static void gen_doz(DisasContext *ctx) { - int l1 = gen_new_label(); - int l2 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); tcg_gen_brcond_tl(TCG_COND_GE, cpu_gpr[rB(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], l1); tcg_gen_sub_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); tcg_gen_br(l2); @@ -4974,8 +5020,8 @@ static void gen_doz(DisasContext *ctx) /* dozo - dozo. */ static void gen_dozo(DisasContext *ctx) { - int l1 = gen_new_label(); - int l2 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); TCGv t0 = tcg_temp_new(); TCGv t1 = tcg_temp_new(); TCGv t2 = tcg_temp_new(); @@ -5005,8 +5051,8 @@ static void gen_dozo(DisasContext *ctx) static void gen_dozi(DisasContext *ctx) { target_long simm = SIMM(ctx->opcode); - int l1 = gen_new_label(); - int l2 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); tcg_gen_brcondi_tl(TCG_COND_LT, cpu_gpr[rA(ctx->opcode)], simm, l1); tcg_gen_subfi_tl(cpu_gpr[rD(ctx->opcode)], simm, cpu_gpr[rA(ctx->opcode)]); tcg_gen_br(l2); @@ -5042,7 +5088,7 @@ static void gen_lscbx(DisasContext *ctx) /* maskg - maskg. */ static void gen_maskg(DisasContext *ctx) { - int l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); TCGv t0 = tcg_temp_new(); TCGv t1 = tcg_temp_new(); TCGv t2 = tcg_temp_new(); @@ -5102,7 +5148,7 @@ static void gen_mul(DisasContext *ctx) /* mulo - mulo. */ static void gen_mulo(DisasContext *ctx) { - int l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); TCGv_i64 t0 = tcg_temp_new_i64(); TCGv_i64 t1 = tcg_temp_new_i64(); TCGv t2 = tcg_temp_new(); @@ -5130,8 +5176,8 @@ static void gen_mulo(DisasContext *ctx) /* nabs - nabs. */ static void gen_nabs(DisasContext *ctx) { - int l1 = gen_new_label(); - int l2 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); tcg_gen_brcondi_tl(TCG_COND_GT, cpu_gpr[rA(ctx->opcode)], 0, l1); tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); tcg_gen_br(l2); @@ -5145,8 +5191,8 @@ static void gen_nabs(DisasContext *ctx) /* nabso - nabso. */ static void gen_nabso(DisasContext *ctx) { - int l1 = gen_new_label(); - int l2 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); tcg_gen_brcondi_tl(TCG_COND_GT, cpu_gpr[rA(ctx->opcode)], 0, l1); tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); tcg_gen_br(l2); @@ -5271,8 +5317,8 @@ static void gen_slliq(DisasContext *ctx) /* sllq - sllq. */ static void gen_sllq(DisasContext *ctx) { - int l1 = gen_new_label(); - int l2 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); TCGv t0 = tcg_temp_local_new(); TCGv t1 = tcg_temp_local_new(); TCGv t2 = tcg_temp_local_new(); @@ -5300,7 +5346,7 @@ static void gen_sllq(DisasContext *ctx) /* slq - slq. */ static void gen_slq(DisasContext *ctx) { - int l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); TCGv t0 = tcg_temp_new(); TCGv t1 = tcg_temp_new(); tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x1F); @@ -5324,7 +5370,7 @@ static void gen_slq(DisasContext *ctx) static void gen_sraiq(DisasContext *ctx) { int sh = SH(ctx->opcode); - int l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); TCGv t0 = tcg_temp_new(); TCGv t1 = tcg_temp_new(); tcg_gen_shri_tl(t0, cpu_gpr[rS(ctx->opcode)], sh); @@ -5346,8 +5392,8 @@ static void gen_sraiq(DisasContext *ctx) /* sraq - sraq. */ static void gen_sraq(DisasContext *ctx) { - int l1 = gen_new_label(); - int l2 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); TCGv t0 = tcg_temp_new(); TCGv t1 = tcg_temp_local_new(); TCGv t2 = tcg_temp_local_new(); @@ -5469,8 +5515,8 @@ static void gen_srliq(DisasContext *ctx) /* srlq */ static void gen_srlq(DisasContext *ctx) { - int l1 = gen_new_label(); - int l2 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); TCGv t0 = tcg_temp_local_new(); TCGv t1 = tcg_temp_local_new(); TCGv t2 = tcg_temp_local_new(); @@ -5499,7 +5545,7 @@ static void gen_srlq(DisasContext *ctx) /* srq */ static void gen_srq(DisasContext *ctx) { - int l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); TCGv t0 = tcg_temp_new(); TCGv t1 = tcg_temp_new(); tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x1F); @@ -5933,7 +5979,7 @@ static inline void gen_405_mulladd_insn(DisasContext *ctx, int opc2, int opc3, if (opc3 & 0x12) { /* Check overflow and/or saturate */ - int l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); if (opc3 & 0x10) { /* Start with XER OV disabled, the most likely case */ @@ -6348,7 +6394,7 @@ static void gen_tlbsx_40x(DisasContext *ctx) gen_helper_4xx_tlbsx(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); tcg_temp_free(t0); if (Rc(ctx->opcode)) { - int l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_gpr[rD(ctx->opcode)], -1, l1); tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 0x02); @@ -6429,7 +6475,7 @@ static void gen_tlbsx_440(DisasContext *ctx) gen_helper_440_tlbsx(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); tcg_temp_free(t0); if (Rc(ctx->opcode)) { - int l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_gpr[rD(ctx->opcode)], -1, l1); tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 0x02); @@ -6743,7 +6789,7 @@ static void gen_st##name(DisasContext *ctx) \ tcg_temp_free(EA); \ } -#define GEN_VR_LVE(name, opc2, opc3) \ +#define GEN_VR_LVE(name, opc2, opc3, size) \ static void gen_lve##name(DisasContext *ctx) \ { \ TCGv EA; \ @@ -6755,13 +6801,16 @@ static void gen_lve##name(DisasContext *ctx) \ gen_set_access_type(ctx, ACCESS_INT); \ EA = tcg_temp_new(); \ gen_addr_reg_index(ctx, EA); \ + if (size > 1) { \ + tcg_gen_andi_tl(EA, EA, ~(size - 1)); \ + } \ rs = gen_avr_ptr(rS(ctx->opcode)); \ gen_helper_lve##name(cpu_env, rs, EA); \ tcg_temp_free(EA); \ tcg_temp_free_ptr(rs); \ } -#define GEN_VR_STVE(name, opc2, opc3) \ +#define GEN_VR_STVE(name, opc2, opc3, size) \ static void gen_stve##name(DisasContext *ctx) \ { \ TCGv EA; \ @@ -6773,6 +6822,9 @@ static void gen_stve##name(DisasContext *ctx) \ gen_set_access_type(ctx, ACCESS_INT); \ EA = tcg_temp_new(); \ gen_addr_reg_index(ctx, EA); \ + if (size > 1) { \ + tcg_gen_andi_tl(EA, EA, ~(size - 1)); \ + } \ rs = gen_avr_ptr(rS(ctx->opcode)); \ gen_helper_stve##name(cpu_env, rs, EA); \ tcg_temp_free(EA); \ @@ -6783,17 +6835,17 @@ GEN_VR_LDX(lvx, 0x07, 0x03); /* As we don't emulate the cache, lvxl is stricly equivalent to lvx */ GEN_VR_LDX(lvxl, 0x07, 0x0B); -GEN_VR_LVE(bx, 0x07, 0x00); -GEN_VR_LVE(hx, 0x07, 0x01); -GEN_VR_LVE(wx, 0x07, 0x02); +GEN_VR_LVE(bx, 0x07, 0x00, 1); +GEN_VR_LVE(hx, 0x07, 0x01, 2); +GEN_VR_LVE(wx, 0x07, 0x02, 4); GEN_VR_STX(svx, 0x07, 0x07); /* As we don't emulate the cache, stvxl is stricly equivalent to stvx */ GEN_VR_STX(svxl, 0x07, 0x0F); -GEN_VR_STVE(bx, 0x07, 0x04); -GEN_VR_STVE(hx, 0x07, 0x05); -GEN_VR_STVE(wx, 0x07, 0x06); +GEN_VR_STVE(bx, 0x07, 0x04, 1); +GEN_VR_STVE(hx, 0x07, 0x05, 2); +GEN_VR_STVE(wx, 0x07, 0x06, 4); static void gen_lvsl(DisasContext *ctx) { @@ -8205,21 +8257,6 @@ static inline TCGv_ptr gen_fprp_ptr(int reg) return r; } -#if defined(TARGET_PPC64) -static void gen_set_cr1_from_fpscr(DisasContext *ctx) -{ - TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_trunc_tl_i32(tmp, cpu_fpscr); - tcg_gen_shri_i32(cpu_crf[1], tmp, 28); - tcg_temp_free_i32(tmp); -} -#else -static void gen_set_cr1_from_fpscr(DisasContext *ctx) -{ - tcg_gen_shri_tl(cpu_crf[1], cpu_fpscr, 28); -} -#endif - #define GEN_DFP_T_A_B_Rc(name) \ static void gen_##name(DisasContext *ctx) \ { \ @@ -8539,8 +8576,8 @@ static inline void gen_##name(DisasContext *ctx) \ static inline void gen_op_evabs(TCGv_i32 ret, TCGv_i32 arg1) { - int l1 = gen_new_label(); - int l2 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); tcg_gen_brcondi_i32(TCG_COND_GE, arg1, 0, l1); tcg_gen_neg_i32(ret, arg1); @@ -8589,12 +8626,10 @@ static inline void gen_##name(DisasContext *ctx) \ static inline void gen_op_evsrwu(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - TCGv_i32 t0; - int l1, l2; + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); + TCGv_i32 t0 = tcg_temp_local_new_i32(); - l1 = gen_new_label(); - l2 = gen_new_label(); - t0 = tcg_temp_local_new_i32(); /* No error here: 6 bits are used */ tcg_gen_andi_i32(t0, arg2, 0x3F); tcg_gen_brcondi_i32(TCG_COND_GE, t0, 32, l1); @@ -8608,12 +8643,10 @@ static inline void gen_op_evsrwu(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) GEN_SPEOP_ARITH2(evsrwu, gen_op_evsrwu); static inline void gen_op_evsrws(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - TCGv_i32 t0; - int l1, l2; + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); + TCGv_i32 t0 = tcg_temp_local_new_i32(); - l1 = gen_new_label(); - l2 = gen_new_label(); - t0 = tcg_temp_local_new_i32(); /* No error here: 6 bits are used */ tcg_gen_andi_i32(t0, arg2, 0x3F); tcg_gen_brcondi_i32(TCG_COND_GE, t0, 32, l1); @@ -8627,12 +8660,10 @@ static inline void gen_op_evsrws(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) GEN_SPEOP_ARITH2(evsrws, gen_op_evsrws); static inline void gen_op_evslw(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - TCGv_i32 t0; - int l1, l2; + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); + TCGv_i32 t0 = tcg_temp_local_new_i32(); - l1 = gen_new_label(); - l2 = gen_new_label(); - t0 = tcg_temp_local_new_i32(); /* No error here: 6 bits are used */ tcg_gen_andi_i32(t0, arg2, 0x3F); tcg_gen_brcondi_i32(TCG_COND_GE, t0, 32, l1); @@ -8700,10 +8731,10 @@ static inline void gen_##name(DisasContext *ctx) \ gen_exception(ctx, POWERPC_EXCP_SPEU); \ return; \ } \ - int l1 = gen_new_label(); \ - int l2 = gen_new_label(); \ - int l3 = gen_new_label(); \ - int l4 = gen_new_label(); \ + TCGLabel *l1 = gen_new_label(); \ + TCGLabel *l2 = gen_new_label(); \ + TCGLabel *l3 = gen_new_label(); \ + TCGLabel *l4 = gen_new_label(); \ \ tcg_gen_ext32s_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); \ tcg_gen_ext32s_tl(cpu_gpr[rB(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); \ @@ -8793,11 +8824,12 @@ static inline void gen_evsplatfi(DisasContext *ctx) static inline void gen_evsel(DisasContext *ctx) { - int l1 = gen_new_label(); - int l2 = gen_new_label(); - int l3 = gen_new_label(); - int l4 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); + TCGLabel *l3 = gen_new_label(); + TCGLabel *l4 = gen_new_label(); TCGv_i32 t0 = tcg_temp_local_new_i32(); + tcg_gen_andi_i32(t0, cpu_crf[ctx->opcode & 0x07], 1 << 3); tcg_gen_brcondi_i32(TCG_COND_EQ, t0, 0, l1); tcg_gen_mov_tl(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rA(ctx->opcode)]); @@ -9642,6 +9674,88 @@ GEN_SPE(efdctsiz, speundef, 0x1D, 0x0B, 0x00180000, 0xFFFFFFFF, PPC_SPE_DOUBLE GEN_SPE(efdtstgt, efdtstlt, 0x1E, 0x0B, 0x00600000, 0x00600000, PPC_SPE_DOUBLE); // GEN_SPE(efdtsteq, speundef, 0x1F, 0x0B, 0x00600000, 0xFFFFFFFF, PPC_SPE_DOUBLE); // +static void gen_tbegin(DisasContext *ctx) +{ + if (unlikely(!ctx->tm_enabled)) { + gen_exception_err(ctx, POWERPC_EXCP_FU, FSCR_IC_TM); + return; + } + gen_helper_tbegin(cpu_env); +} + +#define GEN_TM_NOOP(name) \ +static inline void gen_##name(DisasContext *ctx) \ +{ \ + if (unlikely(!ctx->tm_enabled)) { \ + gen_exception_err(ctx, POWERPC_EXCP_FU, FSCR_IC_TM); \ + return; \ + } \ + /* Because tbegin always fails in QEMU, these user \ + * space instructions all have a simple implementation: \ + * \ + * CR[0] = 0b0 || MSR[TS] || 0b0 \ + * = 0b0 || 0b00 || 0b0 \ + */ \ + tcg_gen_movi_i32(cpu_crf[0], 0); \ +} + +GEN_TM_NOOP(tend); +GEN_TM_NOOP(tabort); +GEN_TM_NOOP(tabortwc); +GEN_TM_NOOP(tabortwci); +GEN_TM_NOOP(tabortdc); +GEN_TM_NOOP(tabortdci); +GEN_TM_NOOP(tsr); + +static void gen_tcheck(DisasContext *ctx) +{ + if (unlikely(!ctx->tm_enabled)) { + gen_exception_err(ctx, POWERPC_EXCP_FU, FSCR_IC_TM); + return; + } + /* Because tbegin always fails, the tcheck implementation + * is simple: + * + * CR[CRF] = TDOOMED || MSR[TS] || 0b0 + * = 0b1 || 0b00 || 0b0 + */ + tcg_gen_movi_i32(cpu_crf[crfD(ctx->opcode)], 0x8); +} + +#if defined(CONFIG_USER_ONLY) +#define GEN_TM_PRIV_NOOP(name) \ +static inline void gen_##name(DisasContext *ctx) \ +{ \ + gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); \ +} + +#else + +#define GEN_TM_PRIV_NOOP(name) \ +static inline void gen_##name(DisasContext *ctx) \ +{ \ + if (unlikely(ctx->pr)) { \ + gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); \ + return; \ + } \ + if (unlikely(!ctx->tm_enabled)) { \ + gen_exception_err(ctx, POWERPC_EXCP_FU, FSCR_IC_TM); \ + return; \ + } \ + /* Because tbegin always fails, the implementation is \ + * simple: \ + * \ + * CR[0] = 0b0 || MSR[TS] || 0b0 \ + * = 0b0 || 0b00 | 0b0 \ + */ \ + tcg_gen_movi_i32(cpu_crf[0], 0); \ +} + +#endif + +GEN_TM_PRIV_NOOP(treclaim); +GEN_TM_PRIV_NOOP(trechkpt); + static opcode_t opcodes[] = { GEN_HANDLER(invalid, 0x00, 0x00, 0x00, 0xFFFFFFFF, PPC_NONE), GEN_HANDLER(cmp, 0x1F, 0x00, 0x00, 0x00400000, PPC_INTEGER), @@ -11054,6 +11168,29 @@ GEN_SPEOP_LDST(evstwhe, 0x18, 2), GEN_SPEOP_LDST(evstwho, 0x1A, 2), GEN_SPEOP_LDST(evstwwe, 0x1C, 2), GEN_SPEOP_LDST(evstwwo, 0x1E, 2), + +GEN_HANDLER2_E(tbegin, "tbegin", 0x1F, 0x0E, 0x14, 0x01DFF800, \ + PPC_NONE, PPC2_TM), +GEN_HANDLER2_E(tend, "tend", 0x1F, 0x0E, 0x15, 0x01FFF800, \ + PPC_NONE, PPC2_TM), +GEN_HANDLER2_E(tabort, "tabort", 0x1F, 0x0E, 0x1C, 0x03E0F800, \ + PPC_NONE, PPC2_TM), +GEN_HANDLER2_E(tabortwc, "tabortwc", 0x1F, 0x0E, 0x18, 0x00000000, \ + PPC_NONE, PPC2_TM), +GEN_HANDLER2_E(tabortwci, "tabortwci", 0x1F, 0x0E, 0x1A, 0x00000000, \ + PPC_NONE, PPC2_TM), +GEN_HANDLER2_E(tabortdc, "tabortdc", 0x1F, 0x0E, 0x19, 0x00000000, \ + PPC_NONE, PPC2_TM), +GEN_HANDLER2_E(tabortdci, "tabortdci", 0x1F, 0x0E, 0x1B, 0x00000000, \ + PPC_NONE, PPC2_TM), +GEN_HANDLER2_E(tsr, "tsr", 0x1F, 0x0E, 0x17, 0x03DFF800, \ + PPC_NONE, PPC2_TM), +GEN_HANDLER2_E(tcheck, "tcheck", 0x1F, 0x0E, 0x16, 0x007FF800, \ + PPC_NONE, PPC2_TM), +GEN_HANDLER2_E(treclaim, "treclaim", 0x1F, 0x0E, 0x1D, 0x03E0F800, \ + PPC_NONE, PPC2_TM), +GEN_HANDLER2_E(trechkpt, "trechkpt", 0x1F, 0x0E, 0x1F, 0x03FFF800, \ + PPC_NONE, PPC2_TM), }; #include "helper_regs.h" @@ -11072,8 +11209,9 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, int i; cpu_fprintf(f, "NIP " TARGET_FMT_lx " LR " TARGET_FMT_lx " CTR " - TARGET_FMT_lx " XER " TARGET_FMT_lx "\n", - env->nip, env->lr, env->ctr, cpu_read_xer(env)); + TARGET_FMT_lx " XER " TARGET_FMT_lx " CPU#%d\n", + env->nip, env->lr, env->ctr, cpu_read_xer(env), + cs->cpu_index); cpu_fprintf(f, "MSR " TARGET_FMT_lx " HID0 " TARGET_FMT_lx " HF " TARGET_FMT_lx " idx %d\n", env->msr, env->spr[SPR_HID0], env->hflags, env->mmu_idx); @@ -11273,14 +11411,12 @@ static inline void gen_intermediate_code_internal(PowerPCCPU *cpu, DisasContext ctx, *ctxp = &ctx; opc_handler_t **table, *handler; target_ulong pc_start; - uint16_t *gen_opc_end; CPUBreakpoint *bp; int j, lj = -1; int num_insns; int max_insns; pc_start = tb->pc; - gen_opc_end = tcg_ctx.gen_opc_buf + OPC_MAX_SIZE; ctx.nip = pc_start; ctx.tb = tb; ctx.exception = POWERPC_EXCP_NONE; @@ -11311,6 +11447,13 @@ static inline void gen_intermediate_code_internal(PowerPCCPU *cpu, } else { ctx.vsx_enabled = 0; } +#if defined(TARGET_PPC64) + if ((env->flags & POWERPC_FLAG_TM) && msr_tm) { + ctx.tm_enabled = msr_tm; + } else { + ctx.tm_enabled = 0; + } +#endif if ((env->flags & POWERPC_FLAG_SE) && msr_se) ctx.singlestep_enabled = CPU_SINGLE_STEP; else @@ -11329,11 +11472,10 @@ static inline void gen_intermediate_code_internal(PowerPCCPU *cpu, if (max_insns == 0) max_insns = CF_COUNT_MASK; - gen_tb_start(); + gen_tb_start(tb); tcg_clear_temp_count(); /* Set env in case of segfault during code fetch */ - while (ctx.exception == POWERPC_EXCP_NONE - && tcg_ctx.gen_opc_ptr < gen_opc_end) { + while (ctx.exception == POWERPC_EXCP_NONE && !tcg_op_buf_full()) { if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) { QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { if (bp->pc == ctx.nip) { @@ -11343,7 +11485,7 @@ static inline void gen_intermediate_code_internal(PowerPCCPU *cpu, } } if (unlikely(search_pc)) { - j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + j = tcg_op_buf_count(); if (lj < j) { lj++; while (lj < j) @@ -11449,9 +11591,9 @@ static inline void gen_intermediate_code_internal(PowerPCCPU *cpu, tcg_gen_exit_tb(0); } gen_tb_end(tb, num_insns); - *tcg_ctx.gen_opc_ptr = INDEX_op_end; + if (unlikely(search_pc)) { - j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + j = tcg_op_buf_count(); lj++; while (lj <= j) tcg_ctx.gen_opc_instr_start[lj++] = 0; diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index 1fece7b..d74f4f0 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -65,7 +65,7 @@ static void spr_load_dump_spr(int sprn) #endif } -static void spr_read_generic (void *opaque, int gprn, int sprn) +static void spr_read_generic (DisasContext *ctx, int gprn, int sprn) { gen_load_spr(cpu_gpr[gprn], sprn); spr_load_dump_spr(sprn); @@ -80,14 +80,14 @@ static void spr_store_dump_spr(int sprn) #endif } -static void spr_write_generic (void *opaque, int sprn, int gprn) +static void spr_write_generic (DisasContext *ctx, int sprn, int gprn) { gen_store_spr(sprn, cpu_gpr[gprn]); spr_store_dump_spr(sprn); } #if !defined(CONFIG_USER_ONLY) -static void spr_write_generic32(void *opaque, int sprn, int gprn) +static void spr_write_generic32(DisasContext *ctx, int sprn, int gprn) { #ifdef TARGET_PPC64 TCGv t0 = tcg_temp_new(); @@ -96,11 +96,11 @@ static void spr_write_generic32(void *opaque, int sprn, int gprn) tcg_temp_free(t0); spr_store_dump_spr(sprn); #else - spr_write_generic(opaque, sprn, gprn); + spr_write_generic(ctx, sprn, gprn); #endif } -static void spr_write_clear (void *opaque, int sprn, int gprn) +static void spr_write_clear (DisasContext *ctx, int sprn, int gprn) { TCGv t0 = tcg_temp_new(); TCGv t1 = tcg_temp_new(); @@ -112,7 +112,7 @@ static void spr_write_clear (void *opaque, int sprn, int gprn) tcg_temp_free(t1); } -static void spr_access_nop(void *opaque, int sprn, int gprn) +static void spr_access_nop(DisasContext *ctx, int sprn, int gprn) { } @@ -120,47 +120,47 @@ static void spr_access_nop(void *opaque, int sprn, int gprn) /* SPR common to all PowerPC */ /* XER */ -static void spr_read_xer (void *opaque, int gprn, int sprn) +static void spr_read_xer (DisasContext *ctx, int gprn, int sprn) { gen_read_xer(cpu_gpr[gprn]); } -static void spr_write_xer (void *opaque, int sprn, int gprn) +static void spr_write_xer (DisasContext *ctx, int sprn, int gprn) { gen_write_xer(cpu_gpr[gprn]); } /* LR */ -static void spr_read_lr (void *opaque, int gprn, int sprn) +static void spr_read_lr (DisasContext *ctx, int gprn, int sprn) { tcg_gen_mov_tl(cpu_gpr[gprn], cpu_lr); } -static void spr_write_lr (void *opaque, int sprn, int gprn) +static void spr_write_lr (DisasContext *ctx, int sprn, int gprn) { tcg_gen_mov_tl(cpu_lr, cpu_gpr[gprn]); } /* CFAR */ #if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) -static void spr_read_cfar (void *opaque, int gprn, int sprn) +static void spr_read_cfar (DisasContext *ctx, int gprn, int sprn) { tcg_gen_mov_tl(cpu_gpr[gprn], cpu_cfar); } -static void spr_write_cfar (void *opaque, int sprn, int gprn) +static void spr_write_cfar (DisasContext *ctx, int sprn, int gprn) { tcg_gen_mov_tl(cpu_cfar, cpu_gpr[gprn]); } #endif /* defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) */ /* CTR */ -static void spr_read_ctr (void *opaque, int gprn, int sprn) +static void spr_read_ctr (DisasContext *ctx, int gprn, int sprn) { tcg_gen_mov_tl(cpu_gpr[gprn], cpu_ctr); } -static void spr_write_ctr (void *opaque, int sprn, int gprn) +static void spr_write_ctr (DisasContext *ctx, int sprn, int gprn) { tcg_gen_mov_tl(cpu_ctr, cpu_gpr[gprn]); } @@ -171,13 +171,13 @@ static void spr_write_ctr (void *opaque, int sprn, int gprn) /* UPMCx */ /* USIA */ /* UDECR */ -static void spr_read_ureg (void *opaque, int gprn, int sprn) +static void spr_read_ureg (DisasContext *ctx, int gprn, int sprn) { gen_load_spr(cpu_gpr[gprn], sprn + 0x10); } #if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) -static void spr_write_ureg(void *opaque, int sprn, int gprn) +static void spr_write_ureg(DisasContext *ctx, int sprn, int gprn) { gen_store_spr(sprn + 0x10, cpu_gpr[gprn]); } @@ -186,109 +186,109 @@ static void spr_write_ureg(void *opaque, int sprn, int gprn) /* SPR common to all non-embedded PowerPC */ /* DECR */ #if !defined(CONFIG_USER_ONLY) -static void spr_read_decr (void *opaque, int gprn, int sprn) +static void spr_read_decr (DisasContext *ctx, int gprn, int sprn) { - if (use_icount) { + if (ctx->tb->cflags & CF_USE_ICOUNT) { gen_io_start(); } gen_helper_load_decr(cpu_gpr[gprn], cpu_env); - if (use_icount) { + if (ctx->tb->cflags & CF_USE_ICOUNT) { gen_io_end(); - gen_stop_exception(opaque); + gen_stop_exception(ctx); } } -static void spr_write_decr (void *opaque, int sprn, int gprn) +static void spr_write_decr (DisasContext *ctx, int sprn, int gprn) { - if (use_icount) { + if (ctx->tb->cflags & CF_USE_ICOUNT) { gen_io_start(); } gen_helper_store_decr(cpu_env, cpu_gpr[gprn]); - if (use_icount) { + if (ctx->tb->cflags & CF_USE_ICOUNT) { gen_io_end(); - gen_stop_exception(opaque); + gen_stop_exception(ctx); } } #endif /* SPR common to all non-embedded PowerPC, except 601 */ /* Time base */ -static void spr_read_tbl (void *opaque, int gprn, int sprn) +static void spr_read_tbl (DisasContext *ctx, int gprn, int sprn) { - if (use_icount) { + if (ctx->tb->cflags & CF_USE_ICOUNT) { gen_io_start(); } gen_helper_load_tbl(cpu_gpr[gprn], cpu_env); - if (use_icount) { + if (ctx->tb->cflags & CF_USE_ICOUNT) { gen_io_end(); - gen_stop_exception(opaque); + gen_stop_exception(ctx); } } -static void spr_read_tbu (void *opaque, int gprn, int sprn) +static void spr_read_tbu (DisasContext *ctx, int gprn, int sprn) { - if (use_icount) { + if (ctx->tb->cflags & CF_USE_ICOUNT) { gen_io_start(); } gen_helper_load_tbu(cpu_gpr[gprn], cpu_env); - if (use_icount) { + if (ctx->tb->cflags & CF_USE_ICOUNT) { gen_io_end(); - gen_stop_exception(opaque); + gen_stop_exception(ctx); } } __attribute__ (( unused )) -static void spr_read_atbl (void *opaque, int gprn, int sprn) +static void spr_read_atbl (DisasContext *ctx, int gprn, int sprn) { gen_helper_load_atbl(cpu_gpr[gprn], cpu_env); } __attribute__ (( unused )) -static void spr_read_atbu (void *opaque, int gprn, int sprn) +static void spr_read_atbu (DisasContext *ctx, int gprn, int sprn) { gen_helper_load_atbu(cpu_gpr[gprn], cpu_env); } #if !defined(CONFIG_USER_ONLY) -static void spr_write_tbl (void *opaque, int sprn, int gprn) +static void spr_write_tbl (DisasContext *ctx, int sprn, int gprn) { - if (use_icount) { + if (ctx->tb->cflags & CF_USE_ICOUNT) { gen_io_start(); } gen_helper_store_tbl(cpu_env, cpu_gpr[gprn]); - if (use_icount) { + if (ctx->tb->cflags & CF_USE_ICOUNT) { gen_io_end(); - gen_stop_exception(opaque); + gen_stop_exception(ctx); } } -static void spr_write_tbu (void *opaque, int sprn, int gprn) +static void spr_write_tbu (DisasContext *ctx, int sprn, int gprn) { - if (use_icount) { + if (ctx->tb->cflags & CF_USE_ICOUNT) { gen_io_start(); } gen_helper_store_tbu(cpu_env, cpu_gpr[gprn]); - if (use_icount) { + if (ctx->tb->cflags & CF_USE_ICOUNT) { gen_io_end(); - gen_stop_exception(opaque); + gen_stop_exception(ctx); } } __attribute__ (( unused )) -static void spr_write_atbl (void *opaque, int sprn, int gprn) +static void spr_write_atbl (DisasContext *ctx, int sprn, int gprn) { gen_helper_store_atbl(cpu_env, cpu_gpr[gprn]); } __attribute__ (( unused )) -static void spr_write_atbu (void *opaque, int sprn, int gprn) +static void spr_write_atbu (DisasContext *ctx, int sprn, int gprn) { gen_helper_store_atbu(cpu_env, cpu_gpr[gprn]); } #if defined(TARGET_PPC64) __attribute__ (( unused )) -static void spr_read_purr (void *opaque, int gprn, int sprn) +static void spr_read_purr (DisasContext *ctx, int gprn, int sprn) { gen_helper_load_purr(cpu_gpr[gprn], cpu_env); } @@ -298,38 +298,38 @@ static void spr_read_purr (void *opaque, int gprn, int sprn) #if !defined(CONFIG_USER_ONLY) /* IBAT0U...IBAT0U */ /* IBAT0L...IBAT7L */ -static void spr_read_ibat (void *opaque, int gprn, int sprn) +static void spr_read_ibat (DisasContext *ctx, int gprn, int sprn) { tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUPPCState, IBAT[sprn & 1][(sprn - SPR_IBAT0U) / 2])); } -static void spr_read_ibat_h (void *opaque, int gprn, int sprn) +static void spr_read_ibat_h (DisasContext *ctx, int gprn, int sprn) { tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUPPCState, IBAT[sprn & 1][(sprn - SPR_IBAT4U) / 2])); } -static void spr_write_ibatu (void *opaque, int sprn, int gprn) +static void spr_write_ibatu (DisasContext *ctx, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0U) / 2); gen_helper_store_ibatu(cpu_env, t0, cpu_gpr[gprn]); tcg_temp_free_i32(t0); } -static void spr_write_ibatu_h (void *opaque, int sprn, int gprn) +static void spr_write_ibatu_h (DisasContext *ctx, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_IBAT4U) / 2) + 4); gen_helper_store_ibatu(cpu_env, t0, cpu_gpr[gprn]); tcg_temp_free_i32(t0); } -static void spr_write_ibatl (void *opaque, int sprn, int gprn) +static void spr_write_ibatl (DisasContext *ctx, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0L) / 2); gen_helper_store_ibatl(cpu_env, t0, cpu_gpr[gprn]); tcg_temp_free_i32(t0); } -static void spr_write_ibatl_h (void *opaque, int sprn, int gprn) +static void spr_write_ibatl_h (DisasContext *ctx, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_IBAT4L) / 2) + 4); gen_helper_store_ibatl(cpu_env, t0, cpu_gpr[gprn]); @@ -338,38 +338,38 @@ static void spr_write_ibatl_h (void *opaque, int sprn, int gprn) /* DBAT0U...DBAT7U */ /* DBAT0L...DBAT7L */ -static void spr_read_dbat (void *opaque, int gprn, int sprn) +static void spr_read_dbat (DisasContext *ctx, int gprn, int sprn) { tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUPPCState, DBAT[sprn & 1][(sprn - SPR_DBAT0U) / 2])); } -static void spr_read_dbat_h (void *opaque, int gprn, int sprn) +static void spr_read_dbat_h (DisasContext *ctx, int gprn, int sprn) { tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUPPCState, DBAT[sprn & 1][((sprn - SPR_DBAT4U) / 2) + 4])); } -static void spr_write_dbatu (void *opaque, int sprn, int gprn) +static void spr_write_dbatu (DisasContext *ctx, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32((sprn - SPR_DBAT0U) / 2); gen_helper_store_dbatu(cpu_env, t0, cpu_gpr[gprn]); tcg_temp_free_i32(t0); } -static void spr_write_dbatu_h (void *opaque, int sprn, int gprn) +static void spr_write_dbatu_h (DisasContext *ctx, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_DBAT4U) / 2) + 4); gen_helper_store_dbatu(cpu_env, t0, cpu_gpr[gprn]); tcg_temp_free_i32(t0); } -static void spr_write_dbatl (void *opaque, int sprn, int gprn) +static void spr_write_dbatl (DisasContext *ctx, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32((sprn - SPR_DBAT0L) / 2); gen_helper_store_dbatl(cpu_env, t0, cpu_gpr[gprn]); tcg_temp_free_i32(t0); } -static void spr_write_dbatl_h (void *opaque, int sprn, int gprn) +static void spr_write_dbatl_h (DisasContext *ctx, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_DBAT4L) / 2) + 4); gen_helper_store_dbatl(cpu_env, t0, cpu_gpr[gprn]); @@ -377,19 +377,19 @@ static void spr_write_dbatl_h (void *opaque, int sprn, int gprn) } /* SDR1 */ -static void spr_write_sdr1 (void *opaque, int sprn, int gprn) +static void spr_write_sdr1 (DisasContext *ctx, int sprn, int gprn) { gen_helper_store_sdr1(cpu_env, cpu_gpr[gprn]); } /* 64 bits PowerPC specific SPRs */ #if defined(TARGET_PPC64) -static void spr_read_hior (void *opaque, int gprn, int sprn) +static void spr_read_hior (DisasContext *ctx, int gprn, int sprn) { tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUPPCState, excp_prefix)); } -static void spr_write_hior (void *opaque, int sprn, int gprn) +static void spr_write_hior (DisasContext *ctx, int sprn, int gprn) { TCGv t0 = tcg_temp_new(); tcg_gen_andi_tl(t0, cpu_gpr[gprn], 0x3FFFFF00000ULL); @@ -401,31 +401,29 @@ static void spr_write_hior (void *opaque, int sprn, int gprn) /* PowerPC 601 specific registers */ /* RTC */ -static void spr_read_601_rtcl (void *opaque, int gprn, int sprn) +static void spr_read_601_rtcl (DisasContext *ctx, int gprn, int sprn) { gen_helper_load_601_rtcl(cpu_gpr[gprn], cpu_env); } -static void spr_read_601_rtcu (void *opaque, int gprn, int sprn) +static void spr_read_601_rtcu (DisasContext *ctx, int gprn, int sprn) { gen_helper_load_601_rtcu(cpu_gpr[gprn], cpu_env); } #if !defined(CONFIG_USER_ONLY) -static void spr_write_601_rtcu (void *opaque, int sprn, int gprn) +static void spr_write_601_rtcu (DisasContext *ctx, int sprn, int gprn) { gen_helper_store_601_rtcu(cpu_env, cpu_gpr[gprn]); } -static void spr_write_601_rtcl (void *opaque, int sprn, int gprn) +static void spr_write_601_rtcl (DisasContext *ctx, int sprn, int gprn) { gen_helper_store_601_rtcl(cpu_env, cpu_gpr[gprn]); } -static void spr_write_hid0_601 (void *opaque, int sprn, int gprn) +static void spr_write_hid0_601 (DisasContext *ctx, int sprn, int gprn) { - DisasContext *ctx = opaque; - gen_helper_store_hid0_601(cpu_env, cpu_gpr[gprn]); /* Must stop the translation as endianness may have changed */ gen_stop_exception(ctx); @@ -434,19 +432,19 @@ static void spr_write_hid0_601 (void *opaque, int sprn, int gprn) /* Unified bats */ #if !defined(CONFIG_USER_ONLY) -static void spr_read_601_ubat (void *opaque, int gprn, int sprn) +static void spr_read_601_ubat (DisasContext *ctx, int gprn, int sprn) { tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUPPCState, IBAT[sprn & 1][(sprn - SPR_IBAT0U) / 2])); } -static void spr_write_601_ubatu (void *opaque, int sprn, int gprn) +static void spr_write_601_ubatu (DisasContext *ctx, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0U) / 2); gen_helper_store_601_batl(cpu_env, t0, cpu_gpr[gprn]); tcg_temp_free_i32(t0); } -static void spr_write_601_ubatl (void *opaque, int sprn, int gprn) +static void spr_write_601_ubatl (DisasContext *ctx, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0U) / 2); gen_helper_store_601_batu(cpu_env, t0, cpu_gpr[gprn]); @@ -456,36 +454,34 @@ static void spr_write_601_ubatl (void *opaque, int sprn, int gprn) /* PowerPC 40x specific registers */ #if !defined(CONFIG_USER_ONLY) -static void spr_read_40x_pit (void *opaque, int gprn, int sprn) +static void spr_read_40x_pit (DisasContext *ctx, int gprn, int sprn) { gen_helper_load_40x_pit(cpu_gpr[gprn], cpu_env); } -static void spr_write_40x_pit (void *opaque, int sprn, int gprn) +static void spr_write_40x_pit (DisasContext *ctx, int sprn, int gprn) { gen_helper_store_40x_pit(cpu_env, cpu_gpr[gprn]); } -static void spr_write_40x_dbcr0 (void *opaque, int sprn, int gprn) +static void spr_write_40x_dbcr0 (DisasContext *ctx, int sprn, int gprn) { - DisasContext *ctx = opaque; - gen_helper_store_40x_dbcr0(cpu_env, cpu_gpr[gprn]); /* We must stop translation as we may have rebooted */ gen_stop_exception(ctx); } -static void spr_write_40x_sler (void *opaque, int sprn, int gprn) +static void spr_write_40x_sler (DisasContext *ctx, int sprn, int gprn) { gen_helper_store_40x_sler(cpu_env, cpu_gpr[gprn]); } -static void spr_write_booke_tcr (void *opaque, int sprn, int gprn) +static void spr_write_booke_tcr (DisasContext *ctx, int sprn, int gprn) { gen_helper_store_booke_tcr(cpu_env, cpu_gpr[gprn]); } -static void spr_write_booke_tsr (void *opaque, int sprn, int gprn) +static void spr_write_booke_tsr (DisasContext *ctx, int sprn, int gprn) { gen_helper_store_booke_tsr(cpu_env, cpu_gpr[gprn]); } @@ -494,19 +490,19 @@ static void spr_write_booke_tsr (void *opaque, int sprn, int gprn) /* PowerPC 403 specific registers */ /* PBL1 / PBU1 / PBL2 / PBU2 */ #if !defined(CONFIG_USER_ONLY) -static void spr_read_403_pbr (void *opaque, int gprn, int sprn) +static void spr_read_403_pbr (DisasContext *ctx, int gprn, int sprn) { tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUPPCState, pb[sprn - SPR_403_PBL1])); } -static void spr_write_403_pbr (void *opaque, int sprn, int gprn) +static void spr_write_403_pbr (DisasContext *ctx, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32(sprn - SPR_403_PBL1); gen_helper_store_403_pbr(cpu_env, t0, cpu_gpr[gprn]); tcg_temp_free_i32(t0); } -static void spr_write_pir (void *opaque, int sprn, int gprn) +static void spr_write_pir (DisasContext *ctx, int sprn, int gprn) { TCGv t0 = tcg_temp_new(); tcg_gen_andi_tl(t0, cpu_gpr[gprn], 0xF); @@ -516,7 +512,7 @@ static void spr_write_pir (void *opaque, int sprn, int gprn) #endif /* SPE specific registers */ -static void spr_read_spefscr (void *opaque, int gprn, int sprn) +static void spr_read_spefscr (DisasContext *ctx, int gprn, int sprn) { TCGv_i32 t0 = tcg_temp_new_i32(); tcg_gen_ld_i32(t0, cpu_env, offsetof(CPUPPCState, spe_fscr)); @@ -524,7 +520,7 @@ static void spr_read_spefscr (void *opaque, int gprn, int sprn) tcg_temp_free_i32(t0); } -static void spr_write_spefscr (void *opaque, int sprn, int gprn) +static void spr_write_spefscr (DisasContext *ctx, int sprn, int gprn) { TCGv_i32 t0 = tcg_temp_new_i32(); tcg_gen_trunc_tl_i32(t0, cpu_gpr[gprn]); @@ -534,7 +530,7 @@ static void spr_write_spefscr (void *opaque, int sprn, int gprn) #if !defined(CONFIG_USER_ONLY) /* Callback used to write the exception vector base */ -static void spr_write_excp_prefix (void *opaque, int sprn, int gprn) +static void spr_write_excp_prefix (DisasContext *ctx, int sprn, int gprn) { TCGv t0 = tcg_temp_new(); tcg_gen_ld_tl(t0, cpu_env, offsetof(CPUPPCState, ivpr_mask)); @@ -544,9 +540,8 @@ static void spr_write_excp_prefix (void *opaque, int sprn, int gprn) tcg_temp_free(t0); } -static void spr_write_excp_vector (void *opaque, int sprn, int gprn) +static void spr_write_excp_vector (DisasContext *ctx, int sprn, int gprn) { - DisasContext *ctx = opaque; int sprn_offs; if (sprn >= SPR_BOOKE_IVOR0 && sprn <= SPR_BOOKE_IVOR15) { @@ -604,12 +599,12 @@ static inline void vscr_init (CPUPPCState *env, uint32_t val) static inline void _spr_register(CPUPPCState *env, int num, const char *name, - void (*uea_read)(void *opaque, int gprn, int sprn), - void (*uea_write)(void *opaque, int sprn, int gprn), + void (*uea_read)(DisasContext *ctx, int gprn, int sprn), + void (*uea_write)(DisasContext *ctx, int sprn, int gprn), #if !defined(CONFIG_USER_ONLY) - void (*oea_read)(void *opaque, int gprn, int sprn), - void (*oea_write)(void *opaque, int sprn, int gprn), + void (*oea_read)(DisasContext *ctx, int gprn, int sprn), + void (*oea_write)(DisasContext *ctx, int sprn, int gprn), #endif #if defined(CONFIG_KVM) uint64_t one_reg_id, @@ -1040,19 +1035,19 @@ static void gen_spr_7xx (CPUPPCState *env) #ifdef TARGET_PPC64 #ifndef CONFIG_USER_ONLY -static void spr_read_uamr (void *opaque, int gprn, int sprn) +static void spr_read_uamr (DisasContext *ctx, int gprn, int sprn) { gen_load_spr(cpu_gpr[gprn], SPR_AMR); spr_load_dump_spr(SPR_AMR); } -static void spr_write_uamr (void *opaque, int sprn, int gprn) +static void spr_write_uamr (DisasContext *ctx, int sprn, int gprn) { gen_store_spr(SPR_AMR, cpu_gpr[gprn]); spr_store_dump_spr(SPR_AMR); } -static void spr_write_uamr_pr (void *opaque, int sprn, int gprn) +static void spr_write_uamr_pr (DisasContext *ctx, int sprn, int gprn) { TCGv t0 = tcg_temp_new(); @@ -1454,7 +1449,7 @@ static void gen_74xx_soft_tlb (CPUPPCState *env, int nb_tlbs, int nb_ways) } #if !defined(CONFIG_USER_ONLY) -static void spr_write_e500_l1csr0 (void *opaque, int sprn, int gprn) +static void spr_write_e500_l1csr0 (DisasContext *ctx, int sprn, int gprn) { TCGv t0 = tcg_temp_new(); @@ -1463,7 +1458,7 @@ static void spr_write_e500_l1csr0 (void *opaque, int sprn, int gprn) tcg_temp_free(t0); } -static void spr_write_e500_l1csr1(void *opaque, int sprn, int gprn) +static void spr_write_e500_l1csr1(DisasContext *ctx, int sprn, int gprn) { TCGv t0 = tcg_temp_new(); @@ -1472,12 +1467,12 @@ static void spr_write_e500_l1csr1(void *opaque, int sprn, int gprn) tcg_temp_free(t0); } -static void spr_write_booke206_mmucsr0 (void *opaque, int sprn, int gprn) +static void spr_write_booke206_mmucsr0 (DisasContext *ctx, int sprn, int gprn) { gen_helper_booke206_tlbflush(cpu_env, cpu_gpr[gprn]); } -static void spr_write_booke_pid (void *opaque, int sprn, int gprn) +static void spr_write_booke_pid (DisasContext *ctx, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32(sprn); gen_helper_booke_setpid(cpu_env, t0, cpu_gpr[gprn]); @@ -1693,7 +1688,7 @@ static void gen_spr_BookE206(CPUPPCState *env, uint32_t mas_mask, /* TLB assist registers */ /* XXX : not implemented */ for (i = 0; i < 8; i++) { - void (*uea_write)(void *o, int sprn, int gprn) = &spr_write_generic32; + void (*uea_write)(DisasContext *ctx, int sprn, int gprn) = &spr_write_generic32; if (i == 2 && (mas_mask & (1 << i)) && (env->insns_flags & PPC_64B)) { uea_write = &spr_write_generic; } @@ -4680,7 +4675,7 @@ POWERPC_FAMILY(e300)(ObjectClass *oc, void *data) } #if !defined(CONFIG_USER_ONLY) -static void spr_write_mas73(void *opaque, int sprn, int gprn) +static void spr_write_mas73(DisasContext *ctx, int sprn, int gprn) { TCGv val = tcg_temp_new(); tcg_gen_ext32u_tl(val, cpu_gpr[gprn]); @@ -4690,7 +4685,7 @@ static void spr_write_mas73(void *opaque, int sprn, int gprn) tcg_temp_free(val); } -static void spr_read_mas73(void *opaque, int gprn, int sprn) +static void spr_read_mas73(DisasContext *ctx, int gprn, int sprn) { TCGv mas7 = tcg_temp_new(); TCGv mas3 = tcg_temp_new(); @@ -7322,14 +7317,14 @@ enum BOOK3S_CPU_TYPE { BOOK3S_CPU_POWER8 }; -static void gen_fscr_facility_check(void *opaque, int facility_sprn, int bit, - int sprn, int cause) +static void gen_fscr_facility_check(DisasContext *ctx, int facility_sprn, + int bit, int sprn, int cause) { TCGv_i32 t1 = tcg_const_i32(bit); TCGv_i32 t2 = tcg_const_i32(sprn); TCGv_i32 t3 = tcg_const_i32(cause); - gen_update_current_nip(opaque); + gen_update_current_nip(ctx); gen_helper_fscr_facility_check(cpu_env, t1, t2, t3); tcg_temp_free_i32(t3); @@ -7337,14 +7332,14 @@ static void gen_fscr_facility_check(void *opaque, int facility_sprn, int bit, tcg_temp_free_i32(t1); } -static void gen_msr_facility_check(void *opaque, int facility_sprn, int bit, - int sprn, int cause) +static void gen_msr_facility_check(DisasContext *ctx, int facility_sprn, + int bit, int sprn, int cause) { TCGv_i32 t1 = tcg_const_i32(bit); TCGv_i32 t2 = tcg_const_i32(sprn); TCGv_i32 t3 = tcg_const_i32(cause); - gen_update_current_nip(opaque); + gen_update_current_nip(ctx); gen_helper_msr_facility_check(cpu_env, t1, t2, t3); tcg_temp_free_i32(t3); @@ -7352,7 +7347,7 @@ static void gen_msr_facility_check(void *opaque, int facility_sprn, int bit, tcg_temp_free_i32(t1); } -static void spr_read_prev_upper32(void *opaque, int gprn, int sprn) +static void spr_read_prev_upper32(DisasContext *ctx, int gprn, int sprn) { TCGv spr_up = tcg_temp_new(); TCGv spr = tcg_temp_new(); @@ -7365,7 +7360,7 @@ static void spr_read_prev_upper32(void *opaque, int gprn, int sprn) tcg_temp_free(spr_up); } -static void spr_write_prev_upper32(void *opaque, int sprn, int gprn) +static void spr_write_prev_upper32(DisasContext *ctx, int sprn, int gprn) { TCGv spr = tcg_temp_new(); @@ -7704,16 +7699,16 @@ static void gen_spr_power6_common(CPUPPCState *env) 0x00000000); } -static void spr_read_tar(void *opaque, int gprn, int sprn) +static void spr_read_tar(DisasContext *ctx, int gprn, int sprn) { - gen_fscr_facility_check(opaque, SPR_FSCR, FSCR_TAR, sprn, FSCR_IC_TAR); - spr_read_generic(opaque, gprn, sprn); + gen_fscr_facility_check(ctx, SPR_FSCR, FSCR_TAR, sprn, FSCR_IC_TAR); + spr_read_generic(ctx, gprn, sprn); } -static void spr_write_tar(void *opaque, int sprn, int gprn) +static void spr_write_tar(DisasContext *ctx, int sprn, int gprn) { - gen_fscr_facility_check(opaque, SPR_FSCR, FSCR_TAR, sprn, FSCR_IC_TAR); - spr_write_generic(opaque, sprn, gprn); + gen_fscr_facility_check(ctx, SPR_FSCR, FSCR_TAR, sprn, FSCR_IC_TAR); + spr_write_generic(ctx, sprn, gprn); } static void gen_spr_power8_tce_address_control(CPUPPCState *env) @@ -7724,28 +7719,28 @@ static void gen_spr_power8_tce_address_control(CPUPPCState *env) 0x00000000); } -static void spr_read_tm(void *opaque, int gprn, int sprn) +static void spr_read_tm(DisasContext *ctx, int gprn, int sprn) { - gen_msr_facility_check(opaque, SPR_FSCR, MSR_TM, sprn, FSCR_IC_TM); - spr_read_generic(opaque, gprn, sprn); + gen_msr_facility_check(ctx, SPR_FSCR, MSR_TM, sprn, FSCR_IC_TM); + spr_read_generic(ctx, gprn, sprn); } -static void spr_write_tm(void *opaque, int sprn, int gprn) +static void spr_write_tm(DisasContext *ctx, int sprn, int gprn) { - gen_msr_facility_check(opaque, SPR_FSCR, MSR_TM, sprn, FSCR_IC_TM); - spr_write_generic(opaque, sprn, gprn); + gen_msr_facility_check(ctx, SPR_FSCR, MSR_TM, sprn, FSCR_IC_TM); + spr_write_generic(ctx, sprn, gprn); } -static void spr_read_tm_upper32(void *opaque, int gprn, int sprn) +static void spr_read_tm_upper32(DisasContext *ctx, int gprn, int sprn) { - gen_msr_facility_check(opaque, SPR_FSCR, MSR_TM, sprn, FSCR_IC_TM); - spr_read_prev_upper32(opaque, gprn, sprn); + gen_msr_facility_check(ctx, SPR_FSCR, MSR_TM, sprn, FSCR_IC_TM); + spr_read_prev_upper32(ctx, gprn, sprn); } -static void spr_write_tm_upper32(void *opaque, int sprn, int gprn) +static void spr_write_tm_upper32(DisasContext *ctx, int sprn, int gprn) { - gen_msr_facility_check(opaque, SPR_FSCR, MSR_TM, sprn, FSCR_IC_TM); - spr_write_prev_upper32(opaque, sprn, gprn); + gen_msr_facility_check(ctx, SPR_FSCR, MSR_TM, sprn, FSCR_IC_TM); + spr_write_prev_upper32(ctx, sprn, gprn); } static void gen_spr_power8_tm(CPUPPCState *env) @@ -7768,28 +7763,28 @@ static void gen_spr_power8_tm(CPUPPCState *env) 0x00000000); } -static void spr_read_ebb(void *opaque, int gprn, int sprn) +static void spr_read_ebb(DisasContext *ctx, int gprn, int sprn) { - gen_fscr_facility_check(opaque, SPR_FSCR, FSCR_EBB, sprn, FSCR_IC_EBB); - spr_read_generic(opaque, gprn, sprn); + gen_fscr_facility_check(ctx, SPR_FSCR, FSCR_EBB, sprn, FSCR_IC_EBB); + spr_read_generic(ctx, gprn, sprn); } -static void spr_write_ebb(void *opaque, int sprn, int gprn) +static void spr_write_ebb(DisasContext *ctx, int sprn, int gprn) { - gen_fscr_facility_check(opaque, SPR_FSCR, FSCR_EBB, sprn, FSCR_IC_EBB); - spr_write_generic(opaque, sprn, gprn); + gen_fscr_facility_check(ctx, SPR_FSCR, FSCR_EBB, sprn, FSCR_IC_EBB); + spr_write_generic(ctx, sprn, gprn); } -static void spr_read_ebb_upper32(void *opaque, int gprn, int sprn) +static void spr_read_ebb_upper32(DisasContext *ctx, int gprn, int sprn) { - gen_fscr_facility_check(opaque, SPR_FSCR, FSCR_EBB, sprn, FSCR_IC_EBB); - spr_read_prev_upper32(opaque, gprn, sprn); + gen_fscr_facility_check(ctx, SPR_FSCR, FSCR_EBB, sprn, FSCR_IC_EBB); + spr_read_prev_upper32(ctx, gprn, sprn); } -static void spr_write_ebb_upper32(void *opaque, int sprn, int gprn) +static void spr_write_ebb_upper32(DisasContext *ctx, int sprn, int gprn) { - gen_fscr_facility_check(opaque, SPR_FSCR, FSCR_EBB, sprn, FSCR_IC_EBB); - spr_write_prev_upper32(opaque, sprn, gprn); + gen_fscr_facility_check(ctx, SPR_FSCR, FSCR_EBB, sprn, FSCR_IC_EBB); + spr_write_prev_upper32(ctx, sprn, gprn); } static void gen_spr_power8_ebb(CPUPPCState *env) @@ -7824,6 +7819,15 @@ static void gen_spr_power8_ebb(CPUPPCState *env) KVM_REG_PPC_BESCR, 0x00000000); } +/* Virtual Time Base */ +static void gen_spr_vtb(CPUPPCState *env) +{ + spr_register(env, SPR_VTB, "VTB", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_tbl, SPR_NOACCESS, + 0x00000000); +} + static void gen_spr_power8_fscr(CPUPPCState *env) { #if defined(CONFIG_USER_ONLY) @@ -7886,6 +7890,7 @@ static void init_proc_book3s_64(CPUPPCState *env, int version) gen_spr_power8_pmu_sup(env); gen_spr_power8_pmu_user(env); gen_spr_power8_tm(env); + gen_spr_vtb(env); } if (version < BOOK3S_CPU_POWER8) { gen_spr_book3s_dbg(env); @@ -8219,7 +8224,8 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) PPC2_ATOMIC_ISA206 | PPC2_FP_CVT_ISA206 | PPC2_FP_TST_ISA206 | PPC2_BCTAR_ISA207 | PPC2_LSQ_ISA207 | PPC2_ALTIVEC_207 | - PPC2_ISA205 | PPC2_ISA207S | PPC2_FP_CVT_S64; + PPC2_ISA205 | PPC2_ISA207S | PPC2_FP_CVT_S64 | + PPC2_TM; pcc->msr_mask = (1ull << MSR_SF) | (1ull << MSR_TM) | (1ull << MSR_VR) | @@ -8247,7 +8253,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | POWERPC_FLAG_BE | POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK | POWERPC_FLAG_CFAR | - POWERPC_FLAG_VSX; + POWERPC_FLAG_VSX | POWERPC_FLAG_TM; pcc->l1_dcache_size = 0x8000; pcc->l1_icache_size = 0x8000; pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr; diff --git a/target-s390x/Makefile.objs b/target-s390x/Makefile.objs index 2c57494..dd62cbd 100644 --- a/target-s390x/Makefile.objs +++ b/target-s390x/Makefile.objs @@ -1,5 +1,5 @@ obj-y += translate.o helper.o cpu.o interrupt.o obj-y += int_helper.o fpu_helper.o cc_helper.o mem_helper.o misc_helper.o obj-y += gdbstub.o -obj-$(CONFIG_SOFTMMU) += machine.o ioinst.o arch_dump.o +obj-$(CONFIG_SOFTMMU) += machine.o ioinst.o arch_dump.o mmu_helper.o obj-$(CONFIG_KVM) += kvm.o diff --git a/target-s390x/cc_helper.c b/target-s390x/cc_helper.c index 373eb17..00bc883 100644 --- a/target-s390x/cc_helper.c +++ b/target-s390x/cc_helper.c @@ -179,16 +179,11 @@ static uint32_t cc_calc_subu_64(uint64_t a1, uint64_t a2, uint64_t ar) static uint32_t cc_calc_subb_64(uint64_t a1, uint64_t a2, uint64_t ar) { - /* We had borrow-in if normal subtraction isn't equal. */ - int borrow_in = ar - (a1 - a2); int borrow_out; - /* If a2 was ULONG_MAX, and borrow_in, then a2 is logically 65 bits, - and we must have had borrow out. */ - if (borrow_in && a2 == (uint64_t)-1) { - borrow_out = 1; + if (ar != a1 - a2) { /* difference means borrow-in */ + borrow_out = (a2 >= a1); } else { - a2 += borrow_in; borrow_out = (a2 > a1); } @@ -285,16 +280,11 @@ static uint32_t cc_calc_subu_32(uint32_t a1, uint32_t a2, uint32_t ar) static uint32_t cc_calc_subb_32(uint32_t a1, uint32_t a2, uint32_t ar) { - /* We had borrow-in if normal subtraction isn't equal. */ - int borrow_in = ar - (a1 - a2); int borrow_out; - /* If a2 was UINT_MAX, and borrow_in, then a2 is logically 65 bits, - and we must have had borrow out. */ - if (borrow_in && a2 == (uint32_t)-1) { - borrow_out = 1; + if (ar != a1 - a2) { /* difference means borrow-in */ + borrow_out = (a2 >= a1); } else { - a2 += borrow_in; borrow_out = (a2 > a1); } diff --git a/target-s390x/cpu.c b/target-s390x/cpu.c index d2f6312..e0537fa 100644 --- a/target-s390x/cpu.c +++ b/target-s390x/cpu.c @@ -96,6 +96,7 @@ static void s390_cpu_reset(CPUState *s) env->pfault_token = -1UL; scc->parent_reset(s); + cpu->env.sigp_order = 0; s390_cpu_set_state(CPU_STATE_STOPPED, cpu); tlb_flush(s, 1); } @@ -131,6 +132,7 @@ static void s390_cpu_full_reset(CPUState *s) CPUS390XState *env = &cpu->env; scc->parent_reset(s); + cpu->env.sigp_order = 0; s390_cpu_set_state(CPU_STATE_STOPPED, cpu); memset(env, 0, offsetof(CPUS390XState, cpu_num)); diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h index fe2f95d..8135dda 100644 --- a/target-s390x/cpu.h +++ b/target-s390x/cpu.h @@ -133,7 +133,9 @@ typedef struct CPUS390XState { /* reset does memset(0) up to here */ - int cpu_num; + uint32_t cpu_num; + uint32_t machine_type; + uint8_t *storage_keys; uint64_t tod_offset; @@ -155,6 +157,9 @@ typedef struct CPUS390XState { #define CPU_STATE_LOAD 0x04 uint8_t cpu_state; + /* currently processed sigp order */ + uint8_t sigp_order; + } CPUS390XState; #include "cpu-qom.h" @@ -285,6 +290,7 @@ typedef struct CPUS390XState { #define FLAG_MASK_32 0x00001000 /* Control register 0 bits */ +#define CR0_LOWPROT 0x0000000010000000ULL #define CR0_EDAT 0x0000000000800000ULL static inline int cpu_mmu_index (CPUS390XState *env) @@ -329,6 +335,7 @@ static inline int get_ilen(uint8_t opc) to re-compute the length by examining the insn in memory. */ #define ILEN_LATER 0x20 #define ILEN_LATER_INC 0x21 +void trigger_pgm_exception(CPUS390XState *env, uint32_t code, uint32_t ilen); #endif S390CPU *cpu_s390x_init(const char *cpu_model); @@ -345,11 +352,10 @@ int s390_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int rw, #include "ioinst.h" + #ifndef CONFIG_USER_ONLY -void *s390_cpu_physical_memory_map(CPUS390XState *env, hwaddr addr, hwaddr *len, - int is_write); -void s390_cpu_physical_memory_unmap(CPUS390XState *env, void *addr, hwaddr len, - int is_write); +void do_restart_interrupt(CPUS390XState *env); + static inline hwaddr decode_basedisp_s(CPUS390XState *env, uint32_t ipb) { hwaddr addr = 0; @@ -394,6 +400,9 @@ void kvm_s390_service_interrupt(uint32_t parm); void kvm_s390_vcpu_interrupt(S390CPU *cpu, struct kvm_s390_irq *irq); void kvm_s390_floating_interrupt(struct kvm_s390_irq *irq); int kvm_s390_inject_flic(struct kvm_s390_irq *irq); +void kvm_s390_access_exception(S390CPU *cpu, uint16_t code, uint64_t te_code); +int kvm_s390_get_clock(uint8_t *tod_high, uint64_t *tod_clock); +int kvm_s390_set_clock(uint8_t *tod_high, uint64_t *tod_clock); #else static inline void kvm_s390_virtio_irq(int config_change, uint64_t token) { @@ -401,11 +410,51 @@ static inline void kvm_s390_virtio_irq(int config_change, uint64_t token) static inline void kvm_s390_service_interrupt(uint32_t parm) { } +static inline int kvm_s390_get_clock(uint8_t *tod_high, uint64_t *tod_low) +{ + return -ENOSYS; +} +static inline int kvm_s390_set_clock(uint8_t *tod_high, uint64_t *tod_low) +{ + return -ENOSYS; +} +static inline void kvm_s390_access_exception(S390CPU *cpu, uint16_t code, + uint64_t te_code) +{ +} #endif + +static inline int s390_get_clock(uint8_t *tod_high, uint64_t *tod_low) +{ + if (kvm_enabled()) { + return kvm_s390_get_clock(tod_high, tod_low); + } + /* Fixme TCG */ + *tod_high = 0; + *tod_low = 0; + return 0; +} + +static inline int s390_set_clock(uint8_t *tod_high, uint64_t *tod_low) +{ + if (kvm_enabled()) { + return kvm_s390_set_clock(tod_high, tod_low); + } + /* Fixme TCG */ + return 0; +} + S390CPU *s390_cpu_addr2state(uint16_t cpu_addr); unsigned int s390_cpu_halt(S390CPU *cpu); void s390_cpu_unhalt(S390CPU *cpu); unsigned int s390_cpu_set_state(uint8_t cpu_state, S390CPU *cpu); +static inline uint8_t s390_cpu_get_state(S390CPU *cpu) +{ + return cpu->env.cpu_state; +} + +void gtod_save(QEMUFile *f, void *opaque); +int gtod_load(QEMUFile *f, void *opaque, int version_id); /* service interrupts are floating therefore we must not pass an cpustate */ void s390_sclp_extint(uint32_t parm); @@ -441,13 +490,15 @@ bool css_subch_visible(SubchDev *sch); void css_conditional_io_interrupt(SubchDev *sch); int css_do_stsch(SubchDev *sch, SCHIB *schib); bool css_schid_final(int m, uint8_t cssid, uint8_t ssid, uint16_t schid); -int css_do_msch(SubchDev *sch, SCHIB *schib); +int css_do_msch(SubchDev *sch, const SCHIB *schib); int css_do_xsch(SubchDev *sch); int css_do_csch(SubchDev *sch); int css_do_hsch(SubchDev *sch); int css_do_ssch(SubchDev *sch, ORB *orb); -int css_do_tsch(SubchDev *sch, IRB *irb); +int css_do_tsch_get_irb(SubchDev *sch, IRB *irb, int *irb_len); +void css_do_tsch_update_subch(SubchDev *sch); int css_do_stcrw(CRW *crw); +void css_undo_stcrw(CRW *crw); int css_do_tpi(IOIntCode *int_code, int lowcore); int css_collect_chp_desc(int m, uint8_t cssid, uint8_t f_chpid, uint8_t l_chpid, int rfmt, void *buf); @@ -457,90 +508,9 @@ int css_enable_mss(void); int css_do_rsch(SubchDev *sch); int css_do_rchp(uint8_t cssid, uint8_t chpid); bool css_present(uint8_t cssid); -#else -static inline SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid, - uint16_t schid) -{ - return NULL; -} -static inline bool css_subch_visible(SubchDev *sch) -{ - return false; -} -static inline void css_conditional_io_interrupt(SubchDev *sch) -{ -} -static inline int css_do_stsch(SubchDev *sch, SCHIB *schib) -{ - return -ENODEV; -} -static inline bool css_schid_final(uint8_t cssid, uint8_t ssid, uint16_t schid) -{ - return true; -} -static inline int css_do_msch(SubchDev *sch, SCHIB *schib) -{ - return -ENODEV; -} -static inline int css_do_xsch(SubchDev *sch) -{ - return -ENODEV; -} -static inline int css_do_csch(SubchDev *sch) -{ - return -ENODEV; -} -static inline int css_do_hsch(SubchDev *sch) -{ - return -ENODEV; -} -static inline int css_do_ssch(SubchDev *sch, ORB *orb) -{ - return -ENODEV; -} -static inline int css_do_tsch(SubchDev *sch, IRB *irb) -{ - return -ENODEV; -} -static inline int css_do_stcrw(CRW *crw) -{ - return 1; -} -static inline int css_do_tpi(IOIntCode *int_code, int lowcore) -{ - return 0; -} -static inline int css_collect_chp_desc(int m, uint8_t cssid, uint8_t f_chpid, - int rfmt, uint8_t l_chpid, void *buf) -{ - return 0; -} -static inline void css_do_schm(uint8_t mbk, int update, int dct, uint64_t mbo) -{ -} -static inline int css_enable_mss(void) -{ - return -EINVAL; -} -static inline int css_enable_mcsse(void) -{ - return -EINVAL; -} -static inline int css_do_rsch(SubchDev *sch) -{ - return -ENODEV; -} -static inline int css_do_rchp(uint8_t cssid, uint8_t chpid) -{ - return -ENODEV; -} -static inline bool css_present(uint8_t cssid) -{ - return false; -} #endif -#define cpu_init(model) (&cpu_s390x_init(model)->env) +#define cpu_init(model) CPU(cpu_s390x_init(model)) #define cpu_exec cpu_s390x_exec #define cpu_gen_code cpu_s390x_gen_code #define cpu_signal_handler cpu_s390x_signal_handler @@ -738,7 +708,7 @@ typedef struct LowCore PSW mcck_old_psw; /* 0x160 */ PSW io_old_psw; /* 0x170 */ uint8_t pad7[0x1a0-0x180]; /* 0x180 */ - PSW restart_psw; /* 0x1a0 */ + PSW restart_new_psw; /* 0x1a0 */ PSW external_new_psw; /* 0x1b0 */ PSW svc_new_psw; /* 0x1c0 */ PSW program_new_psw; /* 0x1d0 */ @@ -915,6 +885,8 @@ struct sysib_322 { #define _ASCE_TABLE_LENGTH 0x03 /* region table length */ #define _REGION_ENTRY_ORIGIN ~0xfffULL /* region/segment table origin */ +#define _REGION_ENTRY_RO 0x200 /* region/segment protection bit */ +#define _REGION_ENTRY_TF 0xc0 /* region/segment table offset */ #define _REGION_ENTRY_INV 0x20 /* invalid region table entry */ #define _REGION_ENTRY_TYPE_MASK 0x0c /* region/segment table type mask */ #define _REGION_ENTRY_TYPE_R1 0x0c /* region first table type */ @@ -929,12 +901,14 @@ struct sysib_322 { #define _PAGE_RO 0x200 /* HW read-only bit */ #define _PAGE_INVALID 0x400 /* HW invalid bit */ +#define _PAGE_RES0 0x800 /* bit must be zero */ #define SK_C (0x1 << 1) #define SK_R (0x1 << 2) #define SK_F (0x1 << 3) #define SK_ACC_MASK (0xf << 4) +/* SIGP order codes */ #define SIGP_SENSE 0x01 #define SIGP_EXTERNAL_CALL 0x02 #define SIGP_EMERGENCY 0x03 @@ -948,7 +922,13 @@ struct sysib_322 { #define SIGP_STORE_STATUS_ADDR 0x0e #define SIGP_SET_ARCH 0x12 -/* cpu status bits */ +/* SIGP condition codes */ +#define SIGP_CC_ORDER_CODE_ACCEPTED 0 +#define SIGP_CC_STATUS_STORED 1 +#define SIGP_CC_BUSY 2 +#define SIGP_CC_NOT_OPERATIONAL 3 + +/* SIGP status bits */ #define SIGP_STAT_EQUIPMENT_CHECK 0x80000000UL #define SIGP_STAT_INCORRECT_STATE 0x00000200UL #define SIGP_STAT_INVALID_PARAMETER 0x00000100UL @@ -960,14 +940,27 @@ struct sysib_322 { #define SIGP_STAT_INVALID_ORDER 0x00000002UL #define SIGP_STAT_RECEIVER_CHECK 0x00000001UL +/* SIGP SET ARCHITECTURE modes */ +#define SIGP_MODE_ESA_S390 0 +#define SIGP_MODE_Z_ARCH_TRANS_ALL_PSW 1 +#define SIGP_MODE_Z_ARCH_TRANS_CUR_PSW 2 + void load_psw(CPUS390XState *env, uint64_t mask, uint64_t addr); int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc, - target_ulong *raddr, int *flags); + target_ulong *raddr, int *flags, bool exc); int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code); uint32_t calc_cc(CPUS390XState *env, uint32_t cc_op, uint64_t src, uint64_t dst, uint64_t vr); -#define TARGET_HAS_ICE 1 +int s390_cpu_virt_mem_rw(S390CPU *cpu, vaddr laddr, void *hostbuf, int len, + bool is_write); + +#define s390_cpu_virt_mem_read(cpu, laddr, dest, len) \ + s390_cpu_virt_mem_rw(cpu, laddr, dest, len, false) +#define s390_cpu_virt_mem_write(cpu, laddr, dest, len) \ + s390_cpu_virt_mem_rw(cpu, laddr, dest, len, true) +#define s390_cpu_virt_mem_check_write(cpu, laddr, len) \ + s390_cpu_virt_mem_rw(cpu, laddr, NULL, len, true) /* The value of the TOD clock for 1.1.1970. */ #define TOD_UNIX_EPOCH 0x7d91048bca000000ULL @@ -1070,6 +1063,7 @@ int kvm_s390_get_memslot_count(KVMState *s); void kvm_s390_clear_cmma_callback(void *opaque); int kvm_s390_set_cpu_state(S390CPU *cpu, uint8_t cpu_state); void kvm_s390_reset_vcpu(S390CPU *cpu); +int kvm_s390_set_mem_limit(KVMState *s, uint64_t new_limit, uint64_t *hw_limit); #else static inline void kvm_s390_io_interrupt(uint16_t subchannel_id, uint16_t subchannel_nr, @@ -1107,8 +1101,21 @@ static inline int kvm_s390_set_cpu_state(S390CPU *cpu, uint8_t cpu_state) static inline void kvm_s390_reset_vcpu(S390CPU *cpu) { } +static inline int kvm_s390_set_mem_limit(KVMState *s, uint64_t new_limit, + uint64_t *hw_limit) +{ + return 0; +} #endif +static inline int s390_set_memory_limit(uint64_t new_limit, uint64_t *hw_limit) +{ + if (kvm_enabled()) { + return kvm_s390_set_mem_limit(kvm_state, new_limit, hw_limit); + } + return 0; +} + static inline void cmma_reset(S390CPU *cpu) { if (kvm_enabled()) { diff --git a/target-s390x/helper.c b/target-s390x/helper.c index 09aec7b..f1060c2 100644 --- a/target-s390x/helper.c +++ b/target-s390x/helper.c @@ -27,7 +27,6 @@ #endif //#define DEBUG_S390 -//#define DEBUG_S390_PTE //#define DEBUG_S390_STDOUT #ifdef DEBUG_S390 @@ -44,12 +43,6 @@ do { } while (0) #endif -#ifdef DEBUG_S390_PTE -#define PTE_DPRINTF DPRINTF -#else -#define PTE_DPRINTF(fmt, ...) \ - do { } while (0) -#endif #ifndef CONFIG_USER_ONLY void s390x_tod_timer(void *opaque) @@ -105,8 +98,7 @@ int s390_cpu_handle_mmu_fault(CPUState *cs, vaddr address, #else /* !CONFIG_USER_ONLY */ /* Ensure to exit the TB after this call! */ -static void trigger_pgm_exception(CPUS390XState *env, uint32_t code, - uint32_t ilen) +void trigger_pgm_exception(CPUS390XState *env, uint32_t code, uint32_t ilen) { CPUState *cs = CPU(s390_env_get_cpu(env)); @@ -115,319 +107,6 @@ static void trigger_pgm_exception(CPUS390XState *env, uint32_t code, env->int_pgm_ilen = ilen; } -static int trans_bits(CPUS390XState *env, uint64_t mode) -{ - S390CPU *cpu = s390_env_get_cpu(env); - int bits = 0; - - switch (mode) { - case PSW_ASC_PRIMARY: - bits = 1; - break; - case PSW_ASC_SECONDARY: - bits = 2; - break; - case PSW_ASC_HOME: - bits = 3; - break; - default: - cpu_abort(CPU(cpu), "unknown asc mode\n"); - break; - } - - return bits; -} - -static void trigger_prot_fault(CPUS390XState *env, target_ulong vaddr, - uint64_t mode) -{ - CPUState *cs = CPU(s390_env_get_cpu(env)); - int ilen = ILEN_LATER_INC; - int bits = trans_bits(env, mode) | 4; - - DPRINTF("%s: vaddr=%016" PRIx64 " bits=%d\n", __func__, vaddr, bits); - - stq_phys(cs->as, - env->psa + offsetof(LowCore, trans_exc_code), vaddr | bits); - trigger_pgm_exception(env, PGM_PROTECTION, ilen); -} - -static void trigger_page_fault(CPUS390XState *env, target_ulong vaddr, - uint32_t type, uint64_t asc, int rw) -{ - CPUState *cs = CPU(s390_env_get_cpu(env)); - int ilen = ILEN_LATER; - int bits = trans_bits(env, asc); - - /* Code accesses have an undefined ilc. */ - if (rw == 2) { - ilen = 2; - } - - DPRINTF("%s: vaddr=%016" PRIx64 " bits=%d\n", __func__, vaddr, bits); - - stq_phys(cs->as, - env->psa + offsetof(LowCore, trans_exc_code), vaddr | bits); - trigger_pgm_exception(env, type, ilen); -} - -/** - * Translate real address to absolute (= physical) - * address by taking care of the prefix mapping. - */ -static target_ulong mmu_real2abs(CPUS390XState *env, target_ulong raddr) -{ - if (raddr < 0x2000) { - return raddr + env->psa; /* Map the lowcore. */ - } else if (raddr >= env->psa && raddr < env->psa + 0x2000) { - return raddr - env->psa; /* Map the 0 page. */ - } - return raddr; -} - -/* Decode page table entry (normal 4KB page) */ -static int mmu_translate_pte(CPUS390XState *env, target_ulong vaddr, - uint64_t asc, uint64_t asce, - target_ulong *raddr, int *flags, int rw) -{ - if (asce & _PAGE_INVALID) { - DPRINTF("%s: PTE=0x%" PRIx64 " invalid\n", __func__, asce); - trigger_page_fault(env, vaddr, PGM_PAGE_TRANS, asc, rw); - return -1; - } - - if (asce & _PAGE_RO) { - *flags &= ~PAGE_WRITE; - } - - *raddr = asce & _ASCE_ORIGIN; - - PTE_DPRINTF("%s: PTE=0x%" PRIx64 "\n", __func__, asce); - - return 0; -} - -/* Decode EDAT1 segment frame absolute address (1MB page) */ -static int mmu_translate_sfaa(CPUS390XState *env, target_ulong vaddr, - uint64_t asc, uint64_t asce, target_ulong *raddr, - int *flags, int rw) -{ - if (asce & _SEGMENT_ENTRY_INV) { - DPRINTF("%s: SEG=0x%" PRIx64 " invalid\n", __func__, asce); - trigger_page_fault(env, vaddr, PGM_SEGMENT_TRANS, asc, rw); - return -1; - } - - if (asce & _SEGMENT_ENTRY_RO) { - *flags &= ~PAGE_WRITE; - } - - *raddr = (asce & 0xfffffffffff00000ULL) | (vaddr & 0xfffff); - - PTE_DPRINTF("%s: SEG=0x%" PRIx64 "\n", __func__, asce); - - return 0; -} - -static int mmu_translate_asce(CPUS390XState *env, target_ulong vaddr, - uint64_t asc, uint64_t asce, int level, - target_ulong *raddr, int *flags, int rw) -{ - CPUState *cs = CPU(s390_env_get_cpu(env)); - uint64_t offs = 0; - uint64_t origin; - uint64_t new_asce; - - PTE_DPRINTF("%s: 0x%" PRIx64 "\n", __func__, asce); - - if (((level != _ASCE_TYPE_SEGMENT) && (asce & _REGION_ENTRY_INV)) || - ((level == _ASCE_TYPE_SEGMENT) && (asce & _SEGMENT_ENTRY_INV))) { - /* XXX different regions have different faults */ - DPRINTF("%s: invalid region\n", __func__); - trigger_page_fault(env, vaddr, PGM_SEGMENT_TRANS, asc, rw); - return -1; - } - - if ((level <= _ASCE_TYPE_MASK) && ((asce & _ASCE_TYPE_MASK) != level)) { - trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw); - return -1; - } - - if (asce & _ASCE_REAL_SPACE) { - /* direct mapping */ - - *raddr = vaddr; - return 0; - } - - origin = asce & _ASCE_ORIGIN; - - switch (level) { - case _ASCE_TYPE_REGION1 + 4: - offs = (vaddr >> 50) & 0x3ff8; - break; - case _ASCE_TYPE_REGION1: - offs = (vaddr >> 39) & 0x3ff8; - break; - case _ASCE_TYPE_REGION2: - offs = (vaddr >> 28) & 0x3ff8; - break; - case _ASCE_TYPE_REGION3: - offs = (vaddr >> 17) & 0x3ff8; - break; - case _ASCE_TYPE_SEGMENT: - offs = (vaddr >> 9) & 0x07f8; - origin = asce & _SEGMENT_ENTRY_ORIGIN; - break; - } - - /* XXX region protection flags */ - /* *flags &= ~PAGE_WRITE */ - - new_asce = ldq_phys(cs->as, origin + offs); - PTE_DPRINTF("%s: 0x%" PRIx64 " + 0x%" PRIx64 " => 0x%016" PRIx64 "\n", - __func__, origin, offs, new_asce); - - if (level == _ASCE_TYPE_SEGMENT) { - /* 4KB page */ - return mmu_translate_pte(env, vaddr, asc, new_asce, raddr, flags, rw); - } else if (level - 4 == _ASCE_TYPE_SEGMENT && - (new_asce & _SEGMENT_ENTRY_FC) && (env->cregs[0] & CR0_EDAT)) { - /* 1MB page */ - return mmu_translate_sfaa(env, vaddr, asc, new_asce, raddr, flags, rw); - } else { - /* yet another region */ - return mmu_translate_asce(env, vaddr, asc, new_asce, level - 4, raddr, - flags, rw); - } -} - -static int mmu_translate_asc(CPUS390XState *env, target_ulong vaddr, - uint64_t asc, target_ulong *raddr, int *flags, - int rw) -{ - uint64_t asce = 0; - int level, new_level; - int r; - - switch (asc) { - case PSW_ASC_PRIMARY: - PTE_DPRINTF("%s: asc=primary\n", __func__); - asce = env->cregs[1]; - break; - case PSW_ASC_SECONDARY: - PTE_DPRINTF("%s: asc=secondary\n", __func__); - asce = env->cregs[7]; - break; - case PSW_ASC_HOME: - PTE_DPRINTF("%s: asc=home\n", __func__); - asce = env->cregs[13]; - break; - } - - switch (asce & _ASCE_TYPE_MASK) { - case _ASCE_TYPE_REGION1: - break; - case _ASCE_TYPE_REGION2: - if (vaddr & 0xffe0000000000000ULL) { - DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64 - " 0xffe0000000000000ULL\n", __func__, vaddr); - trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw); - return -1; - } - break; - case _ASCE_TYPE_REGION3: - if (vaddr & 0xfffffc0000000000ULL) { - DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64 - " 0xfffffc0000000000ULL\n", __func__, vaddr); - trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw); - return -1; - } - break; - case _ASCE_TYPE_SEGMENT: - if (vaddr & 0xffffffff80000000ULL) { - DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64 - " 0xffffffff80000000ULL\n", __func__, vaddr); - trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw); - return -1; - } - break; - } - - /* fake level above current */ - level = asce & _ASCE_TYPE_MASK; - new_level = level + 4; - asce = (asce & ~_ASCE_TYPE_MASK) | (new_level & _ASCE_TYPE_MASK); - - r = mmu_translate_asce(env, vaddr, asc, asce, new_level, raddr, flags, rw); - - if ((rw == 1) && !(*flags & PAGE_WRITE)) { - trigger_prot_fault(env, vaddr, asc); - return -1; - } - - return r; -} - -int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc, - target_ulong *raddr, int *flags) -{ - int r = -1; - uint8_t *sk; - - *flags = PAGE_READ | PAGE_WRITE | PAGE_EXEC; - vaddr &= TARGET_PAGE_MASK; - - if (!(env->psw.mask & PSW_MASK_DAT)) { - *raddr = vaddr; - r = 0; - goto out; - } - - switch (asc) { - case PSW_ASC_PRIMARY: - case PSW_ASC_HOME: - r = mmu_translate_asc(env, vaddr, asc, raddr, flags, rw); - break; - case PSW_ASC_SECONDARY: - /* - * Instruction: Primary - * Data: Secondary - */ - if (rw == 2) { - r = mmu_translate_asc(env, vaddr, PSW_ASC_PRIMARY, raddr, flags, - rw); - *flags &= ~(PAGE_READ | PAGE_WRITE); - } else { - r = mmu_translate_asc(env, vaddr, PSW_ASC_SECONDARY, raddr, flags, - rw); - *flags &= ~(PAGE_EXEC); - } - break; - case PSW_ASC_ACCREG: - default: - hw_error("guest switched to unknown asc mode\n"); - break; - } - - out: - /* Convert real address -> absolute address */ - *raddr = mmu_real2abs(env, *raddr); - - if (*raddr <= ram_size) { - sk = &env->storage_keys[*raddr / TARGET_PAGE_SIZE]; - if (*flags & PAGE_READ) { - *sk |= SK_R; - } - - if (*flags & PAGE_WRITE) { - *sk |= SK_C; - } - } - - return r; -} - int s390_cpu_handle_mmu_fault(CPUState *cs, vaddr orig_vaddr, int rw, int mmu_idx) { @@ -448,7 +127,7 @@ int s390_cpu_handle_mmu_fault(CPUState *cs, vaddr orig_vaddr, vaddr &= 0x7fffffff; } - if (mmu_translate(env, vaddr, rw, asc, &raddr, &prot)) { + if (mmu_translate(env, vaddr, rw, asc, &raddr, &prot, true)) { /* Translation ended in exception */ return 1; } @@ -461,8 +140,8 @@ int s390_cpu_handle_mmu_fault(CPUState *cs, vaddr orig_vaddr, return 1; } - DPRINTF("%s: set tlb %" PRIx64 " -> %" PRIx64 " (%x)\n", __func__, - (uint64_t)vaddr, (uint64_t)raddr, prot); + qemu_log_mask(CPU_LOG_MMU, "%s: set tlb %" PRIx64 " -> %" PRIx64 " (%x)\n", + __func__, (uint64_t)vaddr, (uint64_t)raddr, prot); tlb_set_page(cs, orig_vaddr, raddr, prot, mmu_idx, TARGET_PAGE_SIZE); @@ -475,8 +154,7 @@ hwaddr s390_cpu_get_phys_page_debug(CPUState *cs, vaddr vaddr) S390CPU *cpu = S390_CPU(cs); CPUS390XState *env = &cpu->env; target_ulong raddr; - int prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; - int old_exc = cs->exception_index; + int prot; uint64_t asc = env->psw.mask & PSW_MASK_ASC; /* 31-Bit mode */ @@ -484,8 +162,7 @@ hwaddr s390_cpu_get_phys_page_debug(CPUState *cs, vaddr vaddr) vaddr &= 0x7fffffff; } - mmu_translate(env, vaddr, 2, asc, &raddr, &prot); - cs->exception_index = old_exc; + mmu_translate(env, vaddr, 2, asc, &raddr, &prot, false); return raddr; } @@ -506,7 +183,9 @@ void load_psw(CPUS390XState *env, uint64_t mask, uint64_t addr) { env->psw.addr = addr; env->psw.mask = mask; - env->cc_op = (mask >> 44) & 3; + if (tcg_enabled()) { + env->cc_op = (mask >> 44) & 3; + } if (mask & PSW_MASK_WAIT) { S390CPU *cpu = s390_env_get_cpu(env); @@ -520,14 +199,16 @@ void load_psw(CPUS390XState *env, uint64_t mask, uint64_t addr) static uint64_t get_psw_mask(CPUS390XState *env) { - uint64_t r; + uint64_t r = env->psw.mask; - env->cc_op = calc_cc(env, env->cc_op, env->cc_src, env->cc_dst, env->cc_vr); + if (tcg_enabled()) { + env->cc_op = calc_cc(env, env->cc_op, env->cc_src, env->cc_dst, + env->cc_vr); - r = env->psw.mask; - r &= ~PSW_MASK_CC; - assert(!(env->cc_op & ~3)); - r |= (uint64_t)env->cc_op << 44; + r &= ~PSW_MASK_CC; + assert(!(env->cc_op & ~3)); + r |= (uint64_t)env->cc_op << 44; + } return r; } @@ -552,29 +233,21 @@ static void cpu_unmap_lowcore(LowCore *lowcore) cpu_physical_memory_unmap(lowcore, sizeof(LowCore), 1, sizeof(LowCore)); } -void *s390_cpu_physical_memory_map(CPUS390XState *env, hwaddr addr, hwaddr *len, - int is_write) +void do_restart_interrupt(CPUS390XState *env) { - hwaddr start = addr; - - /* Mind the prefix area. */ - if (addr < 8192) { - /* Map the lowcore. */ - start += env->psa; - *len = MIN(*len, 8192 - addr); - } else if ((addr >= env->psa) && (addr < env->psa + 8192)) { - /* Map the 0 page. */ - start -= env->psa; - *len = MIN(*len, 8192 - start); - } + uint64_t mask, addr; + LowCore *lowcore; - return cpu_physical_memory_map(start, len, is_write); -} + lowcore = cpu_map_lowcore(env); -void s390_cpu_physical_memory_unmap(CPUS390XState *env, void *addr, hwaddr len, - int is_write) -{ - cpu_physical_memory_unmap(addr, len, is_write, len); + lowcore->restart_old_psw.mask = cpu_to_be64(get_psw_mask(env)); + lowcore->restart_old_psw.addr = cpu_to_be64(env->psw.addr); + mask = be64_to_cpu(lowcore->restart_new_psw.mask); + addr = be64_to_cpu(lowcore->restart_new_psw.addr); + + cpu_unmap_lowcore(lowcore); + + load_psw(env, mask, addr); } static void do_svc_interrupt(CPUS390XState *env) @@ -648,7 +321,7 @@ static void do_ext_interrupt(CPUS390XState *env) cpu_abort(CPU(cpu), "Ext int w/o ext mask\n"); } - if (env->ext_index < 0 || env->ext_index > MAX_EXT_QUEUE) { + if (env->ext_index < 0 || env->ext_index >= MAX_EXT_QUEUE) { cpu_abort(CPU(cpu), "Ext queue overrun: %d\n", env->ext_index); } @@ -696,7 +369,7 @@ static void do_io_interrupt(CPUS390XState *env) if (env->io_index[isc] < 0) { continue; } - if (env->io_index[isc] > MAX_IO_QUEUE) { + if (env->io_index[isc] >= MAX_IO_QUEUE) { cpu_abort(CPU(cpu), "I/O queue overrun for isc %d: %d\n", isc, env->io_index[isc]); } @@ -754,7 +427,7 @@ static void do_mchk_interrupt(CPUS390XState *env) cpu_abort(CPU(cpu), "Machine check w/o mchk mask\n"); } - if (env->mchk_index < 0 || env->mchk_index > MAX_MCHK_QUEUE) { + if (env->mchk_index < 0 || env->mchk_index >= MAX_MCHK_QUEUE) { cpu_abort(CPU(cpu), "Mchk queue overrun: %d\n", env->mchk_index); } diff --git a/target-s390x/helper.h b/target-s390x/helper.h index faebfd9..8d2c859 100644 --- a/target-s390x/helper.h +++ b/target-s390x/helper.h @@ -111,5 +111,8 @@ DEF_HELPER_FLAGS_2(sacf, TCG_CALL_NO_WG, void, env, i64) DEF_HELPER_FLAGS_3(ipte, TCG_CALL_NO_RWG, void, env, i64, i64) DEF_HELPER_FLAGS_1(ptlb, TCG_CALL_NO_RWG, void, env) DEF_HELPER_2(lra, i64, env, i64) +DEF_HELPER_FLAGS_2(lura, TCG_CALL_NO_WG, i64, env, i64) +DEF_HELPER_FLAGS_2(lurag, TCG_CALL_NO_WG, i64, env, i64) DEF_HELPER_FLAGS_3(stura, TCG_CALL_NO_WG, void, env, i64, i64) +DEF_HELPER_FLAGS_3(sturg, TCG_CALL_NO_WG, void, env, i64, i64) #endif diff --git a/target-s390x/insn-data.def b/target-s390x/insn-data.def index 4d2feb6..8d8e47e 100644 --- a/target-s390x/insn-data.def +++ b/target-s390x/insn-data.def @@ -285,8 +285,12 @@ /* EXTRACT ACCESS */ C(0xb24f, EAR, RRE, Z, 0, 0, new, r1_32, ear, 0) +/* EXTRACT CPU ATTRIBUTE */ + C(0xeb4c, ECAG, RSY_a, GIE, 0, a2, r1, 0, ecag, 0) /* EXTRACT FPC */ C(0xb38c, EFPC, RRE, Z, 0, 0, new, r1_32, efpc, 0) +/* EXTRACT PSW */ + C(0xb98d, EPSW, RRE, Z, 0, 0, 0, 0, epsw, 0) /* FIND LEFTMOST ONE */ C(0xb983, FLOGR, RRE, EI, 0, r2_o, r1_P, 0, flogr, 0) @@ -566,6 +570,10 @@ /* SET ACCESS */ C(0xb24e, SAR, RRE, Z, 0, r2_o, 0, 0, sar, 0) +/* SET ADDRESSING MODE */ + D(0x010c, SAM24, E, Z, 0, 0, 0, 0, sam, 0, 0) + D(0x010d, SAM31, E, Z, 0, 0, 0, 0, sam, 0, 1) + D(0x010e, SAM64, E, Z, 0, 0, 0, 0, sam, 0, 3) /* SET FPC */ C(0xb384, SFPC, RRE, Z, 0, r1_o, 0, 0, sfpc, 0) /* SET FPC AND SIGNAL */ @@ -733,6 +741,9 @@ C(0xb100, LRA, RX_a, Z, 0, a2, r1, 0, lra, 0) C(0xe313, LRAY, RXY_a, LD, 0, a2, r1, 0, lra, 0) C(0xe303, LRAG, RXY_a, Z, 0, a2, r1, 0, lra, 0) +/* LOAD USING REAL ADDRESS */ + C(0xb24b, LURA, RRE, Z, 0, r2, new, r1_32, lura, 0) + C(0xb905, LURAG, RRE, Z, 0, r2, r1, 0, lurag, 0) /* MOVE TO PRIMARY */ C(0xda00, MVCP, SS_d, Z, la1, a2, 0, 0, mvcp, 0) /* MOVE TO SECONDARY */ @@ -743,10 +754,6 @@ C(0xb22a, RRBE, RRE, Z, 0, r2_o, 0, 0, rrbe, 0) /* SERVICE CALL LOGICAL PROCESSOR (PV hypercall) */ C(0xb220, SERVC, RRE, Z, r1_o, r2_o, 0, 0, servc, 0) -/* SET ADDRESSING MODE */ - D(0x010c, SAM24, E, Z, 0, 0, 0, 0, sam, 0, 0) - D(0x010d, SAM31, E, Z, 0, 0, 0, 0, sam, 0, 1) - D(0x010e, SAM64, E, Z, 0, 0, 0, 0, sam, 0, 3) /* SET ADDRESS SPACE CONTROL FAST */ C(0xb279, SACF, S, Z, 0, a2, 0, 0, sacf, 0) /* SET CLOCK */ @@ -794,6 +801,7 @@ C(0xad00, STOSM, SI, Z, la1, 0, 0, 0, stnosm, 0) /* STORE USING REAL ADDRESS */ C(0xb246, STURA, RRE, Z, r1_o, r2_o, 0, 0, stura, 0) + C(0xb925, STURG, RRE, Z, r1_o, r2_o, 0, 0, sturg, 0) /* TEST PROTECTION */ C(0xe501, TPROT, SSE, Z, la1, a2, 0, 0, tprot, 0) diff --git a/target-s390x/ioinst.c b/target-s390x/ioinst.c index b8a6486..b00a00c 100644 --- a/target-s390x/ioinst.c +++ b/target-s390x/ioinst.c @@ -1,7 +1,7 @@ /* * I/O instructions for S/390 * - * Copyright 2012 IBM Corp. + * Copyright 2012, 2015 IBM Corp. * Author(s): Cornelia Huck * * This work is licensed under the terms of the GNU GPL, version 2 or (at @@ -14,6 +14,7 @@ #include "cpu.h" #include "ioinst.h" #include "trace.h" +#include "hw/s390x/s390-pci-bus.h" int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid, int *schid) @@ -143,11 +144,10 @@ void ioinst_handle_msch(S390CPU *cpu, uint64_t reg1, uint32_t ipb) { int cssid, ssid, schid, m; SubchDev *sch; - SCHIB *schib; + SCHIB schib; uint64_t addr; int ret = -ENODEV; int cc; - hwaddr len = sizeof(*schib); CPUS390XState *env = &cpu->env; addr = decode_basedisp_s(env, ipb); @@ -155,20 +155,18 @@ void ioinst_handle_msch(S390CPU *cpu, uint64_t reg1, uint32_t ipb) program_interrupt(env, PGM_SPECIFICATION, 2); return; } - schib = s390_cpu_physical_memory_map(env, addr, &len, 0); - if (!schib || len != sizeof(*schib)) { - program_interrupt(env, PGM_ADDRESSING, 2); - goto out; + if (s390_cpu_virt_mem_read(cpu, addr, &schib, sizeof(schib))) { + return; } if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid) || - !ioinst_schib_valid(schib)) { + !ioinst_schib_valid(&schib)) { program_interrupt(env, PGM_OPERAND, 2); - goto out; + return; } trace_ioinst_sch_id("msch", cssid, ssid, schid); sch = css_find_subch(m, cssid, ssid, schid); if (sch && css_subch_visible(sch)) { - ret = css_do_msch(sch, schib); + ret = css_do_msch(sch, &schib); } switch (ret) { case -ENODEV: @@ -185,9 +183,6 @@ void ioinst_handle_msch(S390CPU *cpu, uint64_t reg1, uint32_t ipb) break; } setcc(cpu, cc); - -out: - s390_cpu_physical_memory_unmap(env, schib, len, 0); } static void copy_orb_from_guest(ORB *dest, const ORB *src) @@ -215,11 +210,10 @@ void ioinst_handle_ssch(S390CPU *cpu, uint64_t reg1, uint32_t ipb) { int cssid, ssid, schid, m; SubchDev *sch; - ORB *orig_orb, orb; + ORB orig_orb, orb; uint64_t addr; int ret = -ENODEV; int cc; - hwaddr len = sizeof(*orig_orb); CPUS390XState *env = &cpu->env; addr = decode_basedisp_s(env, ipb); @@ -227,16 +221,14 @@ void ioinst_handle_ssch(S390CPU *cpu, uint64_t reg1, uint32_t ipb) program_interrupt(env, PGM_SPECIFICATION, 2); return; } - orig_orb = s390_cpu_physical_memory_map(env, addr, &len, 0); - if (!orig_orb || len != sizeof(*orig_orb)) { - program_interrupt(env, PGM_ADDRESSING, 2); - goto out; + if (s390_cpu_virt_mem_read(cpu, addr, &orig_orb, sizeof(orb))) { + return; } - copy_orb_from_guest(&orb, orig_orb); + copy_orb_from_guest(&orb, &orig_orb); if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid) || !ioinst_orb_valid(&orb)) { program_interrupt(env, PGM_OPERAND, 2); - goto out; + return; } trace_ioinst_sch_id("ssch", cssid, ssid, schid); sch = css_find_subch(m, cssid, ssid, schid); @@ -258,17 +250,13 @@ void ioinst_handle_ssch(S390CPU *cpu, uint64_t reg1, uint32_t ipb) break; } setcc(cpu, cc); - -out: - s390_cpu_physical_memory_unmap(env, orig_orb, len, 0); } void ioinst_handle_stcrw(S390CPU *cpu, uint32_t ipb) { - CRW *crw; + CRW crw; uint64_t addr; int cc; - hwaddr len = sizeof(*crw); CPUS390XState *env = &cpu->env; addr = decode_basedisp_s(env, ipb); @@ -276,17 +264,16 @@ void ioinst_handle_stcrw(S390CPU *cpu, uint32_t ipb) program_interrupt(env, PGM_SPECIFICATION, 2); return; } - crw = s390_cpu_physical_memory_map(env, addr, &len, 1); - if (!crw || len != sizeof(*crw)) { - program_interrupt(env, PGM_ADDRESSING, 2); - goto out; - } - cc = css_do_stcrw(crw); + + cc = css_do_stcrw(&crw); /* 0 - crw stored, 1 - zeroes stored */ - setcc(cpu, cc); -out: - s390_cpu_physical_memory_unmap(env, crw, len, 1); + if (s390_cpu_virt_mem_write(cpu, addr, &crw, sizeof(crw)) == 0) { + setcc(cpu, cc); + } else if (cc == 0) { + /* Write failed: requeue CRW since STCRW is a suppressing instruction */ + css_undo_stcrw(&crw); + } } void ioinst_handle_stsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb) @@ -295,8 +282,7 @@ void ioinst_handle_stsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb) SubchDev *sch; uint64_t addr; int cc; - SCHIB *schib; - hwaddr len = sizeof(*schib); + SCHIB schib; CPUS390XState *env = &cpu->env; addr = decode_basedisp_s(env, ipb); @@ -304,21 +290,23 @@ void ioinst_handle_stsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb) program_interrupt(env, PGM_SPECIFICATION, 2); return; } - schib = s390_cpu_physical_memory_map(env, addr, &len, 1); - if (!schib || len != sizeof(*schib)) { - program_interrupt(env, PGM_ADDRESSING, 2); - goto out; - } if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { - program_interrupt(env, PGM_OPERAND, 2); - goto out; + /* + * As operand exceptions have a lower priority than access exceptions, + * we check whether the memory area is writeable (injecting the + * access execption if it is not) first. + */ + if (!s390_cpu_virt_mem_check_write(cpu, addr, sizeof(schib))) { + program_interrupt(env, PGM_OPERAND, 2); + } + return; } trace_ioinst_sch_id("stsch", cssid, ssid, schid); sch = css_find_subch(m, cssid, ssid, schid); if (sch) { if (css_subch_visible(sch)) { - css_do_stsch(sch, schib); + css_do_stsch(sch, &schib); cc = 0; } else { /* Indicate no more subchannels in this css/ss */ @@ -329,25 +317,31 @@ void ioinst_handle_stsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb) cc = 3; /* No more subchannels in this css/ss */ } else { /* Store an empty schib. */ - memset(schib, 0, sizeof(*schib)); + memset(&schib, 0, sizeof(schib)); cc = 0; } } + if (cc != 3) { + if (s390_cpu_virt_mem_write(cpu, addr, &schib, sizeof(schib)) != 0) { + return; + } + } else { + /* Access exceptions have a higher priority than cc3 */ + if (s390_cpu_virt_mem_check_write(cpu, addr, sizeof(schib)) != 0) { + return; + } + } setcc(cpu, cc); - -out: - s390_cpu_physical_memory_unmap(env, schib, len, 1); } -int ioinst_handle_tsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb) +int ioinst_handle_tsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb) { + CPUS390XState *env = &cpu->env; int cssid, ssid, schid, m; SubchDev *sch; - IRB *irb; + IRB irb; uint64_t addr; - int ret = -ENODEV; - int cc; - hwaddr len = sizeof(*irb); + int cc, irb_len; if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { program_interrupt(env, PGM_OPERAND, 2); @@ -359,23 +353,29 @@ int ioinst_handle_tsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb) program_interrupt(env, PGM_SPECIFICATION, 2); return -EIO; } - irb = s390_cpu_physical_memory_map(env, addr, &len, 1); - if (!irb || len != sizeof(*irb)) { - program_interrupt(env, PGM_ADDRESSING, 2); - cc = -EIO; - goto out; - } + sch = css_find_subch(m, cssid, ssid, schid); if (sch && css_subch_visible(sch)) { - ret = css_do_tsch(sch, irb); - /* 0 - status pending, 1 - not status pending */ - cc = ret; + cc = css_do_tsch_get_irb(sch, &irb, &irb_len); } else { cc = 3; } -out: - s390_cpu_physical_memory_unmap(env, irb, sizeof(*irb), 1); - return cc; + /* 0 - status pending, 1 - not status pending, 3 - not operational */ + if (cc != 3) { + if (s390_cpu_virt_mem_write(cpu, addr, &irb, irb_len) != 0) { + return -EFAULT; + } + css_do_tsch_update_subch(sch); + } else { + irb_len = sizeof(irb) - sizeof(irb.emw); + /* Access exceptions have a higher priority than cc3 */ + if (s390_cpu_virt_mem_check_write(cpu, addr, irb_len) != 0) { + return -EFAULT; + } + } + + setcc(cpu, cc); + return 0; } typedef struct ChscReq { @@ -398,6 +398,7 @@ typedef struct ChscResp { #define CHSC_SCPD 0x0002 #define CHSC_SCSC 0x0010 #define CHSC_SDA 0x0031 +#define CHSC_SEI 0x000e #define CHSC_SCPD_0_M 0x20000000 #define CHSC_SCPD_0_C 0x10000000 @@ -566,6 +567,53 @@ out: res->param = 0; } +static int chsc_sei_nt0_get_event(void *res) +{ + /* no events yet */ + return 1; +} + +static int chsc_sei_nt0_have_event(void) +{ + /* no events yet */ + return 0; +} + +#define CHSC_SEI_NT0 (1ULL << 63) +#define CHSC_SEI_NT2 (1ULL << 61) +static void ioinst_handle_chsc_sei(ChscReq *req, ChscResp *res) +{ + uint64_t selection_mask = ldq_p(&req->param1); + uint8_t *res_flags = (uint8_t *)res->data; + int have_event = 0; + int have_more = 0; + + /* regarding architecture nt0 can not be masked */ + have_event = !chsc_sei_nt0_get_event(res); + have_more = chsc_sei_nt0_have_event(); + + if (selection_mask & CHSC_SEI_NT2) { + if (!have_event) { + have_event = !chsc_sei_nt2_get_event(res); + } + + if (!have_more) { + have_more = chsc_sei_nt2_have_event(); + } + } + + if (have_event) { + res->code = cpu_to_be16(0x0001); + if (have_more) { + (*res_flags) |= 0x80; + } else { + (*res_flags) &= ~0x80; + } + } else { + res->code = cpu_to_be16(0x0004); + } +} + static void ioinst_handle_chsc_unimplemented(ChscResp *res) { res->len = cpu_to_be16(CHSC_MIN_RESP_LEN); @@ -581,8 +629,8 @@ void ioinst_handle_chsc(S390CPU *cpu, uint32_t ipb) int reg; uint16_t len; uint16_t command; - hwaddr map_size = TARGET_PAGE_SIZE; CPUS390XState *env = &cpu->env; + uint8_t buf[TARGET_PAGE_SIZE]; trace_ioinst("chsc"); reg = (ipb >> 20) & 0x00f; @@ -592,16 +640,20 @@ void ioinst_handle_chsc(S390CPU *cpu, uint32_t ipb) program_interrupt(env, PGM_SPECIFICATION, 2); return; } - req = s390_cpu_physical_memory_map(env, addr, &map_size, 1); - if (!req || map_size != TARGET_PAGE_SIZE) { - program_interrupt(env, PGM_ADDRESSING, 2); - goto out; + /* + * Reading sizeof(ChscReq) bytes is currently enough for all of our + * present CHSC sub-handlers ... if we ever need more, we should take + * care of req->len here first. + */ + if (s390_cpu_virt_mem_read(cpu, addr, buf, sizeof(ChscReq))) { + return; } + req = (ChscReq *)buf; len = be16_to_cpu(req->len); /* Length field valid? */ if ((len < 16) || (len > 4088) || (len & 7)) { program_interrupt(env, PGM_OPERAND, 2); - goto out; + return; } memset((char *)req + len, 0, TARGET_PAGE_SIZE - len); res = (void *)((char *)req + len); @@ -617,22 +669,26 @@ void ioinst_handle_chsc(S390CPU *cpu, uint32_t ipb) case CHSC_SDA: ioinst_handle_chsc_sda(req, res); break; + case CHSC_SEI: + ioinst_handle_chsc_sei(req, res); + break; default: ioinst_handle_chsc_unimplemented(res); break; } - setcc(cpu, 0); /* Command execution complete */ -out: - s390_cpu_physical_memory_unmap(env, req, map_size, 1); + if (!s390_cpu_virt_mem_write(cpu, addr + len, res, be16_to_cpu(res->len))) { + setcc(cpu, 0); /* Command execution complete */ + } } -int ioinst_handle_tpi(CPUS390XState *env, uint32_t ipb) +int ioinst_handle_tpi(S390CPU *cpu, uint32_t ipb) { + CPUS390XState *env = &cpu->env; uint64_t addr; int lowcore; - IOIntCode *int_code; - hwaddr len, orig_len; + IOIntCode int_code; + hwaddr len; int ret; trace_ioinst("tpi"); @@ -644,16 +700,10 @@ int ioinst_handle_tpi(CPUS390XState *env, uint32_t ipb) lowcore = addr ? 0 : 1; len = lowcore ? 8 /* two words */ : 12 /* three words */; - orig_len = len; - int_code = s390_cpu_physical_memory_map(env, addr, &len, 1); - if (!int_code || (len != orig_len)) { - program_interrupt(env, PGM_ADDRESSING, 2); - ret = -EIO; - goto out; + ret = css_do_tpi(&int_code, lowcore); + if (ret == 1) { + s390_cpu_virt_mem_write(cpu, lowcore ? 184 : addr, &int_code, len); } - ret = css_do_tpi(int_code, lowcore); -out: - s390_cpu_physical_memory_unmap(env, int_code, len, 1); return ret; } diff --git a/target-s390x/ioinst.h b/target-s390x/ioinst.h index 29f6423..203bdba 100644 --- a/target-s390x/ioinst.h +++ b/target-s390x/ioinst.h @@ -204,6 +204,7 @@ typedef struct CRW { #define CRW_RSC_SUBCH 0x3 #define CRW_RSC_CHP 0x4 +#define CRW_RSC_CSS 0xb /* I/O interruption code */ typedef struct IOIntCode { @@ -233,9 +234,9 @@ void ioinst_handle_msch(S390CPU *cpu, uint64_t reg1, uint32_t ipb); void ioinst_handle_ssch(S390CPU *cpu, uint64_t reg1, uint32_t ipb); void ioinst_handle_stcrw(S390CPU *cpu, uint32_t ipb); void ioinst_handle_stsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb); -int ioinst_handle_tsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb); +int ioinst_handle_tsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb); void ioinst_handle_chsc(S390CPU *cpu, uint32_t ipb); -int ioinst_handle_tpi(CPUS390XState *env, uint32_t ipb); +int ioinst_handle_tpi(S390CPU *cpu, uint32_t ipb); void ioinst_handle_schm(S390CPU *cpu, uint64_t reg1, uint64_t reg2, uint32_t ipb); void ioinst_handle_rsch(S390CPU *cpu, uint64_t reg1); diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c index 2c638ab..b48c643 100644 --- a/target-s390x/kvm.c +++ b/target-s390x/kvm.c @@ -38,8 +38,12 @@ #include "qapi/qmp/qjson.h" #include "monitor/monitor.h" #include "exec/gdbstub.h" +#include "exec/address-spaces.h" #include "trace.h" #include "qapi-event.h" +#include "hw/s390x/s390-pci-inst.h" +#include "hw/s390x/s390-pci-bus.h" +#include "hw/s390x/ipl.h" /* #define DEBUG_KVM */ @@ -51,11 +55,15 @@ do { } while (0) #endif +#define kvm_vm_check_mem_attr(s, attr) \ + kvm_vm_check_attr(s, KVM_S390_VM_MEM_CTRL, attr) + #define IPA0_DIAG 0x8300 #define IPA0_SIGP 0xae00 #define IPA0_B2 0xb200 #define IPA0_B9 0xb900 #define IPA0_EB 0xeb00 +#define IPA0_E3 0xe300 #define PRIV_B2_SCLP_CALL 0x20 #define PRIV_B2_CSCH 0x30 @@ -76,8 +84,17 @@ #define PRIV_B2_XSCH 0x76 #define PRIV_EB_SQBS 0x8a +#define PRIV_EB_PCISTB 0xd0 +#define PRIV_EB_SIC 0xd1 #define PRIV_B9_EQBS 0x9c +#define PRIV_B9_CLP 0xa0 +#define PRIV_B9_PCISTG 0xd0 +#define PRIV_B9_PCILG 0xd2 +#define PRIV_B9_RPCIT 0xd3 + +#define PRIV_E3_MPCIFC 0xd0 +#define PRIV_E3_STPCIFC 0xd4 #define DIAG_IPL 0x308 #define DIAG_KVM_HYPERCALL 0x500 @@ -108,24 +125,39 @@ static int cap_async_pf; static void *legacy_s390_alloc(size_t size, uint64_t *align); -static int kvm_s390_check_clear_cmma(KVMState *s) +static int kvm_s390_query_mem_limit(KVMState *s, uint64_t *memory_limit) { struct kvm_device_attr attr = { .group = KVM_S390_VM_MEM_CTRL, - .attr = KVM_S390_VM_MEM_CLR_CMMA, + .attr = KVM_S390_VM_MEM_LIMIT_SIZE, + .addr = (uint64_t) memory_limit, }; - return kvm_vm_ioctl(s, KVM_HAS_DEVICE_ATTR, &attr); + return kvm_vm_ioctl(s, KVM_GET_DEVICE_ATTR, &attr); } -static int kvm_s390_check_enable_cmma(KVMState *s) +int kvm_s390_set_mem_limit(KVMState *s, uint64_t new_limit, uint64_t *hw_limit) { + int rc; + struct kvm_device_attr attr = { .group = KVM_S390_VM_MEM_CTRL, - .attr = KVM_S390_VM_MEM_ENABLE_CMMA, + .attr = KVM_S390_VM_MEM_LIMIT_SIZE, + .addr = (uint64_t) &new_limit, }; - return kvm_vm_ioctl(s, KVM_HAS_DEVICE_ATTR, &attr); + if (!kvm_vm_check_mem_attr(s, KVM_S390_VM_MEM_LIMIT_SIZE)) { + return 0; + } + + rc = kvm_s390_query_mem_limit(s, hw_limit); + if (rc) { + return rc; + } else if (*hw_limit < new_limit) { + return -E2BIG; + } + + return kvm_vm_ioctl(s, KVM_SET_DEVICE_ATTR, &attr); } void kvm_s390_clear_cmma_callback(void *opaque) @@ -149,7 +181,8 @@ static void kvm_s390_enable_cmma(KVMState *s) .attr = KVM_S390_VM_MEM_ENABLE_CMMA, }; - if (kvm_s390_check_enable_cmma(s) || kvm_s390_check_clear_cmma(s)) { + if (!kvm_vm_check_mem_attr(s, KVM_S390_VM_MEM_ENABLE_CMMA) || + !kvm_vm_check_mem_attr(s, KVM_S390_VM_MEM_CLR_CMMA)) { return; } @@ -160,19 +193,69 @@ static void kvm_s390_enable_cmma(KVMState *s) trace_kvm_enable_cmma(rc); } -int kvm_arch_init(KVMState *s) +static void kvm_s390_set_attr(uint64_t attr) +{ + struct kvm_device_attr attribute = { + .group = KVM_S390_VM_CRYPTO, + .attr = attr, + }; + + int ret = kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attribute); + + if (ret) { + error_report("Failed to set crypto device attribute %lu: %s", + attr, strerror(-ret)); + } +} + +static void kvm_s390_init_aes_kw(void) +{ + uint64_t attr = KVM_S390_VM_CRYPTO_DISABLE_AES_KW; + + if (object_property_get_bool(OBJECT(qdev_get_machine()), "aes-key-wrap", + NULL)) { + attr = KVM_S390_VM_CRYPTO_ENABLE_AES_KW; + } + + if (kvm_vm_check_attr(kvm_state, KVM_S390_VM_CRYPTO, attr)) { + kvm_s390_set_attr(attr); + } +} + +static void kvm_s390_init_dea_kw(void) +{ + uint64_t attr = KVM_S390_VM_CRYPTO_DISABLE_DEA_KW; + + if (object_property_get_bool(OBJECT(qdev_get_machine()), "dea-key-wrap", + NULL)) { + attr = KVM_S390_VM_CRYPTO_ENABLE_DEA_KW; + } + + if (kvm_vm_check_attr(kvm_state, KVM_S390_VM_CRYPTO, attr)) { + kvm_s390_set_attr(attr); + } +} + +static void kvm_s390_init_crypto(void) +{ + kvm_s390_init_aes_kw(); + kvm_s390_init_dea_kw(); +} + +int kvm_arch_init(MachineState *ms, KVMState *s) { cap_sync_regs = kvm_check_extension(s, KVM_CAP_SYNC_REGS); cap_async_pf = kvm_check_extension(s, KVM_CAP_ASYNC_PF); - if (kvm_check_extension(s, KVM_CAP_VM_ATTRIBUTES)) { - kvm_s390_enable_cmma(s); - } + kvm_s390_enable_cmma(s); if (!kvm_check_extension(s, KVM_CAP_S390_GMAP) || !kvm_check_extension(s, KVM_CAP_S390_COW)) { phys_mem_set_alloc(legacy_s390_alloc); } + + kvm_vm_enable_cap(s, KVM_CAP_S390_USER_SIGP, 0); + return 0; } @@ -198,8 +281,15 @@ void kvm_s390_reset_vcpu(S390CPU *cpu) * Before this ioctl cpu_synchronize_state() is called in common kvm * code (kvm-all) */ if (kvm_vcpu_ioctl(cs, KVM_S390_INITIAL_RESET, NULL)) { - error_report("Initial CPU reset failed on CPU %i\n", cs->cpu_index); + error_report("Initial CPU reset failed on CPU %i", cs->cpu_index); } + + kvm_s390_init_crypto(); +} + +static int can_sync_regs(CPUState *cs, int regs) +{ + return cap_sync_regs && (cs->kvm_run->kvm_valid_regs & regs) == regs; } int kvm_arch_put_registers(CPUState *cs, int level) @@ -208,7 +298,7 @@ int kvm_arch_put_registers(CPUState *cs, int level) CPUS390XState *env = &cpu->env; struct kvm_sregs sregs; struct kvm_regs regs; - struct kvm_fpu fpu; + struct kvm_fpu fpu = {}; int r; int i; @@ -216,7 +306,7 @@ int kvm_arch_put_registers(CPUState *cs, int level) cs->kvm_run->psw_addr = env->psw.addr; cs->kvm_run->psw_mask = env->psw.mask; - if (cap_sync_regs && cs->kvm_run->kvm_valid_regs & KVM_SYNC_GPRS) { + if (can_sync_regs(cs, KVM_SYNC_GPRS)) { for (i = 0; i < 16; i++) { cs->kvm_run->s.regs.gprs[i] = env->regs[i]; cs->kvm_run->kvm_dirty_regs |= KVM_SYNC_GPRS; @@ -247,18 +337,33 @@ int kvm_arch_put_registers(CPUState *cs, int level) return 0; } - /* - * These ONE_REGS are not protected by a capability. As they are only - * necessary for migration we just trace a possible error, but don't - * return with an error return code. - */ - kvm_set_one_reg(cs, KVM_REG_S390_CPU_TIMER, &env->cputm); - kvm_set_one_reg(cs, KVM_REG_S390_CLOCK_COMP, &env->ckc); - kvm_set_one_reg(cs, KVM_REG_S390_TODPR, &env->todpr); - kvm_set_one_reg(cs, KVM_REG_S390_GBEA, &env->gbea); - kvm_set_one_reg(cs, KVM_REG_S390_PP, &env->pp); + if (can_sync_regs(cs, KVM_SYNC_ARCH0)) { + cs->kvm_run->s.regs.cputm = env->cputm; + cs->kvm_run->s.regs.ckc = env->ckc; + cs->kvm_run->s.regs.todpr = env->todpr; + cs->kvm_run->s.regs.gbea = env->gbea; + cs->kvm_run->s.regs.pp = env->pp; + cs->kvm_run->kvm_dirty_regs |= KVM_SYNC_ARCH0; + } else { + /* + * These ONE_REGS are not protected by a capability. As they are only + * necessary for migration we just trace a possible error, but don't + * return with an error return code. + */ + kvm_set_one_reg(cs, KVM_REG_S390_CPU_TIMER, &env->cputm); + kvm_set_one_reg(cs, KVM_REG_S390_CLOCK_COMP, &env->ckc); + kvm_set_one_reg(cs, KVM_REG_S390_TODPR, &env->todpr); + kvm_set_one_reg(cs, KVM_REG_S390_GBEA, &env->gbea); + kvm_set_one_reg(cs, KVM_REG_S390_PP, &env->pp); + } - if (cap_async_pf) { + /* pfault parameters */ + if (can_sync_regs(cs, KVM_SYNC_PFAULT)) { + cs->kvm_run->s.regs.pft = env->pfault_token; + cs->kvm_run->s.regs.pfs = env->pfault_select; + cs->kvm_run->s.regs.pfc = env->pfault_compare; + cs->kvm_run->kvm_dirty_regs |= KVM_SYNC_PFAULT; + } else if (cap_async_pf) { r = kvm_set_one_reg(cs, KVM_REG_S390_PFTOKEN, &env->pfault_token); if (r < 0) { return r; @@ -273,9 +378,8 @@ int kvm_arch_put_registers(CPUState *cs, int level) } } - if (cap_sync_regs && - cs->kvm_run->kvm_valid_regs & KVM_SYNC_ACRS && - cs->kvm_run->kvm_valid_regs & KVM_SYNC_CRS) { + /* access registers and control registers*/ + if (can_sync_regs(cs, KVM_SYNC_ACRS | KVM_SYNC_CRS)) { for (i = 0; i < 16; i++) { cs->kvm_run->s.regs.acrs[i] = env->aregs[i]; cs->kvm_run->s.regs.crs[i] = env->cregs[i]; @@ -294,7 +398,7 @@ int kvm_arch_put_registers(CPUState *cs, int level) } /* Finally the prefix */ - if (cap_sync_regs && cs->kvm_run->kvm_valid_regs & KVM_SYNC_PREFIX) { + if (can_sync_regs(cs, KVM_SYNC_PREFIX)) { cs->kvm_run->s.regs.prefix = env->psa; cs->kvm_run->kvm_dirty_regs |= KVM_SYNC_PREFIX; } else { @@ -317,7 +421,7 @@ int kvm_arch_get_registers(CPUState *cs) env->psw.mask = cs->kvm_run->psw_mask; /* the GPRS */ - if (cap_sync_regs && cs->kvm_run->kvm_valid_regs & KVM_SYNC_GPRS) { + if (can_sync_regs(cs, KVM_SYNC_GPRS)) { for (i = 0; i < 16; i++) { env->regs[i] = cs->kvm_run->s.regs.gprs[i]; } @@ -332,9 +436,7 @@ int kvm_arch_get_registers(CPUState *cs) } /* The ACRS and CRS */ - if (cap_sync_regs && - cs->kvm_run->kvm_valid_regs & KVM_SYNC_ACRS && - cs->kvm_run->kvm_valid_regs & KVM_SYNC_CRS) { + if (can_sync_regs(cs, KVM_SYNC_ACRS | KVM_SYNC_CRS)) { for (i = 0; i < 16; i++) { env->aregs[i] = cs->kvm_run->s.regs.acrs[i]; env->cregs[i] = cs->kvm_run->s.regs.crs[i]; @@ -361,22 +463,35 @@ int kvm_arch_get_registers(CPUState *cs) env->fpc = fpu.fpc; /* The prefix */ - if (cap_sync_regs && cs->kvm_run->kvm_valid_regs & KVM_SYNC_PREFIX) { + if (can_sync_regs(cs, KVM_SYNC_PREFIX)) { env->psa = cs->kvm_run->s.regs.prefix; } - /* - * These ONE_REGS are not protected by a capability. As they are only - * necessary for migration we just trace a possible error, but don't - * return with an error return code. - */ - kvm_get_one_reg(cs, KVM_REG_S390_CPU_TIMER, &env->cputm); - kvm_get_one_reg(cs, KVM_REG_S390_CLOCK_COMP, &env->ckc); - kvm_get_one_reg(cs, KVM_REG_S390_TODPR, &env->todpr); - kvm_get_one_reg(cs, KVM_REG_S390_GBEA, &env->gbea); - kvm_get_one_reg(cs, KVM_REG_S390_PP, &env->pp); + if (can_sync_regs(cs, KVM_SYNC_ARCH0)) { + env->cputm = cs->kvm_run->s.regs.cputm; + env->ckc = cs->kvm_run->s.regs.ckc; + env->todpr = cs->kvm_run->s.regs.todpr; + env->gbea = cs->kvm_run->s.regs.gbea; + env->pp = cs->kvm_run->s.regs.pp; + } else { + /* + * These ONE_REGS are not protected by a capability. As they are only + * necessary for migration we just trace a possible error, but don't + * return with an error return code. + */ + kvm_get_one_reg(cs, KVM_REG_S390_CPU_TIMER, &env->cputm); + kvm_get_one_reg(cs, KVM_REG_S390_CLOCK_COMP, &env->ckc); + kvm_get_one_reg(cs, KVM_REG_S390_TODPR, &env->todpr); + kvm_get_one_reg(cs, KVM_REG_S390_GBEA, &env->gbea); + kvm_get_one_reg(cs, KVM_REG_S390_PP, &env->pp); + } - if (cap_async_pf) { + /* pfault parameters */ + if (can_sync_regs(cs, KVM_SYNC_PFAULT)) { + env->pfault_token = cs->kvm_run->s.regs.pft; + env->pfault_select = cs->kvm_run->s.regs.pfs; + env->pfault_compare = cs->kvm_run->s.regs.pfc; + } else if (cap_async_pf) { r = kvm_get_one_reg(cs, KVM_REG_S390_PFTOKEN, &env->pfault_token); if (r < 0) { return r; @@ -394,6 +509,45 @@ int kvm_arch_get_registers(CPUState *cs) return 0; } +int kvm_s390_get_clock(uint8_t *tod_high, uint64_t *tod_low) +{ + int r; + struct kvm_device_attr attr = { + .group = KVM_S390_VM_TOD, + .attr = KVM_S390_VM_TOD_LOW, + .addr = (uint64_t)tod_low, + }; + + r = kvm_vm_ioctl(kvm_state, KVM_GET_DEVICE_ATTR, &attr); + if (r) { + return r; + } + + attr.attr = KVM_S390_VM_TOD_HIGH; + attr.addr = (uint64_t)tod_high; + return kvm_vm_ioctl(kvm_state, KVM_GET_DEVICE_ATTR, &attr); +} + +int kvm_s390_set_clock(uint8_t *tod_high, uint64_t *tod_low) +{ + int r; + + struct kvm_device_attr attr = { + .group = KVM_S390_VM_TOD, + .attr = KVM_S390_VM_TOD_LOW, + .addr = (uint64_t)tod_low, + }; + + r = kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attr); + if (r) { + return r; + } + + attr.attr = KVM_S390_VM_TOD_HIGH; + attr.addr = (uint64_t)tod_high; + return kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attr); +} + /* * Legacy layout for s390: * Older S390 KVM requires the topmost vma of the RAM to be @@ -714,6 +868,18 @@ static void enter_pgmcheck(S390CPU *cpu, uint16_t code) kvm_s390_vcpu_interrupt(cpu, &irq); } +void kvm_s390_access_exception(S390CPU *cpu, uint16_t code, uint64_t te_code) +{ + struct kvm_s390_irq irq = { + .type = KVM_S390_PROGRAM_INT, + .u.pgm.code = code, + .u.pgm.trans_exc_code = te_code, + .u.pgm.exc_access_id = te_code & 3, + }; + + kvm_s390_vcpu_interrupt(cpu, &irq); +} + static int kvm_sclp_service_call(S390CPU *cpu, struct kvm_run *run, uint16_t ipbh0) { @@ -809,11 +975,124 @@ static int handle_b2(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1) return rc; } +static uint64_t get_base_disp_rxy(S390CPU *cpu, struct kvm_run *run) +{ + CPUS390XState *env = &cpu->env; + uint32_t x2 = (run->s390_sieic.ipa & 0x000f); + uint32_t base2 = run->s390_sieic.ipb >> 28; + uint32_t disp2 = ((run->s390_sieic.ipb & 0x0fff0000) >> 16) + + ((run->s390_sieic.ipb & 0xff00) << 4); + + if (disp2 & 0x80000) { + disp2 += 0xfff00000; + } + + return (base2 ? env->regs[base2] : 0) + + (x2 ? env->regs[x2] : 0) + (long)(int)disp2; +} + +static uint64_t get_base_disp_rsy(S390CPU *cpu, struct kvm_run *run) +{ + CPUS390XState *env = &cpu->env; + uint32_t base2 = run->s390_sieic.ipb >> 28; + uint32_t disp2 = ((run->s390_sieic.ipb & 0x0fff0000) >> 16) + + ((run->s390_sieic.ipb & 0xff00) << 4); + + if (disp2 & 0x80000) { + disp2 += 0xfff00000; + } + + return (base2 ? env->regs[base2] : 0) + (long)(int)disp2; +} + +static int kvm_clp_service_call(S390CPU *cpu, struct kvm_run *run) +{ + uint8_t r2 = (run->s390_sieic.ipb & 0x000f0000) >> 16; + + return clp_service_call(cpu, r2); +} + +static int kvm_pcilg_service_call(S390CPU *cpu, struct kvm_run *run) +{ + uint8_t r1 = (run->s390_sieic.ipb & 0x00f00000) >> 20; + uint8_t r2 = (run->s390_sieic.ipb & 0x000f0000) >> 16; + + return pcilg_service_call(cpu, r1, r2); +} + +static int kvm_pcistg_service_call(S390CPU *cpu, struct kvm_run *run) +{ + uint8_t r1 = (run->s390_sieic.ipb & 0x00f00000) >> 20; + uint8_t r2 = (run->s390_sieic.ipb & 0x000f0000) >> 16; + + return pcistg_service_call(cpu, r1, r2); +} + +static int kvm_stpcifc_service_call(S390CPU *cpu, struct kvm_run *run) +{ + uint8_t r1 = (run->s390_sieic.ipa & 0x00f0) >> 4; + uint64_t fiba; + + cpu_synchronize_state(CPU(cpu)); + fiba = get_base_disp_rxy(cpu, run); + + return stpcifc_service_call(cpu, r1, fiba); +} + +static int kvm_sic_service_call(S390CPU *cpu, struct kvm_run *run) +{ + /* NOOP */ + return 0; +} + +static int kvm_rpcit_service_call(S390CPU *cpu, struct kvm_run *run) +{ + uint8_t r1 = (run->s390_sieic.ipb & 0x00f00000) >> 20; + uint8_t r2 = (run->s390_sieic.ipb & 0x000f0000) >> 16; + + return rpcit_service_call(cpu, r1, r2); +} + +static int kvm_pcistb_service_call(S390CPU *cpu, struct kvm_run *run) +{ + uint8_t r1 = (run->s390_sieic.ipa & 0x00f0) >> 4; + uint8_t r3 = run->s390_sieic.ipa & 0x000f; + uint64_t gaddr; + + cpu_synchronize_state(CPU(cpu)); + gaddr = get_base_disp_rsy(cpu, run); + + return pcistb_service_call(cpu, r1, r3, gaddr); +} + +static int kvm_mpcifc_service_call(S390CPU *cpu, struct kvm_run *run) +{ + uint8_t r1 = (run->s390_sieic.ipa & 0x00f0) >> 4; + uint64_t fiba; + + cpu_synchronize_state(CPU(cpu)); + fiba = get_base_disp_rxy(cpu, run); + + return mpcifc_service_call(cpu, r1, fiba); +} + static int handle_b9(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1) { int r = 0; switch (ipa1) { + case PRIV_B9_CLP: + r = kvm_clp_service_call(cpu, run); + break; + case PRIV_B9_PCISTG: + r = kvm_pcistg_service_call(cpu, run); + break; + case PRIV_B9_PCILG: + r = kvm_pcilg_service_call(cpu, run); + break; + case PRIV_B9_RPCIT: + r = kvm_rpcit_service_call(cpu, run); + break; case PRIV_B9_EQBS: /* just inject exception */ r = -1; @@ -832,6 +1111,12 @@ static int handle_eb(S390CPU *cpu, struct kvm_run *run, uint8_t ipbl) int r = 0; switch (ipbl) { + case PRIV_EB_PCISTB: + r = kvm_pcistb_service_call(cpu, run); + break; + case PRIV_EB_SIC: + r = kvm_sic_service_call(cpu, run); + break; case PRIV_EB_SQBS: /* just inject exception */ r = -1; @@ -845,6 +1130,26 @@ static int handle_eb(S390CPU *cpu, struct kvm_run *run, uint8_t ipbl) return r; } +static int handle_e3(S390CPU *cpu, struct kvm_run *run, uint8_t ipbl) +{ + int r = 0; + + switch (ipbl) { + case PRIV_E3_MPCIFC: + r = kvm_mpcifc_service_call(cpu, run); + break; + case PRIV_E3_STPCIFC: + r = kvm_stpcifc_service_call(cpu, run); + break; + default: + r = -1; + DPRINTF("KVM: unhandled PRIV: 0xe3%x\n", ipbl); + break; + } + + return r; +} + static int handle_hypercall(S390CPU *cpu, struct kvm_run *run) { CPUS390XState *env = &cpu->env; @@ -865,7 +1170,7 @@ static void kvm_handle_diag_308(S390CPU *cpu, struct kvm_run *run) uint64_t r1, r3; cpu_synchronize_state(CPU(cpu)); - r1 = (run->s390_sieic.ipa & 0x00f0) >> 8; + r1 = (run->s390_sieic.ipa & 0x00f0) >> 4; r3 = run->s390_sieic.ipa & 0x000f; handle_diag_308(&cpu->env, r1, r3); } @@ -910,117 +1215,363 @@ static int handle_diag(S390CPU *cpu, struct kvm_run *run, uint32_t ipb) break; default: DPRINTF("KVM: unknown DIAG: 0x%x\n", func_code); - r = -1; + enter_pgmcheck(cpu, PGM_SPECIFICATION); break; } return r; } -static void sigp_cpu_start(void *arg) +typedef struct SigpInfo { + S390CPU *cpu; + uint64_t param; + int cc; + uint64_t *status_reg; +} SigpInfo; + +static void set_sigp_status(SigpInfo *si, uint64_t status) { - CPUState *cs = arg; - S390CPU *cpu = S390_CPU(cs); + *si->status_reg &= 0xffffffff00000000ULL; + *si->status_reg |= status; + si->cc = SIGP_CC_STATUS_STORED; +} + +static void sigp_start(void *arg) +{ + SigpInfo *si = arg; + + if (s390_cpu_get_state(si->cpu) != CPU_STATE_STOPPED) { + si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; + return; + } - s390_cpu_set_state(CPU_STATE_OPERATING, cpu); - DPRINTF("DONE: KVM cpu start: %p\n", &cpu->env); + s390_cpu_set_state(CPU_STATE_OPERATING, si->cpu); + si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; } -static void sigp_cpu_restart(void *arg) +static void sigp_stop(void *arg) { - CPUState *cs = arg; - S390CPU *cpu = S390_CPU(cs); + SigpInfo *si = arg; + struct kvm_s390_irq irq = { + .type = KVM_S390_SIGP_STOP, + }; + + if (s390_cpu_get_state(si->cpu) != CPU_STATE_OPERATING) { + si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; + return; + } + + /* disabled wait - sleeping in user space */ + if (CPU(si->cpu)->halted) { + s390_cpu_set_state(CPU_STATE_STOPPED, si->cpu); + } else { + /* execute the stop function */ + si->cpu->env.sigp_order = SIGP_STOP; + kvm_s390_vcpu_interrupt(si->cpu, &irq); + } + si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; +} + +#define KVM_S390_STORE_STATUS_DEF_ADDR offsetof(LowCore, floating_pt_save_area) +#define SAVE_AREA_SIZE 512 +static int kvm_s390_store_status(S390CPU *cpu, hwaddr addr, bool store_arch) +{ + static const uint8_t ar_id = 1; + uint64_t ckc = cpu->env.ckc >> 8; + void *mem; + hwaddr len = SAVE_AREA_SIZE; + + mem = cpu_physical_memory_map(addr, &len, 1); + if (!mem) { + return -EFAULT; + } + if (len != SAVE_AREA_SIZE) { + cpu_physical_memory_unmap(mem, len, 1, 0); + return -EFAULT; + } + + if (store_arch) { + cpu_physical_memory_write(offsetof(LowCore, ar_access_id), &ar_id, 1); + } + memcpy(mem, &cpu->env.fregs, 128); + memcpy(mem + 128, &cpu->env.regs, 128); + memcpy(mem + 256, &cpu->env.psw, 16); + memcpy(mem + 280, &cpu->env.psa, 4); + memcpy(mem + 284, &cpu->env.fpc, 4); + memcpy(mem + 292, &cpu->env.todpr, 4); + memcpy(mem + 296, &cpu->env.cputm, 8); + memcpy(mem + 304, &ckc, 8); + memcpy(mem + 320, &cpu->env.aregs, 64); + memcpy(mem + 384, &cpu->env.cregs, 128); + + cpu_physical_memory_unmap(mem, len, 1, len); + + return 0; +} + +static void sigp_stop_and_store_status(void *arg) +{ + SigpInfo *si = arg; + struct kvm_s390_irq irq = { + .type = KVM_S390_SIGP_STOP, + }; + + /* disabled wait - sleeping in user space */ + if (s390_cpu_get_state(si->cpu) == CPU_STATE_OPERATING && + CPU(si->cpu)->halted) { + s390_cpu_set_state(CPU_STATE_STOPPED, si->cpu); + } + + switch (s390_cpu_get_state(si->cpu)) { + case CPU_STATE_OPERATING: + si->cpu->env.sigp_order = SIGP_STOP_STORE_STATUS; + kvm_s390_vcpu_interrupt(si->cpu, &irq); + /* store will be performed when handling the stop intercept */ + break; + case CPU_STATE_STOPPED: + /* already stopped, just store the status */ + cpu_synchronize_state(CPU(si->cpu)); + kvm_s390_store_status(si->cpu, KVM_S390_STORE_STATUS_DEF_ADDR, true); + break; + } + si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; +} + +static void sigp_store_status_at_address(void *arg) +{ + SigpInfo *si = arg; + uint32_t address = si->param & 0x7ffffe00u; + + /* cpu has to be stopped */ + if (s390_cpu_get_state(si->cpu) != CPU_STATE_STOPPED) { + set_sigp_status(si, SIGP_STAT_INCORRECT_STATE); + return; + } + + cpu_synchronize_state(CPU(si->cpu)); + + if (kvm_s390_store_status(si->cpu, address, false)) { + set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER); + return; + } + si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; +} + +static void sigp_restart(void *arg) +{ + SigpInfo *si = arg; struct kvm_s390_irq irq = { .type = KVM_S390_RESTART, }; - kvm_s390_vcpu_interrupt(cpu, &irq); - s390_cpu_set_state(CPU_STATE_OPERATING, cpu); + switch (s390_cpu_get_state(si->cpu)) { + case CPU_STATE_STOPPED: + /* the restart irq has to be delivered prior to any other pending irq */ + cpu_synchronize_state(CPU(si->cpu)); + do_restart_interrupt(&si->cpu->env); + s390_cpu_set_state(CPU_STATE_OPERATING, si->cpu); + break; + case CPU_STATE_OPERATING: + kvm_s390_vcpu_interrupt(si->cpu, &irq); + break; + } + si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; } int kvm_s390_cpu_restart(S390CPU *cpu) { - run_on_cpu(CPU(cpu), sigp_cpu_restart, CPU(cpu)); + SigpInfo si = { + .cpu = cpu, + }; + + run_on_cpu(CPU(cpu), sigp_restart, &si); DPRINTF("DONE: KVM cpu restart: %p\n", &cpu->env); return 0; } static void sigp_initial_cpu_reset(void *arg) { - CPUState *cpu = arg; - S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); + SigpInfo *si = arg; + CPUState *cs = CPU(si->cpu); + S390CPUClass *scc = S390_CPU_GET_CLASS(si->cpu); - cpu_synchronize_state(cpu); - scc->initial_cpu_reset(cpu); - cpu_synchronize_post_reset(cpu); + cpu_synchronize_state(cs); + scc->initial_cpu_reset(cs); + cpu_synchronize_post_reset(cs); + si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; } static void sigp_cpu_reset(void *arg) { - CPUState *cpu = arg; - S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); + SigpInfo *si = arg; + CPUState *cs = CPU(si->cpu); + S390CPUClass *scc = S390_CPU_GET_CLASS(si->cpu); - cpu_synchronize_state(cpu); - scc->cpu_reset(cpu); - cpu_synchronize_post_reset(cpu); + cpu_synchronize_state(cs); + scc->cpu_reset(cs); + cpu_synchronize_post_reset(cs); + si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; } -#define SIGP_ORDER_MASK 0x000000ff - -static int handle_sigp(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1) +static void sigp_set_prefix(void *arg) { - CPUS390XState *env = &cpu->env; - uint8_t order_code; - uint16_t cpu_addr; - S390CPU *target_cpu; - uint64_t *statusreg = &env->regs[ipa1 >> 4]; - int cc; + SigpInfo *si = arg; + uint32_t addr = si->param & 0x7fffe000u; - cpu_synchronize_state(CPU(cpu)); + cpu_synchronize_state(CPU(si->cpu)); - /* get order code */ - order_code = decode_basedisp_rs(env, run->s390_sieic.ipb) & SIGP_ORDER_MASK; + if (!address_space_access_valid(&address_space_memory, addr, + sizeof(struct LowCore), false)) { + set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER); + return; + } - cpu_addr = env->regs[ipa1 & 0x0f]; - target_cpu = s390_cpu_addr2state(cpu_addr); - if (target_cpu == NULL) { - cc = 3; /* not operational */ - goto out; + /* cpu has to be stopped */ + if (s390_cpu_get_state(si->cpu) != CPU_STATE_STOPPED) { + set_sigp_status(si, SIGP_STAT_INCORRECT_STATE); + return; } - switch (order_code) { + si->cpu->env.psa = addr; + cpu_synchronize_post_init(CPU(si->cpu)); + si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; +} + +static int handle_sigp_single_dst(S390CPU *dst_cpu, uint8_t order, + uint64_t param, uint64_t *status_reg) +{ + SigpInfo si = { + .cpu = dst_cpu, + .param = param, + .status_reg = status_reg, + }; + + /* cpu available? */ + if (dst_cpu == NULL) { + return SIGP_CC_NOT_OPERATIONAL; + } + + /* only resets can break pending orders */ + if (dst_cpu->env.sigp_order != 0 && + order != SIGP_CPU_RESET && + order != SIGP_INITIAL_CPU_RESET) { + return SIGP_CC_BUSY; + } + + switch (order) { case SIGP_START: - run_on_cpu(CPU(target_cpu), sigp_cpu_start, CPU(target_cpu)); - cc = 0; + run_on_cpu(CPU(dst_cpu), sigp_start, &si); + break; + case SIGP_STOP: + run_on_cpu(CPU(dst_cpu), sigp_stop, &si); break; case SIGP_RESTART: - run_on_cpu(CPU(target_cpu), sigp_cpu_restart, CPU(target_cpu)); - cc = 0; + run_on_cpu(CPU(dst_cpu), sigp_restart, &si); break; - case SIGP_SET_ARCH: - *statusreg &= 0xffffffff00000000UL; - *statusreg |= SIGP_STAT_INVALID_PARAMETER; - cc = 1; /* status stored */ + case SIGP_STOP_STORE_STATUS: + run_on_cpu(CPU(dst_cpu), sigp_stop_and_store_status, &si); + break; + case SIGP_STORE_STATUS_ADDR: + run_on_cpu(CPU(dst_cpu), sigp_store_status_at_address, &si); + break; + case SIGP_SET_PREFIX: + run_on_cpu(CPU(dst_cpu), sigp_set_prefix, &si); break; case SIGP_INITIAL_CPU_RESET: - run_on_cpu(CPU(target_cpu), sigp_initial_cpu_reset, CPU(target_cpu)); - cc = 0; + run_on_cpu(CPU(dst_cpu), sigp_initial_cpu_reset, &si); break; case SIGP_CPU_RESET: - run_on_cpu(CPU(target_cpu), sigp_cpu_reset, CPU(target_cpu)); - cc = 0; + run_on_cpu(CPU(dst_cpu), sigp_cpu_reset, &si); break; default: - DPRINTF("KVM: unknown SIGP: 0x%x\n", order_code); - *statusreg &= 0xffffffff00000000UL; - *statusreg |= SIGP_STAT_INVALID_ORDER; - cc = 1; /* status stored */ + DPRINTF("KVM: unknown SIGP: 0x%x\n", order); + set_sigp_status(&si, SIGP_STAT_INVALID_ORDER); + } + + return si.cc; +} + +static int sigp_set_architecture(S390CPU *cpu, uint32_t param, + uint64_t *status_reg) +{ + CPUState *cur_cs; + S390CPU *cur_cpu; + + /* due to the BQL, we are the only active cpu */ + CPU_FOREACH(cur_cs) { + cur_cpu = S390_CPU(cur_cs); + if (cur_cpu->env.sigp_order != 0) { + return SIGP_CC_BUSY; + } + cpu_synchronize_state(cur_cs); + /* all but the current one have to be stopped */ + if (cur_cpu != cpu && + s390_cpu_get_state(cur_cpu) != CPU_STATE_STOPPED) { + *status_reg &= 0xffffffff00000000ULL; + *status_reg |= SIGP_STAT_INCORRECT_STATE; + return SIGP_CC_STATUS_STORED; + } + } + + switch (param & 0xff) { + case SIGP_MODE_ESA_S390: + /* not supported */ + return SIGP_CC_NOT_OPERATIONAL; + case SIGP_MODE_Z_ARCH_TRANS_ALL_PSW: + case SIGP_MODE_Z_ARCH_TRANS_CUR_PSW: + CPU_FOREACH(cur_cs) { + cur_cpu = S390_CPU(cur_cs); + cur_cpu->env.pfault_token = -1UL; + } break; + default: + *status_reg &= 0xffffffff00000000ULL; + *status_reg |= SIGP_STAT_INVALID_PARAMETER; + return SIGP_CC_STATUS_STORED; } -out: - setcc(cpu, cc); - return 0; + return SIGP_CC_ORDER_CODE_ACCEPTED; +} + +#define SIGP_ORDER_MASK 0x000000ff + +static int handle_sigp(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1) +{ + CPUS390XState *env = &cpu->env; + const uint8_t r1 = ipa1 >> 4; + const uint8_t r3 = ipa1 & 0x0f; + int ret; + uint8_t order; + uint64_t *status_reg; + uint64_t param; + S390CPU *dst_cpu = NULL; + + cpu_synchronize_state(CPU(cpu)); + + /* get order code */ + order = decode_basedisp_rs(env, run->s390_sieic.ipb) & SIGP_ORDER_MASK; + status_reg = &env->regs[r1]; + param = (r1 % 2) ? env->regs[r1] : env->regs[r1 + 1]; + + switch (order) { + case SIGP_SET_ARCH: + ret = sigp_set_architecture(cpu, param, status_reg); + break; + default: + /* all other sigp orders target a single vcpu */ + dst_cpu = s390_cpu_addr2state(env->regs[r3]); + ret = handle_sigp_single_dst(dst_cpu, order, param, status_reg); + } + + trace_kvm_sigp_finished(order, CPU(cpu)->cpu_index, + dst_cpu ? CPU(dst_cpu)->cpu_index : -1, ret); + + if (ret >= 0) { + setcc(cpu, ret); + return 0; + } + + return ret; } static int handle_instruction(S390CPU *cpu, struct kvm_run *run) @@ -1041,6 +1592,9 @@ static int handle_instruction(S390CPU *cpu, struct kvm_run *run) case IPA0_EB: r = handle_eb(cpu, run, run->s390_sieic.ipb & 0xff); break; + case IPA0_E3: + r = handle_e3(cpu, run, run->s390_sieic.ipb & 0xff); + break; case IPA0_DIAG: r = handle_diag(cpu, run, run->s390_sieic.ipb); break; @@ -1120,6 +1674,11 @@ static int handle_intercept(S390CPU *cpu) if (s390_cpu_set_state(CPU_STATE_STOPPED, cpu) == 0) { qemu_system_shutdown_request(); } + if (cpu->env.sigp_order == SIGP_STOP_STORE_STATUS) { + kvm_s390_store_status(cpu, KVM_S390_STORE_STATUS_DEF_ADDR, + true); + } + cpu->env.sigp_order = 0; r = EXCP_HALTED; break; case ICPT_SOFT_INTERCEPT: @@ -1141,19 +1700,14 @@ static int handle_intercept(S390CPU *cpu) static int handle_tsch(S390CPU *cpu) { - CPUS390XState *env = &cpu->env; CPUState *cs = CPU(cpu); struct kvm_run *run = cs->kvm_run; int ret; cpu_synchronize_state(cs); - ret = ioinst_handle_tsch(env, env->regs[1], run->s390_tsch.ipb); - if (ret >= 0) { - /* Success; set condition code. */ - setcc(cpu, ret); - ret = 0; - } else if (ret < -1) { + ret = ioinst_handle_tsch(cpu, cpu->env.regs[1], run->s390_tsch.ipb); + if (ret < 0) { /* * Failure. * If an I/O interrupt had been dequeued, we have to reinject it. @@ -1213,7 +1767,7 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) ret = handle_intercept(cpu); break; case KVM_EXIT_S390_RESET: - qemu_system_reset_request(); + s390_reipl_request(); break; case KVM_EXIT_S390_TSCH: ret = handle_tsch(cpu); @@ -1294,7 +1848,6 @@ void kvm_arch_init_irq_routing(KVMState *s) * have to override the common code kvm_halt_in_kernel_allowed setting. */ if (kvm_check_extension(s, KVM_CAP_IRQ_ROUTING)) { - kvm_irqfds_allowed = true; kvm_gsi_routing_allowed = true; kvm_halt_in_kernel_allowed = false; } @@ -1362,3 +1915,28 @@ int kvm_s390_set_cpu_state(S390CPU *cpu, uint8_t cpu_state) return ret; } + +int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, + uint64_t address, uint32_t data) +{ + S390PCIBusDevice *pbdev; + uint32_t fid = data >> ZPCI_MSI_VEC_BITS; + uint32_t vec = data & ZPCI_MSI_VEC_MASK; + + pbdev = s390_pci_find_dev_by_fid(fid); + if (!pbdev) { + DPRINTF("add_msi_route no dev\n"); + return -ENODEV; + } + + pbdev->routes.adapter.ind_offset = vec; + + route->type = KVM_IRQ_ROUTING_S390_ADAPTER; + route->flags = 0; + route->u.adapter.summary_addr = pbdev->routes.adapter.summary_addr; + route->u.adapter.ind_addr = pbdev->routes.adapter.ind_addr; + route->u.adapter.summary_offset = pbdev->routes.adapter.summary_offset; + route->u.adapter.ind_offset = pbdev->routes.adapter.ind_offset; + route->u.adapter.adapter_id = pbdev->routes.adapter.adapter_id; + return 0; +} diff --git a/target-s390x/machine.c b/target-s390x/machine.c index fbcb0d0..bd4cea7 100644 --- a/target-s390x/machine.c +++ b/target-s390x/machine.c @@ -36,8 +36,8 @@ static int cpu_post_load(void *opaque, int version_id) const VMStateDescription vmstate_s390_cpu = { .name = "cpu", .post_load = cpu_post_load, - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (VMStateField[]) { VMSTATE_UINT64(env.fregs[0].ll, S390CPU), VMSTATE_UINT64(env.fregs[1].ll, S390CPU), @@ -71,6 +71,7 @@ const VMStateDescription vmstate_s390_cpu = { VMSTATE_UINT32_ARRAY(env.aregs, S390CPU, 16), VMSTATE_UINT64_ARRAY(env.cregs, S390CPU, 16), VMSTATE_UINT8(env.cpu_state, S390CPU), + VMSTATE_UINT8(env.sigp_order, S390CPU), VMSTATE_END_OF_LIST() }, }; diff --git a/target-s390x/mem_helper.c b/target-s390x/mem_helper.c index 5a55de8..0e8cd0f 100644 --- a/target-s390x/mem_helper.c +++ b/target-s390x/mem_helper.c @@ -65,7 +65,7 @@ static void mvc_fast_memset(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t asc = env->psw.mask & PSW_MASK_ASC; int flags; - if (mmu_translate(env, dest, 1, asc, &dest_phys, &flags)) { + if (mmu_translate(env, dest, 1, asc, &dest_phys, &flags, true)) { cpu_stb_data(env, dest, byte); cpu_abort(CPU(cpu), "should never reach here"); } @@ -90,13 +90,13 @@ static void mvc_fast_memmove(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t asc = env->psw.mask & PSW_MASK_ASC; int flags; - if (mmu_translate(env, dest, 1, asc, &dest_phys, &flags)) { + if (mmu_translate(env, dest, 1, asc, &dest_phys, &flags, true)) { cpu_stb_data(env, dest, 0); cpu_abort(CPU(cpu), "should never reach here"); } dest_phys |= dest & ~TARGET_PAGE_MASK; - if (mmu_translate(env, src, 0, asc, &src_phys, &flags)) { + if (mmu_translate(env, src, 0, asc, &src_phys, &flags, true)) { cpu_ldub_data(env, src); cpu_abort(CPU(cpu), "should never reach here"); } @@ -490,10 +490,18 @@ uint32_t HELPER(ex)(CPUS390XState *env, uint32_t cc, uint64_t v1, helper_mvc(env, l, get_address(env, 0, b1, d1), get_address(env, 0, b2, d2)); break; + case 0x400: + cc = helper_nc(env, l, get_address(env, 0, b1, d1), + get_address(env, 0, b2, d2)); + break; case 0x500: cc = helper_clc(env, l, get_address(env, 0, b1, d1), get_address(env, 0, b2, d2)); break; + case 0x600: + cc = helper_oc(env, l, get_address(env, 0, b1, d1), + get_address(env, 0, b2, d2)); + break; case 0x700: cc = helper_xc(env, l, get_address(env, 0, b1, d1), get_address(env, 0, b2, d2)); @@ -959,12 +967,12 @@ static uint32_t mvc_asc(CPUS390XState *env, int64_t l, uint64_t a1, cc = 3; } - if (mmu_translate(env, a1 & TARGET_PAGE_MASK, 1, mode1, &dest, &flags)) { + if (mmu_translate(env, a1, 1, mode1, &dest, &flags, true)) { cpu_loop_exit(CPU(s390_env_get_cpu(env))); } dest |= a1 & ~TARGET_PAGE_MASK; - if (mmu_translate(env, a2 & TARGET_PAGE_MASK, 0, mode2, &src, &flags)) { + if (mmu_translate(env, a2, 0, mode2, &src, &flags, true)) { cpu_loop_exit(CPU(s390_env_get_cpu(env))); } src |= a2 & ~TARGET_PAGE_MASK; @@ -1034,12 +1042,34 @@ void HELPER(ptlb)(CPUS390XState *env) tlb_flush(CPU(cpu), 1); } +/* load using real address */ +uint64_t HELPER(lura)(CPUS390XState *env, uint64_t addr) +{ + CPUState *cs = CPU(s390_env_get_cpu(env)); + + return (uint32_t)ldl_phys(cs->as, get_address(env, 0, 0, addr)); +} + +uint64_t HELPER(lurag)(CPUS390XState *env, uint64_t addr) +{ + CPUState *cs = CPU(s390_env_get_cpu(env)); + + return ldq_phys(cs->as, get_address(env, 0, 0, addr)); +} + /* store using real address */ void HELPER(stura)(CPUS390XState *env, uint64_t addr, uint64_t v1) { CPUState *cs = CPU(s390_env_get_cpu(env)); - stw_phys(cs->as, get_address(env, 0, 0, addr), (uint32_t)v1); + stl_phys(cs->as, get_address(env, 0, 0, addr), (uint32_t)v1); +} + +void HELPER(sturg)(CPUS390XState *env, uint64_t addr, uint64_t v1) +{ + CPUState *cs = CPU(s390_env_get_cpu(env)); + + stq_phys(cs->as, get_address(env, 0, 0, addr), v1); } /* load real address */ @@ -1058,7 +1088,7 @@ uint64_t HELPER(lra)(CPUS390XState *env, uint64_t addr) } cs->exception_index = old_exc; - if (mmu_translate(env, addr, 0, asc, &ret, &flags)) { + if (mmu_translate(env, addr, 0, asc, &ret, &flags, true)) { cc = 3; } if (cs->exception_index == EXCP_PGM) { diff --git a/target-s390x/misc_helper.c b/target-s390x/misc_helper.c index ef9758a..e1007fa 100644 --- a/target-s390x/misc_helper.c +++ b/target-s390x/misc_helper.c @@ -25,6 +25,7 @@ #include #include "sysemu/kvm.h" #include "qemu/timer.h" +#include "exec/address-spaces.h" #ifdef CONFIG_KVM #include #endif @@ -34,6 +35,7 @@ #include "sysemu/cpus.h" #include "sysemu/sysemu.h" #include "hw/s390x/ebcdic.h" +#include "hw/s390x/ipl.h" #endif /* #define DEBUG_HELPER */ @@ -151,12 +153,15 @@ static int load_normal_reset(S390CPU *cpu) return 0; } +#define DIAG_308_RC_OK 0x0001 #define DIAG_308_RC_NO_CONF 0x0102 #define DIAG_308_RC_INVALID 0x0402 + void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3) { uint64_t addr = env->regs[r1]; uint64_t subcode = env->regs[r3]; + IplParameterBlock *iplb; if (env->psw.mask & PSW_MASK_PSTATE) { program_interrupt(env, PGM_PRIVILEGED, ILEN_LATER_INC); @@ -180,14 +185,38 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3) program_interrupt(env, PGM_SPECIFICATION, ILEN_LATER_INC); return; } - env->regs[r1+1] = DIAG_308_RC_INVALID; + if (!address_space_access_valid(&address_space_memory, addr, + sizeof(IplParameterBlock), false)) { + program_interrupt(env, PGM_ADDRESSING, ILEN_LATER_INC); + return; + } + iplb = g_malloc0(sizeof(struct IplParameterBlock)); + cpu_physical_memory_read(addr, iplb, sizeof(struct IplParameterBlock)); + if (!s390_ipl_update_diag308(iplb)) { + env->regs[r1 + 1] = DIAG_308_RC_OK; + } else { + env->regs[r1 + 1] = DIAG_308_RC_INVALID; + } + g_free(iplb); return; case 6: if ((r1 & 1) || (addr & 0x0fffULL)) { program_interrupt(env, PGM_SPECIFICATION, ILEN_LATER_INC); return; } - env->regs[r1+1] = DIAG_308_RC_NO_CONF; + if (!address_space_access_valid(&address_space_memory, addr, + sizeof(IplParameterBlock), true)) { + program_interrupt(env, PGM_ADDRESSING, ILEN_LATER_INC); + return; + } + iplb = s390_ipl_get_iplb(); + if (iplb) { + cpu_physical_memory_write(addr, iplb, + sizeof(struct IplParameterBlock)); + env->regs[r1 + 1] = DIAG_308_RC_OK; + } else { + env->regs[r1 + 1] = DIAG_308_RC_NO_CONF; + } return; default: hw_error("Unhandled diag308 subcode %" PRIx64, subcode); @@ -427,7 +456,7 @@ uint32_t HELPER(stsi)(CPUS390XState *env, uint64_t a0, uint32_t HELPER(sigp)(CPUS390XState *env, uint64_t order_code, uint32_t r1, uint64_t cpu_addr) { - int cc = 0; + int cc = SIGP_CC_ORDER_CODE_ACCEPTED; HELPER_LOG("%s: %016" PRIx64 " %08x %016" PRIx64 "\n", __func__, order_code, r1, cpu_addr); @@ -461,7 +490,7 @@ uint32_t HELPER(sigp)(CPUS390XState *env, uint64_t order_code, uint32_t r1, default: /* unknown sigp */ fprintf(stderr, "XXX unknown sigp: 0x%" PRIx64 "\n", order_code); - cc = 3; + cc = SIGP_CC_NOT_OPERATIONAL; } return cc; diff --git a/target-s390x/mmu_helper.c b/target-s390x/mmu_helper.c new file mode 100644 index 0000000..b061c85 --- /dev/null +++ b/target-s390x/mmu_helper.c @@ -0,0 +1,472 @@ +/* + * S390x MMU related functions + * + * Copyright (c) 2011 Alexander Graf + * Copyright (c) 2015 Thomas Huth, 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. + */ + +#include "qemu/error-report.h" +#include "exec/address-spaces.h" +#include "sysemu/kvm.h" +#include "cpu.h" + +/* #define DEBUG_S390 */ +/* #define DEBUG_S390_PTE */ +/* #define DEBUG_S390_STDOUT */ + +#ifdef DEBUG_S390 +#ifdef DEBUG_S390_STDOUT +#define DPRINTF(fmt, ...) \ + do { fprintf(stderr, fmt, ## __VA_ARGS__); \ + qemu_log(fmt, ##__VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) \ + do { qemu_log(fmt, ## __VA_ARGS__); } while (0) +#endif +#else +#define DPRINTF(fmt, ...) \ + do { } while (0) +#endif + +#ifdef DEBUG_S390_PTE +#define PTE_DPRINTF DPRINTF +#else +#define PTE_DPRINTF(fmt, ...) \ + do { } while (0) +#endif + +/* Fetch/store bits in the translation exception code: */ +#define FS_READ 0x800 +#define FS_WRITE 0x400 + +static void trigger_access_exception(CPUS390XState *env, uint32_t type, + uint32_t ilen, uint64_t tec) +{ + S390CPU *cpu = s390_env_get_cpu(env); + + if (kvm_enabled()) { + kvm_s390_access_exception(cpu, type, tec); + } else { + CPUState *cs = CPU(cpu); + stq_phys(cs->as, env->psa + offsetof(LowCore, trans_exc_code), tec); + trigger_pgm_exception(env, type, ilen); + } +} + +static void trigger_prot_fault(CPUS390XState *env, target_ulong vaddr, + uint64_t asc, int rw, bool exc) +{ + uint64_t tec; + + tec = vaddr | (rw == 1 ? FS_WRITE : FS_READ) | 4 | asc >> 46; + + DPRINTF("%s: trans_exc_code=%016" PRIx64 "\n", __func__, tec); + + if (!exc) { + return; + } + + trigger_access_exception(env, PGM_PROTECTION, ILEN_LATER_INC, tec); +} + +static void trigger_page_fault(CPUS390XState *env, target_ulong vaddr, + uint32_t type, uint64_t asc, int rw, bool exc) +{ + int ilen = ILEN_LATER; + uint64_t tec; + + tec = vaddr | (rw == 1 ? FS_WRITE : FS_READ) | asc >> 46; + + DPRINTF("%s: vaddr=%016" PRIx64 " bits=%d\n", __func__, vaddr, bits); + + if (!exc) { + return; + } + + /* Code accesses have an undefined ilc. */ + if (rw == 2) { + ilen = 2; + } + + trigger_access_exception(env, type, ilen, tec); +} + +/** + * Translate real address to absolute (= physical) + * address by taking care of the prefix mapping. + */ +static target_ulong mmu_real2abs(CPUS390XState *env, target_ulong raddr) +{ + if (raddr < 0x2000) { + return raddr + env->psa; /* Map the lowcore. */ + } else if (raddr >= env->psa && raddr < env->psa + 0x2000) { + return raddr - env->psa; /* Map the 0 page. */ + } + return raddr; +} + +/* Decode page table entry (normal 4KB page) */ +static int mmu_translate_pte(CPUS390XState *env, target_ulong vaddr, + uint64_t asc, uint64_t pt_entry, + target_ulong *raddr, int *flags, int rw, bool exc) +{ + if (pt_entry & _PAGE_INVALID) { + DPRINTF("%s: PTE=0x%" PRIx64 " invalid\n", __func__, pt_entry); + trigger_page_fault(env, vaddr, PGM_PAGE_TRANS, asc, rw, exc); + return -1; + } + if (pt_entry & _PAGE_RES0) { + trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw, exc); + return -1; + } + if (pt_entry & _PAGE_RO) { + *flags &= ~PAGE_WRITE; + } + + *raddr = pt_entry & _ASCE_ORIGIN; + + PTE_DPRINTF("%s: PTE=0x%" PRIx64 "\n", __func__, pt_entry); + + return 0; +} + +#define VADDR_PX 0xff000 /* Page index bits */ + +/* Decode segment table entry */ +static int mmu_translate_segment(CPUS390XState *env, target_ulong vaddr, + uint64_t asc, uint64_t st_entry, + target_ulong *raddr, int *flags, int rw, + bool exc) +{ + CPUState *cs = CPU(s390_env_get_cpu(env)); + uint64_t origin, offs, pt_entry; + + if (st_entry & _SEGMENT_ENTRY_RO) { + *flags &= ~PAGE_WRITE; + } + + if ((st_entry & _SEGMENT_ENTRY_FC) && (env->cregs[0] & CR0_EDAT)) { + /* Decode EDAT1 segment frame absolute address (1MB page) */ + *raddr = (st_entry & 0xfffffffffff00000ULL) | (vaddr & 0xfffff); + PTE_DPRINTF("%s: SEG=0x%" PRIx64 "\n", __func__, st_entry); + return 0; + } + + /* Look up 4KB page entry */ + origin = st_entry & _SEGMENT_ENTRY_ORIGIN; + offs = (vaddr & VADDR_PX) >> 9; + pt_entry = ldq_phys(cs->as, origin + offs); + PTE_DPRINTF("%s: 0x%" PRIx64 " + 0x%" PRIx64 " => 0x%016" PRIx64 "\n", + __func__, origin, offs, pt_entry); + return mmu_translate_pte(env, vaddr, asc, pt_entry, raddr, flags, rw, exc); +} + +/* Decode region table entries */ +static int mmu_translate_region(CPUS390XState *env, target_ulong vaddr, + uint64_t asc, uint64_t entry, int level, + target_ulong *raddr, int *flags, int rw, + bool exc) +{ + CPUState *cs = CPU(s390_env_get_cpu(env)); + uint64_t origin, offs, new_entry; + const int pchks[4] = { + PGM_SEGMENT_TRANS, PGM_REG_THIRD_TRANS, + PGM_REG_SEC_TRANS, PGM_REG_FIRST_TRANS + }; + + PTE_DPRINTF("%s: 0x%" PRIx64 "\n", __func__, entry); + + origin = entry & _REGION_ENTRY_ORIGIN; + offs = (vaddr >> (17 + 11 * level / 4)) & 0x3ff8; + + new_entry = ldq_phys(cs->as, origin + offs); + PTE_DPRINTF("%s: 0x%" PRIx64 " + 0x%" PRIx64 " => 0x%016" PRIx64 "\n", + __func__, origin, offs, new_entry); + + if ((new_entry & _REGION_ENTRY_INV) != 0) { + DPRINTF("%s: invalid region\n", __func__); + trigger_page_fault(env, vaddr, pchks[level / 4], asc, rw, exc); + return -1; + } + + if ((new_entry & _REGION_ENTRY_TYPE_MASK) != level) { + trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw, exc); + return -1; + } + + if (level == _ASCE_TYPE_SEGMENT) { + return mmu_translate_segment(env, vaddr, asc, new_entry, raddr, flags, + rw, exc); + } + + /* Check region table offset and length */ + offs = (vaddr >> (28 + 11 * (level - 4) / 4)) & 3; + if (offs < ((new_entry & _REGION_ENTRY_TF) >> 6) + || offs > (new_entry & _REGION_ENTRY_LENGTH)) { + DPRINTF("%s: invalid offset or len (%lx)\n", __func__, new_entry); + trigger_page_fault(env, vaddr, pchks[level / 4 - 1], asc, rw, exc); + return -1; + } + + if ((env->cregs[0] & CR0_EDAT) && (new_entry & _REGION_ENTRY_RO)) { + *flags &= ~PAGE_WRITE; + } + + /* yet another region */ + return mmu_translate_region(env, vaddr, asc, new_entry, level - 4, + raddr, flags, rw, exc); +} + +static int mmu_translate_asce(CPUS390XState *env, target_ulong vaddr, + uint64_t asc, uint64_t asce, target_ulong *raddr, + int *flags, int rw, bool exc) +{ + int level; + int r; + + if (asce & _ASCE_REAL_SPACE) { + /* direct mapping */ + *raddr = vaddr; + return 0; + } + + level = asce & _ASCE_TYPE_MASK; + switch (level) { + case _ASCE_TYPE_REGION1: + if ((vaddr >> 62) > (asce & _ASCE_TABLE_LENGTH)) { + trigger_page_fault(env, vaddr, PGM_REG_FIRST_TRANS, asc, rw, exc); + return -1; + } + break; + case _ASCE_TYPE_REGION2: + if (vaddr & 0xffe0000000000000ULL) { + DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64 + " 0xffe0000000000000ULL\n", __func__, vaddr); + trigger_page_fault(env, vaddr, PGM_ASCE_TYPE, asc, rw, exc); + return -1; + } + if ((vaddr >> 51 & 3) > (asce & _ASCE_TABLE_LENGTH)) { + trigger_page_fault(env, vaddr, PGM_REG_SEC_TRANS, asc, rw, exc); + return -1; + } + break; + case _ASCE_TYPE_REGION3: + if (vaddr & 0xfffffc0000000000ULL) { + DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64 + " 0xfffffc0000000000ULL\n", __func__, vaddr); + trigger_page_fault(env, vaddr, PGM_ASCE_TYPE, asc, rw, exc); + return -1; + } + if ((vaddr >> 40 & 3) > (asce & _ASCE_TABLE_LENGTH)) { + trigger_page_fault(env, vaddr, PGM_REG_THIRD_TRANS, asc, rw, exc); + return -1; + } + break; + case _ASCE_TYPE_SEGMENT: + if (vaddr & 0xffffffff80000000ULL) { + DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64 + " 0xffffffff80000000ULL\n", __func__, vaddr); + trigger_page_fault(env, vaddr, PGM_ASCE_TYPE, asc, rw, exc); + return -1; + } + if ((vaddr >> 29 & 3) > (asce & _ASCE_TABLE_LENGTH)) { + trigger_page_fault(env, vaddr, PGM_SEGMENT_TRANS, asc, rw, exc); + return -1; + } + break; + } + + r = mmu_translate_region(env, vaddr, asc, asce, level, raddr, flags, rw, + exc); + if ((rw == 1) && !(*flags & PAGE_WRITE)) { + trigger_prot_fault(env, vaddr, asc, rw, exc); + return -1; + } + + return r; +} + +/** + * Translate a virtual (logical) address into a physical (absolute) address. + * @param vaddr the virtual address + * @param rw 0 = read, 1 = write, 2 = code fetch + * @param asc address space control (one of the PSW_ASC_* modes) + * @param raddr the translated address is stored to this pointer + * @param flags the PAGE_READ/WRITE/EXEC flags are stored to this pointer + * @param exc true = inject a program check if a fault occured + * @return 0 if the translation was successfull, -1 if a fault occured + */ +int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc, + target_ulong *raddr, int *flags, bool exc) +{ + int r = -1; + uint8_t *sk; + + *flags = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + vaddr &= TARGET_PAGE_MASK; + + if (!(env->psw.mask & PSW_MASK_DAT)) { + *raddr = vaddr; + r = 0; + goto out; + } + + switch (asc) { + case PSW_ASC_PRIMARY: + PTE_DPRINTF("%s: asc=primary\n", __func__); + r = mmu_translate_asce(env, vaddr, asc, env->cregs[1], raddr, flags, + rw, exc); + break; + case PSW_ASC_HOME: + PTE_DPRINTF("%s: asc=home\n", __func__); + r = mmu_translate_asce(env, vaddr, asc, env->cregs[13], raddr, flags, + rw, exc); + break; + case PSW_ASC_SECONDARY: + PTE_DPRINTF("%s: asc=secondary\n", __func__); + /* + * Instruction: Primary + * Data: Secondary + */ + if (rw == 2) { + r = mmu_translate_asce(env, vaddr, PSW_ASC_PRIMARY, env->cregs[1], + raddr, flags, rw, exc); + *flags &= ~(PAGE_READ | PAGE_WRITE); + } else { + r = mmu_translate_asce(env, vaddr, PSW_ASC_SECONDARY, env->cregs[7], + raddr, flags, rw, exc); + *flags &= ~(PAGE_EXEC); + } + break; + case PSW_ASC_ACCREG: + default: + hw_error("guest switched to unknown asc mode\n"); + break; + } + + out: + /* Convert real address -> absolute address */ + *raddr = mmu_real2abs(env, *raddr); + + if (*raddr <= ram_size) { + sk = &env->storage_keys[*raddr / TARGET_PAGE_SIZE]; + if (*flags & PAGE_READ) { + *sk |= SK_R; + } + + if (*flags & PAGE_WRITE) { + *sk |= SK_C; + } + } + + return r; +} + +/** + * lowprot_enabled: Check whether low-address protection is enabled + */ +static bool lowprot_enabled(const CPUS390XState *env) +{ + if (!(env->cregs[0] & CR0_LOWPROT)) { + return false; + } + if (!(env->psw.mask & PSW_MASK_DAT)) { + return true; + } + + /* Check the private-space control bit */ + switch (env->psw.mask & PSW_MASK_ASC) { + case PSW_ASC_PRIMARY: + return !(env->cregs[1] & _ASCE_PRIVATE_SPACE); + case PSW_ASC_SECONDARY: + return !(env->cregs[7] & _ASCE_PRIVATE_SPACE); + case PSW_ASC_HOME: + return !(env->cregs[13] & _ASCE_PRIVATE_SPACE); + default: + /* We don't support access register mode */ + error_report("unsupported addressing mode"); + exit(1); + } +} + +/** + * translate_pages: Translate a set of consecutive logical page addresses + * to absolute addresses + */ +static int translate_pages(S390CPU *cpu, vaddr addr, int nr_pages, + target_ulong *pages, bool is_write) +{ + bool lowprot = is_write && lowprot_enabled(&cpu->env); + uint64_t asc = cpu->env.psw.mask & PSW_MASK_ASC; + CPUS390XState *env = &cpu->env; + int ret, i, pflags; + + for (i = 0; i < nr_pages; i++) { + /* Low-address protection? */ + if (lowprot && (addr < 512 || (addr >= 4096 && addr < 4096 + 512))) { + trigger_access_exception(env, PGM_PROTECTION, ILEN_LATER_INC, 0); + return -EACCES; + } + ret = mmu_translate(env, addr, is_write, asc, &pages[i], &pflags, true); + if (ret) { + return ret; + } + if (!address_space_access_valid(&address_space_memory, pages[i], + TARGET_PAGE_SIZE, is_write)) { + program_interrupt(env, PGM_ADDRESSING, 0); + return -EFAULT; + } + addr += TARGET_PAGE_SIZE; + } + + return 0; +} + +/** + * s390_cpu_virt_mem_rw: + * @laddr: the logical start address + * @hostbuf: buffer in host memory. NULL = do only checks w/o copying + * @len: length that should be transfered + * @is_write: true = write, false = read + * Returns: 0 on success, non-zero if an exception occured + * + * Copy from/to guest memory using logical addresses. Note that we inject a + * program interrupt in case there is an error while accessing the memory. + */ +int s390_cpu_virt_mem_rw(S390CPU *cpu, vaddr laddr, void *hostbuf, + int len, bool is_write) +{ + int currlen, nr_pages, i; + target_ulong *pages; + int ret; + + nr_pages = (((laddr & ~TARGET_PAGE_MASK) + len - 1) >> TARGET_PAGE_BITS) + + 1; + pages = g_malloc(nr_pages * sizeof(*pages)); + + ret = translate_pages(cpu, laddr, nr_pages, pages, is_write); + if (ret == 0 && hostbuf != NULL) { + /* Copy data by stepping through the area page by page */ + for (i = 0; i < nr_pages; i++) { + currlen = MIN(len, TARGET_PAGE_SIZE - (laddr % TARGET_PAGE_SIZE)); + cpu_physical_memory_rw(pages[i] | (laddr & ~TARGET_PAGE_MASK), + hostbuf, currlen, is_write); + laddr += currlen; + hostbuf += currlen; + len -= currlen; + } + } + + g_free(pages); + return ret; +} diff --git a/target-s390x/translate.c b/target-s390x/translate.c index dbf1993..4f82edd 100644 --- a/target-s390x/translate.c +++ b/target-s390x/translate.c @@ -317,12 +317,14 @@ static inline void gen_illegal_opcode(DisasContext *s) gen_program_exception(s, PGM_SPECIFICATION); } -static inline void check_privileged(DisasContext *s) +#ifndef CONFIG_USER_ONLY +static void check_privileged(DisasContext *s) { if (s->tb->flags & (PSW_MASK_PSTATE >> 32)) { gen_program_exception(s, PGM_PRIVILEGED); } } +#endif static TCGv_i64 get_address(DisasContext *s, int x2, int b2, int d2) { @@ -1175,7 +1177,7 @@ static ExitStatus help_branch(DisasContext *s, DisasCompare *c, { ExitStatus ret; uint64_t dest = s->pc + 2 * imm; - int lab; + TCGLabel *lab; /* Take care of the special cases first. */ if (c->cond == TCG_COND_NEVER) { @@ -1951,7 +1953,7 @@ static ExitStatus op_cvd(DisasContext *s, DisasOps *o) static ExitStatus op_ct(DisasContext *s, DisasOps *o) { int m3 = get_field(s->fields, m3); - int lab = gen_new_label(); + TCGLabel *lab = gen_new_label(); TCGv_i32 t; TCGCond c; @@ -2045,12 +2047,37 @@ static ExitStatus op_ear(DisasContext *s, DisasOps *o) return NO_EXIT; } +static ExitStatus op_ecag(DisasContext *s, DisasOps *o) +{ + /* No cache information provided. */ + tcg_gen_movi_i64(o->out, -1); + return NO_EXIT; +} + static ExitStatus op_efpc(DisasContext *s, DisasOps *o) { tcg_gen_ld32u_i64(o->out, cpu_env, offsetof(CPUS390XState, fpc)); return NO_EXIT; } +static ExitStatus op_epsw(DisasContext *s, DisasOps *o) +{ + int r1 = get_field(s->fields, r1); + int r2 = get_field(s->fields, r2); + TCGv_i64 t = tcg_temp_new_i64(); + + /* Note the "subsequently" in the PoO, which implies a defined result + if r1 == r2. Thus we cannot defer these writes to an output hook. */ + tcg_gen_shri_i64(t, psw_mask, 32); + store_reg32_i64(r1, t); + if (r2 != 0) { + store_reg32_i64(r2, psw_mask); + } + + tcg_temp_free_i64(t); + return NO_EXIT; +} + static ExitStatus op_ex(DisasContext *s, DisasOps *o) { /* ??? Perhaps a better way to implement EXECUTE is to set a bit in @@ -2460,6 +2487,24 @@ static ExitStatus op_lm64(DisasContext *s, DisasOps *o) return NO_EXIT; } +#ifndef CONFIG_USER_ONLY +static ExitStatus op_lura(DisasContext *s, DisasOps *o) +{ + check_privileged(s); + potential_page_fault(s); + gen_helper_lura(o->out, cpu_env, o->in2); + return NO_EXIT; +} + +static ExitStatus op_lurag(DisasContext *s, DisasOps *o) +{ + check_privileged(s); + potential_page_fault(s); + gen_helper_lurag(o->out, cpu_env, o->in2); + return NO_EXIT; +} +#endif + static ExitStatus op_mov2(DisasContext *s, DisasOps *o) { o->out = o->in2; @@ -2925,19 +2970,42 @@ static ExitStatus op_sacf(DisasContext *s, DisasOps *o) /* Addressing mode has changed, so end the block. */ return EXIT_PC_STALE; } +#endif static ExitStatus op_sam(DisasContext *s, DisasOps *o) { int sam = s->insn->data; - TCGv_i64 tsam = tcg_const_i64(sam); + TCGv_i64 tsam; + uint64_t mask; - /* Overwrite PSW_MASK_64 and PSW_MASK_32 */ - tcg_gen_deposit_i64(psw_mask, psw_mask, tsam, 31, 2); + switch (sam) { + case 0: + mask = 0xffffff; + break; + case 1: + mask = 0x7fffffff; + break; + default: + mask = -1; + break; + } + + /* Bizzare but true, we check the address of the current insn for the + specification exception, not the next to be executed. Thus the PoO + documents that Bad Things Happen two bytes before the end. */ + if (s->pc & ~mask) { + gen_program_exception(s, PGM_SPECIFICATION); + return EXIT_NORETURN; + } + s->next_pc &= mask; + tsam = tcg_const_i64(sam); + tcg_gen_deposit_i64(psw_mask, psw_mask, tsam, 31, 2); tcg_temp_free_i64(tsam); + + /* Always exit the TB, since we (may have) changed execution mode. */ return EXIT_PC_STALE; } -#endif static ExitStatus op_sar(DisasContext *s, DisasOps *o) { @@ -3009,7 +3077,8 @@ static ExitStatus op_soc(DisasContext *s, DisasOps *o) { DisasCompare c; TCGv_i64 a; - int lab, r1; + TCGLabel *lab; + int r1; disas_jcc(s, &c, get_field(s->fields, m3)); @@ -3221,8 +3290,14 @@ static ExitStatus op_stctl(DisasContext *s, DisasOps *o) static ExitStatus op_stidp(DisasContext *s, DisasOps *o) { + TCGv_i64 t1 = tcg_temp_new_i64(); + check_privileged(s); tcg_gen_ld32u_i64(o->out, cpu_env, offsetof(CPUS390XState, cpu_num)); + tcg_gen_ld32u_i64(t1, cpu_env, offsetof(CPUS390XState, machine_type)); + tcg_gen_deposit_i64(o->out, o->out, t1, 32, 32); + tcg_temp_free_i64(t1); + return NO_EXIT; } @@ -3317,6 +3392,14 @@ static ExitStatus op_stura(DisasContext *s, DisasOps *o) gen_helper_stura(cpu_env, o->in2, o->in1); return NO_EXIT; } + +static ExitStatus op_sturg(DisasContext *s, DisasOps *o) +{ + check_privileged(s); + potential_page_fault(s); + gen_helper_sturg(cpu_env, o->in2, o->in1); + return NO_EXIT; +} #endif static ExitStatus op_st8(DisasContext *s, DisasOps *o) @@ -4750,7 +4833,6 @@ static inline void gen_intermediate_code_internal(S390CPU *cpu, DisasContext dc; target_ulong pc_start; uint64_t next_page_start; - uint16_t *gen_opc_end; int j, lj = -1; int num_insns, max_insns; CPUBreakpoint *bp; @@ -4769,8 +4851,6 @@ static inline void gen_intermediate_code_internal(S390CPU *cpu, dc.cc_op = CC_OP_DYNAMIC; do_debug = dc.singlestep_enabled = cs->singlestep_enabled; - gen_opc_end = tcg_ctx.gen_opc_buf + OPC_MAX_SIZE; - next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; num_insns = 0; @@ -4779,11 +4859,11 @@ static inline void gen_intermediate_code_internal(S390CPU *cpu, max_insns = CF_COUNT_MASK; } - gen_tb_start(); + gen_tb_start(tb); do { if (search_pc) { - j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + j = tcg_op_buf_count(); if (lj < j) { lj++; while (lj < j) { @@ -4821,7 +4901,7 @@ static inline void gen_intermediate_code_internal(S390CPU *cpu, or exhaust instruction count, stop generation. */ if (status == NO_EXIT && (dc.pc >= next_page_start - || tcg_ctx.gen_opc_ptr >= gen_opc_end + || tcg_op_buf_full() || num_insns >= max_insns || singlestep || cs->singlestep_enabled)) { @@ -4856,9 +4936,9 @@ static inline void gen_intermediate_code_internal(S390CPU *cpu, } gen_tb_end(tb, num_insns); - *tcg_ctx.gen_opc_ptr = INDEX_op_end; + if (search_pc) { - j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + j = tcg_op_buf_count(); lj++; while (lj <= j) { tcg_ctx.gen_opc_instr_start[lj++] = 0; diff --git a/target-sh4/cpu.h b/target-sh4/cpu.h index a2e9e2c..c8dea6c 100644 --- a/target-sh4/cpu.h +++ b/target-sh4/cpu.h @@ -23,7 +23,6 @@ #include "qemu-common.h" #define TARGET_LONG_BITS 32 -#define TARGET_HAS_ICE 1 #define ELF_MACHINE EM_SH @@ -222,14 +221,7 @@ int cpu_sh4_is_cached(CPUSH4State * env, target_ulong addr); void cpu_load_tlb(CPUSH4State * env); -static inline CPUSH4State *cpu_init(const char *cpu_model) -{ - SuperHCPU *cpu = cpu_sh4_init(cpu_model); - if (cpu == NULL) { - return NULL; - } - return &cpu->env; -} +#define cpu_init(cpu_model) CPU(cpu_sh4_init(cpu_model)) #define cpu_exec cpu_sh4_exec #define cpu_gen_code cpu_sh4_gen_code diff --git a/target-sh4/translate.c b/target-sh4/translate.c index 3088edc..41aa928 100644 --- a/target-sh4/translate.c +++ b/target-sh4/translate.c @@ -211,7 +211,7 @@ static void gen_jump(DisasContext * ctx) static inline void gen_branch_slot(uint32_t delayed_pc, int t) { TCGv sr; - int label = gen_new_label(); + TCGLabel *label = gen_new_label(); tcg_gen_movi_i32(cpu_delayed_pc, delayed_pc); sr = tcg_temp_new(); tcg_gen_andi_i32(sr, cpu_sr, SR_T); @@ -224,7 +224,7 @@ static inline void gen_branch_slot(uint32_t delayed_pc, int t) static void gen_conditional_jump(DisasContext * ctx, target_ulong ift, target_ulong ifnott) { - int l1; + TCGLabel *l1; TCGv sr; l1 = gen_new_label(); @@ -239,7 +239,7 @@ static void gen_conditional_jump(DisasContext * ctx, /* Delayed conditional jump (bt or bf) */ static void gen_delayed_conditional_jump(DisasContext * ctx) { - int l1; + TCGLabel *l1; TCGv ds; l1 = gen_new_label(); @@ -850,10 +850,10 @@ static void _decode_opc(DisasContext * ctx) return; case 0x400c: /* shad Rm,Rn */ { - int label1 = gen_new_label(); - int label2 = gen_new_label(); - int label3 = gen_new_label(); - int label4 = gen_new_label(); + TCGLabel *label1 = gen_new_label(); + TCGLabel *label2 = gen_new_label(); + TCGLabel *label3 = gen_new_label(); + TCGLabel *label4 = gen_new_label(); TCGv shift; tcg_gen_brcondi_i32(TCG_COND_LT, REG(B7_4), 0, label1); /* Rm positive, shift to the left */ @@ -885,9 +885,9 @@ static void _decode_opc(DisasContext * ctx) return; case 0x400d: /* shld Rm,Rn */ { - int label1 = gen_new_label(); - int label2 = gen_new_label(); - int label3 = gen_new_label(); + TCGLabel *label1 = gen_new_label(); + TCGLabel *label2 = gen_new_label(); + TCGLabel *label3 = gen_new_label(); TCGv shift; tcg_gen_brcondi_i32(TCG_COND_LT, REG(B7_4), 0, label1); /* Rm positive, shift to the left */ @@ -1554,7 +1554,7 @@ static void _decode_opc(DisasContext * ctx) 0 -> LDST */ if (ctx->features & SH_FEATURE_SH4A) { - int label = gen_new_label(); + TCGLabel *label = gen_new_label(); tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T); tcg_gen_or_i32(cpu_sr, cpu_sr, cpu_ldst); tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_ldst, 0, label); @@ -1865,14 +1865,12 @@ gen_intermediate_code_internal(SuperHCPU *cpu, TranslationBlock *tb, CPUSH4State *env = &cpu->env; DisasContext ctx; target_ulong pc_start; - static uint16_t *gen_opc_end; CPUBreakpoint *bp; int i, ii; int num_insns; int max_insns; pc_start = tb->pc; - gen_opc_end = tcg_ctx.gen_opc_buf + OPC_MAX_SIZE; ctx.pc = pc_start; ctx.flags = (uint32_t)tb->flags; ctx.bstate = BS_NONE; @@ -1890,8 +1888,8 @@ gen_intermediate_code_internal(SuperHCPU *cpu, TranslationBlock *tb, max_insns = tb->cflags & CF_COUNT_MASK; if (max_insns == 0) max_insns = CF_COUNT_MASK; - gen_tb_start(); - while (ctx.bstate == BS_NONE && tcg_ctx.gen_opc_ptr < gen_opc_end) { + gen_tb_start(tb); + while (ctx.bstate == BS_NONE && !tcg_op_buf_full()) { if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) { QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { if (ctx.pc == bp->pc) { @@ -1904,7 +1902,7 @@ gen_intermediate_code_internal(SuperHCPU *cpu, TranslationBlock *tb, } } if (search_pc) { - i = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + i = tcg_op_buf_count(); if (ii < i) { ii++; while (ii < i) @@ -1962,9 +1960,9 @@ gen_intermediate_code_internal(SuperHCPU *cpu, TranslationBlock *tb, } gen_tb_end(tb, num_insns); - *tcg_ctx.gen_opc_ptr = INDEX_op_end; + if (search_pc) { - i = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + i = tcg_op_buf_count(); ii++; while (ii <= i) tcg_ctx.gen_opc_instr_start[ii++] = 0; diff --git a/target-sparc/cpu.c b/target-sparc/cpu.c index aa7626c..a952097 100644 --- a/target-sparc/cpu.c +++ b/target-sparc/cpu.c @@ -111,8 +111,7 @@ static int cpu_sparc_register(SPARCCPU *cpu, const char *cpu_model) cc->parse_features(CPU(cpu), featurestr, &err); g_free(s); if (err) { - error_report("%s", error_get_pretty(err)); - error_free(err); + error_report_err(err); return -1; } diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h index 836f87f..f5c9006 100644 --- a/target-sparc/cpu.h +++ b/target-sparc/cpu.h @@ -31,8 +31,6 @@ #include "fpu/softfloat.h" -#define TARGET_HAS_ICE 1 - #if !defined(TARGET_SPARC64) #define ELF_MACHINE EM_SPARC #else @@ -596,14 +594,7 @@ hwaddr cpu_get_phys_page_nofault(CPUSPARCState *env, target_ulong addr, int cpu_sparc_signal_handler(int host_signum, void *pinfo, void *puc); #ifndef NO_CPU_IO_DEFS -static inline CPUSPARCState *cpu_init(const char *cpu_model) -{ - SPARCCPU *cpu = cpu_sparc_init(cpu_model); - if (cpu == NULL) { - return NULL; - } - return &cpu->env; -} +#define cpu_init(cpu_model) CPU(cpu_sparc_init(cpu_model)) #endif #define cpu_exec cpu_sparc_exec diff --git a/target-sparc/ldst_helper.c b/target-sparc/ldst_helper.c index 1a62e19..c7ad47d 100644 --- a/target-sparc/ldst_helper.c +++ b/target-sparc/ldst_helper.c @@ -250,6 +250,7 @@ static void replace_tlb_1bit_lru(SparcTLBEntry *tlb, #endif +#if defined(TARGET_SPARC64) || defined(CONFIG_USER_ONLY) static inline target_ulong address_mask(CPUSPARCState *env1, target_ulong addr) { #ifdef TARGET_SPARC64 @@ -259,12 +260,14 @@ static inline target_ulong address_mask(CPUSPARCState *env1, target_ulong addr) #endif return addr; } +#endif +#ifdef TARGET_SPARC64 /* returns true if access using this ASI is to have address translated by MMU otherwise access is to raw physical address */ +/* TODO: check sparc32 bits */ static inline int is_translating_asi(int asi) { -#ifdef TARGET_SPARC64 /* Ultrasparc IIi translating asi - note this list is defined by cpu implementation */ @@ -281,10 +284,6 @@ static inline int is_translating_asi(int asi) default: return 0; } -#else - /* TODO: check sparc32 bits */ - return 0; -#endif } static inline target_ulong asi_address_mask(CPUSPARCState *env, @@ -296,6 +295,7 @@ static inline target_ulong asi_address_mask(CPUSPARCState *env, return addr; } } +#endif void helper_check_align(CPUSPARCState *env, target_ulong addr, uint32_t align) { @@ -1122,17 +1122,17 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, int asi, int size, { switch (size) { case 1: - ret = ldub_raw(addr); + ret = cpu_ldub_data(env, addr); break; case 2: - ret = lduw_raw(addr); + ret = cpu_lduw_data(env, addr); break; case 4: - ret = ldl_raw(addr); + ret = cpu_ldl_data(env, addr); break; default: case 8: - ret = ldq_raw(addr); + ret = cpu_ldq_data(env, addr); break; } } @@ -1239,17 +1239,17 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val, { switch (size) { case 1: - stb_raw(addr, val); + cpu_stb_data(env, addr, val); break; case 2: - stw_raw(addr, val); + cpu_stw_data(env, addr, val); break; case 4: - stl_raw(addr, val); + cpu_stl_data(env, addr, val); break; case 8: default: - stq_raw(addr, val); + cpu_stq_data(env, addr, val); break; } } @@ -2289,8 +2289,8 @@ void helper_ldqf(CPUSPARCState *env, target_ulong addr, int mem_idx) break; } #else - u.ll.upper = ldq_raw(address_mask(env, addr)); - u.ll.lower = ldq_raw(address_mask(env, addr + 8)); + u.ll.upper = cpu_ldq_data(env, address_mask(env, addr)); + u.ll.lower = cpu_ldq_data(env, address_mask(env, addr + 8)); QT0 = u.q; #endif } @@ -2326,8 +2326,8 @@ void helper_stqf(CPUSPARCState *env, target_ulong addr, int mem_idx) } #else u.q = QT0; - stq_raw(address_mask(env, addr), u.ll.upper); - stq_raw(address_mask(env, addr + 8), u.ll.lower); + cpu_stq_data(env, address_mask(env, addr), u.ll.upper); + cpu_stq_data(env, address_mask(env, addr + 8), u.ll.lower); #endif } diff --git a/target-sparc/mmu_helper.c b/target-sparc/mmu_helper.c index 61afbcf..2a0c6f0 100644 --- a/target-sparc/mmu_helper.c +++ b/target-sparc/mmu_helper.c @@ -213,10 +213,9 @@ int sparc_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, address, rw, mmu_idx, &page_size); vaddr = address; if (error_code == 0) { -#ifdef DEBUG_MMU - printf("Translate at %" VADDR_PRIx " -> " TARGET_FMT_plx ", vaddr " - TARGET_FMT_lx "\n", address, paddr, vaddr); -#endif + qemu_log_mask(CPU_LOG_MMU, + "Translate at %" VADDR_PRIx " -> " TARGET_FMT_plx ", vaddr " + TARGET_FMT_lx "\n", address, paddr, vaddr); tlb_set_page(cs, vaddr, paddr, prot, mmu_idx, page_size); return 0; } diff --git a/target-sparc/translate.c b/target-sparc/translate.c index 78c4e21..3708c01 100644 --- a/target-sparc/translate.c +++ b/target-sparc/translate.c @@ -363,14 +363,6 @@ static inline void gen_mov_reg_C(TCGv reg, TCGv_i32 src) tcg_gen_andi_tl(reg, reg, 0x1); } -static inline void gen_op_addi_cc(TCGv dst, TCGv src1, target_long src2) -{ - tcg_gen_mov_tl(cpu_cc_src, src1); - tcg_gen_movi_tl(cpu_cc_src2, src2); - tcg_gen_addi_tl(cpu_cc_dst, cpu_cc_src, src2); - tcg_gen_mov_tl(dst, cpu_cc_dst); -} - static inline void gen_op_add_cc(TCGv dst, TCGv src1, TCGv src2) { tcg_gen_mov_tl(cpu_cc_src, src1); @@ -502,22 +494,6 @@ static void gen_op_addx_int(DisasContext *dc, TCGv dst, TCGv src1, } } -static inline void gen_op_subi_cc(TCGv dst, TCGv src1, target_long src2, DisasContext *dc) -{ - tcg_gen_mov_tl(cpu_cc_src, src1); - tcg_gen_movi_tl(cpu_cc_src2, src2); - if (src2 == 0) { - tcg_gen_mov_tl(cpu_cc_dst, src1); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_LOGIC); - dc->cc_op = CC_OP_LOGIC; - } else { - tcg_gen_subi_tl(cpu_cc_dst, cpu_cc_src, src2); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_SUB); - dc->cc_op = CC_OP_SUB; - } - tcg_gen_mov_tl(dst, cpu_cc_dst); -} - static inline void gen_op_sub_cc(TCGv dst, TCGv src1, TCGv src2) { tcg_gen_mov_tl(cpu_cc_src, src1); @@ -969,9 +945,7 @@ static inline void gen_op_eval_fbo(TCGv dst, TCGv src, static inline void gen_branch2(DisasContext *dc, target_ulong pc1, target_ulong pc2, TCGv r_cond) { - int l1; - - l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); tcg_gen_brcondi_tl(TCG_COND_EQ, r_cond, 0, l1); @@ -984,9 +958,7 @@ static inline void gen_branch2(DisasContext *dc, target_ulong pc1, static inline void gen_branch_a(DisasContext *dc, target_ulong pc1, target_ulong pc2, TCGv r_cond) { - int l1; - - l1 = gen_new_label(); + TCGLabel *l1 = gen_new_label(); tcg_gen_brcondi_tl(TCG_COND_EQ, r_cond, 0, l1); @@ -2324,6 +2296,7 @@ static void gen_fmovq(DisasContext *dc, DisasCompare *cmp, int rd, int rs) gen_update_fprs_dirty(qd); } +#ifndef CONFIG_USER_ONLY static inline void gen_load_trap_state_at_tl(TCGv_ptr r_tsptr, TCGv_ptr cpu_env) { TCGv_i32 r_tl = tcg_temp_new_i32(); @@ -2348,6 +2321,7 @@ static inline void gen_load_trap_state_at_tl(TCGv_ptr r_tsptr, TCGv_ptr cpu_env) tcg_temp_free_i32(r_tl); } +#endif static void gen_edge(DisasContext *dc, TCGv dst, TCGv s1, TCGv s2, int width, bool cc, bool left) @@ -2627,7 +2601,8 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) if (xop == 0x3a) { /* generate trap */ int cond = GET_FIELD(insn, 3, 6); TCGv_i32 trap; - int l1 = -1, mask; + TCGLabel *l1 = NULL; + int mask; if (cond == 0) { /* Trap never. */ @@ -5245,7 +5220,6 @@ static inline void gen_intermediate_code_internal(SPARCCPU *cpu, CPUState *cs = CPU(cpu); CPUSPARCState *env = &cpu->env; target_ulong pc_start, last_pc; - uint16_t *gen_opc_end; DisasContext dc1, *dc = &dc1; CPUBreakpoint *bp; int j, lj = -1; @@ -5265,13 +5239,12 @@ static inline void gen_intermediate_code_internal(SPARCCPU *cpu, dc->fpu_enabled = tb_fpu_enabled(tb->flags); dc->address_mask_32bit = tb_am_enabled(tb->flags); dc->singlestep = (cs->singlestep_enabled || singlestep); - gen_opc_end = tcg_ctx.gen_opc_buf + OPC_MAX_SIZE; num_insns = 0; max_insns = tb->cflags & CF_COUNT_MASK; if (max_insns == 0) max_insns = CF_COUNT_MASK; - gen_tb_start(); + gen_tb_start(tb); do { if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) { QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { @@ -5287,7 +5260,7 @@ static inline void gen_intermediate_code_internal(SPARCCPU *cpu, } if (spc) { qemu_log("Search PC...\n"); - j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + j = tcg_op_buf_count(); if (lj < j) { lj++; while (lj < j) @@ -5320,7 +5293,7 @@ static inline void gen_intermediate_code_internal(SPARCCPU *cpu, if (dc->singlestep) { break; } - } while ((tcg_ctx.gen_opc_ptr < gen_opc_end) && + } while (!tcg_op_buf_full() && (dc->pc - pc_start) < (TARGET_PAGE_SIZE - 32) && num_insns < max_insns); @@ -5342,9 +5315,9 @@ static inline void gen_intermediate_code_internal(SPARCCPU *cpu, } } gen_tb_end(tb, num_insns); - *tcg_ctx.gen_opc_ptr = INDEX_op_end; + if (spc) { - j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + j = tcg_op_buf_count(); lj++; while (lj <= j) tcg_ctx.gen_opc_instr_start[lj++] = 0; diff --git a/target-tricore/cpu.c b/target-tricore/cpu.c index 7bf041a..2ba0cf4 100644 --- a/target-tricore/cpu.c +++ b/target-tricore/cpu.c @@ -63,8 +63,17 @@ static bool tricore_cpu_has_work(CPUState *cs) static void tricore_cpu_realizefn(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); + TriCoreCPU *cpu = TRICORE_CPU(dev); TriCoreCPUClass *tcc = TRICORE_CPU_GET_CLASS(dev); + CPUTriCoreState *env = &cpu->env; + /* Some features automatically imply others */ + if (tricore_feature(env, TRICORE_FEATURE_16)) { + set_feature(env, TRICORE_FEATURE_131); + } + if (tricore_feature(env, TRICORE_FEATURE_131)) { + set_feature(env, TRICORE_FEATURE_13); + } cpu_reset(cs); qemu_init_vcpu(cs); @@ -109,7 +118,7 @@ static void tc1796_initfn(Object *obj) { TriCoreCPU *cpu = TRICORE_CPU(obj); - set_feature(&cpu->env, TRICORE_FEATURE_13); + set_feature(&cpu->env, TRICORE_FEATURE_131); } static void aurix_initfn(Object *obj) diff --git a/target-tricore/cpu.h b/target-tricore/cpu.h index 7555b70..90bf006 100644 --- a/target-tricore/cpu.h +++ b/target-tricore/cpu.h @@ -238,6 +238,14 @@ struct CPUTriCoreState { #define MASK_LCX_LCXS 0x000f0000 #define MASK_LCX_LCX0 0x0000ffff +#define MASK_DBGSR_DE 0x1 +#define MASK_DBGSR_HALT 0x6 +#define MASK_DBGSR_SUSP 0x10 +#define MASK_DBGSR_PREVSUSP 0x20 +#define MASK_DBGSR_PEVT 0x40 +#define MASK_DBGSR_EVTSRC 0x1f00 + +#define TRICORE_HFLAG_KUU 0x3 #define TRICORE_HFLAG_UM0 0x00002 /* user mode-0 flag */ #define TRICORE_HFLAG_UM1 0x00001 /* user mode-1 flag */ #define TRICORE_HFLAG_SM 0x00000 /* kernel mode flag */ @@ -377,15 +385,7 @@ static inline void cpu_get_tb_cpu_state(CPUTriCoreState *env, target_ulong *pc, TriCoreCPU *cpu_tricore_init(const char *cpu_model); -static inline CPUTriCoreState *cpu_init(const char *cpu_model) -{ - TriCoreCPU *cpu = cpu_tricore_init(cpu_model); - if (cpu == NULL) { - return NULL; - } - return &cpu->env; - -} +#define cpu_init(cpu_model) CPU(cpu_tricore_init(cpu_model)) /* helpers.c */ diff --git a/target-tricore/csfr.def b/target-tricore/csfr.def new file mode 100644 index 0000000..05c45dd --- /dev/null +++ b/target-tricore/csfr.def @@ -0,0 +1,124 @@ +/* A(ll) access permited + R(ead only) access + E(nd init protected) access + + A|R|E(offset, register, feature introducing reg) + + NOTE: PSW is handled as a special case in gen_mtcr/mfcr */ + +A(0xfe00, PCXI, TRICORE_FEATURE_13) +A(0xfe08, PC, TRICORE_FEATURE_13) +A(0xfe14, SYSCON, TRICORE_FEATURE_13) +R(0xfe18, CPU_ID, TRICORE_FEATURE_13) +E(0xfe20, BIV, TRICORE_FEATURE_13) +E(0xfe24, BTV, TRICORE_FEATURE_13) +E(0xfe28, ISP, TRICORE_FEATURE_13) +A(0xfe2c, ICR, TRICORE_FEATURE_13) +A(0xfe38, FCX, TRICORE_FEATURE_13) +A(0xfe3c, LCX, TRICORE_FEATURE_13) +E(0x9400, COMPAT, TRICORE_FEATURE_131) +/* memory protection register */ +A(0xC000, DPR0_0L, TRICORE_FEATURE_13) +A(0xC004, DPR0_0U, TRICORE_FEATURE_13) +A(0xC008, DPR0_1L, TRICORE_FEATURE_13) +A(0xC00C, DPR0_1U, TRICORE_FEATURE_13) +A(0xC010, DPR0_2L, TRICORE_FEATURE_13) +A(0xC014, DPR0_2U, TRICORE_FEATURE_13) +A(0xC018, DPR0_3L, TRICORE_FEATURE_13) +A(0xC01C, DPR0_3U, TRICORE_FEATURE_13) +A(0xC400, DPR1_0L, TRICORE_FEATURE_13) +A(0xC404, DPR1_0U, TRICORE_FEATURE_13) +A(0xC408, DPR1_1L, TRICORE_FEATURE_13) +A(0xC40C, DPR1_1U, TRICORE_FEATURE_13) +A(0xC410, DPR1_2L, TRICORE_FEATURE_13) +A(0xC414, DPR1_2U, TRICORE_FEATURE_13) +A(0xC418, DPR1_3L, TRICORE_FEATURE_13) +A(0xC41C, DPR1_3U, TRICORE_FEATURE_13) +A(0xC800, DPR2_0L, TRICORE_FEATURE_13) +A(0xC804, DPR2_0U, TRICORE_FEATURE_13) +A(0xC808, DPR2_1L, TRICORE_FEATURE_13) +A(0xC80C, DPR2_1U, TRICORE_FEATURE_13) +A(0xC810, DPR2_2L, TRICORE_FEATURE_13) +A(0xC814, DPR2_2U, TRICORE_FEATURE_13) +A(0xC818, DPR2_3L, TRICORE_FEATURE_13) +A(0xC81C, DPR2_3U, TRICORE_FEATURE_13) +A(0xCC00, DPR3_0L, TRICORE_FEATURE_13) +A(0xCC04, DPR3_0U, TRICORE_FEATURE_13) +A(0xCC08, DPR3_1L, TRICORE_FEATURE_13) +A(0xCC0C, DPR3_1U, TRICORE_FEATURE_13) +A(0xCC10, DPR3_2L, TRICORE_FEATURE_13) +A(0xCC14, DPR3_2U, TRICORE_FEATURE_13) +A(0xCC18, DPR3_3L, TRICORE_FEATURE_13) +A(0xCC1C, DPR3_3U, TRICORE_FEATURE_13) +A(0xD000, CPR0_0L, TRICORE_FEATURE_13) +A(0xD004, CPR0_0U, TRICORE_FEATURE_13) +A(0xD008, CPR0_1L, TRICORE_FEATURE_13) +A(0xD00C, CPR0_1U, TRICORE_FEATURE_13) +A(0xD010, CPR0_2L, TRICORE_FEATURE_13) +A(0xD014, CPR0_2U, TRICORE_FEATURE_13) +A(0xD018, CPR0_3L, TRICORE_FEATURE_13) +A(0xD01C, CPR0_3U, TRICORE_FEATURE_13) +A(0xD400, CPR1_0L, TRICORE_FEATURE_13) +A(0xD404, CPR1_0U, TRICORE_FEATURE_13) +A(0xD408, CPR1_1L, TRICORE_FEATURE_13) +A(0xD40C, CPR1_1U, TRICORE_FEATURE_13) +A(0xD410, CPR1_2L, TRICORE_FEATURE_13) +A(0xD414, CPR1_2U, TRICORE_FEATURE_13) +A(0xD418, CPR1_3L, TRICORE_FEATURE_13) +A(0xD41C, CPR1_3U, TRICORE_FEATURE_13) +A(0xD800, CPR2_0L, TRICORE_FEATURE_13) +A(0xD804, CPR2_0U, TRICORE_FEATURE_13) +A(0xD808, CPR2_1L, TRICORE_FEATURE_13) +A(0xD80C, CPR2_1U, TRICORE_FEATURE_13) +A(0xD810, CPR2_2L, TRICORE_FEATURE_13) +A(0xD814, CPR2_2U, TRICORE_FEATURE_13) +A(0xD818, CPR2_3L, TRICORE_FEATURE_13) +A(0xD81C, CPR2_3U, TRICORE_FEATURE_13) +A(0xDC00, CPR3_0L, TRICORE_FEATURE_13) +A(0xDC04, CPR3_0U, TRICORE_FEATURE_13) +A(0xDC08, CPR3_1L, TRICORE_FEATURE_13) +A(0xDC0C, CPR3_1U, TRICORE_FEATURE_13) +A(0xDC10, CPR3_2L, TRICORE_FEATURE_13) +A(0xDC14, CPR3_2U, TRICORE_FEATURE_13) +A(0xDC18, CPR3_3L, TRICORE_FEATURE_13) +A(0xDC1C, CPR3_3U, TRICORE_FEATURE_13) +A(0xE000, DPM0, TRICORE_FEATURE_13) +A(0xE080, DPM1, TRICORE_FEATURE_13) +A(0xE100, DPM2, TRICORE_FEATURE_13) +A(0xE180, DPM3, TRICORE_FEATURE_13) +A(0xE200, CPM0, TRICORE_FEATURE_13) +A(0xE280, CPM1, TRICORE_FEATURE_13) +A(0xE300, CPM2, TRICORE_FEATURE_13) +A(0xE380, CPM3, TRICORE_FEATURE_13) +/* memory management registers */ +A(0x8000, MMU_CON, TRICORE_FEATURE_13) +A(0x8004, MMU_ASI, TRICORE_FEATURE_13) +A(0x800C, MMU_TVA, TRICORE_FEATURE_13) +A(0x8010, MMU_TPA, TRICORE_FEATURE_13) +A(0x8014, MMU_TPX, TRICORE_FEATURE_13) +A(0x8018, MMU_TFA, TRICORE_FEATURE_13) +E(0x9004, BMACON, TRICORE_FEATURE_131) +E(0x900C, SMACON, TRICORE_FEATURE_131) +A(0x9020, DIEAR, TRICORE_FEATURE_131) +A(0x9024, DIETR, TRICORE_FEATURE_131) +A(0x9028, CCDIER, TRICORE_FEATURE_131) +E(0x9044, MIECON, TRICORE_FEATURE_131) +A(0x9210, PIEAR, TRICORE_FEATURE_131) +A(0x9214, PIETR, TRICORE_FEATURE_131) +A(0x9218, CCPIER, TRICORE_FEATURE_131) +/* debug registers */ +A(0xFD00, DBGSR, TRICORE_FEATURE_13) +A(0xFD08, EXEVT, TRICORE_FEATURE_13) +A(0xFD0C, CREVT, TRICORE_FEATURE_13) +A(0xFD10, SWEVT, TRICORE_FEATURE_13) +A(0xFD20, TR0EVT, TRICORE_FEATURE_13) +A(0xFD24, TR1EVT, TRICORE_FEATURE_13) +A(0xFD40, DMS, TRICORE_FEATURE_13) +A(0xFD44, DCX, TRICORE_FEATURE_13) +A(0xFD48, DBGTCR, TRICORE_FEATURE_131) +A(0xFC00, CCTRL, TRICORE_FEATURE_131) +A(0xFC04, CCNT, TRICORE_FEATURE_131) +A(0xFC08, ICNT, TRICORE_FEATURE_131) +A(0xFC0C, M1CNT, TRICORE_FEATURE_131) +A(0xFC10, M2CNT, TRICORE_FEATURE_131) +A(0xFC14, M3CNT, TRICORE_FEATURE_131) diff --git a/target-tricore/helper.h b/target-tricore/helper.h index b3fc33c..1a49b00 100644 --- a/target-tricore/helper.h +++ b/target-tricore/helper.h @@ -17,16 +17,121 @@ /* Arithmetic */ DEF_HELPER_3(add_ssov, i32, env, i32, i32) +DEF_HELPER_3(add64_ssov, i64, env, i64, i64) +DEF_HELPER_3(add_suov, i32, env, i32, i32) +DEF_HELPER_3(add_h_ssov, i32, env, i32, i32) +DEF_HELPER_3(add_h_suov, i32, env, i32, i32) +DEF_HELPER_4(addr_h_ssov, i32, env, i64, i32, i32) +DEF_HELPER_4(addsur_h_ssov, i32, env, i64, i32, i32) DEF_HELPER_3(sub_ssov, i32, env, i32, i32) +DEF_HELPER_3(sub64_ssov, i64, env, i64, i64) +DEF_HELPER_3(sub_suov, i32, env, i32, i32) +DEF_HELPER_3(sub_h_ssov, i32, env, i32, i32) +DEF_HELPER_3(sub_h_suov, i32, env, i32, i32) +DEF_HELPER_4(subr_h_ssov, i32, env, i64, i32, i32) +DEF_HELPER_4(subadr_h_ssov, i32, env, i64, i32, i32) +DEF_HELPER_3(mul_ssov, i32, env, i32, i32) +DEF_HELPER_3(mul_suov, i32, env, i32, i32) +DEF_HELPER_3(sha_ssov, i32, env, i32, i32) +DEF_HELPER_3(absdif_ssov, i32, env, i32, i32) +DEF_HELPER_4(madd32_ssov, i32, env, i32, i32, i32) +DEF_HELPER_4(madd32_suov, i32, env, i32, i32, i32) +DEF_HELPER_4(madd64_ssov, i64, env, i32, i64, i32) +DEF_HELPER_5(madd64_q_ssov, i64, env, i64, i32, i32, i32) +DEF_HELPER_3(madd32_q_add_ssov, i32, env, i64, i64) +DEF_HELPER_5(maddr_q_ssov, i32, env, i32, i32, i32, i32) +DEF_HELPER_4(madd64_suov, i64, env, i32, i64, i32) +DEF_HELPER_4(msub32_ssov, i32, env, i32, i32, i32) +DEF_HELPER_4(msub32_suov, i32, env, i32, i32, i32) +DEF_HELPER_4(msub64_ssov, i64, env, i32, i64, i32) +DEF_HELPER_5(msub64_q_ssov, i64, env, i64, i32, i32, i32) +DEF_HELPER_3(msub32_q_sub_ssov, i32, env, i64, i64) +DEF_HELPER_5(msubr_q_ssov, i32, env, i32, i32, i32, i32) +DEF_HELPER_4(msub64_suov, i64, env, i32, i64, i32) +DEF_HELPER_3(absdif_h_ssov, i32, env, i32, i32) +DEF_HELPER_2(abs_ssov, i32, env, i32) +DEF_HELPER_2(abs_h_ssov, i32, env, i32) +/* hword/byte arithmetic */ +DEF_HELPER_2(abs_b, i32, env, i32) +DEF_HELPER_2(abs_h, i32, env, i32) +DEF_HELPER_3(absdif_b, i32, env, i32, i32) +DEF_HELPER_3(absdif_h, i32, env, i32, i32) +DEF_HELPER_4(addr_h, i32, env, i64, i32, i32) +DEF_HELPER_4(addsur_h, i32, env, i64, i32, i32) +DEF_HELPER_5(maddr_q, i32, env, i32, i32, i32, i32) +DEF_HELPER_3(add_b, i32, env, i32, i32) +DEF_HELPER_3(add_h, i32, env, i32, i32) +DEF_HELPER_3(sub_b, i32, env, i32, i32) +DEF_HELPER_3(sub_h, i32, env, i32, i32) +DEF_HELPER_4(subr_h, i32, env, i64, i32, i32) +DEF_HELPER_4(subadr_h, i32, env, i64, i32, i32) +DEF_HELPER_5(msubr_q, i32, env, i32, i32, i32, i32) +DEF_HELPER_FLAGS_2(eq_b, TCG_CALL_NO_RWG_SE, i32, i32, i32) +DEF_HELPER_FLAGS_2(eq_h, TCG_CALL_NO_RWG_SE, i32, i32, i32) +DEF_HELPER_FLAGS_2(eqany_b, TCG_CALL_NO_RWG_SE, i32, i32, i32) +DEF_HELPER_FLAGS_2(eqany_h, TCG_CALL_NO_RWG_SE, i32, i32, i32) +DEF_HELPER_FLAGS_2(lt_b, TCG_CALL_NO_RWG_SE, i32, i32, i32) +DEF_HELPER_FLAGS_2(lt_bu, TCG_CALL_NO_RWG_SE, i32, i32, i32) +DEF_HELPER_FLAGS_2(lt_h, TCG_CALL_NO_RWG_SE, i32, i32, i32) +DEF_HELPER_FLAGS_2(lt_hu, TCG_CALL_NO_RWG_SE, i32, i32, i32) +DEF_HELPER_FLAGS_2(max_b, TCG_CALL_NO_RWG_SE, i32, i32, i32) +DEF_HELPER_FLAGS_2(max_bu, TCG_CALL_NO_RWG_SE, i32, i32, i32) +DEF_HELPER_FLAGS_2(max_h, TCG_CALL_NO_RWG_SE, i32, i32, i32) +DEF_HELPER_FLAGS_2(max_hu, TCG_CALL_NO_RWG_SE, i32, i32, i32) +DEF_HELPER_FLAGS_2(ixmax, TCG_CALL_NO_RWG_SE, i64, i64, i32) +DEF_HELPER_FLAGS_2(ixmax_u, TCG_CALL_NO_RWG_SE, i64, i64, i32) +DEF_HELPER_FLAGS_2(min_b, TCG_CALL_NO_RWG_SE, i32, i32, i32) +DEF_HELPER_FLAGS_2(min_bu, TCG_CALL_NO_RWG_SE, i32, i32, i32) +DEF_HELPER_FLAGS_2(min_h, TCG_CALL_NO_RWG_SE, i32, i32, i32) +DEF_HELPER_FLAGS_2(min_hu, TCG_CALL_NO_RWG_SE, i32, i32, i32) +DEF_HELPER_FLAGS_2(ixmin, TCG_CALL_NO_RWG_SE, i64, i64, i32) +DEF_HELPER_FLAGS_2(ixmin_u, TCG_CALL_NO_RWG_SE, i64, i64, i32) +/* count leading ... */ +DEF_HELPER_FLAGS_1(clo, TCG_CALL_NO_RWG_SE, i32, i32) +DEF_HELPER_FLAGS_1(clo_h, TCG_CALL_NO_RWG_SE, i32, i32) +DEF_HELPER_FLAGS_1(clz, TCG_CALL_NO_RWG_SE, i32, i32) +DEF_HELPER_FLAGS_1(clz_h, TCG_CALL_NO_RWG_SE, i32, i32) +DEF_HELPER_FLAGS_1(cls, TCG_CALL_NO_RWG_SE, i32, i32) +DEF_HELPER_FLAGS_1(cls_h, TCG_CALL_NO_RWG_SE, i32, i32) +/* sh */ +DEF_HELPER_FLAGS_2(sh, TCG_CALL_NO_RWG_SE, i32, i32, i32) +DEF_HELPER_FLAGS_2(sh_h, TCG_CALL_NO_RWG_SE, i32, i32, i32) +DEF_HELPER_3(sha, i32, env, i32, i32) +DEF_HELPER_2(sha_h, i32, i32, i32) +/* merge/split/parity */ +DEF_HELPER_FLAGS_2(bmerge, TCG_CALL_NO_RWG_SE, i32, i32, i32) +DEF_HELPER_FLAGS_1(bsplit, TCG_CALL_NO_RWG_SE, i64, i32) +DEF_HELPER_FLAGS_1(parity, TCG_CALL_NO_RWG_SE, i32, i32) +/* float */ +DEF_HELPER_FLAGS_4(pack, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32, i32) +DEF_HELPER_1(unpack, i64, i32) +/* dvinit */ +DEF_HELPER_3(dvinit_b_13, i64, env, i32, i32) +DEF_HELPER_3(dvinit_b_131, i64, env, i32, i32) +DEF_HELPER_3(dvinit_h_13, i64, env, i32, i32) +DEF_HELPER_3(dvinit_h_131, i64, env, i32, i32) +DEF_HELPER_FLAGS_2(dvadj, TCG_CALL_NO_RWG_SE, i64, i64, i32) +DEF_HELPER_FLAGS_2(dvstep, TCG_CALL_NO_RWG_SE, i64, i64, i32) +DEF_HELPER_FLAGS_2(dvstep_u, TCG_CALL_NO_RWG_SE, i64, i64, i32) +/* mulh */ +DEF_HELPER_FLAGS_5(mul_h, TCG_CALL_NO_RWG_SE, i64, i32, i32, i32, i32, i32) +DEF_HELPER_FLAGS_5(mulm_h, TCG_CALL_NO_RWG_SE, i64, i32, i32, i32, i32, i32) +DEF_HELPER_FLAGS_5(mulr_h, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32, i32, i32) /* CSA */ DEF_HELPER_2(call, void, env, i32) DEF_HELPER_1(ret, void, env) DEF_HELPER_2(bisr, void, env, i32) DEF_HELPER_1(rfe, void, env) +DEF_HELPER_1(rfm, void, env) DEF_HELPER_2(ldlcx, void, env, i32) DEF_HELPER_2(lducx, void, env, i32) DEF_HELPER_2(stlcx, void, env, i32) DEF_HELPER_2(stucx, void, env, i32) +DEF_HELPER_1(svlcx, void, env) +DEF_HELPER_1(rslcx, void, env) /* Address mode helper */ DEF_HELPER_1(br_update, i32, i32) DEF_HELPER_2(circ_update, i32, i32, i32) +/* PSW cache helper */ +DEF_HELPER_2(psw_write, void, env, i32) +DEF_HELPER_1(psw_read, i32, env) diff --git a/target-tricore/op_helper.c b/target-tricore/op_helper.c index a36988a..9907e07 100644 --- a/target-tricore/op_helper.c +++ b/target-tricore/op_helper.c @@ -56,48 +56,2114 @@ uint32_t helper_circ_update(uint32_t reg, uint32_t off) return reg - index + new_index; } -#define SSOV(env, ret, arg, len) do { \ - int64_t max_pos = INT##len ##_MAX; \ - int64_t max_neg = INT##len ##_MIN; \ - if (arg > max_pos) { \ - env->PSW_USB_V = (1 << 31); \ - env->PSW_USB_SV = (1 << 31); \ - ret = (target_ulong)max_pos; \ - } else { \ - if (arg < max_neg) { \ - env->PSW_USB_V = (1 << 31); \ - env->PSW_USB_SV = (1 << 31); \ - ret = (target_ulong)max_neg; \ - } else { \ - env->PSW_USB_V = 0; \ - ret = (target_ulong)arg; \ - } \ - } \ - env->PSW_USB_AV = arg ^ arg * 2u; \ - env->PSW_USB_SAV |= env->PSW_USB_AV; \ -} while (0) +static uint32_t ssov32(CPUTriCoreState *env, int64_t arg) +{ + uint32_t ret; + int64_t max_pos = INT32_MAX; + int64_t max_neg = INT32_MIN; + if (arg > max_pos) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV = (1 << 31); + ret = (target_ulong)max_pos; + } else { + if (arg < max_neg) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV = (1 << 31); + ret = (target_ulong)max_neg; + } else { + env->PSW_USB_V = 0; + ret = (target_ulong)arg; + } + } + env->PSW_USB_AV = arg ^ arg * 2u; + env->PSW_USB_SAV |= env->PSW_USB_AV; + return ret; +} + +static uint32_t suov32_pos(CPUTriCoreState *env, uint64_t arg) +{ + uint32_t ret; + uint64_t max_pos = UINT32_MAX; + if (arg > max_pos) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV = (1 << 31); + ret = (target_ulong)max_pos; + } else { + env->PSW_USB_V = 0; + ret = (target_ulong)arg; + } + env->PSW_USB_AV = arg ^ arg * 2u; + env->PSW_USB_SAV |= env->PSW_USB_AV; + return ret; +} + +static uint32_t suov32_neg(CPUTriCoreState *env, int64_t arg) +{ + uint32_t ret; + + if (arg < 0) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV = (1 << 31); + ret = 0; + } else { + env->PSW_USB_V = 0; + ret = (target_ulong)arg; + } + env->PSW_USB_AV = arg ^ arg * 2u; + env->PSW_USB_SAV |= env->PSW_USB_AV; + return ret; +} + +static uint32_t ssov16(CPUTriCoreState *env, int32_t hw0, int32_t hw1) +{ + int32_t max_pos = INT16_MAX; + int32_t max_neg = INT16_MIN; + int32_t av0, av1; + + env->PSW_USB_V = 0; + av0 = hw0 ^ hw0 * 2u; + if (hw0 > max_pos) { + env->PSW_USB_V = (1 << 31); + hw0 = max_pos; + } else if (hw0 < max_neg) { + env->PSW_USB_V = (1 << 31); + hw0 = max_neg; + } + + av1 = hw1 ^ hw1 * 2u; + if (hw1 > max_pos) { + env->PSW_USB_V = (1 << 31); + hw1 = max_pos; + } else if (hw1 < max_neg) { + env->PSW_USB_V = (1 << 31); + hw1 = max_neg; + } + + env->PSW_USB_SV |= env->PSW_USB_V; + env->PSW_USB_AV = (av0 | av1) << 16; + env->PSW_USB_SAV |= env->PSW_USB_AV; + return (hw0 & 0xffff) | (hw1 << 16); +} + +static uint32_t suov16(CPUTriCoreState *env, int32_t hw0, int32_t hw1) +{ + int32_t max_pos = UINT16_MAX; + int32_t av0, av1; + + env->PSW_USB_V = 0; + av0 = hw0 ^ hw0 * 2u; + if (hw0 > max_pos) { + env->PSW_USB_V = (1 << 31); + hw0 = max_pos; + } else if (hw0 < 0) { + env->PSW_USB_V = (1 << 31); + hw0 = 0; + } + + av1 = hw1 ^ hw1 * 2u; + if (hw1 > max_pos) { + env->PSW_USB_V = (1 << 31); + hw1 = max_pos; + } else if (hw1 < 0) { + env->PSW_USB_V = (1 << 31); + hw1 = 0; + } + + env->PSW_USB_SV |= env->PSW_USB_V; + env->PSW_USB_AV = (av0 | av1) << 16; + env->PSW_USB_SAV |= env->PSW_USB_AV; + return (hw0 & 0xffff) | (hw1 << 16); +} + +target_ulong helper_add_ssov(CPUTriCoreState *env, target_ulong r1, + target_ulong r2) +{ + int64_t t1 = sextract64(r1, 0, 32); + int64_t t2 = sextract64(r2, 0, 32); + int64_t result = t1 + t2; + return ssov32(env, result); +} + +uint64_t helper_add64_ssov(CPUTriCoreState *env, uint64_t r1, uint64_t r2) +{ + uint64_t result; + int64_t ovf; + + result = r1 + r2; + ovf = (result ^ r1) & ~(r1 ^ r2); + env->PSW_USB_AV = (result ^ result * 2u) >> 32; + env->PSW_USB_SAV |= env->PSW_USB_AV; + if (ovf < 0) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV = (1 << 31); + /* ext_ret > MAX_INT */ + if ((int64_t)r1 >= 0) { + result = INT64_MAX; + /* ext_ret < MIN_INT */ + } else { + result = INT64_MIN; + } + } else { + env->PSW_USB_V = 0; + } + return result; +} + +target_ulong helper_add_h_ssov(CPUTriCoreState *env, target_ulong r1, + target_ulong r2) +{ + int32_t ret_hw0, ret_hw1; + + ret_hw0 = sextract32(r1, 0, 16) + sextract32(r2, 0, 16); + ret_hw1 = sextract32(r1, 16, 16) + sextract32(r2, 16, 16); + return ssov16(env, ret_hw0, ret_hw1); +} + +uint32_t helper_addr_h_ssov(CPUTriCoreState *env, uint64_t r1, uint32_t r2_l, + uint32_t r2_h) +{ + int64_t mul_res0 = sextract64(r1, 0, 32); + int64_t mul_res1 = sextract64(r1, 32, 32); + int64_t r2_low = sextract64(r2_l, 0, 32); + int64_t r2_high = sextract64(r2_h, 0, 32); + int64_t result0, result1; + uint32_t ovf0, ovf1; + uint32_t avf0, avf1; + + ovf0 = ovf1 = 0; + + result0 = r2_low + mul_res0 + 0x8000; + result1 = r2_high + mul_res1 + 0x8000; + + avf0 = result0 * 2u; + avf0 = result0 ^ avf0; + avf1 = result1 * 2u; + avf1 = result1 ^ avf1; + + if (result0 > INT32_MAX) { + ovf0 = (1 << 31); + result0 = INT32_MAX; + } else if (result0 < INT32_MIN) { + ovf0 = (1 << 31); + result0 = INT32_MIN; + } + + if (result1 > INT32_MAX) { + ovf1 = (1 << 31); + result1 = INT32_MAX; + } else if (result1 < INT32_MIN) { + ovf1 = (1 << 31); + result1 = INT32_MIN; + } + + env->PSW_USB_V = ovf0 | ovf1; + env->PSW_USB_SV |= env->PSW_USB_V; + + env->PSW_USB_AV = avf0 | avf1; + env->PSW_USB_SAV |= env->PSW_USB_AV; + + return (result1 & 0xffff0000ULL) | ((result0 >> 16) & 0xffffULL); +} + +uint32_t helper_addsur_h_ssov(CPUTriCoreState *env, uint64_t r1, uint32_t r2_l, + uint32_t r2_h) +{ + int64_t mul_res0 = sextract64(r1, 0, 32); + int64_t mul_res1 = sextract64(r1, 32, 32); + int64_t r2_low = sextract64(r2_l, 0, 32); + int64_t r2_high = sextract64(r2_h, 0, 32); + int64_t result0, result1; + uint32_t ovf0, ovf1; + uint32_t avf0, avf1; + + ovf0 = ovf1 = 0; + + result0 = r2_low - mul_res0 + 0x8000; + result1 = r2_high + mul_res1 + 0x8000; + + avf0 = result0 * 2u; + avf0 = result0 ^ avf0; + avf1 = result1 * 2u; + avf1 = result1 ^ avf1; + + if (result0 > INT32_MAX) { + ovf0 = (1 << 31); + result0 = INT32_MAX; + } else if (result0 < INT32_MIN) { + ovf0 = (1 << 31); + result0 = INT32_MIN; + } + + if (result1 > INT32_MAX) { + ovf1 = (1 << 31); + result1 = INT32_MAX; + } else if (result1 < INT32_MIN) { + ovf1 = (1 << 31); + result1 = INT32_MIN; + } + + env->PSW_USB_V = ovf0 | ovf1; + env->PSW_USB_SV |= env->PSW_USB_V; + + env->PSW_USB_AV = avf0 | avf1; + env->PSW_USB_SAV |= env->PSW_USB_AV; + + return (result1 & 0xffff0000ULL) | ((result0 >> 16) & 0xffffULL); +} + + +target_ulong helper_add_suov(CPUTriCoreState *env, target_ulong r1, + target_ulong r2) +{ + int64_t t1 = extract64(r1, 0, 32); + int64_t t2 = extract64(r2, 0, 32); + int64_t result = t1 + t2; + return suov32_pos(env, result); +} + +target_ulong helper_add_h_suov(CPUTriCoreState *env, target_ulong r1, + target_ulong r2) +{ + int32_t ret_hw0, ret_hw1; + + ret_hw0 = extract32(r1, 0, 16) + extract32(r2, 0, 16); + ret_hw1 = extract32(r1, 16, 16) + extract32(r2, 16, 16); + return suov16(env, ret_hw0, ret_hw1); +} + +target_ulong helper_sub_ssov(CPUTriCoreState *env, target_ulong r1, + target_ulong r2) +{ + int64_t t1 = sextract64(r1, 0, 32); + int64_t t2 = sextract64(r2, 0, 32); + int64_t result = t1 - t2; + return ssov32(env, result); +} + +uint64_t helper_sub64_ssov(CPUTriCoreState *env, uint64_t r1, uint64_t r2) +{ + uint64_t result; + int64_t ovf; + + result = r1 - r2; + ovf = (result ^ r1) & (r1 ^ r2); + env->PSW_USB_AV = (result ^ result * 2u) >> 32; + env->PSW_USB_SAV |= env->PSW_USB_AV; + if (ovf < 0) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV = (1 << 31); + /* ext_ret > MAX_INT */ + if ((int64_t)r1 >= 0) { + result = INT64_MAX; + /* ext_ret < MIN_INT */ + } else { + result = INT64_MIN; + } + } else { + env->PSW_USB_V = 0; + } + return result; +} + +target_ulong helper_sub_h_ssov(CPUTriCoreState *env, target_ulong r1, + target_ulong r2) +{ + int32_t ret_hw0, ret_hw1; + + ret_hw0 = sextract32(r1, 0, 16) - sextract32(r2, 0, 16); + ret_hw1 = sextract32(r1, 16, 16) - sextract32(r2, 16, 16); + return ssov16(env, ret_hw0, ret_hw1); +} + +uint32_t helper_subr_h_ssov(CPUTriCoreState *env, uint64_t r1, uint32_t r2_l, + uint32_t r2_h) +{ + int64_t mul_res0 = sextract64(r1, 0, 32); + int64_t mul_res1 = sextract64(r1, 32, 32); + int64_t r2_low = sextract64(r2_l, 0, 32); + int64_t r2_high = sextract64(r2_h, 0, 32); + int64_t result0, result1; + uint32_t ovf0, ovf1; + uint32_t avf0, avf1; + + ovf0 = ovf1 = 0; + + result0 = r2_low - mul_res0 + 0x8000; + result1 = r2_high - mul_res1 + 0x8000; + + avf0 = result0 * 2u; + avf0 = result0 ^ avf0; + avf1 = result1 * 2u; + avf1 = result1 ^ avf1; + + if (result0 > INT32_MAX) { + ovf0 = (1 << 31); + result0 = INT32_MAX; + } else if (result0 < INT32_MIN) { + ovf0 = (1 << 31); + result0 = INT32_MIN; + } + + if (result1 > INT32_MAX) { + ovf1 = (1 << 31); + result1 = INT32_MAX; + } else if (result1 < INT32_MIN) { + ovf1 = (1 << 31); + result1 = INT32_MIN; + } + + env->PSW_USB_V = ovf0 | ovf1; + env->PSW_USB_SV |= env->PSW_USB_V; + + env->PSW_USB_AV = avf0 | avf1; + env->PSW_USB_SAV |= env->PSW_USB_AV; + + return (result1 & 0xffff0000ULL) | ((result0 >> 16) & 0xffffULL); +} + +uint32_t helper_subadr_h_ssov(CPUTriCoreState *env, uint64_t r1, uint32_t r2_l, + uint32_t r2_h) +{ + int64_t mul_res0 = sextract64(r1, 0, 32); + int64_t mul_res1 = sextract64(r1, 32, 32); + int64_t r2_low = sextract64(r2_l, 0, 32); + int64_t r2_high = sextract64(r2_h, 0, 32); + int64_t result0, result1; + uint32_t ovf0, ovf1; + uint32_t avf0, avf1; + + ovf0 = ovf1 = 0; + + result0 = r2_low + mul_res0 + 0x8000; + result1 = r2_high - mul_res1 + 0x8000; + + avf0 = result0 * 2u; + avf0 = result0 ^ avf0; + avf1 = result1 * 2u; + avf1 = result1 ^ avf1; + + if (result0 > INT32_MAX) { + ovf0 = (1 << 31); + result0 = INT32_MAX; + } else if (result0 < INT32_MIN) { + ovf0 = (1 << 31); + result0 = INT32_MIN; + } + + if (result1 > INT32_MAX) { + ovf1 = (1 << 31); + result1 = INT32_MAX; + } else if (result1 < INT32_MIN) { + ovf1 = (1 << 31); + result1 = INT32_MIN; + } + + env->PSW_USB_V = ovf0 | ovf1; + env->PSW_USB_SV |= env->PSW_USB_V; + + env->PSW_USB_AV = avf0 | avf1; + env->PSW_USB_SAV |= env->PSW_USB_AV; + + return (result1 & 0xffff0000ULL) | ((result0 >> 16) & 0xffffULL); +} + +target_ulong helper_sub_suov(CPUTriCoreState *env, target_ulong r1, + target_ulong r2) +{ + int64_t t1 = extract64(r1, 0, 32); + int64_t t2 = extract64(r2, 0, 32); + int64_t result = t1 - t2; + return suov32_neg(env, result); +} + +target_ulong helper_sub_h_suov(CPUTriCoreState *env, target_ulong r1, + target_ulong r2) +{ + int32_t ret_hw0, ret_hw1; + + ret_hw0 = extract32(r1, 0, 16) - extract32(r2, 0, 16); + ret_hw1 = extract32(r1, 16, 16) - extract32(r2, 16, 16); + return suov16(env, ret_hw0, ret_hw1); +} + +target_ulong helper_mul_ssov(CPUTriCoreState *env, target_ulong r1, + target_ulong r2) +{ + int64_t t1 = sextract64(r1, 0, 32); + int64_t t2 = sextract64(r2, 0, 32); + int64_t result = t1 * t2; + return ssov32(env, result); +} + +target_ulong helper_mul_suov(CPUTriCoreState *env, target_ulong r1, + target_ulong r2) +{ + int64_t t1 = extract64(r1, 0, 32); + int64_t t2 = extract64(r2, 0, 32); + int64_t result = t1 * t2; + + return suov32_pos(env, result); +} + +target_ulong helper_sha_ssov(CPUTriCoreState *env, target_ulong r1, + target_ulong r2) +{ + int64_t t1 = sextract64(r1, 0, 32); + int32_t t2 = sextract64(r2, 0, 6); + int64_t result; + if (t2 == 0) { + result = t1; + } else if (t2 > 0) { + result = t1 << t2; + } else { + result = t1 >> -t2; + } + return ssov32(env, result); +} + +uint32_t helper_abs_ssov(CPUTriCoreState *env, target_ulong r1) +{ + target_ulong result; + result = ((int32_t)r1 >= 0) ? r1 : (0 - r1); + return ssov32(env, result); +} + +uint32_t helper_abs_h_ssov(CPUTriCoreState *env, target_ulong r1) +{ + int32_t ret_h0, ret_h1; + + ret_h0 = sextract32(r1, 0, 16); + ret_h0 = (ret_h0 >= 0) ? ret_h0 : (0 - ret_h0); + + ret_h1 = sextract32(r1, 16, 16); + ret_h1 = (ret_h1 >= 0) ? ret_h1 : (0 - ret_h1); + + return ssov16(env, ret_h0, ret_h1); +} + +target_ulong helper_absdif_ssov(CPUTriCoreState *env, target_ulong r1, + target_ulong r2) +{ + int64_t t1 = sextract64(r1, 0, 32); + int64_t t2 = sextract64(r2, 0, 32); + int64_t result; + + if (t1 > t2) { + result = t1 - t2; + } else { + result = t2 - t1; + } + return ssov32(env, result); +} + +uint32_t helper_absdif_h_ssov(CPUTriCoreState *env, target_ulong r1, + target_ulong r2) +{ + int32_t t1, t2; + int32_t ret_h0, ret_h1; + + t1 = sextract32(r1, 0, 16); + t2 = sextract32(r2, 0, 16); + if (t1 > t2) { + ret_h0 = t1 - t2; + } else { + ret_h0 = t2 - t1; + } + + t1 = sextract32(r1, 16, 16); + t2 = sextract32(r2, 16, 16); + if (t1 > t2) { + ret_h1 = t1 - t2; + } else { + ret_h1 = t2 - t1; + } + + return ssov16(env, ret_h0, ret_h1); +} + +target_ulong helper_madd32_ssov(CPUTriCoreState *env, target_ulong r1, + target_ulong r2, target_ulong r3) +{ + int64_t t1 = sextract64(r1, 0, 32); + int64_t t2 = sextract64(r2, 0, 32); + int64_t t3 = sextract64(r3, 0, 32); + int64_t result; + + result = t2 + (t1 * t3); + return ssov32(env, result); +} + +target_ulong helper_madd32_suov(CPUTriCoreState *env, target_ulong r1, + target_ulong r2, target_ulong r3) +{ + uint64_t t1 = extract64(r1, 0, 32); + uint64_t t2 = extract64(r2, 0, 32); + uint64_t t3 = extract64(r3, 0, 32); + int64_t result; + + result = t2 + (t1 * t3); + return suov32_pos(env, result); +} + +uint64_t helper_madd64_ssov(CPUTriCoreState *env, target_ulong r1, + uint64_t r2, target_ulong r3) +{ + uint64_t ret, ovf; + int64_t t1 = sextract64(r1, 0, 32); + int64_t t3 = sextract64(r3, 0, 32); + int64_t mul; + + mul = t1 * t3; + ret = mul + r2; + ovf = (ret ^ mul) & ~(mul ^ r2); + + t1 = ret >> 32; + env->PSW_USB_AV = t1 ^ t1 * 2u; + env->PSW_USB_SAV |= env->PSW_USB_AV; + + if ((int64_t)ovf < 0) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV = (1 << 31); + /* ext_ret > MAX_INT */ + if (mul >= 0) { + ret = INT64_MAX; + /* ext_ret < MIN_INT */ + } else { + ret = INT64_MIN; + } + } else { + env->PSW_USB_V = 0; + } + + return ret; +} + +uint32_t +helper_madd32_q_add_ssov(CPUTriCoreState *env, uint64_t r1, uint64_t r2) +{ + int64_t result; + + result = (r1 + r2); + + env->PSW_USB_AV = (result ^ result * 2u); + env->PSW_USB_SAV |= env->PSW_USB_AV; + + /* we do the saturation by hand, since we produce an overflow on the host + if the mul before was (0x80000000 * 0x80000000) << 1). If this is the + case, we flip the saturated value. */ + if (r2 == 0x8000000000000000LL) { + if (result > 0x7fffffffLL) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV = (1 << 31); + result = INT32_MIN; + } else if (result < -0x80000000LL) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV = (1 << 31); + result = INT32_MAX; + } else { + env->PSW_USB_V = 0; + } + } else { + if (result > 0x7fffffffLL) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV = (1 << 31); + result = INT32_MAX; + } else if (result < -0x80000000LL) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV = (1 << 31); + result = INT32_MIN; + } else { + env->PSW_USB_V = 0; + } + } + return (uint32_t)result; +} + +uint64_t helper_madd64_q_ssov(CPUTriCoreState *env, uint64_t r1, uint32_t r2, + uint32_t r3, uint32_t n) +{ + int64_t t1 = (int64_t)r1; + int64_t t2 = sextract64(r2, 0, 32); + int64_t t3 = sextract64(r3, 0, 32); + int64_t result, mul; + int64_t ovf; + + mul = (t2 * t3) << n; + result = mul + t1; + + env->PSW_USB_AV = (result ^ result * 2u) >> 32; + env->PSW_USB_SAV |= env->PSW_USB_AV; + + ovf = (result ^ mul) & ~(mul ^ t1); + /* we do the saturation by hand, since we produce an overflow on the host + if the mul was (0x80000000 * 0x80000000) << 1). If this is the + case, we flip the saturated value. */ + if ((r2 == 0x80000000) && (r3 == 0x80000000) && (n == 1)) { + if (ovf >= 0) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV = (1 << 31); + /* ext_ret > MAX_INT */ + if (mul < 0) { + result = INT64_MAX; + /* ext_ret < MIN_INT */ + } else { + result = INT64_MIN; + } + } else { + env->PSW_USB_V = 0; + } + } else { + if (ovf < 0) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV = (1 << 31); + /* ext_ret > MAX_INT */ + if (mul >= 0) { + result = INT64_MAX; + /* ext_ret < MIN_INT */ + } else { + result = INT64_MIN; + } + } else { + env->PSW_USB_V = 0; + } + } + return (uint64_t)result; +} + +uint32_t helper_maddr_q_ssov(CPUTriCoreState *env, uint32_t r1, uint32_t r2, + uint32_t r3, uint32_t n) +{ + int64_t t1 = sextract64(r1, 0, 32); + int64_t t2 = sextract64(r2, 0, 32); + int64_t t3 = sextract64(r3, 0, 32); + int64_t mul, ret; + + if ((t2 == -0x8000ll) && (t3 == -0x8000ll) && (n == 1)) { + mul = 0x7fffffff; + } else { + mul = (t2 * t3) << n; + } + + ret = t1 + mul + 0x8000; + + env->PSW_USB_AV = ret ^ ret * 2u; + env->PSW_USB_SAV |= env->PSW_USB_AV; + + if (ret > 0x7fffffffll) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV |= env->PSW_USB_V; + ret = INT32_MAX; + } else if (ret < -0x80000000ll) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV |= env->PSW_USB_V; + ret = INT32_MIN; + } else { + env->PSW_USB_V = 0; + } + return ret & 0xffff0000ll; +} + +uint64_t helper_madd64_suov(CPUTriCoreState *env, target_ulong r1, + uint64_t r2, target_ulong r3) +{ + uint64_t ret, mul; + uint64_t t1 = extract64(r1, 0, 32); + uint64_t t3 = extract64(r3, 0, 32); + + mul = t1 * t3; + ret = mul + r2; + + t1 = ret >> 32; + env->PSW_USB_AV = t1 ^ t1 * 2u; + env->PSW_USB_SAV |= env->PSW_USB_AV; + + if (ret < r2) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV = (1 << 31); + /* saturate */ + ret = UINT64_MAX; + } else { + env->PSW_USB_V = 0; + } + return ret; +} + +target_ulong helper_msub32_ssov(CPUTriCoreState *env, target_ulong r1, + target_ulong r2, target_ulong r3) +{ + int64_t t1 = sextract64(r1, 0, 32); + int64_t t2 = sextract64(r2, 0, 32); + int64_t t3 = sextract64(r3, 0, 32); + int64_t result; + + result = t2 - (t1 * t3); + return ssov32(env, result); +} + +target_ulong helper_msub32_suov(CPUTriCoreState *env, target_ulong r1, + target_ulong r2, target_ulong r3) +{ + uint64_t t1 = extract64(r1, 0, 32); + uint64_t t2 = extract64(r2, 0, 32); + uint64_t t3 = extract64(r3, 0, 32); + uint64_t result; + uint64_t mul; + + mul = (t1 * t3); + result = t2 - mul; + + env->PSW_USB_AV = result ^ result * 2u; + env->PSW_USB_SAV |= env->PSW_USB_AV; + /* we calculate ovf by hand here, because the multiplication can overflow on + the host, which would give false results if we compare to less than + zero */ + if (mul > t2) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV = (1 << 31); + result = 0; + } else { + env->PSW_USB_V = 0; + } + return result; +} + +uint64_t helper_msub64_ssov(CPUTriCoreState *env, target_ulong r1, + uint64_t r2, target_ulong r3) +{ + uint64_t ret, ovf; + int64_t t1 = sextract64(r1, 0, 32); + int64_t t3 = sextract64(r3, 0, 32); + int64_t mul; + + mul = t1 * t3; + ret = r2 - mul; + ovf = (ret ^ r2) & (mul ^ r2); + + t1 = ret >> 32; + env->PSW_USB_AV = t1 ^ t1 * 2u; + env->PSW_USB_SAV |= env->PSW_USB_AV; + + if ((int64_t)ovf < 0) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV = (1 << 31); + /* ext_ret > MAX_INT */ + if (mul < 0) { + ret = INT64_MAX; + /* ext_ret < MIN_INT */ + } else { + ret = INT64_MIN; + } + } else { + env->PSW_USB_V = 0; + } + return ret; +} + +uint64_t helper_msub64_suov(CPUTriCoreState *env, target_ulong r1, + uint64_t r2, target_ulong r3) +{ + uint64_t ret, mul; + uint64_t t1 = extract64(r1, 0, 32); + uint64_t t3 = extract64(r3, 0, 32); + + mul = t1 * t3; + ret = r2 - mul; + + t1 = ret >> 32; + env->PSW_USB_AV = t1 ^ t1 * 2u; + env->PSW_USB_SAV |= env->PSW_USB_AV; + + if (ret > r2) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV = (1 << 31); + /* saturate */ + ret = 0; + } else { + env->PSW_USB_V = 0; + } + return ret; +} + +uint32_t +helper_msub32_q_sub_ssov(CPUTriCoreState *env, uint64_t r1, uint64_t r2) +{ + int64_t result; + int64_t t1 = (int64_t)r1; + int64_t t2 = (int64_t)r2; + + result = t1 - t2; + + env->PSW_USB_AV = (result ^ result * 2u); + env->PSW_USB_SAV |= env->PSW_USB_AV; + + /* we do the saturation by hand, since we produce an overflow on the host + if the mul before was (0x80000000 * 0x80000000) << 1). If this is the + case, we flip the saturated value. */ + if (r2 == 0x8000000000000000LL) { + if (result > 0x7fffffffLL) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV = (1 << 31); + result = INT32_MIN; + } else if (result < -0x80000000LL) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV = (1 << 31); + result = INT32_MAX; + } else { + env->PSW_USB_V = 0; + } + } else { + if (result > 0x7fffffffLL) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV = (1 << 31); + result = INT32_MAX; + } else if (result < -0x80000000LL) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV = (1 << 31); + result = INT32_MIN; + } else { + env->PSW_USB_V = 0; + } + } + return (uint32_t)result; +} + +uint64_t helper_msub64_q_ssov(CPUTriCoreState *env, uint64_t r1, uint32_t r2, + uint32_t r3, uint32_t n) +{ + int64_t t1 = (int64_t)r1; + int64_t t2 = sextract64(r2, 0, 32); + int64_t t3 = sextract64(r3, 0, 32); + int64_t result, mul; + int64_t ovf; + + mul = (t2 * t3) << n; + result = t1 - mul; + + env->PSW_USB_AV = (result ^ result * 2u) >> 32; + env->PSW_USB_SAV |= env->PSW_USB_AV; + + ovf = (result ^ t1) & (t1 ^ mul); + /* we do the saturation by hand, since we produce an overflow on the host + if the mul before was (0x80000000 * 0x80000000) << 1). If this is the + case, we flip the saturated value. */ + if (mul == 0x8000000000000000LL) { + if (ovf >= 0) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV = (1 << 31); + /* ext_ret > MAX_INT */ + if (mul >= 0) { + result = INT64_MAX; + /* ext_ret < MIN_INT */ + } else { + result = INT64_MIN; + } + } + } else { + if (ovf < 0) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV = (1 << 31); + /* ext_ret > MAX_INT */ + if (mul < 0) { + result = INT64_MAX; + /* ext_ret < MIN_INT */ + } else { + result = INT64_MIN; + } + } else { + env->PSW_USB_V = 0; + } + } + + return (uint64_t)result; +} + +uint32_t helper_msubr_q_ssov(CPUTriCoreState *env, uint32_t r1, uint32_t r2, + uint32_t r3, uint32_t n) +{ + int64_t t1 = sextract64(r1, 0, 32); + int64_t t2 = sextract64(r2, 0, 32); + int64_t t3 = sextract64(r3, 0, 32); + int64_t mul, ret; + + if ((t2 == -0x8000ll) && (t3 == -0x8000ll) && (n == 1)) { + mul = 0x7fffffff; + } else { + mul = (t2 * t3) << n; + } + + ret = t1 - mul + 0x8000; + + env->PSW_USB_AV = ret ^ ret * 2u; + env->PSW_USB_SAV |= env->PSW_USB_AV; + + if (ret > 0x7fffffffll) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV |= env->PSW_USB_V; + ret = INT32_MAX; + } else if (ret < -0x80000000ll) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV |= env->PSW_USB_V; + ret = INT32_MIN; + } else { + env->PSW_USB_V = 0; + } + return ret & 0xffff0000ll; +} + +uint32_t helper_abs_b(CPUTriCoreState *env, target_ulong arg) +{ + int32_t b, i; + int32_t ovf = 0; + int32_t avf = 0; + int32_t ret = 0; + + for (i = 0; i < 4; i++) { + b = sextract32(arg, i * 8, 8); + b = (b >= 0) ? b : (0 - b); + ovf |= (b > 0x7F) || (b < -0x80); + avf |= b ^ b * 2u; + ret |= (b & 0xff) << (i * 8); + } + + env->PSW_USB_V = ovf << 31; + env->PSW_USB_SV |= env->PSW_USB_V; + env->PSW_USB_AV = avf << 24; + env->PSW_USB_SAV |= env->PSW_USB_AV; + + return ret; +} + +uint32_t helper_abs_h(CPUTriCoreState *env, target_ulong arg) +{ + int32_t h, i; + int32_t ovf = 0; + int32_t avf = 0; + int32_t ret = 0; + + for (i = 0; i < 2; i++) { + h = sextract32(arg, i * 16, 16); + h = (h >= 0) ? h : (0 - h); + ovf |= (h > 0x7FFF) || (h < -0x8000); + avf |= h ^ h * 2u; + ret |= (h & 0xffff) << (i * 16); + } + + env->PSW_USB_V = ovf << 31; + env->PSW_USB_SV |= env->PSW_USB_V; + env->PSW_USB_AV = avf << 16; + env->PSW_USB_SAV |= env->PSW_USB_AV; + + return ret; +} + +uint32_t helper_absdif_b(CPUTriCoreState *env, target_ulong r1, target_ulong r2) +{ + int32_t b, i; + int32_t extr_r2; + int32_t ovf = 0; + int32_t avf = 0; + int32_t ret = 0; + + for (i = 0; i < 4; i++) { + extr_r2 = sextract32(r2, i * 8, 8); + b = sextract32(r1, i * 8, 8); + b = (b > extr_r2) ? (b - extr_r2) : (extr_r2 - b); + ovf |= (b > 0x7F) || (b < -0x80); + avf |= b ^ b * 2u; + ret |= (b & 0xff) << (i * 8); + } + + env->PSW_USB_V = ovf << 31; + env->PSW_USB_SV |= env->PSW_USB_V; + env->PSW_USB_AV = avf << 24; + env->PSW_USB_SAV |= env->PSW_USB_AV; + return ret; +} + +uint32_t helper_absdif_h(CPUTriCoreState *env, target_ulong r1, target_ulong r2) +{ + int32_t h, i; + int32_t extr_r2; + int32_t ovf = 0; + int32_t avf = 0; + int32_t ret = 0; + + for (i = 0; i < 2; i++) { + extr_r2 = sextract32(r2, i * 16, 16); + h = sextract32(r1, i * 16, 16); + h = (h > extr_r2) ? (h - extr_r2) : (extr_r2 - h); + ovf |= (h > 0x7FFF) || (h < -0x8000); + avf |= h ^ h * 2u; + ret |= (h & 0xffff) << (i * 16); + } + + env->PSW_USB_V = ovf << 31; + env->PSW_USB_SV |= env->PSW_USB_V; + env->PSW_USB_AV = avf << 16; + env->PSW_USB_SAV |= env->PSW_USB_AV; + + return ret; +} + +uint32_t helper_addr_h(CPUTriCoreState *env, uint64_t r1, uint32_t r2_l, + uint32_t r2_h) +{ + int64_t mul_res0 = sextract64(r1, 0, 32); + int64_t mul_res1 = sextract64(r1, 32, 32); + int64_t r2_low = sextract64(r2_l, 0, 32); + int64_t r2_high = sextract64(r2_h, 0, 32); + int64_t result0, result1; + uint32_t ovf0, ovf1; + uint32_t avf0, avf1; + + ovf0 = ovf1 = 0; + + result0 = r2_low + mul_res0 + 0x8000; + result1 = r2_high + mul_res1 + 0x8000; + + if ((result0 > INT32_MAX) || (result0 < INT32_MIN)) { + ovf0 = (1 << 31); + } + + if ((result1 > INT32_MAX) || (result1 < INT32_MIN)) { + ovf1 = (1 << 31); + } + + env->PSW_USB_V = ovf0 | ovf1; + env->PSW_USB_SV |= env->PSW_USB_V; + + avf0 = result0 * 2u; + avf0 = result0 ^ avf0; + avf1 = result1 * 2u; + avf1 = result1 ^ avf1; + + env->PSW_USB_AV = avf0 | avf1; + env->PSW_USB_SAV |= env->PSW_USB_AV; + + return (result1 & 0xffff0000ULL) | ((result0 >> 16) & 0xffffULL); +} + +uint32_t helper_addsur_h(CPUTriCoreState *env, uint64_t r1, uint32_t r2_l, + uint32_t r2_h) +{ + int64_t mul_res0 = sextract64(r1, 0, 32); + int64_t mul_res1 = sextract64(r1, 32, 32); + int64_t r2_low = sextract64(r2_l, 0, 32); + int64_t r2_high = sextract64(r2_h, 0, 32); + int64_t result0, result1; + uint32_t ovf0, ovf1; + uint32_t avf0, avf1; + + ovf0 = ovf1 = 0; + + result0 = r2_low - mul_res0 + 0x8000; + result1 = r2_high + mul_res1 + 0x8000; + + if ((result0 > INT32_MAX) || (result0 < INT32_MIN)) { + ovf0 = (1 << 31); + } + + if ((result1 > INT32_MAX) || (result1 < INT32_MIN)) { + ovf1 = (1 << 31); + } + + env->PSW_USB_V = ovf0 | ovf1; + env->PSW_USB_SV |= env->PSW_USB_V; + + avf0 = result0 * 2u; + avf0 = result0 ^ avf0; + avf1 = result1 * 2u; + avf1 = result1 ^ avf1; + + env->PSW_USB_AV = avf0 | avf1; + env->PSW_USB_SAV |= env->PSW_USB_AV; + + return (result1 & 0xffff0000ULL) | ((result0 >> 16) & 0xffffULL); +} + +uint32_t helper_maddr_q(CPUTriCoreState *env, uint32_t r1, uint32_t r2, + uint32_t r3, uint32_t n) +{ + int64_t t1 = sextract64(r1, 0, 32); + int64_t t2 = sextract64(r2, 0, 32); + int64_t t3 = sextract64(r3, 0, 32); + int64_t mul, ret; + + if ((t2 == -0x8000ll) && (t3 == -0x8000ll) && (n == 1)) { + mul = 0x7fffffff; + } else { + mul = (t2 * t3) << n; + } + + ret = t1 + mul + 0x8000; + + if ((ret > 0x7fffffffll) || (ret < -0x80000000ll)) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV |= env->PSW_USB_V; + } else { + env->PSW_USB_V = 0; + } + env->PSW_USB_AV = ret ^ ret * 2u; + env->PSW_USB_SAV |= env->PSW_USB_AV; + + return ret & 0xffff0000ll; +} + +uint32_t helper_add_b(CPUTriCoreState *env, target_ulong r1, target_ulong r2) +{ + int32_t b, i; + int32_t extr_r1, extr_r2; + int32_t ovf = 0; + int32_t avf = 0; + uint32_t ret = 0; + + for (i = 0; i < 4; i++) { + extr_r1 = sextract32(r1, i * 8, 8); + extr_r2 = sextract32(r2, i * 8, 8); + + b = extr_r1 + extr_r2; + ovf |= ((b > 0x7f) || (b < -0x80)); + avf |= b ^ b * 2u; + ret |= ((b & 0xff) << (i*8)); + } + + env->PSW_USB_V = (ovf << 31); + env->PSW_USB_SV |= env->PSW_USB_V; + env->PSW_USB_AV = avf << 24; + env->PSW_USB_SAV |= env->PSW_USB_AV; + + return ret; +} + +uint32_t helper_add_h(CPUTriCoreState *env, target_ulong r1, target_ulong r2) +{ + int32_t h, i; + int32_t extr_r1, extr_r2; + int32_t ovf = 0; + int32_t avf = 0; + int32_t ret = 0; + + for (i = 0; i < 2; i++) { + extr_r1 = sextract32(r1, i * 16, 16); + extr_r2 = sextract32(r2, i * 16, 16); + h = extr_r1 + extr_r2; + ovf |= ((h > 0x7fff) || (h < -0x8000)); + avf |= h ^ h * 2u; + ret |= (h & 0xffff) << (i * 16); + } + + env->PSW_USB_V = (ovf << 31); + env->PSW_USB_SV |= env->PSW_USB_V; + env->PSW_USB_AV = (avf << 16); + env->PSW_USB_SAV |= env->PSW_USB_AV; + + return ret; +} + +uint32_t helper_subr_h(CPUTriCoreState *env, uint64_t r1, uint32_t r2_l, + uint32_t r2_h) +{ + int64_t mul_res0 = sextract64(r1, 0, 32); + int64_t mul_res1 = sextract64(r1, 32, 32); + int64_t r2_low = sextract64(r2_l, 0, 32); + int64_t r2_high = sextract64(r2_h, 0, 32); + int64_t result0, result1; + uint32_t ovf0, ovf1; + uint32_t avf0, avf1; + + ovf0 = ovf1 = 0; + + result0 = r2_low - mul_res0 + 0x8000; + result1 = r2_high - mul_res1 + 0x8000; + + if ((result0 > INT32_MAX) || (result0 < INT32_MIN)) { + ovf0 = (1 << 31); + } + + if ((result1 > INT32_MAX) || (result1 < INT32_MIN)) { + ovf1 = (1 << 31); + } + + env->PSW_USB_V = ovf0 | ovf1; + env->PSW_USB_SV |= env->PSW_USB_V; + + avf0 = result0 * 2u; + avf0 = result0 ^ avf0; + avf1 = result1 * 2u; + avf1 = result1 ^ avf1; + + env->PSW_USB_AV = avf0 | avf1; + env->PSW_USB_SAV |= env->PSW_USB_AV; + + return (result1 & 0xffff0000ULL) | ((result0 >> 16) & 0xffffULL); +} + +uint32_t helper_subadr_h(CPUTriCoreState *env, uint64_t r1, uint32_t r2_l, + uint32_t r2_h) +{ + int64_t mul_res0 = sextract64(r1, 0, 32); + int64_t mul_res1 = sextract64(r1, 32, 32); + int64_t r2_low = sextract64(r2_l, 0, 32); + int64_t r2_high = sextract64(r2_h, 0, 32); + int64_t result0, result1; + uint32_t ovf0, ovf1; + uint32_t avf0, avf1; + + ovf0 = ovf1 = 0; + + result0 = r2_low + mul_res0 + 0x8000; + result1 = r2_high - mul_res1 + 0x8000; + + if ((result0 > INT32_MAX) || (result0 < INT32_MIN)) { + ovf0 = (1 << 31); + } + + if ((result1 > INT32_MAX) || (result1 < INT32_MIN)) { + ovf1 = (1 << 31); + } + + env->PSW_USB_V = ovf0 | ovf1; + env->PSW_USB_SV |= env->PSW_USB_V; + + avf0 = result0 * 2u; + avf0 = result0 ^ avf0; + avf1 = result1 * 2u; + avf1 = result1 ^ avf1; + + env->PSW_USB_AV = avf0 | avf1; + env->PSW_USB_SAV |= env->PSW_USB_AV; + + return (result1 & 0xffff0000ULL) | ((result0 >> 16) & 0xffffULL); +} + +uint32_t helper_msubr_q(CPUTriCoreState *env, uint32_t r1, uint32_t r2, + uint32_t r3, uint32_t n) +{ + int64_t t1 = sextract64(r1, 0, 32); + int64_t t2 = sextract64(r2, 0, 32); + int64_t t3 = sextract64(r3, 0, 32); + int64_t mul, ret; + + if ((t2 == -0x8000ll) && (t3 == -0x8000ll) && (n == 1)) { + mul = 0x7fffffff; + } else { + mul = (t2 * t3) << n; + } + + ret = t1 - mul + 0x8000; + + if ((ret > 0x7fffffffll) || (ret < -0x80000000ll)) { + env->PSW_USB_V = (1 << 31); + env->PSW_USB_SV |= env->PSW_USB_V; + } else { + env->PSW_USB_V = 0; + } + env->PSW_USB_AV = ret ^ ret * 2u; + env->PSW_USB_SAV |= env->PSW_USB_AV; + + return ret & 0xffff0000ll; +} + +uint32_t helper_sub_b(CPUTriCoreState *env, target_ulong r1, target_ulong r2) +{ + int32_t b, i; + int32_t extr_r1, extr_r2; + int32_t ovf = 0; + int32_t avf = 0; + uint32_t ret = 0; + + for (i = 0; i < 4; i++) { + extr_r1 = sextract32(r1, i * 8, 8); + extr_r2 = sextract32(r2, i * 8, 8); + + b = extr_r1 - extr_r2; + ovf |= ((b > 0x7f) || (b < -0x80)); + avf |= b ^ b * 2u; + ret |= ((b & 0xff) << (i*8)); + } + + env->PSW_USB_V = (ovf << 31); + env->PSW_USB_SV |= env->PSW_USB_V; + env->PSW_USB_AV = avf << 24; + env->PSW_USB_SAV |= env->PSW_USB_AV; + + return ret; +} + +uint32_t helper_sub_h(CPUTriCoreState *env, target_ulong r1, target_ulong r2) +{ + int32_t h, i; + int32_t extr_r1, extr_r2; + int32_t ovf = 0; + int32_t avf = 0; + int32_t ret = 0; + + for (i = 0; i < 2; i++) { + extr_r1 = sextract32(r1, i * 16, 16); + extr_r2 = sextract32(r2, i * 16, 16); + h = extr_r1 - extr_r2; + ovf |= ((h > 0x7fff) || (h < -0x8000)); + avf |= h ^ h * 2u; + ret |= (h & 0xffff) << (i * 16); + } + + env->PSW_USB_V = (ovf << 31); + env->PSW_USB_SV |= env->PSW_USB_V; + env->PSW_USB_AV = avf << 16; + env->PSW_USB_SAV |= env->PSW_USB_AV; + + return ret; +} + +uint32_t helper_eq_b(target_ulong r1, target_ulong r2) +{ + int32_t ret; + int32_t i, msk; + + ret = 0; + msk = 0xff; + for (i = 0; i < 4; i++) { + if ((r1 & msk) == (r2 & msk)) { + ret |= msk; + } + msk = msk << 8; + } + + return ret; +} + +uint32_t helper_eq_h(target_ulong r1, target_ulong r2) +{ + int32_t ret = 0; + + if ((r1 & 0xffff) == (r2 & 0xffff)) { + ret = 0xffff; + } + + if ((r1 & 0xffff0000) == (r2 & 0xffff0000)) { + ret |= 0xffff0000; + } + + return ret; +} + +uint32_t helper_eqany_b(target_ulong r1, target_ulong r2) +{ + int32_t i; + uint32_t ret = 0; + + for (i = 0; i < 4; i++) { + ret |= (sextract32(r1, i * 8, 8) == sextract32(r2, i * 8, 8)); + } + + return ret; +} + +uint32_t helper_eqany_h(target_ulong r1, target_ulong r2) +{ + uint32_t ret; + + ret = (sextract32(r1, 0, 16) == sextract32(r2, 0, 16)); + ret |= (sextract32(r1, 16, 16) == sextract32(r2, 16, 16)); + + return ret; +} + +uint32_t helper_lt_b(target_ulong r1, target_ulong r2) +{ + int32_t i; + uint32_t ret = 0; + + for (i = 0; i < 4; i++) { + if (sextract32(r1, i * 8, 8) < sextract32(r2, i * 8, 8)) { + ret |= (0xff << (i * 8)); + } + } + + return ret; +} + +uint32_t helper_lt_bu(target_ulong r1, target_ulong r2) +{ + int32_t i; + uint32_t ret = 0; + + for (i = 0; i < 4; i++) { + if (extract32(r1, i * 8, 8) < extract32(r2, i * 8, 8)) { + ret |= (0xff << (i * 8)); + } + } + + return ret; +} + +uint32_t helper_lt_h(target_ulong r1, target_ulong r2) +{ + uint32_t ret = 0; + + if (sextract32(r1, 0, 16) < sextract32(r2, 0, 16)) { + ret |= 0xffff; + } + + if (sextract32(r1, 16, 16) < sextract32(r2, 16, 16)) { + ret |= 0xffff0000; + } + + return ret; +} + +uint32_t helper_lt_hu(target_ulong r1, target_ulong r2) +{ + uint32_t ret = 0; + + if (extract32(r1, 0, 16) < extract32(r2, 0, 16)) { + ret |= 0xffff; + } + + if (extract32(r1, 16, 16) < extract32(r2, 16, 16)) { + ret |= 0xffff0000; + } + + return ret; +} + +#define EXTREMA_H_B(name, op) \ +uint32_t helper_##name ##_b(target_ulong r1, target_ulong r2) \ +{ \ + int32_t i, extr_r1, extr_r2; \ + uint32_t ret = 0; \ + \ + for (i = 0; i < 4; i++) { \ + extr_r1 = sextract32(r1, i * 8, 8); \ + extr_r2 = sextract32(r2, i * 8, 8); \ + extr_r1 = (extr_r1 op extr_r2) ? extr_r1 : extr_r2; \ + ret |= (extr_r1 & 0xff) << (i * 8); \ + } \ + return ret; \ +} \ + \ +uint32_t helper_##name ##_bu(target_ulong r1, target_ulong r2)\ +{ \ + int32_t i; \ + uint32_t extr_r1, extr_r2; \ + uint32_t ret = 0; \ + \ + for (i = 0; i < 4; i++) { \ + extr_r1 = extract32(r1, i * 8, 8); \ + extr_r2 = extract32(r2, i * 8, 8); \ + extr_r1 = (extr_r1 op extr_r2) ? extr_r1 : extr_r2; \ + ret |= (extr_r1 & 0xff) << (i * 8); \ + } \ + return ret; \ +} \ + \ +uint32_t helper_##name ##_h(target_ulong r1, target_ulong r2) \ +{ \ + int32_t extr_r1, extr_r2; \ + uint32_t ret = 0; \ + \ + extr_r1 = sextract32(r1, 0, 16); \ + extr_r2 = sextract32(r2, 0, 16); \ + ret = (extr_r1 op extr_r2) ? extr_r1 : extr_r2; \ + ret = ret & 0xffff; \ + \ + extr_r1 = sextract32(r1, 16, 16); \ + extr_r2 = sextract32(r2, 16, 16); \ + extr_r1 = (extr_r1 op extr_r2) ? extr_r1 : extr_r2; \ + ret |= extr_r1 << 16; \ + \ + return ret; \ +} \ + \ +uint32_t helper_##name ##_hu(target_ulong r1, target_ulong r2)\ +{ \ + uint32_t extr_r1, extr_r2; \ + uint32_t ret = 0; \ + \ + extr_r1 = extract32(r1, 0, 16); \ + extr_r2 = extract32(r2, 0, 16); \ + ret = (extr_r1 op extr_r2) ? extr_r1 : extr_r2; \ + ret = ret & 0xffff; \ + \ + extr_r1 = extract32(r1, 16, 16); \ + extr_r2 = extract32(r2, 16, 16); \ + extr_r1 = (extr_r1 op extr_r2) ? extr_r1 : extr_r2; \ + ret |= extr_r1 << (16); \ + \ + return ret; \ +} \ + \ +uint64_t helper_ix##name(uint64_t r1, uint32_t r2) \ +{ \ + int64_t r2l, r2h, r1hl; \ + uint64_t ret = 0; \ + \ + ret = ((r1 + 2) & 0xffff); \ + r2l = sextract64(r2, 0, 16); \ + r2h = sextract64(r2, 16, 16); \ + r1hl = sextract64(r1, 32, 16); \ + \ + if ((r2l op ## = r2h) && (r2l op r1hl)) { \ + ret |= (r2l & 0xffff) << 32; \ + ret |= extract64(r1, 0, 16) << 16; \ + } else if ((r2h op r2l) && (r2h op r1hl)) { \ + ret |= extract64(r2, 16, 16) << 32; \ + ret |= extract64(r1 + 1, 0, 16) << 16; \ + } else { \ + ret |= r1 & 0xffffffff0000ull; \ + } \ + return ret; \ +} \ + \ +uint64_t helper_ix##name ##_u(uint64_t r1, uint32_t r2) \ +{ \ + int64_t r2l, r2h, r1hl; \ + uint64_t ret = 0; \ + \ + ret = ((r1 + 2) & 0xffff); \ + r2l = extract64(r2, 0, 16); \ + r2h = extract64(r2, 16, 16); \ + r1hl = extract64(r1, 32, 16); \ + \ + if ((r2l op ## = r2h) && (r2l op r1hl)) { \ + ret |= (r2l & 0xffff) << 32; \ + ret |= extract64(r1, 0, 16) << 16; \ + } else if ((r2h op r2l) && (r2h op r1hl)) { \ + ret |= extract64(r2, 16, 16) << 32; \ + ret |= extract64(r1 + 1, 0, 16) << 16; \ + } else { \ + ret |= r1 & 0xffffffff0000ull; \ + } \ + return ret; \ +} + +EXTREMA_H_B(max, >) +EXTREMA_H_B(min, <) + +#undef EXTREMA_H_B + +uint32_t helper_clo(target_ulong r1) +{ + return clo32(r1); +} + +uint32_t helper_clo_h(target_ulong r1) +{ + uint32_t ret_hw0 = extract32(r1, 0, 16); + uint32_t ret_hw1 = extract32(r1, 16, 16); + + ret_hw0 = clo32(ret_hw0 << 16); + ret_hw1 = clo32(ret_hw1 << 16); -target_ulong helper_add_ssov(CPUTriCoreState *env, target_ulong r1, - target_ulong r2) + if (ret_hw0 > 16) { + ret_hw0 = 16; + } + if (ret_hw1 > 16) { + ret_hw1 = 16; + } + + return ret_hw0 | (ret_hw1 << 16); +} + +uint32_t helper_clz(target_ulong r1) { - target_ulong ret; - int64_t t1 = sextract64(r1, 0, 32); - int64_t t2 = sextract64(r2, 0, 32); - int64_t result = t1 + t2; - SSOV(env, ret, result, 32); + return clz32(r1); +} + +uint32_t helper_clz_h(target_ulong r1) +{ + uint32_t ret_hw0 = extract32(r1, 0, 16); + uint32_t ret_hw1 = extract32(r1, 16, 16); + + ret_hw0 = clz32(ret_hw0 << 16); + ret_hw1 = clz32(ret_hw1 << 16); + + if (ret_hw0 > 16) { + ret_hw0 = 16; + } + if (ret_hw1 > 16) { + ret_hw1 = 16; + } + + return ret_hw0 | (ret_hw1 << 16); +} + +uint32_t helper_cls(target_ulong r1) +{ + return clrsb32(r1); +} + +uint32_t helper_cls_h(target_ulong r1) +{ + uint32_t ret_hw0 = extract32(r1, 0, 16); + uint32_t ret_hw1 = extract32(r1, 16, 16); + + ret_hw0 = clrsb32(ret_hw0 << 16); + ret_hw1 = clrsb32(ret_hw1 << 16); + + if (ret_hw0 > 15) { + ret_hw0 = 15; + } + if (ret_hw1 > 15) { + ret_hw1 = 15; + } + + return ret_hw0 | (ret_hw1 << 16); +} + +uint32_t helper_sh(target_ulong r1, target_ulong r2) +{ + int32_t shift_count = sextract32(r2, 0, 6); + + if (shift_count == -32) { + return 0; + } else if (shift_count < 0) { + return r1 >> -shift_count; + } else { + return r1 << shift_count; + } +} + +uint32_t helper_sh_h(target_ulong r1, target_ulong r2) +{ + int32_t ret_hw0, ret_hw1; + int32_t shift_count; + + shift_count = sextract32(r2, 0, 5); + + if (shift_count == -16) { + return 0; + } else if (shift_count < 0) { + ret_hw0 = extract32(r1, 0, 16) >> -shift_count; + ret_hw1 = extract32(r1, 16, 16) >> -shift_count; + return (ret_hw0 & 0xffff) | (ret_hw1 << 16); + } else { + ret_hw0 = extract32(r1, 0, 16) << shift_count; + ret_hw1 = extract32(r1, 16, 16) << shift_count; + return (ret_hw0 & 0xffff) | (ret_hw1 << 16); + } +} + +uint32_t helper_sha(CPUTriCoreState *env, target_ulong r1, target_ulong r2) +{ + int32_t shift_count; + int64_t result, t1; + uint32_t ret; + + shift_count = sextract32(r2, 0, 6); + t1 = sextract32(r1, 0, 32); + + if (shift_count == 0) { + env->PSW_USB_C = env->PSW_USB_V = 0; + ret = r1; + } else if (shift_count == -32) { + env->PSW_USB_C = r1; + env->PSW_USB_V = 0; + ret = t1 >> 31; + } else if (shift_count > 0) { + result = t1 << shift_count; + /* calc carry */ + env->PSW_USB_C = ((result & 0xffffffff00000000ULL) != 0); + /* calc v */ + env->PSW_USB_V = (((result > 0x7fffffffLL) || + (result < -0x80000000LL)) << 31); + /* calc sv */ + env->PSW_USB_SV |= env->PSW_USB_V; + ret = (uint32_t)result; + } else { + env->PSW_USB_V = 0; + env->PSW_USB_C = (r1 & ((1 << -shift_count) - 1)); + ret = t1 >> -shift_count; + } + + env->PSW_USB_AV = ret ^ ret * 2u; + env->PSW_USB_SAV |= env->PSW_USB_AV; + return ret; } -target_ulong helper_sub_ssov(CPUTriCoreState *env, target_ulong r1, - target_ulong r2) +uint32_t helper_sha_h(target_ulong r1, target_ulong r2) { - target_ulong ret; - int64_t t1 = sextract64(r1, 0, 32); - int64_t t2 = sextract64(r2, 0, 32); - int64_t result = t1 - t2; - SSOV(env, ret, result, 32); + int32_t shift_count; + int32_t ret_hw0, ret_hw1; + + shift_count = sextract32(r2, 0, 5); + + if (shift_count == 0) { + return r1; + } else if (shift_count < 0) { + ret_hw0 = sextract32(r1, 0, 16) >> -shift_count; + ret_hw1 = sextract32(r1, 16, 16) >> -shift_count; + return (ret_hw0 & 0xffff) | (ret_hw1 << 16); + } else { + ret_hw0 = sextract32(r1, 0, 16) << shift_count; + ret_hw1 = sextract32(r1, 16, 16) << shift_count; + return (ret_hw0 & 0xffff) | (ret_hw1 << 16); + } +} + +uint32_t helper_bmerge(target_ulong r1, target_ulong r2) +{ + uint32_t i, ret; + + ret = 0; + for (i = 0; i < 16; i++) { + ret |= (r1 & 1) << (2 * i + 1); + ret |= (r2 & 1) << (2 * i); + r1 = r1 >> 1; + r2 = r2 >> 1; + } + return ret; +} + +uint64_t helper_bsplit(uint32_t r1) +{ + int32_t i; + uint64_t ret; + + ret = 0; + for (i = 0; i < 32; i = i + 2) { + /* even */ + ret |= (r1 & 1) << (i/2); + r1 = r1 >> 1; + /* odd */ + ret |= (uint64_t)(r1 & 1) << (i/2 + 32); + r1 = r1 >> 1; + } + return ret; +} + +uint32_t helper_parity(target_ulong r1) +{ + uint32_t ret; + uint32_t nOnes, i; + + ret = 0; + nOnes = 0; + for (i = 0; i < 8; i++) { + ret ^= (r1 & 1); + r1 = r1 >> 1; + } + /* second byte */ + nOnes = 0; + for (i = 0; i < 8; i++) { + nOnes ^= (r1 & 1); + r1 = r1 >> 1; + } + ret |= nOnes << 8; + /* third byte */ + nOnes = 0; + for (i = 0; i < 8; i++) { + nOnes ^= (r1 & 1); + r1 = r1 >> 1; + } + ret |= nOnes << 16; + /* fourth byte */ + nOnes = 0; + for (i = 0; i < 8; i++) { + nOnes ^= (r1 & 1); + r1 = r1 >> 1; + } + ret |= nOnes << 24; + + return ret; +} + +uint32_t helper_pack(uint32_t carry, uint32_t r1_low, uint32_t r1_high, + target_ulong r2) +{ + uint32_t ret; + int32_t fp_exp, fp_frac, temp_exp, fp_exp_frac; + int32_t int_exp = r1_high; + int32_t int_mant = r1_low; + uint32_t flag_rnd = (int_mant & (1 << 7)) && ( + (int_mant & (1 << 8)) || + (int_mant & 0x7f) || + (carry != 0)); + if (((int_mant & (1<<31)) == 0) && (int_exp == 255)) { + fp_exp = 255; + fp_frac = extract32(int_mant, 8, 23); + } else if ((int_mant & (1<<31)) && (int_exp >= 127)) { + fp_exp = 255; + fp_frac = 0; + } else if ((int_mant & (1<<31)) && (int_exp <= -128)) { + fp_exp = 0; + fp_frac = 0; + } else if (int_mant == 0) { + fp_exp = 0; + fp_frac = 0; + } else { + if (((int_mant & (1 << 31)) == 0)) { + temp_exp = 0; + } else { + temp_exp = int_exp + 128; + } + fp_exp_frac = (((temp_exp & 0xff) << 23) | + extract32(int_mant, 8, 23)) + + flag_rnd; + fp_exp = extract32(fp_exp_frac, 23, 8); + fp_frac = extract32(fp_exp_frac, 0, 23); + } + ret = r2 & (1 << 31); + ret = ret + (fp_exp << 23); + ret = ret + (fp_frac & 0x7fffff); + + return ret; +} + +uint64_t helper_unpack(target_ulong arg1) +{ + int32_t fp_exp = extract32(arg1, 23, 8); + int32_t fp_frac = extract32(arg1, 0, 23); + uint64_t ret; + int32_t int_exp, int_mant; + + if (fp_exp == 255) { + int_exp = 255; + int_mant = (fp_frac << 7); + } else if ((fp_exp == 0) && (fp_frac == 0)) { + int_exp = -127; + int_mant = 0; + } else if ((fp_exp == 0) && (fp_frac != 0)) { + int_exp = -126; + int_mant = (fp_frac << 7); + } else { + int_exp = fp_exp - 127; + int_mant = (fp_frac << 7); + int_mant |= (1 << 30); + } + ret = int_exp; + ret = ret << 32; + ret |= int_mant; + + return ret; +} + +uint64_t helper_dvinit_b_13(CPUTriCoreState *env, uint32_t r1, uint32_t r2) +{ + uint64_t ret; + int32_t abs_sig_dividend, abs_divisor; + + ret = sextract32(r1, 0, 32); + ret = ret << 24; + if (!((r1 & 0x80000000) == (r2 & 0x80000000))) { + ret |= 0xffffff; + } + + abs_sig_dividend = abs((int32_t)r1) >> 8; + abs_divisor = abs((int32_t)r2); + /* calc overflow + ofv if (a/b >= 255) <=> (a/255 >= b) */ + env->PSW_USB_V = (abs_sig_dividend >= abs_divisor) << 31; + env->PSW_USB_V = env->PSW_USB_V << 31; + env->PSW_USB_SV |= env->PSW_USB_V; + env->PSW_USB_AV = 0; + + return ret; +} + +uint64_t helper_dvinit_b_131(CPUTriCoreState *env, uint32_t r1, uint32_t r2) +{ + uint64_t ret = sextract32(r1, 0, 32); + + ret = ret << 24; + if (!((r1 & 0x80000000) == (r2 & 0x80000000))) { + ret |= 0xffffff; + } + /* calc overflow */ + env->PSW_USB_V = ((r2 == 0) || ((r2 == 0xffffffff) && (r1 == 0xffffff80))); + env->PSW_USB_V = env->PSW_USB_V << 31; + env->PSW_USB_SV |= env->PSW_USB_V; + env->PSW_USB_AV = 0; + + return ret; +} + +uint64_t helper_dvinit_h_13(CPUTriCoreState *env, uint32_t r1, uint32_t r2) +{ + uint64_t ret; + int32_t abs_sig_dividend, abs_divisor; + + ret = sextract32(r1, 0, 32); + ret = ret << 16; + if (!((r1 & 0x80000000) == (r2 & 0x80000000))) { + ret |= 0xffff; + } + + abs_sig_dividend = abs((int32_t)r1) >> 16; + abs_divisor = abs((int32_t)r2); + /* calc overflow + ofv if (a/b >= 0xffff) <=> (a/0xffff >= b) */ + env->PSW_USB_V = (abs_sig_dividend >= abs_divisor) << 31; + env->PSW_USB_V = env->PSW_USB_V << 31; + env->PSW_USB_SV |= env->PSW_USB_V; + env->PSW_USB_AV = 0; + + return ret; +} + +uint64_t helper_dvinit_h_131(CPUTriCoreState *env, uint32_t r1, uint32_t r2) +{ + uint64_t ret = sextract32(r1, 0, 32); + + ret = ret << 16; + if (!((r1 & 0x80000000) == (r2 & 0x80000000))) { + ret |= 0xffff; + } + /* calc overflow */ + env->PSW_USB_V = ((r2 == 0) || ((r2 == 0xffffffff) && (r1 == 0xffff8000))); + env->PSW_USB_V = env->PSW_USB_V << 31; + env->PSW_USB_SV |= env->PSW_USB_V; + env->PSW_USB_AV = 0; + + return ret; +} + +uint64_t helper_dvadj(uint64_t r1, uint32_t r2) +{ + int32_t x_sign = (r1 >> 63); + int32_t q_sign = x_sign ^ (r2 >> 31); + int32_t eq_pos = x_sign & ((r1 >> 32) == r2); + int32_t eq_neg = x_sign & ((r1 >> 32) == -r2); + uint32_t quotient; + uint64_t ret, remainder; + + if ((q_sign & ~eq_neg) | eq_pos) { + quotient = (r1 + 1) & 0xffffffff; + } else { + quotient = r1 & 0xffffffff; + } + + if (eq_pos | eq_neg) { + remainder = 0; + } else { + remainder = (r1 & 0xffffffff00000000ull); + } + ret = remainder|quotient; + return ret; +} + +uint64_t helper_dvstep(uint64_t r1, uint32_t r2) +{ + int32_t dividend_sign = extract64(r1, 63, 1); + int32_t divisor_sign = extract32(r2, 31, 1); + int32_t quotient_sign = (dividend_sign != divisor_sign); + int32_t addend, dividend_quotient, remainder; + int32_t i, temp; + + if (quotient_sign) { + addend = r2; + } else { + addend = -r2; + } + dividend_quotient = (int32_t)r1; + remainder = (int32_t)(r1 >> 32); + + for (i = 0; i < 8; i++) { + remainder = (remainder << 1) | extract32(dividend_quotient, 31, 1); + dividend_quotient <<= 1; + temp = remainder + addend; + if ((temp < 0) == dividend_sign) { + remainder = temp; + } + if (((temp < 0) == dividend_sign)) { + dividend_quotient = dividend_quotient | !quotient_sign; + } else { + dividend_quotient = dividend_quotient | quotient_sign; + } + } + return ((uint64_t)remainder << 32) | (uint32_t)dividend_quotient; +} + +uint64_t helper_dvstep_u(uint64_t r1, uint32_t r2) +{ + int32_t dividend_quotient = extract64(r1, 0, 32); + int64_t remainder = extract64(r1, 32, 32); + int32_t i; + int64_t temp; + for (i = 0; i < 8; i++) { + remainder = (remainder << 1) | extract32(dividend_quotient, 31, 1); + dividend_quotient <<= 1; + temp = (remainder & 0xffffffff) - r2; + if (temp >= 0) { + remainder = temp; + } + dividend_quotient = dividend_quotient | !(temp < 0); + } + return ((uint64_t)remainder << 32) | (uint32_t)dividend_quotient; +} + +uint64_t helper_mul_h(uint32_t arg00, uint32_t arg01, + uint32_t arg10, uint32_t arg11, uint32_t n) +{ + uint64_t ret; + uint32_t result0, result1; + + int32_t sc1 = ((arg00 & 0xffff) == 0x8000) && + ((arg10 & 0xffff) == 0x8000) && (n == 1); + int32_t sc0 = ((arg01 & 0xffff) == 0x8000) && + ((arg11 & 0xffff) == 0x8000) && (n == 1); + if (sc1) { + result1 = 0x7fffffff; + } else { + result1 = (((uint32_t)(arg00 * arg10)) << n); + } + if (sc0) { + result0 = 0x7fffffff; + } else { + result0 = (((uint32_t)(arg01 * arg11)) << n); + } + ret = (((uint64_t)result1 << 32)) | result0; + return ret; +} + +uint64_t helper_mulm_h(uint32_t arg00, uint32_t arg01, + uint32_t arg10, uint32_t arg11, uint32_t n) +{ + uint64_t ret; + int64_t result0, result1; + + int32_t sc1 = ((arg00 & 0xffff) == 0x8000) && + ((arg10 & 0xffff) == 0x8000) && (n == 1); + int32_t sc0 = ((arg01 & 0xffff) == 0x8000) && + ((arg11 & 0xffff) == 0x8000) && (n == 1); + + if (sc1) { + result1 = 0x7fffffff; + } else { + result1 = (((int32_t)arg00 * (int32_t)arg10) << n); + } + if (sc0) { + result0 = 0x7fffffff; + } else { + result0 = (((int32_t)arg01 * (int32_t)arg11) << n); + } + ret = (result1 + result0); + ret = ret << 16; return ret; } +uint32_t helper_mulr_h(uint32_t arg00, uint32_t arg01, + uint32_t arg10, uint32_t arg11, uint32_t n) +{ + uint32_t result0, result1; + + int32_t sc1 = ((arg00 & 0xffff) == 0x8000) && + ((arg10 & 0xffff) == 0x8000) && (n == 1); + int32_t sc0 = ((arg01 & 0xffff) == 0x8000) && + ((arg11 & 0xffff) == 0x8000) && (n == 1); + + if (sc1) { + result1 = 0x7fffffff; + } else { + result1 = ((arg00 * arg10) << n) + 0x8000; + } + if (sc0) { + result0 = 0x7fffffff; + } else { + result0 = ((arg01 * arg11) << n) + 0x8000; + } + return (result1 & 0xffff0000) | (result0 >> 16); +} /* context save area (CSA) related helpers */ @@ -413,6 +2479,26 @@ void helper_rfe(CPUTriCoreState *env) psw_write(env, new_PSW); } +void helper_rfm(CPUTriCoreState *env) +{ + env->PC = (env->gpr_a[11] & ~0x1); + /* ICR.IE = PCXI.PIE; */ + env->ICR = (env->ICR & ~MASK_ICR_IE) | + ((env->PCXI & ~MASK_PCXI_PIE) >> 15); + /* ICR.CCPN = PCXI.PCPN; */ + env->ICR = (env->ICR & ~MASK_ICR_CCPN) | + ((env->PCXI & ~MASK_PCXI_PCPN) >> 24); + /* {PCXI, PSW, A[10], A[11]} = M(DCX, 4 * word); */ + env->PCXI = cpu_ldl_data(env, env->DCX); + psw_write(env, cpu_ldl_data(env, env->DCX+4)); + env->gpr_a[10] = cpu_ldl_data(env, env->DCX+8); + env->gpr_a[11] = cpu_ldl_data(env, env->DCX+12); + + if (tricore_feature(env, TRICORE_FEATURE_131)) { + env->DBGTCR = 0; + } +} + void helper_ldlcx(CPUTriCoreState *env, uint32_t ea) { uint32_t dummy; @@ -437,6 +2523,86 @@ void helper_stucx(CPUTriCoreState *env, uint32_t ea) save_context_upper(env, ea); } +void helper_svlcx(CPUTriCoreState *env) +{ + target_ulong tmp_FCX; + target_ulong ea; + target_ulong new_FCX; + + if (env->FCX == 0) { + /* FCU trap */ + } + /* tmp_FCX = FCX; */ + tmp_FCX = env->FCX; + /* EA = {FCX.FCXS, 6'b0, FCX.FCXO, 6'b0}; */ + ea = ((env->FCX & MASK_FCX_FCXS) << 12) + + ((env->FCX & MASK_FCX_FCXO) << 6); + /* new_FCX = M(EA, word); */ + new_FCX = cpu_ldl_data(env, ea); + /* M(EA, 16 * word) = {PCXI, PSW, A[10], A[11], D[8], D[9], D[10], D[11], + A[12], A[13], A[14], A[15], D[12], D[13], D[14], + D[15]}; */ + save_context_lower(env, ea); + + /* PCXI.PCPN = ICR.CCPN; */ + env->PCXI = (env->PCXI & 0xffffff) + + ((env->ICR & MASK_ICR_CCPN) << 24); + /* PCXI.PIE = ICR.IE; */ + env->PCXI = ((env->PCXI & ~MASK_PCXI_PIE) + + ((env->ICR & MASK_ICR_IE) << 15)); + /* PCXI.UL = 0; */ + env->PCXI &= ~MASK_PCXI_UL; + + /* PCXI[19: 0] = FCX[19: 0]; */ + env->PCXI = (env->PCXI & 0xfff00000) + (env->FCX & 0xfffff); + /* FCX[19: 0] = new_FCX[19: 0]; */ + env->FCX = (env->FCX & 0xfff00000) + (new_FCX & 0xfffff); + + /* if (tmp_FCX == LCX) trap(FCD);*/ + if (tmp_FCX == env->LCX) { + /* FCD trap */ + } +} + +void helper_rslcx(CPUTriCoreState *env) +{ + target_ulong ea; + target_ulong new_PCXI; + /* if (PCXI[19: 0] == 0) then trap(CSU); */ + if ((env->PCXI & 0xfffff) == 0) { + /* CSU trap */ + } + /* if (PCXI.UL == 1) then trap(CTYP); */ + if ((env->PCXI & MASK_PCXI_UL) != 0) { + /* CTYP trap */ + } + /* EA = {PCXI.PCXS, 6'b0, PCXI.PCXO, 6'b0}; */ + ea = ((env->PCXI & MASK_PCXI_PCXS) << 12) + + ((env->PCXI & MASK_PCXI_PCXO) << 6); + /* {new_PCXI, A[11], A[10], A[11], D[8], D[9], D[10], D[11], A[12], + A[13], A[14], A[15], D[12], D[13], D[14], D[15]} = M(EA, 16 * word); */ + restore_context_upper(env, ea, &new_PCXI, &env->gpr_a[11]); + /* M(EA, word) = FCX; */ + cpu_stl_data(env, ea, env->FCX); + /* M(EA, word) = FCX; */ + cpu_stl_data(env, ea, env->FCX); + /* FCX[19: 0] = PCXI[19: 0]; */ + env->FCX = (env->FCX & 0xfff00000) + (env->PCXI & 0x000fffff); + /* PCXI = new_PCXI; */ + env->PCXI = new_PCXI; +} + +void helper_psw_write(CPUTriCoreState *env, uint32_t arg) +{ + psw_write(env, arg); +} + +uint32_t helper_psw_read(CPUTriCoreState *env) +{ + return psw_read(env); +} + + static inline void QEMU_NORETURN do_raise_exception_err(CPUTriCoreState *env, uint32_t exception, int error_code, diff --git a/target-tricore/translate.c b/target-tricore/translate.c index d5a9596..54a48cd 100644 --- a/target-tricore/translate.c +++ b/target-tricore/translate.c @@ -80,27 +80,43 @@ enum { BS_EXCP = 3, }; +enum { + MODE_LL = 0, + MODE_LU = 1, + MODE_UL = 2, + MODE_UU = 3, +}; + void tricore_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, int flags) { TriCoreCPU *cpu = TRICORE_CPU(cs); CPUTriCoreState *env = &cpu->env; + uint32_t psw; int i; - cpu_fprintf(f, "PC=%08x\n", env->PC); + psw = psw_read(env); + + cpu_fprintf(f, "PC: " TARGET_FMT_lx, env->PC); + cpu_fprintf(f, " PSW: " TARGET_FMT_lx, psw); + cpu_fprintf(f, " ICR: " TARGET_FMT_lx, env->ICR); + cpu_fprintf(f, "\nPCXI: " TARGET_FMT_lx, env->PCXI); + cpu_fprintf(f, " FCX: " TARGET_FMT_lx, env->FCX); + cpu_fprintf(f, " LCX: " TARGET_FMT_lx, env->LCX); + for (i = 0; i < 16; ++i) { if ((i & 3) == 0) { - cpu_fprintf(f, "GPR A%02d:", i); + cpu_fprintf(f, "\nGPR A%02d:", i); } - cpu_fprintf(f, " %s " TARGET_FMT_lx, regnames_a[i], env->gpr_a[i]); + cpu_fprintf(f, " " TARGET_FMT_lx, env->gpr_a[i]); } for (i = 0; i < 16; ++i) { if ((i & 3) == 0) { - cpu_fprintf(f, "GPR D%02d:", i); + cpu_fprintf(f, "\nGPR D%02d:", i); } - cpu_fprintf(f, " %s " TARGET_FMT_lx, regnames_d[i], env->gpr_d[i]); + cpu_fprintf(f, " " TARGET_FMT_lx, env->gpr_d[i]); } - + cpu_fprintf(f, "\n"); } /* @@ -115,6 +131,76 @@ void tricore_cpu_dump_state(CPUState *cs, FILE *f, tcg_temp_free_i32(helper_tmp); \ } while (0) +#define GEN_HELPER_LL(name, ret, arg0, arg1, n) do { \ + TCGv arg00 = tcg_temp_new(); \ + TCGv arg01 = tcg_temp_new(); \ + TCGv arg11 = tcg_temp_new(); \ + tcg_gen_sari_tl(arg00, arg0, 16); \ + tcg_gen_ext16s_tl(arg01, arg0); \ + tcg_gen_ext16s_tl(arg11, arg1); \ + gen_helper_##name(ret, arg00, arg01, arg11, arg11, n); \ + tcg_temp_free(arg00); \ + tcg_temp_free(arg01); \ + tcg_temp_free(arg11); \ +} while (0) + +#define GEN_HELPER_LU(name, ret, arg0, arg1, n) do { \ + TCGv arg00 = tcg_temp_new(); \ + TCGv arg01 = tcg_temp_new(); \ + TCGv arg10 = tcg_temp_new(); \ + TCGv arg11 = tcg_temp_new(); \ + tcg_gen_sari_tl(arg00, arg0, 16); \ + tcg_gen_ext16s_tl(arg01, arg0); \ + tcg_gen_sari_tl(arg11, arg1, 16); \ + tcg_gen_ext16s_tl(arg10, arg1); \ + gen_helper_##name(ret, arg00, arg01, arg10, arg11, n); \ + tcg_temp_free(arg00); \ + tcg_temp_free(arg01); \ + tcg_temp_free(arg10); \ + tcg_temp_free(arg11); \ +} while (0) + +#define GEN_HELPER_UL(name, ret, arg0, arg1, n) do { \ + TCGv arg00 = tcg_temp_new(); \ + TCGv arg01 = tcg_temp_new(); \ + TCGv arg10 = tcg_temp_new(); \ + TCGv arg11 = tcg_temp_new(); \ + tcg_gen_sari_tl(arg00, arg0, 16); \ + tcg_gen_ext16s_tl(arg01, arg0); \ + tcg_gen_sari_tl(arg10, arg1, 16); \ + tcg_gen_ext16s_tl(arg11, arg1); \ + gen_helper_##name(ret, arg00, arg01, arg10, arg11, n); \ + tcg_temp_free(arg00); \ + tcg_temp_free(arg01); \ + tcg_temp_free(arg10); \ + tcg_temp_free(arg11); \ +} while (0) + +#define GEN_HELPER_UU(name, ret, arg0, arg1, n) do { \ + TCGv arg00 = tcg_temp_new(); \ + TCGv arg01 = tcg_temp_new(); \ + TCGv arg11 = tcg_temp_new(); \ + tcg_gen_sari_tl(arg01, arg0, 16); \ + tcg_gen_ext16s_tl(arg00, arg0); \ + tcg_gen_sari_tl(arg11, arg1, 16); \ + gen_helper_##name(ret, arg00, arg01, arg11, arg11, n); \ + tcg_temp_free(arg00); \ + tcg_temp_free(arg01); \ + tcg_temp_free(arg11); \ +} while (0) + +#define GEN_HELPER_RRR(name, rl, rh, al1, ah1, arg2) do { \ + TCGv_i64 ret = tcg_temp_new_i64(); \ + TCGv_i64 arg1 = tcg_temp_new_i64(); \ + \ + tcg_gen_concat_i32_i64(arg1, al1, ah1); \ + gen_helper_##name(ret, arg1, arg2); \ + tcg_gen_extr_i64_i32(rl, rh, ret); \ + \ + tcg_temp_free_i64(ret); \ + tcg_temp_free_i64(arg1); \ +} while (0) + #define EA_ABS_FORMAT(con) (((con & 0x3C000) << 14) + (con & 0x3FFF)) #define EA_B_ABSOLUT(con) (((offset & 0xf00000) << 8) | \ ((offset & 0x0fffff) << 1)) @@ -233,6 +319,63 @@ static void gen_swap(DisasContext *ctx, int reg, TCGv ea) tcg_temp_free(temp); } +/* We generate loads and store to core special function register (csfr) through + the function gen_mfcr and gen_mtcr. To handle access permissions, we use 3 + makros R, A and E, which allow read-only, all and endinit protected access. + These makros also specify in which ISA version the csfr was introduced. */ +#define R(ADDRESS, REG, FEATURE) \ + case ADDRESS: \ + if (tricore_feature(env, FEATURE)) { \ + tcg_gen_ld_tl(ret, cpu_env, offsetof(CPUTriCoreState, REG)); \ + } \ + break; +#define A(ADDRESS, REG, FEATURE) R(ADDRESS, REG, FEATURE) +#define E(ADDRESS, REG, FEATURE) R(ADDRESS, REG, FEATURE) +static inline void gen_mfcr(CPUTriCoreState *env, TCGv ret, int32_t offset) +{ + /* since we're caching PSW make this a special case */ + if (offset == 0xfe04) { + gen_helper_psw_read(ret, cpu_env); + } else { + switch (offset) { +#include "csfr.def" + } + } +} +#undef R +#undef A +#undef E + +#define R(ADDRESS, REG, FEATURE) /* don't gen writes to read-only reg, + since no execption occurs */ +#define A(ADDRESS, REG, FEATURE) R(ADDRESS, REG, FEATURE) \ + case ADDRESS: \ + if (tricore_feature(env, FEATURE)) { \ + tcg_gen_st_tl(r1, cpu_env, offsetof(CPUTriCoreState, REG)); \ + } \ + break; +/* Endinit protected registers + TODO: Since the endinit bit is in a register of a not yet implemented + watchdog device, we handle endinit protected registers like + all-access registers for now. */ +#define E(ADDRESS, REG, FEATURE) A(ADDRESS, REG, FEATURE) +static inline void gen_mtcr(CPUTriCoreState *env, DisasContext *ctx, TCGv r1, + int32_t offset) +{ + if ((ctx->hflags & TRICORE_HFLAG_KUU) == TRICORE_HFLAG_SM) { + /* since we're caching PSW make this a special case */ + if (offset == 0xfe04) { + gen_helper_psw_write(cpu_env, r1); + } else { + switch (offset) { +#include "csfr.def" + } + } + } else { + /* generate privilege trap */ + } +} + /* Functions for arithmetic instructions */ static inline void gen_add_d(TCGv ret, TCGv r1, TCGv r2) @@ -259,2037 +402,7387 @@ static inline void gen_add_d(TCGv ret, TCGv r1, TCGv r2) tcg_temp_free(t0); } -static inline void gen_addi_d(TCGv ret, TCGv r1, target_ulong r2) +static inline void +gen_add64_d(TCGv_i64 ret, TCGv_i64 r1, TCGv_i64 r2) { - TCGv temp = tcg_const_i32(r2); - gen_add_d(ret, r1, temp); + TCGv temp = tcg_temp_new(); + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 result = tcg_temp_new_i64(); + + tcg_gen_add_i64(result, r1, r2); + /* calc v bit */ + tcg_gen_xor_i64(t1, result, r1); + tcg_gen_xor_i64(t0, r1, r2); + tcg_gen_andc_i64(t1, t1, t0); + tcg_gen_trunc_shr_i64_i32(cpu_PSW_V, t1, 32); + /* calc SV bit */ + tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + /* calc AV/SAV bits */ + tcg_gen_trunc_shr_i64_i32(temp, result, 32); + tcg_gen_add_tl(cpu_PSW_AV, temp, temp); + tcg_gen_xor_tl(cpu_PSW_AV, temp, cpu_PSW_AV); + /* calc SAV */ + tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + /* write back result */ + tcg_gen_mov_i64(ret, result); + tcg_temp_free(temp); + tcg_temp_free_i64(result); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); } -static inline void gen_cond_add(TCGCond cond, TCGv r1, TCGv r2, TCGv r3, - TCGv r4) +static inline void +gen_addsub64_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, + TCGv r3, void(*op1)(TCGv, TCGv, TCGv), + void(*op2)(TCGv, TCGv, TCGv)) { TCGv temp = tcg_temp_new(); TCGv temp2 = tcg_temp_new(); - TCGv result = tcg_temp_new(); - TCGv mask = tcg_temp_new(); - TCGv t0 = tcg_const_i32(0); - - /* create mask for sticky bits */ - tcg_gen_setcond_tl(cond, mask, r4, t0); - tcg_gen_shli_tl(mask, mask, 31); + TCGv temp3 = tcg_temp_new(); + TCGv temp4 = tcg_temp_new(); + + (*op1)(temp, r1_low, r2); + /* calc V0 bit */ + tcg_gen_xor_tl(temp2, temp, r1_low); + tcg_gen_xor_tl(temp3, r1_low, r2); + if (op1 == tcg_gen_add_tl) { + tcg_gen_andc_tl(temp2, temp2, temp3); + } else { + tcg_gen_and_tl(temp2, temp2, temp3); + } - tcg_gen_add_tl(result, r1, r2); - /* Calc PSW_V */ - tcg_gen_xor_tl(temp, result, r1); - tcg_gen_xor_tl(temp2, r1, r2); - tcg_gen_andc_tl(temp, temp, temp2); - tcg_gen_movcond_tl(cond, cpu_PSW_V, r4, t0, temp, cpu_PSW_V); - /* Set PSW_SV */ - tcg_gen_and_tl(temp, temp, mask); - tcg_gen_or_tl(cpu_PSW_SV, temp, cpu_PSW_SV); + (*op2)(temp3, r1_high, r3); + /* calc V1 bit */ + tcg_gen_xor_tl(cpu_PSW_V, temp3, r1_high); + tcg_gen_xor_tl(temp4, r1_high, r3); + if (op2 == tcg_gen_add_tl) { + tcg_gen_andc_tl(cpu_PSW_V, cpu_PSW_V, temp4); + } else { + tcg_gen_and_tl(cpu_PSW_V, cpu_PSW_V, temp4); + } + /* combine V0/V1 bits */ + tcg_gen_or_tl(cpu_PSW_V, cpu_PSW_V, temp2); + /* calc sv bit */ + tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + /* write result */ + tcg_gen_mov_tl(ret_low, temp); + tcg_gen_mov_tl(ret_high, temp3); /* calc AV bit */ - tcg_gen_add_tl(temp, result, result); - tcg_gen_xor_tl(temp, temp, result); - tcg_gen_movcond_tl(cond, cpu_PSW_AV, r4, t0, temp, cpu_PSW_AV); + tcg_gen_add_tl(temp, ret_low, ret_low); + tcg_gen_xor_tl(temp, temp, ret_low); + tcg_gen_add_tl(cpu_PSW_AV, ret_high, ret_high); + tcg_gen_xor_tl(cpu_PSW_AV, cpu_PSW_AV, ret_high); + tcg_gen_or_tl(cpu_PSW_AV, cpu_PSW_AV, temp); /* calc SAV bit */ - tcg_gen_and_tl(temp, temp, mask); - tcg_gen_or_tl(cpu_PSW_SAV, temp, cpu_PSW_SAV); - /* write back result */ - tcg_gen_movcond_tl(cond, r3, r4, t0, result, r3); + tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); - tcg_temp_free(t0); tcg_temp_free(temp); tcg_temp_free(temp2); - tcg_temp_free(result); - tcg_temp_free(mask); + tcg_temp_free(temp3); + tcg_temp_free(temp4); } -static inline void gen_condi_add(TCGCond cond, TCGv r1, int32_t r2, - TCGv r3, TCGv r4) +/* ret = r2 + (r1 * r3); */ +static inline void gen_madd32_d(TCGv ret, TCGv r1, TCGv r2, TCGv r3) { - TCGv temp = tcg_const_i32(r2); - gen_cond_add(cond, r1, temp, r3, r4); - tcg_temp_free(temp); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t3 = tcg_temp_new_i64(); + + tcg_gen_ext_i32_i64(t1, r1); + tcg_gen_ext_i32_i64(t2, r2); + tcg_gen_ext_i32_i64(t3, r3); + + tcg_gen_mul_i64(t1, t1, t3); + tcg_gen_add_i64(t1, t2, t1); + + tcg_gen_trunc_i64_i32(ret, t1); + /* calc V + t1 > 0x7fffffff */ + tcg_gen_setcondi_i64(TCG_COND_GT, t3, t1, 0x7fffffffLL); + /* t1 < -0x80000000 */ + tcg_gen_setcondi_i64(TCG_COND_LT, t2, t1, -0x80000000LL); + tcg_gen_or_i64(t2, t2, t3); + tcg_gen_trunc_i64_i32(cpu_PSW_V, t2); + tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); + /* Calc SV bit */ + tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + /* Calc AV/SAV bits */ + tcg_gen_add_tl(cpu_PSW_AV, ret, ret); + tcg_gen_xor_tl(cpu_PSW_AV, ret, cpu_PSW_AV); + /* calc SAV */ + tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + + tcg_temp_free_i64(t1); + tcg_temp_free_i64(t2); + tcg_temp_free_i64(t3); } -static inline void gen_sub_d(TCGv ret, TCGv r1, TCGv r2) +static inline void gen_maddi32_d(TCGv ret, TCGv r1, TCGv r2, int32_t con) { - TCGv temp = tcg_temp_new_i32(); - TCGv result = tcg_temp_new_i32(); + TCGv temp = tcg_const_i32(con); + gen_madd32_d(ret, r1, r2, temp); + tcg_temp_free(temp); +} - tcg_gen_sub_tl(result, r1, r2); +static inline void +gen_madd64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, + TCGv r3) +{ + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + TCGv t3 = tcg_temp_new(); + TCGv t4 = tcg_temp_new(); + + tcg_gen_muls2_tl(t1, t2, r1, r3); + /* only the add can overflow */ + tcg_gen_add2_tl(t3, t4, r2_low, r2_high, t1, t2); /* calc V bit */ - tcg_gen_xor_tl(cpu_PSW_V, result, r1); - tcg_gen_xor_tl(temp, r1, r2); - tcg_gen_and_tl(cpu_PSW_V, cpu_PSW_V, temp); - /* calc SV bit */ + tcg_gen_xor_tl(cpu_PSW_V, t4, r2_high); + tcg_gen_xor_tl(t1, r2_high, t2); + tcg_gen_andc_tl(cpu_PSW_V, cpu_PSW_V, t1); + /* Calc SV bit */ tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); - /* Calc AV bit */ - tcg_gen_add_tl(cpu_PSW_AV, result, result); - tcg_gen_xor_tl(cpu_PSW_AV, result, cpu_PSW_AV); - /* calc SAV bit */ + /* Calc AV/SAV bits */ + tcg_gen_add_tl(cpu_PSW_AV, t4, t4); + tcg_gen_xor_tl(cpu_PSW_AV, t4, cpu_PSW_AV); + /* calc SAV */ tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); - /* write back result */ - tcg_gen_mov_tl(ret, result); - - tcg_temp_free(temp); - tcg_temp_free(result); + /* write back the result */ + tcg_gen_mov_tl(ret_low, t3); + tcg_gen_mov_tl(ret_high, t4); + + tcg_temp_free(t1); + tcg_temp_free(t2); + tcg_temp_free(t3); + tcg_temp_free(t4); } -static inline void gen_mul_i32s(TCGv ret, TCGv r1, TCGv r2) +static inline void +gen_maddu64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, + TCGv r3) { - TCGv high = tcg_temp_new(); - TCGv low = tcg_temp_new(); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t3 = tcg_temp_new_i64(); - tcg_gen_muls2_tl(low, high, r1, r2); - tcg_gen_mov_tl(ret, low); - /* calc V bit */ - tcg_gen_sari_tl(low, low, 31); - tcg_gen_setcond_tl(TCG_COND_NE, cpu_PSW_V, high, low); + tcg_gen_extu_i32_i64(t1, r1); + tcg_gen_concat_i32_i64(t2, r2_low, r2_high); + tcg_gen_extu_i32_i64(t3, r3); + + tcg_gen_mul_i64(t1, t1, t3); + tcg_gen_add_i64(t2, t2, t1); + /* write back result */ + tcg_gen_extr_i64_i32(ret_low, ret_high, t2); + /* only the add overflows, if t2 < t1 + calc V bit */ + tcg_gen_setcond_i64(TCG_COND_LTU, t2, t2, t1); + tcg_gen_trunc_i64_i32(cpu_PSW_V, t2); tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); - /* calc SV bit */ + /* Calc SV bit */ tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); - /* Calc AV bit */ - tcg_gen_add_tl(cpu_PSW_AV, ret, ret); - tcg_gen_xor_tl(cpu_PSW_AV, ret, cpu_PSW_AV); - /* calc SAV bit */ + /* Calc AV/SAV bits */ + tcg_gen_add_tl(cpu_PSW_AV, ret_high, ret_high); + tcg_gen_xor_tl(cpu_PSW_AV, ret_high, cpu_PSW_AV); + /* calc SAV */ tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); - tcg_temp_free(high); - tcg_temp_free(low); + tcg_temp_free_i64(t1); + tcg_temp_free_i64(t2); + tcg_temp_free_i64(t3); } -static void gen_saturate(TCGv ret, TCGv arg, int32_t up, int32_t low) +static inline void +gen_maddi64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, + int32_t con) { - TCGv sat_neg = tcg_const_i32(low); - TCGv temp = tcg_const_i32(up); - - /* sat_neg = (arg < low ) ? low : arg; */ - tcg_gen_movcond_tl(TCG_COND_LT, sat_neg, arg, sat_neg, sat_neg, arg); - - /* ret = (sat_neg > up ) ? up : sat_neg; */ - tcg_gen_movcond_tl(TCG_COND_GT, ret, sat_neg, temp, temp, sat_neg); - - tcg_temp_free(sat_neg); + TCGv temp = tcg_const_i32(con); + gen_madd64_d(ret_low, ret_high, r1, r2_low, r2_high, temp); tcg_temp_free(temp); } -static void gen_saturate_u(TCGv ret, TCGv arg, int32_t up) +static inline void +gen_maddui64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, + int32_t con) { - TCGv temp = tcg_const_i32(up); - /* sat_neg = (arg > up ) ? up : arg; */ - tcg_gen_movcond_tl(TCG_COND_GTU, ret, arg, temp, temp, arg); + TCGv temp = tcg_const_i32(con); + gen_maddu64_d(ret_low, ret_high, r1, r2_low, r2_high, temp); tcg_temp_free(temp); } -static void gen_shi(TCGv ret, TCGv r1, int32_t shift_count) +static inline void +gen_madd_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, + TCGv r3, uint32_t n, uint32_t mode) { - if (shift_count == -32) { - tcg_gen_movi_tl(ret, 0); - } else if (shift_count >= 0) { - tcg_gen_shli_tl(ret, r1, shift_count); - } else { - tcg_gen_shri_tl(ret, r1, -shift_count); + TCGv temp = tcg_const_i32(n); + TCGv temp2 = tcg_temp_new(); + TCGv_i64 temp64 = tcg_temp_new_i64(); + switch (mode) { + case MODE_LL: + GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + break; + case MODE_LU: + GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + break; + case MODE_UL: + GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + break; + case MODE_UU: + GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + break; } + tcg_gen_extr_i64_i32(temp, temp2, temp64); + gen_addsub64_h(ret_low, ret_high, r1_low, r1_high, temp, temp2, + tcg_gen_add_tl, tcg_gen_add_tl); + tcg_temp_free(temp); + tcg_temp_free(temp2); + tcg_temp_free_i64(temp64); } -static void gen_shaci(TCGv ret, TCGv r1, int32_t shift_count) +static inline void +gen_maddsu_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, + TCGv r3, uint32_t n, uint32_t mode) { - uint32_t msk, msk_start; - TCGv temp = tcg_temp_new(); + TCGv temp = tcg_const_i32(n); TCGv temp2 = tcg_temp_new(); - TCGv t_0 = tcg_const_i32(0); - - if (shift_count == 0) { - /* Clear PSW.C and PSW.V */ - tcg_gen_movi_tl(cpu_PSW_C, 0); - tcg_gen_mov_tl(cpu_PSW_V, cpu_PSW_C); - tcg_gen_mov_tl(ret, r1); - } else if (shift_count == -32) { - /* set PSW.C */ - tcg_gen_mov_tl(cpu_PSW_C, r1); - /* fill ret completly with sign bit */ - tcg_gen_sari_tl(ret, r1, 31); - /* clear PSW.V */ - tcg_gen_movi_tl(cpu_PSW_V, 0); - } else if (shift_count > 0) { - TCGv t_max = tcg_const_i32(0x7FFFFFFF >> shift_count); - TCGv t_min = tcg_const_i32(((int32_t) -0x80000000) >> shift_count); - - /* calc carry */ - msk_start = 32 - shift_count; - msk = ((1 << shift_count) - 1) << msk_start; - tcg_gen_andi_tl(cpu_PSW_C, r1, msk); - /* calc v/sv bits */ - tcg_gen_setcond_tl(TCG_COND_GT, temp, r1, t_max); - tcg_gen_setcond_tl(TCG_COND_LT, temp2, r1, t_min); - tcg_gen_or_tl(cpu_PSW_V, temp, temp2); - tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); - /* calc sv */ - tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_V, cpu_PSW_SV); - /* do shift */ - tcg_gen_shli_tl(ret, r1, shift_count); - - tcg_temp_free(t_max); - tcg_temp_free(t_min); - } else { - /* clear PSW.V */ - tcg_gen_movi_tl(cpu_PSW_V, 0); - /* calc carry */ - msk = (1 << -shift_count) - 1; - tcg_gen_andi_tl(cpu_PSW_C, r1, msk); - /* do shift */ - tcg_gen_sari_tl(ret, r1, -shift_count); + TCGv_i64 temp64 = tcg_temp_new_i64(); + switch (mode) { + case MODE_LL: + GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + break; + case MODE_LU: + GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + break; + case MODE_UL: + GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + break; + case MODE_UU: + GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + break; } - /* calc av overflow bit */ - tcg_gen_add_tl(cpu_PSW_AV, ret, ret); - tcg_gen_xor_tl(cpu_PSW_AV, ret, cpu_PSW_AV); - /* calc sav overflow bit */ - tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); - + tcg_gen_extr_i64_i32(temp, temp2, temp64); + gen_addsub64_h(ret_low, ret_high, r1_low, r1_high, temp, temp2, + tcg_gen_sub_tl, tcg_gen_add_tl); tcg_temp_free(temp); tcg_temp_free(temp2); - tcg_temp_free(t_0); -} - -static inline void gen_adds(TCGv ret, TCGv r1, TCGv r2) -{ - gen_helper_add_ssov(ret, cpu_env, r1, r2); + tcg_temp_free_i64(temp64); } -static inline void gen_subs(TCGv ret, TCGv r1, TCGv r2) +static inline void +gen_maddsum_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, + TCGv r3, uint32_t n, uint32_t mode) { - gen_helper_sub_ssov(ret, cpu_env, r1, r2); -} + TCGv temp = tcg_const_i32(n); + TCGv_i64 temp64 = tcg_temp_new_i64(); + TCGv_i64 temp64_2 = tcg_temp_new_i64(); + TCGv_i64 temp64_3 = tcg_temp_new_i64(); + switch (mode) { + case MODE_LL: + GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + break; + case MODE_LU: + GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + break; + case MODE_UL: + GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + break; + case MODE_UU: + GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + break; + } + tcg_gen_concat_i32_i64(temp64_3, r1_low, r1_high); + tcg_gen_sari_i64(temp64_2, temp64, 32); /* high */ + tcg_gen_ext32s_i64(temp64, temp64); /* low */ + tcg_gen_sub_i64(temp64, temp64_2, temp64); + tcg_gen_shli_i64(temp64, temp64, 16); -static inline void gen_bit_2op(TCGv ret, TCGv r1, TCGv r2, - int pos1, int pos2, - void(*op1)(TCGv, TCGv, TCGv), - void(*op2)(TCGv, TCGv, TCGv)) -{ - TCGv temp1, temp2; + gen_add64_d(temp64_2, temp64_3, temp64); + /* write back result */ + tcg_gen_extr_i64_i32(ret_low, ret_high, temp64_2); - temp1 = tcg_temp_new(); - temp2 = tcg_temp_new(); + tcg_temp_free(temp); + tcg_temp_free_i64(temp64); + tcg_temp_free_i64(temp64_2); + tcg_temp_free_i64(temp64_3); +} - tcg_gen_shri_tl(temp2, r2, pos2); - tcg_gen_shri_tl(temp1, r1, pos1); +static inline void gen_adds(TCGv ret, TCGv r1, TCGv r2); - (*op1)(temp1, temp1, temp2); - (*op2)(temp1 , ret, temp1); +static inline void +gen_madds_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, + TCGv r3, uint32_t n, uint32_t mode) +{ + TCGv temp = tcg_const_i32(n); + TCGv temp2 = tcg_temp_new(); + TCGv temp3 = tcg_temp_new(); + TCGv_i64 temp64 = tcg_temp_new_i64(); - tcg_gen_deposit_tl(ret, ret, temp1, 0, 1); + switch (mode) { + case MODE_LL: + GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + break; + case MODE_LU: + GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + break; + case MODE_UL: + GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + break; + case MODE_UU: + GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + break; + } + tcg_gen_extr_i64_i32(temp, temp2, temp64); + gen_adds(ret_low, r1_low, temp); + tcg_gen_mov_tl(temp, cpu_PSW_V); + tcg_gen_mov_tl(temp3, cpu_PSW_AV); + gen_adds(ret_high, r1_high, temp2); + /* combine v bits */ + tcg_gen_or_tl(cpu_PSW_V, cpu_PSW_V, temp); + /* combine av bits */ + tcg_gen_or_tl(cpu_PSW_AV, cpu_PSW_AV, temp3); - tcg_temp_free(temp1); + tcg_temp_free(temp); tcg_temp_free(temp2); + tcg_temp_free(temp3); + tcg_temp_free_i64(temp64); + } -/* ret = r1[pos1] op1 r2[pos2]; */ -static inline void gen_bit_1op(TCGv ret, TCGv r1, TCGv r2, - int pos1, int pos2, - void(*op1)(TCGv, TCGv, TCGv)) -{ - TCGv temp1, temp2; - - temp1 = tcg_temp_new(); - temp2 = tcg_temp_new(); +static inline void gen_subs(TCGv ret, TCGv r1, TCGv r2); - tcg_gen_shri_tl(temp2, r2, pos2); - tcg_gen_shri_tl(temp1, r1, pos1); - - (*op1)(ret, temp1, temp2); +static inline void +gen_maddsus_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, + TCGv r3, uint32_t n, uint32_t mode) +{ + TCGv temp = tcg_const_i32(n); + TCGv temp2 = tcg_temp_new(); + TCGv temp3 = tcg_temp_new(); + TCGv_i64 temp64 = tcg_temp_new_i64(); - tcg_gen_andi_tl(ret, ret, 0x1); + switch (mode) { + case MODE_LL: + GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + break; + case MODE_LU: + GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + break; + case MODE_UL: + GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + break; + case MODE_UU: + GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + break; + } + tcg_gen_extr_i64_i32(temp, temp2, temp64); + gen_subs(ret_low, r1_low, temp); + tcg_gen_mov_tl(temp, cpu_PSW_V); + tcg_gen_mov_tl(temp3, cpu_PSW_AV); + gen_adds(ret_high, r1_high, temp2); + /* combine v bits */ + tcg_gen_or_tl(cpu_PSW_V, cpu_PSW_V, temp); + /* combine av bits */ + tcg_gen_or_tl(cpu_PSW_AV, cpu_PSW_AV, temp3); - tcg_temp_free(temp1); + tcg_temp_free(temp); tcg_temp_free(temp2); -} - -/* helpers for generating program flow micro-ops */ - -static inline void gen_save_pc(target_ulong pc) -{ - tcg_gen_movi_tl(cpu_PC, pc); -} + tcg_temp_free(temp3); + tcg_temp_free_i64(temp64); -static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) -{ - TranslationBlock *tb; - tb = ctx->tb; - if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK) && - likely(!ctx->singlestep_enabled)) { - tcg_gen_goto_tb(n); - gen_save_pc(dest); - tcg_gen_exit_tb((uintptr_t)tb + n); - } else { - gen_save_pc(dest); - if (ctx->singlestep_enabled) { - /* raise exception debug */ - } - tcg_gen_exit_tb(0); - } } -static inline void gen_branch_cond(DisasContext *ctx, TCGCond cond, TCGv r1, - TCGv r2, int16_t address) +static inline void +gen_maddsums_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, + TCGv r3, uint32_t n, uint32_t mode) { - int jumpLabel; - jumpLabel = gen_new_label(); - tcg_gen_brcond_tl(cond, r1, r2, jumpLabel); + TCGv temp = tcg_const_i32(n); + TCGv_i64 temp64 = tcg_temp_new_i64(); + TCGv_i64 temp64_2 = tcg_temp_new_i64(); - gen_goto_tb(ctx, 1, ctx->next_pc); + switch (mode) { + case MODE_LL: + GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + break; + case MODE_LU: + GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + break; + case MODE_UL: + GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + break; + case MODE_UU: + GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + break; + } + tcg_gen_sari_i64(temp64_2, temp64, 32); /* high */ + tcg_gen_ext32s_i64(temp64, temp64); /* low */ + tcg_gen_sub_i64(temp64, temp64_2, temp64); + tcg_gen_shli_i64(temp64, temp64, 16); + tcg_gen_concat_i32_i64(temp64_2, r1_low, r1_high); - gen_set_label(jumpLabel); - gen_goto_tb(ctx, 0, ctx->pc + address * 2); -} + gen_helper_add64_ssov(temp64, cpu_env, temp64_2, temp64); + tcg_gen_extr_i64_i32(ret_low, ret_high, temp64); -static inline void gen_branch_condi(DisasContext *ctx, TCGCond cond, TCGv r1, - int r2, int16_t address) -{ - TCGv temp = tcg_const_i32(r2); - gen_branch_cond(ctx, cond, r1, temp, address); tcg_temp_free(temp); + tcg_temp_free_i64(temp64); + tcg_temp_free_i64(temp64_2); } -static void gen_loop(DisasContext *ctx, int r1, int32_t offset) -{ - int l1; - l1 = gen_new_label(); - - tcg_gen_subi_tl(cpu_gpr_a[r1], cpu_gpr_a[r1], 1); - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_gpr_a[r1], -1, l1); - gen_goto_tb(ctx, 1, ctx->pc + offset); - gen_set_label(l1); - gen_goto_tb(ctx, 0, ctx->next_pc); -} -static void gen_compute_branch(DisasContext *ctx, uint32_t opc, int r1, - int r2 , int32_t constant , int32_t offset) +static inline void +gen_maddm_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, + TCGv r3, uint32_t n, uint32_t mode) { - TCGv temp; - - switch (opc) { -/* SB-format jumps */ - case OPC1_16_SB_J: - case OPC1_32_B_J: - gen_goto_tb(ctx, 0, ctx->pc + offset * 2); + TCGv temp = tcg_const_i32(n); + TCGv_i64 temp64 = tcg_temp_new_i64(); + TCGv_i64 temp64_2 = tcg_temp_new_i64(); + TCGv_i64 temp64_3 = tcg_temp_new_i64(); + switch (mode) { + case MODE_LL: + GEN_HELPER_LL(mulm_h, temp64, r2, r3, temp); break; - case OPC1_32_B_CALL: - case OPC1_16_SB_CALL: - gen_helper_1arg(call, ctx->next_pc); - gen_goto_tb(ctx, 0, ctx->pc + offset * 2); + case MODE_LU: + GEN_HELPER_LU(mulm_h, temp64, r2, r3, temp); break; - case OPC1_16_SB_JZ: - gen_branch_condi(ctx, TCG_COND_EQ, cpu_gpr_d[15], 0, offset); + case MODE_UL: + GEN_HELPER_UL(mulm_h, temp64, r2, r3, temp); break; - case OPC1_16_SB_JNZ: - gen_branch_condi(ctx, TCG_COND_NE, cpu_gpr_d[15], 0, offset); + case MODE_UU: + GEN_HELPER_UU(mulm_h, temp64, r2, r3, temp); break; -/* SBC-format jumps */ - case OPC1_16_SBC_JEQ: - gen_branch_condi(ctx, TCG_COND_EQ, cpu_gpr_d[15], constant, offset); + } + tcg_gen_concat_i32_i64(temp64_2, r1_low, r1_high); + gen_add64_d(temp64_3, temp64_2, temp64); + /* write back result */ + tcg_gen_extr_i64_i32(ret_low, ret_high, temp64_3); + + tcg_temp_free(temp); + tcg_temp_free_i64(temp64); + tcg_temp_free_i64(temp64_2); + tcg_temp_free_i64(temp64_3); +} + +static inline void +gen_maddms_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, + TCGv r3, uint32_t n, uint32_t mode) +{ + TCGv temp = tcg_const_i32(n); + TCGv_i64 temp64 = tcg_temp_new_i64(); + TCGv_i64 temp64_2 = tcg_temp_new_i64(); + switch (mode) { + case MODE_LL: + GEN_HELPER_LL(mulm_h, temp64, r2, r3, temp); break; - case OPC1_16_SBC_JNE: - gen_branch_condi(ctx, TCG_COND_NE, cpu_gpr_d[15], constant, offset); + case MODE_LU: + GEN_HELPER_LU(mulm_h, temp64, r2, r3, temp); break; -/* SBRN-format jumps */ - case OPC1_16_SBRN_JZ_T: - temp = tcg_temp_new(); - tcg_gen_andi_tl(temp, cpu_gpr_d[15], 0x1u << constant); - gen_branch_condi(ctx, TCG_COND_EQ, temp, 0, offset); - tcg_temp_free(temp); + case MODE_UL: + GEN_HELPER_UL(mulm_h, temp64, r2, r3, temp); break; - case OPC1_16_SBRN_JNZ_T: - temp = tcg_temp_new(); - tcg_gen_andi_tl(temp, cpu_gpr_d[15], 0x1u << constant); - gen_branch_condi(ctx, TCG_COND_NE, temp, 0, offset); - tcg_temp_free(temp); + case MODE_UU: + GEN_HELPER_UU(mulm_h, temp64, r2, r3, temp); break; -/* SBR-format jumps */ - case OPC1_16_SBR_JEQ: - gen_branch_cond(ctx, TCG_COND_EQ, cpu_gpr_d[r1], cpu_gpr_d[15], - offset); + } + tcg_gen_concat_i32_i64(temp64_2, r1_low, r1_high); + gen_helper_add64_ssov(temp64, cpu_env, temp64_2, temp64); + tcg_gen_extr_i64_i32(ret_low, ret_high, temp64); + + tcg_temp_free(temp); + tcg_temp_free_i64(temp64); + tcg_temp_free_i64(temp64_2); +} + +static inline void +gen_maddr64_h(TCGv ret, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, + uint32_t mode) +{ + TCGv temp = tcg_const_i32(n); + TCGv_i64 temp64 = tcg_temp_new_i64(); + switch (mode) { + case MODE_LL: + GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); break; - case OPC1_16_SBR_JNE: - gen_branch_cond(ctx, TCG_COND_NE, cpu_gpr_d[r1], cpu_gpr_d[15], - offset); + case MODE_LU: + GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); break; - case OPC1_16_SBR_JNZ: - gen_branch_condi(ctx, TCG_COND_NE, cpu_gpr_d[r1], 0, offset); + case MODE_UL: + GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); break; - case OPC1_16_SBR_JNZ_A: - gen_branch_condi(ctx, TCG_COND_NE, cpu_gpr_a[r1], 0, offset); + case MODE_UU: + GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); break; - case OPC1_16_SBR_JGEZ: - gen_branch_condi(ctx, TCG_COND_GE, cpu_gpr_d[r1], 0, offset); + } + gen_helper_addr_h(ret, cpu_env, temp64, r1_low, r1_high); + + tcg_temp_free(temp); + tcg_temp_free_i64(temp64); +} + +static inline void +gen_maddr32_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) +{ + TCGv temp = tcg_temp_new(); + TCGv temp2 = tcg_temp_new(); + + tcg_gen_andi_tl(temp2, r1, 0xffff0000); + tcg_gen_shli_tl(temp, r1, 16); + gen_maddr64_h(ret, temp, temp2, r2, r3, n, mode); + + tcg_temp_free(temp); + tcg_temp_free(temp2); +} + +static inline void +gen_maddsur32_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) +{ + TCGv temp = tcg_const_i32(n); + TCGv temp2 = tcg_temp_new(); + TCGv_i64 temp64 = tcg_temp_new_i64(); + switch (mode) { + case MODE_LL: + GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); break; - case OPC1_16_SBR_JGTZ: - gen_branch_condi(ctx, TCG_COND_GT, cpu_gpr_d[r1], 0, offset); + case MODE_LU: + GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); break; - case OPC1_16_SBR_JLEZ: - gen_branch_condi(ctx, TCG_COND_LE, cpu_gpr_d[r1], 0, offset); + case MODE_UL: + GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); break; - case OPC1_16_SBR_JLTZ: - gen_branch_condi(ctx, TCG_COND_LT, cpu_gpr_d[r1], 0, offset); + case MODE_UU: + GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); break; - case OPC1_16_SBR_JZ: - gen_branch_condi(ctx, TCG_COND_EQ, cpu_gpr_d[r1], 0, offset); + } + tcg_gen_andi_tl(temp2, r1, 0xffff0000); + tcg_gen_shli_tl(temp, r1, 16); + gen_helper_addsur_h(ret, cpu_env, temp64, temp, temp2); + + tcg_temp_free(temp); + tcg_temp_free(temp2); + tcg_temp_free_i64(temp64); +} + + +static inline void +gen_maddr64s_h(TCGv ret, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, + uint32_t n, uint32_t mode) +{ + TCGv temp = tcg_const_i32(n); + TCGv_i64 temp64 = tcg_temp_new_i64(); + switch (mode) { + case MODE_LL: + GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); break; - case OPC1_16_SBR_JZ_A: - gen_branch_condi(ctx, TCG_COND_EQ, cpu_gpr_a[r1], 0, offset); + case MODE_LU: + GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); break; - case OPC1_16_SBR_LOOP: - gen_loop(ctx, r1, offset * 2 - 32); + case MODE_UL: + GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); break; -/* SR-format jumps */ - case OPC1_16_SR_JI: - tcg_gen_andi_tl(cpu_PC, cpu_gpr_a[r1], 0xfffffffe); - tcg_gen_exit_tb(0); + case MODE_UU: + GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); break; - case OPC2_16_SR_RET: - gen_helper_ret(cpu_env); - tcg_gen_exit_tb(0); + } + gen_helper_addr_h_ssov(ret, cpu_env, temp64, r1_low, r1_high); + + tcg_temp_free(temp); + tcg_temp_free_i64(temp64); +} + +static inline void +gen_maddr32s_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) +{ + TCGv temp = tcg_temp_new(); + TCGv temp2 = tcg_temp_new(); + + tcg_gen_andi_tl(temp2, r1, 0xffff0000); + tcg_gen_shli_tl(temp, r1, 16); + gen_maddr64s_h(ret, temp, temp2, r2, r3, n, mode); + + tcg_temp_free(temp); + tcg_temp_free(temp2); +} + +static inline void +gen_maddsur32s_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) +{ + TCGv temp = tcg_const_i32(n); + TCGv temp2 = tcg_temp_new(); + TCGv_i64 temp64 = tcg_temp_new_i64(); + switch (mode) { + case MODE_LL: + GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); break; -/* B-format */ - case OPC1_32_B_CALLA: - gen_helper_1arg(call, ctx->next_pc); - gen_goto_tb(ctx, 0, EA_B_ABSOLUT(offset)); + case MODE_LU: + GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); break; - case OPC1_32_B_JLA: - tcg_gen_movi_tl(cpu_gpr_a[11], ctx->next_pc); - case OPC1_32_B_JA: - gen_goto_tb(ctx, 0, EA_B_ABSOLUT(offset)); + case MODE_UL: + GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); break; - case OPC1_32_B_JL: - tcg_gen_movi_tl(cpu_gpr_a[11], ctx->next_pc); - gen_goto_tb(ctx, 0, ctx->pc + offset * 2); + case MODE_UU: + GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); break; - default: - printf("Branch Error at %x\n", ctx->pc); } - ctx->bstate = BS_BRANCH; + tcg_gen_andi_tl(temp2, r1, 0xffff0000); + tcg_gen_shli_tl(temp, r1, 16); + gen_helper_addsur_h_ssov(ret, cpu_env, temp64, temp, temp2); + + tcg_temp_free(temp); + tcg_temp_free(temp2); + tcg_temp_free_i64(temp64); } +static inline void +gen_maddr_q(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n) +{ + TCGv temp = tcg_const_i32(n); + gen_helper_maddr_q(ret, cpu_env, r1, r2, r3, temp); + tcg_temp_free(temp); +} -/* - * Functions for decoding instructions - */ +static inline void +gen_maddrs_q(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n) +{ + TCGv temp = tcg_const_i32(n); + gen_helper_maddr_q_ssov(ret, cpu_env, r1, r2, r3, temp); + tcg_temp_free(temp); +} -static void decode_src_opc(DisasContext *ctx, int op1) +static inline void +gen_madd32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n, + uint32_t up_shift, CPUTriCoreState *env) { - int r1; - int32_t const4; - TCGv temp, temp2; + TCGv temp = tcg_temp_new(); + TCGv temp2 = tcg_temp_new(); + TCGv temp3 = tcg_temp_new(); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t3 = tcg_temp_new_i64(); + + tcg_gen_ext_i32_i64(t2, arg2); + tcg_gen_ext_i32_i64(t3, arg3); + + tcg_gen_mul_i64(t2, t2, t3); + tcg_gen_shli_i64(t2, t2, n); + + tcg_gen_ext_i32_i64(t1, arg1); + tcg_gen_sari_i64(t2, t2, up_shift); + + tcg_gen_add_i64(t3, t1, t2); + tcg_gen_trunc_i64_i32(temp3, t3); + /* calc v bit */ + tcg_gen_setcondi_i64(TCG_COND_GT, t1, t3, 0x7fffffffLL); + tcg_gen_setcondi_i64(TCG_COND_LT, t2, t3, -0x80000000LL); + tcg_gen_or_i64(t1, t1, t2); + tcg_gen_trunc_i64_i32(cpu_PSW_V, t1); + tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); + /* We produce an overflow on the host if the mul before was + (0x80000000 * 0x80000000) << 1). If this is the + case, we negate the ovf. */ + if (n == 1) { + tcg_gen_setcondi_tl(TCG_COND_EQ, temp, arg2, 0x80000000); + tcg_gen_setcond_tl(TCG_COND_EQ, temp2, arg2, arg3); + tcg_gen_and_tl(temp, temp, temp2); + tcg_gen_shli_tl(temp, temp, 31); + /* negate v bit, if special condition */ + tcg_gen_xor_tl(cpu_PSW_V, cpu_PSW_V, temp); + } + /* Calc SV bit */ + tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + /* Calc AV/SAV bits */ + tcg_gen_add_tl(cpu_PSW_AV, temp3, temp3); + tcg_gen_xor_tl(cpu_PSW_AV, temp3, cpu_PSW_AV); + /* calc SAV */ + tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + /* write back result */ + tcg_gen_mov_tl(ret, temp3); - r1 = MASK_OP_SRC_S1D(ctx->opcode); - const4 = MASK_OP_SRC_CONST4_SEXT(ctx->opcode); + tcg_temp_free(temp); + tcg_temp_free(temp2); + tcg_temp_free(temp3); + tcg_temp_free_i64(t1); + tcg_temp_free_i64(t2); + tcg_temp_free_i64(t3); +} - switch (op1) { - case OPC1_16_SRC_ADD: - gen_addi_d(cpu_gpr_d[r1], cpu_gpr_d[r1], const4); - break; - case OPC1_16_SRC_ADD_A15: - gen_addi_d(cpu_gpr_d[r1], cpu_gpr_d[15], const4); +static inline void +gen_m16add32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n) +{ + TCGv temp = tcg_temp_new(); + TCGv temp2 = tcg_temp_new(); + if (n == 0) { + tcg_gen_mul_tl(temp, arg2, arg3); + } else { /* n is expected to be 1 */ + tcg_gen_mul_tl(temp, arg2, arg3); + tcg_gen_shli_tl(temp, temp, 1); + /* catch special case r1 = r2 = 0x8000 */ + tcg_gen_setcondi_tl(TCG_COND_EQ, temp2, temp, 0x80000000); + tcg_gen_sub_tl(temp, temp, temp2); + } + gen_add_d(ret, arg1, temp); + + tcg_temp_free(temp); + tcg_temp_free(temp2); +} + +static inline void +gen_m16adds32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n) +{ + TCGv temp = tcg_temp_new(); + TCGv temp2 = tcg_temp_new(); + if (n == 0) { + tcg_gen_mul_tl(temp, arg2, arg3); + } else { /* n is expected to be 1 */ + tcg_gen_mul_tl(temp, arg2, arg3); + tcg_gen_shli_tl(temp, temp, 1); + /* catch special case r1 = r2 = 0x8000 */ + tcg_gen_setcondi_tl(TCG_COND_EQ, temp2, temp, 0x80000000); + tcg_gen_sub_tl(temp, temp, temp2); + } + gen_adds(ret, arg1, temp); + + tcg_temp_free(temp); + tcg_temp_free(temp2); +} + +static inline void +gen_m16add64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, + TCGv arg3, uint32_t n) +{ + TCGv temp = tcg_temp_new(); + TCGv temp2 = tcg_temp_new(); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t3 = tcg_temp_new_i64(); + + if (n == 0) { + tcg_gen_mul_tl(temp, arg2, arg3); + } else { /* n is expected to be 1 */ + tcg_gen_mul_tl(temp, arg2, arg3); + tcg_gen_shli_tl(temp, temp, 1); + /* catch special case r1 = r2 = 0x8000 */ + tcg_gen_setcondi_tl(TCG_COND_EQ, temp2, temp, 0x80000000); + tcg_gen_sub_tl(temp, temp, temp2); + } + tcg_gen_ext_i32_i64(t2, temp); + tcg_gen_shli_i64(t2, t2, 16); + tcg_gen_concat_i32_i64(t1, arg1_low, arg1_high); + gen_add64_d(t3, t1, t2); + /* write back result */ + tcg_gen_extr_i64_i32(rl, rh, t3); + + tcg_temp_free_i64(t1); + tcg_temp_free_i64(t2); + tcg_temp_free_i64(t3); + tcg_temp_free(temp); + tcg_temp_free(temp2); +} + +static inline void +gen_m16adds64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, + TCGv arg3, uint32_t n) +{ + TCGv temp = tcg_temp_new(); + TCGv temp2 = tcg_temp_new(); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + + if (n == 0) { + tcg_gen_mul_tl(temp, arg2, arg3); + } else { /* n is expected to be 1 */ + tcg_gen_mul_tl(temp, arg2, arg3); + tcg_gen_shli_tl(temp, temp, 1); + /* catch special case r1 = r2 = 0x8000 */ + tcg_gen_setcondi_tl(TCG_COND_EQ, temp2, temp, 0x80000000); + tcg_gen_sub_tl(temp, temp, temp2); + } + tcg_gen_ext_i32_i64(t2, temp); + tcg_gen_shli_i64(t2, t2, 16); + tcg_gen_concat_i32_i64(t1, arg1_low, arg1_high); + + gen_helper_add64_ssov(t1, cpu_env, t1, t2); + tcg_gen_extr_i64_i32(rl, rh, t1); + + tcg_temp_free(temp); + tcg_temp_free(temp2); + tcg_temp_free_i64(t1); + tcg_temp_free_i64(t2); +} + +static inline void +gen_madd64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, + TCGv arg3, uint32_t n, CPUTriCoreState *env) +{ + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t3 = tcg_temp_new_i64(); + TCGv_i64 t4 = tcg_temp_new_i64(); + TCGv temp, temp2; + + tcg_gen_concat_i32_i64(t1, arg1_low, arg1_high); + tcg_gen_ext_i32_i64(t2, arg2); + tcg_gen_ext_i32_i64(t3, arg3); + + tcg_gen_mul_i64(t2, t2, t3); + if (n != 0) { + tcg_gen_shli_i64(t2, t2, 1); + } + tcg_gen_add_i64(t4, t1, t2); + /* calc v bit */ + tcg_gen_xor_i64(t3, t4, t1); + tcg_gen_xor_i64(t2, t1, t2); + tcg_gen_andc_i64(t3, t3, t2); + tcg_gen_trunc_shr_i64_i32(cpu_PSW_V, t3, 32); + /* We produce an overflow on the host if the mul before was + (0x80000000 * 0x80000000) << 1). If this is the + case, we negate the ovf. */ + if (n == 1) { + temp = tcg_temp_new(); + temp2 = tcg_temp_new(); + tcg_gen_setcondi_tl(TCG_COND_EQ, temp, arg2, 0x80000000); + tcg_gen_setcond_tl(TCG_COND_EQ, temp2, arg2, arg3); + tcg_gen_and_tl(temp, temp, temp2); + tcg_gen_shli_tl(temp, temp, 31); + /* negate v bit, if special condition */ + tcg_gen_xor_tl(cpu_PSW_V, cpu_PSW_V, temp); + + tcg_temp_free(temp); + tcg_temp_free(temp2); + } + /* write back result */ + tcg_gen_extr_i64_i32(rl, rh, t4); + /* Calc SV bit */ + tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + /* Calc AV/SAV bits */ + tcg_gen_add_tl(cpu_PSW_AV, rh, rh); + tcg_gen_xor_tl(cpu_PSW_AV, rh, cpu_PSW_AV); + /* calc SAV */ + tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + + tcg_temp_free_i64(t1); + tcg_temp_free_i64(t2); + tcg_temp_free_i64(t3); + tcg_temp_free_i64(t4); +} + +static inline void +gen_madds32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n, + uint32_t up_shift) +{ + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t3 = tcg_temp_new_i64(); + + tcg_gen_ext_i32_i64(t1, arg1); + tcg_gen_ext_i32_i64(t2, arg2); + tcg_gen_ext_i32_i64(t3, arg3); + + tcg_gen_mul_i64(t2, t2, t3); + tcg_gen_sari_i64(t2, t2, up_shift - n); + + gen_helper_madd32_q_add_ssov(ret, cpu_env, t1, t2); + + tcg_temp_free_i64(t1); + tcg_temp_free_i64(t2); + tcg_temp_free_i64(t3); +} + +static inline void +gen_madds64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, + TCGv arg3, uint32_t n) +{ + TCGv_i64 r1 = tcg_temp_new_i64(); + TCGv temp = tcg_const_i32(n); + + tcg_gen_concat_i32_i64(r1, arg1_low, arg1_high); + gen_helper_madd64_q_ssov(r1, cpu_env, r1, arg2, arg3, temp); + tcg_gen_extr_i64_i32(rl, rh, r1); + + tcg_temp_free_i64(r1); + tcg_temp_free(temp); +} +/* ret = r2 - (r1 * r3); */ +static inline void gen_msub32_d(TCGv ret, TCGv r1, TCGv r2, TCGv r3) +{ + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t3 = tcg_temp_new_i64(); + + tcg_gen_ext_i32_i64(t1, r1); + tcg_gen_ext_i32_i64(t2, r2); + tcg_gen_ext_i32_i64(t3, r3); + + tcg_gen_mul_i64(t1, t1, t3); + tcg_gen_sub_i64(t1, t2, t1); + + tcg_gen_trunc_i64_i32(ret, t1); + /* calc V + t2 > 0x7fffffff */ + tcg_gen_setcondi_i64(TCG_COND_GT, t3, t1, 0x7fffffffLL); + /* result < -0x80000000 */ + tcg_gen_setcondi_i64(TCG_COND_LT, t2, t1, -0x80000000LL); + tcg_gen_or_i64(t2, t2, t3); + tcg_gen_trunc_i64_i32(cpu_PSW_V, t2); + tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); + + /* Calc SV bit */ + tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + /* Calc AV/SAV bits */ + tcg_gen_add_tl(cpu_PSW_AV, ret, ret); + tcg_gen_xor_tl(cpu_PSW_AV, ret, cpu_PSW_AV); + /* calc SAV */ + tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + + tcg_temp_free_i64(t1); + tcg_temp_free_i64(t2); + tcg_temp_free_i64(t3); +} + +static inline void gen_msubi32_d(TCGv ret, TCGv r1, TCGv r2, int32_t con) +{ + TCGv temp = tcg_const_i32(con); + gen_msub32_d(ret, r1, r2, temp); + tcg_temp_free(temp); +} + +static inline void +gen_msub64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, + TCGv r3) +{ + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + TCGv t3 = tcg_temp_new(); + TCGv t4 = tcg_temp_new(); + + tcg_gen_muls2_tl(t1, t2, r1, r3); + /* only the sub can overflow */ + tcg_gen_sub2_tl(t3, t4, r2_low, r2_high, t1, t2); + /* calc V bit */ + tcg_gen_xor_tl(cpu_PSW_V, t4, r2_high); + tcg_gen_xor_tl(t1, r2_high, t2); + tcg_gen_and_tl(cpu_PSW_V, cpu_PSW_V, t1); + /* Calc SV bit */ + tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + /* Calc AV/SAV bits */ + tcg_gen_add_tl(cpu_PSW_AV, t4, t4); + tcg_gen_xor_tl(cpu_PSW_AV, t4, cpu_PSW_AV); + /* calc SAV */ + tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + /* write back the result */ + tcg_gen_mov_tl(ret_low, t3); + tcg_gen_mov_tl(ret_high, t4); + + tcg_temp_free(t1); + tcg_temp_free(t2); + tcg_temp_free(t3); + tcg_temp_free(t4); +} + +static inline void +gen_msubi64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, + int32_t con) +{ + TCGv temp = tcg_const_i32(con); + gen_msub64_d(ret_low, ret_high, r1, r2_low, r2_high, temp); + tcg_temp_free(temp); +} + +static inline void +gen_msubu64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, + TCGv r3) +{ + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t3 = tcg_temp_new_i64(); + + tcg_gen_extu_i32_i64(t1, r1); + tcg_gen_concat_i32_i64(t2, r2_low, r2_high); + tcg_gen_extu_i32_i64(t3, r3); + + tcg_gen_mul_i64(t1, t1, t3); + tcg_gen_sub_i64(t3, t2, t1); + tcg_gen_extr_i64_i32(ret_low, ret_high, t3); + /* calc V bit, only the sub can overflow, if t1 > t2 */ + tcg_gen_setcond_i64(TCG_COND_GTU, t1, t1, t2); + tcg_gen_trunc_i64_i32(cpu_PSW_V, t1); + tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); + /* Calc SV bit */ + tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + /* Calc AV/SAV bits */ + tcg_gen_add_tl(cpu_PSW_AV, ret_high, ret_high); + tcg_gen_xor_tl(cpu_PSW_AV, ret_high, cpu_PSW_AV); + /* calc SAV */ + tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + + tcg_temp_free_i64(t1); + tcg_temp_free_i64(t2); + tcg_temp_free_i64(t3); +} + +static inline void +gen_msubui64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, + int32_t con) +{ + TCGv temp = tcg_const_i32(con); + gen_msubu64_d(ret_low, ret_high, r1, r2_low, r2_high, temp); + tcg_temp_free(temp); +} + +static inline void gen_addi_d(TCGv ret, TCGv r1, target_ulong r2) +{ + TCGv temp = tcg_const_i32(r2); + gen_add_d(ret, r1, temp); + tcg_temp_free(temp); +} +/* calculate the carry bit too */ +static inline void gen_add_CC(TCGv ret, TCGv r1, TCGv r2) +{ + TCGv t0 = tcg_temp_new_i32(); + TCGv result = tcg_temp_new_i32(); + + tcg_gen_movi_tl(t0, 0); + /* Addition and set C/V/SV bits */ + tcg_gen_add2_i32(result, cpu_PSW_C, r1, t0, r2, t0); + /* calc V bit */ + tcg_gen_xor_tl(cpu_PSW_V, result, r1); + tcg_gen_xor_tl(t0, r1, r2); + tcg_gen_andc_tl(cpu_PSW_V, cpu_PSW_V, t0); + /* Calc SV bit */ + tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + /* Calc AV/SAV bits */ + tcg_gen_add_tl(cpu_PSW_AV, result, result); + tcg_gen_xor_tl(cpu_PSW_AV, result, cpu_PSW_AV); + /* calc SAV */ + tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + /* write back result */ + tcg_gen_mov_tl(ret, result); + + tcg_temp_free(result); + tcg_temp_free(t0); +} + +static inline void gen_addi_CC(TCGv ret, TCGv r1, int32_t con) +{ + TCGv temp = tcg_const_i32(con); + gen_add_CC(ret, r1, temp); + tcg_temp_free(temp); +} + +static inline void gen_addc_CC(TCGv ret, TCGv r1, TCGv r2) +{ + TCGv carry = tcg_temp_new_i32(); + TCGv t0 = tcg_temp_new_i32(); + TCGv result = tcg_temp_new_i32(); + + tcg_gen_movi_tl(t0, 0); + tcg_gen_setcondi_tl(TCG_COND_NE, carry, cpu_PSW_C, 0); + /* Addition, carry and set C/V/SV bits */ + tcg_gen_add2_i32(result, cpu_PSW_C, r1, t0, carry, t0); + tcg_gen_add2_i32(result, cpu_PSW_C, result, cpu_PSW_C, r2, t0); + /* calc V bit */ + tcg_gen_xor_tl(cpu_PSW_V, result, r1); + tcg_gen_xor_tl(t0, r1, r2); + tcg_gen_andc_tl(cpu_PSW_V, cpu_PSW_V, t0); + /* Calc SV bit */ + tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + /* Calc AV/SAV bits */ + tcg_gen_add_tl(cpu_PSW_AV, result, result); + tcg_gen_xor_tl(cpu_PSW_AV, result, cpu_PSW_AV); + /* calc SAV */ + tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + /* write back result */ + tcg_gen_mov_tl(ret, result); + + tcg_temp_free(result); + tcg_temp_free(t0); + tcg_temp_free(carry); +} + +static inline void gen_addci_CC(TCGv ret, TCGv r1, int32_t con) +{ + TCGv temp = tcg_const_i32(con); + gen_addc_CC(ret, r1, temp); + tcg_temp_free(temp); +} + +static inline void gen_cond_add(TCGCond cond, TCGv r1, TCGv r2, TCGv r3, + TCGv r4) +{ + TCGv temp = tcg_temp_new(); + TCGv temp2 = tcg_temp_new(); + TCGv result = tcg_temp_new(); + TCGv mask = tcg_temp_new(); + TCGv t0 = tcg_const_i32(0); + + /* create mask for sticky bits */ + tcg_gen_setcond_tl(cond, mask, r4, t0); + tcg_gen_shli_tl(mask, mask, 31); + + tcg_gen_add_tl(result, r1, r2); + /* Calc PSW_V */ + tcg_gen_xor_tl(temp, result, r1); + tcg_gen_xor_tl(temp2, r1, r2); + tcg_gen_andc_tl(temp, temp, temp2); + tcg_gen_movcond_tl(cond, cpu_PSW_V, r4, t0, temp, cpu_PSW_V); + /* Set PSW_SV */ + tcg_gen_and_tl(temp, temp, mask); + tcg_gen_or_tl(cpu_PSW_SV, temp, cpu_PSW_SV); + /* calc AV bit */ + tcg_gen_add_tl(temp, result, result); + tcg_gen_xor_tl(temp, temp, result); + tcg_gen_movcond_tl(cond, cpu_PSW_AV, r4, t0, temp, cpu_PSW_AV); + /* calc SAV bit */ + tcg_gen_and_tl(temp, temp, mask); + tcg_gen_or_tl(cpu_PSW_SAV, temp, cpu_PSW_SAV); + /* write back result */ + tcg_gen_movcond_tl(cond, r3, r4, t0, result, r1); + + tcg_temp_free(t0); + tcg_temp_free(temp); + tcg_temp_free(temp2); + tcg_temp_free(result); + tcg_temp_free(mask); +} + +static inline void gen_condi_add(TCGCond cond, TCGv r1, int32_t r2, + TCGv r3, TCGv r4) +{ + TCGv temp = tcg_const_i32(r2); + gen_cond_add(cond, r1, temp, r3, r4); + tcg_temp_free(temp); +} + +static inline void gen_sub_d(TCGv ret, TCGv r1, TCGv r2) +{ + TCGv temp = tcg_temp_new_i32(); + TCGv result = tcg_temp_new_i32(); + + tcg_gen_sub_tl(result, r1, r2); + /* calc V bit */ + tcg_gen_xor_tl(cpu_PSW_V, result, r1); + tcg_gen_xor_tl(temp, r1, r2); + tcg_gen_and_tl(cpu_PSW_V, cpu_PSW_V, temp); + /* calc SV bit */ + tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + /* Calc AV bit */ + tcg_gen_add_tl(cpu_PSW_AV, result, result); + tcg_gen_xor_tl(cpu_PSW_AV, result, cpu_PSW_AV); + /* calc SAV bit */ + tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + /* write back result */ + tcg_gen_mov_tl(ret, result); + + tcg_temp_free(temp); + tcg_temp_free(result); +} + +static inline void +gen_sub64_d(TCGv_i64 ret, TCGv_i64 r1, TCGv_i64 r2) +{ + TCGv temp = tcg_temp_new(); + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 result = tcg_temp_new_i64(); + + tcg_gen_sub_i64(result, r1, r2); + /* calc v bit */ + tcg_gen_xor_i64(t1, result, r1); + tcg_gen_xor_i64(t0, r1, r2); + tcg_gen_and_i64(t1, t1, t0); + tcg_gen_trunc_shr_i64_i32(cpu_PSW_V, t1, 32); + /* calc SV bit */ + tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + /* calc AV/SAV bits */ + tcg_gen_trunc_shr_i64_i32(temp, result, 32); + tcg_gen_add_tl(cpu_PSW_AV, temp, temp); + tcg_gen_xor_tl(cpu_PSW_AV, temp, cpu_PSW_AV); + /* calc SAV */ + tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + /* write back result */ + tcg_gen_mov_i64(ret, result); + + tcg_temp_free(temp); + tcg_temp_free_i64(result); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); +} + +static inline void gen_sub_CC(TCGv ret, TCGv r1, TCGv r2) +{ + TCGv result = tcg_temp_new(); + TCGv temp = tcg_temp_new(); + + tcg_gen_sub_tl(result, r1, r2); + /* calc C bit */ + tcg_gen_setcond_tl(TCG_COND_GEU, cpu_PSW_C, r1, r2); + /* calc V bit */ + tcg_gen_xor_tl(cpu_PSW_V, result, r1); + tcg_gen_xor_tl(temp, r1, r2); + tcg_gen_and_tl(cpu_PSW_V, cpu_PSW_V, temp); + /* calc SV bit */ + tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + /* Calc AV bit */ + tcg_gen_add_tl(cpu_PSW_AV, result, result); + tcg_gen_xor_tl(cpu_PSW_AV, result, cpu_PSW_AV); + /* calc SAV bit */ + tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + /* write back result */ + tcg_gen_mov_tl(ret, result); + + tcg_temp_free(result); + tcg_temp_free(temp); +} + +static inline void gen_subc_CC(TCGv ret, TCGv r1, TCGv r2) +{ + TCGv temp = tcg_temp_new(); + tcg_gen_not_tl(temp, r2); + gen_addc_CC(ret, r1, temp); + tcg_temp_free(temp); +} + +static inline void gen_cond_sub(TCGCond cond, TCGv r1, TCGv r2, TCGv r3, + TCGv r4) +{ + TCGv temp = tcg_temp_new(); + TCGv temp2 = tcg_temp_new(); + TCGv result = tcg_temp_new(); + TCGv mask = tcg_temp_new(); + TCGv t0 = tcg_const_i32(0); + + /* create mask for sticky bits */ + tcg_gen_setcond_tl(cond, mask, r4, t0); + tcg_gen_shli_tl(mask, mask, 31); + + tcg_gen_sub_tl(result, r1, r2); + /* Calc PSW_V */ + tcg_gen_xor_tl(temp, result, r1); + tcg_gen_xor_tl(temp2, r1, r2); + tcg_gen_and_tl(temp, temp, temp2); + tcg_gen_movcond_tl(cond, cpu_PSW_V, r4, t0, temp, cpu_PSW_V); + /* Set PSW_SV */ + tcg_gen_and_tl(temp, temp, mask); + tcg_gen_or_tl(cpu_PSW_SV, temp, cpu_PSW_SV); + /* calc AV bit */ + tcg_gen_add_tl(temp, result, result); + tcg_gen_xor_tl(temp, temp, result); + tcg_gen_movcond_tl(cond, cpu_PSW_AV, r4, t0, temp, cpu_PSW_AV); + /* calc SAV bit */ + tcg_gen_and_tl(temp, temp, mask); + tcg_gen_or_tl(cpu_PSW_SAV, temp, cpu_PSW_SAV); + /* write back result */ + tcg_gen_movcond_tl(cond, r3, r4, t0, result, r1); + + tcg_temp_free(t0); + tcg_temp_free(temp); + tcg_temp_free(temp2); + tcg_temp_free(result); + tcg_temp_free(mask); +} + +static inline void +gen_msub_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, + TCGv r3, uint32_t n, uint32_t mode) +{ + TCGv temp = tcg_const_i32(n); + TCGv temp2 = tcg_temp_new(); + TCGv_i64 temp64 = tcg_temp_new_i64(); + switch (mode) { + case MODE_LL: + GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + break; + case MODE_LU: + GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + break; + case MODE_UL: + GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + break; + case MODE_UU: + GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + break; + } + tcg_gen_extr_i64_i32(temp, temp2, temp64); + gen_addsub64_h(ret_low, ret_high, r1_low, r1_high, temp, temp2, + tcg_gen_sub_tl, tcg_gen_sub_tl); + tcg_temp_free(temp); + tcg_temp_free(temp2); + tcg_temp_free_i64(temp64); +} + +static inline void +gen_msubs_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, + TCGv r3, uint32_t n, uint32_t mode) +{ + TCGv temp = tcg_const_i32(n); + TCGv temp2 = tcg_temp_new(); + TCGv temp3 = tcg_temp_new(); + TCGv_i64 temp64 = tcg_temp_new_i64(); + + switch (mode) { + case MODE_LL: + GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + break; + case MODE_LU: + GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + break; + case MODE_UL: + GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + break; + case MODE_UU: + GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + break; + } + tcg_gen_extr_i64_i32(temp, temp2, temp64); + gen_subs(ret_low, r1_low, temp); + tcg_gen_mov_tl(temp, cpu_PSW_V); + tcg_gen_mov_tl(temp3, cpu_PSW_AV); + gen_subs(ret_high, r1_high, temp2); + /* combine v bits */ + tcg_gen_or_tl(cpu_PSW_V, cpu_PSW_V, temp); + /* combine av bits */ + tcg_gen_or_tl(cpu_PSW_AV, cpu_PSW_AV, temp3); + + tcg_temp_free(temp); + tcg_temp_free(temp2); + tcg_temp_free(temp3); + tcg_temp_free_i64(temp64); +} + +static inline void +gen_msubm_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, + TCGv r3, uint32_t n, uint32_t mode) +{ + TCGv temp = tcg_const_i32(n); + TCGv_i64 temp64 = tcg_temp_new_i64(); + TCGv_i64 temp64_2 = tcg_temp_new_i64(); + TCGv_i64 temp64_3 = tcg_temp_new_i64(); + switch (mode) { + case MODE_LL: + GEN_HELPER_LL(mulm_h, temp64, r2, r3, temp); + break; + case MODE_LU: + GEN_HELPER_LU(mulm_h, temp64, r2, r3, temp); + break; + case MODE_UL: + GEN_HELPER_UL(mulm_h, temp64, r2, r3, temp); + break; + case MODE_UU: + GEN_HELPER_UU(mulm_h, temp64, r2, r3, temp); + break; + } + tcg_gen_concat_i32_i64(temp64_2, r1_low, r1_high); + gen_sub64_d(temp64_3, temp64_2, temp64); + /* write back result */ + tcg_gen_extr_i64_i32(ret_low, ret_high, temp64_3); + + tcg_temp_free(temp); + tcg_temp_free_i64(temp64); + tcg_temp_free_i64(temp64_2); + tcg_temp_free_i64(temp64_3); +} + +static inline void +gen_msubms_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, + TCGv r3, uint32_t n, uint32_t mode) +{ + TCGv temp = tcg_const_i32(n); + TCGv_i64 temp64 = tcg_temp_new_i64(); + TCGv_i64 temp64_2 = tcg_temp_new_i64(); + switch (mode) { + case MODE_LL: + GEN_HELPER_LL(mulm_h, temp64, r2, r3, temp); + break; + case MODE_LU: + GEN_HELPER_LU(mulm_h, temp64, r2, r3, temp); + break; + case MODE_UL: + GEN_HELPER_UL(mulm_h, temp64, r2, r3, temp); + break; + case MODE_UU: + GEN_HELPER_UU(mulm_h, temp64, r2, r3, temp); + break; + } + tcg_gen_concat_i32_i64(temp64_2, r1_low, r1_high); + gen_helper_sub64_ssov(temp64, cpu_env, temp64_2, temp64); + tcg_gen_extr_i64_i32(ret_low, ret_high, temp64); + + tcg_temp_free(temp); + tcg_temp_free_i64(temp64); + tcg_temp_free_i64(temp64_2); +} + +static inline void +gen_msubr64_h(TCGv ret, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, + uint32_t mode) +{ + TCGv temp = tcg_const_i32(n); + TCGv_i64 temp64 = tcg_temp_new_i64(); + switch (mode) { + case MODE_LL: + GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + break; + case MODE_LU: + GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + break; + case MODE_UL: + GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + break; + case MODE_UU: + GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + break; + } + gen_helper_subr_h(ret, cpu_env, temp64, r1_low, r1_high); + + tcg_temp_free(temp); + tcg_temp_free_i64(temp64); +} + +static inline void +gen_msubr32_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) +{ + TCGv temp = tcg_temp_new(); + TCGv temp2 = tcg_temp_new(); + + tcg_gen_andi_tl(temp2, r1, 0xffff0000); + tcg_gen_shli_tl(temp, r1, 16); + gen_msubr64_h(ret, temp, temp2, r2, r3, n, mode); + + tcg_temp_free(temp); + tcg_temp_free(temp2); +} + +static inline void +gen_msubr64s_h(TCGv ret, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, + uint32_t n, uint32_t mode) +{ + TCGv temp = tcg_const_i32(n); + TCGv_i64 temp64 = tcg_temp_new_i64(); + switch (mode) { + case MODE_LL: + GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + break; + case MODE_LU: + GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + break; + case MODE_UL: + GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + break; + case MODE_UU: + GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + break; + } + gen_helper_subr_h_ssov(ret, cpu_env, temp64, r1_low, r1_high); + + tcg_temp_free(temp); + tcg_temp_free_i64(temp64); +} + +static inline void +gen_msubr32s_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) +{ + TCGv temp = tcg_temp_new(); + TCGv temp2 = tcg_temp_new(); + + tcg_gen_andi_tl(temp2, r1, 0xffff0000); + tcg_gen_shli_tl(temp, r1, 16); + gen_msubr64s_h(ret, temp, temp2, r2, r3, n, mode); + + tcg_temp_free(temp); + tcg_temp_free(temp2); +} + +static inline void +gen_msubr_q(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n) +{ + TCGv temp = tcg_const_i32(n); + gen_helper_msubr_q(ret, cpu_env, r1, r2, r3, temp); + tcg_temp_free(temp); +} + +static inline void +gen_msubrs_q(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n) +{ + TCGv temp = tcg_const_i32(n); + gen_helper_msubr_q_ssov(ret, cpu_env, r1, r2, r3, temp); + tcg_temp_free(temp); +} + +static inline void +gen_msub32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n, + uint32_t up_shift, CPUTriCoreState *env) +{ + TCGv temp = tcg_temp_new(); + TCGv temp2 = tcg_temp_new(); + TCGv temp3 = tcg_temp_new(); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t3 = tcg_temp_new_i64(); + TCGv_i64 t4 = tcg_temp_new_i64(); + + tcg_gen_ext_i32_i64(t2, arg2); + tcg_gen_ext_i32_i64(t3, arg3); + + tcg_gen_mul_i64(t2, t2, t3); + + tcg_gen_ext_i32_i64(t1, arg1); + /* if we shift part of the fraction out, we need to round up */ + tcg_gen_andi_i64(t4, t2, (1ll << (up_shift - n)) - 1); + tcg_gen_setcondi_i64(TCG_COND_NE, t4, t4, 0); + tcg_gen_sari_i64(t2, t2, up_shift - n); + tcg_gen_add_i64(t2, t2, t4); + + tcg_gen_sub_i64(t3, t1, t2); + tcg_gen_trunc_i64_i32(temp3, t3); + /* calc v bit */ + tcg_gen_setcondi_i64(TCG_COND_GT, t1, t3, 0x7fffffffLL); + tcg_gen_setcondi_i64(TCG_COND_LT, t2, t3, -0x80000000LL); + tcg_gen_or_i64(t1, t1, t2); + tcg_gen_trunc_i64_i32(cpu_PSW_V, t1); + tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); + /* We produce an overflow on the host if the mul before was + (0x80000000 * 0x80000000) << 1). If this is the + case, we negate the ovf. */ + if (n == 1) { + tcg_gen_setcondi_tl(TCG_COND_EQ, temp, arg2, 0x80000000); + tcg_gen_setcond_tl(TCG_COND_EQ, temp2, arg2, arg3); + tcg_gen_and_tl(temp, temp, temp2); + tcg_gen_shli_tl(temp, temp, 31); + /* negate v bit, if special condition */ + tcg_gen_xor_tl(cpu_PSW_V, cpu_PSW_V, temp); + } + /* Calc SV bit */ + tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + /* Calc AV/SAV bits */ + tcg_gen_add_tl(cpu_PSW_AV, temp3, temp3); + tcg_gen_xor_tl(cpu_PSW_AV, temp3, cpu_PSW_AV); + /* calc SAV */ + tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + /* write back result */ + tcg_gen_mov_tl(ret, temp3); + + tcg_temp_free(temp); + tcg_temp_free(temp2); + tcg_temp_free(temp3); + tcg_temp_free_i64(t1); + tcg_temp_free_i64(t2); + tcg_temp_free_i64(t3); + tcg_temp_free_i64(t4); +} + +static inline void +gen_m16sub32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n) +{ + TCGv temp = tcg_temp_new(); + TCGv temp2 = tcg_temp_new(); + if (n == 0) { + tcg_gen_mul_tl(temp, arg2, arg3); + } else { /* n is expected to be 1 */ + tcg_gen_mul_tl(temp, arg2, arg3); + tcg_gen_shli_tl(temp, temp, 1); + /* catch special case r1 = r2 = 0x8000 */ + tcg_gen_setcondi_tl(TCG_COND_EQ, temp2, temp, 0x80000000); + tcg_gen_sub_tl(temp, temp, temp2); + } + gen_sub_d(ret, arg1, temp); + + tcg_temp_free(temp); + tcg_temp_free(temp2); +} + +static inline void +gen_m16subs32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n) +{ + TCGv temp = tcg_temp_new(); + TCGv temp2 = tcg_temp_new(); + if (n == 0) { + tcg_gen_mul_tl(temp, arg2, arg3); + } else { /* n is expected to be 1 */ + tcg_gen_mul_tl(temp, arg2, arg3); + tcg_gen_shli_tl(temp, temp, 1); + /* catch special case r1 = r2 = 0x8000 */ + tcg_gen_setcondi_tl(TCG_COND_EQ, temp2, temp, 0x80000000); + tcg_gen_sub_tl(temp, temp, temp2); + } + gen_subs(ret, arg1, temp); + + tcg_temp_free(temp); + tcg_temp_free(temp2); +} + +static inline void +gen_m16sub64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, + TCGv arg3, uint32_t n) +{ + TCGv temp = tcg_temp_new(); + TCGv temp2 = tcg_temp_new(); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t3 = tcg_temp_new_i64(); + + if (n == 0) { + tcg_gen_mul_tl(temp, arg2, arg3); + } else { /* n is expected to be 1 */ + tcg_gen_mul_tl(temp, arg2, arg3); + tcg_gen_shli_tl(temp, temp, 1); + /* catch special case r1 = r2 = 0x8000 */ + tcg_gen_setcondi_tl(TCG_COND_EQ, temp2, temp, 0x80000000); + tcg_gen_sub_tl(temp, temp, temp2); + } + tcg_gen_ext_i32_i64(t2, temp); + tcg_gen_shli_i64(t2, t2, 16); + tcg_gen_concat_i32_i64(t1, arg1_low, arg1_high); + gen_sub64_d(t3, t1, t2); + /* write back result */ + tcg_gen_extr_i64_i32(rl, rh, t3); + + tcg_temp_free_i64(t1); + tcg_temp_free_i64(t2); + tcg_temp_free_i64(t3); + tcg_temp_free(temp); + tcg_temp_free(temp2); +} + +static inline void +gen_m16subs64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, + TCGv arg3, uint32_t n) +{ + TCGv temp = tcg_temp_new(); + TCGv temp2 = tcg_temp_new(); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + + if (n == 0) { + tcg_gen_mul_tl(temp, arg2, arg3); + } else { /* n is expected to be 1 */ + tcg_gen_mul_tl(temp, arg2, arg3); + tcg_gen_shli_tl(temp, temp, 1); + /* catch special case r1 = r2 = 0x8000 */ + tcg_gen_setcondi_tl(TCG_COND_EQ, temp2, temp, 0x80000000); + tcg_gen_sub_tl(temp, temp, temp2); + } + tcg_gen_ext_i32_i64(t2, temp); + tcg_gen_shli_i64(t2, t2, 16); + tcg_gen_concat_i32_i64(t1, arg1_low, arg1_high); + + gen_helper_sub64_ssov(t1, cpu_env, t1, t2); + tcg_gen_extr_i64_i32(rl, rh, t1); + + tcg_temp_free(temp); + tcg_temp_free(temp2); + tcg_temp_free_i64(t1); + tcg_temp_free_i64(t2); +} + +static inline void +gen_msub64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, + TCGv arg3, uint32_t n, CPUTriCoreState *env) +{ + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t3 = tcg_temp_new_i64(); + TCGv_i64 t4 = tcg_temp_new_i64(); + TCGv temp, temp2; + + tcg_gen_concat_i32_i64(t1, arg1_low, arg1_high); + tcg_gen_ext_i32_i64(t2, arg2); + tcg_gen_ext_i32_i64(t3, arg3); + + tcg_gen_mul_i64(t2, t2, t3); + if (n != 0) { + tcg_gen_shli_i64(t2, t2, 1); + } + tcg_gen_sub_i64(t4, t1, t2); + /* calc v bit */ + tcg_gen_xor_i64(t3, t4, t1); + tcg_gen_xor_i64(t2, t1, t2); + tcg_gen_and_i64(t3, t3, t2); + tcg_gen_trunc_shr_i64_i32(cpu_PSW_V, t3, 32); + /* We produce an overflow on the host if the mul before was + (0x80000000 * 0x80000000) << 1). If this is the + case, we negate the ovf. */ + if (n == 1) { + temp = tcg_temp_new(); + temp2 = tcg_temp_new(); + tcg_gen_setcondi_tl(TCG_COND_EQ, temp, arg2, 0x80000000); + tcg_gen_setcond_tl(TCG_COND_EQ, temp2, arg2, arg3); + tcg_gen_and_tl(temp, temp, temp2); + tcg_gen_shli_tl(temp, temp, 31); + /* negate v bit, if special condition */ + tcg_gen_xor_tl(cpu_PSW_V, cpu_PSW_V, temp); + + tcg_temp_free(temp); + tcg_temp_free(temp2); + } + /* write back result */ + tcg_gen_extr_i64_i32(rl, rh, t4); + /* Calc SV bit */ + tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + /* Calc AV/SAV bits */ + tcg_gen_add_tl(cpu_PSW_AV, rh, rh); + tcg_gen_xor_tl(cpu_PSW_AV, rh, cpu_PSW_AV); + /* calc SAV */ + tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + + tcg_temp_free_i64(t1); + tcg_temp_free_i64(t2); + tcg_temp_free_i64(t3); + tcg_temp_free_i64(t4); +} + +static inline void +gen_msubs32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n, + uint32_t up_shift) +{ + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t3 = tcg_temp_new_i64(); + TCGv_i64 t4 = tcg_temp_new_i64(); + + tcg_gen_ext_i32_i64(t1, arg1); + tcg_gen_ext_i32_i64(t2, arg2); + tcg_gen_ext_i32_i64(t3, arg3); + + tcg_gen_mul_i64(t2, t2, t3); + /* if we shift part of the fraction out, we need to round up */ + tcg_gen_andi_i64(t4, t2, (1ll << (up_shift - n)) - 1); + tcg_gen_setcondi_i64(TCG_COND_NE, t4, t4, 0); + tcg_gen_sari_i64(t3, t2, up_shift - n); + tcg_gen_add_i64(t3, t3, t4); + + gen_helper_msub32_q_sub_ssov(ret, cpu_env, t1, t3); + + tcg_temp_free_i64(t1); + tcg_temp_free_i64(t2); + tcg_temp_free_i64(t3); + tcg_temp_free_i64(t4); +} + +static inline void +gen_msubs64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, + TCGv arg3, uint32_t n) +{ + TCGv_i64 r1 = tcg_temp_new_i64(); + TCGv temp = tcg_const_i32(n); + + tcg_gen_concat_i32_i64(r1, arg1_low, arg1_high); + gen_helper_msub64_q_ssov(r1, cpu_env, r1, arg2, arg3, temp); + tcg_gen_extr_i64_i32(rl, rh, r1); + + tcg_temp_free_i64(r1); + tcg_temp_free(temp); +} + +static inline void +gen_msubad_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, + TCGv r3, uint32_t n, uint32_t mode) +{ + TCGv temp = tcg_const_i32(n); + TCGv temp2 = tcg_temp_new(); + TCGv_i64 temp64 = tcg_temp_new_i64(); + switch (mode) { + case MODE_LL: + GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + break; + case MODE_LU: + GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + break; + case MODE_UL: + GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + break; + case MODE_UU: + GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + break; + } + tcg_gen_extr_i64_i32(temp, temp2, temp64); + gen_addsub64_h(ret_low, ret_high, r1_low, r1_high, temp, temp2, + tcg_gen_add_tl, tcg_gen_sub_tl); + tcg_temp_free(temp); + tcg_temp_free(temp2); + tcg_temp_free_i64(temp64); +} + +static inline void +gen_msubadm_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, + TCGv r3, uint32_t n, uint32_t mode) +{ + TCGv temp = tcg_const_i32(n); + TCGv_i64 temp64 = tcg_temp_new_i64(); + TCGv_i64 temp64_2 = tcg_temp_new_i64(); + TCGv_i64 temp64_3 = tcg_temp_new_i64(); + switch (mode) { + case MODE_LL: + GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + break; + case MODE_LU: + GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + break; + case MODE_UL: + GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + break; + case MODE_UU: + GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + break; + } + tcg_gen_concat_i32_i64(temp64_3, r1_low, r1_high); + tcg_gen_sari_i64(temp64_2, temp64, 32); /* high */ + tcg_gen_ext32s_i64(temp64, temp64); /* low */ + tcg_gen_sub_i64(temp64, temp64_2, temp64); + tcg_gen_shli_i64(temp64, temp64, 16); + + gen_sub64_d(temp64_2, temp64_3, temp64); + /* write back result */ + tcg_gen_extr_i64_i32(ret_low, ret_high, temp64_2); + + tcg_temp_free(temp); + tcg_temp_free_i64(temp64); + tcg_temp_free_i64(temp64_2); + tcg_temp_free_i64(temp64_3); +} + +static inline void +gen_msubadr32_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) +{ + TCGv temp = tcg_const_i32(n); + TCGv temp2 = tcg_temp_new(); + TCGv_i64 temp64 = tcg_temp_new_i64(); + switch (mode) { + case MODE_LL: + GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + break; + case MODE_LU: + GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + break; + case MODE_UL: + GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + break; + case MODE_UU: + GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + break; + } + tcg_gen_andi_tl(temp2, r1, 0xffff0000); + tcg_gen_shli_tl(temp, r1, 16); + gen_helper_subadr_h(ret, cpu_env, temp64, temp, temp2); + + tcg_temp_free(temp); + tcg_temp_free(temp2); + tcg_temp_free_i64(temp64); +} + +static inline void +gen_msubads_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, + TCGv r3, uint32_t n, uint32_t mode) +{ + TCGv temp = tcg_const_i32(n); + TCGv temp2 = tcg_temp_new(); + TCGv temp3 = tcg_temp_new(); + TCGv_i64 temp64 = tcg_temp_new_i64(); + + switch (mode) { + case MODE_LL: + GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + break; + case MODE_LU: + GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + break; + case MODE_UL: + GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + break; + case MODE_UU: + GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + break; + } + tcg_gen_extr_i64_i32(temp, temp2, temp64); + gen_adds(ret_low, r1_low, temp); + tcg_gen_mov_tl(temp, cpu_PSW_V); + tcg_gen_mov_tl(temp3, cpu_PSW_AV); + gen_subs(ret_high, r1_high, temp2); + /* combine v bits */ + tcg_gen_or_tl(cpu_PSW_V, cpu_PSW_V, temp); + /* combine av bits */ + tcg_gen_or_tl(cpu_PSW_AV, cpu_PSW_AV, temp3); + + tcg_temp_free(temp); + tcg_temp_free(temp2); + tcg_temp_free(temp3); + tcg_temp_free_i64(temp64); +} + +static inline void +gen_msubadms_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, + TCGv r3, uint32_t n, uint32_t mode) +{ + TCGv temp = tcg_const_i32(n); + TCGv_i64 temp64 = tcg_temp_new_i64(); + TCGv_i64 temp64_2 = tcg_temp_new_i64(); + + switch (mode) { + case MODE_LL: + GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + break; + case MODE_LU: + GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + break; + case MODE_UL: + GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + break; + case MODE_UU: + GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + break; + } + tcg_gen_sari_i64(temp64_2, temp64, 32); /* high */ + tcg_gen_ext32s_i64(temp64, temp64); /* low */ + tcg_gen_sub_i64(temp64, temp64_2, temp64); + tcg_gen_shli_i64(temp64, temp64, 16); + tcg_gen_concat_i32_i64(temp64_2, r1_low, r1_high); + + gen_helper_sub64_ssov(temp64, cpu_env, temp64_2, temp64); + tcg_gen_extr_i64_i32(ret_low, ret_high, temp64); + + tcg_temp_free(temp); + tcg_temp_free_i64(temp64); + tcg_temp_free_i64(temp64_2); +} + +static inline void +gen_msubadr32s_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) +{ + TCGv temp = tcg_const_i32(n); + TCGv temp2 = tcg_temp_new(); + TCGv_i64 temp64 = tcg_temp_new_i64(); + switch (mode) { + case MODE_LL: + GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + break; + case MODE_LU: + GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + break; + case MODE_UL: + GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + break; + case MODE_UU: + GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + break; + } + tcg_gen_andi_tl(temp2, r1, 0xffff0000); + tcg_gen_shli_tl(temp, r1, 16); + gen_helper_subadr_h_ssov(ret, cpu_env, temp64, temp, temp2); + + tcg_temp_free(temp); + tcg_temp_free(temp2); + tcg_temp_free_i64(temp64); +} + +static inline void gen_abs(TCGv ret, TCGv r1) +{ + TCGv temp = tcg_temp_new(); + TCGv t0 = tcg_const_i32(0); + + tcg_gen_neg_tl(temp, r1); + tcg_gen_movcond_tl(TCG_COND_GE, ret, r1, t0, r1, temp); + /* overflow can only happen, if r1 = 0x80000000 */ + tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_PSW_V, r1, 0x80000000); + tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); + /* calc SV bit */ + tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + /* Calc AV bit */ + tcg_gen_add_tl(cpu_PSW_AV, ret, ret); + tcg_gen_xor_tl(cpu_PSW_AV, ret, cpu_PSW_AV); + /* calc SAV bit */ + tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + + tcg_temp_free(temp); + tcg_temp_free(t0); +} + +static inline void gen_absdif(TCGv ret, TCGv r1, TCGv r2) +{ + TCGv temp = tcg_temp_new_i32(); + TCGv result = tcg_temp_new_i32(); + + tcg_gen_sub_tl(result, r1, r2); + tcg_gen_sub_tl(temp, r2, r1); + tcg_gen_movcond_tl(TCG_COND_GT, result, r1, r2, result, temp); + + /* calc V bit */ + tcg_gen_xor_tl(cpu_PSW_V, result, r1); + tcg_gen_xor_tl(temp, result, r2); + tcg_gen_movcond_tl(TCG_COND_GT, cpu_PSW_V, r1, r2, cpu_PSW_V, temp); + tcg_gen_xor_tl(temp, r1, r2); + tcg_gen_and_tl(cpu_PSW_V, cpu_PSW_V, temp); + /* calc SV bit */ + tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + /* Calc AV bit */ + tcg_gen_add_tl(cpu_PSW_AV, result, result); + tcg_gen_xor_tl(cpu_PSW_AV, result, cpu_PSW_AV); + /* calc SAV bit */ + tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + /* write back result */ + tcg_gen_mov_tl(ret, result); + + tcg_temp_free(temp); + tcg_temp_free(result); +} + +static inline void gen_absdifi(TCGv ret, TCGv r1, int32_t con) +{ + TCGv temp = tcg_const_i32(con); + gen_absdif(ret, r1, temp); + tcg_temp_free(temp); +} + +static inline void gen_absdifsi(TCGv ret, TCGv r1, int32_t con) +{ + TCGv temp = tcg_const_i32(con); + gen_helper_absdif_ssov(ret, cpu_env, r1, temp); + tcg_temp_free(temp); +} + +static inline void gen_mul_i32s(TCGv ret, TCGv r1, TCGv r2) +{ + TCGv high = tcg_temp_new(); + TCGv low = tcg_temp_new(); + + tcg_gen_muls2_tl(low, high, r1, r2); + tcg_gen_mov_tl(ret, low); + /* calc V bit */ + tcg_gen_sari_tl(low, low, 31); + tcg_gen_setcond_tl(TCG_COND_NE, cpu_PSW_V, high, low); + tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); + /* calc SV bit */ + tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + /* Calc AV bit */ + tcg_gen_add_tl(cpu_PSW_AV, ret, ret); + tcg_gen_xor_tl(cpu_PSW_AV, ret, cpu_PSW_AV); + /* calc SAV bit */ + tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + + tcg_temp_free(high); + tcg_temp_free(low); +} + +static inline void gen_muli_i32s(TCGv ret, TCGv r1, int32_t con) +{ + TCGv temp = tcg_const_i32(con); + gen_mul_i32s(ret, r1, temp); + tcg_temp_free(temp); +} + +static inline void gen_mul_i64s(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2) +{ + tcg_gen_muls2_tl(ret_low, ret_high, r1, r2); + /* clear V bit */ + tcg_gen_movi_tl(cpu_PSW_V, 0); + /* calc SV bit */ + tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + /* Calc AV bit */ + tcg_gen_add_tl(cpu_PSW_AV, ret_high, ret_high); + tcg_gen_xor_tl(cpu_PSW_AV, ret_high, cpu_PSW_AV); + /* calc SAV bit */ + tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); +} + +static inline void gen_muli_i64s(TCGv ret_low, TCGv ret_high, TCGv r1, + int32_t con) +{ + TCGv temp = tcg_const_i32(con); + gen_mul_i64s(ret_low, ret_high, r1, temp); + tcg_temp_free(temp); +} + +static inline void gen_mul_i64u(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2) +{ + tcg_gen_mulu2_tl(ret_low, ret_high, r1, r2); + /* clear V bit */ + tcg_gen_movi_tl(cpu_PSW_V, 0); + /* calc SV bit */ + tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + /* Calc AV bit */ + tcg_gen_add_tl(cpu_PSW_AV, ret_high, ret_high); + tcg_gen_xor_tl(cpu_PSW_AV, ret_high, cpu_PSW_AV); + /* calc SAV bit */ + tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); +} + +static inline void gen_muli_i64u(TCGv ret_low, TCGv ret_high, TCGv r1, + int32_t con) +{ + TCGv temp = tcg_const_i32(con); + gen_mul_i64u(ret_low, ret_high, r1, temp); + tcg_temp_free(temp); +} + +static inline void gen_mulsi_i32(TCGv ret, TCGv r1, int32_t con) +{ + TCGv temp = tcg_const_i32(con); + gen_helper_mul_ssov(ret, cpu_env, r1, temp); + tcg_temp_free(temp); +} + +static inline void gen_mulsui_i32(TCGv ret, TCGv r1, int32_t con) +{ + TCGv temp = tcg_const_i32(con); + gen_helper_mul_suov(ret, cpu_env, r1, temp); + tcg_temp_free(temp); +} +/* gen_maddsi_32(cpu_gpr_d[r4], cpu_gpr_d[r1], cpu_gpr_d[r3], const9); */ +static inline void gen_maddsi_32(TCGv ret, TCGv r1, TCGv r2, int32_t con) +{ + TCGv temp = tcg_const_i32(con); + gen_helper_madd32_ssov(ret, cpu_env, r1, r2, temp); + tcg_temp_free(temp); +} + +static inline void gen_maddsui_32(TCGv ret, TCGv r1, TCGv r2, int32_t con) +{ + TCGv temp = tcg_const_i32(con); + gen_helper_madd32_suov(ret, cpu_env, r1, r2, temp); + tcg_temp_free(temp); +} + +static void +gen_mul_q(TCGv rl, TCGv rh, TCGv arg1, TCGv arg2, uint32_t n, uint32_t up_shift) +{ + TCGv temp = tcg_temp_new(); + TCGv_i64 temp_64 = tcg_temp_new_i64(); + TCGv_i64 temp2_64 = tcg_temp_new_i64(); + + if (n == 0) { + if (up_shift == 32) { + tcg_gen_muls2_tl(rh, rl, arg1, arg2); + } else if (up_shift == 16) { + tcg_gen_ext_i32_i64(temp_64, arg1); + tcg_gen_ext_i32_i64(temp2_64, arg2); + + tcg_gen_mul_i64(temp_64, temp_64, temp2_64); + tcg_gen_shri_i64(temp_64, temp_64, up_shift); + tcg_gen_extr_i64_i32(rl, rh, temp_64); + } else { + tcg_gen_muls2_tl(rl, rh, arg1, arg2); + } + /* reset v bit */ + tcg_gen_movi_tl(cpu_PSW_V, 0); + } else { /* n is expected to be 1 */ + tcg_gen_ext_i32_i64(temp_64, arg1); + tcg_gen_ext_i32_i64(temp2_64, arg2); + + tcg_gen_mul_i64(temp_64, temp_64, temp2_64); + + if (up_shift == 0) { + tcg_gen_shli_i64(temp_64, temp_64, 1); + } else { + tcg_gen_shri_i64(temp_64, temp_64, up_shift - 1); + } + tcg_gen_extr_i64_i32(rl, rh, temp_64); + /* overflow only occurs if r1 = r2 = 0x8000 */ + if (up_shift == 0) {/* result is 64 bit */ + tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_PSW_V, rh, + 0x80000000); + } else { /* result is 32 bit */ + tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_PSW_V, rl, + 0x80000000); + } + tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); + /* calc sv overflow bit */ + tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + } + /* calc av overflow bit */ + if (up_shift == 0) { + tcg_gen_add_tl(cpu_PSW_AV, rh, rh); + tcg_gen_xor_tl(cpu_PSW_AV, rh, cpu_PSW_AV); + } else { + tcg_gen_add_tl(cpu_PSW_AV, rl, rl); + tcg_gen_xor_tl(cpu_PSW_AV, rl, cpu_PSW_AV); + } + /* calc sav overflow bit */ + tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + tcg_temp_free(temp); + tcg_temp_free_i64(temp_64); + tcg_temp_free_i64(temp2_64); +} + +static void +gen_mul_q_16(TCGv ret, TCGv arg1, TCGv arg2, uint32_t n) +{ + TCGv temp = tcg_temp_new(); + if (n == 0) { + tcg_gen_mul_tl(ret, arg1, arg2); + } else { /* n is expected to be 1 */ + tcg_gen_mul_tl(ret, arg1, arg2); + tcg_gen_shli_tl(ret, ret, 1); + /* catch special case r1 = r2 = 0x8000 */ + tcg_gen_setcondi_tl(TCG_COND_EQ, temp, ret, 0x80000000); + tcg_gen_sub_tl(ret, ret, temp); + } + /* reset v bit */ + tcg_gen_movi_tl(cpu_PSW_V, 0); + /* calc av overflow bit */ + tcg_gen_add_tl(cpu_PSW_AV, ret, ret); + tcg_gen_xor_tl(cpu_PSW_AV, ret, cpu_PSW_AV); + /* calc sav overflow bit */ + tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + + tcg_temp_free(temp); +} + +static void gen_mulr_q(TCGv ret, TCGv arg1, TCGv arg2, uint32_t n) +{ + TCGv temp = tcg_temp_new(); + if (n == 0) { + tcg_gen_mul_tl(ret, arg1, arg2); + tcg_gen_addi_tl(ret, ret, 0x8000); + } else { + tcg_gen_mul_tl(ret, arg1, arg2); + tcg_gen_shli_tl(ret, ret, 1); + tcg_gen_addi_tl(ret, ret, 0x8000); + /* catch special case r1 = r2 = 0x8000 */ + tcg_gen_setcondi_tl(TCG_COND_EQ, temp, ret, 0x80008000); + tcg_gen_muli_tl(temp, temp, 0x8001); + tcg_gen_sub_tl(ret, ret, temp); + } + /* reset v bit */ + tcg_gen_movi_tl(cpu_PSW_V, 0); + /* calc av overflow bit */ + tcg_gen_add_tl(cpu_PSW_AV, ret, ret); + tcg_gen_xor_tl(cpu_PSW_AV, ret, cpu_PSW_AV); + /* calc sav overflow bit */ + tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + /* cut halfword off */ + tcg_gen_andi_tl(ret, ret, 0xffff0000); + + tcg_temp_free(temp); +} + +static inline void +gen_madds_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, + TCGv r3) +{ + TCGv_i64 temp64 = tcg_temp_new_i64(); + tcg_gen_concat_i32_i64(temp64, r2_low, r2_high); + gen_helper_madd64_ssov(temp64, cpu_env, r1, temp64, r3); + tcg_gen_extr_i64_i32(ret_low, ret_high, temp64); + tcg_temp_free_i64(temp64); +} + +static inline void +gen_maddsi_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, + int32_t con) +{ + TCGv temp = tcg_const_i32(con); + gen_madds_64(ret_low, ret_high, r1, r2_low, r2_high, temp); + tcg_temp_free(temp); +} + +static inline void +gen_maddsu_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, + TCGv r3) +{ + TCGv_i64 temp64 = tcg_temp_new_i64(); + tcg_gen_concat_i32_i64(temp64, r2_low, r2_high); + gen_helper_madd64_suov(temp64, cpu_env, r1, temp64, r3); + tcg_gen_extr_i64_i32(ret_low, ret_high, temp64); + tcg_temp_free_i64(temp64); +} + +static inline void +gen_maddsui_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, + int32_t con) +{ + TCGv temp = tcg_const_i32(con); + gen_maddsu_64(ret_low, ret_high, r1, r2_low, r2_high, temp); + tcg_temp_free(temp); +} + +static inline void gen_msubsi_32(TCGv ret, TCGv r1, TCGv r2, int32_t con) +{ + TCGv temp = tcg_const_i32(con); + gen_helper_msub32_ssov(ret, cpu_env, r1, r2, temp); + tcg_temp_free(temp); +} + +static inline void gen_msubsui_32(TCGv ret, TCGv r1, TCGv r2, int32_t con) +{ + TCGv temp = tcg_const_i32(con); + gen_helper_msub32_suov(ret, cpu_env, r1, r2, temp); + tcg_temp_free(temp); +} + +static inline void +gen_msubs_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, + TCGv r3) +{ + TCGv_i64 temp64 = tcg_temp_new_i64(); + tcg_gen_concat_i32_i64(temp64, r2_low, r2_high); + gen_helper_msub64_ssov(temp64, cpu_env, r1, temp64, r3); + tcg_gen_extr_i64_i32(ret_low, ret_high, temp64); + tcg_temp_free_i64(temp64); +} + +static inline void +gen_msubsi_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, + int32_t con) +{ + TCGv temp = tcg_const_i32(con); + gen_msubs_64(ret_low, ret_high, r1, r2_low, r2_high, temp); + tcg_temp_free(temp); +} + +static inline void +gen_msubsu_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, + TCGv r3) +{ + TCGv_i64 temp64 = tcg_temp_new_i64(); + tcg_gen_concat_i32_i64(temp64, r2_low, r2_high); + gen_helper_msub64_suov(temp64, cpu_env, r1, temp64, r3); + tcg_gen_extr_i64_i32(ret_low, ret_high, temp64); + tcg_temp_free_i64(temp64); +} + +static inline void +gen_msubsui_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, + int32_t con) +{ + TCGv temp = tcg_const_i32(con); + gen_msubsu_64(ret_low, ret_high, r1, r2_low, r2_high, temp); + tcg_temp_free(temp); +} + +static void gen_saturate(TCGv ret, TCGv arg, int32_t up, int32_t low) +{ + TCGv sat_neg = tcg_const_i32(low); + TCGv temp = tcg_const_i32(up); + + /* sat_neg = (arg < low ) ? low : arg; */ + tcg_gen_movcond_tl(TCG_COND_LT, sat_neg, arg, sat_neg, sat_neg, arg); + + /* ret = (sat_neg > up ) ? up : sat_neg; */ + tcg_gen_movcond_tl(TCG_COND_GT, ret, sat_neg, temp, temp, sat_neg); + + tcg_temp_free(sat_neg); + tcg_temp_free(temp); +} + +static void gen_saturate_u(TCGv ret, TCGv arg, int32_t up) +{ + TCGv temp = tcg_const_i32(up); + /* sat_neg = (arg > up ) ? up : arg; */ + tcg_gen_movcond_tl(TCG_COND_GTU, ret, arg, temp, temp, arg); + tcg_temp_free(temp); +} + +static void gen_shi(TCGv ret, TCGv r1, int32_t shift_count) +{ + if (shift_count == -32) { + tcg_gen_movi_tl(ret, 0); + } else if (shift_count >= 0) { + tcg_gen_shli_tl(ret, r1, shift_count); + } else { + tcg_gen_shri_tl(ret, r1, -shift_count); + } +} + +static void gen_sh_hi(TCGv ret, TCGv r1, int32_t shiftcount) +{ + TCGv temp_low, temp_high; + + if (shiftcount == -16) { + tcg_gen_movi_tl(ret, 0); + } else { + temp_high = tcg_temp_new(); + temp_low = tcg_temp_new(); + + tcg_gen_andi_tl(temp_low, r1, 0xffff); + tcg_gen_andi_tl(temp_high, r1, 0xffff0000); + gen_shi(temp_low, temp_low, shiftcount); + gen_shi(ret, temp_high, shiftcount); + tcg_gen_deposit_tl(ret, ret, temp_low, 0, 16); + + tcg_temp_free(temp_low); + tcg_temp_free(temp_high); + } +} + +static void gen_shaci(TCGv ret, TCGv r1, int32_t shift_count) +{ + uint32_t msk, msk_start; + TCGv temp = tcg_temp_new(); + TCGv temp2 = tcg_temp_new(); + TCGv t_0 = tcg_const_i32(0); + + if (shift_count == 0) { + /* Clear PSW.C and PSW.V */ + tcg_gen_movi_tl(cpu_PSW_C, 0); + tcg_gen_mov_tl(cpu_PSW_V, cpu_PSW_C); + tcg_gen_mov_tl(ret, r1); + } else if (shift_count == -32) { + /* set PSW.C */ + tcg_gen_mov_tl(cpu_PSW_C, r1); + /* fill ret completly with sign bit */ + tcg_gen_sari_tl(ret, r1, 31); + /* clear PSW.V */ + tcg_gen_movi_tl(cpu_PSW_V, 0); + } else if (shift_count > 0) { + TCGv t_max = tcg_const_i32(0x7FFFFFFF >> shift_count); + TCGv t_min = tcg_const_i32(((int32_t) -0x80000000) >> shift_count); + + /* calc carry */ + msk_start = 32 - shift_count; + msk = ((1 << shift_count) - 1) << msk_start; + tcg_gen_andi_tl(cpu_PSW_C, r1, msk); + /* calc v/sv bits */ + tcg_gen_setcond_tl(TCG_COND_GT, temp, r1, t_max); + tcg_gen_setcond_tl(TCG_COND_LT, temp2, r1, t_min); + tcg_gen_or_tl(cpu_PSW_V, temp, temp2); + tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); + /* calc sv */ + tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_V, cpu_PSW_SV); + /* do shift */ + tcg_gen_shli_tl(ret, r1, shift_count); + + tcg_temp_free(t_max); + tcg_temp_free(t_min); + } else { + /* clear PSW.V */ + tcg_gen_movi_tl(cpu_PSW_V, 0); + /* calc carry */ + msk = (1 << -shift_count) - 1; + tcg_gen_andi_tl(cpu_PSW_C, r1, msk); + /* do shift */ + tcg_gen_sari_tl(ret, r1, -shift_count); + } + /* calc av overflow bit */ + tcg_gen_add_tl(cpu_PSW_AV, ret, ret); + tcg_gen_xor_tl(cpu_PSW_AV, ret, cpu_PSW_AV); + /* calc sav overflow bit */ + tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + + tcg_temp_free(temp); + tcg_temp_free(temp2); + tcg_temp_free(t_0); +} + +static void gen_shas(TCGv ret, TCGv r1, TCGv r2) +{ + gen_helper_sha_ssov(ret, cpu_env, r1, r2); +} + +static void gen_shasi(TCGv ret, TCGv r1, int32_t con) +{ + TCGv temp = tcg_const_i32(con); + gen_shas(ret, r1, temp); + tcg_temp_free(temp); +} + +static void gen_sha_hi(TCGv ret, TCGv r1, int32_t shift_count) +{ + TCGv low, high; + + if (shift_count == 0) { + tcg_gen_mov_tl(ret, r1); + } else if (shift_count > 0) { + low = tcg_temp_new(); + high = tcg_temp_new(); + + tcg_gen_andi_tl(high, r1, 0xffff0000); + tcg_gen_shli_tl(low, r1, shift_count); + tcg_gen_shli_tl(ret, high, shift_count); + tcg_gen_deposit_tl(ret, ret, low, 0, 16); + + tcg_temp_free(low); + tcg_temp_free(high); + } else { + low = tcg_temp_new(); + high = tcg_temp_new(); + + tcg_gen_ext16s_tl(low, r1); + tcg_gen_sari_tl(low, low, -shift_count); + tcg_gen_sari_tl(ret, r1, -shift_count); + tcg_gen_deposit_tl(ret, ret, low, 0, 16); + + tcg_temp_free(low); + tcg_temp_free(high); + } + +} + +/* ret = {ret[30:0], (r1 cond r2)}; */ +static void gen_sh_cond(int cond, TCGv ret, TCGv r1, TCGv r2) +{ + TCGv temp = tcg_temp_new(); + TCGv temp2 = tcg_temp_new(); + + tcg_gen_shli_tl(temp, ret, 1); + tcg_gen_setcond_tl(cond, temp2, r1, r2); + tcg_gen_or_tl(ret, temp, temp2); + + tcg_temp_free(temp); + tcg_temp_free(temp2); +} + +static void gen_sh_condi(int cond, TCGv ret, TCGv r1, int32_t con) +{ + TCGv temp = tcg_const_i32(con); + gen_sh_cond(cond, ret, r1, temp); + tcg_temp_free(temp); +} + +static inline void gen_adds(TCGv ret, TCGv r1, TCGv r2) +{ + gen_helper_add_ssov(ret, cpu_env, r1, r2); +} + +static inline void gen_addsi(TCGv ret, TCGv r1, int32_t con) +{ + TCGv temp = tcg_const_i32(con); + gen_helper_add_ssov(ret, cpu_env, r1, temp); + tcg_temp_free(temp); +} + +static inline void gen_addsui(TCGv ret, TCGv r1, int32_t con) +{ + TCGv temp = tcg_const_i32(con); + gen_helper_add_suov(ret, cpu_env, r1, temp); + tcg_temp_free(temp); +} + +static inline void gen_subs(TCGv ret, TCGv r1, TCGv r2) +{ + gen_helper_sub_ssov(ret, cpu_env, r1, r2); +} + +static inline void gen_subsu(TCGv ret, TCGv r1, TCGv r2) +{ + gen_helper_sub_suov(ret, cpu_env, r1, r2); +} + +static inline void gen_bit_2op(TCGv ret, TCGv r1, TCGv r2, + int pos1, int pos2, + void(*op1)(TCGv, TCGv, TCGv), + void(*op2)(TCGv, TCGv, TCGv)) +{ + TCGv temp1, temp2; + + temp1 = tcg_temp_new(); + temp2 = tcg_temp_new(); + + tcg_gen_shri_tl(temp2, r2, pos2); + tcg_gen_shri_tl(temp1, r1, pos1); + + (*op1)(temp1, temp1, temp2); + (*op2)(temp1 , ret, temp1); + + tcg_gen_deposit_tl(ret, ret, temp1, 0, 1); + + tcg_temp_free(temp1); + tcg_temp_free(temp2); +} + +/* ret = r1[pos1] op1 r2[pos2]; */ +static inline void gen_bit_1op(TCGv ret, TCGv r1, TCGv r2, + int pos1, int pos2, + void(*op1)(TCGv, TCGv, TCGv)) +{ + TCGv temp1, temp2; + + temp1 = tcg_temp_new(); + temp2 = tcg_temp_new(); + + tcg_gen_shri_tl(temp2, r2, pos2); + tcg_gen_shri_tl(temp1, r1, pos1); + + (*op1)(ret, temp1, temp2); + + tcg_gen_andi_tl(ret, ret, 0x1); + + tcg_temp_free(temp1); + tcg_temp_free(temp2); +} + +static inline void gen_accumulating_cond(int cond, TCGv ret, TCGv r1, TCGv r2, + void(*op)(TCGv, TCGv, TCGv)) +{ + TCGv temp = tcg_temp_new(); + TCGv temp2 = tcg_temp_new(); + /* temp = (arg1 cond arg2 )*/ + tcg_gen_setcond_tl(cond, temp, r1, r2); + /* temp2 = ret[0]*/ + tcg_gen_andi_tl(temp2, ret, 0x1); + /* temp = temp insn temp2 */ + (*op)(temp, temp, temp2); + /* ret = {ret[31:1], temp} */ + tcg_gen_deposit_tl(ret, ret, temp, 0, 1); + + tcg_temp_free(temp); + tcg_temp_free(temp2); +} + +static inline void +gen_accumulating_condi(int cond, TCGv ret, TCGv r1, int32_t con, + void(*op)(TCGv, TCGv, TCGv)) +{ + TCGv temp = tcg_const_i32(con); + gen_accumulating_cond(cond, ret, r1, temp, op); + tcg_temp_free(temp); +} + +/* ret = (r1 cond r2) ? 0xFFFFFFFF ? 0x00000000;*/ +static inline void gen_cond_w(TCGCond cond, TCGv ret, TCGv r1, TCGv r2) +{ + tcg_gen_setcond_tl(cond, ret, r1, r2); + tcg_gen_neg_tl(ret, ret); +} + +static inline void gen_eqany_bi(TCGv ret, TCGv r1, int32_t con) +{ + TCGv b0 = tcg_temp_new(); + TCGv b1 = tcg_temp_new(); + TCGv b2 = tcg_temp_new(); + TCGv b3 = tcg_temp_new(); + + /* byte 0 */ + tcg_gen_andi_tl(b0, r1, 0xff); + tcg_gen_setcondi_tl(TCG_COND_EQ, b0, b0, con & 0xff); + + /* byte 1 */ + tcg_gen_andi_tl(b1, r1, 0xff00); + tcg_gen_setcondi_tl(TCG_COND_EQ, b1, b1, con & 0xff00); + + /* byte 2 */ + tcg_gen_andi_tl(b2, r1, 0xff0000); + tcg_gen_setcondi_tl(TCG_COND_EQ, b2, b2, con & 0xff0000); + + /* byte 3 */ + tcg_gen_andi_tl(b3, r1, 0xff000000); + tcg_gen_setcondi_tl(TCG_COND_EQ, b3, b3, con & 0xff000000); + + /* combine them */ + tcg_gen_or_tl(ret, b0, b1); + tcg_gen_or_tl(ret, ret, b2); + tcg_gen_or_tl(ret, ret, b3); + + tcg_temp_free(b0); + tcg_temp_free(b1); + tcg_temp_free(b2); + tcg_temp_free(b3); +} + +static inline void gen_eqany_hi(TCGv ret, TCGv r1, int32_t con) +{ + TCGv h0 = tcg_temp_new(); + TCGv h1 = tcg_temp_new(); + + /* halfword 0 */ + tcg_gen_andi_tl(h0, r1, 0xffff); + tcg_gen_setcondi_tl(TCG_COND_EQ, h0, h0, con & 0xffff); + + /* halfword 1 */ + tcg_gen_andi_tl(h1, r1, 0xffff0000); + tcg_gen_setcondi_tl(TCG_COND_EQ, h1, h1, con & 0xffff0000); + + /* combine them */ + tcg_gen_or_tl(ret, h0, h1); + + tcg_temp_free(h0); + tcg_temp_free(h1); +} +/* mask = ((1 << width) -1) << pos; + ret = (r1 & ~mask) | (r2 << pos) & mask); */ +static inline void gen_insert(TCGv ret, TCGv r1, TCGv r2, TCGv width, TCGv pos) +{ + TCGv mask = tcg_temp_new(); + TCGv temp = tcg_temp_new(); + TCGv temp2 = tcg_temp_new(); + + tcg_gen_movi_tl(mask, 1); + tcg_gen_shl_tl(mask, mask, width); + tcg_gen_subi_tl(mask, mask, 1); + tcg_gen_shl_tl(mask, mask, pos); + + tcg_gen_shl_tl(temp, r2, pos); + tcg_gen_and_tl(temp, temp, mask); + tcg_gen_andc_tl(temp2, r1, mask); + tcg_gen_or_tl(ret, temp, temp2); + + tcg_temp_free(mask); + tcg_temp_free(temp); + tcg_temp_free(temp2); +} + +static inline void gen_bsplit(TCGv rl, TCGv rh, TCGv r1) +{ + TCGv_i64 temp = tcg_temp_new_i64(); + + gen_helper_bsplit(temp, r1); + tcg_gen_extr_i64_i32(rl, rh, temp); + + tcg_temp_free_i64(temp); +} + +static inline void gen_unpack(TCGv rl, TCGv rh, TCGv r1) +{ + TCGv_i64 temp = tcg_temp_new_i64(); + + gen_helper_unpack(temp, r1); + tcg_gen_extr_i64_i32(rl, rh, temp); + + tcg_temp_free_i64(temp); +} + +static inline void +gen_dvinit_b(CPUTriCoreState *env, TCGv rl, TCGv rh, TCGv r1, TCGv r2) +{ + TCGv_i64 ret = tcg_temp_new_i64(); + + if (!tricore_feature(env, TRICORE_FEATURE_131)) { + gen_helper_dvinit_b_13(ret, cpu_env, r1, r2); + } else { + gen_helper_dvinit_b_131(ret, cpu_env, r1, r2); + } + tcg_gen_extr_i64_i32(rl, rh, ret); + + tcg_temp_free_i64(ret); +} + +static inline void +gen_dvinit_h(CPUTriCoreState *env, TCGv rl, TCGv rh, TCGv r1, TCGv r2) +{ + TCGv_i64 ret = tcg_temp_new_i64(); + + if (!tricore_feature(env, TRICORE_FEATURE_131)) { + gen_helper_dvinit_h_13(ret, cpu_env, r1, r2); + } else { + gen_helper_dvinit_h_131(ret, cpu_env, r1, r2); + } + tcg_gen_extr_i64_i32(rl, rh, ret); + + tcg_temp_free_i64(ret); +} + +static void gen_calc_usb_mul_h(TCGv arg_low, TCGv arg_high) +{ + TCGv temp = tcg_temp_new(); + /* calc AV bit */ + tcg_gen_add_tl(temp, arg_low, arg_low); + tcg_gen_xor_tl(temp, temp, arg_low); + tcg_gen_add_tl(cpu_PSW_AV, arg_high, arg_high); + tcg_gen_xor_tl(cpu_PSW_AV, cpu_PSW_AV, arg_high); + tcg_gen_or_tl(cpu_PSW_AV, cpu_PSW_AV, temp); + /* calc SAV bit */ + tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + tcg_gen_movi_tl(cpu_PSW_V, 0); + tcg_temp_free(temp); +} + +static void gen_calc_usb_mulr_h(TCGv arg) +{ + TCGv temp = tcg_temp_new(); + /* calc AV bit */ + tcg_gen_add_tl(temp, arg, arg); + tcg_gen_xor_tl(temp, temp, arg); + tcg_gen_shli_tl(cpu_PSW_AV, temp, 16); + tcg_gen_or_tl(cpu_PSW_AV, cpu_PSW_AV, temp); + /* calc SAV bit */ + tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + /* clear V bit */ + tcg_gen_movi_tl(cpu_PSW_V, 0); + tcg_temp_free(temp); +} + +/* helpers for generating program flow micro-ops */ + +static inline void gen_save_pc(target_ulong pc) +{ + tcg_gen_movi_tl(cpu_PC, pc); +} + +static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) +{ + TranslationBlock *tb; + tb = ctx->tb; + if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK) && + likely(!ctx->singlestep_enabled)) { + tcg_gen_goto_tb(n); + gen_save_pc(dest); + tcg_gen_exit_tb((uintptr_t)tb + n); + } else { + gen_save_pc(dest); + if (ctx->singlestep_enabled) { + /* raise exception debug */ + } + tcg_gen_exit_tb(0); + } +} + +static inline void gen_branch_cond(DisasContext *ctx, TCGCond cond, TCGv r1, + TCGv r2, int16_t address) +{ + TCGLabel *jumpLabel = gen_new_label(); + tcg_gen_brcond_tl(cond, r1, r2, jumpLabel); + + gen_goto_tb(ctx, 1, ctx->next_pc); + + gen_set_label(jumpLabel); + gen_goto_tb(ctx, 0, ctx->pc + address * 2); +} + +static inline void gen_branch_condi(DisasContext *ctx, TCGCond cond, TCGv r1, + int r2, int16_t address) +{ + TCGv temp = tcg_const_i32(r2); + gen_branch_cond(ctx, cond, r1, temp, address); + tcg_temp_free(temp); +} + +static void gen_loop(DisasContext *ctx, int r1, int32_t offset) +{ + TCGLabel *l1 = gen_new_label(); + + tcg_gen_subi_tl(cpu_gpr_a[r1], cpu_gpr_a[r1], 1); + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_gpr_a[r1], -1, l1); + gen_goto_tb(ctx, 1, ctx->pc + offset); + gen_set_label(l1); + gen_goto_tb(ctx, 0, ctx->next_pc); +} + +static void gen_compute_branch(DisasContext *ctx, uint32_t opc, int r1, + int r2 , int32_t constant , int32_t offset) +{ + TCGv temp, temp2; + int n; + + switch (opc) { +/* SB-format jumps */ + case OPC1_16_SB_J: + case OPC1_32_B_J: + gen_goto_tb(ctx, 0, ctx->pc + offset * 2); + break; + case OPC1_32_B_CALL: + case OPC1_16_SB_CALL: + gen_helper_1arg(call, ctx->next_pc); + gen_goto_tb(ctx, 0, ctx->pc + offset * 2); + break; + case OPC1_16_SB_JZ: + gen_branch_condi(ctx, TCG_COND_EQ, cpu_gpr_d[15], 0, offset); + break; + case OPC1_16_SB_JNZ: + gen_branch_condi(ctx, TCG_COND_NE, cpu_gpr_d[15], 0, offset); + break; +/* SBC-format jumps */ + case OPC1_16_SBC_JEQ: + gen_branch_condi(ctx, TCG_COND_EQ, cpu_gpr_d[15], constant, offset); + break; + case OPC1_16_SBC_JNE: + gen_branch_condi(ctx, TCG_COND_NE, cpu_gpr_d[15], constant, offset); + break; +/* SBRN-format jumps */ + case OPC1_16_SBRN_JZ_T: + temp = tcg_temp_new(); + tcg_gen_andi_tl(temp, cpu_gpr_d[15], 0x1u << constant); + gen_branch_condi(ctx, TCG_COND_EQ, temp, 0, offset); + tcg_temp_free(temp); + break; + case OPC1_16_SBRN_JNZ_T: + temp = tcg_temp_new(); + tcg_gen_andi_tl(temp, cpu_gpr_d[15], 0x1u << constant); + gen_branch_condi(ctx, TCG_COND_NE, temp, 0, offset); + tcg_temp_free(temp); + break; +/* SBR-format jumps */ + case OPC1_16_SBR_JEQ: + gen_branch_cond(ctx, TCG_COND_EQ, cpu_gpr_d[r1], cpu_gpr_d[15], + offset); + break; + case OPC1_16_SBR_JNE: + gen_branch_cond(ctx, TCG_COND_NE, cpu_gpr_d[r1], cpu_gpr_d[15], + offset); + break; + case OPC1_16_SBR_JNZ: + gen_branch_condi(ctx, TCG_COND_NE, cpu_gpr_d[r1], 0, offset); + break; + case OPC1_16_SBR_JNZ_A: + gen_branch_condi(ctx, TCG_COND_NE, cpu_gpr_a[r1], 0, offset); + break; + case OPC1_16_SBR_JGEZ: + gen_branch_condi(ctx, TCG_COND_GE, cpu_gpr_d[r1], 0, offset); + break; + case OPC1_16_SBR_JGTZ: + gen_branch_condi(ctx, TCG_COND_GT, cpu_gpr_d[r1], 0, offset); + break; + case OPC1_16_SBR_JLEZ: + gen_branch_condi(ctx, TCG_COND_LE, cpu_gpr_d[r1], 0, offset); + break; + case OPC1_16_SBR_JLTZ: + gen_branch_condi(ctx, TCG_COND_LT, cpu_gpr_d[r1], 0, offset); + break; + case OPC1_16_SBR_JZ: + gen_branch_condi(ctx, TCG_COND_EQ, cpu_gpr_d[r1], 0, offset); + break; + case OPC1_16_SBR_JZ_A: + gen_branch_condi(ctx, TCG_COND_EQ, cpu_gpr_a[r1], 0, offset); + break; + case OPC1_16_SBR_LOOP: + gen_loop(ctx, r1, offset * 2 - 32); + break; +/* SR-format jumps */ + case OPC1_16_SR_JI: + tcg_gen_andi_tl(cpu_PC, cpu_gpr_a[r1], 0xfffffffe); + tcg_gen_exit_tb(0); + break; + case OPC2_32_SYS_RET: + case OPC2_16_SR_RET: + gen_helper_ret(cpu_env); + tcg_gen_exit_tb(0); + break; +/* B-format */ + case OPC1_32_B_CALLA: + gen_helper_1arg(call, ctx->next_pc); + gen_goto_tb(ctx, 0, EA_B_ABSOLUT(offset)); + break; + case OPC1_32_B_JLA: + tcg_gen_movi_tl(cpu_gpr_a[11], ctx->next_pc); + /* fall through */ + case OPC1_32_B_JA: + gen_goto_tb(ctx, 0, EA_B_ABSOLUT(offset)); + break; + case OPC1_32_B_JL: + tcg_gen_movi_tl(cpu_gpr_a[11], ctx->next_pc); + gen_goto_tb(ctx, 0, ctx->pc + offset * 2); + break; +/* BOL format */ + case OPCM_32_BRC_EQ_NEQ: + if (MASK_OP_BRC_OP2(ctx->opcode) == OPC2_32_BRC_JEQ) { + gen_branch_condi(ctx, TCG_COND_EQ, cpu_gpr_d[r1], constant, offset); + } else { + gen_branch_condi(ctx, TCG_COND_NE, cpu_gpr_d[r1], constant, offset); + } + break; + case OPCM_32_BRC_GE: + if (MASK_OP_BRC_OP2(ctx->opcode) == OP2_32_BRC_JGE) { + gen_branch_condi(ctx, TCG_COND_GE, cpu_gpr_d[r1], constant, offset); + } else { + constant = MASK_OP_BRC_CONST4(ctx->opcode); + gen_branch_condi(ctx, TCG_COND_GEU, cpu_gpr_d[r1], constant, + offset); + } + break; + case OPCM_32_BRC_JLT: + if (MASK_OP_BRC_OP2(ctx->opcode) == OPC2_32_BRC_JLT) { + gen_branch_condi(ctx, TCG_COND_LT, cpu_gpr_d[r1], constant, offset); + } else { + constant = MASK_OP_BRC_CONST4(ctx->opcode); + gen_branch_condi(ctx, TCG_COND_LTU, cpu_gpr_d[r1], constant, + offset); + } + break; + case OPCM_32_BRC_JNE: + temp = tcg_temp_new(); + if (MASK_OP_BRC_OP2(ctx->opcode) == OPC2_32_BRC_JNED) { + tcg_gen_mov_tl(temp, cpu_gpr_d[r1]); + /* subi is unconditional */ + tcg_gen_subi_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], 1); + gen_branch_condi(ctx, TCG_COND_NE, temp, constant, offset); + } else { + tcg_gen_mov_tl(temp, cpu_gpr_d[r1]); + /* addi is unconditional */ + tcg_gen_addi_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], 1); + gen_branch_condi(ctx, TCG_COND_NE, temp, constant, offset); + } + tcg_temp_free(temp); + break; +/* BRN format */ + case OPCM_32_BRN_JTT: + n = MASK_OP_BRN_N(ctx->opcode); + + temp = tcg_temp_new(); + tcg_gen_andi_tl(temp, cpu_gpr_d[r1], (1 << n)); + + if (MASK_OP_BRN_OP2(ctx->opcode) == OPC2_32_BRN_JNZ_T) { + gen_branch_condi(ctx, TCG_COND_NE, temp, 0, offset); + } else { + gen_branch_condi(ctx, TCG_COND_EQ, temp, 0, offset); + } + tcg_temp_free(temp); + break; +/* BRR Format */ + case OPCM_32_BRR_EQ_NEQ: + if (MASK_OP_BRR_OP2(ctx->opcode) == OPC2_32_BRR_JEQ) { + gen_branch_cond(ctx, TCG_COND_EQ, cpu_gpr_d[r1], cpu_gpr_d[r2], + offset); + } else { + gen_branch_cond(ctx, TCG_COND_NE, cpu_gpr_d[r1], cpu_gpr_d[r2], + offset); + } + break; + case OPCM_32_BRR_ADDR_EQ_NEQ: + if (MASK_OP_BRR_OP2(ctx->opcode) == OPC2_32_BRR_JEQ_A) { + gen_branch_cond(ctx, TCG_COND_EQ, cpu_gpr_a[r1], cpu_gpr_a[r2], + offset); + } else { + gen_branch_cond(ctx, TCG_COND_NE, cpu_gpr_a[r1], cpu_gpr_a[r2], + offset); + } + break; + case OPCM_32_BRR_GE: + if (MASK_OP_BRR_OP2(ctx->opcode) == OPC2_32_BRR_JGE) { + gen_branch_cond(ctx, TCG_COND_GE, cpu_gpr_d[r1], cpu_gpr_d[r2], + offset); + } else { + gen_branch_cond(ctx, TCG_COND_GEU, cpu_gpr_d[r1], cpu_gpr_d[r2], + offset); + } + break; + case OPCM_32_BRR_JLT: + if (MASK_OP_BRR_OP2(ctx->opcode) == OPC2_32_BRR_JLT) { + gen_branch_cond(ctx, TCG_COND_LT, cpu_gpr_d[r1], cpu_gpr_d[r2], + offset); + } else { + gen_branch_cond(ctx, TCG_COND_LTU, cpu_gpr_d[r1], cpu_gpr_d[r2], + offset); + } + break; + case OPCM_32_BRR_LOOP: + if (MASK_OP_BRR_OP2(ctx->opcode) == OPC2_32_BRR_LOOP) { + gen_loop(ctx, r1, offset * 2); + } else { + /* OPC2_32_BRR_LOOPU */ + gen_goto_tb(ctx, 0, ctx->pc + offset * 2); + } + break; + case OPCM_32_BRR_JNE: + temp = tcg_temp_new(); + temp2 = tcg_temp_new(); + if (MASK_OP_BRC_OP2(ctx->opcode) == OPC2_32_BRR_JNED) { + tcg_gen_mov_tl(temp, cpu_gpr_d[r1]); + /* also save r2, in case of r1 == r2, so r2 is not decremented */ + tcg_gen_mov_tl(temp2, cpu_gpr_d[r2]); + /* subi is unconditional */ + tcg_gen_subi_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], 1); + gen_branch_cond(ctx, TCG_COND_NE, temp, temp2, offset); + } else { + tcg_gen_mov_tl(temp, cpu_gpr_d[r1]); + /* also save r2, in case of r1 == r2, so r2 is not decremented */ + tcg_gen_mov_tl(temp2, cpu_gpr_d[r2]); + /* addi is unconditional */ + tcg_gen_addi_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], 1); + gen_branch_cond(ctx, TCG_COND_NE, temp, temp2, offset); + } + tcg_temp_free(temp); + tcg_temp_free(temp2); + break; + case OPCM_32_BRR_JNZ: + if (MASK_OP_BRR_OP2(ctx->opcode) == OPC2_32_BRR_JNZ_A) { + gen_branch_condi(ctx, TCG_COND_NE, cpu_gpr_a[r1], 0, offset); + } else { + gen_branch_condi(ctx, TCG_COND_EQ, cpu_gpr_a[r1], 0, offset); + } + break; + default: + printf("Branch Error at %x\n", ctx->pc); + } + ctx->bstate = BS_BRANCH; +} + + +/* + * Functions for decoding instructions + */ + +static void decode_src_opc(DisasContext *ctx, int op1) +{ + int r1; + int32_t const4; + TCGv temp, temp2; + + r1 = MASK_OP_SRC_S1D(ctx->opcode); + const4 = MASK_OP_SRC_CONST4_SEXT(ctx->opcode); + + switch (op1) { + case OPC1_16_SRC_ADD: + gen_addi_d(cpu_gpr_d[r1], cpu_gpr_d[r1], const4); + break; + case OPC1_16_SRC_ADD_A15: + gen_addi_d(cpu_gpr_d[r1], cpu_gpr_d[15], const4); + break; + case OPC1_16_SRC_ADD_15A: + gen_addi_d(cpu_gpr_d[15], cpu_gpr_d[r1], const4); + break; + case OPC1_16_SRC_ADD_A: + tcg_gen_addi_tl(cpu_gpr_a[r1], cpu_gpr_a[r1], const4); + break; + case OPC1_16_SRC_CADD: + gen_condi_add(TCG_COND_NE, cpu_gpr_d[r1], const4, cpu_gpr_d[r1], + cpu_gpr_d[15]); + break; + case OPC1_16_SRC_CADDN: + gen_condi_add(TCG_COND_EQ, cpu_gpr_d[r1], const4, cpu_gpr_d[r1], + cpu_gpr_d[15]); + break; + case OPC1_16_SRC_CMOV: + temp = tcg_const_tl(0); + temp2 = tcg_const_tl(const4); + tcg_gen_movcond_tl(TCG_COND_NE, cpu_gpr_d[r1], cpu_gpr_d[15], temp, + temp2, cpu_gpr_d[r1]); + tcg_temp_free(temp); + tcg_temp_free(temp2); + break; + case OPC1_16_SRC_CMOVN: + temp = tcg_const_tl(0); + temp2 = tcg_const_tl(const4); + tcg_gen_movcond_tl(TCG_COND_EQ, cpu_gpr_d[r1], cpu_gpr_d[15], temp, + temp2, cpu_gpr_d[r1]); + tcg_temp_free(temp); + tcg_temp_free(temp2); + break; + case OPC1_16_SRC_EQ: + tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_gpr_d[15], cpu_gpr_d[r1], + const4); + break; + case OPC1_16_SRC_LT: + tcg_gen_setcondi_tl(TCG_COND_LT, cpu_gpr_d[15], cpu_gpr_d[r1], + const4); + break; + case OPC1_16_SRC_MOV: + tcg_gen_movi_tl(cpu_gpr_d[r1], const4); + break; + case OPC1_16_SRC_MOV_A: + const4 = MASK_OP_SRC_CONST4(ctx->opcode); + tcg_gen_movi_tl(cpu_gpr_a[r1], const4); + break; + case OPC1_16_SRC_SH: + gen_shi(cpu_gpr_d[r1], cpu_gpr_d[r1], const4); + break; + case OPC1_16_SRC_SHA: + gen_shaci(cpu_gpr_d[r1], cpu_gpr_d[r1], const4); + break; + } +} + +static void decode_srr_opc(DisasContext *ctx, int op1) +{ + int r1, r2; + TCGv temp; + + r1 = MASK_OP_SRR_S1D(ctx->opcode); + r2 = MASK_OP_SRR_S2(ctx->opcode); + + switch (op1) { + case OPC1_16_SRR_ADD: + gen_add_d(cpu_gpr_d[r1], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC1_16_SRR_ADD_A15: + gen_add_d(cpu_gpr_d[r1], cpu_gpr_d[15], cpu_gpr_d[r2]); + break; + case OPC1_16_SRR_ADD_15A: + gen_add_d(cpu_gpr_d[15], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC1_16_SRR_ADD_A: + tcg_gen_add_tl(cpu_gpr_a[r1], cpu_gpr_a[r1], cpu_gpr_a[r2]); + break; + case OPC1_16_SRR_ADDS: + gen_adds(cpu_gpr_d[r1], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC1_16_SRR_AND: + tcg_gen_and_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC1_16_SRR_CMOV: + temp = tcg_const_tl(0); + tcg_gen_movcond_tl(TCG_COND_NE, cpu_gpr_d[r1], cpu_gpr_d[15], temp, + cpu_gpr_d[r2], cpu_gpr_d[r1]); + tcg_temp_free(temp); + break; + case OPC1_16_SRR_CMOVN: + temp = tcg_const_tl(0); + tcg_gen_movcond_tl(TCG_COND_EQ, cpu_gpr_d[r1], cpu_gpr_d[15], temp, + cpu_gpr_d[r2], cpu_gpr_d[r1]); + tcg_temp_free(temp); + break; + case OPC1_16_SRR_EQ: + tcg_gen_setcond_tl(TCG_COND_EQ, cpu_gpr_d[15], cpu_gpr_d[r1], + cpu_gpr_d[r2]); + break; + case OPC1_16_SRR_LT: + tcg_gen_setcond_tl(TCG_COND_LT, cpu_gpr_d[15], cpu_gpr_d[r1], + cpu_gpr_d[r2]); + break; + case OPC1_16_SRR_MOV: + tcg_gen_mov_tl(cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC1_16_SRR_MOV_A: + tcg_gen_mov_tl(cpu_gpr_a[r1], cpu_gpr_d[r2]); + break; + case OPC1_16_SRR_MOV_AA: + tcg_gen_mov_tl(cpu_gpr_a[r1], cpu_gpr_a[r2]); + break; + case OPC1_16_SRR_MOV_D: + tcg_gen_mov_tl(cpu_gpr_d[r1], cpu_gpr_a[r2]); + break; + case OPC1_16_SRR_MUL: + gen_mul_i32s(cpu_gpr_d[r1], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC1_16_SRR_OR: + tcg_gen_or_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC1_16_SRR_SUB: + gen_sub_d(cpu_gpr_d[r1], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC1_16_SRR_SUB_A15B: + gen_sub_d(cpu_gpr_d[r1], cpu_gpr_d[15], cpu_gpr_d[r2]); + break; + case OPC1_16_SRR_SUB_15AB: + gen_sub_d(cpu_gpr_d[15], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC1_16_SRR_SUBS: + gen_subs(cpu_gpr_d[r1], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC1_16_SRR_XOR: + tcg_gen_xor_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + } +} + +static void decode_ssr_opc(DisasContext *ctx, int op1) +{ + int r1, r2; + + r1 = MASK_OP_SSR_S1(ctx->opcode); + r2 = MASK_OP_SSR_S2(ctx->opcode); + + switch (op1) { + case OPC1_16_SSR_ST_A: + tcg_gen_qemu_st_tl(cpu_gpr_a[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LEUL); + break; + case OPC1_16_SSR_ST_A_POSTINC: + tcg_gen_qemu_st_tl(cpu_gpr_a[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LEUL); + tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], 4); + break; + case OPC1_16_SSR_ST_B: + tcg_gen_qemu_st_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_UB); + break; + case OPC1_16_SSR_ST_B_POSTINC: + tcg_gen_qemu_st_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_UB); + tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], 1); + break; + case OPC1_16_SSR_ST_H: + tcg_gen_qemu_st_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LEUW); + break; + case OPC1_16_SSR_ST_H_POSTINC: + tcg_gen_qemu_st_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LEUW); + tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], 2); + break; + case OPC1_16_SSR_ST_W: + tcg_gen_qemu_st_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LEUL); + break; + case OPC1_16_SSR_ST_W_POSTINC: + tcg_gen_qemu_st_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LEUL); + tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], 4); + break; + } +} + +static void decode_sc_opc(DisasContext *ctx, int op1) +{ + int32_t const16; + + const16 = MASK_OP_SC_CONST8(ctx->opcode); + + switch (op1) { + case OPC1_16_SC_AND: + tcg_gen_andi_tl(cpu_gpr_d[15], cpu_gpr_d[15], const16); + break; + case OPC1_16_SC_BISR: + gen_helper_1arg(bisr, const16 & 0xff); + break; + case OPC1_16_SC_LD_A: + gen_offset_ld(ctx, cpu_gpr_a[15], cpu_gpr_a[10], const16 * 4, MO_LESL); + break; + case OPC1_16_SC_LD_W: + gen_offset_ld(ctx, cpu_gpr_d[15], cpu_gpr_a[10], const16 * 4, MO_LESL); + break; + case OPC1_16_SC_MOV: + tcg_gen_movi_tl(cpu_gpr_d[15], const16); + break; + case OPC1_16_SC_OR: + tcg_gen_ori_tl(cpu_gpr_d[15], cpu_gpr_d[15], const16); + break; + case OPC1_16_SC_ST_A: + gen_offset_st(ctx, cpu_gpr_a[15], cpu_gpr_a[10], const16 * 4, MO_LESL); + break; + case OPC1_16_SC_ST_W: + gen_offset_st(ctx, cpu_gpr_d[15], cpu_gpr_a[10], const16 * 4, MO_LESL); + break; + case OPC1_16_SC_SUB_A: + tcg_gen_subi_tl(cpu_gpr_a[10], cpu_gpr_a[10], const16); + break; + } +} + +static void decode_slr_opc(DisasContext *ctx, int op1) +{ + int r1, r2; + + r1 = MASK_OP_SLR_D(ctx->opcode); + r2 = MASK_OP_SLR_S2(ctx->opcode); + + switch (op1) { +/* SLR-format */ + case OPC1_16_SLR_LD_A: + tcg_gen_qemu_ld_tl(cpu_gpr_a[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LESL); + break; + case OPC1_16_SLR_LD_A_POSTINC: + tcg_gen_qemu_ld_tl(cpu_gpr_a[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LESL); + tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], 4); + break; + case OPC1_16_SLR_LD_BU: + tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_UB); + break; + case OPC1_16_SLR_LD_BU_POSTINC: + tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_UB); + tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], 1); + break; + case OPC1_16_SLR_LD_H: + tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LESW); + break; + case OPC1_16_SLR_LD_H_POSTINC: + tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LESW); + tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], 2); + break; + case OPC1_16_SLR_LD_W: + tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LESW); + break; + case OPC1_16_SLR_LD_W_POSTINC: + tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LESW); + tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], 4); + break; + } +} + +static void decode_sro_opc(DisasContext *ctx, int op1) +{ + int r2; + int32_t address; + + r2 = MASK_OP_SRO_S2(ctx->opcode); + address = MASK_OP_SRO_OFF4(ctx->opcode); + +/* SRO-format */ + switch (op1) { + case OPC1_16_SRO_LD_A: + gen_offset_ld(ctx, cpu_gpr_a[15], cpu_gpr_a[r2], address * 4, MO_LESL); + break; + case OPC1_16_SRO_LD_BU: + gen_offset_ld(ctx, cpu_gpr_d[15], cpu_gpr_a[r2], address, MO_UB); + break; + case OPC1_16_SRO_LD_H: + gen_offset_ld(ctx, cpu_gpr_d[15], cpu_gpr_a[r2], address, MO_LESW); + break; + case OPC1_16_SRO_LD_W: + gen_offset_ld(ctx, cpu_gpr_d[15], cpu_gpr_a[r2], address * 4, MO_LESL); + break; + case OPC1_16_SRO_ST_A: + gen_offset_st(ctx, cpu_gpr_a[15], cpu_gpr_a[r2], address * 4, MO_LESL); + break; + case OPC1_16_SRO_ST_B: + gen_offset_st(ctx, cpu_gpr_d[15], cpu_gpr_a[r2], address, MO_UB); + break; + case OPC1_16_SRO_ST_H: + gen_offset_st(ctx, cpu_gpr_d[15], cpu_gpr_a[r2], address * 2, MO_LESW); + break; + case OPC1_16_SRO_ST_W: + gen_offset_st(ctx, cpu_gpr_d[15], cpu_gpr_a[r2], address * 4, MO_LESL); + break; + } +} + +static void decode_sr_system(CPUTriCoreState *env, DisasContext *ctx) +{ + uint32_t op2; + op2 = MASK_OP_SR_OP2(ctx->opcode); + + switch (op2) { + case OPC2_16_SR_NOP: + break; + case OPC2_16_SR_RET: + gen_compute_branch(ctx, op2, 0, 0, 0, 0); + break; + case OPC2_16_SR_RFE: + gen_helper_rfe(cpu_env); + tcg_gen_exit_tb(0); + ctx->bstate = BS_BRANCH; + break; + case OPC2_16_SR_DEBUG: + /* raise EXCP_DEBUG */ + break; + } +} + +static void decode_sr_accu(CPUTriCoreState *env, DisasContext *ctx) +{ + uint32_t op2; + uint32_t r1; + TCGv temp; + + r1 = MASK_OP_SR_S1D(ctx->opcode); + op2 = MASK_OP_SR_OP2(ctx->opcode); + + switch (op2) { + case OPC2_16_SR_RSUB: + /* overflow only if r1 = -0x80000000 */ + temp = tcg_const_i32(-0x80000000); + /* calc V bit */ + tcg_gen_setcond_tl(TCG_COND_EQ, cpu_PSW_V, cpu_gpr_d[r1], temp); + tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); + /* calc SV bit */ + tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + /* sub */ + tcg_gen_neg_tl(cpu_gpr_d[r1], cpu_gpr_d[r1]); + /* calc av */ + tcg_gen_add_tl(cpu_PSW_AV, cpu_gpr_d[r1], cpu_gpr_d[r1]); + tcg_gen_xor_tl(cpu_PSW_AV, cpu_gpr_d[r1], cpu_PSW_AV); + /* calc sav */ + tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); + tcg_temp_free(temp); + break; + case OPC2_16_SR_SAT_B: + gen_saturate(cpu_gpr_d[r1], cpu_gpr_d[r1], 0x7f, -0x80); + break; + case OPC2_16_SR_SAT_BU: + gen_saturate_u(cpu_gpr_d[r1], cpu_gpr_d[r1], 0xff); + break; + case OPC2_16_SR_SAT_H: + gen_saturate(cpu_gpr_d[r1], cpu_gpr_d[r1], 0x7fff, -0x8000); + break; + case OPC2_16_SR_SAT_HU: + gen_saturate_u(cpu_gpr_d[r1], cpu_gpr_d[r1], 0xffff); + break; + } +} + +static void decode_16Bit_opc(CPUTriCoreState *env, DisasContext *ctx) +{ + int op1; + int r1, r2; + int32_t const16; + int32_t address; + TCGv temp; + + op1 = MASK_OP_MAJOR(ctx->opcode); + + /* handle ADDSC.A opcode only being 6 bit long */ + if (unlikely((op1 & 0x3f) == OPC1_16_SRRS_ADDSC_A)) { + op1 = OPC1_16_SRRS_ADDSC_A; + } + + switch (op1) { + case OPC1_16_SRC_ADD: + case OPC1_16_SRC_ADD_A15: + case OPC1_16_SRC_ADD_15A: + case OPC1_16_SRC_ADD_A: + case OPC1_16_SRC_CADD: + case OPC1_16_SRC_CADDN: + case OPC1_16_SRC_CMOV: + case OPC1_16_SRC_CMOVN: + case OPC1_16_SRC_EQ: + case OPC1_16_SRC_LT: + case OPC1_16_SRC_MOV: + case OPC1_16_SRC_MOV_A: + case OPC1_16_SRC_SH: + case OPC1_16_SRC_SHA: + decode_src_opc(ctx, op1); + break; +/* SRR-format */ + case OPC1_16_SRR_ADD: + case OPC1_16_SRR_ADD_A15: + case OPC1_16_SRR_ADD_15A: + case OPC1_16_SRR_ADD_A: + case OPC1_16_SRR_ADDS: + case OPC1_16_SRR_AND: + case OPC1_16_SRR_CMOV: + case OPC1_16_SRR_CMOVN: + case OPC1_16_SRR_EQ: + case OPC1_16_SRR_LT: + case OPC1_16_SRR_MOV: + case OPC1_16_SRR_MOV_A: + case OPC1_16_SRR_MOV_AA: + case OPC1_16_SRR_MOV_D: + case OPC1_16_SRR_MUL: + case OPC1_16_SRR_OR: + case OPC1_16_SRR_SUB: + case OPC1_16_SRR_SUB_A15B: + case OPC1_16_SRR_SUB_15AB: + case OPC1_16_SRR_SUBS: + case OPC1_16_SRR_XOR: + decode_srr_opc(ctx, op1); + break; +/* SSR-format */ + case OPC1_16_SSR_ST_A: + case OPC1_16_SSR_ST_A_POSTINC: + case OPC1_16_SSR_ST_B: + case OPC1_16_SSR_ST_B_POSTINC: + case OPC1_16_SSR_ST_H: + case OPC1_16_SSR_ST_H_POSTINC: + case OPC1_16_SSR_ST_W: + case OPC1_16_SSR_ST_W_POSTINC: + decode_ssr_opc(ctx, op1); + break; +/* SRRS-format */ + case OPC1_16_SRRS_ADDSC_A: + r2 = MASK_OP_SRRS_S2(ctx->opcode); + r1 = MASK_OP_SRRS_S1D(ctx->opcode); + const16 = MASK_OP_SRRS_N(ctx->opcode); + temp = tcg_temp_new(); + tcg_gen_shli_tl(temp, cpu_gpr_d[15], const16); + tcg_gen_add_tl(cpu_gpr_a[r1], cpu_gpr_a[r2], temp); + tcg_temp_free(temp); + break; +/* SLRO-format */ + case OPC1_16_SLRO_LD_A: + r1 = MASK_OP_SLRO_D(ctx->opcode); + const16 = MASK_OP_SLRO_OFF4(ctx->opcode); + gen_offset_ld(ctx, cpu_gpr_a[r1], cpu_gpr_a[15], const16 * 4, MO_LESL); + break; + case OPC1_16_SLRO_LD_BU: + r1 = MASK_OP_SLRO_D(ctx->opcode); + const16 = MASK_OP_SLRO_OFF4(ctx->opcode); + gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[15], const16, MO_UB); + break; + case OPC1_16_SLRO_LD_H: + r1 = MASK_OP_SLRO_D(ctx->opcode); + const16 = MASK_OP_SLRO_OFF4(ctx->opcode); + gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[15], const16 * 2, MO_LESW); + break; + case OPC1_16_SLRO_LD_W: + r1 = MASK_OP_SLRO_D(ctx->opcode); + const16 = MASK_OP_SLRO_OFF4(ctx->opcode); + gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[15], const16 * 4, MO_LESL); + break; +/* SB-format */ + case OPC1_16_SB_CALL: + case OPC1_16_SB_J: + case OPC1_16_SB_JNZ: + case OPC1_16_SB_JZ: + address = MASK_OP_SB_DISP8_SEXT(ctx->opcode); + gen_compute_branch(ctx, op1, 0, 0, 0, address); + break; +/* SBC-format */ + case OPC1_16_SBC_JEQ: + case OPC1_16_SBC_JNE: + address = MASK_OP_SBC_DISP4(ctx->opcode); + const16 = MASK_OP_SBC_CONST4_SEXT(ctx->opcode); + gen_compute_branch(ctx, op1, 0, 0, const16, address); + break; +/* SBRN-format */ + case OPC1_16_SBRN_JNZ_T: + case OPC1_16_SBRN_JZ_T: + address = MASK_OP_SBRN_DISP4(ctx->opcode); + const16 = MASK_OP_SBRN_N(ctx->opcode); + gen_compute_branch(ctx, op1, 0, 0, const16, address); + break; +/* SBR-format */ + case OPC1_16_SBR_JEQ: + case OPC1_16_SBR_JGEZ: + case OPC1_16_SBR_JGTZ: + case OPC1_16_SBR_JLEZ: + case OPC1_16_SBR_JLTZ: + case OPC1_16_SBR_JNE: + case OPC1_16_SBR_JNZ: + case OPC1_16_SBR_JNZ_A: + case OPC1_16_SBR_JZ: + case OPC1_16_SBR_JZ_A: + case OPC1_16_SBR_LOOP: + r1 = MASK_OP_SBR_S2(ctx->opcode); + address = MASK_OP_SBR_DISP4(ctx->opcode); + gen_compute_branch(ctx, op1, r1, 0, 0, address); + break; +/* SC-format */ + case OPC1_16_SC_AND: + case OPC1_16_SC_BISR: + case OPC1_16_SC_LD_A: + case OPC1_16_SC_LD_W: + case OPC1_16_SC_MOV: + case OPC1_16_SC_OR: + case OPC1_16_SC_ST_A: + case OPC1_16_SC_ST_W: + case OPC1_16_SC_SUB_A: + decode_sc_opc(ctx, op1); + break; +/* SLR-format */ + case OPC1_16_SLR_LD_A: + case OPC1_16_SLR_LD_A_POSTINC: + case OPC1_16_SLR_LD_BU: + case OPC1_16_SLR_LD_BU_POSTINC: + case OPC1_16_SLR_LD_H: + case OPC1_16_SLR_LD_H_POSTINC: + case OPC1_16_SLR_LD_W: + case OPC1_16_SLR_LD_W_POSTINC: + decode_slr_opc(ctx, op1); + break; +/* SRO-format */ + case OPC1_16_SRO_LD_A: + case OPC1_16_SRO_LD_BU: + case OPC1_16_SRO_LD_H: + case OPC1_16_SRO_LD_W: + case OPC1_16_SRO_ST_A: + case OPC1_16_SRO_ST_B: + case OPC1_16_SRO_ST_H: + case OPC1_16_SRO_ST_W: + decode_sro_opc(ctx, op1); + break; +/* SSRO-format */ + case OPC1_16_SSRO_ST_A: + r1 = MASK_OP_SSRO_S1(ctx->opcode); + const16 = MASK_OP_SSRO_OFF4(ctx->opcode); + gen_offset_st(ctx, cpu_gpr_a[r1], cpu_gpr_a[15], const16 * 4, MO_LESL); + break; + case OPC1_16_SSRO_ST_B: + r1 = MASK_OP_SSRO_S1(ctx->opcode); + const16 = MASK_OP_SSRO_OFF4(ctx->opcode); + gen_offset_st(ctx, cpu_gpr_d[r1], cpu_gpr_a[15], const16, MO_UB); + break; + case OPC1_16_SSRO_ST_H: + r1 = MASK_OP_SSRO_S1(ctx->opcode); + const16 = MASK_OP_SSRO_OFF4(ctx->opcode); + gen_offset_st(ctx, cpu_gpr_d[r1], cpu_gpr_a[15], const16 * 2, MO_LESW); + break; + case OPC1_16_SSRO_ST_W: + r1 = MASK_OP_SSRO_S1(ctx->opcode); + const16 = MASK_OP_SSRO_OFF4(ctx->opcode); + gen_offset_st(ctx, cpu_gpr_d[r1], cpu_gpr_a[15], const16 * 4, MO_LESL); + break; +/* SR-format */ + case OPCM_16_SR_SYSTEM: + decode_sr_system(env, ctx); + break; + case OPCM_16_SR_ACCU: + decode_sr_accu(env, ctx); + break; + case OPC1_16_SR_JI: + r1 = MASK_OP_SR_S1D(ctx->opcode); + gen_compute_branch(ctx, op1, r1, 0, 0, 0); + break; + case OPC1_16_SR_NOT: + r1 = MASK_OP_SR_S1D(ctx->opcode); + tcg_gen_not_tl(cpu_gpr_d[r1], cpu_gpr_d[r1]); + break; + } +} + +/* + * 32 bit instructions + */ + +/* ABS-format */ +static void decode_abs_ldw(CPUTriCoreState *env, DisasContext *ctx) +{ + int32_t op2; + int32_t r1; + uint32_t address; + TCGv temp; + + r1 = MASK_OP_ABS_S1D(ctx->opcode); + address = MASK_OP_ABS_OFF18(ctx->opcode); + op2 = MASK_OP_ABS_OP2(ctx->opcode); + + temp = tcg_const_i32(EA_ABS_FORMAT(address)); + + switch (op2) { + case OPC2_32_ABS_LD_A: + tcg_gen_qemu_ld_tl(cpu_gpr_a[r1], temp, ctx->mem_idx, MO_LESL); + break; + case OPC2_32_ABS_LD_D: + gen_ld_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], temp, ctx); + break; + case OPC2_32_ABS_LD_DA: + gen_ld_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], temp, ctx); + break; + case OPC2_32_ABS_LD_W: + tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LESL); + break; + } + + tcg_temp_free(temp); +} + +static void decode_abs_ldb(CPUTriCoreState *env, DisasContext *ctx) +{ + int32_t op2; + int32_t r1; + uint32_t address; + TCGv temp; + + r1 = MASK_OP_ABS_S1D(ctx->opcode); + address = MASK_OP_ABS_OFF18(ctx->opcode); + op2 = MASK_OP_ABS_OP2(ctx->opcode); + + temp = tcg_const_i32(EA_ABS_FORMAT(address)); + + switch (op2) { + case OPC2_32_ABS_LD_B: + tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_SB); + break; + case OPC2_32_ABS_LD_BU: + tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_UB); + break; + case OPC2_32_ABS_LD_H: + tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LESW); + break; + case OPC2_32_ABS_LD_HU: + tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LEUW); + break; + } + + tcg_temp_free(temp); +} + +static void decode_abs_ldst_swap(CPUTriCoreState *env, DisasContext *ctx) +{ + int32_t op2; + int32_t r1; + uint32_t address; + TCGv temp; + + r1 = MASK_OP_ABS_S1D(ctx->opcode); + address = MASK_OP_ABS_OFF18(ctx->opcode); + op2 = MASK_OP_ABS_OP2(ctx->opcode); + + temp = tcg_const_i32(EA_ABS_FORMAT(address)); + + switch (op2) { + case OPC2_32_ABS_LDMST: + gen_ldmst(ctx, r1, temp); + break; + case OPC2_32_ABS_SWAP_W: + gen_swap(ctx, r1, temp); + break; + } + + tcg_temp_free(temp); +} + +static void decode_abs_ldst_context(CPUTriCoreState *env, DisasContext *ctx) +{ + uint32_t op2; + int32_t off18; + + off18 = MASK_OP_ABS_OFF18(ctx->opcode); + op2 = MASK_OP_ABS_OP2(ctx->opcode); + + switch (op2) { + case OPC2_32_ABS_LDLCX: + gen_helper_1arg(ldlcx, EA_ABS_FORMAT(off18)); + break; + case OPC2_32_ABS_LDUCX: + gen_helper_1arg(lducx, EA_ABS_FORMAT(off18)); + break; + case OPC2_32_ABS_STLCX: + gen_helper_1arg(stlcx, EA_ABS_FORMAT(off18)); + break; + case OPC2_32_ABS_STUCX: + gen_helper_1arg(stucx, EA_ABS_FORMAT(off18)); + break; + } +} + +static void decode_abs_store(CPUTriCoreState *env, DisasContext *ctx) +{ + int32_t op2; + int32_t r1; + uint32_t address; + TCGv temp; + + r1 = MASK_OP_ABS_S1D(ctx->opcode); + address = MASK_OP_ABS_OFF18(ctx->opcode); + op2 = MASK_OP_ABS_OP2(ctx->opcode); + + temp = tcg_const_i32(EA_ABS_FORMAT(address)); + + switch (op2) { + case OPC2_32_ABS_ST_A: + tcg_gen_qemu_st_tl(cpu_gpr_a[r1], temp, ctx->mem_idx, MO_LESL); + break; + case OPC2_32_ABS_ST_D: + gen_st_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], temp, ctx); + break; + case OPC2_32_ABS_ST_DA: + gen_st_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], temp, ctx); + break; + case OPC2_32_ABS_ST_W: + tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LESL); + break; + + } + tcg_temp_free(temp); +} + +static void decode_abs_storeb_h(CPUTriCoreState *env, DisasContext *ctx) +{ + int32_t op2; + int32_t r1; + uint32_t address; + TCGv temp; + + r1 = MASK_OP_ABS_S1D(ctx->opcode); + address = MASK_OP_ABS_OFF18(ctx->opcode); + op2 = MASK_OP_ABS_OP2(ctx->opcode); + + temp = tcg_const_i32(EA_ABS_FORMAT(address)); + + switch (op2) { + case OPC2_32_ABS_ST_B: + tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_UB); + break; + case OPC2_32_ABS_ST_H: + tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LEUW); + break; + } + tcg_temp_free(temp); +} + +/* Bit-format */ + +static void decode_bit_andacc(CPUTriCoreState *env, DisasContext *ctx) +{ + uint32_t op2; + int r1, r2, r3; + int pos1, pos2; + + r1 = MASK_OP_BIT_S1(ctx->opcode); + r2 = MASK_OP_BIT_S2(ctx->opcode); + r3 = MASK_OP_BIT_D(ctx->opcode); + pos1 = MASK_OP_BIT_POS1(ctx->opcode); + pos2 = MASK_OP_BIT_POS2(ctx->opcode); + op2 = MASK_OP_BIT_OP2(ctx->opcode); + + + switch (op2) { + case OPC2_32_BIT_AND_AND_T: + gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], + pos1, pos2, &tcg_gen_and_tl, &tcg_gen_and_tl); + break; + case OPC2_32_BIT_AND_ANDN_T: + gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], + pos1, pos2, &tcg_gen_andc_tl, &tcg_gen_and_tl); + break; + case OPC2_32_BIT_AND_NOR_T: + if (TCG_TARGET_HAS_andc_i32) { + gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], + pos1, pos2, &tcg_gen_or_tl, &tcg_gen_andc_tl); + } else { + gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], + pos1, pos2, &tcg_gen_nor_tl, &tcg_gen_and_tl); + } + break; + case OPC2_32_BIT_AND_OR_T: + gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], + pos1, pos2, &tcg_gen_or_tl, &tcg_gen_and_tl); + break; + } +} + +static void decode_bit_logical_t(CPUTriCoreState *env, DisasContext *ctx) +{ + uint32_t op2; + int r1, r2, r3; + int pos1, pos2; + r1 = MASK_OP_BIT_S1(ctx->opcode); + r2 = MASK_OP_BIT_S2(ctx->opcode); + r3 = MASK_OP_BIT_D(ctx->opcode); + pos1 = MASK_OP_BIT_POS1(ctx->opcode); + pos2 = MASK_OP_BIT_POS2(ctx->opcode); + op2 = MASK_OP_BIT_OP2(ctx->opcode); + + switch (op2) { + case OPC2_32_BIT_AND_T: + gen_bit_1op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], + pos1, pos2, &tcg_gen_and_tl); + break; + case OPC2_32_BIT_ANDN_T: + gen_bit_1op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], + pos1, pos2, &tcg_gen_andc_tl); + break; + case OPC2_32_BIT_NOR_T: + gen_bit_1op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], + pos1, pos2, &tcg_gen_nor_tl); + break; + case OPC2_32_BIT_OR_T: + gen_bit_1op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], + pos1, pos2, &tcg_gen_or_tl); + break; + } +} + +static void decode_bit_insert(CPUTriCoreState *env, DisasContext *ctx) +{ + uint32_t op2; + int r1, r2, r3; + int pos1, pos2; + TCGv temp; + op2 = MASK_OP_BIT_OP2(ctx->opcode); + r1 = MASK_OP_BIT_S1(ctx->opcode); + r2 = MASK_OP_BIT_S2(ctx->opcode); + r3 = MASK_OP_BIT_D(ctx->opcode); + pos1 = MASK_OP_BIT_POS1(ctx->opcode); + pos2 = MASK_OP_BIT_POS2(ctx->opcode); + + temp = tcg_temp_new(); + + tcg_gen_shri_tl(temp, cpu_gpr_d[r2], pos2); + if (op2 == OPC2_32_BIT_INSN_T) { + tcg_gen_not_tl(temp, temp); + } + tcg_gen_deposit_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], temp, pos1, 1); + tcg_temp_free(temp); +} + +static void decode_bit_logical_t2(CPUTriCoreState *env, DisasContext *ctx) +{ + uint32_t op2; + + int r1, r2, r3; + int pos1, pos2; + + op2 = MASK_OP_BIT_OP2(ctx->opcode); + r1 = MASK_OP_BIT_S1(ctx->opcode); + r2 = MASK_OP_BIT_S2(ctx->opcode); + r3 = MASK_OP_BIT_D(ctx->opcode); + pos1 = MASK_OP_BIT_POS1(ctx->opcode); + pos2 = MASK_OP_BIT_POS2(ctx->opcode); + + switch (op2) { + case OPC2_32_BIT_NAND_T: + gen_bit_1op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], + pos1, pos2, &tcg_gen_nand_tl); break; - case OPC1_16_SRC_ADD_15A: - gen_addi_d(cpu_gpr_d[15], cpu_gpr_d[r1], const4); + case OPC2_32_BIT_ORN_T: + gen_bit_1op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], + pos1, pos2, &tcg_gen_orc_tl); + break; + case OPC2_32_BIT_XNOR_T: + gen_bit_1op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], + pos1, pos2, &tcg_gen_eqv_tl); + break; + case OPC2_32_BIT_XOR_T: + gen_bit_1op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], + pos1, pos2, &tcg_gen_xor_tl); + break; + } +} + +static void decode_bit_orand(CPUTriCoreState *env, DisasContext *ctx) +{ + uint32_t op2; + + int r1, r2, r3; + int pos1, pos2; + + op2 = MASK_OP_BIT_OP2(ctx->opcode); + r1 = MASK_OP_BIT_S1(ctx->opcode); + r2 = MASK_OP_BIT_S2(ctx->opcode); + r3 = MASK_OP_BIT_D(ctx->opcode); + pos1 = MASK_OP_BIT_POS1(ctx->opcode); + pos2 = MASK_OP_BIT_POS2(ctx->opcode); + + switch (op2) { + case OPC2_32_BIT_OR_AND_T: + gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], + pos1, pos2, &tcg_gen_and_tl, &tcg_gen_or_tl); + break; + case OPC2_32_BIT_OR_ANDN_T: + gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], + pos1, pos2, &tcg_gen_andc_tl, &tcg_gen_or_tl); + break; + case OPC2_32_BIT_OR_NOR_T: + if (TCG_TARGET_HAS_orc_i32) { + gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], + pos1, pos2, &tcg_gen_or_tl, &tcg_gen_orc_tl); + } else { + gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], + pos1, pos2, &tcg_gen_nor_tl, &tcg_gen_or_tl); + } + break; + case OPC2_32_BIT_OR_OR_T: + gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], + pos1, pos2, &tcg_gen_or_tl, &tcg_gen_or_tl); + break; + } +} + +static void decode_bit_sh_logic1(CPUTriCoreState *env, DisasContext *ctx) +{ + uint32_t op2; + int r1, r2, r3; + int pos1, pos2; + TCGv temp; + + op2 = MASK_OP_BIT_OP2(ctx->opcode); + r1 = MASK_OP_BIT_S1(ctx->opcode); + r2 = MASK_OP_BIT_S2(ctx->opcode); + r3 = MASK_OP_BIT_D(ctx->opcode); + pos1 = MASK_OP_BIT_POS1(ctx->opcode); + pos2 = MASK_OP_BIT_POS2(ctx->opcode); + + temp = tcg_temp_new(); + + switch (op2) { + case OPC2_32_BIT_SH_AND_T: + gen_bit_1op(temp, cpu_gpr_d[r1], cpu_gpr_d[r2], + pos1, pos2, &tcg_gen_and_tl); + break; + case OPC2_32_BIT_SH_ANDN_T: + gen_bit_1op(temp, cpu_gpr_d[r1], cpu_gpr_d[r2], + pos1, pos2, &tcg_gen_andc_tl); + break; + case OPC2_32_BIT_SH_NOR_T: + gen_bit_1op(temp, cpu_gpr_d[r1], cpu_gpr_d[r2], + pos1, pos2, &tcg_gen_nor_tl); + break; + case OPC2_32_BIT_SH_OR_T: + gen_bit_1op(temp, cpu_gpr_d[r1], cpu_gpr_d[r2], + pos1, pos2, &tcg_gen_or_tl); + break; + } + tcg_gen_shli_tl(cpu_gpr_d[r3], cpu_gpr_d[r3], 1); + tcg_gen_add_tl(cpu_gpr_d[r3], cpu_gpr_d[r3], temp); + tcg_temp_free(temp); +} + +static void decode_bit_sh_logic2(CPUTriCoreState *env, DisasContext *ctx) +{ + uint32_t op2; + int r1, r2, r3; + int pos1, pos2; + TCGv temp; + + op2 = MASK_OP_BIT_OP2(ctx->opcode); + r1 = MASK_OP_BIT_S1(ctx->opcode); + r2 = MASK_OP_BIT_S2(ctx->opcode); + r3 = MASK_OP_BIT_D(ctx->opcode); + pos1 = MASK_OP_BIT_POS1(ctx->opcode); + pos2 = MASK_OP_BIT_POS2(ctx->opcode); + + temp = tcg_temp_new(); + + switch (op2) { + case OPC2_32_BIT_SH_NAND_T: + gen_bit_1op(temp, cpu_gpr_d[r1] , cpu_gpr_d[r2] , + pos1, pos2, &tcg_gen_nand_tl); + break; + case OPC2_32_BIT_SH_ORN_T: + gen_bit_1op(temp, cpu_gpr_d[r1], cpu_gpr_d[r2], + pos1, pos2, &tcg_gen_orc_tl); + break; + case OPC2_32_BIT_SH_XNOR_T: + gen_bit_1op(temp, cpu_gpr_d[r1], cpu_gpr_d[r2], + pos1, pos2, &tcg_gen_eqv_tl); + break; + case OPC2_32_BIT_SH_XOR_T: + gen_bit_1op(temp, cpu_gpr_d[r1], cpu_gpr_d[r2], + pos1, pos2, &tcg_gen_xor_tl); + break; + } + tcg_gen_shli_tl(cpu_gpr_d[r3], cpu_gpr_d[r3], 1); + tcg_gen_add_tl(cpu_gpr_d[r3], cpu_gpr_d[r3], temp); + tcg_temp_free(temp); +} + +/* BO-format */ + + +static void decode_bo_addrmode_post_pre_base(CPUTriCoreState *env, + DisasContext *ctx) +{ + uint32_t op2; + uint32_t off10; + int32_t r1, r2; + TCGv temp; + + r1 = MASK_OP_BO_S1D(ctx->opcode); + r2 = MASK_OP_BO_S2(ctx->opcode); + off10 = MASK_OP_BO_OFF10_SEXT(ctx->opcode); + op2 = MASK_OP_BO_OP2(ctx->opcode); + + switch (op2) { + case OPC2_32_BO_CACHEA_WI_SHORTOFF: + case OPC2_32_BO_CACHEA_W_SHORTOFF: + case OPC2_32_BO_CACHEA_I_SHORTOFF: + /* instruction to access the cache */ + break; + case OPC2_32_BO_CACHEA_WI_POSTINC: + case OPC2_32_BO_CACHEA_W_POSTINC: + case OPC2_32_BO_CACHEA_I_POSTINC: + /* instruction to access the cache, but we still need to handle + the addressing mode */ + tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + break; + case OPC2_32_BO_CACHEA_WI_PREINC: + case OPC2_32_BO_CACHEA_W_PREINC: + case OPC2_32_BO_CACHEA_I_PREINC: + /* instruction to access the cache, but we still need to handle + the addressing mode */ + tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + break; + case OPC2_32_BO_CACHEI_WI_SHORTOFF: + case OPC2_32_BO_CACHEI_W_SHORTOFF: + /* TODO: Raise illegal opcode trap, + if !tricore_feature(TRICORE_FEATURE_131) */ + break; + case OPC2_32_BO_CACHEI_W_POSTINC: + case OPC2_32_BO_CACHEI_WI_POSTINC: + if (tricore_feature(env, TRICORE_FEATURE_131)) { + tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + } /* TODO: else raise illegal opcode trap */ + break; + case OPC2_32_BO_CACHEI_W_PREINC: + case OPC2_32_BO_CACHEI_WI_PREINC: + if (tricore_feature(env, TRICORE_FEATURE_131)) { + tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + } /* TODO: else raise illegal opcode trap */ + break; + case OPC2_32_BO_ST_A_SHORTOFF: + gen_offset_st(ctx, cpu_gpr_a[r1], cpu_gpr_a[r2], off10, MO_LESL); + break; + case OPC2_32_BO_ST_A_POSTINC: + tcg_gen_qemu_st_tl(cpu_gpr_a[r1], cpu_gpr_a[r2], ctx->mem_idx, + MO_LESL); + tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + break; + case OPC2_32_BO_ST_A_PREINC: + gen_st_preincr(ctx, cpu_gpr_a[r1], cpu_gpr_a[r2], off10, MO_LESL); + break; + case OPC2_32_BO_ST_B_SHORTOFF: + gen_offset_st(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_UB); + break; + case OPC2_32_BO_ST_B_POSTINC: + tcg_gen_qemu_st_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, + MO_UB); + tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + break; + case OPC2_32_BO_ST_B_PREINC: + gen_st_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_UB); + break; + case OPC2_32_BO_ST_D_SHORTOFF: + gen_offset_st_2regs(cpu_gpr_d[r1+1], cpu_gpr_d[r1], cpu_gpr_a[r2], + off10, ctx); + break; + case OPC2_32_BO_ST_D_POSTINC: + gen_st_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], cpu_gpr_a[r2], ctx); + tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + break; + case OPC2_32_BO_ST_D_PREINC: + temp = tcg_temp_new(); + tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); + gen_st_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], temp, ctx); + tcg_gen_mov_tl(cpu_gpr_a[r2], temp); + tcg_temp_free(temp); + break; + case OPC2_32_BO_ST_DA_SHORTOFF: + gen_offset_st_2regs(cpu_gpr_a[r1+1], cpu_gpr_a[r1], cpu_gpr_a[r2], + off10, ctx); + break; + case OPC2_32_BO_ST_DA_POSTINC: + gen_st_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], cpu_gpr_a[r2], ctx); + tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + break; + case OPC2_32_BO_ST_DA_PREINC: + temp = tcg_temp_new(); + tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); + gen_st_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], temp, ctx); + tcg_gen_mov_tl(cpu_gpr_a[r2], temp); + tcg_temp_free(temp); break; - case OPC1_16_SRC_ADD_A: - tcg_gen_addi_tl(cpu_gpr_a[r1], cpu_gpr_a[r1], const4); + case OPC2_32_BO_ST_H_SHORTOFF: + gen_offset_st(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUW); break; - case OPC1_16_SRC_CADD: - gen_condi_add(TCG_COND_NE, cpu_gpr_d[r1], const4, cpu_gpr_d[r1], - cpu_gpr_d[15]); + case OPC2_32_BO_ST_H_POSTINC: + tcg_gen_qemu_st_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, + MO_LEUW); + tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; - case OPC1_16_SRC_CADDN: - gen_condi_add(TCG_COND_EQ, cpu_gpr_d[r1], const4, cpu_gpr_d[r1], - cpu_gpr_d[15]); + case OPC2_32_BO_ST_H_PREINC: + gen_st_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUW); break; - case OPC1_16_SRC_CMOV: - temp = tcg_const_tl(0); - temp2 = tcg_const_tl(const4); - tcg_gen_movcond_tl(TCG_COND_NE, cpu_gpr_d[r1], cpu_gpr_d[15], temp, - temp2, cpu_gpr_d[r1]); + case OPC2_32_BO_ST_Q_SHORTOFF: + temp = tcg_temp_new(); + tcg_gen_shri_tl(temp, cpu_gpr_d[r1], 16); + gen_offset_st(ctx, temp, cpu_gpr_a[r2], off10, MO_LEUW); tcg_temp_free(temp); - tcg_temp_free(temp2); break; - case OPC1_16_SRC_CMOVN: - temp = tcg_const_tl(0); - temp2 = tcg_const_tl(const4); - tcg_gen_movcond_tl(TCG_COND_EQ, cpu_gpr_d[r1], cpu_gpr_d[15], temp, - temp2, cpu_gpr_d[r1]); + case OPC2_32_BO_ST_Q_POSTINC: + temp = tcg_temp_new(); + tcg_gen_shri_tl(temp, cpu_gpr_d[r1], 16); + tcg_gen_qemu_st_tl(temp, cpu_gpr_a[r2], ctx->mem_idx, + MO_LEUW); + tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); tcg_temp_free(temp); - tcg_temp_free(temp2); - break; - case OPC1_16_SRC_EQ: - tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_gpr_d[15], cpu_gpr_d[r1], - const4); - break; - case OPC1_16_SRC_LT: - tcg_gen_setcondi_tl(TCG_COND_LT, cpu_gpr_d[15], cpu_gpr_d[r1], - const4); break; - case OPC1_16_SRC_MOV: - tcg_gen_movi_tl(cpu_gpr_d[r1], const4); + case OPC2_32_BO_ST_Q_PREINC: + temp = tcg_temp_new(); + tcg_gen_shri_tl(temp, cpu_gpr_d[r1], 16); + gen_st_preincr(ctx, temp, cpu_gpr_a[r2], off10, MO_LEUW); + tcg_temp_free(temp); break; - case OPC1_16_SRC_MOV_A: - const4 = MASK_OP_SRC_CONST4(ctx->opcode); - tcg_gen_movi_tl(cpu_gpr_a[r1], const4); + case OPC2_32_BO_ST_W_SHORTOFF: + gen_offset_st(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUL); break; - case OPC1_16_SRC_SH: - gen_shi(cpu_gpr_d[r1], cpu_gpr_d[r1], const4); + case OPC2_32_BO_ST_W_POSTINC: + tcg_gen_qemu_st_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, + MO_LEUL); + tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; - case OPC1_16_SRC_SHA: - gen_shaci(cpu_gpr_d[r1], cpu_gpr_d[r1], const4); + case OPC2_32_BO_ST_W_PREINC: + gen_st_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUL); break; } } -static void decode_srr_opc(DisasContext *ctx, int op1) +static void decode_bo_addrmode_bitreverse_circular(CPUTriCoreState *env, + DisasContext *ctx) { - int r1, r2; - TCGv temp; + uint32_t op2; + uint32_t off10; + int32_t r1, r2; + TCGv temp, temp2, temp3; - r1 = MASK_OP_SRR_S1D(ctx->opcode); - r2 = MASK_OP_SRR_S2(ctx->opcode); + r1 = MASK_OP_BO_S1D(ctx->opcode); + r2 = MASK_OP_BO_S2(ctx->opcode); + off10 = MASK_OP_BO_OFF10_SEXT(ctx->opcode); + op2 = MASK_OP_BO_OP2(ctx->opcode); - switch (op1) { - case OPC1_16_SRR_ADD: - gen_add_d(cpu_gpr_d[r1], cpu_gpr_d[r1], cpu_gpr_d[r2]); + temp = tcg_temp_new(); + temp2 = tcg_temp_new(); + temp3 = tcg_const_i32(off10); + + tcg_gen_ext16u_tl(temp, cpu_gpr_a[r2+1]); + tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp); + + switch (op2) { + case OPC2_32_BO_CACHEA_WI_BR: + case OPC2_32_BO_CACHEA_W_BR: + case OPC2_32_BO_CACHEA_I_BR: + gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); break; - case OPC1_16_SRR_ADD_A15: - gen_add_d(cpu_gpr_d[r1], cpu_gpr_d[15], cpu_gpr_d[r2]); + case OPC2_32_BO_CACHEA_WI_CIRC: + case OPC2_32_BO_CACHEA_W_CIRC: + case OPC2_32_BO_CACHEA_I_CIRC: + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); break; - case OPC1_16_SRR_ADD_15A: - gen_add_d(cpu_gpr_d[15], cpu_gpr_d[r1], cpu_gpr_d[r2]); + case OPC2_32_BO_ST_A_BR: + tcg_gen_qemu_st_tl(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL); + gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); break; - case OPC1_16_SRR_ADD_A: - tcg_gen_add_tl(cpu_gpr_a[r1], cpu_gpr_a[r1], cpu_gpr_a[r2]); + case OPC2_32_BO_ST_A_CIRC: + tcg_gen_qemu_st_tl(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); break; - case OPC1_16_SRR_ADDS: - gen_adds(cpu_gpr_d[r1], cpu_gpr_d[r1], cpu_gpr_d[r2]); + case OPC2_32_BO_ST_B_BR: + tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_UB); + gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); break; - case OPC1_16_SRR_AND: - tcg_gen_and_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], cpu_gpr_d[r2]); + case OPC2_32_BO_ST_B_CIRC: + tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_UB); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); break; - case OPC1_16_SRR_CMOV: - temp = tcg_const_tl(0); - tcg_gen_movcond_tl(TCG_COND_NE, cpu_gpr_d[r1], cpu_gpr_d[15], temp, - cpu_gpr_d[r2], cpu_gpr_d[r1]); - tcg_temp_free(temp); + case OPC2_32_BO_ST_D_BR: + gen_st_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], temp2, ctx); + gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); break; - case OPC1_16_SRR_CMOVN: - temp = tcg_const_tl(0); - tcg_gen_movcond_tl(TCG_COND_EQ, cpu_gpr_d[r1], cpu_gpr_d[15], temp, - cpu_gpr_d[r2], cpu_gpr_d[r1]); - tcg_temp_free(temp); + case OPC2_32_BO_ST_D_CIRC: + tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL); + tcg_gen_shri_tl(temp2, cpu_gpr_a[r2+1], 16); + tcg_gen_addi_tl(temp, temp, 4); + tcg_gen_rem_tl(temp, temp, temp2); + tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp); + tcg_gen_qemu_st_tl(cpu_gpr_d[r1+1], temp2, ctx->mem_idx, MO_LEUL); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); break; - case OPC1_16_SRR_EQ: - tcg_gen_setcond_tl(TCG_COND_EQ, cpu_gpr_d[15], cpu_gpr_d[r1], - cpu_gpr_d[r2]); + case OPC2_32_BO_ST_DA_BR: + gen_st_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], temp2, ctx); + gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); break; - case OPC1_16_SRR_LT: - tcg_gen_setcond_tl(TCG_COND_LT, cpu_gpr_d[15], cpu_gpr_d[r1], - cpu_gpr_d[r2]); + case OPC2_32_BO_ST_DA_CIRC: + tcg_gen_qemu_st_tl(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL); + tcg_gen_shri_tl(temp2, cpu_gpr_a[r2+1], 16); + tcg_gen_addi_tl(temp, temp, 4); + tcg_gen_rem_tl(temp, temp, temp2); + tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp); + tcg_gen_qemu_st_tl(cpu_gpr_a[r1+1], temp2, ctx->mem_idx, MO_LEUL); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); break; - case OPC1_16_SRR_MOV: - tcg_gen_mov_tl(cpu_gpr_d[r1], cpu_gpr_d[r2]); + case OPC2_32_BO_ST_H_BR: + tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW); + gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); break; - case OPC1_16_SRR_MOV_A: - tcg_gen_mov_tl(cpu_gpr_a[r1], cpu_gpr_d[r2]); + case OPC2_32_BO_ST_H_CIRC: + tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); break; - case OPC1_16_SRR_MOV_AA: - tcg_gen_mov_tl(cpu_gpr_a[r1], cpu_gpr_a[r2]); + case OPC2_32_BO_ST_Q_BR: + tcg_gen_shri_tl(temp, cpu_gpr_d[r1], 16); + tcg_gen_qemu_st_tl(temp, temp2, ctx->mem_idx, MO_LEUW); + gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); break; - case OPC1_16_SRR_MOV_D: - tcg_gen_mov_tl(cpu_gpr_d[r1], cpu_gpr_a[r2]); + case OPC2_32_BO_ST_Q_CIRC: + tcg_gen_shri_tl(temp, cpu_gpr_d[r1], 16); + tcg_gen_qemu_st_tl(temp, temp2, ctx->mem_idx, MO_LEUW); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); break; - case OPC1_16_SRR_MUL: - gen_mul_i32s(cpu_gpr_d[r1], cpu_gpr_d[r1], cpu_gpr_d[r2]); + case OPC2_32_BO_ST_W_BR: + tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL); + gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); break; - case OPC1_16_SRR_OR: - tcg_gen_or_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], cpu_gpr_d[r2]); + case OPC2_32_BO_ST_W_CIRC: + tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); break; - case OPC1_16_SRR_SUB: - gen_sub_d(cpu_gpr_d[r1], cpu_gpr_d[r1], cpu_gpr_d[r2]); + } + tcg_temp_free(temp); + tcg_temp_free(temp2); + tcg_temp_free(temp3); +} + +static void decode_bo_addrmode_ld_post_pre_base(CPUTriCoreState *env, + DisasContext *ctx) +{ + uint32_t op2; + uint32_t off10; + int32_t r1, r2; + TCGv temp; + + r1 = MASK_OP_BO_S1D(ctx->opcode); + r2 = MASK_OP_BO_S2(ctx->opcode); + off10 = MASK_OP_BO_OFF10_SEXT(ctx->opcode); + op2 = MASK_OP_BO_OP2(ctx->opcode); + + switch (op2) { + case OPC2_32_BO_LD_A_SHORTOFF: + gen_offset_ld(ctx, cpu_gpr_a[r1], cpu_gpr_a[r2], off10, MO_LEUL); break; - case OPC1_16_SRR_SUB_A15B: - gen_sub_d(cpu_gpr_d[r1], cpu_gpr_d[15], cpu_gpr_d[r2]); + case OPC2_32_BO_LD_A_POSTINC: + tcg_gen_qemu_ld_tl(cpu_gpr_a[r1], cpu_gpr_a[r2], ctx->mem_idx, + MO_LEUL); + tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; - case OPC1_16_SRR_SUB_15AB: - gen_sub_d(cpu_gpr_d[15], cpu_gpr_d[r1], cpu_gpr_d[r2]); + case OPC2_32_BO_LD_A_PREINC: + gen_ld_preincr(ctx, cpu_gpr_a[r1], cpu_gpr_a[r2], off10, MO_LEUL); break; - case OPC1_16_SRR_SUBS: - gen_subs(cpu_gpr_d[r1], cpu_gpr_d[r1], cpu_gpr_d[r2]); + case OPC2_32_BO_LD_B_SHORTOFF: + gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_SB); + break; + case OPC2_32_BO_LD_B_POSTINC: + tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, + MO_SB); + tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + break; + case OPC2_32_BO_LD_B_PREINC: + gen_ld_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_SB); + break; + case OPC2_32_BO_LD_BU_SHORTOFF: + gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_UB); + break; + case OPC2_32_BO_LD_BU_POSTINC: + tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, + MO_UB); + tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + break; + case OPC2_32_BO_LD_BU_PREINC: + gen_ld_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_SB); break; - case OPC1_16_SRR_XOR: - tcg_gen_xor_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], cpu_gpr_d[r2]); + case OPC2_32_BO_LD_D_SHORTOFF: + gen_offset_ld_2regs(cpu_gpr_d[r1+1], cpu_gpr_d[r1], cpu_gpr_a[r2], + off10, ctx); break; - } -} - -static void decode_ssr_opc(DisasContext *ctx, int op1) -{ - int r1, r2; - - r1 = MASK_OP_SSR_S1(ctx->opcode); - r2 = MASK_OP_SSR_S2(ctx->opcode); - - switch (op1) { - case OPC1_16_SSR_ST_A: - tcg_gen_qemu_st_tl(cpu_gpr_a[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LEUL); + case OPC2_32_BO_LD_D_POSTINC: + gen_ld_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], cpu_gpr_a[r2], ctx); + tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; - case OPC1_16_SSR_ST_A_POSTINC: - tcg_gen_qemu_st_tl(cpu_gpr_a[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LEUL); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], 4); + case OPC2_32_BO_LD_D_PREINC: + temp = tcg_temp_new(); + tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); + gen_ld_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], temp, ctx); + tcg_gen_mov_tl(cpu_gpr_a[r2], temp); + tcg_temp_free(temp); break; - case OPC1_16_SSR_ST_B: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_UB); + case OPC2_32_BO_LD_DA_SHORTOFF: + gen_offset_ld_2regs(cpu_gpr_a[r1+1], cpu_gpr_a[r1], cpu_gpr_a[r2], + off10, ctx); break; - case OPC1_16_SSR_ST_B_POSTINC: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_UB); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], 1); + case OPC2_32_BO_LD_DA_POSTINC: + gen_ld_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], cpu_gpr_a[r2], ctx); + tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; - case OPC1_16_SSR_ST_H: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LEUW); + case OPC2_32_BO_LD_DA_PREINC: + temp = tcg_temp_new(); + tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); + gen_ld_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], temp, ctx); + tcg_gen_mov_tl(cpu_gpr_a[r2], temp); + tcg_temp_free(temp); break; - case OPC1_16_SSR_ST_H_POSTINC: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LEUW); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], 2); + case OPC2_32_BO_LD_H_SHORTOFF: + gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LESW); break; - case OPC1_16_SSR_ST_W: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LEUL); + case OPC2_32_BO_LD_H_POSTINC: + tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, + MO_LESW); + tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; - case OPC1_16_SSR_ST_W_POSTINC: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LEUL); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], 4); + case OPC2_32_BO_LD_H_PREINC: + gen_ld_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LESW); break; - } -} - -static void decode_sc_opc(DisasContext *ctx, int op1) -{ - int32_t const16; - - const16 = MASK_OP_SC_CONST8(ctx->opcode); - - switch (op1) { - case OPC1_16_SC_AND: - tcg_gen_andi_tl(cpu_gpr_d[15], cpu_gpr_d[15], const16); + case OPC2_32_BO_LD_HU_SHORTOFF: + gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUW); break; - case OPC1_16_SC_BISR: - gen_helper_1arg(bisr, const16 & 0xff); + case OPC2_32_BO_LD_HU_POSTINC: + tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, + MO_LEUW); + tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; - case OPC1_16_SC_LD_A: - gen_offset_ld(ctx, cpu_gpr_a[15], cpu_gpr_a[10], const16 * 4, MO_LESL); + case OPC2_32_BO_LD_HU_PREINC: + gen_ld_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUW); break; - case OPC1_16_SC_LD_W: - gen_offset_ld(ctx, cpu_gpr_d[15], cpu_gpr_a[10], const16 * 4, MO_LESL); + case OPC2_32_BO_LD_Q_SHORTOFF: + gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUW); + tcg_gen_shli_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], 16); break; - case OPC1_16_SC_MOV: - tcg_gen_movi_tl(cpu_gpr_d[15], const16); + case OPC2_32_BO_LD_Q_POSTINC: + tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, + MO_LEUW); + tcg_gen_shli_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], 16); + tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; - case OPC1_16_SC_OR: - tcg_gen_ori_tl(cpu_gpr_d[15], cpu_gpr_d[15], const16); + case OPC2_32_BO_LD_Q_PREINC: + gen_ld_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUW); + tcg_gen_shli_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], 16); break; - case OPC1_16_SC_ST_A: - gen_offset_st(ctx, cpu_gpr_a[15], cpu_gpr_a[10], const16 * 4, MO_LESL); + case OPC2_32_BO_LD_W_SHORTOFF: + gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUL); break; - case OPC1_16_SC_ST_W: - gen_offset_st(ctx, cpu_gpr_d[15], cpu_gpr_a[10], const16 * 4, MO_LESL); + case OPC2_32_BO_LD_W_POSTINC: + tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, + MO_LEUL); + tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; - case OPC1_16_SC_SUB_A: - tcg_gen_subi_tl(cpu_gpr_a[10], cpu_gpr_a[10], const16); + case OPC2_32_BO_LD_W_PREINC: + gen_ld_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUL); break; } } -static void decode_slr_opc(DisasContext *ctx, int op1) +static void decode_bo_addrmode_ld_bitreverse_circular(CPUTriCoreState *env, + DisasContext *ctx) { + uint32_t op2; + uint32_t off10; int r1, r2; - r1 = MASK_OP_SLR_D(ctx->opcode); - r2 = MASK_OP_SLR_S2(ctx->opcode); + TCGv temp, temp2, temp3; - switch (op1) { -/* SLR-format */ - case OPC1_16_SLR_LD_A: - tcg_gen_qemu_ld_tl(cpu_gpr_a[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LESL); + r1 = MASK_OP_BO_S1D(ctx->opcode); + r2 = MASK_OP_BO_S2(ctx->opcode); + off10 = MASK_OP_BO_OFF10_SEXT(ctx->opcode); + op2 = MASK_OP_BO_OP2(ctx->opcode); + + temp = tcg_temp_new(); + temp2 = tcg_temp_new(); + temp3 = tcg_const_i32(off10); + + tcg_gen_ext16u_tl(temp, cpu_gpr_a[r2+1]); + tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp); + + + switch (op2) { + case OPC2_32_BO_LD_A_BR: + tcg_gen_qemu_ld_tl(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL); + gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); break; - case OPC1_16_SLR_LD_A_POSTINC: - tcg_gen_qemu_ld_tl(cpu_gpr_a[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LESL); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], 4); + case OPC2_32_BO_LD_A_CIRC: + tcg_gen_qemu_ld_tl(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); break; - case OPC1_16_SLR_LD_BU: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_UB); + case OPC2_32_BO_LD_B_BR: + tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_SB); + gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); break; - case OPC1_16_SLR_LD_BU_POSTINC: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_UB); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], 1); + case OPC2_32_BO_LD_B_CIRC: + tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_SB); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); break; - case OPC1_16_SLR_LD_H: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LESW); + case OPC2_32_BO_LD_BU_BR: + tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_UB); + gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); break; - case OPC1_16_SLR_LD_H_POSTINC: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LESW); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], 2); + case OPC2_32_BO_LD_BU_CIRC: + tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_UB); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); break; - case OPC1_16_SLR_LD_W: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LESW); + case OPC2_32_BO_LD_D_BR: + gen_ld_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], temp2, ctx); + gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); break; - case OPC1_16_SLR_LD_W_POSTINC: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, MO_LESW); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], 4); + case OPC2_32_BO_LD_D_CIRC: + tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL); + tcg_gen_shri_tl(temp2, cpu_gpr_a[r2+1], 16); + tcg_gen_addi_tl(temp, temp, 4); + tcg_gen_rem_tl(temp, temp, temp2); + tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp); + tcg_gen_qemu_ld_tl(cpu_gpr_d[r1+1], temp2, ctx->mem_idx, MO_LEUL); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); break; - } -} - -static void decode_sro_opc(DisasContext *ctx, int op1) -{ - int r2; - int32_t address; - - r2 = MASK_OP_SRO_S2(ctx->opcode); - address = MASK_OP_SRO_OFF4(ctx->opcode); - -/* SRO-format */ - switch (op1) { - case OPC1_16_SRO_LD_A: - gen_offset_ld(ctx, cpu_gpr_a[15], cpu_gpr_a[r2], address * 4, MO_LESL); + case OPC2_32_BO_LD_DA_BR: + gen_ld_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], temp2, ctx); + gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); break; - case OPC1_16_SRO_LD_BU: - gen_offset_ld(ctx, cpu_gpr_d[15], cpu_gpr_a[r2], address, MO_UB); + case OPC2_32_BO_LD_DA_CIRC: + tcg_gen_qemu_ld_tl(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL); + tcg_gen_shri_tl(temp2, cpu_gpr_a[r2+1], 16); + tcg_gen_addi_tl(temp, temp, 4); + tcg_gen_rem_tl(temp, temp, temp2); + tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp); + tcg_gen_qemu_ld_tl(cpu_gpr_a[r1+1], temp2, ctx->mem_idx, MO_LEUL); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); break; - case OPC1_16_SRO_LD_H: - gen_offset_ld(ctx, cpu_gpr_d[15], cpu_gpr_a[r2], address, MO_LESW); + case OPC2_32_BO_LD_H_BR: + tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LESW); + gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); break; - case OPC1_16_SRO_LD_W: - gen_offset_ld(ctx, cpu_gpr_d[15], cpu_gpr_a[r2], address * 4, MO_LESL); + case OPC2_32_BO_LD_H_CIRC: + tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LESW); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); break; - case OPC1_16_SRO_ST_A: - gen_offset_st(ctx, cpu_gpr_a[15], cpu_gpr_a[r2], address * 4, MO_LESL); + case OPC2_32_BO_LD_HU_BR: + tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW); + gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); break; - case OPC1_16_SRO_ST_B: - gen_offset_st(ctx, cpu_gpr_d[15], cpu_gpr_a[r2], address, MO_UB); + case OPC2_32_BO_LD_HU_CIRC: + tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); break; - case OPC1_16_SRO_ST_H: - gen_offset_st(ctx, cpu_gpr_d[15], cpu_gpr_a[r2], address * 2, MO_LESW); + case OPC2_32_BO_LD_Q_BR: + tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW); + tcg_gen_shli_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], 16); + gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + break; + case OPC2_32_BO_LD_Q_CIRC: + tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW); + tcg_gen_shli_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], 16); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + break; + case OPC2_32_BO_LD_W_BR: + tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL); + gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); break; - case OPC1_16_SRO_ST_W: - gen_offset_st(ctx, cpu_gpr_d[15], cpu_gpr_a[r2], address * 4, MO_LESL); + case OPC2_32_BO_LD_W_CIRC: + tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); break; } + tcg_temp_free(temp); + tcg_temp_free(temp2); + tcg_temp_free(temp3); } -static void decode_sr_system(CPUTriCoreState *env, DisasContext *ctx) +static void decode_bo_addrmode_stctx_post_pre_base(CPUTriCoreState *env, + DisasContext *ctx) { uint32_t op2; - op2 = MASK_OP_SR_OP2(ctx->opcode); + uint32_t off10; + int r1, r2; + + TCGv temp, temp2; + + r1 = MASK_OP_BO_S1D(ctx->opcode); + r2 = MASK_OP_BO_S2(ctx->opcode); + off10 = MASK_OP_BO_OFF10_SEXT(ctx->opcode); + op2 = MASK_OP_BO_OP2(ctx->opcode); + + + temp = tcg_temp_new(); + temp2 = tcg_temp_new(); switch (op2) { - case OPC2_16_SR_NOP: + case OPC2_32_BO_LDLCX_SHORTOFF: + tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); + gen_helper_ldlcx(cpu_env, temp); break; - case OPC2_16_SR_RET: - gen_compute_branch(ctx, op2, 0, 0, 0, 0); + case OPC2_32_BO_LDMST_SHORTOFF: + tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); + gen_ldmst(ctx, r1, temp); break; - case OPC2_16_SR_RFE: - gen_helper_rfe(cpu_env); - tcg_gen_exit_tb(0); - ctx->bstate = BS_BRANCH; + case OPC2_32_BO_LDMST_POSTINC: + gen_ldmst(ctx, r1, cpu_gpr_a[r2]); + tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; - case OPC2_16_SR_DEBUG: - /* raise EXCP_DEBUG */ + case OPC2_32_BO_LDMST_PREINC: + tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + gen_ldmst(ctx, r1, cpu_gpr_a[r2]); break; - } -} - -static void decode_sr_accu(CPUTriCoreState *env, DisasContext *ctx) -{ - uint32_t op2; - uint32_t r1; - TCGv temp; - - r1 = MASK_OP_SR_S1D(ctx->opcode); - op2 = MASK_OP_SR_OP2(ctx->opcode); - - switch (op2) { - case OPC2_16_SR_RSUB: - /* overflow only if r1 = -0x80000000 */ - temp = tcg_const_i32(-0x80000000); - /* calc V bit */ - tcg_gen_setcond_tl(TCG_COND_EQ, cpu_PSW_V, cpu_gpr_d[r1], temp); - tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); - /* calc SV bit */ - tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); - /* sub */ - tcg_gen_neg_tl(cpu_gpr_d[r1], cpu_gpr_d[r1]); - /* calc av */ - tcg_gen_add_tl(cpu_PSW_AV, cpu_gpr_d[r1], cpu_gpr_d[r1]); - tcg_gen_xor_tl(cpu_PSW_AV, cpu_gpr_d[r1], cpu_PSW_AV); - /* calc sav */ - tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); - tcg_temp_free(temp); + case OPC2_32_BO_LDUCX_SHORTOFF: + tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); + gen_helper_lducx(cpu_env, temp); break; - case OPC2_16_SR_SAT_B: - gen_saturate(cpu_gpr_d[r1], cpu_gpr_d[r1], 0x7f, -0x80); + case OPC2_32_BO_LEA_SHORTOFF: + tcg_gen_addi_tl(cpu_gpr_a[r1], cpu_gpr_a[r2], off10); break; - case OPC2_16_SR_SAT_BU: - gen_saturate_u(cpu_gpr_d[r1], cpu_gpr_d[r1], 0xff); + case OPC2_32_BO_STLCX_SHORTOFF: + tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); + gen_helper_stlcx(cpu_env, temp); break; - case OPC2_16_SR_SAT_H: - gen_saturate(cpu_gpr_d[r1], cpu_gpr_d[r1], 0x7fff, -0x8000); + case OPC2_32_BO_STUCX_SHORTOFF: + tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); + gen_helper_stucx(cpu_env, temp); break; - case OPC2_16_SR_SAT_HU: - gen_saturate_u(cpu_gpr_d[r1], cpu_gpr_d[r1], 0xffff); + case OPC2_32_BO_SWAP_W_SHORTOFF: + tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); + gen_swap(ctx, r1, temp); + break; + case OPC2_32_BO_SWAP_W_POSTINC: + gen_swap(ctx, r1, cpu_gpr_a[r2]); + tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + break; + case OPC2_32_BO_SWAP_W_PREINC: + tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + gen_swap(ctx, r1, cpu_gpr_a[r2]); break; } + tcg_temp_free(temp); + tcg_temp_free(temp2); } -static void decode_16Bit_opc(CPUTriCoreState *env, DisasContext *ctx) +static void decode_bo_addrmode_ldmst_bitreverse_circular(CPUTriCoreState *env, + DisasContext *ctx) { - int op1; + uint32_t op2; + uint32_t off10; int r1, r2; - int32_t const16; - int32_t address; - TCGv temp; - op1 = MASK_OP_MAJOR(ctx->opcode); + TCGv temp, temp2, temp3; - /* handle ADDSC.A opcode only being 6 bit long */ - if (unlikely((op1 & 0x3f) == OPC1_16_SRRS_ADDSC_A)) { - op1 = OPC1_16_SRRS_ADDSC_A; - } + r1 = MASK_OP_BO_S1D(ctx->opcode); + r2 = MASK_OP_BO_S2(ctx->opcode); + off10 = MASK_OP_BO_OFF10_SEXT(ctx->opcode); + op2 = MASK_OP_BO_OP2(ctx->opcode); - switch (op1) { - case OPC1_16_SRC_ADD: - case OPC1_16_SRC_ADD_A15: - case OPC1_16_SRC_ADD_15A: - case OPC1_16_SRC_ADD_A: - case OPC1_16_SRC_CADD: - case OPC1_16_SRC_CADDN: - case OPC1_16_SRC_CMOV: - case OPC1_16_SRC_CMOVN: - case OPC1_16_SRC_EQ: - case OPC1_16_SRC_LT: - case OPC1_16_SRC_MOV: - case OPC1_16_SRC_MOV_A: - case OPC1_16_SRC_SH: - case OPC1_16_SRC_SHA: - decode_src_opc(ctx, op1); - break; -/* SRR-format */ - case OPC1_16_SRR_ADD: - case OPC1_16_SRR_ADD_A15: - case OPC1_16_SRR_ADD_15A: - case OPC1_16_SRR_ADD_A: - case OPC1_16_SRR_ADDS: - case OPC1_16_SRR_AND: - case OPC1_16_SRR_CMOV: - case OPC1_16_SRR_CMOVN: - case OPC1_16_SRR_EQ: - case OPC1_16_SRR_LT: - case OPC1_16_SRR_MOV: - case OPC1_16_SRR_MOV_A: - case OPC1_16_SRR_MOV_AA: - case OPC1_16_SRR_MOV_D: - case OPC1_16_SRR_MUL: - case OPC1_16_SRR_OR: - case OPC1_16_SRR_SUB: - case OPC1_16_SRR_SUB_A15B: - case OPC1_16_SRR_SUB_15AB: - case OPC1_16_SRR_SUBS: - case OPC1_16_SRR_XOR: - decode_srr_opc(ctx, op1); - break; -/* SSR-format */ - case OPC1_16_SSR_ST_A: - case OPC1_16_SSR_ST_A_POSTINC: - case OPC1_16_SSR_ST_B: - case OPC1_16_SSR_ST_B_POSTINC: - case OPC1_16_SSR_ST_H: - case OPC1_16_SSR_ST_H_POSTINC: - case OPC1_16_SSR_ST_W: - case OPC1_16_SSR_ST_W_POSTINC: - decode_ssr_opc(ctx, op1); - break; -/* SRRS-format */ - case OPC1_16_SRRS_ADDSC_A: - r2 = MASK_OP_SRRS_S2(ctx->opcode); - r1 = MASK_OP_SRRS_S1D(ctx->opcode); - const16 = MASK_OP_SRRS_N(ctx->opcode); - temp = tcg_temp_new(); - tcg_gen_shli_tl(temp, cpu_gpr_d[15], const16); - tcg_gen_add_tl(cpu_gpr_a[r1], cpu_gpr_a[r2], temp); - tcg_temp_free(temp); - break; -/* SLRO-format */ - case OPC1_16_SLRO_LD_A: - r1 = MASK_OP_SLRO_D(ctx->opcode); - const16 = MASK_OP_SLRO_OFF4(ctx->opcode); - gen_offset_ld(ctx, cpu_gpr_a[r1], cpu_gpr_a[15], const16 * 4, MO_LESL); - break; - case OPC1_16_SLRO_LD_BU: - r1 = MASK_OP_SLRO_D(ctx->opcode); - const16 = MASK_OP_SLRO_OFF4(ctx->opcode); - gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[15], const16, MO_UB); - break; - case OPC1_16_SLRO_LD_H: - r1 = MASK_OP_SLRO_D(ctx->opcode); - const16 = MASK_OP_SLRO_OFF4(ctx->opcode); - gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[15], const16 * 2, MO_LESW); - break; - case OPC1_16_SLRO_LD_W: - r1 = MASK_OP_SLRO_D(ctx->opcode); - const16 = MASK_OP_SLRO_OFF4(ctx->opcode); - gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[15], const16 * 4, MO_LESL); - break; -/* SB-format */ - case OPC1_16_SB_CALL: - case OPC1_16_SB_J: - case OPC1_16_SB_JNZ: - case OPC1_16_SB_JZ: - address = MASK_OP_SB_DISP8_SEXT(ctx->opcode); - gen_compute_branch(ctx, op1, 0, 0, 0, address); - break; -/* SBC-format */ - case OPC1_16_SBC_JEQ: - case OPC1_16_SBC_JNE: - address = MASK_OP_SBC_DISP4(ctx->opcode); - const16 = MASK_OP_SBC_CONST4_SEXT(ctx->opcode); - gen_compute_branch(ctx, op1, 0, 0, const16, address); - break; -/* SBRN-format */ - case OPC1_16_SBRN_JNZ_T: - case OPC1_16_SBRN_JZ_T: - address = MASK_OP_SBRN_DISP4(ctx->opcode); - const16 = MASK_OP_SBRN_N(ctx->opcode); - gen_compute_branch(ctx, op1, 0, 0, const16, address); + temp = tcg_temp_new(); + temp2 = tcg_temp_new(); + temp3 = tcg_const_i32(off10); + + tcg_gen_ext16u_tl(temp, cpu_gpr_a[r2+1]); + tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp); + + switch (op2) { + case OPC2_32_BO_LDMST_BR: + gen_ldmst(ctx, r1, temp2); + gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); break; -/* SBR-format */ - case OPC1_16_SBR_JEQ: - case OPC1_16_SBR_JGEZ: - case OPC1_16_SBR_JGTZ: - case OPC1_16_SBR_JLEZ: - case OPC1_16_SBR_JLTZ: - case OPC1_16_SBR_JNE: - case OPC1_16_SBR_JNZ: - case OPC1_16_SBR_JNZ_A: - case OPC1_16_SBR_JZ: - case OPC1_16_SBR_JZ_A: - case OPC1_16_SBR_LOOP: - r1 = MASK_OP_SBR_S2(ctx->opcode); - address = MASK_OP_SBR_DISP4(ctx->opcode); - gen_compute_branch(ctx, op1, r1, 0, 0, address); + case OPC2_32_BO_LDMST_CIRC: + gen_ldmst(ctx, r1, temp2); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); break; -/* SC-format */ - case OPC1_16_SC_AND: - case OPC1_16_SC_BISR: - case OPC1_16_SC_LD_A: - case OPC1_16_SC_LD_W: - case OPC1_16_SC_MOV: - case OPC1_16_SC_OR: - case OPC1_16_SC_ST_A: - case OPC1_16_SC_ST_W: - case OPC1_16_SC_SUB_A: - decode_sc_opc(ctx, op1); + case OPC2_32_BO_SWAP_W_BR: + gen_swap(ctx, r1, temp2); + gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); break; -/* SLR-format */ - case OPC1_16_SLR_LD_A: - case OPC1_16_SLR_LD_A_POSTINC: - case OPC1_16_SLR_LD_BU: - case OPC1_16_SLR_LD_BU_POSTINC: - case OPC1_16_SLR_LD_H: - case OPC1_16_SLR_LD_H_POSTINC: - case OPC1_16_SLR_LD_W: - case OPC1_16_SLR_LD_W_POSTINC: - decode_slr_opc(ctx, op1); + case OPC2_32_BO_SWAP_W_CIRC: + gen_swap(ctx, r1, temp2); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); break; -/* SRO-format */ - case OPC1_16_SRO_LD_A: - case OPC1_16_SRO_LD_BU: - case OPC1_16_SRO_LD_H: - case OPC1_16_SRO_LD_W: - case OPC1_16_SRO_ST_A: - case OPC1_16_SRO_ST_B: - case OPC1_16_SRO_ST_H: - case OPC1_16_SRO_ST_W: - decode_sro_opc(ctx, op1); + } + tcg_temp_free(temp); + tcg_temp_free(temp2); + tcg_temp_free(temp3); +} + +static void decode_bol_opc(CPUTriCoreState *env, DisasContext *ctx, int32_t op1) +{ + int r1, r2; + int32_t address; + TCGv temp; + + r1 = MASK_OP_BOL_S1D(ctx->opcode); + r2 = MASK_OP_BOL_S2(ctx->opcode); + address = MASK_OP_BOL_OFF16_SEXT(ctx->opcode); + + switch (op1) { + case OPC1_32_BOL_LD_A_LONGOFF: + temp = tcg_temp_new(); + tcg_gen_addi_tl(temp, cpu_gpr_a[r2], address); + tcg_gen_qemu_ld_tl(cpu_gpr_a[r1], temp, ctx->mem_idx, MO_LEUL); + tcg_temp_free(temp); break; -/* SSRO-format */ - case OPC1_16_SSRO_ST_A: - r1 = MASK_OP_SSRO_S1(ctx->opcode); - const16 = MASK_OP_SSRO_OFF4(ctx->opcode); - gen_offset_st(ctx, cpu_gpr_a[r1], cpu_gpr_a[15], const16 * 4, MO_LESL); + case OPC1_32_BOL_LD_W_LONGOFF: + temp = tcg_temp_new(); + tcg_gen_addi_tl(temp, cpu_gpr_a[r2], address); + tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LEUL); + tcg_temp_free(temp); break; - case OPC1_16_SSRO_ST_B: - r1 = MASK_OP_SSRO_S1(ctx->opcode); - const16 = MASK_OP_SSRO_OFF4(ctx->opcode); - gen_offset_st(ctx, cpu_gpr_d[r1], cpu_gpr_a[15], const16, MO_UB); + case OPC1_32_BOL_LEA_LONGOFF: + tcg_gen_addi_tl(cpu_gpr_a[r1], cpu_gpr_a[r2], address); break; - case OPC1_16_SSRO_ST_H: - r1 = MASK_OP_SSRO_S1(ctx->opcode); - const16 = MASK_OP_SSRO_OFF4(ctx->opcode); - gen_offset_st(ctx, cpu_gpr_d[r1], cpu_gpr_a[15], const16 * 2, MO_LESW); + case OPC1_32_BOL_ST_A_LONGOFF: + if (tricore_feature(env, TRICORE_FEATURE_16)) { + gen_offset_st(ctx, cpu_gpr_a[r1], cpu_gpr_a[r2], address, MO_LEUL); + } else { + /* raise illegal opcode trap */ + } break; - case OPC1_16_SSRO_ST_W: - r1 = MASK_OP_SSRO_S1(ctx->opcode); - const16 = MASK_OP_SSRO_OFF4(ctx->opcode); - gen_offset_st(ctx, cpu_gpr_d[r1], cpu_gpr_a[15], const16 * 4, MO_LESL); + case OPC1_32_BOL_ST_W_LONGOFF: + gen_offset_st(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], address, MO_LEUL); break; -/* SR-format */ - case OPCM_16_SR_SYSTEM: - decode_sr_system(env, ctx); + case OPC1_32_BOL_LD_B_LONGOFF: + if (tricore_feature(env, TRICORE_FEATURE_16)) { + gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], address, MO_SB); + } else { + /* raise illegal opcode trap */ + } break; - case OPCM_16_SR_ACCU: - decode_sr_accu(env, ctx); + case OPC1_32_BOL_LD_BU_LONGOFF: + if (tricore_feature(env, TRICORE_FEATURE_16)) { + gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], address, MO_UB); + } else { + /* raise illegal opcode trap */ + } break; - case OPC1_16_SR_JI: - r1 = MASK_OP_SR_S1D(ctx->opcode); - gen_compute_branch(ctx, op1, r1, 0, 0, 0); + case OPC1_32_BOL_LD_H_LONGOFF: + if (tricore_feature(env, TRICORE_FEATURE_16)) { + gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], address, MO_LESW); + } else { + /* raise illegal opcode trap */ + } break; - case OPC1_16_SR_NOT: - r1 = MASK_OP_SR_S1D(ctx->opcode); - tcg_gen_not_tl(cpu_gpr_d[r1], cpu_gpr_d[r1]); + case OPC1_32_BOL_LD_HU_LONGOFF: + if (tricore_feature(env, TRICORE_FEATURE_16)) { + gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], address, MO_LEUW); + } else { + /* raise illegal opcode trap */ + } + break; + case OPC1_32_BOL_ST_B_LONGOFF: + if (tricore_feature(env, TRICORE_FEATURE_16)) { + gen_offset_st(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], address, MO_SB); + } else { + /* raise illegal opcode trap */ + } + break; + case OPC1_32_BOL_ST_H_LONGOFF: + if (tricore_feature(env, TRICORE_FEATURE_16)) { + gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], address, MO_LESW); + } else { + /* raise illegal opcode trap */ + } break; } } -/* - * 32 bit instructions - */ - -/* ABS-format */ -static void decode_abs_ldw(CPUTriCoreState *env, DisasContext *ctx) +/* RC format */ +static void decode_rc_logical_shift(CPUTriCoreState *env, DisasContext *ctx) { - int32_t op2; - int32_t r1; - uint32_t address; + uint32_t op2; + int r1, r2; + int32_t const9; TCGv temp; - r1 = MASK_OP_ABS_S1D(ctx->opcode); - address = MASK_OP_ABS_OFF18(ctx->opcode); - op2 = MASK_OP_ABS_OP2(ctx->opcode); + r2 = MASK_OP_RC_D(ctx->opcode); + r1 = MASK_OP_RC_S1(ctx->opcode); + const9 = MASK_OP_RC_CONST9(ctx->opcode); + op2 = MASK_OP_RC_OP2(ctx->opcode); - temp = tcg_const_i32(EA_ABS_FORMAT(address)); + temp = tcg_temp_new(); switch (op2) { - case OPC2_32_ABS_LD_A: - tcg_gen_qemu_ld_tl(cpu_gpr_a[r1], temp, ctx->mem_idx, MO_LESL); + case OPC2_32_RC_AND: + tcg_gen_andi_tl(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); break; - case OPC2_32_ABS_LD_D: - gen_ld_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], temp, ctx); + case OPC2_32_RC_ANDN: + tcg_gen_andi_tl(cpu_gpr_d[r2], cpu_gpr_d[r1], ~const9); break; - case OPC2_32_ABS_LD_DA: - gen_ld_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], temp, ctx); + case OPC2_32_RC_NAND: + tcg_gen_movi_tl(temp, const9); + tcg_gen_nand_tl(cpu_gpr_d[r2], cpu_gpr_d[r1], temp); break; - case OPC2_32_ABS_LD_W: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LESL); + case OPC2_32_RC_NOR: + tcg_gen_movi_tl(temp, const9); + tcg_gen_nor_tl(cpu_gpr_d[r2], cpu_gpr_d[r1], temp); + break; + case OPC2_32_RC_OR: + tcg_gen_ori_tl(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + break; + case OPC2_32_RC_ORN: + tcg_gen_ori_tl(cpu_gpr_d[r2], cpu_gpr_d[r1], ~const9); + break; + case OPC2_32_RC_SH: + const9 = sextract32(const9, 0, 6); + gen_shi(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + break; + case OPC2_32_RC_SH_H: + const9 = sextract32(const9, 0, 5); + gen_sh_hi(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + break; + case OPC2_32_RC_SHA: + const9 = sextract32(const9, 0, 6); + gen_shaci(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + break; + case OPC2_32_RC_SHA_H: + const9 = sextract32(const9, 0, 5); + gen_sha_hi(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + break; + case OPC2_32_RC_SHAS: + gen_shasi(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + break; + case OPC2_32_RC_XNOR: + tcg_gen_xori_tl(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + tcg_gen_not_tl(cpu_gpr_d[r2], cpu_gpr_d[r2]); + break; + case OPC2_32_RC_XOR: + tcg_gen_xori_tl(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); break; } - tcg_temp_free(temp); } -static void decode_abs_ldb(CPUTriCoreState *env, DisasContext *ctx) +static void decode_rc_accumulator(CPUTriCoreState *env, DisasContext *ctx) { - int32_t op2; - int32_t r1; - uint32_t address; + uint32_t op2; + int r1, r2; + int16_t const9; + TCGv temp; - r1 = MASK_OP_ABS_S1D(ctx->opcode); - address = MASK_OP_ABS_OFF18(ctx->opcode); - op2 = MASK_OP_ABS_OP2(ctx->opcode); + r2 = MASK_OP_RC_D(ctx->opcode); + r1 = MASK_OP_RC_S1(ctx->opcode); + const9 = MASK_OP_RC_CONST9_SEXT(ctx->opcode); - temp = tcg_const_i32(EA_ABS_FORMAT(address)); + op2 = MASK_OP_RC_OP2(ctx->opcode); + + temp = tcg_temp_new(); switch (op2) { - case OPC2_32_ABS_LD_B: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_SB); + case OPC2_32_RC_ABSDIF: + gen_absdifi(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); break; - case OPC2_32_ABS_LD_BU: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_UB); + case OPC2_32_RC_ABSDIFS: + gen_absdifsi(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); break; - case OPC2_32_ABS_LD_H: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LESW); + case OPC2_32_RC_ADD: + gen_addi_d(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); break; - case OPC2_32_ABS_LD_HU: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LEUW); + case OPC2_32_RC_ADDC: + gen_addci_CC(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + break; + case OPC2_32_RC_ADDS: + gen_addsi(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + break; + case OPC2_32_RC_ADDS_U: + gen_addsui(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + break; + case OPC2_32_RC_ADDX: + gen_addi_CC(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + break; + case OPC2_32_RC_AND_EQ: + gen_accumulating_condi(TCG_COND_EQ, cpu_gpr_d[r2], cpu_gpr_d[r1], + const9, &tcg_gen_and_tl); + break; + case OPC2_32_RC_AND_GE: + gen_accumulating_condi(TCG_COND_GE, cpu_gpr_d[r2], cpu_gpr_d[r1], + const9, &tcg_gen_and_tl); + break; + case OPC2_32_RC_AND_GE_U: + const9 = MASK_OP_RC_CONST9(ctx->opcode); + gen_accumulating_condi(TCG_COND_GEU, cpu_gpr_d[r2], cpu_gpr_d[r1], + const9, &tcg_gen_and_tl); + break; + case OPC2_32_RC_AND_LT: + gen_accumulating_condi(TCG_COND_LT, cpu_gpr_d[r2], cpu_gpr_d[r1], + const9, &tcg_gen_and_tl); + break; + case OPC2_32_RC_AND_LT_U: + const9 = MASK_OP_RC_CONST9(ctx->opcode); + gen_accumulating_condi(TCG_COND_LTU, cpu_gpr_d[r2], cpu_gpr_d[r1], + const9, &tcg_gen_and_tl); + break; + case OPC2_32_RC_AND_NE: + gen_accumulating_condi(TCG_COND_NE, cpu_gpr_d[r2], cpu_gpr_d[r1], + const9, &tcg_gen_and_tl); + break; + case OPC2_32_RC_EQ: + tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + break; + case OPC2_32_RC_EQANY_B: + gen_eqany_bi(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + break; + case OPC2_32_RC_EQANY_H: + gen_eqany_hi(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + break; + case OPC2_32_RC_GE: + tcg_gen_setcondi_tl(TCG_COND_GE, cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + break; + case OPC2_32_RC_GE_U: + const9 = MASK_OP_RC_CONST9(ctx->opcode); + tcg_gen_setcondi_tl(TCG_COND_GEU, cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + break; + case OPC2_32_RC_LT: + tcg_gen_setcondi_tl(TCG_COND_LT, cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + break; + case OPC2_32_RC_LT_U: + const9 = MASK_OP_RC_CONST9(ctx->opcode); + tcg_gen_setcondi_tl(TCG_COND_LTU, cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + break; + case OPC2_32_RC_MAX: + tcg_gen_movi_tl(temp, const9); + tcg_gen_movcond_tl(TCG_COND_GT, cpu_gpr_d[r2], cpu_gpr_d[r1], temp, + cpu_gpr_d[r1], temp); + break; + case OPC2_32_RC_MAX_U: + tcg_gen_movi_tl(temp, MASK_OP_RC_CONST9(ctx->opcode)); + tcg_gen_movcond_tl(TCG_COND_GTU, cpu_gpr_d[r2], cpu_gpr_d[r1], temp, + cpu_gpr_d[r1], temp); + break; + case OPC2_32_RC_MIN: + tcg_gen_movi_tl(temp, const9); + tcg_gen_movcond_tl(TCG_COND_LT, cpu_gpr_d[r2], cpu_gpr_d[r1], temp, + cpu_gpr_d[r1], temp); + break; + case OPC2_32_RC_MIN_U: + tcg_gen_movi_tl(temp, MASK_OP_RC_CONST9(ctx->opcode)); + tcg_gen_movcond_tl(TCG_COND_LTU, cpu_gpr_d[r2], cpu_gpr_d[r1], temp, + cpu_gpr_d[r1], temp); + break; + case OPC2_32_RC_NE: + tcg_gen_setcondi_tl(TCG_COND_NE, cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + break; + case OPC2_32_RC_OR_EQ: + gen_accumulating_condi(TCG_COND_EQ, cpu_gpr_d[r2], cpu_gpr_d[r1], + const9, &tcg_gen_or_tl); + break; + case OPC2_32_RC_OR_GE: + gen_accumulating_condi(TCG_COND_GE, cpu_gpr_d[r2], cpu_gpr_d[r1], + const9, &tcg_gen_or_tl); + break; + case OPC2_32_RC_OR_GE_U: + const9 = MASK_OP_RC_CONST9(ctx->opcode); + gen_accumulating_condi(TCG_COND_GEU, cpu_gpr_d[r2], cpu_gpr_d[r1], + const9, &tcg_gen_or_tl); + break; + case OPC2_32_RC_OR_LT: + gen_accumulating_condi(TCG_COND_LT, cpu_gpr_d[r2], cpu_gpr_d[r1], + const9, &tcg_gen_or_tl); + break; + case OPC2_32_RC_OR_LT_U: + const9 = MASK_OP_RC_CONST9(ctx->opcode); + gen_accumulating_condi(TCG_COND_LTU, cpu_gpr_d[r2], cpu_gpr_d[r1], + const9, &tcg_gen_or_tl); + break; + case OPC2_32_RC_OR_NE: + gen_accumulating_condi(TCG_COND_NE, cpu_gpr_d[r2], cpu_gpr_d[r1], + const9, &tcg_gen_or_tl); + break; + case OPC2_32_RC_RSUB: + tcg_gen_movi_tl(temp, const9); + gen_sub_d(cpu_gpr_d[r2], temp, cpu_gpr_d[r1]); + break; + case OPC2_32_RC_RSUBS: + tcg_gen_movi_tl(temp, const9); + gen_subs(cpu_gpr_d[r2], temp, cpu_gpr_d[r1]); + break; + case OPC2_32_RC_RSUBS_U: + tcg_gen_movi_tl(temp, const9); + gen_subsu(cpu_gpr_d[r2], temp, cpu_gpr_d[r1]); + break; + case OPC2_32_RC_SH_EQ: + gen_sh_condi(TCG_COND_EQ, cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + break; + case OPC2_32_RC_SH_GE: + gen_sh_condi(TCG_COND_GE, cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + break; + case OPC2_32_RC_SH_GE_U: + const9 = MASK_OP_RC_CONST9(ctx->opcode); + gen_sh_condi(TCG_COND_GEU, cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + break; + case OPC2_32_RC_SH_LT: + gen_sh_condi(TCG_COND_LT, cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + break; + case OPC2_32_RC_SH_LT_U: + const9 = MASK_OP_RC_CONST9(ctx->opcode); + gen_sh_condi(TCG_COND_LTU, cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + break; + case OPC2_32_RC_SH_NE: + gen_sh_condi(TCG_COND_NE, cpu_gpr_d[r2], cpu_gpr_d[r1], const9); + break; + case OPC2_32_RC_XOR_EQ: + gen_accumulating_condi(TCG_COND_EQ, cpu_gpr_d[r2], cpu_gpr_d[r1], + const9, &tcg_gen_xor_tl); + break; + case OPC2_32_RC_XOR_GE: + gen_accumulating_condi(TCG_COND_GE, cpu_gpr_d[r2], cpu_gpr_d[r1], + const9, &tcg_gen_xor_tl); + break; + case OPC2_32_RC_XOR_GE_U: + const9 = MASK_OP_RC_CONST9(ctx->opcode); + gen_accumulating_condi(TCG_COND_GEU, cpu_gpr_d[r2], cpu_gpr_d[r1], + const9, &tcg_gen_xor_tl); + break; + case OPC2_32_RC_XOR_LT: + gen_accumulating_condi(TCG_COND_LT, cpu_gpr_d[r2], cpu_gpr_d[r1], + const9, &tcg_gen_xor_tl); + break; + case OPC2_32_RC_XOR_LT_U: + const9 = MASK_OP_RC_CONST9(ctx->opcode); + gen_accumulating_condi(TCG_COND_LTU, cpu_gpr_d[r2], cpu_gpr_d[r1], + const9, &tcg_gen_xor_tl); + break; + case OPC2_32_RC_XOR_NE: + gen_accumulating_condi(TCG_COND_NE, cpu_gpr_d[r2], cpu_gpr_d[r1], + const9, &tcg_gen_xor_tl); break; } - tcg_temp_free(temp); } -static void decode_abs_ldst_swap(CPUTriCoreState *env, DisasContext *ctx) +static void decode_rc_serviceroutine(CPUTriCoreState *env, DisasContext *ctx) { - int32_t op2; - int32_t r1; - uint32_t address; - TCGv temp; - - r1 = MASK_OP_ABS_S1D(ctx->opcode); - address = MASK_OP_ABS_OFF18(ctx->opcode); - op2 = MASK_OP_ABS_OP2(ctx->opcode); + uint32_t op2; + uint32_t const9; - temp = tcg_const_i32(EA_ABS_FORMAT(address)); + op2 = MASK_OP_RC_OP2(ctx->opcode); + const9 = MASK_OP_RC_CONST9(ctx->opcode); switch (op2) { - case OPC2_32_ABS_LDMST: - gen_ldmst(ctx, r1, temp); + case OPC2_32_RC_BISR: + gen_helper_1arg(bisr, const9); break; - case OPC2_32_ABS_SWAP_W: - gen_swap(ctx, r1, temp); + case OPC2_32_RC_SYSCALL: + /* TODO: Add exception generation */ break; } - - tcg_temp_free(temp); } -static void decode_abs_ldst_context(CPUTriCoreState *env, DisasContext *ctx) +static void decode_rc_mul(CPUTriCoreState *env, DisasContext *ctx) { uint32_t op2; - int32_t off18; + int r1, r2; + int16_t const9; - off18 = MASK_OP_ABS_OFF18(ctx->opcode); - op2 = MASK_OP_ABS_OP2(ctx->opcode); + r2 = MASK_OP_RC_D(ctx->opcode); + r1 = MASK_OP_RC_S1(ctx->opcode); + const9 = MASK_OP_RC_CONST9_SEXT(ctx->opcode); + + op2 = MASK_OP_RC_OP2(ctx->opcode); switch (op2) { - case OPC2_32_ABS_LDLCX: - gen_helper_1arg(ldlcx, EA_ABS_FORMAT(off18)); + case OPC2_32_RC_MUL_32: + gen_muli_i32s(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); break; - case OPC2_32_ABS_LDUCX: - gen_helper_1arg(lducx, EA_ABS_FORMAT(off18)); + case OPC2_32_RC_MUL_64: + gen_muli_i64s(cpu_gpr_d[r2], cpu_gpr_d[r2+1], cpu_gpr_d[r1], const9); break; - case OPC2_32_ABS_STLCX: - gen_helper_1arg(stlcx, EA_ABS_FORMAT(off18)); + case OPC2_32_RC_MULS_32: + gen_mulsi_i32(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); break; - case OPC2_32_ABS_STUCX: - gen_helper_1arg(stucx, EA_ABS_FORMAT(off18)); + case OPC2_32_RC_MUL_U_64: + const9 = MASK_OP_RC_CONST9(ctx->opcode); + gen_muli_i64u(cpu_gpr_d[r2], cpu_gpr_d[r2+1], cpu_gpr_d[r1], const9); + break; + case OPC2_32_RC_MULS_U_32: + const9 = MASK_OP_RC_CONST9(ctx->opcode); + gen_mulsui_i32(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); break; } } -static void decode_abs_store(CPUTriCoreState *env, DisasContext *ctx) +/* RCPW format */ +static void decode_rcpw_insert(CPUTriCoreState *env, DisasContext *ctx) { - int32_t op2; - int32_t r1; - uint32_t address; - TCGv temp; + uint32_t op2; + int r1, r2; + int32_t pos, width, const4; - r1 = MASK_OP_ABS_S1D(ctx->opcode); - address = MASK_OP_ABS_OFF18(ctx->opcode); - op2 = MASK_OP_ABS_OP2(ctx->opcode); + TCGv temp; - temp = tcg_const_i32(EA_ABS_FORMAT(address)); + op2 = MASK_OP_RCPW_OP2(ctx->opcode); + r1 = MASK_OP_RCPW_S1(ctx->opcode); + r2 = MASK_OP_RCPW_D(ctx->opcode); + const4 = MASK_OP_RCPW_CONST4(ctx->opcode); + width = MASK_OP_RCPW_WIDTH(ctx->opcode); + pos = MASK_OP_RCPW_POS(ctx->opcode); switch (op2) { - case OPC2_32_ABS_ST_A: - tcg_gen_qemu_st_tl(cpu_gpr_a[r1], temp, ctx->mem_idx, MO_LESL); - break; - case OPC2_32_ABS_ST_D: - gen_st_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], temp, ctx); - break; - case OPC2_32_ABS_ST_DA: - gen_st_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], temp, ctx); + case OPC2_32_RCPW_IMASK: + /* if pos + width > 31 undefined result */ + if (pos + width <= 31) { + tcg_gen_movi_tl(cpu_gpr_d[r2+1], ((1u << width) - 1) << pos); + tcg_gen_movi_tl(cpu_gpr_d[r2], (const4 << pos)); + } break; - case OPC2_32_ABS_ST_W: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LESL); + case OPC2_32_RCPW_INSERT: + /* if pos + width > 32 undefined result */ + if (pos + width <= 32) { + temp = tcg_const_i32(const4); + tcg_gen_deposit_tl(cpu_gpr_d[r2], cpu_gpr_d[r1], temp, pos, width); + tcg_temp_free(temp); + } break; - } - tcg_temp_free(temp); } -static void decode_abs_storeb_h(CPUTriCoreState *env, DisasContext *ctx) +/* RCRW format */ + +static void decode_rcrw_insert(CPUTriCoreState *env, DisasContext *ctx) { - int32_t op2; - int32_t r1; - uint32_t address; - TCGv temp; + uint32_t op2; + int r1, r3, r4; + int32_t width, const4; - r1 = MASK_OP_ABS_S1D(ctx->opcode); - address = MASK_OP_ABS_OFF18(ctx->opcode); - op2 = MASK_OP_ABS_OP2(ctx->opcode); + TCGv temp, temp2, temp3; - temp = tcg_const_i32(EA_ABS_FORMAT(address)); + op2 = MASK_OP_RCRW_OP2(ctx->opcode); + r1 = MASK_OP_RCRW_S1(ctx->opcode); + r3 = MASK_OP_RCRW_S3(ctx->opcode); + r4 = MASK_OP_RCRW_D(ctx->opcode); + width = MASK_OP_RCRW_WIDTH(ctx->opcode); + const4 = MASK_OP_RCRW_CONST4(ctx->opcode); + + temp = tcg_temp_new(); + temp2 = tcg_temp_new(); switch (op2) { - case OPC2_32_ABS_ST_B: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_UB); + case OPC2_32_RCRW_IMASK: + tcg_gen_andi_tl(temp, cpu_gpr_d[r4], 0x1f); + tcg_gen_movi_tl(temp2, (1 << width) - 1); + tcg_gen_shl_tl(cpu_gpr_d[r3 + 1], temp2, temp); + tcg_gen_movi_tl(temp2, const4); + tcg_gen_shl_tl(cpu_gpr_d[r3], temp2, temp); break; - case OPC2_32_ABS_ST_H: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LEUW); + case OPC2_32_RCRW_INSERT: + temp3 = tcg_temp_new(); + + tcg_gen_movi_tl(temp, width); + tcg_gen_movi_tl(temp2, const4); + tcg_gen_andi_tl(temp3, cpu_gpr_d[r4], 0x1f); + gen_insert(cpu_gpr_d[r3], cpu_gpr_d[r1], temp2, temp, temp3); + + tcg_temp_free(temp3); break; } tcg_temp_free(temp); + tcg_temp_free(temp2); } -/* Bit-format */ +/* RCR format */ -static void decode_bit_andacc(CPUTriCoreState *env, DisasContext *ctx) +static void decode_rcr_cond_select(CPUTriCoreState *env, DisasContext *ctx) { uint32_t op2; - int r1, r2, r3; - int pos1, pos2; + int r1, r3, r4; + int32_t const9; - r1 = MASK_OP_BIT_S1(ctx->opcode); - r2 = MASK_OP_BIT_S2(ctx->opcode); - r3 = MASK_OP_BIT_D(ctx->opcode); - pos1 = MASK_OP_BIT_POS1(ctx->opcode); - pos2 = MASK_OP_BIT_POS2(ctx->opcode); - op2 = MASK_OP_BIT_OP2(ctx->opcode); + TCGv temp, temp2; + op2 = MASK_OP_RCR_OP2(ctx->opcode); + r1 = MASK_OP_RCR_S1(ctx->opcode); + const9 = MASK_OP_RCR_CONST9_SEXT(ctx->opcode); + r3 = MASK_OP_RCR_S3(ctx->opcode); + r4 = MASK_OP_RCR_D(ctx->opcode); switch (op2) { - case OPC2_32_BIT_AND_AND_T: - gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], - pos1, pos2, &tcg_gen_and_tl, &tcg_gen_and_tl); - break; - case OPC2_32_BIT_AND_ANDN_T: - gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], - pos1, pos2, &tcg_gen_andc_tl, &tcg_gen_and_tl); - break; - case OPC2_32_BIT_AND_NOR_T: - if (TCG_TARGET_HAS_andc_i32) { - gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], - pos1, pos2, &tcg_gen_or_tl, &tcg_gen_andc_tl); - } else { - gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], - pos1, pos2, &tcg_gen_nor_tl, &tcg_gen_and_tl); - } + case OPC2_32_RCR_CADD: + gen_condi_add(TCG_COND_NE, cpu_gpr_d[r1], const9, cpu_gpr_d[r3], + cpu_gpr_d[r4]); + break; + case OPC2_32_RCR_CADDN: + gen_condi_add(TCG_COND_EQ, cpu_gpr_d[r1], const9, cpu_gpr_d[r3], + cpu_gpr_d[r4]); + break; + case OPC2_32_RCR_SEL: + temp = tcg_const_i32(0); + temp2 = tcg_const_i32(const9); + tcg_gen_movcond_tl(TCG_COND_NE, cpu_gpr_d[r4], cpu_gpr_d[r3], temp, + cpu_gpr_d[r1], temp2); + tcg_temp_free(temp); + tcg_temp_free(temp2); break; - case OPC2_32_BIT_AND_OR_T: - gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], - pos1, pos2, &tcg_gen_or_tl, &tcg_gen_and_tl); + case OPC2_32_RCR_SELN: + temp = tcg_const_i32(0); + temp2 = tcg_const_i32(const9); + tcg_gen_movcond_tl(TCG_COND_EQ, cpu_gpr_d[r4], cpu_gpr_d[r3], temp, + cpu_gpr_d[r1], temp2); + tcg_temp_free(temp); + tcg_temp_free(temp2); break; } } -static void decode_bit_logical_t(CPUTriCoreState *env, DisasContext *ctx) +static void decode_rcr_madd(CPUTriCoreState *env, DisasContext *ctx) { uint32_t op2; - int r1, r2, r3; - int pos1, pos2; - r1 = MASK_OP_BIT_S1(ctx->opcode); - r2 = MASK_OP_BIT_S2(ctx->opcode); - r3 = MASK_OP_BIT_D(ctx->opcode); - pos1 = MASK_OP_BIT_POS1(ctx->opcode); - pos2 = MASK_OP_BIT_POS2(ctx->opcode); - op2 = MASK_OP_BIT_OP2(ctx->opcode); + int r1, r3, r4; + int32_t const9; + + + op2 = MASK_OP_RCR_OP2(ctx->opcode); + r1 = MASK_OP_RCR_S1(ctx->opcode); + const9 = MASK_OP_RCR_CONST9_SEXT(ctx->opcode); + r3 = MASK_OP_RCR_S3(ctx->opcode); + r4 = MASK_OP_RCR_D(ctx->opcode); switch (op2) { - case OPC2_32_BIT_AND_T: - gen_bit_1op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], - pos1, pos2, &tcg_gen_and_tl); + case OPC2_32_RCR_MADD_32: + gen_maddi32_d(cpu_gpr_d[r4], cpu_gpr_d[r1], cpu_gpr_d[r3], const9); break; - case OPC2_32_BIT_ANDN_T: - gen_bit_1op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], - pos1, pos2, &tcg_gen_andc_tl); + case OPC2_32_RCR_MADD_64: + gen_maddi64_d(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r3+1], const9); break; - case OPC2_32_BIT_NOR_T: - gen_bit_1op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], - pos1, pos2, &tcg_gen_nor_tl); + case OPC2_32_RCR_MADDS_32: + gen_maddsi_32(cpu_gpr_d[r4], cpu_gpr_d[r1], cpu_gpr_d[r3], const9); break; - case OPC2_32_BIT_OR_T: - gen_bit_1op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], - pos1, pos2, &tcg_gen_or_tl); + case OPC2_32_RCR_MADDS_64: + gen_maddsi_64(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r3+1], const9); + break; + case OPC2_32_RCR_MADD_U_64: + const9 = MASK_OP_RCR_CONST9(ctx->opcode); + gen_maddui64_d(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r3+1], const9); + break; + case OPC2_32_RCR_MADDS_U_32: + const9 = MASK_OP_RCR_CONST9(ctx->opcode); + gen_maddsui_32(cpu_gpr_d[r4], cpu_gpr_d[r1], cpu_gpr_d[r3], const9); + break; + case OPC2_32_RCR_MADDS_U_64: + const9 = MASK_OP_RCR_CONST9(ctx->opcode); + gen_maddsui_64(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r3+1], const9); break; } } -static void decode_bit_insert(CPUTriCoreState *env, DisasContext *ctx) +static void decode_rcr_msub(CPUTriCoreState *env, DisasContext *ctx) { uint32_t op2; - int r1, r2, r3; - int pos1, pos2; - TCGv temp; - op2 = MASK_OP_BIT_OP2(ctx->opcode); - r1 = MASK_OP_BIT_S1(ctx->opcode); - r2 = MASK_OP_BIT_S2(ctx->opcode); - r3 = MASK_OP_BIT_D(ctx->opcode); - pos1 = MASK_OP_BIT_POS1(ctx->opcode); - pos2 = MASK_OP_BIT_POS2(ctx->opcode); + int r1, r3, r4; + int32_t const9; - temp = tcg_temp_new(); - tcg_gen_shri_tl(temp, cpu_gpr_d[r2], pos2); - if (op2 == OPC2_32_BIT_INSN_T) { - tcg_gen_not_tl(temp, temp); + op2 = MASK_OP_RCR_OP2(ctx->opcode); + r1 = MASK_OP_RCR_S1(ctx->opcode); + const9 = MASK_OP_RCR_CONST9_SEXT(ctx->opcode); + r3 = MASK_OP_RCR_S3(ctx->opcode); + r4 = MASK_OP_RCR_D(ctx->opcode); + + switch (op2) { + case OPC2_32_RCR_MSUB_32: + gen_msubi32_d(cpu_gpr_d[r4], cpu_gpr_d[r1], cpu_gpr_d[r3], const9); + break; + case OPC2_32_RCR_MSUB_64: + gen_msubi64_d(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r3+1], const9); + break; + case OPC2_32_RCR_MSUBS_32: + gen_msubsi_32(cpu_gpr_d[r4], cpu_gpr_d[r1], cpu_gpr_d[r3], const9); + break; + case OPC2_32_RCR_MSUBS_64: + gen_msubsi_64(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r3+1], const9); + break; + case OPC2_32_RCR_MSUB_U_64: + const9 = MASK_OP_RCR_CONST9(ctx->opcode); + gen_msubui64_d(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r3+1], const9); + break; + case OPC2_32_RCR_MSUBS_U_32: + const9 = MASK_OP_RCR_CONST9(ctx->opcode); + gen_msubsui_32(cpu_gpr_d[r4], cpu_gpr_d[r1], cpu_gpr_d[r3], const9); + break; + case OPC2_32_RCR_MSUBS_U_64: + const9 = MASK_OP_RCR_CONST9(ctx->opcode); + gen_msubsui_64(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r3+1], const9); + break; } - tcg_gen_deposit_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], temp, pos1, 1); - tcg_temp_free(temp); } -static void decode_bit_logical_t2(CPUTriCoreState *env, DisasContext *ctx) -{ - uint32_t op2; +/* RLC format */ - int r1, r2, r3; - int pos1, pos2; +static void decode_rlc_opc(CPUTriCoreState *env, DisasContext *ctx, + uint32_t op1) +{ + int32_t const16; + int r1, r2; - op2 = MASK_OP_BIT_OP2(ctx->opcode); - r1 = MASK_OP_BIT_S1(ctx->opcode); - r2 = MASK_OP_BIT_S2(ctx->opcode); - r3 = MASK_OP_BIT_D(ctx->opcode); - pos1 = MASK_OP_BIT_POS1(ctx->opcode); - pos2 = MASK_OP_BIT_POS2(ctx->opcode); + const16 = MASK_OP_RLC_CONST16_SEXT(ctx->opcode); + r1 = MASK_OP_RLC_S1(ctx->opcode); + r2 = MASK_OP_RLC_D(ctx->opcode); - switch (op2) { - case OPC2_32_BIT_NAND_T: - gen_bit_1op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], - pos1, pos2, &tcg_gen_nand_tl); + switch (op1) { + case OPC1_32_RLC_ADDI: + gen_addi_d(cpu_gpr_d[r2], cpu_gpr_d[r1], const16); break; - case OPC2_32_BIT_ORN_T: - gen_bit_1op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], - pos1, pos2, &tcg_gen_orc_tl); + case OPC1_32_RLC_ADDIH: + gen_addi_d(cpu_gpr_d[r2], cpu_gpr_d[r1], const16 << 16); break; - case OPC2_32_BIT_XNOR_T: - gen_bit_1op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], - pos1, pos2, &tcg_gen_eqv_tl); + case OPC1_32_RLC_ADDIH_A: + tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r1], const16 << 16); break; - case OPC2_32_BIT_XOR_T: - gen_bit_1op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], - pos1, pos2, &tcg_gen_xor_tl); + case OPC1_32_RLC_MFCR: + const16 = MASK_OP_RLC_CONST16(ctx->opcode); + gen_mfcr(env, cpu_gpr_d[r2], const16); + break; + case OPC1_32_RLC_MOV: + tcg_gen_movi_tl(cpu_gpr_d[r2], const16); + break; + case OPC1_32_RLC_MOV_64: + if (tricore_feature(env, TRICORE_FEATURE_16)) { + if ((r2 & 0x1) != 0) { + /* TODO: raise OPD trap */ + } + tcg_gen_movi_tl(cpu_gpr_d[r2], const16); + tcg_gen_movi_tl(cpu_gpr_d[r2+1], const16 >> 15); + } else { + /* TODO: raise illegal opcode trap */ + } + break; + case OPC1_32_RLC_MOV_U: + const16 = MASK_OP_RLC_CONST16(ctx->opcode); + tcg_gen_movi_tl(cpu_gpr_d[r2], const16); + break; + case OPC1_32_RLC_MOV_H: + tcg_gen_movi_tl(cpu_gpr_d[r2], const16 << 16); + break; + case OPC1_32_RLC_MOVH_A: + tcg_gen_movi_tl(cpu_gpr_a[r2], const16 << 16); + break; + case OPC1_32_RLC_MTCR: + const16 = MASK_OP_RLC_CONST16(ctx->opcode); + gen_mtcr(env, ctx, cpu_gpr_d[r1], const16); break; } } -static void decode_bit_orand(CPUTriCoreState *env, DisasContext *ctx) +/* RR format */ +static void decode_rr_accumulator(CPUTriCoreState *env, DisasContext *ctx) { uint32_t op2; + int r3, r2, r1; - int r1, r2, r3; - int pos1, pos2; - - op2 = MASK_OP_BIT_OP2(ctx->opcode); - r1 = MASK_OP_BIT_S1(ctx->opcode); - r2 = MASK_OP_BIT_S2(ctx->opcode); - r3 = MASK_OP_BIT_D(ctx->opcode); - pos1 = MASK_OP_BIT_POS1(ctx->opcode); - pos2 = MASK_OP_BIT_POS2(ctx->opcode); + r3 = MASK_OP_RR_D(ctx->opcode); + r2 = MASK_OP_RR_S2(ctx->opcode); + r1 = MASK_OP_RR_S1(ctx->opcode); + op2 = MASK_OP_RR_OP2(ctx->opcode); switch (op2) { - case OPC2_32_BIT_OR_AND_T: - gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], - pos1, pos2, &tcg_gen_and_tl, &tcg_gen_or_tl); + case OPC2_32_RR_ABS: + gen_abs(cpu_gpr_d[r3], cpu_gpr_d[r2]); break; - case OPC2_32_BIT_OR_ANDN_T: - gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], - pos1, pos2, &tcg_gen_andc_tl, &tcg_gen_or_tl); + case OPC2_32_RR_ABS_B: + gen_helper_abs_b(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r2]); break; - case OPC2_32_BIT_OR_NOR_T: - if (TCG_TARGET_HAS_orc_i32) { - gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], - pos1, pos2, &tcg_gen_or_tl, &tcg_gen_orc_tl); - } else { - gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], - pos1, pos2, &tcg_gen_nor_tl, &tcg_gen_or_tl); - } + case OPC2_32_RR_ABS_H: + gen_helper_abs_h(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r2]); + break; + case OPC2_32_RR_ABSDIF: + gen_absdif(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_ABSDIF_B: + gen_helper_absdif_b(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], + cpu_gpr_d[r2]); + break; + case OPC2_32_RR_ABSDIF_H: + gen_helper_absdif_h(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], + cpu_gpr_d[r2]); + break; + case OPC2_32_RR_ABSDIFS: + gen_helper_absdif_ssov(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], + cpu_gpr_d[r2]); + break; + case OPC2_32_RR_ABSDIFS_H: + gen_helper_absdif_h_ssov(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], + cpu_gpr_d[r2]); + break; + case OPC2_32_RR_ABSS: + gen_helper_abs_ssov(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r2]); + break; + case OPC2_32_RR_ABSS_H: + gen_helper_abs_h_ssov(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r2]); + break; + case OPC2_32_RR_ADD: + gen_add_d(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_ADD_B: + gen_helper_add_b(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_ADD_H: + gen_helper_add_h(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_ADDC: + gen_addc_CC(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_ADDS: + gen_adds(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_ADDS_H: + gen_helper_add_h_ssov(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], + cpu_gpr_d[r2]); + break; + case OPC2_32_RR_ADDS_HU: + gen_helper_add_h_suov(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], + cpu_gpr_d[r2]); + break; + case OPC2_32_RR_ADDS_U: + gen_helper_add_suov(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], + cpu_gpr_d[r2]); + break; + case OPC2_32_RR_ADDX: + gen_add_CC(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_AND_EQ: + gen_accumulating_cond(TCG_COND_EQ, cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], &tcg_gen_and_tl); + break; + case OPC2_32_RR_AND_GE: + gen_accumulating_cond(TCG_COND_GE, cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], &tcg_gen_and_tl); + break; + case OPC2_32_RR_AND_GE_U: + gen_accumulating_cond(TCG_COND_GEU, cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], &tcg_gen_and_tl); + break; + case OPC2_32_RR_AND_LT: + gen_accumulating_cond(TCG_COND_LT, cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], &tcg_gen_and_tl); + break; + case OPC2_32_RR_AND_LT_U: + gen_accumulating_cond(TCG_COND_LTU, cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], &tcg_gen_and_tl); + break; + case OPC2_32_RR_AND_NE: + gen_accumulating_cond(TCG_COND_NE, cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], &tcg_gen_and_tl); + break; + case OPC2_32_RR_EQ: + tcg_gen_setcond_tl(TCG_COND_EQ, cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2]); + break; + case OPC2_32_RR_EQ_B: + gen_helper_eq_b(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_EQ_H: + gen_helper_eq_h(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_EQ_W: + gen_cond_w(TCG_COND_EQ, cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_EQANY_B: + gen_helper_eqany_b(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_EQANY_H: + gen_helper_eqany_h(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_GE: + tcg_gen_setcond_tl(TCG_COND_GE, cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2]); + break; + case OPC2_32_RR_GE_U: + tcg_gen_setcond_tl(TCG_COND_GEU, cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2]); + break; + case OPC2_32_RR_LT: + tcg_gen_setcond_tl(TCG_COND_LT, cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2]); + break; + case OPC2_32_RR_LT_U: + tcg_gen_setcond_tl(TCG_COND_LTU, cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2]); + break; + case OPC2_32_RR_LT_B: + gen_helper_lt_b(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_LT_BU: + gen_helper_lt_bu(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_LT_H: + gen_helper_lt_h(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_LT_HU: + gen_helper_lt_hu(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_LT_W: + gen_cond_w(TCG_COND_LT, cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_LT_WU: + gen_cond_w(TCG_COND_LTU, cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_MAX: + tcg_gen_movcond_tl(TCG_COND_GT, cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_MAX_U: + tcg_gen_movcond_tl(TCG_COND_GTU, cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_MAX_B: + gen_helper_max_b(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_MAX_BU: + gen_helper_max_bu(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_MAX_H: + gen_helper_max_h(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_MAX_HU: + gen_helper_max_hu(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_MIN: + tcg_gen_movcond_tl(TCG_COND_LT, cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_MIN_U: + tcg_gen_movcond_tl(TCG_COND_LTU, cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_MIN_B: + gen_helper_min_b(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_MIN_BU: + gen_helper_min_bu(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_MIN_H: + gen_helper_min_h(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_MIN_HU: + gen_helper_min_hu(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_MOV: + tcg_gen_mov_tl(cpu_gpr_d[r3], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_NE: + tcg_gen_setcond_tl(TCG_COND_NE, cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2]); + break; + case OPC2_32_RR_OR_EQ: + gen_accumulating_cond(TCG_COND_EQ, cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], &tcg_gen_or_tl); + break; + case OPC2_32_RR_OR_GE: + gen_accumulating_cond(TCG_COND_GE, cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], &tcg_gen_or_tl); + break; + case OPC2_32_RR_OR_GE_U: + gen_accumulating_cond(TCG_COND_GEU, cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], &tcg_gen_or_tl); + break; + case OPC2_32_RR_OR_LT: + gen_accumulating_cond(TCG_COND_LT, cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], &tcg_gen_or_tl); + break; + case OPC2_32_RR_OR_LT_U: + gen_accumulating_cond(TCG_COND_LTU, cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], &tcg_gen_or_tl); + break; + case OPC2_32_RR_OR_NE: + gen_accumulating_cond(TCG_COND_NE, cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], &tcg_gen_or_tl); + break; + case OPC2_32_RR_SAT_B: + gen_saturate(cpu_gpr_d[r3], cpu_gpr_d[r1], 0x7f, -0x80); + break; + case OPC2_32_RR_SAT_BU: + gen_saturate_u(cpu_gpr_d[r3], cpu_gpr_d[r1], 0xff); + break; + case OPC2_32_RR_SAT_H: + gen_saturate(cpu_gpr_d[r3], cpu_gpr_d[r1], 0x7fff, -0x8000); + break; + case OPC2_32_RR_SAT_HU: + gen_saturate_u(cpu_gpr_d[r3], cpu_gpr_d[r1], 0xffff); + break; + case OPC2_32_RR_SH_EQ: + gen_sh_cond(TCG_COND_EQ, cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2]); + break; + case OPC2_32_RR_SH_GE: + gen_sh_cond(TCG_COND_GE, cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2]); + break; + case OPC2_32_RR_SH_GE_U: + gen_sh_cond(TCG_COND_GEU, cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2]); + break; + case OPC2_32_RR_SH_LT: + gen_sh_cond(TCG_COND_LT, cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2]); + break; + case OPC2_32_RR_SH_LT_U: + gen_sh_cond(TCG_COND_LTU, cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2]); + break; + case OPC2_32_RR_SH_NE: + gen_sh_cond(TCG_COND_NE, cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2]); + break; + case OPC2_32_RR_SUB: + gen_sub_d(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_SUB_B: + gen_helper_sub_b(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_SUB_H: + gen_helper_sub_h(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_SUBC: + gen_subc_CC(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_SUBS: + gen_subs(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_SUBS_U: + gen_subsu(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_SUBS_H: + gen_helper_sub_h_ssov(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], + cpu_gpr_d[r2]); + break; + case OPC2_32_RR_SUBS_HU: + gen_helper_sub_h_suov(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], + cpu_gpr_d[r2]); + break; + case OPC2_32_RR_SUBX: + gen_sub_CC(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_XOR_EQ: + gen_accumulating_cond(TCG_COND_EQ, cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], &tcg_gen_xor_tl); + break; + case OPC2_32_RR_XOR_GE: + gen_accumulating_cond(TCG_COND_GE, cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], &tcg_gen_xor_tl); + break; + case OPC2_32_RR_XOR_GE_U: + gen_accumulating_cond(TCG_COND_GEU, cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], &tcg_gen_xor_tl); + break; + case OPC2_32_RR_XOR_LT: + gen_accumulating_cond(TCG_COND_LT, cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], &tcg_gen_xor_tl); + break; + case OPC2_32_RR_XOR_LT_U: + gen_accumulating_cond(TCG_COND_LTU, cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], &tcg_gen_xor_tl); break; - case OPC2_32_BIT_OR_OR_T: - gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], - pos1, pos2, &tcg_gen_or_tl, &tcg_gen_or_tl); + case OPC2_32_RR_XOR_NE: + gen_accumulating_cond(TCG_COND_NE, cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], &tcg_gen_xor_tl); break; } } -static void decode_bit_sh_logic1(CPUTriCoreState *env, DisasContext *ctx) +static void decode_rr_logical_shift(CPUTriCoreState *env, DisasContext *ctx) { uint32_t op2; - int r1, r2, r3; - int pos1, pos2; + int r3, r2, r1; TCGv temp; - op2 = MASK_OP_BIT_OP2(ctx->opcode); - r1 = MASK_OP_BIT_S1(ctx->opcode); - r2 = MASK_OP_BIT_S2(ctx->opcode); - r3 = MASK_OP_BIT_D(ctx->opcode); - pos1 = MASK_OP_BIT_POS1(ctx->opcode); - pos2 = MASK_OP_BIT_POS2(ctx->opcode); + r3 = MASK_OP_RR_D(ctx->opcode); + r2 = MASK_OP_RR_S2(ctx->opcode); + r1 = MASK_OP_RR_S1(ctx->opcode); temp = tcg_temp_new(); + op2 = MASK_OP_RR_OP2(ctx->opcode); switch (op2) { - case OPC2_32_BIT_SH_AND_T: - gen_bit_1op(temp, cpu_gpr_d[r1], cpu_gpr_d[r2], - pos1, pos2, &tcg_gen_and_tl); + case OPC2_32_RR_AND: + tcg_gen_and_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; - case OPC2_32_BIT_SH_ANDN_T: - gen_bit_1op(temp, cpu_gpr_d[r1], cpu_gpr_d[r2], - pos1, pos2, &tcg_gen_andc_tl); + case OPC2_32_RR_ANDN: + tcg_gen_andc_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; - case OPC2_32_BIT_SH_NOR_T: - gen_bit_1op(temp, cpu_gpr_d[r1], cpu_gpr_d[r2], - pos1, pos2, &tcg_gen_nor_tl); + case OPC2_32_RR_CLO: + gen_helper_clo(cpu_gpr_d[r3], cpu_gpr_d[r1]); break; - case OPC2_32_BIT_SH_OR_T: - gen_bit_1op(temp, cpu_gpr_d[r1], cpu_gpr_d[r2], - pos1, pos2, &tcg_gen_or_tl); + case OPC2_32_RR_CLO_H: + gen_helper_clo_h(cpu_gpr_d[r3], cpu_gpr_d[r1]); break; - } - tcg_gen_shli_tl(cpu_gpr_d[r3], cpu_gpr_d[r3], 1); - tcg_gen_add_tl(cpu_gpr_d[r3], cpu_gpr_d[r3], temp); - tcg_temp_free(temp); -} - -static void decode_bit_sh_logic2(CPUTriCoreState *env, DisasContext *ctx) -{ - uint32_t op2; - int r1, r2, r3; - int pos1, pos2; - TCGv temp; - - op2 = MASK_OP_BIT_OP2(ctx->opcode); - r1 = MASK_OP_BIT_S1(ctx->opcode); - r2 = MASK_OP_BIT_S2(ctx->opcode); - r3 = MASK_OP_BIT_D(ctx->opcode); - pos1 = MASK_OP_BIT_POS1(ctx->opcode); - pos2 = MASK_OP_BIT_POS2(ctx->opcode); - - temp = tcg_temp_new(); - - switch (op2) { - case OPC2_32_BIT_SH_NAND_T: - gen_bit_1op(temp, cpu_gpr_d[r1] , cpu_gpr_d[r2] , - pos1, pos2, &tcg_gen_nand_tl); + case OPC2_32_RR_CLS: + gen_helper_cls(cpu_gpr_d[r3], cpu_gpr_d[r1]); break; - case OPC2_32_BIT_SH_ORN_T: - gen_bit_1op(temp, cpu_gpr_d[r1], cpu_gpr_d[r2], - pos1, pos2, &tcg_gen_orc_tl); + case OPC2_32_RR_CLS_H: + gen_helper_cls_h(cpu_gpr_d[r3], cpu_gpr_d[r1]); break; - case OPC2_32_BIT_SH_XNOR_T: - gen_bit_1op(temp, cpu_gpr_d[r1], cpu_gpr_d[r2], - pos1, pos2, &tcg_gen_eqv_tl); + case OPC2_32_RR_CLZ: + gen_helper_clz(cpu_gpr_d[r3], cpu_gpr_d[r1]); break; - case OPC2_32_BIT_SH_XOR_T: - gen_bit_1op(temp, cpu_gpr_d[r1], cpu_gpr_d[r2], - pos1, pos2, &tcg_gen_xor_tl); + case OPC2_32_RR_CLZ_H: + gen_helper_clz_h(cpu_gpr_d[r3], cpu_gpr_d[r1]); + break; + case OPC2_32_RR_NAND: + tcg_gen_nand_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_NOR: + tcg_gen_nor_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_OR: + tcg_gen_or_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_ORN: + tcg_gen_orc_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_SH: + gen_helper_sh(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_SH_H: + gen_helper_sh_h(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_SHA: + gen_helper_sha(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_SHA_H: + gen_helper_sha_h(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_SHAS: + gen_shas(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_XNOR: + tcg_gen_eqv_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_XOR: + tcg_gen_xor_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; } - tcg_gen_shli_tl(cpu_gpr_d[r3], cpu_gpr_d[r3], 1); - tcg_gen_add_tl(cpu_gpr_d[r3], cpu_gpr_d[r3], temp); tcg_temp_free(temp); } -/* BO-format */ - - -static void decode_bo_addrmode_post_pre_base(CPUTriCoreState *env, - DisasContext *ctx) +static void decode_rr_address(CPUTriCoreState *env, DisasContext *ctx) { - uint32_t op2; - uint32_t off10; - int32_t r1, r2; + uint32_t op2, n; + int r1, r2, r3; TCGv temp; - r1 = MASK_OP_BO_S1D(ctx->opcode); - r2 = MASK_OP_BO_S2(ctx->opcode); - off10 = MASK_OP_BO_OFF10_SEXT(ctx->opcode); - op2 = MASK_OP_BO_OP2(ctx->opcode); + op2 = MASK_OP_RR_OP2(ctx->opcode); + r3 = MASK_OP_RR_D(ctx->opcode); + r2 = MASK_OP_RR_S2(ctx->opcode); + r1 = MASK_OP_RR_S1(ctx->opcode); + n = MASK_OP_RR_N(ctx->opcode); switch (op2) { - case OPC2_32_BO_CACHEA_WI_SHORTOFF: - case OPC2_32_BO_CACHEA_W_SHORTOFF: - case OPC2_32_BO_CACHEA_I_SHORTOFF: - /* instruction to access the cache */ - break; - case OPC2_32_BO_CACHEA_WI_POSTINC: - case OPC2_32_BO_CACHEA_W_POSTINC: - case OPC2_32_BO_CACHEA_I_POSTINC: - /* instruction to access the cache, but we still need to handle - the addressing mode */ - tcg_gen_addi_tl(cpu_gpr_d[r2], cpu_gpr_d[r2], off10); - break; - case OPC2_32_BO_CACHEA_WI_PREINC: - case OPC2_32_BO_CACHEA_W_PREINC: - case OPC2_32_BO_CACHEA_I_PREINC: - /* instruction to access the cache, but we still need to handle - the addressing mode */ - tcg_gen_addi_tl(cpu_gpr_d[r2], cpu_gpr_d[r2], off10); + case OPC2_32_RR_ADD_A: + tcg_gen_add_tl(cpu_gpr_a[r3], cpu_gpr_a[r1], cpu_gpr_a[r2]); break; - case OPC2_32_BO_CACHEI_WI_SHORTOFF: - case OPC2_32_BO_CACHEI_W_SHORTOFF: - /* TODO: Raise illegal opcode trap, - if tricore_feature(TRICORE_FEATURE_13) */ + case OPC2_32_RR_ADDSC_A: + temp = tcg_temp_new(); + tcg_gen_shli_tl(temp, cpu_gpr_d[r1], n); + tcg_gen_add_tl(cpu_gpr_a[r3], cpu_gpr_a[r2], temp); + tcg_temp_free(temp); break; - case OPC2_32_BO_CACHEI_W_POSTINC: - case OPC2_32_BO_CACHEI_WI_POSTINC: - if (!tricore_feature(env, TRICORE_FEATURE_13)) { - tcg_gen_addi_tl(cpu_gpr_d[r2], cpu_gpr_d[r2], off10); - } /* TODO: else raise illegal opcode trap */ + case OPC2_32_RR_ADDSC_AT: + temp = tcg_temp_new(); + tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 3); + tcg_gen_add_tl(temp, cpu_gpr_a[r2], temp); + tcg_gen_andi_tl(cpu_gpr_a[r3], temp, 0xFFFFFFFC); + tcg_temp_free(temp); break; - case OPC2_32_BO_CACHEI_W_PREINC: - case OPC2_32_BO_CACHEI_WI_PREINC: - if (!tricore_feature(env, TRICORE_FEATURE_13)) { - tcg_gen_addi_tl(cpu_gpr_d[r2], cpu_gpr_d[r2], off10); - } /* TODO: else raise illegal opcode trap */ + case OPC2_32_RR_EQ_A: + tcg_gen_setcond_tl(TCG_COND_EQ, cpu_gpr_d[r3], cpu_gpr_a[r1], + cpu_gpr_a[r2]); break; - case OPC2_32_BO_ST_A_SHORTOFF: - gen_offset_st(ctx, cpu_gpr_a[r1], cpu_gpr_a[r2], off10, MO_LESL); + case OPC2_32_RR_EQZ: + tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_gpr_d[r3], cpu_gpr_a[r1], 0); break; - case OPC2_32_BO_ST_A_POSTINC: - tcg_gen_qemu_st_tl(cpu_gpr_a[r1], cpu_gpr_a[r2], ctx->mem_idx, - MO_LESL); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + case OPC2_32_RR_GE_A: + tcg_gen_setcond_tl(TCG_COND_GEU, cpu_gpr_d[r3], cpu_gpr_a[r1], + cpu_gpr_a[r2]); break; - case OPC2_32_BO_ST_A_PREINC: - gen_st_preincr(ctx, cpu_gpr_a[r1], cpu_gpr_a[r2], off10, MO_LESL); + case OPC2_32_RR_LT_A: + tcg_gen_setcond_tl(TCG_COND_LTU, cpu_gpr_d[r3], cpu_gpr_a[r1], + cpu_gpr_a[r2]); break; - case OPC2_32_BO_ST_B_SHORTOFF: - gen_offset_st(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_UB); + case OPC2_32_RR_MOV_A: + tcg_gen_mov_tl(cpu_gpr_a[r3], cpu_gpr_d[r2]); break; - case OPC2_32_BO_ST_B_POSTINC: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, - MO_UB); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + case OPC2_32_RR_MOV_AA: + tcg_gen_mov_tl(cpu_gpr_a[r3], cpu_gpr_a[r2]); break; - case OPC2_32_BO_ST_B_PREINC: - gen_st_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_UB); + case OPC2_32_RR_MOV_D: + tcg_gen_mov_tl(cpu_gpr_d[r3], cpu_gpr_a[r2]); break; - case OPC2_32_BO_ST_D_SHORTOFF: - gen_offset_st_2regs(cpu_gpr_d[r1+1], cpu_gpr_d[r1], cpu_gpr_a[r2], - off10, ctx); + case OPC2_32_RR_NE_A: + tcg_gen_setcond_tl(TCG_COND_NE, cpu_gpr_d[r3], cpu_gpr_a[r1], + cpu_gpr_a[r2]); break; - case OPC2_32_BO_ST_D_POSTINC: - gen_st_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], cpu_gpr_a[r2], ctx); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + case OPC2_32_RR_NEZ_A: + tcg_gen_setcondi_tl(TCG_COND_NE, cpu_gpr_d[r3], cpu_gpr_a[r1], 0); break; - case OPC2_32_BO_ST_D_PREINC: - temp = tcg_temp_new(); - tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); - gen_st_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], temp, ctx); - tcg_gen_mov_tl(cpu_gpr_a[r2], temp); - tcg_temp_free(temp); + case OPC2_32_RR_SUB_A: + tcg_gen_sub_tl(cpu_gpr_a[r3], cpu_gpr_a[r1], cpu_gpr_a[r2]); break; - case OPC2_32_BO_ST_DA_SHORTOFF: - gen_offset_st_2regs(cpu_gpr_a[r1+1], cpu_gpr_a[r1], cpu_gpr_a[r2], - off10, ctx); + } +} + +static void decode_rr_idirect(CPUTriCoreState *env, DisasContext *ctx) +{ + uint32_t op2; + int r1; + + op2 = MASK_OP_RR_OP2(ctx->opcode); + r1 = MASK_OP_RR_S1(ctx->opcode); + + switch (op2) { + case OPC2_32_RR_JI: + tcg_gen_andi_tl(cpu_PC, cpu_gpr_a[r1], ~0x1); break; - case OPC2_32_BO_ST_DA_POSTINC: - gen_st_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], cpu_gpr_a[r2], ctx); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + case OPC2_32_RR_JLI: + tcg_gen_movi_tl(cpu_gpr_a[11], ctx->next_pc); + tcg_gen_andi_tl(cpu_PC, cpu_gpr_a[r1], ~0x1); break; - case OPC2_32_BO_ST_DA_PREINC: - temp = tcg_temp_new(); - tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); - gen_st_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], temp, ctx); - tcg_gen_mov_tl(cpu_gpr_a[r2], temp); - tcg_temp_free(temp); + case OPC2_32_RR_CALLI: + gen_helper_1arg(call, ctx->next_pc); + tcg_gen_andi_tl(cpu_PC, cpu_gpr_a[r1], ~0x1); break; - case OPC2_32_BO_ST_H_SHORTOFF: - gen_offset_st(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUW); + } + tcg_gen_exit_tb(0); + ctx->bstate = BS_BRANCH; +} + +static void decode_rr_divide(CPUTriCoreState *env, DisasContext *ctx) +{ + uint32_t op2; + int r1, r2, r3; + + TCGv temp, temp2, temp3; + + op2 = MASK_OP_RR_OP2(ctx->opcode); + r3 = MASK_OP_RR_D(ctx->opcode); + r2 = MASK_OP_RR_S2(ctx->opcode); + r1 = MASK_OP_RR_S1(ctx->opcode); + + switch (op2) { + case OPC2_32_RR_BMERGE: + gen_helper_bmerge(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; - case OPC2_32_BO_ST_H_POSTINC: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, - MO_LEUW); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + case OPC2_32_RR_BSPLIT: + gen_bsplit(cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1]); break; - case OPC2_32_BO_ST_H_PREINC: - gen_st_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUW); + case OPC2_32_RR_DVINIT_B: + gen_dvinit_b(env, cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], + cpu_gpr_d[r2]); break; - case OPC2_32_BO_ST_Q_SHORTOFF: + case OPC2_32_RR_DVINIT_BU: temp = tcg_temp_new(); - tcg_gen_shri_tl(temp, cpu_gpr_d[r1], 16); - gen_offset_st(ctx, temp, cpu_gpr_a[r2], off10, MO_LEUW); + temp2 = tcg_temp_new(); + temp3 = tcg_temp_new(); + + tcg_gen_shri_tl(temp3, cpu_gpr_d[r1], 8); + /* reset av */ + tcg_gen_movi_tl(cpu_PSW_AV, 0); + if (!tricore_feature(env, TRICORE_FEATURE_131)) { + /* overflow = (abs(D[r3+1]) >= abs(D[r2])) */ + tcg_gen_neg_tl(temp, temp3); + /* use cpu_PSW_AV to compare against 0 */ + tcg_gen_movcond_tl(TCG_COND_LT, temp, temp3, cpu_PSW_AV, + temp, temp3); + tcg_gen_neg_tl(temp2, cpu_gpr_d[r2]); + tcg_gen_movcond_tl(TCG_COND_LT, temp2, cpu_gpr_d[r2], cpu_PSW_AV, + temp2, cpu_gpr_d[r2]); + tcg_gen_setcond_tl(TCG_COND_GE, cpu_PSW_V, temp, temp2); + } else { + /* overflow = (D[b] == 0) */ + tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_PSW_V, cpu_gpr_d[r2], 0); + } + tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); + /* sv */ + tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + /* write result */ + tcg_gen_shli_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], 24); + tcg_gen_mov_tl(cpu_gpr_d[r3+1], temp3); + tcg_temp_free(temp); + tcg_temp_free(temp2); + tcg_temp_free(temp3); break; - case OPC2_32_BO_ST_Q_POSTINC: + case OPC2_32_RR_DVINIT_H: + gen_dvinit_h(env, cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], + cpu_gpr_d[r2]); + break; + case OPC2_32_RR_DVINIT_HU: temp = tcg_temp_new(); - tcg_gen_shri_tl(temp, cpu_gpr_d[r1], 16); - tcg_gen_qemu_st_tl(temp, cpu_gpr_a[r2], ctx->mem_idx, - MO_LEUW); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + temp2 = tcg_temp_new(); + temp3 = tcg_temp_new(); + + tcg_gen_shri_tl(temp3, cpu_gpr_d[r1], 16); + /* reset av */ + tcg_gen_movi_tl(cpu_PSW_AV, 0); + if (!tricore_feature(env, TRICORE_FEATURE_131)) { + /* overflow = (abs(D[r3+1]) >= abs(D[r2])) */ + tcg_gen_neg_tl(temp, temp3); + /* use cpu_PSW_AV to compare against 0 */ + tcg_gen_movcond_tl(TCG_COND_LT, temp, temp3, cpu_PSW_AV, + temp, temp3); + tcg_gen_neg_tl(temp2, cpu_gpr_d[r2]); + tcg_gen_movcond_tl(TCG_COND_LT, temp2, cpu_gpr_d[r2], cpu_PSW_AV, + temp2, cpu_gpr_d[r2]); + tcg_gen_setcond_tl(TCG_COND_GE, cpu_PSW_V, temp, temp2); + } else { + /* overflow = (D[b] == 0) */ + tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_PSW_V, cpu_gpr_d[r2], 0); + } + tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); + /* sv */ + tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + /* write result */ + tcg_gen_mov_tl(cpu_gpr_d[r3+1], temp3); + tcg_gen_shli_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], 16); tcg_temp_free(temp); + tcg_temp_free(temp2); + tcg_temp_free(temp3); break; - case OPC2_32_BO_ST_Q_PREINC: + case OPC2_32_RR_DVINIT: temp = tcg_temp_new(); - tcg_gen_shri_tl(temp, cpu_gpr_d[r1], 16); - gen_st_preincr(ctx, temp, cpu_gpr_a[r2], off10, MO_LEUW); + temp2 = tcg_temp_new(); + /* overflow = ((D[b] == 0) || + ((D[b] == 0xFFFFFFFF) && (D[a] == 0x80000000))) */ + tcg_gen_setcondi_tl(TCG_COND_EQ, temp, cpu_gpr_d[r2], 0xffffffff); + tcg_gen_setcondi_tl(TCG_COND_EQ, temp2, cpu_gpr_d[r1], 0x80000000); + tcg_gen_and_tl(temp, temp, temp2); + tcg_gen_setcondi_tl(TCG_COND_EQ, temp2, cpu_gpr_d[r2], 0); + tcg_gen_or_tl(cpu_PSW_V, temp, temp2); + tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); + /* sv */ + tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + /* reset av */ + tcg_gen_movi_tl(cpu_PSW_AV, 0); + /* write result */ + tcg_gen_mov_tl(cpu_gpr_d[r3], cpu_gpr_d[r1]); + /* sign extend to high reg */ + tcg_gen_sari_tl(cpu_gpr_d[r3+1], cpu_gpr_d[r1], 31); tcg_temp_free(temp); + tcg_temp_free(temp2); break; - case OPC2_32_BO_ST_W_SHORTOFF: - gen_offset_st(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUL); + case OPC2_32_RR_DVINIT_U: + /* overflow = (D[b] == 0) */ + tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_PSW_V, cpu_gpr_d[r2], 0); + tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); + /* sv */ + tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); + /* reset av */ + tcg_gen_movi_tl(cpu_PSW_AV, 0); + /* write result */ + tcg_gen_mov_tl(cpu_gpr_d[r3], cpu_gpr_d[r1]); + /* zero extend to high reg*/ + tcg_gen_movi_tl(cpu_gpr_d[r3+1], 0); break; - case OPC2_32_BO_ST_W_POSTINC: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, - MO_LEUL); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + case OPC2_32_RR_PARITY: + gen_helper_parity(cpu_gpr_d[r3], cpu_gpr_d[r1]); break; - case OPC2_32_BO_ST_W_PREINC: - gen_st_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUL); + case OPC2_32_RR_UNPACK: + gen_unpack(cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1]); break; } } -static void decode_bo_addrmode_bitreverse_circular(CPUTriCoreState *env, - DisasContext *ctx) +/* RR1 Format */ +static void decode_rr1_mul(CPUTriCoreState *env, DisasContext *ctx) { uint32_t op2; - uint32_t off10; - int32_t r1, r2; - TCGv temp, temp2, temp3; - r1 = MASK_OP_BO_S1D(ctx->opcode); - r2 = MASK_OP_BO_S2(ctx->opcode); - off10 = MASK_OP_BO_OFF10_SEXT(ctx->opcode); - op2 = MASK_OP_BO_OP2(ctx->opcode); + int r1, r2, r3; + TCGv n; + TCGv_i64 temp64; + + r1 = MASK_OP_RR1_S1(ctx->opcode); + r2 = MASK_OP_RR1_S2(ctx->opcode); + r3 = MASK_OP_RR1_D(ctx->opcode); + n = tcg_const_i32(MASK_OP_RR1_N(ctx->opcode)); + op2 = MASK_OP_RR1_OP2(ctx->opcode); + + switch (op2) { + case OPC2_32_RR1_MUL_H_32_LL: + temp64 = tcg_temp_new_i64(); + GEN_HELPER_LL(mul_h, temp64, cpu_gpr_d[r1], cpu_gpr_d[r2], n); + tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3+1], temp64); + gen_calc_usb_mul_h(cpu_gpr_d[r3], cpu_gpr_d[r3+1]); + tcg_temp_free_i64(temp64); + break; + case OPC2_32_RR1_MUL_H_32_LU: + temp64 = tcg_temp_new_i64(); + GEN_HELPER_LU(mul_h, temp64, cpu_gpr_d[r1], cpu_gpr_d[r2], n); + tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3+1], temp64); + gen_calc_usb_mul_h(cpu_gpr_d[r3], cpu_gpr_d[r3+1]); + tcg_temp_free_i64(temp64); + break; + case OPC2_32_RR1_MUL_H_32_UL: + temp64 = tcg_temp_new_i64(); + GEN_HELPER_UL(mul_h, temp64, cpu_gpr_d[r1], cpu_gpr_d[r2], n); + tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3+1], temp64); + gen_calc_usb_mul_h(cpu_gpr_d[r3], cpu_gpr_d[r3+1]); + tcg_temp_free_i64(temp64); + break; + case OPC2_32_RR1_MUL_H_32_UU: + temp64 = tcg_temp_new_i64(); + GEN_HELPER_UU(mul_h, temp64, cpu_gpr_d[r1], cpu_gpr_d[r2], n); + tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3+1], temp64); + gen_calc_usb_mul_h(cpu_gpr_d[r3], cpu_gpr_d[r3+1]); + tcg_temp_free_i64(temp64); + break; + case OPC2_32_RR1_MULM_H_64_LL: + temp64 = tcg_temp_new_i64(); + GEN_HELPER_LL(mulm_h, temp64, cpu_gpr_d[r1], cpu_gpr_d[r2], n); + tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3+1], temp64); + /* reset V bit */ + tcg_gen_movi_tl(cpu_PSW_V, 0); + /* reset AV bit */ + tcg_gen_mov_tl(cpu_PSW_AV, cpu_PSW_V); + tcg_temp_free_i64(temp64); + break; + case OPC2_32_RR1_MULM_H_64_LU: + temp64 = tcg_temp_new_i64(); + GEN_HELPER_LU(mulm_h, temp64, cpu_gpr_d[r1], cpu_gpr_d[r2], n); + tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3+1], temp64); + /* reset V bit */ + tcg_gen_movi_tl(cpu_PSW_V, 0); + /* reset AV bit */ + tcg_gen_mov_tl(cpu_PSW_AV, cpu_PSW_V); + tcg_temp_free_i64(temp64); + break; + case OPC2_32_RR1_MULM_H_64_UL: + temp64 = tcg_temp_new_i64(); + GEN_HELPER_UL(mulm_h, temp64, cpu_gpr_d[r1], cpu_gpr_d[r2], n); + tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3+1], temp64); + /* reset V bit */ + tcg_gen_movi_tl(cpu_PSW_V, 0); + /* reset AV bit */ + tcg_gen_mov_tl(cpu_PSW_AV, cpu_PSW_V); + tcg_temp_free_i64(temp64); + break; + case OPC2_32_RR1_MULM_H_64_UU: + temp64 = tcg_temp_new_i64(); + GEN_HELPER_UU(mulm_h, temp64, cpu_gpr_d[r1], cpu_gpr_d[r2], n); + tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3+1], temp64); + /* reset V bit */ + tcg_gen_movi_tl(cpu_PSW_V, 0); + /* reset AV bit */ + tcg_gen_mov_tl(cpu_PSW_AV, cpu_PSW_V); + tcg_temp_free_i64(temp64); + + break; + case OPC2_32_RR1_MULR_H_16_LL: + GEN_HELPER_LL(mulr_h, cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], n); + gen_calc_usb_mulr_h(cpu_gpr_d[r3]); + break; + case OPC2_32_RR1_MULR_H_16_LU: + GEN_HELPER_LU(mulr_h, cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], n); + gen_calc_usb_mulr_h(cpu_gpr_d[r3]); + break; + case OPC2_32_RR1_MULR_H_16_UL: + GEN_HELPER_UL(mulr_h, cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], n); + gen_calc_usb_mulr_h(cpu_gpr_d[r3]); + break; + case OPC2_32_RR1_MULR_H_16_UU: + GEN_HELPER_UU(mulr_h, cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], n); + gen_calc_usb_mulr_h(cpu_gpr_d[r3]); + break; + } + tcg_temp_free(n); +} + +static void decode_rr1_mulq(CPUTriCoreState *env, DisasContext *ctx) +{ + uint32_t op2; + int r1, r2, r3; + uint32_t n; + + TCGv temp, temp2; + + r1 = MASK_OP_RR1_S1(ctx->opcode); + r2 = MASK_OP_RR1_S2(ctx->opcode); + r3 = MASK_OP_RR1_D(ctx->opcode); + n = MASK_OP_RR1_N(ctx->opcode); + op2 = MASK_OP_RR1_OP2(ctx->opcode); temp = tcg_temp_new(); temp2 = tcg_temp_new(); - temp3 = tcg_const_i32(off10); - - tcg_gen_ext16u_tl(temp, cpu_gpr_a[r2+1]); - tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp); switch (op2) { - case OPC2_32_BO_CACHEA_WI_BR: - case OPC2_32_BO_CACHEA_W_BR: - case OPC2_32_BO_CACHEA_I_BR: - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); - break; - case OPC2_32_BO_CACHEA_WI_CIRC: - case OPC2_32_BO_CACHEA_W_CIRC: - case OPC2_32_BO_CACHEA_I_CIRC: - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + case OPC2_32_RR1_MUL_Q_32: + gen_mul_q(cpu_gpr_d[r3], temp, cpu_gpr_d[r1], cpu_gpr_d[r2], n, 32); break; - case OPC2_32_BO_ST_A_BR: - tcg_gen_qemu_st_tl(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + case OPC2_32_RR1_MUL_Q_64: + gen_mul_q(cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + n, 0); break; - case OPC2_32_BO_ST_A_CIRC: - tcg_gen_qemu_st_tl(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + case OPC2_32_RR1_MUL_Q_32_L: + tcg_gen_ext16s_tl(temp, cpu_gpr_d[r2]); + gen_mul_q(cpu_gpr_d[r3], temp, cpu_gpr_d[r1], temp, n, 16); break; - case OPC2_32_BO_ST_B_BR: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_UB); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + case OPC2_32_RR1_MUL_Q_64_L: + tcg_gen_ext16s_tl(temp, cpu_gpr_d[r2]); + gen_mul_q(cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], temp, n, 0); break; - case OPC2_32_BO_ST_B_CIRC: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_UB); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + case OPC2_32_RR1_MUL_Q_32_U: + tcg_gen_sari_tl(temp, cpu_gpr_d[r2], 16); + gen_mul_q(cpu_gpr_d[r3], temp, cpu_gpr_d[r1], temp, n, 16); break; - case OPC2_32_BO_ST_D_BR: - gen_st_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], temp2, ctx); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + case OPC2_32_RR1_MUL_Q_64_U: + tcg_gen_sari_tl(temp, cpu_gpr_d[r2], 16); + gen_mul_q(cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], temp, n, 0); break; - case OPC2_32_BO_ST_D_CIRC: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL); - tcg_gen_shri_tl(temp2, cpu_gpr_a[r2+1], 16); - tcg_gen_addi_tl(temp, temp, 4); - tcg_gen_rem_tl(temp, temp, temp2); - tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp); - tcg_gen_qemu_st_tl(cpu_gpr_d[r1+1], temp2, ctx->mem_idx, MO_LEUL); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + case OPC2_32_RR1_MUL_Q_32_LL: + tcg_gen_ext16s_tl(temp, cpu_gpr_d[r1]); + tcg_gen_ext16s_tl(temp2, cpu_gpr_d[r2]); + gen_mul_q_16(cpu_gpr_d[r3], temp, temp2, n); break; - case OPC2_32_BO_ST_DA_BR: - gen_st_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], temp2, ctx); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + case OPC2_32_RR1_MUL_Q_32_UU: + tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 16); + tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); + gen_mul_q_16(cpu_gpr_d[r3], temp, temp2, n); break; - case OPC2_32_BO_ST_DA_CIRC: - tcg_gen_qemu_st_tl(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL); - tcg_gen_shri_tl(temp2, cpu_gpr_a[r2+1], 16); - tcg_gen_addi_tl(temp, temp, 4); - tcg_gen_rem_tl(temp, temp, temp2); - tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp); - tcg_gen_qemu_st_tl(cpu_gpr_a[r1+1], temp2, ctx->mem_idx, MO_LEUL); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + case OPC2_32_RR1_MULR_Q_32_L: + tcg_gen_ext16s_tl(temp, cpu_gpr_d[r1]); + tcg_gen_ext16s_tl(temp2, cpu_gpr_d[r2]); + gen_mulr_q(cpu_gpr_d[r3], temp, temp2, n); break; - case OPC2_32_BO_ST_H_BR: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + case OPC2_32_RR1_MULR_Q_32_U: + tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 16); + tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); + gen_mulr_q(cpu_gpr_d[r3], temp, temp2, n); break; - case OPC2_32_BO_ST_H_CIRC: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + } + tcg_temp_free(temp); + tcg_temp_free(temp2); +} + +/* RR2 format */ +static void decode_rr2_mul(CPUTriCoreState *env, DisasContext *ctx) +{ + uint32_t op2; + int r1, r2, r3; + + op2 = MASK_OP_RR2_OP2(ctx->opcode); + r1 = MASK_OP_RR2_S1(ctx->opcode); + r2 = MASK_OP_RR2_S2(ctx->opcode); + r3 = MASK_OP_RR2_D(ctx->opcode); + switch (op2) { + case OPC2_32_RR2_MUL_32: + gen_mul_i32s(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; - case OPC2_32_BO_ST_Q_BR: - tcg_gen_shri_tl(temp, cpu_gpr_d[r1], 16); - tcg_gen_qemu_st_tl(temp, temp2, ctx->mem_idx, MO_LEUW); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + case OPC2_32_RR2_MUL_64: + gen_mul_i64s(cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], + cpu_gpr_d[r2]); break; - case OPC2_32_BO_ST_Q_CIRC: - tcg_gen_shri_tl(temp, cpu_gpr_d[r1], 16); - tcg_gen_qemu_st_tl(temp, temp2, ctx->mem_idx, MO_LEUW); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + case OPC2_32_RR2_MULS_32: + gen_helper_mul_ssov(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], + cpu_gpr_d[r2]); break; - case OPC2_32_BO_ST_W_BR: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + case OPC2_32_RR2_MUL_U_64: + gen_mul_i64u(cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], + cpu_gpr_d[r2]); break; - case OPC2_32_BO_ST_W_CIRC: - tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + case OPC2_32_RR2_MULS_U_32: + gen_helper_mul_suov(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], + cpu_gpr_d[r2]); break; } - tcg_temp_free(temp); - tcg_temp_free(temp2); - tcg_temp_free(temp3); } -static void decode_bo_addrmode_ld_post_pre_base(CPUTriCoreState *env, - DisasContext *ctx) +/* RRPW format */ +static void decode_rrpw_extract_insert(CPUTriCoreState *env, DisasContext *ctx) { uint32_t op2; - uint32_t off10; - int32_t r1, r2; - TCGv temp; + int r1, r2, r3; + int32_t pos, width; - r1 = MASK_OP_BO_S1D(ctx->opcode); - r2 = MASK_OP_BO_S2(ctx->opcode); - off10 = MASK_OP_BO_OFF10_SEXT(ctx->opcode); - op2 = MASK_OP_BO_OP2(ctx->opcode); + op2 = MASK_OP_RRPW_OP2(ctx->opcode); + r1 = MASK_OP_RRPW_S1(ctx->opcode); + r2 = MASK_OP_RRPW_S2(ctx->opcode); + r3 = MASK_OP_RRPW_D(ctx->opcode); + pos = MASK_OP_RRPW_POS(ctx->opcode); + width = MASK_OP_RRPW_WIDTH(ctx->opcode); switch (op2) { - case OPC2_32_BO_LD_A_SHORTOFF: - gen_offset_ld(ctx, cpu_gpr_a[r1], cpu_gpr_a[r2], off10, MO_LEUL); + case OPC2_32_RRPW_EXTR: + if (pos + width <= 31) { + /* optimize special cases */ + if ((pos == 0) && (width == 8)) { + tcg_gen_ext8s_tl(cpu_gpr_d[r3], cpu_gpr_d[r1]); + } else if ((pos == 0) && (width == 16)) { + tcg_gen_ext16s_tl(cpu_gpr_d[r3], cpu_gpr_d[r1]); + } else { + tcg_gen_shli_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], 32 - pos - width); + tcg_gen_sari_tl(cpu_gpr_d[r3], cpu_gpr_d[r3], 32 - width); + } + } break; - case OPC2_32_BO_LD_A_POSTINC: - tcg_gen_qemu_ld_tl(cpu_gpr_a[r1], cpu_gpr_a[r2], ctx->mem_idx, - MO_LEUL); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + case OPC2_32_RRPW_EXTR_U: + if (width == 0) { + tcg_gen_movi_tl(cpu_gpr_d[r3], 0); + } else { + tcg_gen_shri_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], pos); + tcg_gen_andi_tl(cpu_gpr_d[r3], cpu_gpr_d[r3], ~0u >> (32-width)); + } break; - case OPC2_32_BO_LD_A_PREINC: - gen_ld_preincr(ctx, cpu_gpr_a[r1], cpu_gpr_a[r2], off10, MO_LEUL); + case OPC2_32_RRPW_IMASK: + if (pos + width <= 31) { + tcg_gen_movi_tl(cpu_gpr_d[r3+1], ((1u << width) - 1) << pos); + tcg_gen_shli_tl(cpu_gpr_d[r3], cpu_gpr_d[r2], pos); + } break; - case OPC2_32_BO_LD_B_SHORTOFF: - gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_SB); + case OPC2_32_RRPW_INSERT: + if (pos + width <= 31) { + tcg_gen_deposit_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], + width, pos); + } break; - case OPC2_32_BO_LD_B_POSTINC: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, - MO_SB); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + } +} + +/* RRR format */ +static void decode_rrr_cond_select(CPUTriCoreState *env, DisasContext *ctx) +{ + uint32_t op2; + int r1, r2, r3, r4; + TCGv temp; + + op2 = MASK_OP_RRR_OP2(ctx->opcode); + r1 = MASK_OP_RRR_S1(ctx->opcode); + r2 = MASK_OP_RRR_S2(ctx->opcode); + r3 = MASK_OP_RRR_S3(ctx->opcode); + r4 = MASK_OP_RRR_D(ctx->opcode); + + switch (op2) { + case OPC2_32_RRR_CADD: + gen_cond_add(TCG_COND_NE, cpu_gpr_d[r1], cpu_gpr_d[r2], + cpu_gpr_d[r4], cpu_gpr_d[r3]); + break; + case OPC2_32_RRR_CADDN: + gen_cond_add(TCG_COND_EQ, cpu_gpr_d[r1], cpu_gpr_d[r2], cpu_gpr_d[r4], + cpu_gpr_d[r3]); + break; + case OPC2_32_RRR_CSUB: + gen_cond_sub(TCG_COND_NE, cpu_gpr_d[r1], cpu_gpr_d[r2], cpu_gpr_d[r4], + cpu_gpr_d[r3]); + break; + case OPC2_32_RRR_CSUBN: + gen_cond_sub(TCG_COND_EQ, cpu_gpr_d[r1], cpu_gpr_d[r2], cpu_gpr_d[r4], + cpu_gpr_d[r3]); + break; + case OPC2_32_RRR_SEL: + temp = tcg_const_i32(0); + tcg_gen_movcond_tl(TCG_COND_NE, cpu_gpr_d[r4], cpu_gpr_d[r3], temp, + cpu_gpr_d[r1], cpu_gpr_d[r2]); + tcg_temp_free(temp); break; - case OPC2_32_BO_LD_B_PREINC: - gen_ld_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_SB); + case OPC2_32_RRR_SELN: + temp = tcg_const_i32(0); + tcg_gen_movcond_tl(TCG_COND_EQ, cpu_gpr_d[r4], cpu_gpr_d[r3], temp, + cpu_gpr_d[r1], cpu_gpr_d[r2]); + tcg_temp_free(temp); break; - case OPC2_32_BO_LD_BU_SHORTOFF: - gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_UB); + } +} + +static void decode_rrr_divide(CPUTriCoreState *env, DisasContext *ctx) +{ + uint32_t op2; + + int r1, r2, r3, r4; + + op2 = MASK_OP_RRR_OP2(ctx->opcode); + r1 = MASK_OP_RRR_S1(ctx->opcode); + r2 = MASK_OP_RRR_S2(ctx->opcode); + r3 = MASK_OP_RRR_S3(ctx->opcode); + r4 = MASK_OP_RRR_D(ctx->opcode); + + switch (op2) { + case OPC2_32_RRR_DVADJ: + GEN_HELPER_RRR(dvadj, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; - case OPC2_32_BO_LD_BU_POSTINC: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, - MO_UB); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + case OPC2_32_RRR_DVSTEP: + GEN_HELPER_RRR(dvstep, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; - case OPC2_32_BO_LD_BU_PREINC: - gen_ld_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_SB); + case OPC2_32_RRR_DVSTEP_U: + GEN_HELPER_RRR(dvstep_u, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; - case OPC2_32_BO_LD_D_SHORTOFF: - gen_offset_ld_2regs(cpu_gpr_d[r1+1], cpu_gpr_d[r1], cpu_gpr_a[r2], - off10, ctx); + case OPC2_32_RRR_IXMAX: + GEN_HELPER_RRR(ixmax, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; - case OPC2_32_BO_LD_D_POSTINC: - gen_ld_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], cpu_gpr_a[r2], ctx); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + case OPC2_32_RRR_IXMAX_U: + GEN_HELPER_RRR(ixmax_u, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; - case OPC2_32_BO_LD_D_PREINC: - temp = tcg_temp_new(); - tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); - gen_ld_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], temp, ctx); - tcg_gen_mov_tl(cpu_gpr_a[r2], temp); - tcg_temp_free(temp); + case OPC2_32_RRR_IXMIN: + GEN_HELPER_RRR(ixmin, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; - case OPC2_32_BO_LD_DA_SHORTOFF: - gen_offset_ld_2regs(cpu_gpr_a[r1+1], cpu_gpr_a[r1], cpu_gpr_a[r2], - off10, ctx); + case OPC2_32_RRR_IXMIN_U: + GEN_HELPER_RRR(ixmin_u, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; - case OPC2_32_BO_LD_DA_POSTINC: - gen_ld_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], cpu_gpr_a[r2], ctx); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + case OPC2_32_RRR_PACK: + gen_helper_pack(cpu_gpr_d[r4], cpu_PSW_C, cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1]); break; - case OPC2_32_BO_LD_DA_PREINC: - temp = tcg_temp_new(); - tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); - gen_ld_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], temp, ctx); - tcg_gen_mov_tl(cpu_gpr_a[r2], temp); - tcg_temp_free(temp); + } +} + +/* RRR2 format */ +static void decode_rrr2_madd(CPUTriCoreState *env, DisasContext *ctx) +{ + uint32_t op2; + uint32_t r1, r2, r3, r4; + + op2 = MASK_OP_RRR2_OP2(ctx->opcode); + r1 = MASK_OP_RRR2_S1(ctx->opcode); + r2 = MASK_OP_RRR2_S2(ctx->opcode); + r3 = MASK_OP_RRR2_S3(ctx->opcode); + r4 = MASK_OP_RRR2_D(ctx->opcode); + switch (op2) { + case OPC2_32_RRR2_MADD_32: + gen_madd32_d(cpu_gpr_d[r4], cpu_gpr_d[r1], cpu_gpr_d[r3], + cpu_gpr_d[r2]); break; - case OPC2_32_BO_LD_H_SHORTOFF: - gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LESW); + case OPC2_32_RRR2_MADD_64: + gen_madd64_d(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; - case OPC2_32_BO_LD_H_POSTINC: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, - MO_LESW); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + case OPC2_32_RRR2_MADDS_32: + gen_helper_madd32_ssov(cpu_gpr_d[r4], cpu_env, cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r2]); break; - case OPC2_32_BO_LD_H_PREINC: - gen_ld_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LESW); + case OPC2_32_RRR2_MADDS_64: + gen_madds_64(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; - case OPC2_32_BO_LD_HU_SHORTOFF: - gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUW); + case OPC2_32_RRR2_MADD_U_64: + gen_maddu64_d(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; - case OPC2_32_BO_LD_HU_POSTINC: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, - MO_LEUW); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + case OPC2_32_RRR2_MADDS_U_32: + gen_helper_madd32_suov(cpu_gpr_d[r4], cpu_env, cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r2]); break; - case OPC2_32_BO_LD_HU_PREINC: - gen_ld_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUW); + case OPC2_32_RRR2_MADDS_U_64: + gen_maddsu_64(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; - case OPC2_32_BO_LD_Q_SHORTOFF: - gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUW); - tcg_gen_shli_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], 16); + } +} + +static void decode_rrr2_msub(CPUTriCoreState *env, DisasContext *ctx) +{ + uint32_t op2; + uint32_t r1, r2, r3, r4; + + op2 = MASK_OP_RRR2_OP2(ctx->opcode); + r1 = MASK_OP_RRR2_S1(ctx->opcode); + r2 = MASK_OP_RRR2_S2(ctx->opcode); + r3 = MASK_OP_RRR2_S3(ctx->opcode); + r4 = MASK_OP_RRR2_D(ctx->opcode); + + switch (op2) { + case OPC2_32_RRR2_MSUB_32: + gen_msub32_d(cpu_gpr_d[r4], cpu_gpr_d[r1], cpu_gpr_d[r3], + cpu_gpr_d[r2]); break; - case OPC2_32_BO_LD_Q_POSTINC: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, - MO_LEUW); - tcg_gen_shli_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], 16); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + case OPC2_32_RRR2_MSUB_64: + gen_msub64_d(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; - case OPC2_32_BO_LD_Q_PREINC: - gen_ld_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUW); - tcg_gen_shli_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], 16); + case OPC2_32_RRR2_MSUBS_32: + gen_helper_msub32_ssov(cpu_gpr_d[r4], cpu_env, cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r2]); break; - case OPC2_32_BO_LD_W_SHORTOFF: - gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUL); + case OPC2_32_RRR2_MSUBS_64: + gen_msubs_64(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; - case OPC2_32_BO_LD_W_POSTINC: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], cpu_gpr_a[r2], ctx->mem_idx, - MO_LEUL); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + case OPC2_32_RRR2_MSUB_U_64: + gen_msubu64_d(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; - case OPC2_32_BO_LD_W_PREINC: - gen_ld_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUL); + case OPC2_32_RRR2_MSUBS_U_32: + gen_helper_msub32_suov(cpu_gpr_d[r4], cpu_env, cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r2]); + break; + case OPC2_32_RRR2_MSUBS_U_64: + gen_msubsu_64(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], + cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; } } -static void decode_bo_addrmode_ld_bitreverse_circular(CPUTriCoreState *env, - DisasContext *ctx) +/* RRR1 format */ +static void decode_rrr1_madd(CPUTriCoreState *env, DisasContext *ctx) { uint32_t op2; - uint32_t off10; - int r1, r2; - - TCGv temp, temp2, temp3; - - r1 = MASK_OP_BO_S1D(ctx->opcode); - r2 = MASK_OP_BO_S2(ctx->opcode); - off10 = MASK_OP_BO_OFF10_SEXT(ctx->opcode); - op2 = MASK_OP_BO_OP2(ctx->opcode); - - temp = tcg_temp_new(); - temp2 = tcg_temp_new(); - temp3 = tcg_const_i32(off10); - - tcg_gen_ext16u_tl(temp, cpu_gpr_a[r2+1]); - tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp); + uint32_t r1, r2, r3, r4, n; + op2 = MASK_OP_RRR1_OP2(ctx->opcode); + r1 = MASK_OP_RRR1_S1(ctx->opcode); + r2 = MASK_OP_RRR1_S2(ctx->opcode); + r3 = MASK_OP_RRR1_S3(ctx->opcode); + r4 = MASK_OP_RRR1_D(ctx->opcode); + n = MASK_OP_RRR1_N(ctx->opcode); switch (op2) { - case OPC2_32_BO_LD_A_BR: - tcg_gen_qemu_ld_tl(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + case OPC2_32_RRR1_MADD_H_LL: + gen_madd_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; - case OPC2_32_BO_LD_A_CIRC: - tcg_gen_qemu_ld_tl(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + case OPC2_32_RRR1_MADD_H_LU: + gen_madd_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; - case OPC2_32_BO_LD_B_BR: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_SB); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + case OPC2_32_RRR1_MADD_H_UL: + gen_madd_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; - case OPC2_32_BO_LD_B_CIRC: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_SB); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + case OPC2_32_RRR1_MADD_H_UU: + gen_madd_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; - case OPC2_32_BO_LD_BU_BR: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_UB); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + case OPC2_32_RRR1_MADDS_H_LL: + gen_madds_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; - case OPC2_32_BO_LD_BU_CIRC: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_UB); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + case OPC2_32_RRR1_MADDS_H_LU: + gen_madds_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); + break; + case OPC2_32_RRR1_MADDS_H_UL: + gen_madds_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); + break; + case OPC2_32_RRR1_MADDS_H_UU: + gen_madds_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); + break; + case OPC2_32_RRR1_MADDM_H_LL: + gen_maddm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); + break; + case OPC2_32_RRR1_MADDM_H_LU: + gen_maddm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); + break; + case OPC2_32_RRR1_MADDM_H_UL: + gen_maddm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; - case OPC2_32_BO_LD_D_BR: - gen_ld_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], temp2, ctx); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + case OPC2_32_RRR1_MADDM_H_UU: + gen_maddm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; - case OPC2_32_BO_LD_D_CIRC: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL); - tcg_gen_shri_tl(temp2, cpu_gpr_a[r2+1], 16); - tcg_gen_addi_tl(temp, temp, 4); - tcg_gen_rem_tl(temp, temp, temp2); - tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp); - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1+1], temp2, ctx->mem_idx, MO_LEUL); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + case OPC2_32_RRR1_MADDMS_H_LL: + gen_maddms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; - case OPC2_32_BO_LD_DA_BR: - gen_ld_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], temp2, ctx); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + case OPC2_32_RRR1_MADDMS_H_LU: + gen_maddms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; - case OPC2_32_BO_LD_DA_CIRC: - tcg_gen_qemu_ld_tl(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL); - tcg_gen_shri_tl(temp2, cpu_gpr_a[r2+1], 16); - tcg_gen_addi_tl(temp, temp, 4); - tcg_gen_rem_tl(temp, temp, temp2); - tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp); - tcg_gen_qemu_ld_tl(cpu_gpr_a[r1+1], temp2, ctx->mem_idx, MO_LEUL); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + case OPC2_32_RRR1_MADDMS_H_UL: + gen_maddms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; - case OPC2_32_BO_LD_H_BR: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LESW); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + case OPC2_32_RRR1_MADDMS_H_UU: + gen_maddms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; - case OPC2_32_BO_LD_H_CIRC: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LESW); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + case OPC2_32_RRR1_MADDR_H_LL: + gen_maddr32_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, MODE_LL); break; - case OPC2_32_BO_LD_HU_BR: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + case OPC2_32_RRR1_MADDR_H_LU: + gen_maddr32_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, MODE_LU); break; - case OPC2_32_BO_LD_HU_CIRC: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + case OPC2_32_RRR1_MADDR_H_UL: + gen_maddr32_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, MODE_UL); break; - case OPC2_32_BO_LD_Q_BR: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW); - tcg_gen_shli_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], 16); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + case OPC2_32_RRR1_MADDR_H_UU: + gen_maddr32_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, MODE_UU); break; - case OPC2_32_BO_LD_Q_CIRC: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW); - tcg_gen_shli_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], 16); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + case OPC2_32_RRR1_MADDRS_H_LL: + gen_maddr32s_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, MODE_LL); break; - case OPC2_32_BO_LD_W_BR: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + case OPC2_32_RRR1_MADDRS_H_LU: + gen_maddr32s_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, MODE_LU); break; - case OPC2_32_BO_LD_W_CIRC: - tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + case OPC2_32_RRR1_MADDRS_H_UL: + gen_maddr32s_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, MODE_UL); + break; + case OPC2_32_RRR1_MADDRS_H_UU: + gen_maddr32s_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, MODE_UU); + break; + } +} + +static void decode_rrr1_maddq_h(CPUTriCoreState *env, DisasContext *ctx) +{ + uint32_t op2; + uint32_t r1, r2, r3, r4, n; + TCGv temp, temp2; + + op2 = MASK_OP_RRR1_OP2(ctx->opcode); + r1 = MASK_OP_RRR1_S1(ctx->opcode); + r2 = MASK_OP_RRR1_S2(ctx->opcode); + r3 = MASK_OP_RRR1_S3(ctx->opcode); + r4 = MASK_OP_RRR1_D(ctx->opcode); + n = MASK_OP_RRR1_N(ctx->opcode); + + temp = tcg_const_i32(n); + temp2 = tcg_temp_new(); + + switch (op2) { + case OPC2_32_RRR1_MADD_Q_32: + gen_madd32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, 32, env); + break; + case OPC2_32_RRR1_MADD_Q_64: + gen_madd64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + n, env); + break; + case OPC2_32_RRR1_MADD_Q_32_L: + tcg_gen_ext16s_tl(temp, cpu_gpr_d[r2]); + gen_madd32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + temp, n, 16, env); + break; + case OPC2_32_RRR1_MADD_Q_64_L: + tcg_gen_ext16s_tl(temp, cpu_gpr_d[r2]); + gen_madd64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], temp, + n, env); + break; + case OPC2_32_RRR1_MADD_Q_32_U: + tcg_gen_sari_tl(temp, cpu_gpr_d[r2], 16); + gen_madd32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + temp, n, 16, env); + break; + case OPC2_32_RRR1_MADD_Q_64_U: + tcg_gen_sari_tl(temp, cpu_gpr_d[r2], 16); + gen_madd64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], temp, + n, env); + break; + case OPC2_32_RRR1_MADD_Q_32_LL: + tcg_gen_ext16s_tl(temp, cpu_gpr_d[r1]); + tcg_gen_ext16s_tl(temp2, cpu_gpr_d[r2]); + gen_m16add32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); + break; + case OPC2_32_RRR1_MADD_Q_64_LL: + tcg_gen_ext16s_tl(temp, cpu_gpr_d[r1]); + tcg_gen_ext16s_tl(temp2, cpu_gpr_d[r2]); + gen_m16add64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], temp, temp2, n); + break; + case OPC2_32_RRR1_MADD_Q_32_UU: + tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 16); + tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); + gen_m16add32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); + break; + case OPC2_32_RRR1_MADD_Q_64_UU: + tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 16); + tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); + gen_m16add64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], temp, temp2, n); + break; + case OPC2_32_RRR1_MADDS_Q_32: + gen_madds32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, 32); + break; + case OPC2_32_RRR1_MADDS_Q_64: + gen_madds64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + n); + break; + case OPC2_32_RRR1_MADDS_Q_32_L: + tcg_gen_ext16s_tl(temp, cpu_gpr_d[r2]); + gen_madds32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + temp, n, 16); + break; + case OPC2_32_RRR1_MADDS_Q_64_L: + tcg_gen_ext16s_tl(temp, cpu_gpr_d[r2]); + gen_madds64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], temp, + n); + break; + case OPC2_32_RRR1_MADDS_Q_32_U: + tcg_gen_sari_tl(temp, cpu_gpr_d[r2], 16); + gen_madds32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + temp, n, 16); + break; + case OPC2_32_RRR1_MADDS_Q_64_U: + tcg_gen_sari_tl(temp, cpu_gpr_d[r2], 16); + gen_madds64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], temp, + n); + break; + case OPC2_32_RRR1_MADDS_Q_32_LL: + tcg_gen_ext16s_tl(temp, cpu_gpr_d[r1]); + tcg_gen_ext16s_tl(temp2, cpu_gpr_d[r2]); + gen_m16adds32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); + break; + case OPC2_32_RRR1_MADDS_Q_64_LL: + tcg_gen_ext16s_tl(temp, cpu_gpr_d[r1]); + tcg_gen_ext16s_tl(temp2, cpu_gpr_d[r2]); + gen_m16adds64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], temp, temp2, n); + break; + case OPC2_32_RRR1_MADDS_Q_32_UU: + tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 16); + tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); + gen_m16adds32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); + break; + case OPC2_32_RRR1_MADDS_Q_64_UU: + tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 16); + tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); + gen_m16adds64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], temp, temp2, n); + break; + case OPC2_32_RRR1_MADDR_H_64_UL: + gen_maddr64_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r3+1], + cpu_gpr_d[r1], cpu_gpr_d[r2], n, 2); + break; + case OPC2_32_RRR1_MADDRS_H_64_UL: + gen_maddr64s_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r3+1], + cpu_gpr_d[r1], cpu_gpr_d[r2], n, 2); + break; + case OPC2_32_RRR1_MADDR_Q_32_LL: + tcg_gen_ext16s_tl(temp, cpu_gpr_d[r1]); + tcg_gen_ext16s_tl(temp2, cpu_gpr_d[r2]); + gen_maddr_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); + break; + case OPC2_32_RRR1_MADDR_Q_32_UU: + tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 16); + tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); + gen_maddr_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); + break; + case OPC2_32_RRR1_MADDRS_Q_32_LL: + tcg_gen_ext16s_tl(temp, cpu_gpr_d[r1]); + tcg_gen_ext16s_tl(temp2, cpu_gpr_d[r2]); + gen_maddrs_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); + break; + case OPC2_32_RRR1_MADDRS_Q_32_UU: + tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 16); + tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); + gen_maddrs_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); break; } tcg_temp_free(temp); tcg_temp_free(temp2); - tcg_temp_free(temp3); } -static void decode_bo_addrmode_stctx_post_pre_base(CPUTriCoreState *env, - DisasContext *ctx) +static void decode_rrr1_maddsu_h(CPUTriCoreState *env, DisasContext *ctx) { uint32_t op2; - uint32_t off10; - int r1, r2; + uint32_t r1, r2, r3, r4, n; - TCGv temp, temp2; + op2 = MASK_OP_RRR1_OP2(ctx->opcode); + r1 = MASK_OP_RRR1_S1(ctx->opcode); + r2 = MASK_OP_RRR1_S2(ctx->opcode); + r3 = MASK_OP_RRR1_S3(ctx->opcode); + r4 = MASK_OP_RRR1_D(ctx->opcode); + n = MASK_OP_RRR1_N(ctx->opcode); - r1 = MASK_OP_BO_S1D(ctx->opcode); - r2 = MASK_OP_BO_S2(ctx->opcode); - off10 = MASK_OP_BO_OFF10_SEXT(ctx->opcode); - op2 = MASK_OP_BO_OP2(ctx->opcode); + switch (op2) { + case OPC2_32_RRR1_MADDSU_H_32_LL: + gen_maddsu_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); + break; + case OPC2_32_RRR1_MADDSU_H_32_LU: + gen_maddsu_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); + break; + case OPC2_32_RRR1_MADDSU_H_32_UL: + gen_maddsu_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); + break; + case OPC2_32_RRR1_MADDSU_H_32_UU: + gen_maddsu_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); + break; + case OPC2_32_RRR1_MADDSUS_H_32_LL: + gen_maddsus_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + n, MODE_LL); + break; + case OPC2_32_RRR1_MADDSUS_H_32_LU: + gen_maddsus_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + n, MODE_LU); + break; + case OPC2_32_RRR1_MADDSUS_H_32_UL: + gen_maddsus_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + n, MODE_UL); + break; + case OPC2_32_RRR1_MADDSUS_H_32_UU: + gen_maddsus_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + n, MODE_UU); + break; + case OPC2_32_RRR1_MADDSUM_H_64_LL: + gen_maddsum_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + n, MODE_LL); + break; + case OPC2_32_RRR1_MADDSUM_H_64_LU: + gen_maddsum_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + n, MODE_LU); + break; + case OPC2_32_RRR1_MADDSUM_H_64_UL: + gen_maddsum_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + n, MODE_UL); + break; + case OPC2_32_RRR1_MADDSUM_H_64_UU: + gen_maddsum_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + n, MODE_UU); + break; + case OPC2_32_RRR1_MADDSUMS_H_64_LL: + gen_maddsums_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + n, MODE_LL); + break; + case OPC2_32_RRR1_MADDSUMS_H_64_LU: + gen_maddsums_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + n, MODE_LU); + break; + case OPC2_32_RRR1_MADDSUMS_H_64_UL: + gen_maddsums_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + n, MODE_UL); + break; + case OPC2_32_RRR1_MADDSUMS_H_64_UU: + gen_maddsums_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + n, MODE_UU); + break; + case OPC2_32_RRR1_MADDSUR_H_16_LL: + gen_maddsur32_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, MODE_LL); + break; + case OPC2_32_RRR1_MADDSUR_H_16_LU: + gen_maddsur32_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, MODE_LU); + break; + case OPC2_32_RRR1_MADDSUR_H_16_UL: + gen_maddsur32_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, MODE_UL); + break; + case OPC2_32_RRR1_MADDSUR_H_16_UU: + gen_maddsur32_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, MODE_UU); + break; + case OPC2_32_RRR1_MADDSURS_H_16_LL: + gen_maddsur32s_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, MODE_LL); + break; + case OPC2_32_RRR1_MADDSURS_H_16_LU: + gen_maddsur32s_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, MODE_LU); + break; + case OPC2_32_RRR1_MADDSURS_H_16_UL: + gen_maddsur32s_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, MODE_UL); + break; + case OPC2_32_RRR1_MADDSURS_H_16_UU: + gen_maddsur32s_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, MODE_UU); + break; + } +} +static void decode_rrr1_msub(CPUTriCoreState *env, DisasContext *ctx) +{ + uint32_t op2; + uint32_t r1, r2, r3, r4, n; - temp = tcg_temp_new(); - temp2 = tcg_temp_new(); + op2 = MASK_OP_RRR1_OP2(ctx->opcode); + r1 = MASK_OP_RRR1_S1(ctx->opcode); + r2 = MASK_OP_RRR1_S2(ctx->opcode); + r3 = MASK_OP_RRR1_S3(ctx->opcode); + r4 = MASK_OP_RRR1_D(ctx->opcode); + n = MASK_OP_RRR1_N(ctx->opcode); switch (op2) { - case OPC2_32_BO_LDLCX_SHORTOFF: - tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); - gen_helper_ldlcx(cpu_env, temp); + case OPC2_32_RRR1_MSUB_H_LL: + gen_msub_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; - case OPC2_32_BO_LDMST_SHORTOFF: - tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); - gen_ldmst(ctx, r1, temp); + case OPC2_32_RRR1_MSUB_H_LU: + gen_msub_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; - case OPC2_32_BO_LDMST_POSTINC: - gen_ldmst(ctx, r1, cpu_gpr_a[r2]); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + case OPC2_32_RRR1_MSUB_H_UL: + gen_msub_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; - case OPC2_32_BO_LDMST_PREINC: - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); - gen_ldmst(ctx, r1, cpu_gpr_a[r2]); + case OPC2_32_RRR1_MSUB_H_UU: + gen_msub_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; - case OPC2_32_BO_LDUCX_SHORTOFF: - tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); - gen_helper_lducx(cpu_env, temp); + case OPC2_32_RRR1_MSUBS_H_LL: + gen_msubs_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; - case OPC2_32_BO_LEA_SHORTOFF: - tcg_gen_addi_tl(cpu_gpr_a[r1], cpu_gpr_a[r2], off10); + case OPC2_32_RRR1_MSUBS_H_LU: + gen_msubs_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; - case OPC2_32_BO_STLCX_SHORTOFF: - tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); - gen_helper_stlcx(cpu_env, temp); + case OPC2_32_RRR1_MSUBS_H_UL: + gen_msubs_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); break; - case OPC2_32_BO_STUCX_SHORTOFF: - tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); - gen_helper_stucx(cpu_env, temp); + case OPC2_32_RRR1_MSUBS_H_UU: + gen_msubs_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); break; - case OPC2_32_BO_SWAP_W_SHORTOFF: - tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); - gen_swap(ctx, r1, temp); + case OPC2_32_RRR1_MSUBM_H_LL: + gen_msubm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); break; - case OPC2_32_BO_SWAP_W_POSTINC: - gen_swap(ctx, r1, cpu_gpr_a[r2]); - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); + case OPC2_32_RRR1_MSUBM_H_LU: + gen_msubm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); break; - case OPC2_32_BO_SWAP_W_PREINC: - tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); - gen_swap(ctx, r1, cpu_gpr_a[r2]); + case OPC2_32_RRR1_MSUBM_H_UL: + gen_msubm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); + break; + case OPC2_32_RRR1_MSUBM_H_UU: + gen_msubm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); + break; + case OPC2_32_RRR1_MSUBMS_H_LL: + gen_msubms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); + break; + case OPC2_32_RRR1_MSUBMS_H_LU: + gen_msubms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); + break; + case OPC2_32_RRR1_MSUBMS_H_UL: + gen_msubms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); + break; + case OPC2_32_RRR1_MSUBMS_H_UU: + gen_msubms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); + break; + case OPC2_32_RRR1_MSUBR_H_LL: + gen_msubr32_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, MODE_LL); + break; + case OPC2_32_RRR1_MSUBR_H_LU: + gen_msubr32_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, MODE_LU); + break; + case OPC2_32_RRR1_MSUBR_H_UL: + gen_msubr32_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, MODE_UL); + break; + case OPC2_32_RRR1_MSUBR_H_UU: + gen_msubr32_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, MODE_UU); + break; + case OPC2_32_RRR1_MSUBRS_H_LL: + gen_msubr32s_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, MODE_LL); + break; + case OPC2_32_RRR1_MSUBRS_H_LU: + gen_msubr32s_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, MODE_LU); + break; + case OPC2_32_RRR1_MSUBRS_H_UL: + gen_msubr32s_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, MODE_UL); + break; + case OPC2_32_RRR1_MSUBRS_H_UU: + gen_msubr32s_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, MODE_UU); + break; + } +} + +static void decode_rrr1_msubq_h(CPUTriCoreState *env, DisasContext *ctx) +{ + uint32_t op2; + uint32_t r1, r2, r3, r4, n; + TCGv temp, temp2; + + op2 = MASK_OP_RRR1_OP2(ctx->opcode); + r1 = MASK_OP_RRR1_S1(ctx->opcode); + r2 = MASK_OP_RRR1_S2(ctx->opcode); + r3 = MASK_OP_RRR1_S3(ctx->opcode); + r4 = MASK_OP_RRR1_D(ctx->opcode); + n = MASK_OP_RRR1_N(ctx->opcode); + + temp = tcg_const_i32(n); + temp2 = tcg_temp_new(); + + switch (op2) { + case OPC2_32_RRR1_MSUB_Q_32: + gen_msub32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, 32, env); + break; + case OPC2_32_RRR1_MSUB_Q_64: + gen_msub64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + n, env); + break; + case OPC2_32_RRR1_MSUB_Q_32_L: + tcg_gen_ext16s_tl(temp, cpu_gpr_d[r2]); + gen_msub32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + temp, n, 16, env); + break; + case OPC2_32_RRR1_MSUB_Q_64_L: + tcg_gen_ext16s_tl(temp, cpu_gpr_d[r2]); + gen_msub64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], temp, + n, env); + break; + case OPC2_32_RRR1_MSUB_Q_32_U: + tcg_gen_sari_tl(temp, cpu_gpr_d[r2], 16); + gen_msub32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + temp, n, 16, env); + break; + case OPC2_32_RRR1_MSUB_Q_64_U: + tcg_gen_sari_tl(temp, cpu_gpr_d[r2], 16); + gen_msub64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], temp, + n, env); + break; + case OPC2_32_RRR1_MSUB_Q_32_LL: + tcg_gen_ext16s_tl(temp, cpu_gpr_d[r1]); + tcg_gen_ext16s_tl(temp2, cpu_gpr_d[r2]); + gen_m16sub32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); + break; + case OPC2_32_RRR1_MSUB_Q_64_LL: + tcg_gen_ext16s_tl(temp, cpu_gpr_d[r1]); + tcg_gen_ext16s_tl(temp2, cpu_gpr_d[r2]); + gen_m16sub64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], temp, temp2, n); + break; + case OPC2_32_RRR1_MSUB_Q_32_UU: + tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 16); + tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); + gen_m16sub32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); + break; + case OPC2_32_RRR1_MSUB_Q_64_UU: + tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 16); + tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); + gen_m16sub64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], temp, temp2, n); + break; + case OPC2_32_RRR1_MSUBS_Q_32: + gen_msubs32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, 32); + break; + case OPC2_32_RRR1_MSUBS_Q_64: + gen_msubs64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + n); + break; + case OPC2_32_RRR1_MSUBS_Q_32_L: + tcg_gen_ext16s_tl(temp, cpu_gpr_d[r2]); + gen_msubs32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + temp, n, 16); + break; + case OPC2_32_RRR1_MSUBS_Q_64_L: + tcg_gen_ext16s_tl(temp, cpu_gpr_d[r2]); + gen_msubs64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], temp, + n); + break; + case OPC2_32_RRR1_MSUBS_Q_32_U: + tcg_gen_sari_tl(temp, cpu_gpr_d[r2], 16); + gen_msubs32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + temp, n, 16); + break; + case OPC2_32_RRR1_MSUBS_Q_64_U: + tcg_gen_sari_tl(temp, cpu_gpr_d[r2], 16); + gen_msubs64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], temp, + n); + break; + case OPC2_32_RRR1_MSUBS_Q_32_LL: + tcg_gen_ext16s_tl(temp, cpu_gpr_d[r1]); + tcg_gen_ext16s_tl(temp2, cpu_gpr_d[r2]); + gen_m16subs32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); + break; + case OPC2_32_RRR1_MSUBS_Q_64_LL: + tcg_gen_ext16s_tl(temp, cpu_gpr_d[r1]); + tcg_gen_ext16s_tl(temp2, cpu_gpr_d[r2]); + gen_m16subs64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], temp, temp2, n); + break; + case OPC2_32_RRR1_MSUBS_Q_32_UU: + tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 16); + tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); + gen_m16subs32_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); + break; + case OPC2_32_RRR1_MSUBS_Q_64_UU: + tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 16); + tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); + gen_m16subs64_q(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], temp, temp2, n); + break; + case OPC2_32_RRR1_MSUBR_H_64_UL: + gen_msubr64_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r3+1], + cpu_gpr_d[r1], cpu_gpr_d[r2], n, 2); + break; + case OPC2_32_RRR1_MSUBRS_H_64_UL: + gen_msubr64s_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r3+1], + cpu_gpr_d[r1], cpu_gpr_d[r2], n, 2); + break; + case OPC2_32_RRR1_MSUBR_Q_32_LL: + tcg_gen_ext16s_tl(temp, cpu_gpr_d[r1]); + tcg_gen_ext16s_tl(temp2, cpu_gpr_d[r2]); + gen_msubr_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); + break; + case OPC2_32_RRR1_MSUBR_Q_32_UU: + tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 16); + tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); + gen_msubr_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); + break; + case OPC2_32_RRR1_MSUBRS_Q_32_LL: + tcg_gen_ext16s_tl(temp, cpu_gpr_d[r1]); + tcg_gen_ext16s_tl(temp2, cpu_gpr_d[r2]); + gen_msubrs_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); + break; + case OPC2_32_RRR1_MSUBRS_Q_32_UU: + tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 16); + tcg_gen_sari_tl(temp2, cpu_gpr_d[r2], 16); + gen_msubrs_q(cpu_gpr_d[r4], cpu_gpr_d[r3], temp, temp2, n); break; } tcg_temp_free(temp); tcg_temp_free(temp2); } -static void decode_bo_addrmode_ldmst_bitreverse_circular(CPUTriCoreState *env, - DisasContext *ctx) +static void decode_rrr1_msubad_h(CPUTriCoreState *env, DisasContext *ctx) { uint32_t op2; - uint32_t off10; - int r1, r2; + uint32_t r1, r2, r3, r4, n; - TCGv temp, temp2, temp3; + op2 = MASK_OP_RRR1_OP2(ctx->opcode); + r1 = MASK_OP_RRR1_S1(ctx->opcode); + r2 = MASK_OP_RRR1_S2(ctx->opcode); + r3 = MASK_OP_RRR1_S3(ctx->opcode); + r4 = MASK_OP_RRR1_D(ctx->opcode); + n = MASK_OP_RRR1_N(ctx->opcode); - r1 = MASK_OP_BO_S1D(ctx->opcode); - r2 = MASK_OP_BO_S2(ctx->opcode); - off10 = MASK_OP_BO_OFF10_SEXT(ctx->opcode); - op2 = MASK_OP_BO_OP2(ctx->opcode); + switch (op2) { + case OPC2_32_RRR1_MSUBAD_H_32_LL: + gen_msubad_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LL); + break; + case OPC2_32_RRR1_MSUBAD_H_32_LU: + gen_msubad_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_LU); + break; + case OPC2_32_RRR1_MSUBAD_H_32_UL: + gen_msubad_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UL); + break; + case OPC2_32_RRR1_MSUBAD_H_32_UU: + gen_msubad_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], n, MODE_UU); + break; + case OPC2_32_RRR1_MSUBADS_H_32_LL: + gen_msubads_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + n, MODE_LL); + break; + case OPC2_32_RRR1_MSUBADS_H_32_LU: + gen_msubads_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + n, MODE_LU); + break; + case OPC2_32_RRR1_MSUBADS_H_32_UL: + gen_msubads_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + n, MODE_UL); + break; + case OPC2_32_RRR1_MSUBADS_H_32_UU: + gen_msubads_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + n, MODE_UU); + break; + case OPC2_32_RRR1_MSUBADM_H_64_LL: + gen_msubadm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + n, MODE_LL); + break; + case OPC2_32_RRR1_MSUBADM_H_64_LU: + gen_msubadm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + n, MODE_LU); + break; + case OPC2_32_RRR1_MSUBADM_H_64_UL: + gen_msubadm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + n, MODE_UL); + break; + case OPC2_32_RRR1_MSUBADM_H_64_UU: + gen_msubadm_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + n, MODE_UU); + break; + case OPC2_32_RRR1_MSUBADMS_H_64_LL: + gen_msubadms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + n, MODE_LL); + break; + case OPC2_32_RRR1_MSUBADMS_H_64_LU: + gen_msubadms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + n, MODE_LU); + break; + case OPC2_32_RRR1_MSUBADMS_H_64_UL: + gen_msubadms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + n, MODE_UL); + break; + case OPC2_32_RRR1_MSUBADMS_H_64_UU: + gen_msubadms_h(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], + cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2], + n, MODE_UU); + break; + case OPC2_32_RRR1_MSUBADR_H_16_LL: + gen_msubadr32_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, MODE_LL); + break; + case OPC2_32_RRR1_MSUBADR_H_16_LU: + gen_msubadr32_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, MODE_LU); + break; + case OPC2_32_RRR1_MSUBADR_H_16_UL: + gen_msubadr32_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, MODE_UL); + break; + case OPC2_32_RRR1_MSUBADR_H_16_UU: + gen_msubadr32_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, MODE_UU); + break; + case OPC2_32_RRR1_MSUBADRS_H_16_LL: + gen_msubadr32s_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, MODE_LL); + break; + case OPC2_32_RRR1_MSUBADRS_H_16_LU: + gen_msubadr32s_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, MODE_LU); + break; + case OPC2_32_RRR1_MSUBADRS_H_16_UL: + gen_msubadr32s_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, MODE_UL); + break; + case OPC2_32_RRR1_MSUBADRS_H_16_UU: + gen_msubadr32s_h(cpu_gpr_d[r4], cpu_gpr_d[r3], cpu_gpr_d[r1], + cpu_gpr_d[r2], n, MODE_UU); + break; + } +} - temp = tcg_temp_new(); - temp2 = tcg_temp_new(); - temp3 = tcg_const_i32(off10); +/* RRRR format */ +static void decode_rrrr_extract_insert(CPUTriCoreState *env, DisasContext *ctx) +{ + uint32_t op2; + int r1, r2, r3, r4; + TCGv tmp_width, tmp_pos; - tcg_gen_ext16u_tl(temp, cpu_gpr_a[r2+1]); - tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp); + r1 = MASK_OP_RRRR_S1(ctx->opcode); + r2 = MASK_OP_RRRR_S2(ctx->opcode); + r3 = MASK_OP_RRRR_S3(ctx->opcode); + r4 = MASK_OP_RRRR_D(ctx->opcode); + op2 = MASK_OP_RRRR_OP2(ctx->opcode); + + tmp_pos = tcg_temp_new(); + tmp_width = tcg_temp_new(); switch (op2) { - case OPC2_32_BO_LDMST_BR: - gen_ldmst(ctx, r1, temp2); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + case OPC2_32_RRRR_DEXTR: + tcg_gen_andi_tl(tmp_pos, cpu_gpr_d[r3], 0x1f); + if (r1 == r2) { + tcg_gen_rotl_tl(cpu_gpr_d[r4], cpu_gpr_d[r1], tmp_pos); + } else { + tcg_gen_shl_tl(tmp_width, cpu_gpr_d[r1], tmp_pos); + tcg_gen_subfi_tl(tmp_pos, 32, tmp_pos); + tcg_gen_shr_tl(tmp_pos, cpu_gpr_d[r2], tmp_pos); + tcg_gen_or_tl(cpu_gpr_d[r4], tmp_width, tmp_pos); + } break; - case OPC2_32_BO_LDMST_CIRC: - gen_ldmst(ctx, r1, temp2); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + case OPC2_32_RRRR_EXTR: + case OPC2_32_RRRR_EXTR_U: + tcg_gen_andi_tl(tmp_width, cpu_gpr_d[r3+1], 0x1f); + tcg_gen_andi_tl(tmp_pos, cpu_gpr_d[r3], 0x1f); + tcg_gen_add_tl(tmp_pos, tmp_pos, tmp_width); + tcg_gen_subfi_tl(tmp_pos, 32, tmp_pos); + tcg_gen_shl_tl(cpu_gpr_d[r4], cpu_gpr_d[r1], tmp_pos); + tcg_gen_subfi_tl(tmp_width, 32, tmp_width); + if (op2 == OPC2_32_RRRR_EXTR) { + tcg_gen_sar_tl(cpu_gpr_d[r4], cpu_gpr_d[r4], tmp_width); + } else { + tcg_gen_shr_tl(cpu_gpr_d[r4], cpu_gpr_d[r4], tmp_width); + } break; - case OPC2_32_BO_SWAP_W_BR: - gen_swap(ctx, r1, temp2); - gen_helper_br_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1]); + case OPC2_32_RRRR_INSERT: + tcg_gen_andi_tl(tmp_width, cpu_gpr_d[r3+1], 0x1f); + tcg_gen_andi_tl(tmp_pos, cpu_gpr_d[r3], 0x1f); + gen_insert(cpu_gpr_d[r4], cpu_gpr_d[r1], cpu_gpr_d[r2], tmp_width, + tmp_pos); break; - case OPC2_32_BO_SWAP_W_CIRC: - gen_swap(ctx, r1, temp2); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + } + tcg_temp_free(tmp_pos); + tcg_temp_free(tmp_width); +} + +/* RRRW format */ +static void decode_rrrw_extract_insert(CPUTriCoreState *env, DisasContext *ctx) +{ + uint32_t op2; + int r1, r2, r3, r4; + int32_t width; + + TCGv temp, temp2; + + op2 = MASK_OP_RRRW_OP2(ctx->opcode); + r1 = MASK_OP_RRRW_S1(ctx->opcode); + r2 = MASK_OP_RRRW_S2(ctx->opcode); + r3 = MASK_OP_RRRW_S3(ctx->opcode); + r4 = MASK_OP_RRRW_D(ctx->opcode); + width = MASK_OP_RRRW_WIDTH(ctx->opcode); + + temp = tcg_temp_new(); + + switch (op2) { + case OPC2_32_RRRW_EXTR: + tcg_gen_andi_tl(temp, cpu_gpr_d[r3], 0x1f); + tcg_gen_addi_tl(temp, temp, width); + tcg_gen_subfi_tl(temp, 32, temp); + tcg_gen_shl_tl(cpu_gpr_d[r4], cpu_gpr_d[r1], temp); + tcg_gen_sari_tl(cpu_gpr_d[r4], cpu_gpr_d[r4], 32 - width); + break; + case OPC2_32_RRRW_EXTR_U: + if (width == 0) { + tcg_gen_movi_tl(cpu_gpr_d[r4], 0); + } else { + tcg_gen_andi_tl(temp, cpu_gpr_d[r3], 0x1f); + tcg_gen_shr_tl(cpu_gpr_d[r4], cpu_gpr_d[r1], temp); + tcg_gen_andi_tl(cpu_gpr_d[r4], cpu_gpr_d[r4], ~0u >> (32-width)); + } + break; + case OPC2_32_RRRW_IMASK: + temp2 = tcg_temp_new(); + + tcg_gen_andi_tl(temp, cpu_gpr_d[r3], 0x1f); + tcg_gen_movi_tl(temp2, (1 << width) - 1); + tcg_gen_shl_tl(temp2, temp2, temp); + tcg_gen_shl_tl(cpu_gpr_d[r4], cpu_gpr_d[r2], temp); + tcg_gen_mov_tl(cpu_gpr_d[r4+1], temp2); + + tcg_temp_free(temp2); + break; + case OPC2_32_RRRW_INSERT: + temp2 = tcg_temp_new(); + + tcg_gen_movi_tl(temp, width); + tcg_gen_andi_tl(temp2, cpu_gpr_d[r3], 0x1f); + gen_insert(cpu_gpr_d[r4], cpu_gpr_d[r1], cpu_gpr_d[r2], temp, temp2); + + tcg_temp_free(temp2); break; } tcg_temp_free(temp); - tcg_temp_free(temp2); - tcg_temp_free(temp3); +} + +/* SYS Format*/ +static void decode_sys_interrupts(CPUTriCoreState *env, DisasContext *ctx) +{ + uint32_t op2; + TCGLabel *l1; + TCGv tmp; + + op2 = MASK_OP_SYS_OP2(ctx->opcode); + + switch (op2) { + case OPC2_32_SYS_DEBUG: + /* raise EXCP_DEBUG */ + break; + case OPC2_32_SYS_DISABLE: + tcg_gen_andi_tl(cpu_ICR, cpu_ICR, ~MASK_ICR_IE); + break; + case OPC2_32_SYS_DSYNC: + break; + case OPC2_32_SYS_ENABLE: + tcg_gen_ori_tl(cpu_ICR, cpu_ICR, MASK_ICR_IE); + break; + case OPC2_32_SYS_ISYNC: + break; + case OPC2_32_SYS_NOP: + break; + case OPC2_32_SYS_RET: + gen_compute_branch(ctx, op2, 0, 0, 0, 0); + break; + case OPC2_32_SYS_RFE: + gen_helper_rfe(cpu_env); + tcg_gen_exit_tb(0); + ctx->bstate = BS_BRANCH; + break; + case OPC2_32_SYS_RFM: + if ((ctx->hflags & TRICORE_HFLAG_KUU) == TRICORE_HFLAG_SM) { + tmp = tcg_temp_new(); + l1 = gen_new_label(); + + tcg_gen_ld32u_tl(tmp, cpu_env, offsetof(CPUTriCoreState, DBGSR)); + tcg_gen_andi_tl(tmp, tmp, MASK_DBGSR_DE); + tcg_gen_brcondi_tl(TCG_COND_NE, tmp, 1, l1); + gen_helper_rfm(cpu_env); + gen_set_label(l1); + tcg_gen_exit_tb(0); + ctx->bstate = BS_BRANCH; + tcg_temp_free(tmp); + } else { + /* generate privilege trap */ + } + break; + case OPC2_32_SYS_RSLCX: + gen_helper_rslcx(cpu_env); + break; + case OPC2_32_SYS_SVLCX: + gen_helper_svlcx(cpu_env); + break; + case OPC2_32_SYS_TRAPSV: + /* TODO: raise sticky overflow trap */ + break; + case OPC2_32_SYS_TRAPV: + /* TODO: raise overflow trap */ + break; + } } static void decode_32Bit_opc(CPUTriCoreState *env, DisasContext *ctx) { int op1; - int32_t r1; - int32_t address; - int8_t b; + int32_t r1, r2, r3; + int32_t address, const16; + int8_t b, const4; int32_t bpos; - TCGv temp, temp2; + TCGv temp, temp2, temp3; op1 = MASK_OP_MAJOR(ctx->opcode); + /* handle JNZ.T opcode only being 7 bit long */ + if (unlikely((op1 & 0x7f) == OPCM_32_BRN_JTT)) { + op1 = OPCM_32_BRN_JTT; + } + switch (op1) { /* ABS-format */ case OPCM_32_ABS_LDW: @@ -2361,7 +7854,7 @@ static void decode_32Bit_opc(CPUTriCoreState *env, DisasContext *ctx) case OPC1_32_B_JA: case OPC1_32_B_JL: case OPC1_32_B_JLA: - address = MASK_OP_B_DISP24(ctx->opcode); + address = MASK_OP_B_DISP24_SEXT(ctx->opcode); gen_compute_branch(ctx, op1, 0, 0, 0, address); break; /* Bit-format */ @@ -2405,6 +7898,207 @@ static void decode_32Bit_opc(CPUTriCoreState *env, DisasContext *ctx) case OPCM_32_BO_ADDRMODE_LDMST_BITREVERSE_CIRCULAR: decode_bo_addrmode_ldmst_bitreverse_circular(env, ctx); break; +/* BOL-format */ + case OPC1_32_BOL_LD_A_LONGOFF: + case OPC1_32_BOL_LD_W_LONGOFF: + case OPC1_32_BOL_LEA_LONGOFF: + case OPC1_32_BOL_ST_W_LONGOFF: + case OPC1_32_BOL_ST_A_LONGOFF: + case OPC1_32_BOL_LD_B_LONGOFF: + case OPC1_32_BOL_LD_BU_LONGOFF: + case OPC1_32_BOL_LD_H_LONGOFF: + case OPC1_32_BOL_LD_HU_LONGOFF: + case OPC1_32_BOL_ST_B_LONGOFF: + case OPC1_32_BOL_ST_H_LONGOFF: + decode_bol_opc(env, ctx, op1); + break; +/* BRC Format */ + case OPCM_32_BRC_EQ_NEQ: + case OPCM_32_BRC_GE: + case OPCM_32_BRC_JLT: + case OPCM_32_BRC_JNE: + const4 = MASK_OP_BRC_CONST4_SEXT(ctx->opcode); + address = MASK_OP_BRC_DISP15_SEXT(ctx->opcode); + r1 = MASK_OP_BRC_S1(ctx->opcode); + gen_compute_branch(ctx, op1, r1, 0, const4, address); + break; +/* BRN Format */ + case OPCM_32_BRN_JTT: + address = MASK_OP_BRN_DISP15_SEXT(ctx->opcode); + r1 = MASK_OP_BRN_S1(ctx->opcode); + gen_compute_branch(ctx, op1, r1, 0, 0, address); + break; +/* BRR Format */ + case OPCM_32_BRR_EQ_NEQ: + case OPCM_32_BRR_ADDR_EQ_NEQ: + case OPCM_32_BRR_GE: + case OPCM_32_BRR_JLT: + case OPCM_32_BRR_JNE: + case OPCM_32_BRR_JNZ: + case OPCM_32_BRR_LOOP: + address = MASK_OP_BRR_DISP15_SEXT(ctx->opcode); + r2 = MASK_OP_BRR_S2(ctx->opcode); + r1 = MASK_OP_BRR_S1(ctx->opcode); + gen_compute_branch(ctx, op1, r1, r2, 0, address); + break; +/* RC Format */ + case OPCM_32_RC_LOGICAL_SHIFT: + decode_rc_logical_shift(env, ctx); + break; + case OPCM_32_RC_ACCUMULATOR: + decode_rc_accumulator(env, ctx); + break; + case OPCM_32_RC_SERVICEROUTINE: + decode_rc_serviceroutine(env, ctx); + break; + case OPCM_32_RC_MUL: + decode_rc_mul(env, ctx); + break; +/* RCPW Format */ + case OPCM_32_RCPW_MASK_INSERT: + decode_rcpw_insert(env, ctx); + break; +/* RCRR Format */ + case OPC1_32_RCRR_INSERT: + r1 = MASK_OP_RCRR_S1(ctx->opcode); + r2 = MASK_OP_RCRR_S3(ctx->opcode); + r3 = MASK_OP_RCRR_D(ctx->opcode); + const16 = MASK_OP_RCRR_CONST4(ctx->opcode); + temp = tcg_const_i32(const16); + temp2 = tcg_temp_new(); /* width*/ + temp3 = tcg_temp_new(); /* pos */ + + tcg_gen_andi_tl(temp2, cpu_gpr_d[r3+1], 0x1f); + tcg_gen_andi_tl(temp3, cpu_gpr_d[r3], 0x1f); + + gen_insert(cpu_gpr_d[r2], cpu_gpr_d[r1], temp, temp2, temp3); + + tcg_temp_free(temp); + tcg_temp_free(temp2); + tcg_temp_free(temp3); + break; +/* RCRW Format */ + case OPCM_32_RCRW_MASK_INSERT: + decode_rcrw_insert(env, ctx); + break; +/* RCR Format */ + case OPCM_32_RCR_COND_SELECT: + decode_rcr_cond_select(env, ctx); + break; + case OPCM_32_RCR_MADD: + decode_rcr_madd(env, ctx); + break; + case OPCM_32_RCR_MSUB: + decode_rcr_msub(env, ctx); + break; +/* RLC Format */ + case OPC1_32_RLC_ADDI: + case OPC1_32_RLC_ADDIH: + case OPC1_32_RLC_ADDIH_A: + case OPC1_32_RLC_MFCR: + case OPC1_32_RLC_MOV: + case OPC1_32_RLC_MOV_64: + case OPC1_32_RLC_MOV_U: + case OPC1_32_RLC_MOV_H: + case OPC1_32_RLC_MOVH_A: + case OPC1_32_RLC_MTCR: + decode_rlc_opc(env, ctx, op1); + break; +/* RR Format */ + case OPCM_32_RR_ACCUMULATOR: + decode_rr_accumulator(env, ctx); + break; + case OPCM_32_RR_LOGICAL_SHIFT: + decode_rr_logical_shift(env, ctx); + break; + case OPCM_32_RR_ADDRESS: + decode_rr_address(env, ctx); + break; + case OPCM_32_RR_IDIRECT: + decode_rr_idirect(env, ctx); + break; + case OPCM_32_RR_DIVIDE: + decode_rr_divide(env, ctx); + break; +/* RR1 Format */ + case OPCM_32_RR1_MUL: + decode_rr1_mul(env, ctx); + break; + case OPCM_32_RR1_MULQ: + decode_rr1_mulq(env, ctx); + break; +/* RR2 format */ + case OPCM_32_RR2_MUL: + decode_rr2_mul(env, ctx); + break; +/* RRPW format */ + case OPCM_32_RRPW_EXTRACT_INSERT: + decode_rrpw_extract_insert(env, ctx); + break; + case OPC1_32_RRPW_DEXTR: + r1 = MASK_OP_RRPW_S1(ctx->opcode); + r2 = MASK_OP_RRPW_S2(ctx->opcode); + r3 = MASK_OP_RRPW_D(ctx->opcode); + const16 = MASK_OP_RRPW_POS(ctx->opcode); + if (r1 == r2) { + tcg_gen_rotli_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], const16); + } else { + temp = tcg_temp_new(); + tcg_gen_shli_tl(temp, cpu_gpr_d[r1], const16); + tcg_gen_shri_tl(cpu_gpr_d[r3], cpu_gpr_d[r2], 32 - const16); + tcg_gen_or_tl(cpu_gpr_d[r3], cpu_gpr_d[r3], temp); + tcg_temp_free(temp); + } + break; +/* RRR Format */ + case OPCM_32_RRR_COND_SELECT: + decode_rrr_cond_select(env, ctx); + break; + case OPCM_32_RRR_DIVIDE: + decode_rrr_divide(env, ctx); +/* RRR2 Format */ + case OPCM_32_RRR2_MADD: + decode_rrr2_madd(env, ctx); + break; + case OPCM_32_RRR2_MSUB: + decode_rrr2_msub(env, ctx); + break; +/* RRR1 format */ + case OPCM_32_RRR1_MADD: + decode_rrr1_madd(env, ctx); + break; + case OPCM_32_RRR1_MADDQ_H: + decode_rrr1_maddq_h(env, ctx); + break; + case OPCM_32_RRR1_MADDSU_H: + decode_rrr1_maddsu_h(env, ctx); + break; + case OPCM_32_RRR1_MSUB_H: + decode_rrr1_msub(env, ctx); + break; + case OPCM_32_RRR1_MSUB_Q: + decode_rrr1_msubq_h(env, ctx); + break; + case OPCM_32_RRR1_MSUBAD_H: + decode_rrr1_msubad_h(env, ctx); + break; +/* RRRR format */ + case OPCM_32_RRRR_EXTRACT_INSERT: + decode_rrrr_extract_insert(env, ctx); +/* RRRW format */ + case OPCM_32_RRRW_EXTRACT_INSERT: + decode_rrrw_extract_insert(env, ctx); + break; +/* SYS format */ + case OPCM_32_SYS_INTERRUPTS: + decode_sys_interrupts(env, ctx); + break; + case OPC1_32_SYS_RSTV: + tcg_gen_movi_tl(cpu_PSW_V, 0); + tcg_gen_mov_tl(cpu_PSW_SV, cpu_PSW_V); + tcg_gen_mov_tl(cpu_PSW_AV, cpu_PSW_V); + tcg_gen_mov_tl(cpu_PSW_SAV, cpu_PSW_V); + break; } } @@ -2430,7 +8124,6 @@ gen_intermediate_code_internal(TriCoreCPU *cpu, struct TranslationBlock *tb, DisasContext ctx; target_ulong pc_start; int num_insns; - uint16_t *gen_opc_end; if (search_pc) { qemu_log("search pc %d\n", search_pc); @@ -2438,7 +8131,6 @@ gen_intermediate_code_internal(TriCoreCPU *cpu, struct TranslationBlock *tb, num_insns = 0; pc_start = tb->pc; - gen_opc_end = tcg_ctx.gen_opc_buf + OPC_MAX_SIZE; ctx.pc = pc_start; ctx.saved_pc = -1; ctx.tb = tb; @@ -2447,14 +8139,14 @@ gen_intermediate_code_internal(TriCoreCPU *cpu, struct TranslationBlock *tb, ctx.mem_idx = cpu_mmu_index(env); tcg_clear_temp_count(); - gen_tb_start(); + gen_tb_start(tb); while (ctx.bstate == BS_NONE) { ctx.opcode = cpu_ldl_code(env, ctx.pc); decode_opc(env, &ctx, 0); num_insns++; - if (tcg_ctx.gen_opc_ptr >= gen_opc_end) { + if (tcg_op_buf_full()) { gen_save_pc(ctx.next_pc); tcg_gen_exit_tb(0); break; @@ -2468,7 +8160,6 @@ gen_intermediate_code_internal(TriCoreCPU *cpu, struct TranslationBlock *tb, } gen_tb_end(tb, num_insns); - *tcg_ctx.gen_opc_ptr = INDEX_op_end; if (search_pc) { printf("done_generating search pc\n"); } else { diff --git a/target-tricore/tricore-opcodes.h b/target-tricore/tricore-opcodes.h index 7e6f33b..d3a9bc1 100644 --- a/target-tricore/tricore-opcodes.h +++ b/target-tricore/tricore-opcodes.h @@ -94,6 +94,8 @@ /* B Format */ #define MASK_OP_B_DISP24(op) (MASK_BITS_SHIFT(op, 16, 31) + \ (MASK_BITS_SHIFT(op, 8, 15) << 16)) +#define MASK_OP_B_DISP24_SEXT(op) (MASK_BITS_SHIFT(op, 16, 31) + \ + (MASK_BITS_SHIFT_SEXT(op, 8, 15) << 16)) /* BIT Format */ #define MASK_OP_BIT_D(op) MASK_BITS_SHIFT(op, 28, 31) #define MASK_OP_BIT_POS2(op) MASK_BITS_SHIFT(op, 23, 27) @@ -114,26 +116,32 @@ /* BOL Format */ #define MASK_OP_BOL_OFF16(op) ((MASK_BITS_SHIFT(op, 16, 21) + \ (MASK_BITS_SHIFT(op, 28, 31) << 6)) + \ - (MASK_BITS_SHIFT(op, 22, 27) >> 10)) - + (MASK_BITS_SHIFT(op, 22, 27) << 10)) +#define MASK_OP_BOL_OFF16_SEXT(op) ((MASK_BITS_SHIFT(op, 16, 21) + \ + (MASK_BITS_SHIFT(op, 28, 31) << 6)) + \ + (MASK_BITS_SHIFT_SEXT(op, 22, 27) << 10)) #define MASK_OP_BOL_S2(op) MASK_BITS_SHIFT(op, 12, 15) #define MASK_OP_BOL_S1D(op) MASK_BITS_SHIFT(op, 8, 11) /* BRC Format */ #define MASK_OP_BRC_OP2(op) MASK_BITS_SHIFT(op, 31, 31) #define MASK_OP_BRC_DISP15(op) MASK_BITS_SHIFT(op, 16, 30) +#define MASK_OP_BRC_DISP15_SEXT(op) MASK_BITS_SHIFT_SEXT(op, 16, 30) #define MASK_OP_BRC_CONST4(op) MASK_BITS_SHIFT(op, 12, 15) +#define MASK_OP_BRC_CONST4_SEXT(op) MASK_BITS_SHIFT_SEXT(op, 12, 15) #define MASK_OP_BRC_S1(op) MASK_BITS_SHIFT(op, 8, 11) /* BRN Format */ #define MASK_OP_BRN_OP2(op) MASK_BITS_SHIFT(op, 31, 31) #define MASK_OP_BRN_DISP15(op) MASK_BITS_SHIFT(op, 16, 30) +#define MASK_OP_BRN_DISP15_SEXT(op) MASK_BITS_SHIFT_SEXT(op, 16, 30) #define MASK_OP_BRN_N(op) (MASK_BITS_SHIFT(op, 12, 15) + \ (MASK_BITS_SHIFT(op, 7, 7) << 4)) #define MASK_OP_BRN_S1(op) MASK_BITS_SHIFT(op, 8, 11) /* BRR Format */ #define MASK_OP_BRR_OP2(op) MASK_BITS_SHIFT(op, 31, 31) #define MASK_OP_BRR_DISP15(op) MASK_BITS_SHIFT(op, 16, 30) +#define MASK_OP_BRR_DISP15_SEXT(op) MASK_BITS_SHIFT_SEXT(op, 16, 30) #define MASK_OP_BRR_S2(op) MASK_BITS_SHIFT(op, 12, 15) #define MASK_OP_BRR_S1(op) MASK_BITS_SHIFT(op, 8, 11) @@ -145,6 +153,7 @@ #define MASK_OP_RC_D(op) MASK_OP_META_D(op) #define MASK_OP_RC_OP2(op) MASK_BITS_SHIFT(op, 21, 27) #define MASK_OP_RC_CONST9(op) MASK_BITS_SHIFT(op, 12, 20) +#define MASK_OP_RC_CONST9_SEXT(op) MASK_BITS_SHIFT_SEXT(op, 12, 20) #define MASK_OP_RC_S1(op) MASK_OP_META_S1(op) /* RCPW Format */ @@ -162,6 +171,7 @@ #define MASK_OP_RCR_S3(op) MASK_BITS_SHIFT(op, 24, 27) #define MASK_OP_RCR_OP2(op) MASK_BITS_SHIFT(op, 21, 23) #define MASK_OP_RCR_CONST9(op) MASK_BITS_SHIFT(op, 12, 20) +#define MASK_OP_RCR_CONST9_SEXT(op) MASK_BITS_SHIFT_SEXT(op, 12, 20) #define MASK_OP_RCR_S1(op) MASK_OP_META_S1(op) /* RCRR Format */ @@ -185,6 +195,7 @@ #define MASK_OP_RLC_D(op) MASK_OP_META_D(op) #define MASK_OP_RLC_CONST16(op) MASK_BITS_SHIFT(op, 12, 27) +#define MASK_OP_RLC_CONST16_SEXT(op) MASK_BITS_SHIFT_SEXT(op, 12, 27) #define MASK_OP_RLC_S1(op) MASK_OP_META_S1(op) /* RR Format */ @@ -438,10 +449,16 @@ enum { OPCM_32_BO_ADDRMODE_LDMST_BITREVERSE_CIRCULAR = 0x69, /* BOL Format */ OPC1_32_BOL_LD_A_LONGOFF = 0x99, - OPC1_32_BOL_LD_W_LONFOFF = 0x19, + OPC1_32_BOL_LD_W_LONGOFF = 0x19, OPC1_32_BOL_LEA_LONGOFF = 0xd9, OPC1_32_BOL_ST_W_LONGOFF = 0x59, OPC1_32_BOL_ST_A_LONGOFF = 0xb5, /* 1.6 only */ + OPC1_32_BOL_LD_B_LONGOFF = 0x79, /* 1.6 only */ + OPC1_32_BOL_LD_BU_LONGOFF = 0x39, /* 1.6 only */ + OPC1_32_BOL_LD_H_LONGOFF = 0xc9, /* 1.6 only */ + OPC1_32_BOL_LD_HU_LONGOFF = 0xb9, /* 1.6 only */ + OPC1_32_BOL_ST_B_LONGOFF = 0xe9, /* 1.6 only */ + OPC1_32_BOL_ST_H_LONGOFF = 0xf9, /* 1.6 only */ /* BRC Format */ OPCM_32_BRC_EQ_NEQ = 0xdf, OPCM_32_BRC_GE = 0xff, @@ -478,6 +495,7 @@ enum { OPC1_32_RLC_ADDIH_A = 0x11, OPC1_32_RLC_MFCR = 0x4d, OPC1_32_RLC_MOV = 0x3b, + OPC1_32_RLC_MOV_64 = 0xfb, /* 1.6 only */ OPC1_32_RLC_MOV_U = 0xbb, OPC1_32_RLC_MOV_H = 0x7b, OPC1_32_RLC_MOVH_A = 0x91, @@ -485,8 +503,8 @@ enum { /* RR Format */ OPCM_32_RR_LOGICAL_SHIFT = 0x0f, OPCM_32_RR_ACCUMULATOR = 0x0b, - OPCM_32_RR_ADRESS = 0x01, - OPCM_32_RR_FLOAT = 0x4b, + OPCM_32_RR_ADDRESS = 0x01, + OPCM_32_RR_DIVIDE = 0x4b, OPCM_32_RR_IDIRECT = 0x2d, /* RR1 Format */ OPCM_32_RR1_MUL = 0xb3, @@ -498,14 +516,14 @@ enum { OPC1_32_RRPW_DEXTR = 0x77, /* RRR Format */ OPCM_32_RRR_COND_SELECT = 0x2b, - OPCM_32_RRR_FLOAT = 0x6b, + OPCM_32_RRR_DIVIDE = 0x6b, /* RRR1 Format */ OPCM_32_RRR1_MADD = 0x83, OPCM_32_RRR1_MADDQ_H = 0x43, OPCM_32_RRR1_MADDSU_H = 0xc3, OPCM_32_RRR1_MSUB_H = 0xa3, OPCM_32_RRR1_MSUB_Q = 0x63, - OPCM_32_RRR1_MSUBADS_H = 0xe3, + OPCM_32_RRR1_MSUBAD_H = 0xe3, /* RRR2 Format */ OPCM_32_RRR2_MADD = 0x03, OPCM_32_RRR2_MSUB = 0x23, @@ -763,8 +781,8 @@ enum { }; /* OPCM_32_BRC_GE */ enum { - OP2_BRC_JGE = 0x00, - OPC_BRC_JGE_U = 0x01, + OP2_32_BRC_JGE = 0x00, + OPC_32_BRC_JGE_U = 0x01, }; /* OPCM_32_BRC_JLT */ enum { @@ -937,7 +955,7 @@ enum { OPC2_32_RCR_MSUB_64 = 0x03, OPC2_32_RCR_MSUBS_32 = 0x05, OPC2_32_RCR_MSUBS_64 = 0x07, - OPC2_32_RCR_MSUB_U_32 = 0x02, + OPC2_32_RCR_MSUB_U_64 = 0x02, OPC2_32_RCR_MSUBS_U_32 = 0x04, OPC2_32_RCR_MSUBS_U_64 = 0x06, }; @@ -1024,8 +1042,8 @@ enum { OPC2_32_RR_MAX_BU = 0x5b, OPC2_32_RR_MAX_H = 0x7a, OPC2_32_RR_MAX_HU = 0x7b, - OPC2_32_RR_MIN = 0x19, - OPC2_32_RR_MIN_U = 0x18, + OPC2_32_RR_MIN = 0x18, + OPC2_32_RR_MIN_U = 0x19, OPC2_32_RR_MIN_B = 0x58, OPC2_32_RR_MIN_BU = 0x59, OPC2_32_RR_MIN_H = 0x78, @@ -1064,7 +1082,7 @@ enum { OPC2_32_RR_XOR_LT_U = 0x32, OPC2_32_RR_XOR_NE = 0x30, }; -/* OPCM_32_RR_ADRESS */ +/* OPCM_32_RR_ADDRESS */ enum { OPC2_32_RR_ADD_A = 0x01, OPC2_32_RR_ADDSC_A = 0x60, @@ -1227,10 +1245,10 @@ enum { OPC2_32_RRR1_MADDS_Q_64_LL = 0x3d, OPC2_32_RRR1_MADDS_Q_32_UU = 0x24, OPC2_32_RRR1_MADDS_Q_64_UU = 0x3c, - OPC2_32_RRR1_MADDR_H_16_UL = 0x1e, - OPC2_32_RRR1_MADDRS_H_16_UL = 0x3e, - OPC2_32_RRR1_MADDR_Q_32_L = 0x07, - OPC2_32_RRR1_MADDR_Q_32_U = 0x06, + OPC2_32_RRR1_MADDR_H_64_UL = 0x1e, + OPC2_32_RRR1_MADDRS_H_64_UL = 0x3e, + OPC2_32_RRR1_MADDR_Q_32_LL = 0x07, + OPC2_32_RRR1_MADDR_Q_32_UU = 0x06, OPC2_32_RRR1_MADDRS_Q_32_LL = 0x27, OPC2_32_RRR1_MADDRS_Q_32_UU = 0x26, }; @@ -1263,30 +1281,30 @@ enum { }; /* OPCM_32_RRR1_MSUB_H */ enum { - OPC2_32_RRR1_MSUB_H_32_LL = 0x1a, - OPC2_32_RRR1_MSUB_H_32_LU = 0x19, - OPC2_32_RRR1_MSUB_H_32_UL = 0x18, - OPC2_32_RRR1_MSUB_H_32_UU = 0x1b, - OPC2_32_RRR1_MSUBS_H_32_LL = 0x3a, - OPC2_32_RRR1_MSUBS_H_32_LU = 0x39, - OPC2_32_RRR1_MSUBS_H_32_UL = 0x38, - OPC2_32_RRR1_MSUBS_H_32_UU = 0x3b, - OPC2_32_RRR1_MSUBM_H_64_LL = 0x1e, - OPC2_32_RRR1_MSUBM_H_64_LU = 0x1d, - OPC2_32_RRR1_MSUBM_H_64_UL = 0x1c, - OPC2_32_RRR1_MSUBM_H_64_UU = 0x1f, - OPC2_32_RRR1_MSUBMS_H_64_LL = 0x3e, - OPC2_32_RRR1_MSUBMS_H_64_LU = 0x3d, - OPC2_32_RRR1_MSUBMS_H_64_UL = 0x3c, - OPC2_32_RRR1_MSUBMS_H_64_UU = 0x3f, - OPC2_32_RRR1_MSUBR_H_16_LL = 0x0e, - OPC2_32_RRR1_MSUBR_H_16_LU = 0x0d, - OPC2_32_RRR1_MSUBR_H_16_UL = 0x0c, - OPC2_32_RRR1_MSUBR_H_16_UU = 0x0f, - OPC2_32_RRR1_MSUBRS_H_16_LL = 0x2e, - OPC2_32_RRR1_MSUBRS_H_16_LU = 0x2d, - OPC2_32_RRR1_MSUBRS_H_16_UL = 0x2c, - OPC2_32_RRR1_MSUBRS_H_16_UU = 0x2f, + OPC2_32_RRR1_MSUB_H_LL = 0x1a, + OPC2_32_RRR1_MSUB_H_LU = 0x19, + OPC2_32_RRR1_MSUB_H_UL = 0x18, + OPC2_32_RRR1_MSUB_H_UU = 0x1b, + OPC2_32_RRR1_MSUBS_H_LL = 0x3a, + OPC2_32_RRR1_MSUBS_H_LU = 0x39, + OPC2_32_RRR1_MSUBS_H_UL = 0x38, + OPC2_32_RRR1_MSUBS_H_UU = 0x3b, + OPC2_32_RRR1_MSUBM_H_LL = 0x1e, + OPC2_32_RRR1_MSUBM_H_LU = 0x1d, + OPC2_32_RRR1_MSUBM_H_UL = 0x1c, + OPC2_32_RRR1_MSUBM_H_UU = 0x1f, + OPC2_32_RRR1_MSUBMS_H_LL = 0x3e, + OPC2_32_RRR1_MSUBMS_H_LU = 0x3d, + OPC2_32_RRR1_MSUBMS_H_UL = 0x3c, + OPC2_32_RRR1_MSUBMS_H_UU = 0x3f, + OPC2_32_RRR1_MSUBR_H_LL = 0x0e, + OPC2_32_RRR1_MSUBR_H_LU = 0x0d, + OPC2_32_RRR1_MSUBR_H_UL = 0x0c, + OPC2_32_RRR1_MSUBR_H_UU = 0x0f, + OPC2_32_RRR1_MSUBRS_H_LL = 0x2e, + OPC2_32_RRR1_MSUBRS_H_LU = 0x2d, + OPC2_32_RRR1_MSUBRS_H_UL = 0x2c, + OPC2_32_RRR1_MSUBRS_H_UU = 0x2f, }; /* OPCM_32_RRR1_MSUB_Q */ enum { @@ -1310,8 +1328,8 @@ enum { OPC2_32_RRR1_MSUBS_Q_64_LL = 0x3d, OPC2_32_RRR1_MSUBS_Q_32_UU = 0x24, OPC2_32_RRR1_MSUBS_Q_64_UU = 0x3c, - OPC2_32_RRR1_MSUBR_H_32_UL = 0x1e, - OPC2_32_RRR1_MSUBRS_H_32_UL = 0x3e, + OPC2_32_RRR1_MSUBR_H_64_UL = 0x1e, + OPC2_32_RRR1_MSUBRS_H_64_UL = 0x3e, OPC2_32_RRR1_MSUBR_Q_32_LL = 0x07, OPC2_32_RRR1_MSUBR_Q_32_UU = 0x06, OPC2_32_RRR1_MSUBRS_Q_32_LL = 0x27, @@ -1334,7 +1352,7 @@ enum { OPC2_32_RRR1_MSUBADMS_H_64_LL = 0x3e, OPC2_32_RRR1_MSUBADMS_H_64_LU = 0x3d, OPC2_32_RRR1_MSUBADMS_H_64_UL = 0x3c, - OPC2_32_RRR1_MSUBADMS_H_16_UU = 0x3f, + OPC2_32_RRR1_MSUBADMS_H_64_UU = 0x3f, OPC2_32_RRR1_MSUBADR_H_16_LL = 0x0e, OPC2_32_RRR1_MSUBADR_H_16_LU = 0x0d, OPC2_32_RRR1_MSUBADR_H_16_UL = 0x0c, @@ -1353,7 +1371,7 @@ enum { OPC2_32_RRR2_MADD_64 = 0x6a, OPC2_32_RRR2_MADDS_32 = 0x8a, OPC2_32_RRR2_MADDS_64 = 0xea, - OPC2_32_RRR2_MADD_U_32 = 0x68, + OPC2_32_RRR2_MADD_U_64 = 0x68, OPC2_32_RRR2_MADDS_U_32 = 0x88, OPC2_32_RRR2_MADDS_U_64 = 0xe8, }; diff --git a/target-unicore32/cpu.h b/target-unicore32/cpu.h index 50972f9..14dc862 100644 --- a/target-unicore32/cpu.h +++ b/target-unicore32/cpu.h @@ -122,11 +122,9 @@ void cpu_asr_write(CPUUniCore32State *env1, target_ulong val, target_ulong mask) #define UC32_HWCAP_CMOV 4 /* 1 << 2 */ #define UC32_HWCAP_UCF64 8 /* 1 << 3 */ -#define cpu_init uc32_cpu_init #define cpu_exec uc32_cpu_exec #define cpu_signal_handler uc32_cpu_signal_handler -CPUUniCore32State *uc32_cpu_init(const char *cpu_model); int uc32_cpu_exec(CPUUniCore32State *s); int uc32_cpu_signal_handler(int host_signum, void *pinfo, void *puc); @@ -143,6 +141,10 @@ static inline int cpu_mmu_index(CPUUniCore32State *env) #include "cpu-qom.h" #include "exec/exec-all.h" +UniCore32CPU *uc32_cpu_init(const char *cpu_model); + +#define cpu_init(cpu_model) CPU(uc32_cpu_init(cpu_model)) + static inline void cpu_get_tb_cpu_state(CPUUniCore32State *env, target_ulong *pc, target_ulong *cs_base, int *flags) { diff --git a/target-unicore32/helper.c b/target-unicore32/helper.c index b4654fa..ae63277 100644 --- a/target-unicore32/helper.c +++ b/target-unicore32/helper.c @@ -25,15 +25,9 @@ #define DPRINTF(fmt, ...) do {} while (0) #endif -CPUUniCore32State *uc32_cpu_init(const char *cpu_model) +UniCore32CPU *uc32_cpu_init(const char *cpu_model) { - UniCore32CPU *cpu; - - cpu = UNICORE32_CPU(cpu_generic_init(TYPE_UNICORE32_CPU, cpu_model)); - if (cpu == NULL) { - return NULL; - } - return &cpu->env; + return UNICORE32_CPU(cpu_generic_init(TYPE_UNICORE32_CPU, cpu_model)); } uint32_t HELPER(clo)(uint32_t x) diff --git a/target-unicore32/translate.c b/target-unicore32/translate.c index 653c225..9efcff5 100644 --- a/target-unicore32/translate.c +++ b/target-unicore32/translate.c @@ -33,7 +33,7 @@ typedef struct DisasContext { /* Nonzero if this instruction has been conditionally skipped. */ int condjmp; /* The label that will be jumped to when the instruction is skipped. */ - int condlabel; + TCGLabel *condlabel; struct TranslationBlock *tb; int singlestep_enabled; #ifndef CONFIG_USER_ONLY @@ -419,11 +419,11 @@ static inline void gen_uc32_shift_reg(TCGv var, int shiftop, dead_tmp(shift); } -static void gen_test_cc(int cc, int label) +static void gen_test_cc(int cc, TCGLabel *label) { TCGv tmp; TCGv tmp2; - int inv; + TCGLabel *inv; switch (cc) { case 0: /* eq: Z */ @@ -1877,7 +1877,6 @@ static inline void gen_intermediate_code_internal(UniCore32CPU *cpu, CPUUniCore32State *env = &cpu->env; DisasContext dc1, *dc = &dc1; CPUBreakpoint *bp; - uint16_t *gen_opc_end; int j, lj; target_ulong pc_start; uint32_t next_page_start; @@ -1891,8 +1890,6 @@ static inline void gen_intermediate_code_internal(UniCore32CPU *cpu, dc->tb = tb; - gen_opc_end = tcg_ctx.gen_opc_buf + OPC_MAX_SIZE; - dc->is_jmp = DISAS_NEXT; dc->pc = pc_start; dc->singlestep_enabled = cs->singlestep_enabled; @@ -1917,7 +1914,7 @@ static inline void gen_intermediate_code_internal(UniCore32CPU *cpu, } #endif - gen_tb_start(); + gen_tb_start(tb); do { if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) { QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { @@ -1933,7 +1930,7 @@ static inline void gen_intermediate_code_internal(UniCore32CPU *cpu, } } if (search_pc) { - j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + j = tcg_op_buf_count(); if (lj < j) { lj++; while (lj < j) { @@ -1965,7 +1962,7 @@ static inline void gen_intermediate_code_internal(UniCore32CPU *cpu, * Also stop translation when a page boundary is reached. This * ensures prefetch aborts occur at the right place. */ num_insns++; - } while (!dc->is_jmp && tcg_ctx.gen_opc_ptr < gen_opc_end && + } while (!dc->is_jmp && !tcg_op_buf_full() && !cs->singlestep_enabled && !singlestep && dc->pc < next_page_start && @@ -2037,7 +2034,6 @@ static inline void gen_intermediate_code_internal(UniCore32CPU *cpu, done_generating: gen_tb_end(tb, num_insns); - *tcg_ctx.gen_opc_ptr = INDEX_op_end; #ifdef DEBUG_DISAS if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { @@ -2048,7 +2044,7 @@ done_generating: } #endif if (search_pc) { - j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + j = tcg_op_buf_count(); lj++; while (lj <= j) { tcg_ctx.gen_opc_instr_start[lj++] = 0; diff --git a/target-xtensa/cpu-qom.h b/target-xtensa/cpu-qom.h index 9de5c6e..2258224 100644 --- a/target-xtensa/cpu-qom.h +++ b/target-xtensa/cpu-qom.h @@ -85,6 +85,9 @@ static inline XtensaCPU *xtensa_env_get_cpu(const CPUXtensaState *env) void xtensa_cpu_do_interrupt(CPUState *cpu); bool xtensa_cpu_exec_interrupt(CPUState *cpu, int interrupt_request); +void xtensa_cpu_do_unassigned_access(CPUState *cpu, hwaddr addr, + bool is_write, bool is_exec, int opaque, + unsigned size); void xtensa_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, int flags); hwaddr xtensa_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); diff --git a/target-xtensa/cpu.c b/target-xtensa/cpu.c index 6a5414f..2b75678 100644 --- a/target-xtensa/cpu.c +++ b/target-xtensa/cpu.c @@ -151,6 +151,7 @@ static void xtensa_cpu_class_init(ObjectClass *oc, void *data) #ifndef CONFIG_USER_ONLY cc->do_unaligned_access = xtensa_cpu_do_unaligned_access; cc->get_phys_page_debug = xtensa_cpu_get_phys_page_debug; + cc->do_unassigned_access = xtensa_cpu_do_unassigned_access; #endif cc->debug_excp_handler = xtensa_breakpoint_handler; dc->vmsd = &vmstate_xtensa_cpu; diff --git a/target-xtensa/cpu.h b/target-xtensa/cpu.h index ac463f2..dfd0d1c 100644 --- a/target-xtensa/cpu.h +++ b/target-xtensa/cpu.h @@ -39,8 +39,6 @@ #include "exec/cpu-defs.h" #include "fpu/softfloat.h" -#define TARGET_HAS_ICE 1 - #define NB_MMU_MODES 4 #define TARGET_PHYS_ADDR_SPACE_BITS 32 @@ -381,14 +379,7 @@ typedef struct CPUXtensaState { XtensaCPU *cpu_xtensa_init(const char *cpu_model); -static inline CPUXtensaState *cpu_init(const char *cpu_model) -{ - XtensaCPU *cpu = cpu_xtensa_init(cpu_model); - if (cpu == NULL) { - return NULL; - } - return &cpu->env; -} +#define cpu_init(cpu_model) CPU(cpu_xtensa_init(cpu_model)) void xtensa_translate_init(void); void xtensa_breakpoint_handler(CPUState *cs); @@ -497,6 +488,8 @@ static inline int cpu_mmu_index(CPUXtensaState *env) #define XTENSA_TBFLAG_CPENABLE_MASK 0x3fc0 #define XTENSA_TBFLAG_CPENABLE_SHIFT 6 #define XTENSA_TBFLAG_EXCEPTION 0x4000 +#define XTENSA_TBFLAG_WINDOW_MASK 0x18000 +#define XTENSA_TBFLAG_WINDOW_SHIFT 15 static inline void cpu_get_tb_cpu_state(CPUXtensaState *env, target_ulong *pc, target_ulong *cs_base, int *flags) @@ -528,6 +521,16 @@ static inline void cpu_get_tb_cpu_state(CPUXtensaState *env, target_ulong *pc, if (cs->singlestep_enabled && env->exception_taken) { *flags |= XTENSA_TBFLAG_EXCEPTION; } + if (xtensa_option_enabled(env->config, XTENSA_OPTION_WINDOWED_REGISTER) && + (env->sregs[PS] & (PS_WOE | PS_EXCM)) == PS_WOE) { + uint32_t windowstart = xtensa_replicate_windowstart(env) >> + (env->sregs[WINDOW_BASE] + 1); + uint32_t w = ctz32(windowstart | 0x8); + + *flags |= w << XTENSA_TBFLAG_WINDOW_SHIFT; + } else { + *flags |= 3 << XTENSA_TBFLAG_WINDOW_SHIFT; + } } #include "exec/cpu-all.h" diff --git a/target-xtensa/helper.h b/target-xtensa/helper.h index ed3af0b..5ea9c5b 100644 --- a/target-xtensa/helper.h +++ b/target-xtensa/helper.h @@ -9,7 +9,7 @@ DEF_HELPER_2(wsr_windowbase, void, env, i32) DEF_HELPER_4(entry, void, env, i32, i32, i32) DEF_HELPER_2(retw, i32, env, i32) DEF_HELPER_2(rotw, void, env, i32) -DEF_HELPER_3(window_check, void, env, i32, i32) +DEF_HELPER_3(window_check, noreturn, env, i32, i32) DEF_HELPER_1(restore_owb, void, env) DEF_HELPER_2(movsp, void, env, i32) DEF_HELPER_2(wsr_lbeg, void, env, i32) diff --git a/target-xtensa/op_helper.c b/target-xtensa/op_helper.c index 872e5a8..be657e6 100644 --- a/target-xtensa/op_helper.c +++ b/target-xtensa/op_helper.c @@ -71,6 +71,20 @@ void tlb_fill(CPUState *cs, } } +void xtensa_cpu_do_unassigned_access(CPUState *cs, hwaddr addr, + bool is_write, bool is_exec, int opaque, + unsigned size) +{ + XtensaCPU *cpu = XTENSA_CPU(cs); + CPUXtensaState *env = &cpu->env; + + HELPER(exception_cause_vaddr)(env, env->pc, + is_exec ? + INSTR_PIF_ADDR_ERROR_CAUSE : + LOAD_STORE_PIF_ADDR_ERROR_CAUSE, + is_exec ? addr : cs->mem_io_vaddr); +} + static void tb_invalidate_virtual_addr(CPUXtensaState *env, uint32_t vaddr) { uint32_t paddr; @@ -251,34 +265,27 @@ void HELPER(entry)(CPUXtensaState *env, uint32_t pc, uint32_t s, uint32_t imm) void HELPER(window_check)(CPUXtensaState *env, uint32_t pc, uint32_t w) { uint32_t windowbase = windowbase_bound(env->sregs[WINDOW_BASE], env); - uint32_t windowstart = env->sregs[WINDOW_START]; - uint32_t m, n; + uint32_t windowstart = xtensa_replicate_windowstart(env) >> + (env->sregs[WINDOW_BASE] + 1); + uint32_t n = ctz32(windowstart) + 1; - if ((env->sregs[PS] & (PS_WOE | PS_EXCM)) ^ PS_WOE) { - return; - } + assert(n <= w); - for (n = 1; ; ++n) { - if (n > w) { - return; - } - if (windowstart & windowstart_bit(windowbase + n, env)) { - break; - } - } - - m = windowbase_bound(windowbase + n, env); rotate_window(env, n); env->sregs[PS] = (env->sregs[PS] & ~PS_OWB) | (windowbase << PS_OWB_SHIFT) | PS_EXCM; env->sregs[EPC1] = env->pc = pc; - if (windowstart & windowstart_bit(m + 1, env)) { + switch (ctz32(windowstart >> n)) { + case 0: HELPER(exception)(env, EXC_WINDOW_OVERFLOW4); - } else if (windowstart & windowstart_bit(m + 2, env)) { + break; + case 1: HELPER(exception)(env, EXC_WINDOW_OVERFLOW8); - } else { + break; + default: HELPER(exception)(env, EXC_WINDOW_OVERFLOW12); + break; } } diff --git a/target-xtensa/translate.c b/target-xtensa/translate.c index badca19..6e5096c 100644 --- a/target-xtensa/translate.c +++ b/target-xtensa/translate.c @@ -63,7 +63,7 @@ typedef struct DisasContext { TCGv_i32 sar_m32; uint32_t ccount_delta; - unsigned used_window; + unsigned window; bool debug; bool icount; @@ -311,26 +311,16 @@ static void gen_left_shift_sar(DisasContext *dc, TCGv_i32 sa) tcg_temp_free(tmp); } -static void gen_advance_ccount_cond(DisasContext *dc) +static void gen_advance_ccount(DisasContext *dc) { if (dc->ccount_delta > 0) { TCGv_i32 tmp = tcg_const_i32(dc->ccount_delta); gen_helper_advance_ccount(cpu_env, tmp); tcg_temp_free(tmp); } -} - -static void gen_advance_ccount(DisasContext *dc) -{ - gen_advance_ccount_cond(dc); dc->ccount_delta = 0; } -static void reset_used_window(DisasContext *dc) -{ - dc->used_window = 0; -} - static void gen_exception(DisasContext *dc, int excp) { TCGv_i32 tmp = tcg_const_i32(excp); @@ -377,21 +367,25 @@ static void gen_debug_exception(DisasContext *dc, uint32_t cause) } } -static void gen_check_privilege(DisasContext *dc) +static bool gen_check_privilege(DisasContext *dc) { if (dc->cring) { gen_exception_cause(dc, PRIVILEGED_CAUSE); dc->is_jmp = DISAS_UPDATE; + return false; } + return true; } -static void gen_check_cpenable(DisasContext *dc, unsigned cp) +static bool gen_check_cpenable(DisasContext *dc, unsigned cp) { if (option_enabled(dc, XTENSA_OPTION_COPROCESSOR) && !(dc->cpenable & (1 << cp))) { gen_exception_cause(dc, COPROCESSOR0_DISABLED + cp); dc->is_jmp = DISAS_UPDATE; + return false; } + return true; } static void gen_jump_slot(DisasContext *dc, TCGv dest, int slot) @@ -462,7 +456,7 @@ static bool gen_check_loop_end(DisasContext *dc, int slot) if (option_enabled(dc, XTENSA_OPTION_LOOP) && !(dc->tb->flags & XTENSA_TBFLAG_EXCM) && dc->next_pc == dc->lend) { - int label = gen_new_label(); + TCGLabel *label = gen_new_label(); gen_advance_ccount(dc); tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_SR[LCOUNT], 0, label); @@ -485,7 +479,7 @@ static void gen_jumpi_check_loop_end(DisasContext *dc, int slot) static void gen_brcond(DisasContext *dc, TCGCond cond, TCGv_i32 t0, TCGv_i32 t1, uint32_t offset) { - int label = gen_new_label(); + TCGLabel *label = gen_new_label(); gen_advance_ccount(dc); tcg_gen_brcond_i32(cond, t0, t1, label); @@ -597,13 +591,15 @@ static void gen_wsr_acchi(DisasContext *dc, uint32_t sr, TCGv_i32 s) static void gen_wsr_windowbase(DisasContext *dc, uint32_t sr, TCGv_i32 v) { gen_helper_wsr_windowbase(cpu_env, v); - reset_used_window(dc); + /* This can change tb->flags, so exit tb */ + gen_jumpi_check_loop_end(dc, -1); } static void gen_wsr_windowstart(DisasContext *dc, uint32_t sr, TCGv_i32 v) { tcg_gen_andi_i32(cpu_SR[sr], v, (1 << dc->config->nareg / 4) - 1); - reset_used_window(dc); + /* This can change tb->flags, so exit tb */ + gen_jumpi_check_loop_end(dc, -1); } static void gen_wsr_ptevaddr(DisasContext *dc, uint32_t sr, TCGv_i32 v) @@ -712,7 +708,6 @@ static void gen_wsr_ps(DisasContext *dc, uint32_t sr, TCGv_i32 v) mask |= PS_RING; } tcg_gen_andi_i32(cpu_SR[sr], v, mask); - reset_used_window(dc); gen_helper_check_interrupts(cpu_env); /* This can change mmu index and tb->flags, so exit tb */ gen_jumpi_check_loop_end(dc, -1); @@ -813,7 +808,7 @@ static void gen_load_store_alignment(DisasContext *dc, int shift, tcg_gen_andi_i32(addr, addr, ~0 << shift); } else if (option_enabled(dc, XTENSA_OPTION_HW_ALIGNMENT) && no_hw_alignment) { - int label = gen_new_label(); + TCGLabel *label = gen_new_label(); TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_andi_i32(tmp, addr, ~(~0 << shift)); tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, label); @@ -833,46 +828,29 @@ static void gen_waiti(DisasContext *dc, uint32_t imm4) tcg_temp_free(intlevel); } -static void gen_window_check1(DisasContext *dc, unsigned r1) +static bool gen_window_check1(DisasContext *dc, unsigned r1) { - if (dc->tb->flags & XTENSA_TBFLAG_EXCM) { - return; - } - if (option_enabled(dc, XTENSA_OPTION_WINDOWED_REGISTER) && - r1 / 4 > dc->used_window) { - int label = gen_new_label(); - TCGv_i32 ws = tcg_temp_new_i32(); - - dc->used_window = r1 / 4; - tcg_gen_deposit_i32(ws, cpu_SR[WINDOW_START], cpu_SR[WINDOW_START], - dc->config->nareg / 4, dc->config->nareg / 4); - tcg_gen_shr_i32(ws, ws, cpu_SR[WINDOW_BASE]); - tcg_gen_andi_i32(ws, ws, (2 << (r1 / 4)) - 2); - tcg_gen_brcondi_i32(TCG_COND_EQ, ws, 0, label); - { - TCGv_i32 pc = tcg_const_i32(dc->pc); - TCGv_i32 w = tcg_const_i32(r1 / 4); - - gen_advance_ccount_cond(dc); - gen_helper_window_check(cpu_env, pc, w); + if (r1 / 4 > dc->window) { + TCGv_i32 pc = tcg_const_i32(dc->pc); + TCGv_i32 w = tcg_const_i32(r1 / 4); - tcg_temp_free(w); - tcg_temp_free(pc); - } - gen_set_label(label); - tcg_temp_free(ws); + gen_advance_ccount(dc); + gen_helper_window_check(cpu_env, pc, w); + dc->is_jmp = DISAS_UPDATE; + return false; } + return true; } -static void gen_window_check2(DisasContext *dc, unsigned r1, unsigned r2) +static bool gen_window_check2(DisasContext *dc, unsigned r1, unsigned r2) { - gen_window_check1(dc, r1 > r2 ? r1 : r2); + return gen_window_check1(dc, r1 > r2 ? r1 : r2); } -static void gen_window_check3(DisasContext *dc, unsigned r1, unsigned r2, +static bool gen_window_check3(DisasContext *dc, unsigned r1, unsigned r2, unsigned r3) { - gen_window_check2(dc, r1, r2 > r3 ? r2 : r3); + return gen_window_check2(dc, r1, r2 > r3 ? r2 : r3); } static TCGv_i32 gen_mac16_m(TCGv_i32 v, bool hi, bool is_unsigned) @@ -887,6 +865,11 @@ static TCGv_i32 gen_mac16_m(TCGv_i32 v, bool hi, bool is_unsigned) return m; } +static inline unsigned xtensa_op0_insn_len(unsigned op0) +{ + return op0 >= 8 ? 2 : 3; +} + static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) { #define HAS_OPTION_BITS(opt) do { \ @@ -989,6 +972,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) uint8_t b0 = cpu_ldub_code(env, dc->pc); uint8_t b1 = cpu_ldub_code(env, dc->pc + 1); uint8_t b2 = 0; + unsigned len = xtensa_op0_insn_len(OP0); static const uint32_t B4CONST[] = { 0xffffffff, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 16, 32, 64, 128, 256 @@ -998,13 +982,19 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) 32768, 65536, 2, 3, 4, 5, 6, 7, 8, 10, 12, 16, 32, 64, 128, 256 }; - if (OP0 >= 8) { - dc->next_pc = dc->pc + 2; + switch (len) { + case 2: HAS_OPTION(XTENSA_OPTION_CODE_DENSITY); - } else { - dc->next_pc = dc->pc + 3; + break; + + case 3: b2 = cpu_ldub_code(env, dc->pc + 2); + break; + + default: + RESERVED(); } + dc->next_pc = dc->pc + len; switch (OP0) { case 0: /*QRST*/ @@ -1031,8 +1021,9 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) switch (CALLX_N) { case 0: /*RET*/ case 2: /*JX*/ - gen_window_check1(dc, CALLX_S); - gen_jump(dc, cpu_R[CALLX_S]); + if (gen_window_check1(dc, CALLX_S)) { + gen_jump(dc, cpu_R[CALLX_S]); + } break; case 1: /*RETWw*/ @@ -1053,7 +1044,9 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) break; case 3: /*CALLX*/ - gen_window_check2(dc, CALLX_S, CALLX_N << 2); + if (!gen_window_check2(dc, CALLX_S, CALLX_N << 2)) { + break; + } switch (CALLX_N) { case 0: /*CALLX0*/ { @@ -1084,8 +1077,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) case 1: /*MOVSPw*/ HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER); - gen_window_check2(dc, RRR_T, RRR_S); - { + if (gen_window_check2(dc, RRR_T, RRR_S)) { TCGv_i32 pc = tcg_const_i32(dc->pc); gen_advance_ccount(dc); gen_helper_movsp(cpu_env, pc); @@ -1133,10 +1125,11 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) HAS_OPTION(XTENSA_OPTION_EXCEPTION); switch (RRR_S) { case 0: /*RFEx*/ - gen_check_privilege(dc); - tcg_gen_andi_i32(cpu_SR[PS], cpu_SR[PS], ~PS_EXCM); - gen_helper_check_interrupts(cpu_env); - gen_jump(dc, cpu_SR[EPC1]); + if (gen_check_privilege(dc)) { + tcg_gen_andi_i32(cpu_SR[PS], cpu_SR[PS], ~PS_EXCM); + gen_helper_check_interrupts(cpu_env); + gen_jump(dc, cpu_SR[EPC1]); + } break; case 1: /*RFUEx*/ @@ -1144,16 +1137,16 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) break; case 2: /*RFDEx*/ - gen_check_privilege(dc); - gen_jump(dc, cpu_SR[ - dc->config->ndepc ? DEPC : EPC1]); + if (gen_check_privilege(dc)) { + gen_jump(dc, cpu_SR[ + dc->config->ndepc ? DEPC : EPC1]); + } break; case 4: /*RFWOw*/ case 5: /*RFWUw*/ HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER); - gen_check_privilege(dc); - { + if (gen_check_privilege(dc)) { TCGv_i32 tmp = tcg_const_i32(1); tcg_gen_andi_i32( @@ -1185,11 +1178,12 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) case 1: /*RFIx*/ HAS_OPTION(XTENSA_OPTION_HIGH_PRIORITY_INTERRUPT); if (RRR_S >= 2 && RRR_S <= dc->config->nlevel) { - gen_check_privilege(dc); - tcg_gen_mov_i32(cpu_SR[PS], - cpu_SR[EPS2 + RRR_S - 2]); - gen_helper_check_interrupts(cpu_env); - gen_jump(dc, cpu_SR[EPC1 + RRR_S - 1]); + if (gen_check_privilege(dc)) { + tcg_gen_mov_i32(cpu_SR[PS], + cpu_SR[EPS2 + RRR_S - 2]); + gen_helper_check_interrupts(cpu_env); + gen_jump(dc, cpu_SR[EPC1 + RRR_S - 1]); + } } else { qemu_log("RFI %d is illegal\n", RRR_S); gen_exception_cause(dc, ILLEGAL_INSTRUCTION_CAUSE); @@ -1223,8 +1217,9 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) case 1: /*SIMCALL*/ if (semihosting_enabled) { - gen_check_privilege(dc); - gen_helper_simcall(cpu_env); + if (gen_check_privilege(dc)) { + gen_helper_simcall(cpu_env); + } } else { qemu_log("SIMCALL but semihosting is disabled\n"); gen_exception_cause(dc, ILLEGAL_INSTRUCTION_CAUSE); @@ -1239,19 +1234,21 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) case 6: /*RSILx*/ HAS_OPTION(XTENSA_OPTION_INTERRUPT); - gen_check_privilege(dc); - gen_window_check1(dc, RRR_T); - tcg_gen_mov_i32(cpu_R[RRR_T], cpu_SR[PS]); - tcg_gen_andi_i32(cpu_SR[PS], cpu_SR[PS], ~PS_INTLEVEL); - tcg_gen_ori_i32(cpu_SR[PS], cpu_SR[PS], RRR_S); - gen_helper_check_interrupts(cpu_env); - gen_jumpi_check_loop_end(dc, 0); + if (gen_check_privilege(dc) && + gen_window_check1(dc, RRR_T)) { + tcg_gen_mov_i32(cpu_R[RRR_T], cpu_SR[PS]); + tcg_gen_andi_i32(cpu_SR[PS], cpu_SR[PS], ~PS_INTLEVEL); + tcg_gen_ori_i32(cpu_SR[PS], cpu_SR[PS], RRR_S); + gen_helper_check_interrupts(cpu_env); + gen_jumpi_check_loop_end(dc, 0); + } break; case 7: /*WAITIx*/ HAS_OPTION(XTENSA_OPTION_INTERRUPT); - gen_check_privilege(dc); - gen_waiti(dc, RRR_S); + if (gen_check_privilege(dc)) { + gen_waiti(dc, RRR_S); + } break; case 8: /*ANY4p*/ @@ -1287,35 +1284,39 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) break; case 1: /*AND*/ - gen_window_check3(dc, RRR_R, RRR_S, RRR_T); - tcg_gen_and_i32(cpu_R[RRR_R], cpu_R[RRR_S], cpu_R[RRR_T]); + if (gen_window_check3(dc, RRR_R, RRR_S, RRR_T)) { + tcg_gen_and_i32(cpu_R[RRR_R], cpu_R[RRR_S], cpu_R[RRR_T]); + } break; case 2: /*OR*/ - gen_window_check3(dc, RRR_R, RRR_S, RRR_T); - tcg_gen_or_i32(cpu_R[RRR_R], cpu_R[RRR_S], cpu_R[RRR_T]); + if (gen_window_check3(dc, RRR_R, RRR_S, RRR_T)) { + tcg_gen_or_i32(cpu_R[RRR_R], cpu_R[RRR_S], cpu_R[RRR_T]); + } break; case 3: /*XOR*/ - gen_window_check3(dc, RRR_R, RRR_S, RRR_T); - tcg_gen_xor_i32(cpu_R[RRR_R], cpu_R[RRR_S], cpu_R[RRR_T]); + if (gen_window_check3(dc, RRR_R, RRR_S, RRR_T)) { + tcg_gen_xor_i32(cpu_R[RRR_R], cpu_R[RRR_S], cpu_R[RRR_T]); + } break; case 4: /*ST1*/ switch (RRR_R) { case 0: /*SSR*/ - gen_window_check1(dc, RRR_S); - gen_right_shift_sar(dc, cpu_R[RRR_S]); + if (gen_window_check1(dc, RRR_S)) { + gen_right_shift_sar(dc, cpu_R[RRR_S]); + } break; case 1: /*SSL*/ - gen_window_check1(dc, RRR_S); - gen_left_shift_sar(dc, cpu_R[RRR_S]); + if (gen_window_check1(dc, RRR_S)) { + gen_left_shift_sar(dc, cpu_R[RRR_S]); + } break; case 2: /*SSA8L*/ - gen_window_check1(dc, RRR_S); - { + if (gen_window_check1(dc, RRR_S)) { TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_shli_i32(tmp, cpu_R[RRR_S], 3); gen_right_shift_sar(dc, tmp); @@ -1324,8 +1325,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) break; case 3: /*SSA8B*/ - gen_window_check1(dc, RRR_S); - { + if (gen_window_check1(dc, RRR_S)) { TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_shli_i32(tmp, cpu_R[RRR_S], 3); gen_left_shift_sar(dc, tmp); @@ -1352,26 +1352,28 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) case 8: /*ROTWw*/ HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER); - gen_check_privilege(dc); - { + if (gen_check_privilege(dc)) { TCGv_i32 tmp = tcg_const_i32( RRR_T | ((RRR_T & 8) ? 0xfffffff0 : 0)); gen_helper_rotw(cpu_env, tmp); tcg_temp_free(tmp); - reset_used_window(dc); + /* This can change tb->flags, so exit tb */ + gen_jumpi_check_loop_end(dc, -1); } break; case 14: /*NSAu*/ HAS_OPTION(XTENSA_OPTION_MISC_OP_NSA); - gen_window_check2(dc, RRR_S, RRR_T); - gen_helper_nsa(cpu_R[RRR_T], cpu_R[RRR_S]); + if (gen_window_check2(dc, RRR_S, RRR_T)) { + gen_helper_nsa(cpu_R[RRR_T], cpu_R[RRR_S]); + } break; case 15: /*NSAUu*/ HAS_OPTION(XTENSA_OPTION_MISC_OP_NSA); - gen_window_check2(dc, RRR_S, RRR_T); - gen_helper_nsau(cpu_R[RRR_T], cpu_R[RRR_S]); + if (gen_window_check2(dc, RRR_S, RRR_T)) { + gen_helper_nsau(cpu_R[RRR_T], cpu_R[RRR_S]); + } break; default: /*reserved*/ @@ -1385,9 +1387,8 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) XTENSA_OPTION_BIT(XTENSA_OPTION_MMU) | XTENSA_OPTION_BIT(XTENSA_OPTION_REGION_PROTECTION) | XTENSA_OPTION_BIT(XTENSA_OPTION_REGION_TRANSLATION)); - gen_check_privilege(dc); - gen_window_check2(dc, RRR_S, RRR_T); - { + if (gen_check_privilege(dc) && + gen_window_check2(dc, RRR_S, RRR_T)) { TCGv_i32 dtlb = tcg_const_i32((RRR_R & 8) != 0); switch (RRR_R & 7) { @@ -1430,7 +1431,9 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) break; case 6: /*RT0*/ - gen_window_check2(dc, RRR_R, RRR_T); + if (!gen_window_check2(dc, RRR_R, RRR_T)) { + break; + } switch (RRR_S) { case 0: /*NEG*/ tcg_gen_neg_i32(cpu_R[RRR_R], cpu_R[RRR_T]); @@ -1460,15 +1463,15 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) break; case 8: /*ADD*/ - gen_window_check3(dc, RRR_R, RRR_S, RRR_T); - tcg_gen_add_i32(cpu_R[RRR_R], cpu_R[RRR_S], cpu_R[RRR_T]); + if (gen_window_check3(dc, RRR_R, RRR_S, RRR_T)) { + tcg_gen_add_i32(cpu_R[RRR_R], cpu_R[RRR_S], cpu_R[RRR_T]); + } break; case 9: /*ADD**/ case 10: case 11: - gen_window_check3(dc, RRR_R, RRR_S, RRR_T); - { + if (gen_window_check3(dc, RRR_R, RRR_S, RRR_T)) { TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_shli_i32(tmp, cpu_R[RRR_S], OP2 - 8); tcg_gen_add_i32(cpu_R[RRR_R], tmp, cpu_R[RRR_T]); @@ -1477,15 +1480,15 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) break; case 12: /*SUB*/ - gen_window_check3(dc, RRR_R, RRR_S, RRR_T); - tcg_gen_sub_i32(cpu_R[RRR_R], cpu_R[RRR_S], cpu_R[RRR_T]); + if (gen_window_check3(dc, RRR_R, RRR_S, RRR_T)) { + tcg_gen_sub_i32(cpu_R[RRR_R], cpu_R[RRR_S], cpu_R[RRR_T]); + } break; case 13: /*SUB**/ case 14: case 15: - gen_window_check3(dc, RRR_R, RRR_S, RRR_T); - { + if (gen_window_check3(dc, RRR_R, RRR_S, RRR_T)) { TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_shli_i32(tmp, cpu_R[RRR_S], OP2 - 12); tcg_gen_sub_i32(cpu_R[RRR_R], tmp, cpu_R[RRR_T]); @@ -1499,31 +1502,32 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) switch (OP2) { case 0: /*SLLI*/ case 1: - gen_window_check2(dc, RRR_R, RRR_S); - tcg_gen_shli_i32(cpu_R[RRR_R], cpu_R[RRR_S], - 32 - (RRR_T | ((OP2 & 1) << 4))); + if (gen_window_check2(dc, RRR_R, RRR_S)) { + tcg_gen_shli_i32(cpu_R[RRR_R], cpu_R[RRR_S], + 32 - (RRR_T | ((OP2 & 1) << 4))); + } break; case 2: /*SRAI*/ case 3: - gen_window_check2(dc, RRR_R, RRR_T); - tcg_gen_sari_i32(cpu_R[RRR_R], cpu_R[RRR_T], - RRR_S | ((OP2 & 1) << 4)); + if (gen_window_check2(dc, RRR_R, RRR_T)) { + tcg_gen_sari_i32(cpu_R[RRR_R], cpu_R[RRR_T], + RRR_S | ((OP2 & 1) << 4)); + } break; case 4: /*SRLI*/ - gen_window_check2(dc, RRR_R, RRR_T); - tcg_gen_shri_i32(cpu_R[RRR_R], cpu_R[RRR_T], RRR_S); + if (gen_window_check2(dc, RRR_R, RRR_T)) { + tcg_gen_shri_i32(cpu_R[RRR_R], cpu_R[RRR_T], RRR_S); + } break; case 6: /*XSR*/ - if (gen_check_sr(dc, RSR_SR, SR_X)) { + if (gen_check_sr(dc, RSR_SR, SR_X) && + (RSR_SR < 64 || gen_check_privilege(dc)) && + gen_window_check1(dc, RRR_T)) { TCGv_i32 tmp = tcg_temp_new_i32(); - if (RSR_SR >= 64) { - gen_check_privilege(dc); - } - gen_window_check1(dc, RRR_T); tcg_gen_mov_i32(tmp, cpu_R[RRR_T]); gen_rsr(dc, cpu_R[RRR_T], RSR_SR); gen_wsr(dc, RSR_SR, tmp); @@ -1547,8 +1551,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) #define gen_shift(cmd) gen_shift_reg(cmd, cpu_SR[SAR]) case 8: /*SRC*/ - gen_window_check3(dc, RRR_R, RRR_S, RRR_T); - { + if (gen_window_check3(dc, RRR_R, RRR_S, RRR_T)) { TCGv_i64 v = tcg_temp_new_i64(); tcg_gen_concat_i32_i64(v, cpu_R[RRR_T], cpu_R[RRR_S]); gen_shift(shr); @@ -1556,7 +1559,9 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) break; case 9: /*SRL*/ - gen_window_check2(dc, RRR_R, RRR_T); + if (!gen_window_check2(dc, RRR_R, RRR_T)) { + break; + } if (dc->sar_5bit) { tcg_gen_shr_i32(cpu_R[RRR_R], cpu_R[RRR_T], cpu_SR[SAR]); } else { @@ -1567,7 +1572,9 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) break; case 10: /*SLL*/ - gen_window_check2(dc, RRR_R, RRR_S); + if (!gen_window_check2(dc, RRR_R, RRR_S)) { + break; + } if (dc->sar_m32_5bit) { tcg_gen_shl_i32(cpu_R[RRR_R], cpu_R[RRR_S], dc->sar_m32); } else { @@ -1582,7 +1589,9 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) break; case 11: /*SRA*/ - gen_window_check2(dc, RRR_R, RRR_T); + if (!gen_window_check2(dc, RRR_R, RRR_T)) { + break; + } if (dc->sar_5bit) { tcg_gen_sar_i32(cpu_R[RRR_R], cpu_R[RRR_T], cpu_SR[SAR]); } else { @@ -1596,8 +1605,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) case 12: /*MUL16U*/ HAS_OPTION(XTENSA_OPTION_16_BIT_IMUL); - gen_window_check3(dc, RRR_R, RRR_S, RRR_T); - { + if (gen_window_check3(dc, RRR_R, RRR_S, RRR_T)) { TCGv_i32 v1 = tcg_temp_new_i32(); TCGv_i32 v2 = tcg_temp_new_i32(); tcg_gen_ext16u_i32(v1, cpu_R[RRR_S]); @@ -1610,8 +1618,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) case 13: /*MUL16S*/ HAS_OPTION(XTENSA_OPTION_16_BIT_IMUL); - gen_window_check3(dc, RRR_R, RRR_S, RRR_T); - { + if (gen_window_check3(dc, RRR_R, RRR_S, RRR_T)) { TCGv_i32 v1 = tcg_temp_new_i32(); TCGv_i32 v2 = tcg_temp_new_i32(); tcg_gen_ext16s_i32(v1, cpu_R[RRR_S]); @@ -1629,13 +1636,13 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) break; case 2: /*RST2*/ - if (OP2 >= 8) { - gen_window_check3(dc, RRR_R, RRR_S, RRR_T); + if (OP2 >= 8 && !gen_window_check3(dc, RRR_R, RRR_S, RRR_T)) { + break; } if (OP2 >= 12) { HAS_OPTION(XTENSA_OPTION_32_BIT_IDIV); - int label = gen_new_label(); + TCGLabel *label = gen_new_label(); tcg_gen_brcondi_i32(TCG_COND_NE, cpu_R[RRR_T], 0, label); gen_exception_cause(dc, INTEGER_DIVIDE_BY_ZERO_CAUSE); gen_set_label(label); @@ -1707,8 +1714,8 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) case 13: /*QUOSi*/ case 15: /*REMSi*/ { - int label1 = gen_new_label(); - int label2 = gen_new_label(); + TCGLabel *label1 = gen_new_label(); + TCGLabel *label2 = gen_new_label(); tcg_gen_brcondi_i32(TCG_COND_NE, cpu_R[RRR_S], 0x80000000, label1); @@ -1742,29 +1749,24 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) case 3: /*RST3*/ switch (OP2) { case 0: /*RSR*/ - if (gen_check_sr(dc, RSR_SR, SR_R)) { - if (RSR_SR >= 64) { - gen_check_privilege(dc); - } - gen_window_check1(dc, RRR_T); + if (gen_check_sr(dc, RSR_SR, SR_R) && + (RSR_SR < 64 || gen_check_privilege(dc)) && + gen_window_check1(dc, RRR_T)) { gen_rsr(dc, cpu_R[RRR_T], RSR_SR); } break; case 1: /*WSR*/ - if (gen_check_sr(dc, RSR_SR, SR_W)) { - if (RSR_SR >= 64) { - gen_check_privilege(dc); - } - gen_window_check1(dc, RRR_T); + if (gen_check_sr(dc, RSR_SR, SR_W) && + (RSR_SR < 64 || gen_check_privilege(dc)) && + gen_window_check1(dc, RRR_T)) { gen_wsr(dc, RSR_SR, cpu_R[RRR_T]); } break; case 2: /*SEXTu*/ HAS_OPTION(XTENSA_OPTION_MISC_OP_SEXT); - gen_window_check2(dc, RRR_R, RRR_S); - { + if (gen_window_check2(dc, RRR_R, RRR_S)) { int shift = 24 - RRR_T; if (shift == 24) { @@ -1782,8 +1784,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) case 3: /*CLAMPSu*/ HAS_OPTION(XTENSA_OPTION_MISC_OP_CLAMPS); - gen_window_check2(dc, RRR_R, RRR_S); - { + if (gen_window_check2(dc, RRR_R, RRR_S)) { TCGv_i32 tmp1 = tcg_temp_new_i32(); TCGv_i32 tmp2 = tcg_temp_new_i32(); TCGv_i32 zero = tcg_const_i32(0); @@ -1808,8 +1809,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) case 6: /*MINUu*/ case 7: /*MAXUu*/ HAS_OPTION(XTENSA_OPTION_MISC_OP_MINMAX); - gen_window_check3(dc, RRR_R, RRR_S, RRR_T); - { + if (gen_window_check3(dc, RRR_R, RRR_S, RRR_T)) { static const TCGCond cond[] = { TCG_COND_LE, TCG_COND_GE, @@ -1826,8 +1826,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) case 9: /*MOVNEZ*/ case 10: /*MOVLTZ*/ case 11: /*MOVGEZ*/ - gen_window_check3(dc, RRR_R, RRR_S, RRR_T); - { + if (gen_window_check3(dc, RRR_R, RRR_S, RRR_T)) { static const TCGCond cond[] = { TCG_COND_EQ, TCG_COND_NE, @@ -1845,8 +1844,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) case 12: /*MOVFp*/ case 13: /*MOVTp*/ HAS_OPTION(XTENSA_OPTION_BOOLEAN); - gen_window_check2(dc, RRR_R, RRR_S); - { + if (gen_window_check2(dc, RRR_R, RRR_S)) { TCGv_i32 zero = tcg_const_i32(0); TCGv_i32 tmp = tcg_temp_new_i32(); @@ -1861,8 +1859,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) break; case 14: /*RUR*/ - gen_window_check1(dc, RRR_R); - { + if (gen_window_check1(dc, RRR_R)) { int st = (RRR_S << 4) + RRR_T; if (uregnames[st].name) { tcg_gen_mov_i32(cpu_R[RRR_R], cpu_UR[st]); @@ -1874,12 +1871,13 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) break; case 15: /*WUR*/ - gen_window_check1(dc, RRR_T); - if (uregnames[RSR_SR].name) { - gen_wur(RSR_SR, cpu_R[RRR_T]); - } else { - qemu_log("WUR %d not implemented, ", RSR_SR); - TBD(); + if (gen_window_check1(dc, RRR_T)) { + if (uregnames[RSR_SR].name) { + gen_wur(RSR_SR, cpu_R[RRR_T]); + } else { + qemu_log("WUR %d not implemented, ", RSR_SR); + TBD(); + } } break; @@ -1888,8 +1886,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) case 4: /*EXTUI*/ case 5: - gen_window_check2(dc, RRR_R, RRR_T); - { + if (gen_window_check2(dc, RRR_R, RRR_T)) { int shiftimm = RRR_S | ((OP1 & 1) << 4); int maskimm = (1 << (OP2 + 1)) - 1; @@ -1915,9 +1912,8 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) case 4: /*SSXf*/ case 5: /*SSXUf*/ HAS_OPTION(XTENSA_OPTION_FP_COPROCESSOR); - gen_window_check2(dc, RRR_S, RRR_T); - gen_check_cpenable(dc, 0); - { + if (gen_window_check2(dc, RRR_S, RRR_T) && + gen_check_cpenable(dc, 0)) { TCGv_i32 addr = tcg_temp_new_i32(); tcg_gen_add_i32(addr, cpu_R[RRR_S], cpu_R[RRR_T]); gen_load_store_alignment(dc, 2, addr, false); @@ -1940,12 +1936,13 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) break; case 9: /*LSC4*/ - gen_window_check2(dc, RRR_S, RRR_T); + if (!gen_window_check2(dc, RRR_S, RRR_T)) { + break; + } switch (OP2) { case 0: /*L32E*/ HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER); - gen_check_privilege(dc); - { + if (gen_check_privilege(dc)) { TCGv_i32 addr = tcg_temp_new_i32(); tcg_gen_addi_i32(addr, cpu_R[RRR_S], (0xffffffc0 | (RRR_R << 2))); @@ -1956,8 +1953,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) case 4: /*S32E*/ HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER); - gen_check_privilege(dc); - { + if (gen_check_privilege(dc)) { TCGv_i32 addr = tcg_temp_new_i32(); tcg_gen_addi_i32(addr, cpu_R[RRR_S], (0xffffffc0 | (RRR_R << 2))); @@ -1976,33 +1972,40 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) HAS_OPTION(XTENSA_OPTION_FP_COPROCESSOR); switch (OP2) { case 0: /*ADD.Sf*/ - gen_check_cpenable(dc, 0); - gen_helper_add_s(cpu_FR[RRR_R], cpu_env, - cpu_FR[RRR_S], cpu_FR[RRR_T]); + if (gen_check_cpenable(dc, 0)) { + gen_helper_add_s(cpu_FR[RRR_R], cpu_env, + cpu_FR[RRR_S], cpu_FR[RRR_T]); + } break; case 1: /*SUB.Sf*/ - gen_check_cpenable(dc, 0); - gen_helper_sub_s(cpu_FR[RRR_R], cpu_env, - cpu_FR[RRR_S], cpu_FR[RRR_T]); + if (gen_check_cpenable(dc, 0)) { + gen_helper_sub_s(cpu_FR[RRR_R], cpu_env, + cpu_FR[RRR_S], cpu_FR[RRR_T]); + } break; case 2: /*MUL.Sf*/ - gen_check_cpenable(dc, 0); - gen_helper_mul_s(cpu_FR[RRR_R], cpu_env, - cpu_FR[RRR_S], cpu_FR[RRR_T]); + if (gen_check_cpenable(dc, 0)) { + gen_helper_mul_s(cpu_FR[RRR_R], cpu_env, + cpu_FR[RRR_S], cpu_FR[RRR_T]); + } break; case 4: /*MADD.Sf*/ - gen_check_cpenable(dc, 0); - gen_helper_madd_s(cpu_FR[RRR_R], cpu_env, - cpu_FR[RRR_R], cpu_FR[RRR_S], cpu_FR[RRR_T]); + if (gen_check_cpenable(dc, 0)) { + gen_helper_madd_s(cpu_FR[RRR_R], cpu_env, + cpu_FR[RRR_R], cpu_FR[RRR_S], + cpu_FR[RRR_T]); + } break; case 5: /*MSUB.Sf*/ - gen_check_cpenable(dc, 0); - gen_helper_msub_s(cpu_FR[RRR_R], cpu_env, - cpu_FR[RRR_R], cpu_FR[RRR_S], cpu_FR[RRR_T]); + if (gen_check_cpenable(dc, 0)) { + gen_helper_msub_s(cpu_FR[RRR_R], cpu_env, + cpu_FR[RRR_R], cpu_FR[RRR_S], + cpu_FR[RRR_T]); + } break; case 8: /*ROUND.Sf*/ @@ -2010,9 +2013,8 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) case 10: /*FLOOR.Sf*/ case 11: /*CEIL.Sf*/ case 14: /*UTRUNC.Sf*/ - gen_window_check1(dc, RRR_R); - gen_check_cpenable(dc, 0); - { + if (gen_window_check1(dc, RRR_R) && + gen_check_cpenable(dc, 0)) { static const unsigned rounding_mode_const[] = { float_round_nearest_even, float_round_to_zero, @@ -2039,9 +2041,8 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) case 12: /*FLOAT.Sf*/ case 13: /*UFLOAT.Sf*/ - gen_window_check1(dc, RRR_S); - gen_check_cpenable(dc, 0); - { + if (gen_window_check1(dc, RRR_S) && + gen_check_cpenable(dc, 0)) { TCGv_i32 scale = tcg_const_i32(-RRR_T); if (OP2 == 13) { @@ -2058,30 +2059,35 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) case 15: /*FP1OP*/ switch (RRR_T) { case 0: /*MOV.Sf*/ - gen_check_cpenable(dc, 0); - tcg_gen_mov_i32(cpu_FR[RRR_R], cpu_FR[RRR_S]); + if (gen_check_cpenable(dc, 0)) { + tcg_gen_mov_i32(cpu_FR[RRR_R], cpu_FR[RRR_S]); + } break; case 1: /*ABS.Sf*/ - gen_check_cpenable(dc, 0); - gen_helper_abs_s(cpu_FR[RRR_R], cpu_FR[RRR_S]); + if (gen_check_cpenable(dc, 0)) { + gen_helper_abs_s(cpu_FR[RRR_R], cpu_FR[RRR_S]); + } break; case 4: /*RFRf*/ - gen_window_check1(dc, RRR_R); - gen_check_cpenable(dc, 0); - tcg_gen_mov_i32(cpu_R[RRR_R], cpu_FR[RRR_S]); + if (gen_window_check1(dc, RRR_R) && + gen_check_cpenable(dc, 0)) { + tcg_gen_mov_i32(cpu_R[RRR_R], cpu_FR[RRR_S]); + } break; case 5: /*WFRf*/ - gen_window_check1(dc, RRR_S); - gen_check_cpenable(dc, 0); - tcg_gen_mov_i32(cpu_FR[RRR_R], cpu_R[RRR_S]); + if (gen_window_check1(dc, RRR_S) && + gen_check_cpenable(dc, 0)) { + tcg_gen_mov_i32(cpu_FR[RRR_R], cpu_R[RRR_S]); + } break; case 6: /*NEG.Sf*/ - gen_check_cpenable(dc, 0); - gen_helper_neg_s(cpu_FR[RRR_R], cpu_FR[RRR_S]); + if (gen_check_cpenable(dc, 0)) { + gen_helper_neg_s(cpu_FR[RRR_R], cpu_FR[RRR_S]); + } break; default: /*reserved*/ @@ -2101,11 +2107,12 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) #define gen_compare(rel, br, a, b) \ do { \ - TCGv_i32 bit = tcg_const_i32(1 << br); \ - \ - gen_check_cpenable(dc, 0); \ - gen_helper_##rel(cpu_env, bit, cpu_FR[a], cpu_FR[b]); \ - tcg_temp_free(bit); \ + if (gen_check_cpenable(dc, 0)) { \ + TCGv_i32 bit = tcg_const_i32(1 << br); \ + \ + gen_helper_##rel(cpu_env, bit, cpu_FR[a], cpu_FR[b]); \ + tcg_temp_free(bit); \ + } \ } while (0) switch (OP2) { @@ -2143,9 +2150,8 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) case 9: /*MOVNEZ.Sf*/ case 10: /*MOVLTZ.Sf*/ case 11: /*MOVGEZ.Sf*/ - gen_window_check1(dc, RRR_T); - gen_check_cpenable(dc, 0); - { + if (gen_window_check1(dc, RRR_T) && + gen_check_cpenable(dc, 0)) { static const TCGCond cond[] = { TCG_COND_EQ, TCG_COND_NE, @@ -2163,8 +2169,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) case 12: /*MOVF.Sf*/ case 13: /*MOVT.Sf*/ HAS_OPTION(XTENSA_OPTION_BOOLEAN); - gen_check_cpenable(dc, 0); - { + if (gen_check_cpenable(dc, 0)) { TCGv_i32 zero = tcg_const_i32(0); TCGv_i32 tmp = tcg_temp_new_i32(); @@ -2191,8 +2196,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) break; case 1: /*L32R*/ - gen_window_check1(dc, RRR_T); - { + if (gen_window_check1(dc, RRR_T)) { TCGv_i32 tmp = tcg_const_i32( ((dc->tb->flags & XTENSA_TBFLAG_LITBASE) ? 0 : ((dc->pc + 3) & ~3)) + @@ -2208,14 +2212,16 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) case 2: /*LSAI*/ #define gen_load_store(type, shift) do { \ - TCGv_i32 addr = tcg_temp_new_i32(); \ - gen_window_check2(dc, RRI8_S, RRI8_T); \ - tcg_gen_addi_i32(addr, cpu_R[RRI8_S], RRI8_IMM8 << shift); \ - if (shift) { \ - gen_load_store_alignment(dc, shift, addr, false); \ + if (gen_window_check2(dc, RRI8_S, RRI8_T)) { \ + TCGv_i32 addr = tcg_temp_new_i32(); \ + \ + tcg_gen_addi_i32(addr, cpu_R[RRI8_S], RRI8_IMM8 << shift); \ + if (shift) { \ + gen_load_store_alignment(dc, shift, addr, false); \ + } \ + tcg_gen_qemu_##type(cpu_R[RRI8_T], addr, dc->cring); \ + tcg_temp_free(addr); \ } \ - tcg_gen_qemu_##type(cpu_R[RRI8_T], addr, dc->cring); \ - tcg_temp_free(addr); \ } while (0) switch (RRI8_R) { @@ -2244,14 +2250,15 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) break; #define gen_dcache_hit_test(w, shift) do { \ - TCGv_i32 addr = tcg_temp_new_i32(); \ - TCGv_i32 res = tcg_temp_new_i32(); \ - gen_window_check1(dc, RRI##w##_S); \ - tcg_gen_addi_i32(addr, cpu_R[RRI##w##_S], \ - RRI##w##_IMM##w << shift); \ - tcg_gen_qemu_ld8u(res, addr, dc->cring); \ - tcg_temp_free(addr); \ - tcg_temp_free(res); \ + if (gen_window_check1(dc, RRI##w##_S)) { \ + TCGv_i32 addr = tcg_temp_new_i32(); \ + TCGv_i32 res = tcg_temp_new_i32(); \ + tcg_gen_addi_i32(addr, cpu_R[RRI##w##_S], \ + RRI##w##_IMM##w << shift); \ + tcg_gen_qemu_ld8u(res, addr, dc->cring); \ + tcg_temp_free(addr); \ + tcg_temp_free(res); \ + } \ } while (0) #define gen_dcache_hit_test4() gen_dcache_hit_test(4, 4) @@ -2288,45 +2295,52 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) break; case 6: /*DHIc*/ - gen_check_privilege(dc); - gen_dcache_hit_test8(); + if (gen_check_privilege(dc)) { + gen_dcache_hit_test8(); + } break; case 7: /*DIIc*/ - gen_check_privilege(dc); - gen_window_check1(dc, RRI8_S); + if (gen_check_privilege(dc)) { + gen_window_check1(dc, RRI8_S); + } break; case 8: /*DCEc*/ switch (OP1) { case 0: /*DPFLl*/ HAS_OPTION(XTENSA_OPTION_DCACHE_INDEX_LOCK); - gen_check_privilege(dc); - gen_dcache_hit_test4(); + if (gen_check_privilege(dc)) { + gen_dcache_hit_test4(); + } break; case 2: /*DHUl*/ HAS_OPTION(XTENSA_OPTION_DCACHE_INDEX_LOCK); - gen_check_privilege(dc); - gen_dcache_hit_test4(); + if (gen_check_privilege(dc)) { + gen_dcache_hit_test4(); + } break; case 3: /*DIUl*/ HAS_OPTION(XTENSA_OPTION_DCACHE_INDEX_LOCK); - gen_check_privilege(dc); - gen_window_check1(dc, RRI4_S); + if (gen_check_privilege(dc)) { + gen_window_check1(dc, RRI4_S); + } break; case 4: /*DIWBc*/ HAS_OPTION(XTENSA_OPTION_DCACHE); - gen_check_privilege(dc); - gen_window_check1(dc, RRI4_S); + if (gen_check_privilege(dc)) { + gen_window_check1(dc, RRI4_S); + } break; case 5: /*DIWBIc*/ HAS_OPTION(XTENSA_OPTION_DCACHE); - gen_check_privilege(dc); - gen_window_check1(dc, RRI4_S); + if (gen_check_privilege(dc)) { + gen_window_check1(dc, RRI4_S); + } break; default: /*reserved*/ @@ -2341,13 +2355,14 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) #undef gen_dcache_hit_test8 #define gen_icache_hit_test(w, shift) do { \ - TCGv_i32 addr = tcg_temp_new_i32(); \ - gen_window_check1(dc, RRI##w##_S); \ - tcg_gen_movi_i32(cpu_pc, dc->pc); \ - tcg_gen_addi_i32(addr, cpu_R[RRI##w##_S], \ - RRI##w##_IMM##w << shift); \ - gen_helper_itlb_hit_test(cpu_env, addr); \ - tcg_temp_free(addr); \ + if (gen_window_check1(dc, RRI##w##_S)) { \ + TCGv_i32 addr = tcg_temp_new_i32(); \ + tcg_gen_movi_i32(cpu_pc, dc->pc); \ + tcg_gen_addi_i32(addr, cpu_R[RRI##w##_S], \ + RRI##w##_IMM##w << shift); \ + gen_helper_itlb_hit_test(cpu_env, addr); \ + tcg_temp_free(addr); \ + }\ } while (0) #define gen_icache_hit_test4() gen_icache_hit_test(4, 4) @@ -2362,20 +2377,23 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) switch (OP1) { case 0: /*IPFLl*/ HAS_OPTION(XTENSA_OPTION_ICACHE_INDEX_LOCK); - gen_check_privilege(dc); - gen_icache_hit_test4(); + if (gen_check_privilege(dc)) { + gen_icache_hit_test4(); + } break; case 2: /*IHUl*/ HAS_OPTION(XTENSA_OPTION_ICACHE_INDEX_LOCK); - gen_check_privilege(dc); - gen_icache_hit_test4(); + if (gen_check_privilege(dc)) { + gen_icache_hit_test4(); + } break; case 3: /*IIUl*/ HAS_OPTION(XTENSA_OPTION_ICACHE_INDEX_LOCK); - gen_check_privilege(dc); - gen_window_check1(dc, RRI4_S); + if (gen_check_privilege(dc)) { + gen_window_check1(dc, RRI4_S); + } break; default: /*reserved*/ @@ -2391,8 +2409,9 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) case 15: /*IIIc*/ HAS_OPTION(XTENSA_OPTION_ICACHE); - gen_check_privilege(dc); - gen_window_check1(dc, RRI8_S); + if (gen_check_privilege(dc)) { + gen_window_check1(dc, RRI8_S); + } break; default: /*reserved*/ @@ -2411,19 +2430,21 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) #undef gen_load_store case 10: /*MOVI*/ - gen_window_check1(dc, RRI8_T); - tcg_gen_movi_i32(cpu_R[RRI8_T], - RRI8_IMM8 | (RRI8_S << 8) | - ((RRI8_S & 0x8) ? 0xfffff000 : 0)); + if (gen_window_check1(dc, RRI8_T)) { + tcg_gen_movi_i32(cpu_R[RRI8_T], + RRI8_IMM8 | (RRI8_S << 8) | + ((RRI8_S & 0x8) ? 0xfffff000 : 0)); + } break; #define gen_load_store_no_hw_align(type) do { \ - TCGv_i32 addr = tcg_temp_local_new_i32(); \ - gen_window_check2(dc, RRI8_S, RRI8_T); \ - tcg_gen_addi_i32(addr, cpu_R[RRI8_S], RRI8_IMM8 << 2); \ - gen_load_store_alignment(dc, 2, addr, true); \ - tcg_gen_qemu_##type(cpu_R[RRI8_T], addr, dc->cring); \ - tcg_temp_free(addr); \ + if (gen_window_check2(dc, RRI8_S, RRI8_T)) { \ + TCGv_i32 addr = tcg_temp_local_new_i32(); \ + tcg_gen_addi_i32(addr, cpu_R[RRI8_S], RRI8_IMM8 << 2); \ + gen_load_store_alignment(dc, 2, addr, true); \ + tcg_gen_qemu_##type(cpu_R[RRI8_T], addr, dc->cring); \ + tcg_temp_free(addr); \ + } \ } while (0) case 11: /*L32AIy*/ @@ -2432,20 +2453,22 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) break; case 12: /*ADDI*/ - gen_window_check2(dc, RRI8_S, RRI8_T); - tcg_gen_addi_i32(cpu_R[RRI8_T], cpu_R[RRI8_S], RRI8_IMM8_SE); + if (gen_window_check2(dc, RRI8_S, RRI8_T)) { + tcg_gen_addi_i32(cpu_R[RRI8_T], cpu_R[RRI8_S], RRI8_IMM8_SE); + } break; case 13: /*ADDMI*/ - gen_window_check2(dc, RRI8_S, RRI8_T); - tcg_gen_addi_i32(cpu_R[RRI8_T], cpu_R[RRI8_S], RRI8_IMM8_SE << 8); + if (gen_window_check2(dc, RRI8_S, RRI8_T)) { + tcg_gen_addi_i32(cpu_R[RRI8_T], cpu_R[RRI8_S], + RRI8_IMM8_SE << 8); + } break; case 14: /*S32C1Iy*/ HAS_OPTION(XTENSA_OPTION_CONDITIONAL_STORE); - gen_window_check2(dc, RRI8_S, RRI8_T); - { - int label = gen_new_label(); + if (gen_window_check2(dc, RRI8_S, RRI8_T)) { + TCGLabel *label = gen_new_label(); TCGv_i32 tmp = tcg_temp_local_new_i32(); TCGv_i32 addr = tcg_temp_local_new_i32(); TCGv_i32 tpc; @@ -2489,9 +2512,8 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) case 8: /*LSIUf*/ case 12: /*SSIUf*/ HAS_OPTION(XTENSA_OPTION_FP_COPROCESSOR); - gen_window_check1(dc, RRI8_S); - gen_check_cpenable(dc, 0); - { + if (gen_window_check1(dc, RRI8_S) && + gen_check_cpenable(dc, 0)) { TCGv_i32 addr = tcg_temp_new_i32(); tcg_gen_addi_i32(addr, cpu_R[RRI8_S], RRI8_IMM8 << 2); gen_load_store_alignment(dc, 2, addr, false); @@ -2555,20 +2577,23 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) } if (op != MAC16_NONE) { - if (!is_m1_sr) { - gen_window_check1(dc, RRR_S); + if (!is_m1_sr && !gen_window_check1(dc, RRR_S)) { + break; } - if (!is_m2_sr) { - gen_window_check1(dc, RRR_T); + if (!is_m2_sr && !gen_window_check1(dc, RRR_T)) { + break; } } + if (ld_offset && !gen_window_check1(dc, RRR_S)) { + break; + } + { TCGv_i32 vaddr = tcg_temp_new_i32(); TCGv_i32 mem32 = tcg_temp_new_i32(); if (ld_offset) { - gen_window_check1(dc, RRR_S); tcg_gen_addi_i32(vaddr, cpu_R[RRR_S], ld_offset); gen_load_store_alignment(dc, 2, vaddr, false); tcg_gen_qemu_ld32u(mem32, vaddr, dc->cring); @@ -2632,9 +2657,10 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) case 2: /*CALL8w*/ case 3: /*CALL12w*/ HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER); - gen_window_check1(dc, CALL_N << 2); - gen_callwi(dc, CALL_N, - (dc->pc & ~3) + (CALL_OFFSET_SE << 2) + 4, 0); + if (gen_window_check1(dc, CALL_N << 2)) { + gen_callwi(dc, CALL_N, + (dc->pc & ~3) + (CALL_OFFSET_SE << 2) + 4, 0); + } break; } break; @@ -2646,8 +2672,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) break; case 1: /*BZ*/ - gen_window_check1(dc, BRI12_S); - { + if (gen_window_check1(dc, BRI12_S)) { static const TCGCond cond[] = { TCG_COND_EQ, /*BEQZ*/ TCG_COND_NE, /*BNEZ*/ @@ -2661,8 +2686,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) break; case 2: /*BI0*/ - gen_window_check1(dc, BRI8_S); - { + if (gen_window_check1(dc, BRI8_S)) { static const TCGCond cond[] = { TCG_COND_EQ, /*BEQI*/ TCG_COND_NE, /*BNEI*/ @@ -2688,7 +2712,8 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) tcg_temp_free(imm); tcg_temp_free(s); tcg_temp_free(pc); - reset_used_window(dc); + /* This can change tb->flags, so exit tb */ + gen_jumpi_check_loop_end(dc, -1); } break; @@ -2711,8 +2736,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) case 9: /*LOOPNEZ*/ case 10: /*LOOPGTZ*/ HAS_OPTION(XTENSA_OPTION_LOOP); - gen_window_check1(dc, RRI8_S); - { + if (gen_window_check1(dc, RRI8_S)) { uint32_t lend = dc->pc + RRI8_IMM8 + 4; TCGv_i32 tmp = tcg_const_i32(lend); @@ -2722,7 +2746,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) tcg_temp_free(tmp); if (BRI8_R > 8) { - int label = gen_new_label(); + TCGLabel *label = gen_new_label(); tcg_gen_brcondi_i32( BRI8_R == 9 ? TCG_COND_NE : TCG_COND_GT, cpu_R[RRI8_S], 0, label); @@ -2743,9 +2767,11 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) case 2: /*BLTUI*/ case 3: /*BGEUI*/ - gen_window_check1(dc, BRI8_S); - gen_brcondi(dc, BRI8_M == 2 ? TCG_COND_LTU : TCG_COND_GEU, - cpu_R[BRI8_S], B4CONSTU[BRI8_R], 4 + BRI8_IMM8_SE); + if (gen_window_check1(dc, BRI8_S)) { + gen_brcondi(dc, BRI8_M == 2 ? TCG_COND_LTU : TCG_COND_GEU, + cpu_R[BRI8_S], B4CONSTU[BRI8_R], + 4 + BRI8_IMM8_SE); + } break; } break; @@ -2759,8 +2785,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) switch (RRI8_R & 7) { case 0: /*BNONE*/ /*BANY*/ - gen_window_check2(dc, RRI8_S, RRI8_T); - { + if (gen_window_check2(dc, RRI8_S, RRI8_T)) { TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_and_i32(tmp, cpu_R[RRI8_S], cpu_R[RRI8_T]); gen_brcondi(dc, eq_ne, tmp, 0, 4 + RRI8_IMM8_SE); @@ -2771,8 +2796,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) case 1: /*BEQ*/ /*BNE*/ case 2: /*BLT*/ /*BGE*/ case 3: /*BLTU*/ /*BGEU*/ - gen_window_check2(dc, RRI8_S, RRI8_T); - { + if (gen_window_check2(dc, RRI8_S, RRI8_T)) { static const TCGCond cond[] = { [1] = TCG_COND_EQ, [2] = TCG_COND_LT, @@ -2787,8 +2811,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) break; case 4: /*BALL*/ /*BNALL*/ - gen_window_check2(dc, RRI8_S, RRI8_T); - { + if (gen_window_check2(dc, RRI8_S, RRI8_T)) { TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_and_i32(tmp, cpu_R[RRI8_S], cpu_R[RRI8_T]); gen_brcond(dc, eq_ne, tmp, cpu_R[RRI8_T], @@ -2798,8 +2821,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) break; case 5: /*BBC*/ /*BBS*/ - gen_window_check2(dc, RRI8_S, RRI8_T); - { + if (gen_window_check2(dc, RRI8_S, RRI8_T)) { #ifdef TARGET_WORDS_BIGENDIAN TCGv_i32 bit = tcg_const_i32(0x80000000); #else @@ -2821,8 +2843,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) case 6: /*BBCI*/ /*BBSI*/ case 7: - gen_window_check1(dc, RRI8_S); - { + if (gen_window_check1(dc, RRI8_S)) { TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_andi_i32(tmp, cpu_R[RRI8_S], #ifdef TARGET_WORDS_BIGENDIAN @@ -2840,12 +2861,13 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) break; #define gen_narrow_load_store(type) do { \ - TCGv_i32 addr = tcg_temp_new_i32(); \ - gen_window_check2(dc, RRRN_S, RRRN_T); \ - tcg_gen_addi_i32(addr, cpu_R[RRRN_S], RRRN_R << 2); \ - gen_load_store_alignment(dc, 2, addr, false); \ - tcg_gen_qemu_##type(cpu_R[RRRN_T], addr, dc->cring); \ - tcg_temp_free(addr); \ + if (gen_window_check2(dc, RRRN_S, RRRN_T)) { \ + TCGv_i32 addr = tcg_temp_new_i32(); \ + tcg_gen_addi_i32(addr, cpu_R[RRRN_S], RRRN_R << 2); \ + gen_load_store_alignment(dc, 2, addr, false); \ + tcg_gen_qemu_##type(cpu_R[RRRN_T], addr, dc->cring); \ + tcg_temp_free(addr); \ + } \ } while (0) case 8: /*L32I.Nn*/ @@ -2858,17 +2880,22 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) #undef gen_narrow_load_store case 10: /*ADD.Nn*/ - gen_window_check3(dc, RRRN_R, RRRN_S, RRRN_T); - tcg_gen_add_i32(cpu_R[RRRN_R], cpu_R[RRRN_S], cpu_R[RRRN_T]); + if (gen_window_check3(dc, RRRN_R, RRRN_S, RRRN_T)) { + tcg_gen_add_i32(cpu_R[RRRN_R], cpu_R[RRRN_S], cpu_R[RRRN_T]); + } break; case 11: /*ADDI.Nn*/ - gen_window_check2(dc, RRRN_R, RRRN_S); - tcg_gen_addi_i32(cpu_R[RRRN_R], cpu_R[RRRN_S], RRRN_T ? RRRN_T : -1); + if (gen_window_check2(dc, RRRN_R, RRRN_S)) { + tcg_gen_addi_i32(cpu_R[RRRN_R], cpu_R[RRRN_S], + RRRN_T ? RRRN_T : -1); + } break; case 12: /*ST2n*/ - gen_window_check1(dc, RRRN_S); + if (!gen_window_check1(dc, RRRN_S)) { + break; + } if (RRRN_T < 8) { /*MOVI.Nn*/ tcg_gen_movi_i32(cpu_R[RRRN_S], RRRN_R | (RRRN_T << 4) | @@ -2884,8 +2911,9 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) case 13: /*ST3n*/ switch (RRRN_R) { case 0: /*MOV.Nn*/ - gen_window_check2(dc, RRRN_S, RRRN_T); - tcg_gen_mov_i32(cpu_R[RRRN_T], cpu_R[RRRN_S]); + if (gen_window_check2(dc, RRRN_S, RRRN_T)) { + tcg_gen_mov_i32(cpu_R[RRRN_T], cpu_R[RRRN_S]); + } break; case 15: /*S3*/ @@ -2949,6 +2977,12 @@ invalid_opcode: #undef HAS_OPTION } +static inline unsigned xtensa_insn_len(CPUXtensaState *env, DisasContext *dc) +{ + uint8_t b0 = cpu_ldub_code(env, dc->pc); + return xtensa_op0_insn_len(OP0); +} + static void check_breakpoint(CPUXtensaState *env, DisasContext *dc) { CPUState *cs = CPU(xtensa_env_get_cpu(env)); @@ -2987,7 +3021,6 @@ void gen_intermediate_code_internal(XtensaCPU *cpu, DisasContext dc; int insn_count = 0; int j, lj = -1; - uint16_t *gen_opc_end = tcg_ctx.gen_opc_buf + OPC_MAX_SIZE; int max_insns = tb->cflags & CF_COUNT_MASK; uint32_t pc_start = tb->pc; uint32_t next_page_start = @@ -3011,15 +3044,16 @@ void gen_intermediate_code_internal(XtensaCPU *cpu, dc.icount = tb->flags & XTENSA_TBFLAG_ICOUNT; dc.cpenable = (tb->flags & XTENSA_TBFLAG_CPENABLE_MASK) >> XTENSA_TBFLAG_CPENABLE_SHIFT; + dc.window = ((tb->flags & XTENSA_TBFLAG_WINDOW_MASK) >> + XTENSA_TBFLAG_WINDOW_SHIFT); init_litbase(&dc); init_sar_tracker(&dc); - reset_used_window(&dc); if (dc.icount) { dc.next_icount = tcg_temp_local_new_i32(); } - gen_tb_start(); + gen_tb_start(tb); if (tb->flags & XTENSA_TBFLAG_EXCEPTION) { tcg_gen_movi_i32(cpu_pc, dc.pc); @@ -3030,7 +3064,7 @@ void gen_intermediate_code_internal(XtensaCPU *cpu, check_breakpoint(env, &dc); if (search_pc) { - j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + j = tcg_op_buf_count(); if (lj < j) { lj++; while (lj < j) { @@ -3053,7 +3087,7 @@ void gen_intermediate_code_internal(XtensaCPU *cpu, } if (dc.icount) { - int label = gen_new_label(); + TCGLabel *label = gen_new_label(); tcg_gen_addi_i32(dc.next_icount, cpu_SR[ICOUNT], 1); tcg_gen_brcondi_i32(TCG_COND_NE, dc.next_icount, 0, label); @@ -3081,7 +3115,8 @@ void gen_intermediate_code_internal(XtensaCPU *cpu, } while (dc.is_jmp == DISAS_NEXT && insn_count < max_insns && dc.pc < next_page_start && - tcg_ctx.gen_opc_ptr < gen_opc_end); + dc.pc + xtensa_insn_len(env, &dc) <= next_page_start && + !tcg_op_buf_full()); reset_litbase(&dc); reset_sar_tracker(&dc); @@ -3097,7 +3132,6 @@ void gen_intermediate_code_internal(XtensaCPU *cpu, gen_jumpi(&dc, dc.pc, 0); } gen_tb_end(tb, insn_count); - *tcg_ctx.gen_opc_ptr = INDEX_op_end; #ifdef DEBUG_DISAS if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { @@ -3108,7 +3142,7 @@ void gen_intermediate_code_internal(XtensaCPU *cpu, } #endif if (search_pc) { - j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; + j = tcg_op_buf_count(); memset(tcg_ctx.gen_opc_instr_start + lj + 1, 0, (j - lj) * sizeof(tcg_ctx.gen_opc_instr_start[0])); } else { diff --git a/tcg/aarch64/tcg-target.c b/tcg/aarch64/tcg-target.c index 987c0bd..87dc245 100644 --- a/tcg/aarch64/tcg-target.c +++ b/tcg/aarch64/tcg-target.c @@ -837,12 +837,10 @@ void aarch64_tb_set_jmp_target(uintptr_t jmp_addr, uintptr_t addr) flush_icache_range(jmp_addr, jmp_addr + 4); } -static inline void tcg_out_goto_label(TCGContext *s, int label_index) +static inline void tcg_out_goto_label(TCGContext *s, TCGLabel *l) { - TCGLabel *l = &s->labels[label_index]; - if (!l->has_value) { - tcg_out_reloc(s, s->code_ptr, R_AARCH64_JUMP26, label_index, 0); + tcg_out_reloc(s, s->code_ptr, R_AARCH64_JUMP26, l, 0); tcg_out_goto_noaddr(s); } else { tcg_out_goto(s, l->u.value_ptr); @@ -850,9 +848,8 @@ static inline void tcg_out_goto_label(TCGContext *s, int label_index) } static void tcg_out_brcond(TCGContext *s, TCGMemOp ext, TCGCond c, TCGArg a, - TCGArg b, bool b_const, int label) + TCGArg b, bool b_const, TCGLabel *l) { - TCGLabel *l = &s->labels[label]; intptr_t offset; bool need_cmp; @@ -864,7 +861,7 @@ static void tcg_out_brcond(TCGContext *s, TCGMemOp ext, TCGCond c, TCGArg a, } if (!l->has_value) { - tcg_out_reloc(s, s->code_ptr, R_AARCH64_CONDBR19, label, 0); + tcg_out_reloc(s, s->code_ptr, R_AARCH64_CONDBR19, l, 0); offset = tcg_in32(s) >> 5; } else { offset = l->u.value_ptr - s->code_ptr; @@ -1272,7 +1269,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, break; case INDEX_op_br: - tcg_out_goto_label(s, a0); + tcg_out_goto_label(s, arg_label(a0)); break; case INDEX_op_ld8u_i32: @@ -1495,7 +1492,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, a1 = (int32_t)a1; /* FALLTHRU */ case INDEX_op_brcond_i64: - tcg_out_brcond(s, ext, a2, a0, a1, const_args[1], args[3]); + tcg_out_brcond(s, ext, a2, a0, a1, const_args[1], arg_label(args[3])); break; case INDEX_op_setcond_i32: diff --git a/tcg/arm/tcg-target.c b/tcg/arm/tcg-target.c index e40301c..01e6fbf 100644 --- a/tcg/arm/tcg-target.c +++ b/tcg/arm/tcg-target.c @@ -1038,14 +1038,12 @@ static void tcg_out_call(TCGContext *s, tcg_insn_unit *addr) } } -static inline void tcg_out_goto_label(TCGContext *s, int cond, int label_index) +static inline void tcg_out_goto_label(TCGContext *s, int cond, TCGLabel *l) { - TCGLabel *l = &s->labels[label_index]; - if (l->has_value) { tcg_out_goto(s, cond, l->u.value_ptr); } else { - tcg_out_reloc(s, s->code_ptr, R_ARM_PC24, label_index, 0); + tcg_out_reloc(s, s->code_ptr, R_ARM_PC24, l, 0); tcg_out_b_noaddr(s, cond); } } @@ -1657,7 +1655,7 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, s->tb_next_offset[args[0]] = tcg_current_code_size(s); break; case INDEX_op_br: - tcg_out_goto_label(s, COND_AL, args[0]); + tcg_out_goto_label(s, COND_AL, arg_label(args[0])); break; case INDEX_op_ld8u_i32: @@ -1821,7 +1819,8 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, case INDEX_op_brcond_i32: tcg_out_dat_rIN(s, COND_AL, ARITH_CMP, ARITH_CMN, 0, args[0], args[1], const_args[1]); - tcg_out_goto_label(s, tcg_cond_to_arm_cond[args[2]], args[3]); + tcg_out_goto_label(s, tcg_cond_to_arm_cond[args[2]], + arg_label(args[3])); break; case INDEX_op_brcond2_i32: /* The resulting conditions are: @@ -1836,7 +1835,8 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, args[1], args[3], const_args[3]); tcg_out_dat_rIN(s, COND_EQ, ARITH_CMP, ARITH_CMN, 0, args[0], args[2], const_args[2]); - tcg_out_goto_label(s, tcg_cond_to_arm_cond[args[4]], args[5]); + tcg_out_goto_label(s, tcg_cond_to_arm_cond[args[4]], + arg_label(args[5])); break; case INDEX_op_setcond_i32: tcg_out_dat_rIN(s, COND_AL, ARITH_CMP, ARITH_CMN, 0, diff --git a/tcg/i386/tcg-target.c b/tcg/i386/tcg-target.c index 4133dcf..ab63823 100644 --- a/tcg/i386/tcg-target.c +++ b/tcg/i386/tcg-target.c @@ -853,10 +853,9 @@ static void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val) } /* Use SMALL != 0 to force a short forward branch. */ -static void tcg_out_jxx(TCGContext *s, int opc, int label_index, int small) +static void tcg_out_jxx(TCGContext *s, int opc, TCGLabel *l, int small) { int32_t val, val1; - TCGLabel *l = &s->labels[label_index]; if (l->has_value) { val = tcg_pcrel_diff(s, l->u.value_ptr); @@ -886,7 +885,7 @@ static void tcg_out_jxx(TCGContext *s, int opc, int label_index, int small) } else { tcg_out8(s, OPC_JCC_short + opc); } - tcg_out_reloc(s, s->code_ptr, R_386_PC8, label_index, -1); + tcg_out_reloc(s, s->code_ptr, R_386_PC8, l, -1); s->code_ptr += 1; } else { if (opc == -1) { @@ -894,7 +893,7 @@ static void tcg_out_jxx(TCGContext *s, int opc, int label_index, int small) } else { tcg_out_opc(s, OPC_JCC_long + opc, 0, 0, 0); } - tcg_out_reloc(s, s->code_ptr, R_386_PC32, label_index, -4); + tcg_out_reloc(s, s->code_ptr, R_386_PC32, l, -4); s->code_ptr += 4; } } @@ -916,19 +915,19 @@ static void tcg_out_cmp(TCGContext *s, TCGArg arg1, TCGArg arg2, static void tcg_out_brcond32(TCGContext *s, TCGCond cond, TCGArg arg1, TCGArg arg2, int const_arg2, - int label_index, int small) + TCGLabel *label, int small) { tcg_out_cmp(s, arg1, arg2, const_arg2, 0); - tcg_out_jxx(s, tcg_cond_to_jcc[cond], label_index, small); + tcg_out_jxx(s, tcg_cond_to_jcc[cond], label, small); } #if TCG_TARGET_REG_BITS == 64 static void tcg_out_brcond64(TCGContext *s, TCGCond cond, TCGArg arg1, TCGArg arg2, int const_arg2, - int label_index, int small) + TCGLabel *label, int small) { tcg_out_cmp(s, arg1, arg2, const_arg2, P_REXW); - tcg_out_jxx(s, tcg_cond_to_jcc[cond], label_index, small); + tcg_out_jxx(s, tcg_cond_to_jcc[cond], label, small); } #else /* XXX: we implement it at the target level to avoid having to @@ -936,76 +935,77 @@ static void tcg_out_brcond64(TCGContext *s, TCGCond cond, static void tcg_out_brcond2(TCGContext *s, const TCGArg *args, const int *const_args, int small) { - int label_next; - label_next = gen_new_label(); + TCGLabel *label_next = gen_new_label(); + TCGLabel *label_this = arg_label(args[5]); + switch(args[4]) { case TCG_COND_EQ: tcg_out_brcond32(s, TCG_COND_NE, args[0], args[2], const_args[2], label_next, 1); tcg_out_brcond32(s, TCG_COND_EQ, args[1], args[3], const_args[3], - args[5], small); + label_this, small); break; case TCG_COND_NE: tcg_out_brcond32(s, TCG_COND_NE, args[0], args[2], const_args[2], - args[5], small); + label_this, small); tcg_out_brcond32(s, TCG_COND_NE, args[1], args[3], const_args[3], - args[5], small); + label_this, small); break; case TCG_COND_LT: tcg_out_brcond32(s, TCG_COND_LT, args[1], args[3], const_args[3], - args[5], small); + label_this, small); tcg_out_jxx(s, JCC_JNE, label_next, 1); tcg_out_brcond32(s, TCG_COND_LTU, args[0], args[2], const_args[2], - args[5], small); + label_this, small); break; case TCG_COND_LE: tcg_out_brcond32(s, TCG_COND_LT, args[1], args[3], const_args[3], - args[5], small); + label_this, small); tcg_out_jxx(s, JCC_JNE, label_next, 1); tcg_out_brcond32(s, TCG_COND_LEU, args[0], args[2], const_args[2], - args[5], small); + label_this, small); break; case TCG_COND_GT: tcg_out_brcond32(s, TCG_COND_GT, args[1], args[3], const_args[3], - args[5], small); + label_this, small); tcg_out_jxx(s, JCC_JNE, label_next, 1); tcg_out_brcond32(s, TCG_COND_GTU, args[0], args[2], const_args[2], - args[5], small); + label_this, small); break; case TCG_COND_GE: tcg_out_brcond32(s, TCG_COND_GT, args[1], args[3], const_args[3], - args[5], small); + label_this, small); tcg_out_jxx(s, JCC_JNE, label_next, 1); tcg_out_brcond32(s, TCG_COND_GEU, args[0], args[2], const_args[2], - args[5], small); + label_this, small); break; case TCG_COND_LTU: tcg_out_brcond32(s, TCG_COND_LTU, args[1], args[3], const_args[3], - args[5], small); + label_this, small); tcg_out_jxx(s, JCC_JNE, label_next, 1); tcg_out_brcond32(s, TCG_COND_LTU, args[0], args[2], const_args[2], - args[5], small); + label_this, small); break; case TCG_COND_LEU: tcg_out_brcond32(s, TCG_COND_LTU, args[1], args[3], const_args[3], - args[5], small); + label_this, small); tcg_out_jxx(s, JCC_JNE, label_next, 1); tcg_out_brcond32(s, TCG_COND_LEU, args[0], args[2], const_args[2], - args[5], small); + label_this, small); break; case TCG_COND_GTU: tcg_out_brcond32(s, TCG_COND_GTU, args[1], args[3], const_args[3], - args[5], small); + label_this, small); tcg_out_jxx(s, JCC_JNE, label_next, 1); tcg_out_brcond32(s, TCG_COND_GTU, args[0], args[2], const_args[2], - args[5], small); + label_this, small); break; case TCG_COND_GEU: tcg_out_brcond32(s, TCG_COND_GTU, args[1], args[3], const_args[3], - args[5], small); + label_this, small); tcg_out_jxx(s, JCC_JNE, label_next, 1); tcg_out_brcond32(s, TCG_COND_GEU, args[0], args[2], const_args[2], - args[5], small); + label_this, small); break; default: tcg_abort(); @@ -1035,7 +1035,7 @@ static void tcg_out_setcond2(TCGContext *s, const TCGArg *args, const int *const_args) { TCGArg new_args[6]; - int label_true, label_over; + TCGLabel *label_true, *label_over; memcpy(new_args, args+1, 5*sizeof(TCGArg)); @@ -1047,7 +1047,7 @@ static void tcg_out_setcond2(TCGContext *s, const TCGArg *args, label_true = gen_new_label(); label_over = gen_new_label(); - new_args[5] = label_true; + new_args[5] = label_arg(label_true); tcg_out_brcond2(s, new_args, const_args+1, 1); tcg_out_movi(s, TCG_TYPE_I32, args[0], 0); @@ -1065,7 +1065,7 @@ static void tcg_out_setcond2(TCGContext *s, const TCGArg *args, label_over = gen_new_label(); new_args[4] = tcg_invert_cond(new_args[4]); - new_args[5] = label_over; + new_args[5] = label_arg(label_over); tcg_out_brcond2(s, new_args, const_args+1, 1); tgen_arithi(s, ARITH_ADD, args[0], 1, 0); @@ -1082,7 +1082,7 @@ static void tcg_out_movcond32(TCGContext *s, TCGCond cond, TCGArg dest, if (have_cmov) { tcg_out_modrm(s, OPC_CMOVCC | tcg_cond_to_jcc[cond], dest, v1); } else { - int over = gen_new_label(); + TCGLabel *over = gen_new_label(); tcg_out_jxx(s, tcg_cond_to_jcc[tcg_invert_cond(cond)], over, 1); tcg_out_mov(s, TCG_TYPE_I32, dest, v1); tcg_out_label(s, over, s->code_ptr); @@ -1748,7 +1748,7 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, s->tb_next_offset[args[0]] = tcg_current_code_size(s); break; case INDEX_op_br: - tcg_out_jxx(s, JCC_JMP, args[0], 0); + tcg_out_jxx(s, JCC_JMP, arg_label(args[0]), 0); break; OP_32_64(ld8u): /* Note that we can ignore REXW for the zero-extend to 64-bit. */ @@ -1909,7 +1909,7 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, case INDEX_op_brcond_i32: tcg_out_brcond32(s, args[2], args[0], args[1], const_args[1], - args[3], 0); + arg_label(args[3]), 0); break; case INDEX_op_setcond_i32: tcg_out_setcond32(s, args[3], args[0], args[1], @@ -2017,7 +2017,7 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, case INDEX_op_brcond_i64: tcg_out_brcond64(s, args[2], args[0], args[1], const_args[1], - args[3], 0); + arg_label(args[3]), 0); break; case INDEX_op_setcond_i64: tcg_out_setcond64(s, args[3], args[0], args[1], diff --git a/tcg/ia64/tcg-target.c b/tcg/ia64/tcg-target.c index 6bc9924..25f207d 100644 --- a/tcg/ia64/tcg-target.c +++ b/tcg/ia64/tcg-target.c @@ -827,9 +827,8 @@ static inline void tcg_out_movi(TCGContext *s, TCGType type, tcg_opc_x2 (TCG_REG_P0, OPC_MOVL_X2, reg, arg)); } -static void tcg_out_br(TCGContext *s, int label_index) +static void tcg_out_br(TCGContext *s, TCGLabel *l) { - TCGLabel *l = &s->labels[label_index]; uint64_t imm; /* We pay attention here to not modify the branch target by reading @@ -839,7 +838,7 @@ static void tcg_out_br(TCGContext *s, int label_index) imm = l->u.value_ptr - s->code_ptr; } else { imm = get_reloc_pcrel21b_slot2(s->code_ptr); - tcg_out_reloc(s, s->code_ptr, R_IA64_PCREL21B, label_index, 0); + tcg_out_reloc(s, s->code_ptr, R_IA64_PCREL21B, l, 0); } tcg_out_bundle(s, mmB, @@ -1424,9 +1423,8 @@ static inline uint64_t tcg_opc_cmp_a(int qp, TCGCond cond, TCGArg arg1, } static inline void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1, - TCGReg arg2, int label_index, int cmp4) + TCGReg arg2, TCGLabel *l, int cmp4) { - TCGLabel *l = &s->labels[label_index]; uint64_t imm; /* We pay attention here to not modify the branch target by reading @@ -1436,7 +1434,7 @@ static inline void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1, imm = l->u.value_ptr - s->code_ptr; } else { imm = get_reloc_pcrel21b_slot2(s->code_ptr); - tcg_out_reloc(s, s->code_ptr, R_IA64_PCREL21B, label_index, 0); + tcg_out_reloc(s, s->code_ptr, R_IA64_PCREL21B, l, 0); } tcg_out_bundle(s, miB, @@ -1550,34 +1548,33 @@ static inline void tcg_out_qemu_tlb(TCGContext *s, TCGReg addr_reg, bswap2); } -#define TCG_MAX_QEMU_LDST 640 - typedef struct TCGLabelQemuLdst { bool is_ld; TCGMemOp size; tcg_insn_unit *label_ptr; /* label pointers to be updated */ + struct TCGLabelQemuLdst *next; } TCGLabelQemuLdst; typedef struct TCGBackendData { - int nb_ldst_labels; - TCGLabelQemuLdst ldst_labels[TCG_MAX_QEMU_LDST]; + TCGLabelQemuLdst *labels; } TCGBackendData; static inline void tcg_out_tb_init(TCGContext *s) { - s->be->nb_ldst_labels = 0; + s->be->labels = NULL; } static void add_qemu_ldst_label(TCGContext *s, bool is_ld, TCGMemOp opc, tcg_insn_unit *label_ptr) { TCGBackendData *be = s->be; - TCGLabelQemuLdst *l = &be->ldst_labels[be->nb_ldst_labels++]; + TCGLabelQemuLdst *l = tcg_malloc(sizeof(*l)); - assert(be->nb_ldst_labels <= TCG_MAX_QEMU_LDST); l->is_ld = is_ld; l->size = opc & MO_SIZE; l->label_ptr = label_ptr; + l->next = be->labels; + be->labels = l; } static void tcg_out_tb_finalize(TCGContext *s) @@ -1593,11 +1590,9 @@ static void tcg_out_tb_finalize(TCGContext *s) helper_le_ldq_mmu, }; tcg_insn_unit *thunks[8] = { }; - TCGBackendData *be = s->be; - size_t i, n = be->nb_ldst_labels; + TCGLabelQemuLdst *l; - for (i = 0; i < n; i++) { - TCGLabelQemuLdst *l = &be->ldst_labels[i]; + for (l = s->be->labels; l != NULL; l = l->next) { long x = l->is_ld * 4 + l->size; tcg_insn_unit *dest = thunks[x]; @@ -1993,7 +1988,7 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, tcg_out_exit_tb(s, args[0]); break; case INDEX_op_br: - tcg_out_br(s, args[0]); + tcg_out_br(s, arg_label(args[0])); break; case INDEX_op_goto_tb: tcg_out_goto_tb(s, args[0]); @@ -2175,10 +2170,10 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, break; case INDEX_op_brcond_i32: - tcg_out_brcond(s, args[2], args[0], args[1], args[3], 1); + tcg_out_brcond(s, args[2], args[0], args[1], arg_label(args[3]), 1); break; case INDEX_op_brcond_i64: - tcg_out_brcond(s, args[2], args[0], args[1], args[3], 0); + tcg_out_brcond(s, args[2], args[0], args[1], arg_label(args[3]), 0); break; case INDEX_op_setcond_i32: tcg_out_setcond(s, args[3], args[0], args[1], args[2], 1); diff --git a/tcg/mips/tcg-target.c b/tcg/mips/tcg-target.c index b7f4d67..015ceab 100644 --- a/tcg/mips/tcg-target.c +++ b/tcg/mips/tcg-target.c @@ -635,7 +635,7 @@ static void tcg_out_setcond(TCGContext *s, TCGCond cond, TCGReg ret, } static void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1, - TCGReg arg2, int label_index) + TCGReg arg2, TCGLabel *l) { static const MIPSInsn b_zero[16] = { [TCG_COND_LT] = OPC_BLTZ, @@ -644,7 +644,6 @@ static void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1, [TCG_COND_GE] = OPC_BGEZ, }; - TCGLabel *l; MIPSInsn s_opc = OPC_SLTU; MIPSInsn b_opc; int cmp_map; @@ -692,11 +691,10 @@ static void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1, } tcg_out_opc_br(s, b_opc, arg1, arg2); - l = &s->labels[label_index]; if (l->has_value) { reloc_pc16(s->code_ptr - 1, l->u.value_ptr); } else { - tcg_out_reloc(s, s->code_ptr - 1, R_MIPS_PC16, label_index, 0); + tcg_out_reloc(s, s->code_ptr - 1, R_MIPS_PC16, l, 0); } tcg_out_nop(s); } @@ -765,7 +763,7 @@ static void tcg_out_setcond2(TCGContext *s, TCGCond cond, TCGReg ret, } static void tcg_out_brcond2(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah, - TCGReg bl, TCGReg bh, int label_index) + TCGReg bl, TCGReg bh, TCGLabel *l) { TCGCond b_cond = TCG_COND_NE; TCGReg tmp = TCG_TMP1; @@ -790,7 +788,7 @@ static void tcg_out_brcond2(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah, break; } - tcg_out_brcond(s, b_cond, tmp, TCG_REG_ZERO, label_index); + tcg_out_brcond(s, b_cond, tmp, TCG_REG_ZERO, l); } static void tcg_out_movcond(TCGContext *s, TCGCond cond, TCGReg ret, @@ -965,9 +963,11 @@ static void tcg_out_tlb_load(TCGContext *s, TCGReg base, TCGReg addrl, } /* Load the tlb comparator. */ - tcg_out_opc_imm(s, OPC_LW, TCG_TMP0, TCG_REG_A0, cmp_off + LO_OFF); if (TARGET_LONG_BITS == 64) { + tcg_out_opc_imm(s, OPC_LW, TCG_TMP0, TCG_REG_A0, cmp_off + LO_OFF); tcg_out_opc_imm(s, OPC_LW, base, TCG_REG_A0, cmp_off + HI_OFF); + } else { + tcg_out_opc_imm(s, OPC_LW, TCG_TMP0, TCG_REG_A0, cmp_off); } /* Mask the page bits, keeping the alignment bits to compare against. @@ -1269,6 +1269,9 @@ static void tcg_out_addsub2(TCGContext *s, TCGReg rl, TCGReg rh, TCGReg al, if (cbl) { tcg_out_opc_imm(s, OPC_ADDIU, rl, al, bl); tcg_out_opc_imm(s, OPC_SLTIU, TCG_TMP0, rl, bl); + } else if (rl == al && rl == bl) { + tcg_out_opc_sa(s, OPC_SRL, TCG_TMP0, al, 31); + tcg_out_opc_reg(s, OPC_ADDU, rl, al, bl); } else { tcg_out_opc_reg(s, OPC_ADDU, rl, al, bl); tcg_out_opc_reg(s, OPC_SLTU, TCG_TMP0, rl, (rl == bl ? al : bl)); @@ -1367,7 +1370,8 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, s->tb_next_offset[a0] = tcg_current_code_size(s); break; case INDEX_op_br: - tcg_out_brcond(s, TCG_COND_EQ, TCG_REG_ZERO, TCG_REG_ZERO, a0); + tcg_out_brcond(s, TCG_COND_EQ, TCG_REG_ZERO, TCG_REG_ZERO, + arg_label(a0)); break; case INDEX_op_ld8u_i32: @@ -1527,10 +1531,10 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, break; case INDEX_op_brcond_i32: - tcg_out_brcond(s, a2, a0, a1, args[3]); + tcg_out_brcond(s, a2, a0, a1, arg_label(args[3])); break; case INDEX_op_brcond2_i32: - tcg_out_brcond2(s, args[4], a0, a1, a2, args[3], args[5]); + tcg_out_brcond2(s, args[4], a0, a1, a2, args[3], arg_label(args[5])); break; case INDEX_op_movcond_i32: diff --git a/tcg/optimize.c b/tcg/optimize.c index 34ae3c2..37c1110 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -67,6 +67,37 @@ static void reset_temp(TCGArg temp) temps[temp].mask = -1; } +static TCGOp *insert_op_before(TCGContext *s, TCGOp *old_op, + TCGOpcode opc, int nargs) +{ + int oi = s->gen_next_op_idx; + int pi = s->gen_next_parm_idx; + int prev = old_op->prev; + int next = old_op - s->gen_op_buf; + TCGOp *new_op; + + tcg_debug_assert(oi < OPC_BUF_SIZE); + tcg_debug_assert(pi + nargs <= OPPARAM_BUF_SIZE); + s->gen_next_op_idx = oi + 1; + s->gen_next_parm_idx = pi + nargs; + + new_op = &s->gen_op_buf[oi]; + *new_op = (TCGOp){ + .opc = opc, + .args = pi, + .prev = prev, + .next = next + }; + if (prev >= 0) { + s->gen_op_buf[prev].next = oi; + } else { + s->gen_first_op_idx = oi; + } + old_op->prev = oi; + + return new_op; +} + /* Reset all temporaries, given that there are NB_TEMPS of them. */ static void reset_all_temps(int nb_temps) { @@ -162,13 +193,13 @@ static bool temps_are_copies(TCGArg arg1, TCGArg arg2) return false; } -static void tcg_opt_gen_mov(TCGContext *s, int op_index, TCGArg *gen_args, +static void tcg_opt_gen_mov(TCGContext *s, TCGOp *op, TCGArg *args, TCGOpcode old_op, TCGArg dst, TCGArg src) { TCGOpcode new_op = op_to_mov(old_op); tcg_target_ulong mask; - s->gen_opc_buf[op_index] = new_op; + op->opc = new_op; reset_temp(dst); mask = temps[src].mask; @@ -193,17 +224,17 @@ static void tcg_opt_gen_mov(TCGContext *s, int op_index, TCGArg *gen_args, temps[src].next_copy = dst; } - gen_args[0] = dst; - gen_args[1] = src; + args[0] = dst; + args[1] = src; } -static void tcg_opt_gen_movi(TCGContext *s, int op_index, TCGArg *gen_args, +static void tcg_opt_gen_movi(TCGContext *s, TCGOp *op, TCGArg *args, TCGOpcode old_op, TCGArg dst, TCGArg val) { TCGOpcode new_op = op_to_movi(old_op); tcg_target_ulong mask; - s->gen_opc_buf[op_index] = new_op; + op->opc = new_op; reset_temp(dst); temps[dst].state = TCG_TEMP_CONST; @@ -215,8 +246,8 @@ static void tcg_opt_gen_movi(TCGContext *s, int op_index, TCGArg *gen_args, } temps[dst].mask = mask; - gen_args[0] = dst; - gen_args[1] = val; + args[0] = dst; + args[1] = val; } static TCGArg do_constant_folding_2(TCGOpcode op, TCGArg x, TCGArg y) @@ -533,11 +564,9 @@ static bool swap_commutative2(TCGArg *p1, TCGArg *p2) } /* Propagate constants and copies, fold constant expressions. */ -static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, - TCGArg *args, TCGOpDef *tcg_op_defs) +static void tcg_constant_folding(TCGContext *s) { - int nb_ops, op_index, nb_temps, nb_globals; - TCGArg *gen_args; + int oi, oi_next, nb_temps, nb_globals; /* Array VALS has an element for each temp. If this temp holds a constant then its value is kept in VALS' element. @@ -548,24 +577,23 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, nb_globals = s->nb_globals; reset_all_temps(nb_temps); - nb_ops = tcg_opc_ptr - s->gen_opc_buf; - gen_args = args; - for (op_index = 0; op_index < nb_ops; op_index++) { - TCGOpcode op = s->gen_opc_buf[op_index]; - const TCGOpDef *def = &tcg_op_defs[op]; + for (oi = s->gen_first_op_idx; oi >= 0; oi = oi_next) { tcg_target_ulong mask, partmask, affected; - int nb_oargs, nb_iargs, nb_args, i; + int nb_oargs, nb_iargs, i; TCGArg tmp; - if (op == INDEX_op_call) { - *gen_args++ = tmp = *args++; - nb_oargs = tmp >> 16; - nb_iargs = tmp & 0xffff; - nb_args = nb_oargs + nb_iargs + def->nb_cargs; + TCGOp * const op = &s->gen_op_buf[oi]; + TCGArg * const args = &s->gen_opparam_buf[op->args]; + TCGOpcode opc = op->opc; + const TCGOpDef *def = &tcg_op_defs[opc]; + + oi_next = op->next; + if (opc == INDEX_op_call) { + nb_oargs = op->callo; + nb_iargs = op->calli; } else { nb_oargs = def->nb_oargs; nb_iargs = def->nb_iargs; - nb_args = def->nb_args; } /* Do copy propagation */ @@ -576,7 +604,7 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, } /* For commutative operations make constant second argument */ - switch (op) { + switch (opc) { CASE_OP_32_64(add): CASE_OP_32_64(mul): CASE_OP_32_64(and): @@ -634,7 +662,7 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, /* Simplify expressions for "shift/rot r, 0, a => movi r, 0", and "sub r, 0, a => neg r, a" case. */ - switch (op) { + switch (opc) { CASE_OP_32_64(shl): CASE_OP_32_64(shr): CASE_OP_32_64(sar): @@ -642,9 +670,7 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, CASE_OP_32_64(rotr): if (temps[args[1]].state == TCG_TEMP_CONST && temps[args[1]].val == 0) { - tcg_opt_gen_movi(s, op_index, gen_args, op, args[0], 0); - args += 3; - gen_args += 2; + tcg_opt_gen_movi(s, op, args, opc, args[0], 0); continue; } break; @@ -657,7 +683,7 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, /* Proceed with possible constant folding. */ break; } - if (op == INDEX_op_sub_i32) { + if (opc == INDEX_op_sub_i32) { neg_op = INDEX_op_neg_i32; have_neg = TCG_TARGET_HAS_neg_i32; } else { @@ -669,12 +695,9 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, } if (temps[args[1]].state == TCG_TEMP_CONST && temps[args[1]].val == 0) { - s->gen_opc_buf[op_index] = neg_op; + op->opc = neg_op; reset_temp(args[0]); - gen_args[0] = args[0]; - gen_args[1] = args[2]; - args += 3; - gen_args += 2; + args[1] = args[2]; continue; } } @@ -728,12 +751,9 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, if (!have_not) { break; } - s->gen_opc_buf[op_index] = not_op; + op->opc = not_op; reset_temp(args[0]); - gen_args[0] = args[0]; - gen_args[1] = args[i]; - args += 3; - gen_args += 2; + args[1] = args[i]; continue; } default: @@ -741,7 +761,7 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, } /* Simplify expression for "op r, a, const => mov r, a" cases */ - switch (op) { + switch (opc) { CASE_OP_32_64(add): CASE_OP_32_64(sub): CASE_OP_32_64(shl): @@ -769,12 +789,10 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, break; do_mov3: if (temps_are_copies(args[0], args[1])) { - s->gen_opc_buf[op_index] = INDEX_op_nop; + tcg_op_remove(s, op); } else { - tcg_opt_gen_mov(s, op_index, gen_args, op, args[0], args[1]); - gen_args += 2; + tcg_opt_gen_mov(s, op, args, opc, args[0], args[1]); } - args += 3; continue; default: break; @@ -784,7 +802,7 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, output argument is supported. */ mask = -1; affected = -1; - switch (op) { + switch (opc) { CASE_OP_32_64(ext8s): if ((temps[args[1]].mask & 0x80) != 0) { break; @@ -923,38 +941,31 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, if (partmask == 0) { assert(nb_oargs == 1); - tcg_opt_gen_movi(s, op_index, gen_args, op, args[0], 0); - args += nb_args; - gen_args += 2; + tcg_opt_gen_movi(s, op, args, opc, args[0], 0); continue; } if (affected == 0) { assert(nb_oargs == 1); if (temps_are_copies(args[0], args[1])) { - s->gen_opc_buf[op_index] = INDEX_op_nop; + tcg_op_remove(s, op); } else if (temps[args[1]].state != TCG_TEMP_CONST) { - tcg_opt_gen_mov(s, op_index, gen_args, op, args[0], args[1]); - gen_args += 2; + tcg_opt_gen_mov(s, op, args, opc, args[0], args[1]); } else { - tcg_opt_gen_movi(s, op_index, gen_args, op, + tcg_opt_gen_movi(s, op, args, opc, args[0], temps[args[1]].val); - gen_args += 2; } - args += nb_args; continue; } /* Simplify expression for "op r, a, 0 => movi r, 0" cases */ - switch (op) { + switch (opc) { CASE_OP_32_64(and): CASE_OP_32_64(mul): CASE_OP_32_64(muluh): CASE_OP_32_64(mulsh): if ((temps[args[2]].state == TCG_TEMP_CONST && temps[args[2]].val == 0)) { - tcg_opt_gen_movi(s, op_index, gen_args, op, args[0], 0); - args += 3; - gen_args += 2; + tcg_opt_gen_movi(s, op, args, opc, args[0], 0); continue; } break; @@ -963,18 +974,18 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, } /* Simplify expression for "op r, a, a => mov r, a" cases */ - switch (op) { + switch (opc) { CASE_OP_32_64(or): CASE_OP_32_64(and): if (temps_are_copies(args[1], args[2])) { if (temps_are_copies(args[0], args[1])) { - s->gen_opc_buf[op_index] = INDEX_op_nop; + tcg_op_remove(s, op); + } else if (temps[args[1]].state != TCG_TEMP_CONST) { + tcg_opt_gen_mov(s, op, args, opc, args[0], args[1]); } else { - tcg_opt_gen_mov(s, op_index, gen_args, op, - args[0], args[1]); - gen_args += 2; + tcg_opt_gen_movi(s, op, args, opc, + args[0], temps[args[1]].val); } - args += 3; continue; } break; @@ -983,14 +994,12 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, } /* Simplify expression for "op r, a, a => movi r, 0" cases */ - switch (op) { + switch (opc) { CASE_OP_32_64(andc): CASE_OP_32_64(sub): CASE_OP_32_64(xor): if (temps_are_copies(args[1], args[2])) { - tcg_opt_gen_movi(s, op_index, gen_args, op, args[0], 0); - gen_args += 2; - args += 3; + tcg_opt_gen_movi(s, op, args, opc, args[0], 0); continue; } break; @@ -1001,17 +1010,14 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, /* Propagate constants through copy operations and do constant folding. Constants will be substituted to arguments by register allocator where needed and possible. Also detect copies. */ - switch (op) { + switch (opc) { CASE_OP_32_64(mov): if (temps_are_copies(args[0], args[1])) { - args += 2; - s->gen_opc_buf[op_index] = INDEX_op_nop; + tcg_op_remove(s, op); break; } if (temps[args[1]].state != TCG_TEMP_CONST) { - tcg_opt_gen_mov(s, op_index, gen_args, op, args[0], args[1]); - gen_args += 2; - args += 2; + tcg_opt_gen_mov(s, op, args, opc, args[0], args[1]); break; } /* Source argument is constant. Rewrite the operation and @@ -1019,9 +1025,7 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, args[1] = temps[args[1]].val; /* fallthrough */ CASE_OP_32_64(movi): - tcg_opt_gen_movi(s, op_index, gen_args, op, args[0], args[1]); - gen_args += 2; - args += 2; + tcg_opt_gen_movi(s, op, args, opc, args[0], args[1]); break; CASE_OP_32_64(not): @@ -1033,20 +1037,16 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, case INDEX_op_ext32s_i64: case INDEX_op_ext32u_i64: if (temps[args[1]].state == TCG_TEMP_CONST) { - tmp = do_constant_folding(op, temps[args[1]].val, 0); - tcg_opt_gen_movi(s, op_index, gen_args, op, args[0], tmp); - gen_args += 2; - args += 2; + tmp = do_constant_folding(opc, temps[args[1]].val, 0); + tcg_opt_gen_movi(s, op, args, opc, args[0], tmp); break; } goto do_default; case INDEX_op_trunc_shr_i32: if (temps[args[1]].state == TCG_TEMP_CONST) { - tmp = do_constant_folding(op, temps[args[1]].val, args[2]); - tcg_opt_gen_movi(s, op_index, gen_args, op, args[0], tmp); - gen_args += 2; - args += 3; + tmp = do_constant_folding(opc, temps[args[1]].val, args[2]); + tcg_opt_gen_movi(s, op, args, opc, args[0], tmp); break; } goto do_default; @@ -1075,11 +1075,9 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, CASE_OP_32_64(remu): if (temps[args[1]].state == TCG_TEMP_CONST && temps[args[2]].state == TCG_TEMP_CONST) { - tmp = do_constant_folding(op, temps[args[1]].val, + tmp = do_constant_folding(opc, temps[args[1]].val, temps[args[2]].val); - tcg_opt_gen_movi(s, op_index, gen_args, op, args[0], tmp); - gen_args += 2; - args += 3; + tcg_opt_gen_movi(s, op, args, opc, args[0], tmp); break; } goto do_default; @@ -1089,54 +1087,44 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, && temps[args[2]].state == TCG_TEMP_CONST) { tmp = deposit64(temps[args[1]].val, args[3], args[4], temps[args[2]].val); - tcg_opt_gen_movi(s, op_index, gen_args, op, args[0], tmp); - gen_args += 2; - args += 5; + tcg_opt_gen_movi(s, op, args, opc, args[0], tmp); break; } goto do_default; CASE_OP_32_64(setcond): - tmp = do_constant_folding_cond(op, args[1], args[2], args[3]); + tmp = do_constant_folding_cond(opc, args[1], args[2], args[3]); if (tmp != 2) { - tcg_opt_gen_movi(s, op_index, gen_args, op, args[0], tmp); - gen_args += 2; - args += 4; + tcg_opt_gen_movi(s, op, args, opc, args[0], tmp); break; } goto do_default; CASE_OP_32_64(brcond): - tmp = do_constant_folding_cond(op, args[0], args[1], args[2]); + tmp = do_constant_folding_cond(opc, args[0], args[1], args[2]); if (tmp != 2) { if (tmp) { reset_all_temps(nb_temps); - s->gen_opc_buf[op_index] = INDEX_op_br; - gen_args[0] = args[3]; - gen_args += 1; + op->opc = INDEX_op_br; + args[0] = args[3]; } else { - s->gen_opc_buf[op_index] = INDEX_op_nop; + tcg_op_remove(s, op); } - args += 4; break; } goto do_default; CASE_OP_32_64(movcond): - tmp = do_constant_folding_cond(op, args[1], args[2], args[5]); + tmp = do_constant_folding_cond(opc, args[1], args[2], args[5]); if (tmp != 2) { if (temps_are_copies(args[0], args[4-tmp])) { - s->gen_opc_buf[op_index] = INDEX_op_nop; + tcg_op_remove(s, op); } else if (temps[args[4-tmp]].state == TCG_TEMP_CONST) { - tcg_opt_gen_movi(s, op_index, gen_args, op, + tcg_opt_gen_movi(s, op, args, opc, args[0], temps[args[4-tmp]].val); - gen_args += 2; } else { - tcg_opt_gen_mov(s, op_index, gen_args, op, - args[0], args[4-tmp]); - gen_args += 2; + tcg_opt_gen_mov(s, op, args, opc, args[0], args[4-tmp]); } - args += 6; break; } goto do_default; @@ -1154,24 +1142,22 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, uint64_t a = ((uint64_t)ah << 32) | al; uint64_t b = ((uint64_t)bh << 32) | bl; TCGArg rl, rh; + TCGOp *op2 = insert_op_before(s, op, INDEX_op_movi_i32, 2); + TCGArg *args2 = &s->gen_opparam_buf[op2->args]; - if (op == INDEX_op_add2_i32) { + if (opc == INDEX_op_add2_i32) { a += b; } else { a -= b; } - /* We emit the extra nop when we emit the add2/sub2. */ - assert(s->gen_opc_buf[op_index + 1] == INDEX_op_nop); - rl = args[0]; rh = args[1]; - tcg_opt_gen_movi(s, op_index, &gen_args[0], - op, rl, (uint32_t)a); - tcg_opt_gen_movi(s, ++op_index, &gen_args[2], - op, rh, (uint32_t)(a >> 32)); - gen_args += 4; - args += 6; + tcg_opt_gen_movi(s, op, args, opc, rl, (uint32_t)a); + tcg_opt_gen_movi(s, op2, args2, opc, rh, (uint32_t)(a >> 32)); + + /* We've done all we need to do with the movi. Skip it. */ + oi_next = op2->next; break; } goto do_default; @@ -1183,18 +1169,16 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, uint32_t b = temps[args[3]].val; uint64_t r = (uint64_t)a * b; TCGArg rl, rh; - - /* We emit the extra nop when we emit the mulu2. */ - assert(s->gen_opc_buf[op_index + 1] == INDEX_op_nop); + TCGOp *op2 = insert_op_before(s, op, INDEX_op_movi_i32, 2); + TCGArg *args2 = &s->gen_opparam_buf[op2->args]; rl = args[0]; rh = args[1]; - tcg_opt_gen_movi(s, op_index, &gen_args[0], - op, rl, (uint32_t)r); - tcg_opt_gen_movi(s, ++op_index, &gen_args[2], - op, rh, (uint32_t)(r >> 32)); - gen_args += 4; - args += 4; + tcg_opt_gen_movi(s, op, args, opc, rl, (uint32_t)r); + tcg_opt_gen_movi(s, op2, args2, opc, rh, (uint32_t)(r >> 32)); + + /* We've done all we need to do with the movi. Skip it. */ + oi_next = op2->next; break; } goto do_default; @@ -1205,12 +1189,11 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, if (tmp) { do_brcond_true: reset_all_temps(nb_temps); - s->gen_opc_buf[op_index] = INDEX_op_br; - gen_args[0] = args[5]; - gen_args += 1; + op->opc = INDEX_op_br; + args[0] = args[5]; } else { do_brcond_false: - s->gen_opc_buf[op_index] = INDEX_op_nop; + tcg_op_remove(s, op); } } else if ((args[4] == TCG_COND_LT || args[4] == TCG_COND_GE) && temps[args[2]].state == TCG_TEMP_CONST @@ -1221,12 +1204,11 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, vs the high word of the input. */ do_brcond_high: reset_all_temps(nb_temps); - s->gen_opc_buf[op_index] = INDEX_op_brcond_i32; - gen_args[0] = args[1]; - gen_args[1] = args[3]; - gen_args[2] = args[4]; - gen_args[3] = args[5]; - gen_args += 4; + op->opc = INDEX_op_brcond_i32; + args[0] = args[1]; + args[1] = args[3]; + args[2] = args[4]; + args[3] = args[5]; } else if (args[4] == TCG_COND_EQ) { /* Simplify EQ comparisons where one of the pairs can be simplified. */ @@ -1246,12 +1228,10 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, } do_brcond_low: reset_all_temps(nb_temps); - s->gen_opc_buf[op_index] = INDEX_op_brcond_i32; - gen_args[0] = args[0]; - gen_args[1] = args[2]; - gen_args[2] = args[4]; - gen_args[3] = args[5]; - gen_args += 4; + op->opc = INDEX_op_brcond_i32; + args[1] = args[2]; + args[2] = args[4]; + args[3] = args[5]; } else if (args[4] == TCG_COND_NE) { /* Simplify NE comparisons where one of the pairs can be simplified. */ @@ -1273,15 +1253,13 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, } else { goto do_default; } - args += 6; break; case INDEX_op_setcond2_i32: tmp = do_constant_folding_cond2(&args[1], &args[3], args[5]); if (tmp != 2) { do_setcond_const: - tcg_opt_gen_movi(s, op_index, gen_args, op, args[0], tmp); - gen_args += 2; + tcg_opt_gen_movi(s, op, args, opc, args[0], tmp); } else if ((args[5] == TCG_COND_LT || args[5] == TCG_COND_GE) && temps[args[3]].state == TCG_TEMP_CONST && temps[args[4]].state == TCG_TEMP_CONST @@ -1290,14 +1268,12 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, /* Simplify LT/GE comparisons vs zero to a single compare vs the high word of the input. */ do_setcond_high: - s->gen_opc_buf[op_index] = INDEX_op_setcond_i32; reset_temp(args[0]); temps[args[0]].mask = 1; - gen_args[0] = args[0]; - gen_args[1] = args[2]; - gen_args[2] = args[4]; - gen_args[3] = args[5]; - gen_args += 4; + op->opc = INDEX_op_setcond_i32; + args[1] = args[2]; + args[2] = args[4]; + args[3] = args[5]; } else if (args[5] == TCG_COND_EQ) { /* Simplify EQ comparisons where one of the pairs can be simplified. */ @@ -1318,12 +1294,9 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, do_setcond_low: reset_temp(args[0]); temps[args[0]].mask = 1; - s->gen_opc_buf[op_index] = INDEX_op_setcond_i32; - gen_args[0] = args[0]; - gen_args[1] = args[1]; - gen_args[2] = args[3]; - gen_args[3] = args[5]; - gen_args += 4; + op->opc = INDEX_op_setcond_i32; + args[2] = args[3]; + args[3] = args[5]; } else if (args[5] == TCG_COND_NE) { /* Simplify NE comparisons where one of the pairs can be simplified. */ @@ -1345,7 +1318,6 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, } else { goto do_default; } - args += 6; break; case INDEX_op_call: @@ -1377,22 +1349,12 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, } } } - for (i = 0; i < nb_args; i++) { - gen_args[i] = args[i]; - } - args += nb_args; - gen_args += nb_args; break; } } - - return gen_args; } -TCGArg *tcg_optimize(TCGContext *s, uint16_t *tcg_opc_ptr, - TCGArg *args, TCGOpDef *tcg_op_defs) +void tcg_optimize(TCGContext *s) { - TCGArg *res; - res = tcg_constant_folding(s, tcg_opc_ptr, args, tcg_op_defs); - return res; + tcg_constant_folding(s); } diff --git a/tcg/ppc/tcg-target.c b/tcg/ppc/tcg-target.c index 203027e..36fd314 100644 --- a/tcg/ppc/tcg-target.c +++ b/tcg/ppc/tcg-target.c @@ -1100,24 +1100,22 @@ static void tcg_out_setcond(TCGContext *s, TCGType type, TCGCond cond, } } -static void tcg_out_bc(TCGContext *s, int bc, int label_index) +static void tcg_out_bc(TCGContext *s, int bc, TCGLabel *l) { - TCGLabel *l = &s->labels[label_index]; - if (l->has_value) { tcg_out32(s, bc | reloc_pc14_val(s->code_ptr, l->u.value_ptr)); } else { - tcg_out_reloc(s, s->code_ptr, R_PPC_REL14, label_index, 0); + tcg_out_reloc(s, s->code_ptr, R_PPC_REL14, l, 0); tcg_out_bc_noaddr(s, bc); } } static void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGArg arg1, TCGArg arg2, int const_arg2, - int label_index, TCGType type) + TCGLabel *l, TCGType type) { tcg_out_cmp(s, cond, arg1, arg2, const_arg2, 7, type); - tcg_out_bc(s, tcg_to_bc[cond], label_index); + tcg_out_bc(s, tcg_to_bc[cond], l); } static void tcg_out_movcond(TCGContext *s, TCGType type, TCGCond cond, @@ -1242,7 +1240,7 @@ static void tcg_out_brcond2 (TCGContext *s, const TCGArg *args, const int *const_args) { tcg_out_cmp2(s, args, const_args); - tcg_out_bc(s, BC | BI(7, CR_EQ) | BO_COND_TRUE, args[5]); + tcg_out_bc(s, BC | BI(7, CR_EQ) | BO_COND_TRUE, arg_label(args[5])); } void ppc_tb_set_jmp_target(uintptr_t jmp_addr, uintptr_t addr) @@ -1866,12 +1864,12 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, break; case INDEX_op_br: { - TCGLabel *l = &s->labels[args[0]]; + TCGLabel *l = arg_label(args[0]); if (l->has_value) { tcg_out_b(s, 0, l->u.value_ptr); } else { - tcg_out_reloc(s, s->code_ptr, R_PPC_REL24, args[0], 0); + tcg_out_reloc(s, s->code_ptr, R_PPC_REL24, l, 0); tcg_out_b_noaddr(s, B); } } @@ -2079,11 +2077,11 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, case INDEX_op_brcond_i32: tcg_out_brcond(s, args[2], args[0], args[1], const_args[1], - args[3], TCG_TYPE_I32); + arg_label(args[3]), TCG_TYPE_I32); break; case INDEX_op_brcond_i64: tcg_out_brcond(s, args[2], args[0], args[1], const_args[1], - args[3], TCG_TYPE_I64); + arg_label(args[3]), TCG_TYPE_I64); break; case INDEX_op_brcond2_i32: tcg_out_brcond2(s, args, const_args); diff --git a/tcg/s390/tcg-target.c b/tcg/s390/tcg-target.c index 63e9c82..be984f5 100644 --- a/tcg/s390/tcg-target.c +++ b/tcg/s390/tcg-target.c @@ -1274,26 +1274,24 @@ static void tgen_gotoi(TCGContext *s, int cc, tcg_insn_unit *dest) } } -static void tgen_branch(TCGContext *s, int cc, int labelno) +static void tgen_branch(TCGContext *s, int cc, TCGLabel *l) { - TCGLabel* l = &s->labels[labelno]; if (l->has_value) { tgen_gotoi(s, cc, l->u.value_ptr); } else if (USE_LONG_BRANCHES) { tcg_out16(s, RIL_BRCL | (cc << 4)); - tcg_out_reloc(s, s->code_ptr, R_390_PC32DBL, labelno, -2); + tcg_out_reloc(s, s->code_ptr, R_390_PC32DBL, l, -2); s->code_ptr += 2; } else { tcg_out16(s, RI_BRC | (cc << 4)); - tcg_out_reloc(s, s->code_ptr, R_390_PC16DBL, labelno, -2); + tcg_out_reloc(s, s->code_ptr, R_390_PC16DBL, l, -2); s->code_ptr += 1; } } static void tgen_compare_branch(TCGContext *s, S390Opcode opc, int cc, - TCGReg r1, TCGReg r2, int labelno) + TCGReg r1, TCGReg r2, TCGLabel *l) { - TCGLabel* l = &s->labels[labelno]; intptr_t off; if (l->has_value) { @@ -1301,7 +1299,7 @@ static void tgen_compare_branch(TCGContext *s, S390Opcode opc, int cc, } else { /* We need to keep the offset unchanged for retranslation. */ off = s->code_ptr[1]; - tcg_out_reloc(s, s->code_ptr + 1, R_390_PC16DBL, labelno, -2); + tcg_out_reloc(s, s->code_ptr + 1, R_390_PC16DBL, l, -2); } tcg_out16(s, (opc & 0xff00) | (r1 << 4) | r2); @@ -1310,9 +1308,8 @@ static void tgen_compare_branch(TCGContext *s, S390Opcode opc, int cc, } static void tgen_compare_imm_branch(TCGContext *s, S390Opcode opc, int cc, - TCGReg r1, int i2, int labelno) + TCGReg r1, int i2, TCGLabel *l) { - TCGLabel* l = &s->labels[labelno]; tcg_target_long off; if (l->has_value) { @@ -1320,7 +1317,7 @@ static void tgen_compare_imm_branch(TCGContext *s, S390Opcode opc, int cc, } else { /* We need to keep the offset unchanged for retranslation. */ off = s->code_ptr[1]; - tcg_out_reloc(s, s->code_ptr + 1, R_390_PC16DBL, labelno, -2); + tcg_out_reloc(s, s->code_ptr + 1, R_390_PC16DBL, l, -2); } tcg_out16(s, (opc & 0xff00) | (r1 << 4) | cc); @@ -1329,7 +1326,7 @@ static void tgen_compare_imm_branch(TCGContext *s, S390Opcode opc, int cc, } static void tgen_brcond(TCGContext *s, TCGType type, TCGCond c, - TCGReg r1, TCGArg c2, int c2const, int labelno) + TCGReg r1, TCGArg c2, int c2const, TCGLabel *l) { int cc; @@ -1344,7 +1341,7 @@ static void tgen_brcond(TCGContext *s, TCGType type, TCGCond c, opc = (type == TCG_TYPE_I32 ? (is_unsigned ? RIE_CLRJ : RIE_CRJ) : (is_unsigned ? RIE_CLGRJ : RIE_CGRJ)); - tgen_compare_branch(s, opc, cc, r1, c2, labelno); + tgen_compare_branch(s, opc, cc, r1, c2, l); return; } @@ -1370,13 +1367,13 @@ static void tgen_brcond(TCGContext *s, TCGType type, TCGCond c, } } if (in_range) { - tgen_compare_imm_branch(s, opc, cc, r1, c2, labelno); + tgen_compare_imm_branch(s, opc, cc, r1, c2, l); return; } } cc = tgen_cmp(s, type, c, r1, c2, c2const); - tgen_branch(s, cc, labelno); + tgen_branch(s, cc, l); } static void tcg_out_call(TCGContext *s, tcg_insn_unit *dest) @@ -1904,12 +1901,12 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, break; case INDEX_op_br: - tgen_branch(s, S390_CC_ALWAYS, args[0]); + tgen_branch(s, S390_CC_ALWAYS, arg_label(args[0])); break; case INDEX_op_brcond_i32: tgen_brcond(s, TCG_TYPE_I32, args[2], args[0], - args[1], const_args[1], args[3]); + args[1], const_args[1], arg_label(args[3])); break; case INDEX_op_setcond_i32: tgen_setcond(s, TCG_TYPE_I32, args[3], args[0], args[1], @@ -2126,7 +2123,7 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, case INDEX_op_brcond_i64: tgen_brcond(s, TCG_TYPE_I64, args[2], args[0], - args[1], const_args[1], args[3]); + args[1], const_args[1], arg_label(args[3])); break; case INDEX_op_setcond_i64: tgen_setcond(s, TCG_TYPE_I64, args[3], args[0], args[1], diff --git a/tcg/sparc/tcg-target.c b/tcg/sparc/tcg-target.c index 0c4b028..f9b616f 100644 --- a/tcg/sparc/tcg-target.c +++ b/tcg/sparc/tcg-target.c @@ -569,9 +569,8 @@ static void tcg_out_bpcc0(TCGContext *s, int scond, int flags, int off19) tcg_out32(s, INSN_OP(0) | INSN_OP2(1) | INSN_COND(scond) | flags | off19); } -static void tcg_out_bpcc(TCGContext *s, int scond, int flags, int label) +static void tcg_out_bpcc(TCGContext *s, int scond, int flags, TCGLabel *l) { - TCGLabel *l = &s->labels[label]; int off19; if (l->has_value) { @@ -579,7 +578,7 @@ static void tcg_out_bpcc(TCGContext *s, int scond, int flags, int label) } else { /* Make sure to preserve destinations during retranslation. */ off19 = *s->code_ptr & INSN_OFF19(-1); - tcg_out_reloc(s, s->code_ptr, R_SPARC_WDISP19, label, 0); + tcg_out_reloc(s, s->code_ptr, R_SPARC_WDISP19, l, 0); } tcg_out_bpcc0(s, scond, flags, off19); } @@ -590,10 +589,10 @@ static void tcg_out_cmp(TCGContext *s, TCGReg c1, int32_t c2, int c2const) } static void tcg_out_brcond_i32(TCGContext *s, TCGCond cond, TCGReg arg1, - int32_t arg2, int const_arg2, int label) + int32_t arg2, int const_arg2, TCGLabel *l) { tcg_out_cmp(s, arg1, arg2, const_arg2); - tcg_out_bpcc(s, tcg_cond_to_bcond[cond], BPCC_ICC | BPCC_PT, label); + tcg_out_bpcc(s, tcg_cond_to_bcond[cond], BPCC_ICC | BPCC_PT, l); tcg_out_nop(s); } @@ -614,11 +613,10 @@ static void tcg_out_movcond_i32(TCGContext *s, TCGCond cond, TCGReg ret, } static void tcg_out_brcond_i64(TCGContext *s, TCGCond cond, TCGReg arg1, - int32_t arg2, int const_arg2, int label) + int32_t arg2, int const_arg2, TCGLabel *l) { /* For 64-bit signed comparisons vs zero, we can avoid the compare. */ if (arg2 == 0 && !is_unsigned_cond(cond)) { - TCGLabel *l = &s->labels[label]; int off16; if (l->has_value) { @@ -626,13 +624,13 @@ static void tcg_out_brcond_i64(TCGContext *s, TCGCond cond, TCGReg arg1, } else { /* Make sure to preserve destinations during retranslation. */ off16 = *s->code_ptr & INSN_OFF16(-1); - tcg_out_reloc(s, s->code_ptr, R_SPARC_WDISP16, label, 0); + tcg_out_reloc(s, s->code_ptr, R_SPARC_WDISP16, l, 0); } tcg_out32(s, INSN_OP(0) | INSN_OP2(3) | BPR_PT | INSN_RS1(arg1) | INSN_COND(tcg_cond_to_rcond[cond]) | off16); } else { tcg_out_cmp(s, arg1, arg2, const_arg2); - tcg_out_bpcc(s, tcg_cond_to_bcond[cond], BPCC_XCC | BPCC_PT, label); + tcg_out_bpcc(s, tcg_cond_to_bcond[cond], BPCC_XCC | BPCC_PT, l); } tcg_out_nop(s); } @@ -1243,7 +1241,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, s->tb_next_offset[a0] = tcg_current_code_size(s); break; case INDEX_op_br: - tcg_out_bpcc(s, COND_A, BPCC_PT, a0); + tcg_out_bpcc(s, COND_A, BPCC_PT, arg_label(a0)); tcg_out_nop(s); break; @@ -1329,7 +1327,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, break; case INDEX_op_brcond_i32: - tcg_out_brcond_i32(s, a2, a0, a1, const_args[1], args[3]); + tcg_out_brcond_i32(s, a2, a0, a1, const_args[1], arg_label(args[3])); break; case INDEX_op_setcond_i32: tcg_out_setcond_i32(s, args[3], a0, a1, a2, c2); @@ -1420,7 +1418,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, break; case INDEX_op_brcond_i64: - tcg_out_brcond_i64(s, a2, a0, a1, const_args[1], args[3]); + tcg_out_brcond_i64(s, a2, a0, a1, const_args[1], arg_label(args[3])); break; case INDEX_op_setcond_i64: tcg_out_setcond_i64(s, args[3], a0, a1, a2, c2); diff --git a/tcg/tcg-be-ldst.h b/tcg/tcg-be-ldst.h index 429cba2..4a45102 100644 --- a/tcg/tcg-be-ldst.h +++ b/tcg/tcg-be-ldst.h @@ -21,7 +21,6 @@ */ #ifdef CONFIG_SOFTMMU -#define TCG_MAX_QEMU_LDST 640 typedef struct TCGLabelQemuLdst { bool is_ld; /* qemu_ld: true, qemu_st: false */ @@ -34,11 +33,11 @@ typedef struct TCGLabelQemuLdst { int mem_index; /* soft MMU memory index */ tcg_insn_unit *raddr; /* gen code addr of the next IR of qemu_ld/st IR */ tcg_insn_unit *label_ptr[2]; /* label pointers to be updated */ + struct TCGLabelQemuLdst *next; } TCGLabelQemuLdst; typedef struct TCGBackendData { - int nb_ldst_labels; - TCGLabelQemuLdst ldst_labels[TCG_MAX_QEMU_LDST]; + TCGLabelQemuLdst *labels; } TCGBackendData; @@ -48,7 +47,7 @@ typedef struct TCGBackendData { static inline void tcg_out_tb_init(TCGContext *s) { - s->be->nb_ldst_labels = 0; + s->be->labels = NULL; } /* @@ -60,15 +59,14 @@ static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l); static void tcg_out_tb_finalize(TCGContext *s) { - TCGLabelQemuLdst *lb = s->be->ldst_labels; - int i, n = s->be->nb_ldst_labels; + TCGLabelQemuLdst *lb; /* qemu_ld/st slow paths */ - for (i = 0; i < n; i++) { - if (lb[i].is_ld) { - tcg_out_qemu_ld_slow_path(s, lb + i); + for (lb = s->be->labels; lb != NULL; lb = lb->next) { + if (lb->is_ld) { + tcg_out_qemu_ld_slow_path(s, lb); } else { - tcg_out_qemu_st_slow_path(s, lb + i); + tcg_out_qemu_st_slow_path(s, lb); } } } @@ -80,11 +78,11 @@ static void tcg_out_tb_finalize(TCGContext *s) static inline TCGLabelQemuLdst *new_ldst_label(TCGContext *s) { TCGBackendData *be = s->be; - int n = be->nb_ldst_labels; + TCGLabelQemuLdst *l = tcg_malloc(sizeof(*l)); - assert(n < TCG_MAX_QEMU_LDST); - be->nb_ldst_labels = n + 1; - return &be->ldst_labels[n]; + l->next = be->labels; + be->labels = l; + return l; } #else #include "tcg-be-null.h" diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c new file mode 100644 index 0000000..2b6be75 --- /dev/null +++ b/tcg/tcg-op.c @@ -0,0 +1,1947 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2008 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 "tcg.h" +#include "tcg-op.h" + +/* Reduce the number of ifdefs below. This assumes that all uses of + TCGV_HIGH and TCGV_LOW are properly protected by a conditional that + the compiler can eliminate. */ +#if TCG_TARGET_REG_BITS == 64 +extern TCGv_i32 TCGV_LOW_link_error(TCGv_i64); +extern TCGv_i32 TCGV_HIGH_link_error(TCGv_i64); +#define TCGV_LOW TCGV_LOW_link_error +#define TCGV_HIGH TCGV_HIGH_link_error +#endif + +/* Note that this is optimized for sequential allocation during translate. + Up to and including filling in the forward link immediately. We'll do + proper termination of the end of the list after we finish translation. */ + +static void tcg_emit_op(TCGContext *ctx, TCGOpcode opc, int args) +{ + int oi = ctx->gen_next_op_idx; + int ni = oi + 1; + int pi = oi - 1; + + tcg_debug_assert(oi < OPC_BUF_SIZE); + ctx->gen_last_op_idx = oi; + ctx->gen_next_op_idx = ni; + + ctx->gen_op_buf[oi] = (TCGOp){ + .opc = opc, + .args = args, + .prev = pi, + .next = ni + }; +} + +void tcg_gen_op1(TCGContext *ctx, TCGOpcode opc, TCGArg a1) +{ + int pi = ctx->gen_next_parm_idx; + + tcg_debug_assert(pi + 1 <= OPPARAM_BUF_SIZE); + ctx->gen_next_parm_idx = pi + 1; + ctx->gen_opparam_buf[pi] = a1; + + tcg_emit_op(ctx, opc, pi); +} + +void tcg_gen_op2(TCGContext *ctx, TCGOpcode opc, TCGArg a1, TCGArg a2) +{ + int pi = ctx->gen_next_parm_idx; + + tcg_debug_assert(pi + 2 <= OPPARAM_BUF_SIZE); + ctx->gen_next_parm_idx = pi + 2; + ctx->gen_opparam_buf[pi + 0] = a1; + ctx->gen_opparam_buf[pi + 1] = a2; + + tcg_emit_op(ctx, opc, pi); +} + +void tcg_gen_op3(TCGContext *ctx, TCGOpcode opc, TCGArg a1, + TCGArg a2, TCGArg a3) +{ + int pi = ctx->gen_next_parm_idx; + + tcg_debug_assert(pi + 3 <= OPPARAM_BUF_SIZE); + ctx->gen_next_parm_idx = pi + 3; + ctx->gen_opparam_buf[pi + 0] = a1; + ctx->gen_opparam_buf[pi + 1] = a2; + ctx->gen_opparam_buf[pi + 2] = a3; + + tcg_emit_op(ctx, opc, pi); +} + +void tcg_gen_op4(TCGContext *ctx, TCGOpcode opc, TCGArg a1, + TCGArg a2, TCGArg a3, TCGArg a4) +{ + int pi = ctx->gen_next_parm_idx; + + tcg_debug_assert(pi + 4 <= OPPARAM_BUF_SIZE); + ctx->gen_next_parm_idx = pi + 4; + ctx->gen_opparam_buf[pi + 0] = a1; + ctx->gen_opparam_buf[pi + 1] = a2; + ctx->gen_opparam_buf[pi + 2] = a3; + ctx->gen_opparam_buf[pi + 3] = a4; + + tcg_emit_op(ctx, opc, pi); +} + +void tcg_gen_op5(TCGContext *ctx, TCGOpcode opc, TCGArg a1, + TCGArg a2, TCGArg a3, TCGArg a4, TCGArg a5) +{ + int pi = ctx->gen_next_parm_idx; + + tcg_debug_assert(pi + 5 <= OPPARAM_BUF_SIZE); + ctx->gen_next_parm_idx = pi + 5; + ctx->gen_opparam_buf[pi + 0] = a1; + ctx->gen_opparam_buf[pi + 1] = a2; + ctx->gen_opparam_buf[pi + 2] = a3; + ctx->gen_opparam_buf[pi + 3] = a4; + ctx->gen_opparam_buf[pi + 4] = a5; + + tcg_emit_op(ctx, opc, pi); +} + +void tcg_gen_op6(TCGContext *ctx, TCGOpcode opc, TCGArg a1, TCGArg a2, + TCGArg a3, TCGArg a4, TCGArg a5, TCGArg a6) +{ + int pi = ctx->gen_next_parm_idx; + + tcg_debug_assert(pi + 6 <= OPPARAM_BUF_SIZE); + ctx->gen_next_parm_idx = pi + 6; + ctx->gen_opparam_buf[pi + 0] = a1; + ctx->gen_opparam_buf[pi + 1] = a2; + ctx->gen_opparam_buf[pi + 2] = a3; + ctx->gen_opparam_buf[pi + 3] = a4; + ctx->gen_opparam_buf[pi + 4] = a5; + ctx->gen_opparam_buf[pi + 5] = a6; + + tcg_emit_op(ctx, opc, pi); +} + +/* 32 bit ops */ + +void tcg_gen_addi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) +{ + /* some cases can be optimized here */ + if (arg2 == 0) { + tcg_gen_mov_i32(ret, arg1); + } else { + TCGv_i32 t0 = tcg_const_i32(arg2); + tcg_gen_add_i32(ret, arg1, t0); + tcg_temp_free_i32(t0); + } +} + +void tcg_gen_subfi_i32(TCGv_i32 ret, int32_t arg1, TCGv_i32 arg2) +{ + if (arg1 == 0 && TCG_TARGET_HAS_neg_i32) { + /* Don't recurse with tcg_gen_neg_i32. */ + tcg_gen_op2_i32(INDEX_op_neg_i32, ret, arg2); + } else { + TCGv_i32 t0 = tcg_const_i32(arg1); + tcg_gen_sub_i32(ret, t0, arg2); + tcg_temp_free_i32(t0); + } +} + +void tcg_gen_subi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) +{ + /* some cases can be optimized here */ + if (arg2 == 0) { + tcg_gen_mov_i32(ret, arg1); + } else { + TCGv_i32 t0 = tcg_const_i32(arg2); + tcg_gen_sub_i32(ret, arg1, t0); + tcg_temp_free_i32(t0); + } +} + +void tcg_gen_andi_i32(TCGv_i32 ret, TCGv_i32 arg1, uint32_t arg2) +{ + TCGv_i32 t0; + /* Some cases can be optimized here. */ + switch (arg2) { + case 0: + tcg_gen_movi_i32(ret, 0); + return; + case 0xffffffffu: + tcg_gen_mov_i32(ret, arg1); + return; + case 0xffu: + /* Don't recurse with tcg_gen_ext8u_i32. */ + if (TCG_TARGET_HAS_ext8u_i32) { + tcg_gen_op2_i32(INDEX_op_ext8u_i32, ret, arg1); + return; + } + break; + case 0xffffu: + if (TCG_TARGET_HAS_ext16u_i32) { + tcg_gen_op2_i32(INDEX_op_ext16u_i32, ret, arg1); + return; + } + break; + } + t0 = tcg_const_i32(arg2); + tcg_gen_and_i32(ret, arg1, t0); + tcg_temp_free_i32(t0); +} + +void tcg_gen_ori_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) +{ + /* Some cases can be optimized here. */ + if (arg2 == -1) { + tcg_gen_movi_i32(ret, -1); + } else if (arg2 == 0) { + tcg_gen_mov_i32(ret, arg1); + } else { + TCGv_i32 t0 = tcg_const_i32(arg2); + tcg_gen_or_i32(ret, arg1, t0); + tcg_temp_free_i32(t0); + } +} + +void tcg_gen_xori_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) +{ + /* Some cases can be optimized here. */ + if (arg2 == 0) { + tcg_gen_mov_i32(ret, arg1); + } else if (arg2 == -1 && TCG_TARGET_HAS_not_i32) { + /* Don't recurse with tcg_gen_not_i32. */ + tcg_gen_op2_i32(INDEX_op_not_i32, ret, arg1); + } else { + TCGv_i32 t0 = tcg_const_i32(arg2); + tcg_gen_xor_i32(ret, arg1, t0); + tcg_temp_free_i32(t0); + } +} + +void tcg_gen_shli_i32(TCGv_i32 ret, TCGv_i32 arg1, unsigned arg2) +{ + tcg_debug_assert(arg2 < 32); + if (arg2 == 0) { + tcg_gen_mov_i32(ret, arg1); + } else { + TCGv_i32 t0 = tcg_const_i32(arg2); + tcg_gen_shl_i32(ret, arg1, t0); + tcg_temp_free_i32(t0); + } +} + +void tcg_gen_shri_i32(TCGv_i32 ret, TCGv_i32 arg1, unsigned arg2) +{ + tcg_debug_assert(arg2 < 32); + if (arg2 == 0) { + tcg_gen_mov_i32(ret, arg1); + } else { + TCGv_i32 t0 = tcg_const_i32(arg2); + tcg_gen_shr_i32(ret, arg1, t0); + tcg_temp_free_i32(t0); + } +} + +void tcg_gen_sari_i32(TCGv_i32 ret, TCGv_i32 arg1, unsigned arg2) +{ + tcg_debug_assert(arg2 < 32); + if (arg2 == 0) { + tcg_gen_mov_i32(ret, arg1); + } else { + TCGv_i32 t0 = tcg_const_i32(arg2); + tcg_gen_sar_i32(ret, arg1, t0); + tcg_temp_free_i32(t0); + } +} + +void tcg_gen_brcond_i32(TCGCond cond, TCGv_i32 arg1, TCGv_i32 arg2, TCGLabel *l) +{ + if (cond == TCG_COND_ALWAYS) { + tcg_gen_br(l); + } else if (cond != TCG_COND_NEVER) { + tcg_gen_op4ii_i32(INDEX_op_brcond_i32, arg1, arg2, cond, label_arg(l)); + } +} + +void tcg_gen_brcondi_i32(TCGCond cond, TCGv_i32 arg1, int32_t arg2, TCGLabel *l) +{ + if (cond == TCG_COND_ALWAYS) { + tcg_gen_br(l); + } else if (cond != TCG_COND_NEVER) { + TCGv_i32 t0 = tcg_const_i32(arg2); + tcg_gen_brcond_i32(cond, arg1, t0, l); + tcg_temp_free_i32(t0); + } +} + +void tcg_gen_setcond_i32(TCGCond cond, TCGv_i32 ret, + TCGv_i32 arg1, TCGv_i32 arg2) +{ + if (cond == TCG_COND_ALWAYS) { + tcg_gen_movi_i32(ret, 1); + } else if (cond == TCG_COND_NEVER) { + tcg_gen_movi_i32(ret, 0); + } else { + tcg_gen_op4i_i32(INDEX_op_setcond_i32, ret, arg1, arg2, cond); + } +} + +void tcg_gen_setcondi_i32(TCGCond cond, TCGv_i32 ret, + TCGv_i32 arg1, int32_t arg2) +{ + TCGv_i32 t0 = tcg_const_i32(arg2); + tcg_gen_setcond_i32(cond, ret, arg1, t0); + tcg_temp_free_i32(t0); +} + +void tcg_gen_muli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) +{ + TCGv_i32 t0 = tcg_const_i32(arg2); + tcg_gen_mul_i32(ret, arg1, t0); + tcg_temp_free_i32(t0); +} + +void tcg_gen_div_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + if (TCG_TARGET_HAS_div_i32) { + tcg_gen_op3_i32(INDEX_op_div_i32, ret, arg1, arg2); + } else if (TCG_TARGET_HAS_div2_i32) { + TCGv_i32 t0 = tcg_temp_new_i32(); + tcg_gen_sari_i32(t0, arg1, 31); + tcg_gen_op5_i32(INDEX_op_div2_i32, ret, t0, arg1, t0, arg2); + tcg_temp_free_i32(t0); + } else { + gen_helper_div_i32(ret, arg1, arg2); + } +} + +void tcg_gen_rem_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + if (TCG_TARGET_HAS_rem_i32) { + tcg_gen_op3_i32(INDEX_op_rem_i32, ret, arg1, arg2); + } else if (TCG_TARGET_HAS_div_i32) { + TCGv_i32 t0 = tcg_temp_new_i32(); + tcg_gen_op3_i32(INDEX_op_div_i32, t0, arg1, arg2); + tcg_gen_mul_i32(t0, t0, arg2); + tcg_gen_sub_i32(ret, arg1, t0); + tcg_temp_free_i32(t0); + } else if (TCG_TARGET_HAS_div2_i32) { + TCGv_i32 t0 = tcg_temp_new_i32(); + tcg_gen_sari_i32(t0, arg1, 31); + tcg_gen_op5_i32(INDEX_op_div2_i32, t0, ret, arg1, t0, arg2); + tcg_temp_free_i32(t0); + } else { + gen_helper_rem_i32(ret, arg1, arg2); + } +} + +void tcg_gen_divu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + if (TCG_TARGET_HAS_div_i32) { + tcg_gen_op3_i32(INDEX_op_divu_i32, ret, arg1, arg2); + } else if (TCG_TARGET_HAS_div2_i32) { + TCGv_i32 t0 = tcg_temp_new_i32(); + tcg_gen_movi_i32(t0, 0); + tcg_gen_op5_i32(INDEX_op_divu2_i32, ret, t0, arg1, t0, arg2); + tcg_temp_free_i32(t0); + } else { + gen_helper_divu_i32(ret, arg1, arg2); + } +} + +void tcg_gen_remu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + if (TCG_TARGET_HAS_rem_i32) { + tcg_gen_op3_i32(INDEX_op_remu_i32, ret, arg1, arg2); + } else if (TCG_TARGET_HAS_div_i32) { + TCGv_i32 t0 = tcg_temp_new_i32(); + tcg_gen_op3_i32(INDEX_op_divu_i32, t0, arg1, arg2); + tcg_gen_mul_i32(t0, t0, arg2); + tcg_gen_sub_i32(ret, arg1, t0); + tcg_temp_free_i32(t0); + } else if (TCG_TARGET_HAS_div2_i32) { + TCGv_i32 t0 = tcg_temp_new_i32(); + tcg_gen_movi_i32(t0, 0); + tcg_gen_op5_i32(INDEX_op_divu2_i32, t0, ret, arg1, t0, arg2); + tcg_temp_free_i32(t0); + } else { + gen_helper_remu_i32(ret, arg1, arg2); + } +} + +void tcg_gen_andc_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + if (TCG_TARGET_HAS_andc_i32) { + tcg_gen_op3_i32(INDEX_op_andc_i32, ret, arg1, arg2); + } else { + TCGv_i32 t0 = tcg_temp_new_i32(); + tcg_gen_not_i32(t0, arg2); + tcg_gen_and_i32(ret, arg1, t0); + tcg_temp_free_i32(t0); + } +} + +void tcg_gen_eqv_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + if (TCG_TARGET_HAS_eqv_i32) { + tcg_gen_op3_i32(INDEX_op_eqv_i32, ret, arg1, arg2); + } else { + tcg_gen_xor_i32(ret, arg1, arg2); + tcg_gen_not_i32(ret, ret); + } +} + +void tcg_gen_nand_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + if (TCG_TARGET_HAS_nand_i32) { + tcg_gen_op3_i32(INDEX_op_nand_i32, ret, arg1, arg2); + } else { + tcg_gen_and_i32(ret, arg1, arg2); + tcg_gen_not_i32(ret, ret); + } +} + +void tcg_gen_nor_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + if (TCG_TARGET_HAS_nor_i32) { + tcg_gen_op3_i32(INDEX_op_nor_i32, ret, arg1, arg2); + } else { + tcg_gen_or_i32(ret, arg1, arg2); + tcg_gen_not_i32(ret, ret); + } +} + +void tcg_gen_orc_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + if (TCG_TARGET_HAS_orc_i32) { + tcg_gen_op3_i32(INDEX_op_orc_i32, ret, arg1, arg2); + } else { + TCGv_i32 t0 = tcg_temp_new_i32(); + tcg_gen_not_i32(t0, arg2); + tcg_gen_or_i32(ret, arg1, t0); + tcg_temp_free_i32(t0); + } +} + +void tcg_gen_rotl_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + if (TCG_TARGET_HAS_rot_i32) { + tcg_gen_op3_i32(INDEX_op_rotl_i32, ret, arg1, arg2); + } else { + TCGv_i32 t0, t1; + + t0 = tcg_temp_new_i32(); + t1 = tcg_temp_new_i32(); + tcg_gen_shl_i32(t0, arg1, arg2); + tcg_gen_subfi_i32(t1, 32, arg2); + tcg_gen_shr_i32(t1, arg1, t1); + tcg_gen_or_i32(ret, t0, t1); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); + } +} + +void tcg_gen_rotli_i32(TCGv_i32 ret, TCGv_i32 arg1, unsigned arg2) +{ + tcg_debug_assert(arg2 < 32); + /* some cases can be optimized here */ + if (arg2 == 0) { + tcg_gen_mov_i32(ret, arg1); + } else if (TCG_TARGET_HAS_rot_i32) { + TCGv_i32 t0 = tcg_const_i32(arg2); + tcg_gen_rotl_i32(ret, arg1, t0); + tcg_temp_free_i32(t0); + } else { + TCGv_i32 t0, t1; + t0 = tcg_temp_new_i32(); + t1 = tcg_temp_new_i32(); + tcg_gen_shli_i32(t0, arg1, arg2); + tcg_gen_shri_i32(t1, arg1, 32 - arg2); + tcg_gen_or_i32(ret, t0, t1); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); + } +} + +void tcg_gen_rotr_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + if (TCG_TARGET_HAS_rot_i32) { + tcg_gen_op3_i32(INDEX_op_rotr_i32, ret, arg1, arg2); + } else { + TCGv_i32 t0, t1; + + t0 = tcg_temp_new_i32(); + t1 = tcg_temp_new_i32(); + tcg_gen_shr_i32(t0, arg1, arg2); + tcg_gen_subfi_i32(t1, 32, arg2); + tcg_gen_shl_i32(t1, arg1, t1); + tcg_gen_or_i32(ret, t0, t1); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); + } +} + +void tcg_gen_rotri_i32(TCGv_i32 ret, TCGv_i32 arg1, unsigned arg2) +{ + tcg_debug_assert(arg2 < 32); + /* some cases can be optimized here */ + if (arg2 == 0) { + tcg_gen_mov_i32(ret, arg1); + } else { + tcg_gen_rotli_i32(ret, arg1, 32 - arg2); + } +} + +void tcg_gen_deposit_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2, + unsigned int ofs, unsigned int len) +{ + uint32_t mask; + TCGv_i32 t1; + + tcg_debug_assert(ofs < 32); + tcg_debug_assert(len <= 32); + tcg_debug_assert(ofs + len <= 32); + + if (ofs == 0 && len == 32) { + tcg_gen_mov_i32(ret, arg2); + return; + } + if (TCG_TARGET_HAS_deposit_i32 && TCG_TARGET_deposit_i32_valid(ofs, len)) { + tcg_gen_op5ii_i32(INDEX_op_deposit_i32, ret, arg1, arg2, ofs, len); + return; + } + + mask = (1u << len) - 1; + t1 = tcg_temp_new_i32(); + + if (ofs + len < 32) { + tcg_gen_andi_i32(t1, arg2, mask); + tcg_gen_shli_i32(t1, t1, ofs); + } else { + tcg_gen_shli_i32(t1, arg2, ofs); + } + tcg_gen_andi_i32(ret, arg1, ~(mask << ofs)); + tcg_gen_or_i32(ret, ret, t1); + + tcg_temp_free_i32(t1); +} + +void tcg_gen_movcond_i32(TCGCond cond, TCGv_i32 ret, TCGv_i32 c1, + TCGv_i32 c2, TCGv_i32 v1, TCGv_i32 v2) +{ + if (cond == TCG_COND_ALWAYS) { + tcg_gen_mov_i32(ret, v1); + } else if (cond == TCG_COND_NEVER) { + tcg_gen_mov_i32(ret, v2); + } else if (TCG_TARGET_HAS_movcond_i32) { + tcg_gen_op6i_i32(INDEX_op_movcond_i32, ret, c1, c2, v1, v2, cond); + } else { + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + tcg_gen_setcond_i32(cond, t0, c1, c2); + tcg_gen_neg_i32(t0, t0); + tcg_gen_and_i32(t1, v1, t0); + tcg_gen_andc_i32(ret, v2, t0); + tcg_gen_or_i32(ret, ret, t1); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); + } +} + +void tcg_gen_add2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 al, + TCGv_i32 ah, TCGv_i32 bl, TCGv_i32 bh) +{ + if (TCG_TARGET_HAS_add2_i32) { + tcg_gen_op6_i32(INDEX_op_add2_i32, rl, rh, al, ah, bl, bh); + } else { + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + tcg_gen_concat_i32_i64(t0, al, ah); + tcg_gen_concat_i32_i64(t1, bl, bh); + tcg_gen_add_i64(t0, t0, t1); + tcg_gen_extr_i64_i32(rl, rh, t0); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + } +} + +void tcg_gen_sub2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 al, + TCGv_i32 ah, TCGv_i32 bl, TCGv_i32 bh) +{ + if (TCG_TARGET_HAS_sub2_i32) { + tcg_gen_op6_i32(INDEX_op_sub2_i32, rl, rh, al, ah, bl, bh); + } else { + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + tcg_gen_concat_i32_i64(t0, al, ah); + tcg_gen_concat_i32_i64(t1, bl, bh); + tcg_gen_sub_i64(t0, t0, t1); + tcg_gen_extr_i64_i32(rl, rh, t0); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + } +} + +void tcg_gen_mulu2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2) +{ + if (TCG_TARGET_HAS_mulu2_i32) { + tcg_gen_op4_i32(INDEX_op_mulu2_i32, rl, rh, arg1, arg2); + } else if (TCG_TARGET_HAS_muluh_i32) { + TCGv_i32 t = tcg_temp_new_i32(); + tcg_gen_op3_i32(INDEX_op_mul_i32, t, arg1, arg2); + tcg_gen_op3_i32(INDEX_op_muluh_i32, rh, arg1, arg2); + tcg_gen_mov_i32(rl, t); + tcg_temp_free_i32(t); + } else { + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + tcg_gen_extu_i32_i64(t0, arg1); + tcg_gen_extu_i32_i64(t1, arg2); + tcg_gen_mul_i64(t0, t0, t1); + tcg_gen_extr_i64_i32(rl, rh, t0); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + } +} + +void tcg_gen_muls2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2) +{ + if (TCG_TARGET_HAS_muls2_i32) { + tcg_gen_op4_i32(INDEX_op_muls2_i32, rl, rh, arg1, arg2); + } else if (TCG_TARGET_HAS_mulsh_i32) { + TCGv_i32 t = tcg_temp_new_i32(); + tcg_gen_op3_i32(INDEX_op_mul_i32, t, arg1, arg2); + tcg_gen_op3_i32(INDEX_op_mulsh_i32, rh, arg1, arg2); + tcg_gen_mov_i32(rl, t); + tcg_temp_free_i32(t); + } else if (TCG_TARGET_REG_BITS == 32) { + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + TCGv_i32 t2 = tcg_temp_new_i32(); + TCGv_i32 t3 = tcg_temp_new_i32(); + tcg_gen_mulu2_i32(t0, t1, arg1, arg2); + /* Adjust for negative inputs. */ + tcg_gen_sari_i32(t2, arg1, 31); + tcg_gen_sari_i32(t3, arg2, 31); + tcg_gen_and_i32(t2, t2, arg2); + tcg_gen_and_i32(t3, t3, arg1); + tcg_gen_sub_i32(rh, t1, t2); + tcg_gen_sub_i32(rh, rh, t3); + tcg_gen_mov_i32(rl, t0); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t3); + } else { + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + tcg_gen_ext_i32_i64(t0, arg1); + tcg_gen_ext_i32_i64(t1, arg2); + tcg_gen_mul_i64(t0, t0, t1); + tcg_gen_extr_i64_i32(rl, rh, t0); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + } +} + +void tcg_gen_ext8s_i32(TCGv_i32 ret, TCGv_i32 arg) +{ + if (TCG_TARGET_HAS_ext8s_i32) { + tcg_gen_op2_i32(INDEX_op_ext8s_i32, ret, arg); + } else { + tcg_gen_shli_i32(ret, arg, 24); + tcg_gen_sari_i32(ret, ret, 24); + } +} + +void tcg_gen_ext16s_i32(TCGv_i32 ret, TCGv_i32 arg) +{ + if (TCG_TARGET_HAS_ext16s_i32) { + tcg_gen_op2_i32(INDEX_op_ext16s_i32, ret, arg); + } else { + tcg_gen_shli_i32(ret, arg, 16); + tcg_gen_sari_i32(ret, ret, 16); + } +} + +void tcg_gen_ext8u_i32(TCGv_i32 ret, TCGv_i32 arg) +{ + if (TCG_TARGET_HAS_ext8u_i32) { + tcg_gen_op2_i32(INDEX_op_ext8u_i32, ret, arg); + } else { + tcg_gen_andi_i32(ret, arg, 0xffu); + } +} + +void tcg_gen_ext16u_i32(TCGv_i32 ret, TCGv_i32 arg) +{ + if (TCG_TARGET_HAS_ext16u_i32) { + tcg_gen_op2_i32(INDEX_op_ext16u_i32, ret, arg); + } else { + tcg_gen_andi_i32(ret, arg, 0xffffu); + } +} + +/* Note: we assume the two high bytes are set to zero */ +void tcg_gen_bswap16_i32(TCGv_i32 ret, TCGv_i32 arg) +{ + if (TCG_TARGET_HAS_bswap16_i32) { + tcg_gen_op2_i32(INDEX_op_bswap16_i32, ret, arg); + } else { + TCGv_i32 t0 = tcg_temp_new_i32(); + + tcg_gen_ext8u_i32(t0, arg); + tcg_gen_shli_i32(t0, t0, 8); + tcg_gen_shri_i32(ret, arg, 8); + tcg_gen_or_i32(ret, ret, t0); + tcg_temp_free_i32(t0); + } +} + +void tcg_gen_bswap32_i32(TCGv_i32 ret, TCGv_i32 arg) +{ + if (TCG_TARGET_HAS_bswap32_i32) { + tcg_gen_op2_i32(INDEX_op_bswap32_i32, ret, arg); + } else { + TCGv_i32 t0, t1; + t0 = tcg_temp_new_i32(); + t1 = tcg_temp_new_i32(); + + tcg_gen_shli_i32(t0, arg, 24); + + tcg_gen_andi_i32(t1, arg, 0x0000ff00); + tcg_gen_shli_i32(t1, t1, 8); + tcg_gen_or_i32(t0, t0, t1); + + tcg_gen_shri_i32(t1, arg, 8); + tcg_gen_andi_i32(t1, t1, 0x0000ff00); + tcg_gen_or_i32(t0, t0, t1); + + tcg_gen_shri_i32(t1, arg, 24); + tcg_gen_or_i32(ret, t0, t1); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); + } +} + +/* 64-bit ops */ + +#if TCG_TARGET_REG_BITS == 32 +/* These are all inline for TCG_TARGET_REG_BITS == 64. */ + +void tcg_gen_discard_i64(TCGv_i64 arg) +{ + tcg_gen_discard_i32(TCGV_LOW(arg)); + tcg_gen_discard_i32(TCGV_HIGH(arg)); +} + +void tcg_gen_mov_i64(TCGv_i64 ret, TCGv_i64 arg) +{ + tcg_gen_mov_i32(TCGV_LOW(ret), TCGV_LOW(arg)); + tcg_gen_mov_i32(TCGV_HIGH(ret), TCGV_HIGH(arg)); +} + +void tcg_gen_movi_i64(TCGv_i64 ret, int64_t arg) +{ + tcg_gen_movi_i32(TCGV_LOW(ret), arg); + tcg_gen_movi_i32(TCGV_HIGH(ret), arg >> 32); +} + +void tcg_gen_ld8u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) +{ + tcg_gen_ld8u_i32(TCGV_LOW(ret), arg2, offset); + tcg_gen_movi_i32(TCGV_HIGH(ret), 0); +} + +void tcg_gen_ld8s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) +{ + tcg_gen_ld8s_i32(TCGV_LOW(ret), arg2, offset); + tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_HIGH(ret), 31); +} + +void tcg_gen_ld16u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) +{ + tcg_gen_ld16u_i32(TCGV_LOW(ret), arg2, offset); + tcg_gen_movi_i32(TCGV_HIGH(ret), 0); +} + +void tcg_gen_ld16s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) +{ + tcg_gen_ld16s_i32(TCGV_LOW(ret), arg2, offset); + tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); +} + +void tcg_gen_ld32u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) +{ + tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset); + tcg_gen_movi_i32(TCGV_HIGH(ret), 0); +} + +void tcg_gen_ld32s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) +{ + tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset); + tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); +} + +void tcg_gen_ld_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) +{ + /* Since arg2 and ret have different types, + they cannot be the same temporary */ +#ifdef HOST_WORDS_BIGENDIAN + tcg_gen_ld_i32(TCGV_HIGH(ret), arg2, offset); + tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset + 4); +#else + tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset); + tcg_gen_ld_i32(TCGV_HIGH(ret), arg2, offset + 4); +#endif +} + +void tcg_gen_st_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset) +{ +#ifdef HOST_WORDS_BIGENDIAN + tcg_gen_st_i32(TCGV_HIGH(arg1), arg2, offset); + tcg_gen_st_i32(TCGV_LOW(arg1), arg2, offset + 4); +#else + tcg_gen_st_i32(TCGV_LOW(arg1), arg2, offset); + tcg_gen_st_i32(TCGV_HIGH(arg1), arg2, offset + 4); +#endif +} + +void tcg_gen_and_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + tcg_gen_and_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); + tcg_gen_and_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); +} + +void tcg_gen_or_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + tcg_gen_or_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); + tcg_gen_or_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); +} + +void tcg_gen_xor_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + tcg_gen_xor_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); + tcg_gen_xor_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); +} + +void tcg_gen_shl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + gen_helper_shl_i64(ret, arg1, arg2); +} + +void tcg_gen_shr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + gen_helper_shr_i64(ret, arg1, arg2); +} + +void tcg_gen_sar_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + gen_helper_sar_i64(ret, arg1, arg2); +} + +void tcg_gen_mul_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + TCGv_i64 t0; + TCGv_i32 t1; + + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i32(); + + tcg_gen_mulu2_i32(TCGV_LOW(t0), TCGV_HIGH(t0), + TCGV_LOW(arg1), TCGV_LOW(arg2)); + + tcg_gen_mul_i32(t1, TCGV_LOW(arg1), TCGV_HIGH(arg2)); + tcg_gen_add_i32(TCGV_HIGH(t0), TCGV_HIGH(t0), t1); + tcg_gen_mul_i32(t1, TCGV_HIGH(arg1), TCGV_LOW(arg2)); + tcg_gen_add_i32(TCGV_HIGH(t0), TCGV_HIGH(t0), t1); + + tcg_gen_mov_i64(ret, t0); + tcg_temp_free_i64(t0); + tcg_temp_free_i32(t1); +} +#endif /* TCG_TARGET_REG_SIZE == 32 */ + +void tcg_gen_addi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) +{ + /* some cases can be optimized here */ + if (arg2 == 0) { + tcg_gen_mov_i64(ret, arg1); + } else { + TCGv_i64 t0 = tcg_const_i64(arg2); + tcg_gen_add_i64(ret, arg1, t0); + tcg_temp_free_i64(t0); + } +} + +void tcg_gen_subfi_i64(TCGv_i64 ret, int64_t arg1, TCGv_i64 arg2) +{ + if (arg1 == 0 && TCG_TARGET_HAS_neg_i64) { + /* Don't recurse with tcg_gen_neg_i64. */ + tcg_gen_op2_i64(INDEX_op_neg_i64, ret, arg2); + } else { + TCGv_i64 t0 = tcg_const_i64(arg1); + tcg_gen_sub_i64(ret, t0, arg2); + tcg_temp_free_i64(t0); + } +} + +void tcg_gen_subi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) +{ + /* some cases can be optimized here */ + if (arg2 == 0) { + tcg_gen_mov_i64(ret, arg1); + } else { + TCGv_i64 t0 = tcg_const_i64(arg2); + tcg_gen_sub_i64(ret, arg1, t0); + tcg_temp_free_i64(t0); + } +} + +void tcg_gen_andi_i64(TCGv_i64 ret, TCGv_i64 arg1, uint64_t arg2) +{ + TCGv_i64 t0; + + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_andi_i32(TCGV_LOW(ret), TCGV_LOW(arg1), arg2); + tcg_gen_andi_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), arg2 >> 32); + return; + } + + /* Some cases can be optimized here. */ + switch (arg2) { + case 0: + tcg_gen_movi_i64(ret, 0); + return; + case 0xffffffffffffffffull: + tcg_gen_mov_i64(ret, arg1); + return; + case 0xffull: + /* Don't recurse with tcg_gen_ext8u_i64. */ + if (TCG_TARGET_HAS_ext8u_i64) { + tcg_gen_op2_i64(INDEX_op_ext8u_i64, ret, arg1); + return; + } + break; + case 0xffffu: + if (TCG_TARGET_HAS_ext16u_i64) { + tcg_gen_op2_i64(INDEX_op_ext16u_i64, ret, arg1); + return; + } + break; + case 0xffffffffull: + if (TCG_TARGET_HAS_ext32u_i64) { + tcg_gen_op2_i64(INDEX_op_ext32u_i64, ret, arg1); + return; + } + break; + } + t0 = tcg_const_i64(arg2); + tcg_gen_and_i64(ret, arg1, t0); + tcg_temp_free_i64(t0); +} + +void tcg_gen_ori_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) +{ + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_ori_i32(TCGV_LOW(ret), TCGV_LOW(arg1), arg2); + tcg_gen_ori_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), arg2 >> 32); + return; + } + /* Some cases can be optimized here. */ + if (arg2 == -1) { + tcg_gen_movi_i64(ret, -1); + } else if (arg2 == 0) { + tcg_gen_mov_i64(ret, arg1); + } else { + TCGv_i64 t0 = tcg_const_i64(arg2); + tcg_gen_or_i64(ret, arg1, t0); + tcg_temp_free_i64(t0); + } +} + +void tcg_gen_xori_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) +{ + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_xori_i32(TCGV_LOW(ret), TCGV_LOW(arg1), arg2); + tcg_gen_xori_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), arg2 >> 32); + return; + } + /* Some cases can be optimized here. */ + if (arg2 == 0) { + tcg_gen_mov_i64(ret, arg1); + } else if (arg2 == -1 && TCG_TARGET_HAS_not_i64) { + /* Don't recurse with tcg_gen_not_i64. */ + tcg_gen_op2_i64(INDEX_op_not_i64, ret, arg1); + } else { + TCGv_i64 t0 = tcg_const_i64(arg2); + tcg_gen_xor_i64(ret, arg1, t0); + tcg_temp_free_i64(t0); + } +} + +static inline void tcg_gen_shifti_i64(TCGv_i64 ret, TCGv_i64 arg1, + unsigned c, bool right, bool arith) +{ + tcg_debug_assert(c < 64); + if (c == 0) { + tcg_gen_mov_i32(TCGV_LOW(ret), TCGV_LOW(arg1)); + tcg_gen_mov_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1)); + } else if (c >= 32) { + c -= 32; + if (right) { + if (arith) { + tcg_gen_sari_i32(TCGV_LOW(ret), TCGV_HIGH(arg1), c); + tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), 31); + } else { + tcg_gen_shri_i32(TCGV_LOW(ret), TCGV_HIGH(arg1), c); + tcg_gen_movi_i32(TCGV_HIGH(ret), 0); + } + } else { + tcg_gen_shli_i32(TCGV_HIGH(ret), TCGV_LOW(arg1), c); + tcg_gen_movi_i32(TCGV_LOW(ret), 0); + } + } else { + TCGv_i32 t0, t1; + + t0 = tcg_temp_new_i32(); + t1 = tcg_temp_new_i32(); + if (right) { + tcg_gen_shli_i32(t0, TCGV_HIGH(arg1), 32 - c); + if (arith) { + tcg_gen_sari_i32(t1, TCGV_HIGH(arg1), c); + } else { + tcg_gen_shri_i32(t1, TCGV_HIGH(arg1), c); + } + tcg_gen_shri_i32(TCGV_LOW(ret), TCGV_LOW(arg1), c); + tcg_gen_or_i32(TCGV_LOW(ret), TCGV_LOW(ret), t0); + tcg_gen_mov_i32(TCGV_HIGH(ret), t1); + } else { + tcg_gen_shri_i32(t0, TCGV_LOW(arg1), 32 - c); + /* Note: ret can be the same as arg1, so we use t1 */ + tcg_gen_shli_i32(t1, TCGV_LOW(arg1), c); + tcg_gen_shli_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), c); + tcg_gen_or_i32(TCGV_HIGH(ret), TCGV_HIGH(ret), t0); + tcg_gen_mov_i32(TCGV_LOW(ret), t1); + } + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); + } +} + +void tcg_gen_shli_i64(TCGv_i64 ret, TCGv_i64 arg1, unsigned arg2) +{ + tcg_debug_assert(arg2 < 64); + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_shifti_i64(ret, arg1, arg2, 0, 0); + } else if (arg2 == 0) { + tcg_gen_mov_i64(ret, arg1); + } else { + TCGv_i64 t0 = tcg_const_i64(arg2); + tcg_gen_shl_i64(ret, arg1, t0); + tcg_temp_free_i64(t0); + } +} + +void tcg_gen_shri_i64(TCGv_i64 ret, TCGv_i64 arg1, unsigned arg2) +{ + tcg_debug_assert(arg2 < 64); + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_shifti_i64(ret, arg1, arg2, 1, 0); + } else if (arg2 == 0) { + tcg_gen_mov_i64(ret, arg1); + } else { + TCGv_i64 t0 = tcg_const_i64(arg2); + tcg_gen_shr_i64(ret, arg1, t0); + tcg_temp_free_i64(t0); + } +} + +void tcg_gen_sari_i64(TCGv_i64 ret, TCGv_i64 arg1, unsigned arg2) +{ + tcg_debug_assert(arg2 < 64); + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_shifti_i64(ret, arg1, arg2, 1, 1); + } else if (arg2 == 0) { + tcg_gen_mov_i64(ret, arg1); + } else { + TCGv_i64 t0 = tcg_const_i64(arg2); + tcg_gen_sar_i64(ret, arg1, t0); + tcg_temp_free_i64(t0); + } +} + +void tcg_gen_brcond_i64(TCGCond cond, TCGv_i64 arg1, TCGv_i64 arg2, TCGLabel *l) +{ + if (cond == TCG_COND_ALWAYS) { + tcg_gen_br(l); + } else if (cond != TCG_COND_NEVER) { + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_op6ii_i32(INDEX_op_brcond2_i32, TCGV_LOW(arg1), + TCGV_HIGH(arg1), TCGV_LOW(arg2), + TCGV_HIGH(arg2), cond, label_arg(l)); + } else { + tcg_gen_op4ii_i64(INDEX_op_brcond_i64, arg1, arg2, cond, + label_arg(l)); + } + } +} + +void tcg_gen_brcondi_i64(TCGCond cond, TCGv_i64 arg1, int64_t arg2, TCGLabel *l) +{ + if (cond == TCG_COND_ALWAYS) { + tcg_gen_br(l); + } else if (cond != TCG_COND_NEVER) { + TCGv_i64 t0 = tcg_const_i64(arg2); + tcg_gen_brcond_i64(cond, arg1, t0, l); + tcg_temp_free_i64(t0); + } +} + +void tcg_gen_setcond_i64(TCGCond cond, TCGv_i64 ret, + TCGv_i64 arg1, TCGv_i64 arg2) +{ + if (cond == TCG_COND_ALWAYS) { + tcg_gen_movi_i64(ret, 1); + } else if (cond == TCG_COND_NEVER) { + tcg_gen_movi_i64(ret, 0); + } else { + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_op6i_i32(INDEX_op_setcond2_i32, TCGV_LOW(ret), + TCGV_LOW(arg1), TCGV_HIGH(arg1), + TCGV_LOW(arg2), TCGV_HIGH(arg2), cond); + tcg_gen_movi_i32(TCGV_HIGH(ret), 0); + } else { + tcg_gen_op4i_i64(INDEX_op_setcond_i64, ret, arg1, arg2, cond); + } + } +} + +void tcg_gen_setcondi_i64(TCGCond cond, TCGv_i64 ret, + TCGv_i64 arg1, int64_t arg2) +{ + TCGv_i64 t0 = tcg_const_i64(arg2); + tcg_gen_setcond_i64(cond, ret, arg1, t0); + tcg_temp_free_i64(t0); +} + +void tcg_gen_muli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) +{ + TCGv_i64 t0 = tcg_const_i64(arg2); + tcg_gen_mul_i64(ret, arg1, t0); + tcg_temp_free_i64(t0); +} + +void tcg_gen_div_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + if (TCG_TARGET_HAS_div_i64) { + tcg_gen_op3_i64(INDEX_op_div_i64, ret, arg1, arg2); + } else if (TCG_TARGET_HAS_div2_i64) { + TCGv_i64 t0 = tcg_temp_new_i64(); + tcg_gen_sari_i64(t0, arg1, 63); + tcg_gen_op5_i64(INDEX_op_div2_i64, ret, t0, arg1, t0, arg2); + tcg_temp_free_i64(t0); + } else { + gen_helper_div_i64(ret, arg1, arg2); + } +} + +void tcg_gen_rem_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + if (TCG_TARGET_HAS_rem_i64) { + tcg_gen_op3_i64(INDEX_op_rem_i64, ret, arg1, arg2); + } else if (TCG_TARGET_HAS_div_i64) { + TCGv_i64 t0 = tcg_temp_new_i64(); + tcg_gen_op3_i64(INDEX_op_div_i64, t0, arg1, arg2); + tcg_gen_mul_i64(t0, t0, arg2); + tcg_gen_sub_i64(ret, arg1, t0); + tcg_temp_free_i64(t0); + } else if (TCG_TARGET_HAS_div2_i64) { + TCGv_i64 t0 = tcg_temp_new_i64(); + tcg_gen_sari_i64(t0, arg1, 63); + tcg_gen_op5_i64(INDEX_op_div2_i64, t0, ret, arg1, t0, arg2); + tcg_temp_free_i64(t0); + } else { + gen_helper_rem_i64(ret, arg1, arg2); + } +} + +void tcg_gen_divu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + if (TCG_TARGET_HAS_div_i64) { + tcg_gen_op3_i64(INDEX_op_divu_i64, ret, arg1, arg2); + } else if (TCG_TARGET_HAS_div2_i64) { + TCGv_i64 t0 = tcg_temp_new_i64(); + tcg_gen_movi_i64(t0, 0); + tcg_gen_op5_i64(INDEX_op_divu2_i64, ret, t0, arg1, t0, arg2); + tcg_temp_free_i64(t0); + } else { + gen_helper_divu_i64(ret, arg1, arg2); + } +} + +void tcg_gen_remu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + if (TCG_TARGET_HAS_rem_i64) { + tcg_gen_op3_i64(INDEX_op_remu_i64, ret, arg1, arg2); + } else if (TCG_TARGET_HAS_div_i64) { + TCGv_i64 t0 = tcg_temp_new_i64(); + tcg_gen_op3_i64(INDEX_op_divu_i64, t0, arg1, arg2); + tcg_gen_mul_i64(t0, t0, arg2); + tcg_gen_sub_i64(ret, arg1, t0); + tcg_temp_free_i64(t0); + } else if (TCG_TARGET_HAS_div2_i64) { + TCGv_i64 t0 = tcg_temp_new_i64(); + tcg_gen_movi_i64(t0, 0); + tcg_gen_op5_i64(INDEX_op_divu2_i64, t0, ret, arg1, t0, arg2); + tcg_temp_free_i64(t0); + } else { + gen_helper_remu_i64(ret, arg1, arg2); + } +} + +void tcg_gen_ext8s_i64(TCGv_i64 ret, TCGv_i64 arg) +{ + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_ext8s_i32(TCGV_LOW(ret), TCGV_LOW(arg)); + tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); + } else if (TCG_TARGET_HAS_ext8s_i64) { + tcg_gen_op2_i64(INDEX_op_ext8s_i64, ret, arg); + } else { + tcg_gen_shli_i64(ret, arg, 56); + tcg_gen_sari_i64(ret, ret, 56); + } +} + +void tcg_gen_ext16s_i64(TCGv_i64 ret, TCGv_i64 arg) +{ + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_ext16s_i32(TCGV_LOW(ret), TCGV_LOW(arg)); + tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); + } else if (TCG_TARGET_HAS_ext16s_i64) { + tcg_gen_op2_i64(INDEX_op_ext16s_i64, ret, arg); + } else { + tcg_gen_shli_i64(ret, arg, 48); + tcg_gen_sari_i64(ret, ret, 48); + } +} + +void tcg_gen_ext32s_i64(TCGv_i64 ret, TCGv_i64 arg) +{ + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_mov_i32(TCGV_LOW(ret), TCGV_LOW(arg)); + tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); + } else if (TCG_TARGET_HAS_ext32s_i64) { + tcg_gen_op2_i64(INDEX_op_ext32s_i64, ret, arg); + } else { + tcg_gen_shli_i64(ret, arg, 32); + tcg_gen_sari_i64(ret, ret, 32); + } +} + +void tcg_gen_ext8u_i64(TCGv_i64 ret, TCGv_i64 arg) +{ + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_ext8u_i32(TCGV_LOW(ret), TCGV_LOW(arg)); + tcg_gen_movi_i32(TCGV_HIGH(ret), 0); + } else if (TCG_TARGET_HAS_ext8u_i64) { + tcg_gen_op2_i64(INDEX_op_ext8u_i64, ret, arg); + } else { + tcg_gen_andi_i64(ret, arg, 0xffu); + } +} + +void tcg_gen_ext16u_i64(TCGv_i64 ret, TCGv_i64 arg) +{ + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_ext16u_i32(TCGV_LOW(ret), TCGV_LOW(arg)); + tcg_gen_movi_i32(TCGV_HIGH(ret), 0); + } else if (TCG_TARGET_HAS_ext16u_i64) { + tcg_gen_op2_i64(INDEX_op_ext16u_i64, ret, arg); + } else { + tcg_gen_andi_i64(ret, arg, 0xffffu); + } +} + +void tcg_gen_ext32u_i64(TCGv_i64 ret, TCGv_i64 arg) +{ + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_mov_i32(TCGV_LOW(ret), TCGV_LOW(arg)); + tcg_gen_movi_i32(TCGV_HIGH(ret), 0); + } else if (TCG_TARGET_HAS_ext32u_i64) { + tcg_gen_op2_i64(INDEX_op_ext32u_i64, ret, arg); + } else { + tcg_gen_andi_i64(ret, arg, 0xffffffffu); + } +} + +/* Note: we assume the six high bytes are set to zero */ +void tcg_gen_bswap16_i64(TCGv_i64 ret, TCGv_i64 arg) +{ + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_bswap16_i32(TCGV_LOW(ret), TCGV_LOW(arg)); + tcg_gen_movi_i32(TCGV_HIGH(ret), 0); + } else if (TCG_TARGET_HAS_bswap16_i64) { + tcg_gen_op2_i64(INDEX_op_bswap16_i64, ret, arg); + } else { + TCGv_i64 t0 = tcg_temp_new_i64(); + + tcg_gen_ext8u_i64(t0, arg); + tcg_gen_shli_i64(t0, t0, 8); + tcg_gen_shri_i64(ret, arg, 8); + tcg_gen_or_i64(ret, ret, t0); + tcg_temp_free_i64(t0); + } +} + +/* Note: we assume the four high bytes are set to zero */ +void tcg_gen_bswap32_i64(TCGv_i64 ret, TCGv_i64 arg) +{ + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_bswap32_i32(TCGV_LOW(ret), TCGV_LOW(arg)); + tcg_gen_movi_i32(TCGV_HIGH(ret), 0); + } else if (TCG_TARGET_HAS_bswap32_i64) { + tcg_gen_op2_i64(INDEX_op_bswap32_i64, ret, arg); + } else { + TCGv_i64 t0, t1; + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + + tcg_gen_shli_i64(t0, arg, 24); + tcg_gen_ext32u_i64(t0, t0); + + tcg_gen_andi_i64(t1, arg, 0x0000ff00); + tcg_gen_shli_i64(t1, t1, 8); + tcg_gen_or_i64(t0, t0, t1); + + tcg_gen_shri_i64(t1, arg, 8); + tcg_gen_andi_i64(t1, t1, 0x0000ff00); + tcg_gen_or_i64(t0, t0, t1); + + tcg_gen_shri_i64(t1, arg, 24); + tcg_gen_or_i64(ret, t0, t1); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + } +} + +void tcg_gen_bswap64_i64(TCGv_i64 ret, TCGv_i64 arg) +{ + if (TCG_TARGET_REG_BITS == 32) { + TCGv_i32 t0, t1; + t0 = tcg_temp_new_i32(); + t1 = tcg_temp_new_i32(); + + tcg_gen_bswap32_i32(t0, TCGV_LOW(arg)); + tcg_gen_bswap32_i32(t1, TCGV_HIGH(arg)); + tcg_gen_mov_i32(TCGV_LOW(ret), t1); + tcg_gen_mov_i32(TCGV_HIGH(ret), t0); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); + } else if (TCG_TARGET_HAS_bswap64_i64) { + tcg_gen_op2_i64(INDEX_op_bswap64_i64, ret, arg); + } else { + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + + tcg_gen_shli_i64(t0, arg, 56); + + tcg_gen_andi_i64(t1, arg, 0x0000ff00); + tcg_gen_shli_i64(t1, t1, 40); + tcg_gen_or_i64(t0, t0, t1); + + tcg_gen_andi_i64(t1, arg, 0x00ff0000); + tcg_gen_shli_i64(t1, t1, 24); + tcg_gen_or_i64(t0, t0, t1); + + tcg_gen_andi_i64(t1, arg, 0xff000000); + tcg_gen_shli_i64(t1, t1, 8); + tcg_gen_or_i64(t0, t0, t1); + + tcg_gen_shri_i64(t1, arg, 8); + tcg_gen_andi_i64(t1, t1, 0xff000000); + tcg_gen_or_i64(t0, t0, t1); + + tcg_gen_shri_i64(t1, arg, 24); + tcg_gen_andi_i64(t1, t1, 0x00ff0000); + tcg_gen_or_i64(t0, t0, t1); + + tcg_gen_shri_i64(t1, arg, 40); + tcg_gen_andi_i64(t1, t1, 0x0000ff00); + tcg_gen_or_i64(t0, t0, t1); + + tcg_gen_shri_i64(t1, arg, 56); + tcg_gen_or_i64(ret, t0, t1); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + } +} + +void tcg_gen_not_i64(TCGv_i64 ret, TCGv_i64 arg) +{ + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_not_i32(TCGV_LOW(ret), TCGV_LOW(arg)); + tcg_gen_not_i32(TCGV_HIGH(ret), TCGV_HIGH(arg)); + } else if (TCG_TARGET_HAS_not_i64) { + tcg_gen_op2_i64(INDEX_op_not_i64, ret, arg); + } else { + tcg_gen_xori_i64(ret, arg, -1); + } +} + +void tcg_gen_andc_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_andc_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); + tcg_gen_andc_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); + } else if (TCG_TARGET_HAS_andc_i64) { + tcg_gen_op3_i64(INDEX_op_andc_i64, ret, arg1, arg2); + } else { + TCGv_i64 t0 = tcg_temp_new_i64(); + tcg_gen_not_i64(t0, arg2); + tcg_gen_and_i64(ret, arg1, t0); + tcg_temp_free_i64(t0); + } +} + +void tcg_gen_eqv_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_eqv_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); + tcg_gen_eqv_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); + } else if (TCG_TARGET_HAS_eqv_i64) { + tcg_gen_op3_i64(INDEX_op_eqv_i64, ret, arg1, arg2); + } else { + tcg_gen_xor_i64(ret, arg1, arg2); + tcg_gen_not_i64(ret, ret); + } +} + +void tcg_gen_nand_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_nand_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); + tcg_gen_nand_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); + } else if (TCG_TARGET_HAS_nand_i64) { + tcg_gen_op3_i64(INDEX_op_nand_i64, ret, arg1, arg2); + } else { + tcg_gen_and_i64(ret, arg1, arg2); + tcg_gen_not_i64(ret, ret); + } +} + +void tcg_gen_nor_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_nor_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); + tcg_gen_nor_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); + } else if (TCG_TARGET_HAS_nor_i64) { + tcg_gen_op3_i64(INDEX_op_nor_i64, ret, arg1, arg2); + } else { + tcg_gen_or_i64(ret, arg1, arg2); + tcg_gen_not_i64(ret, ret); + } +} + +void tcg_gen_orc_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_orc_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); + tcg_gen_orc_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); + } else if (TCG_TARGET_HAS_orc_i64) { + tcg_gen_op3_i64(INDEX_op_orc_i64, ret, arg1, arg2); + } else { + TCGv_i64 t0 = tcg_temp_new_i64(); + tcg_gen_not_i64(t0, arg2); + tcg_gen_or_i64(ret, arg1, t0); + tcg_temp_free_i64(t0); + } +} + +void tcg_gen_rotl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + if (TCG_TARGET_HAS_rot_i64) { + tcg_gen_op3_i64(INDEX_op_rotl_i64, ret, arg1, arg2); + } else { + TCGv_i64 t0, t1; + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + tcg_gen_shl_i64(t0, arg1, arg2); + tcg_gen_subfi_i64(t1, 64, arg2); + tcg_gen_shr_i64(t1, arg1, t1); + tcg_gen_or_i64(ret, t0, t1); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + } +} + +void tcg_gen_rotli_i64(TCGv_i64 ret, TCGv_i64 arg1, unsigned arg2) +{ + tcg_debug_assert(arg2 < 64); + /* some cases can be optimized here */ + if (arg2 == 0) { + tcg_gen_mov_i64(ret, arg1); + } else if (TCG_TARGET_HAS_rot_i64) { + TCGv_i64 t0 = tcg_const_i64(arg2); + tcg_gen_rotl_i64(ret, arg1, t0); + tcg_temp_free_i64(t0); + } else { + TCGv_i64 t0, t1; + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + tcg_gen_shli_i64(t0, arg1, arg2); + tcg_gen_shri_i64(t1, arg1, 64 - arg2); + tcg_gen_or_i64(ret, t0, t1); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + } +} + +void tcg_gen_rotr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + if (TCG_TARGET_HAS_rot_i64) { + tcg_gen_op3_i64(INDEX_op_rotr_i64, ret, arg1, arg2); + } else { + TCGv_i64 t0, t1; + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + tcg_gen_shr_i64(t0, arg1, arg2); + tcg_gen_subfi_i64(t1, 64, arg2); + tcg_gen_shl_i64(t1, arg1, t1); + tcg_gen_or_i64(ret, t0, t1); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + } +} + +void tcg_gen_rotri_i64(TCGv_i64 ret, TCGv_i64 arg1, unsigned arg2) +{ + tcg_debug_assert(arg2 < 64); + /* some cases can be optimized here */ + if (arg2 == 0) { + tcg_gen_mov_i64(ret, arg1); + } else { + tcg_gen_rotli_i64(ret, arg1, 64 - arg2); + } +} + +void tcg_gen_deposit_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2, + unsigned int ofs, unsigned int len) +{ + uint64_t mask; + TCGv_i64 t1; + + tcg_debug_assert(ofs < 64); + tcg_debug_assert(len <= 64); + tcg_debug_assert(ofs + len <= 64); + + if (ofs == 0 && len == 64) { + tcg_gen_mov_i64(ret, arg2); + return; + } + if (TCG_TARGET_HAS_deposit_i64 && TCG_TARGET_deposit_i64_valid(ofs, len)) { + tcg_gen_op5ii_i64(INDEX_op_deposit_i64, ret, arg1, arg2, ofs, len); + return; + } + + if (TCG_TARGET_REG_BITS == 32) { + if (ofs >= 32) { + tcg_gen_deposit_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), + TCGV_LOW(arg2), ofs - 32, len); + tcg_gen_mov_i32(TCGV_LOW(ret), TCGV_LOW(arg1)); + return; + } + if (ofs + len <= 32) { + tcg_gen_deposit_i32(TCGV_LOW(ret), TCGV_LOW(arg1), + TCGV_LOW(arg2), ofs, len); + tcg_gen_mov_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1)); + return; + } + } + + mask = (1ull << len) - 1; + t1 = tcg_temp_new_i64(); + + if (ofs + len < 64) { + tcg_gen_andi_i64(t1, arg2, mask); + tcg_gen_shli_i64(t1, t1, ofs); + } else { + tcg_gen_shli_i64(t1, arg2, ofs); + } + tcg_gen_andi_i64(ret, arg1, ~(mask << ofs)); + tcg_gen_or_i64(ret, ret, t1); + + tcg_temp_free_i64(t1); +} + +void tcg_gen_movcond_i64(TCGCond cond, TCGv_i64 ret, TCGv_i64 c1, + TCGv_i64 c2, TCGv_i64 v1, TCGv_i64 v2) +{ + if (cond == TCG_COND_ALWAYS) { + tcg_gen_mov_i64(ret, v1); + } else if (cond == TCG_COND_NEVER) { + tcg_gen_mov_i64(ret, v2); + } else if (TCG_TARGET_REG_BITS == 32) { + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + tcg_gen_op6i_i32(INDEX_op_setcond2_i32, t0, + TCGV_LOW(c1), TCGV_HIGH(c1), + TCGV_LOW(c2), TCGV_HIGH(c2), cond); + + if (TCG_TARGET_HAS_movcond_i32) { + tcg_gen_movi_i32(t1, 0); + tcg_gen_movcond_i32(TCG_COND_NE, TCGV_LOW(ret), t0, t1, + TCGV_LOW(v1), TCGV_LOW(v2)); + tcg_gen_movcond_i32(TCG_COND_NE, TCGV_HIGH(ret), t0, t1, + TCGV_HIGH(v1), TCGV_HIGH(v2)); + } else { + tcg_gen_neg_i32(t0, t0); + + tcg_gen_and_i32(t1, TCGV_LOW(v1), t0); + tcg_gen_andc_i32(TCGV_LOW(ret), TCGV_LOW(v2), t0); + tcg_gen_or_i32(TCGV_LOW(ret), TCGV_LOW(ret), t1); + + tcg_gen_and_i32(t1, TCGV_HIGH(v1), t0); + tcg_gen_andc_i32(TCGV_HIGH(ret), TCGV_HIGH(v2), t0); + tcg_gen_or_i32(TCGV_HIGH(ret), TCGV_HIGH(ret), t1); + } + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); + } else if (TCG_TARGET_HAS_movcond_i64) { + tcg_gen_op6i_i64(INDEX_op_movcond_i64, ret, c1, c2, v1, v2, cond); + } else { + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + tcg_gen_setcond_i64(cond, t0, c1, c2); + tcg_gen_neg_i64(t0, t0); + tcg_gen_and_i64(t1, v1, t0); + tcg_gen_andc_i64(ret, v2, t0); + tcg_gen_or_i64(ret, ret, t1); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + } +} + +void tcg_gen_add2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 al, + TCGv_i64 ah, TCGv_i64 bl, TCGv_i64 bh) +{ + if (TCG_TARGET_HAS_add2_i64) { + tcg_gen_op6_i64(INDEX_op_add2_i64, rl, rh, al, ah, bl, bh); + } else { + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + tcg_gen_add_i64(t0, al, bl); + tcg_gen_setcond_i64(TCG_COND_LTU, t1, t0, al); + tcg_gen_add_i64(rh, ah, bh); + tcg_gen_add_i64(rh, rh, t1); + tcg_gen_mov_i64(rl, t0); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + } +} + +void tcg_gen_sub2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 al, + TCGv_i64 ah, TCGv_i64 bl, TCGv_i64 bh) +{ + if (TCG_TARGET_HAS_sub2_i64) { + tcg_gen_op6_i64(INDEX_op_sub2_i64, rl, rh, al, ah, bl, bh); + } else { + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + tcg_gen_sub_i64(t0, al, bl); + tcg_gen_setcond_i64(TCG_COND_LTU, t1, al, bl); + tcg_gen_sub_i64(rh, ah, bh); + tcg_gen_sub_i64(rh, rh, t1); + tcg_gen_mov_i64(rl, t0); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + } +} + +void tcg_gen_mulu2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2) +{ + if (TCG_TARGET_HAS_mulu2_i64) { + tcg_gen_op4_i64(INDEX_op_mulu2_i64, rl, rh, arg1, arg2); + } else if (TCG_TARGET_HAS_muluh_i64) { + TCGv_i64 t = tcg_temp_new_i64(); + tcg_gen_op3_i64(INDEX_op_mul_i64, t, arg1, arg2); + tcg_gen_op3_i64(INDEX_op_muluh_i64, rh, arg1, arg2); + tcg_gen_mov_i64(rl, t); + tcg_temp_free_i64(t); + } else { + TCGv_i64 t0 = tcg_temp_new_i64(); + tcg_gen_mul_i64(t0, arg1, arg2); + gen_helper_muluh_i64(rh, arg1, arg2); + tcg_gen_mov_i64(rl, t0); + tcg_temp_free_i64(t0); + } +} + +void tcg_gen_muls2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2) +{ + if (TCG_TARGET_HAS_muls2_i64) { + tcg_gen_op4_i64(INDEX_op_muls2_i64, rl, rh, arg1, arg2); + } else if (TCG_TARGET_HAS_mulsh_i64) { + TCGv_i64 t = tcg_temp_new_i64(); + tcg_gen_op3_i64(INDEX_op_mul_i64, t, arg1, arg2); + tcg_gen_op3_i64(INDEX_op_mulsh_i64, rh, arg1, arg2); + tcg_gen_mov_i64(rl, t); + tcg_temp_free_i64(t); + } else if (TCG_TARGET_HAS_mulu2_i64 || TCG_TARGET_HAS_muluh_i64) { + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t3 = tcg_temp_new_i64(); + tcg_gen_mulu2_i64(t0, t1, arg1, arg2); + /* Adjust for negative inputs. */ + tcg_gen_sari_i64(t2, arg1, 63); + tcg_gen_sari_i64(t3, arg2, 63); + tcg_gen_and_i64(t2, t2, arg2); + tcg_gen_and_i64(t3, t3, arg1); + tcg_gen_sub_i64(rh, t1, t2); + tcg_gen_sub_i64(rh, rh, t3); + tcg_gen_mov_i64(rl, t0); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + tcg_temp_free_i64(t2); + tcg_temp_free_i64(t3); + } else { + TCGv_i64 t0 = tcg_temp_new_i64(); + tcg_gen_mul_i64(t0, arg1, arg2); + gen_helper_mulsh_i64(rh, arg1, arg2); + tcg_gen_mov_i64(rl, t0); + tcg_temp_free_i64(t0); + } +} + +/* Size changing operations. */ + +void tcg_gen_trunc_shr_i64_i32(TCGv_i32 ret, TCGv_i64 arg, unsigned count) +{ + tcg_debug_assert(count < 64); + if (TCG_TARGET_REG_BITS == 32) { + if (count >= 32) { + tcg_gen_shri_i32(ret, TCGV_HIGH(arg), count - 32); + } else if (count == 0) { + tcg_gen_mov_i32(ret, TCGV_LOW(arg)); + } else { + TCGv_i64 t = tcg_temp_new_i64(); + tcg_gen_shri_i64(t, arg, count); + tcg_gen_mov_i32(ret, TCGV_LOW(t)); + tcg_temp_free_i64(t); + } + } else if (TCG_TARGET_HAS_trunc_shr_i32) { + tcg_gen_op3i_i32(INDEX_op_trunc_shr_i32, ret, + MAKE_TCGV_I32(GET_TCGV_I64(arg)), count); + } else if (count == 0) { + tcg_gen_mov_i32(ret, MAKE_TCGV_I32(GET_TCGV_I64(arg))); + } else { + TCGv_i64 t = tcg_temp_new_i64(); + tcg_gen_shri_i64(t, arg, count); + tcg_gen_mov_i32(ret, MAKE_TCGV_I32(GET_TCGV_I64(t))); + tcg_temp_free_i64(t); + } +} + +void tcg_gen_extu_i32_i64(TCGv_i64 ret, TCGv_i32 arg) +{ + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_mov_i32(TCGV_LOW(ret), arg); + tcg_gen_movi_i32(TCGV_HIGH(ret), 0); + } else { + /* Note: we assume the target supports move between + 32 and 64 bit registers. */ + tcg_gen_ext32u_i64(ret, MAKE_TCGV_I64(GET_TCGV_I32(arg))); + } +} + +void tcg_gen_ext_i32_i64(TCGv_i64 ret, TCGv_i32 arg) +{ + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_mov_i32(TCGV_LOW(ret), arg); + tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); + } else { + /* Note: we assume the target supports move between + 32 and 64 bit registers. */ + tcg_gen_ext32s_i64(ret, MAKE_TCGV_I64(GET_TCGV_I32(arg))); + } +} + +void tcg_gen_concat_i32_i64(TCGv_i64 dest, TCGv_i32 low, TCGv_i32 high) +{ + TCGv_i64 tmp; + + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_mov_i32(TCGV_LOW(dest), low); + tcg_gen_mov_i32(TCGV_HIGH(dest), high); + return; + } + + tmp = tcg_temp_new_i64(); + /* These extensions are only needed for type correctness. + We may be able to do better given target specific information. */ + tcg_gen_extu_i32_i64(tmp, high); + tcg_gen_extu_i32_i64(dest, low); + /* If deposit is available, use it. Otherwise use the extra + knowledge that we have of the zero-extensions above. */ + if (TCG_TARGET_HAS_deposit_i64 && TCG_TARGET_deposit_i64_valid(32, 32)) { + tcg_gen_deposit_i64(dest, dest, tmp, 32, 32); + } else { + tcg_gen_shli_i64(tmp, tmp, 32); + tcg_gen_or_i64(dest, dest, tmp); + } + tcg_temp_free_i64(tmp); +} + +void tcg_gen_extr_i64_i32(TCGv_i32 lo, TCGv_i32 hi, TCGv_i64 arg) +{ + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_mov_i32(lo, TCGV_LOW(arg)); + tcg_gen_mov_i32(hi, TCGV_HIGH(arg)); + } else { + tcg_gen_trunc_shr_i64_i32(lo, arg, 0); + tcg_gen_trunc_shr_i64_i32(hi, arg, 32); + } +} + +void tcg_gen_extr32_i64(TCGv_i64 lo, TCGv_i64 hi, TCGv_i64 arg) +{ + tcg_gen_ext32u_i64(lo, arg); + tcg_gen_shri_i64(hi, arg, 32); +} + +/* QEMU specific operations. */ + +void tcg_gen_goto_tb(unsigned idx) +{ + /* We only support two chained exits. */ + tcg_debug_assert(idx <= 1); +#ifdef CONFIG_DEBUG_TCG + /* Verify that we havn't seen this numbered exit before. */ + tcg_debug_assert((tcg_ctx.goto_tb_issue_mask & (1 << idx)) == 0); + tcg_ctx.goto_tb_issue_mask |= 1 << idx; +#endif + tcg_gen_op1i(INDEX_op_goto_tb, idx); +} + +static inline TCGMemOp tcg_canonicalize_memop(TCGMemOp op, bool is64, bool st) +{ + switch (op & MO_SIZE) { + case MO_8: + op &= ~MO_BSWAP; + break; + case MO_16: + break; + case MO_32: + if (!is64) { + op &= ~MO_SIGN; + } + break; + case MO_64: + if (!is64) { + tcg_abort(); + } + break; + } + if (st) { + op &= ~MO_SIGN; + } + return op; +} + +static void gen_ldst_i32(TCGOpcode opc, TCGv_i32 val, TCGv addr, + TCGMemOp memop, TCGArg idx) +{ +#if TARGET_LONG_BITS == 32 + tcg_gen_op4ii_i32(opc, val, addr, memop, idx); +#else + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_op5ii_i32(opc, val, TCGV_LOW(addr), TCGV_HIGH(addr), + memop, idx); + } else { + tcg_gen_op4(&tcg_ctx, opc, GET_TCGV_I32(val), GET_TCGV_I64(addr), + memop, idx); + } +#endif +} + +static void gen_ldst_i64(TCGOpcode opc, TCGv_i64 val, TCGv addr, + TCGMemOp memop, TCGArg idx) +{ +#if TARGET_LONG_BITS == 32 + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_op5ii_i32(opc, TCGV_LOW(val), TCGV_HIGH(val), + addr, memop, idx); + } else { + tcg_gen_op4(&tcg_ctx, opc, GET_TCGV_I64(val), GET_TCGV_I32(addr), + memop, idx); + } +#else + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_op6ii_i32(opc, TCGV_LOW(val), TCGV_HIGH(val), + TCGV_LOW(addr), TCGV_HIGH(addr), memop, idx); + } else { + tcg_gen_op4ii_i64(opc, val, addr, memop, idx); + } +#endif +} + +void tcg_gen_qemu_ld_i32(TCGv_i32 val, TCGv addr, TCGArg idx, TCGMemOp memop) +{ + memop = tcg_canonicalize_memop(memop, 0, 0); + gen_ldst_i32(INDEX_op_qemu_ld_i32, val, addr, memop, idx); +} + +void tcg_gen_qemu_st_i32(TCGv_i32 val, TCGv addr, TCGArg idx, TCGMemOp memop) +{ + memop = tcg_canonicalize_memop(memop, 0, 1); + gen_ldst_i32(INDEX_op_qemu_st_i32, val, addr, memop, idx); +} + +void tcg_gen_qemu_ld_i64(TCGv_i64 val, TCGv addr, TCGArg idx, TCGMemOp memop) +{ + if (TCG_TARGET_REG_BITS == 32 && (memop & MO_SIZE) < MO_64) { + tcg_gen_qemu_ld_i32(TCGV_LOW(val), addr, idx, memop); + if (memop & MO_SIGN) { + tcg_gen_sari_i32(TCGV_HIGH(val), TCGV_LOW(val), 31); + } else { + tcg_gen_movi_i32(TCGV_HIGH(val), 0); + } + return; + } + + memop = tcg_canonicalize_memop(memop, 1, 0); + gen_ldst_i64(INDEX_op_qemu_ld_i64, val, addr, memop, idx); +} + +void tcg_gen_qemu_st_i64(TCGv_i64 val, TCGv addr, TCGArg idx, TCGMemOp memop) +{ + if (TCG_TARGET_REG_BITS == 32 && (memop & MO_SIZE) < MO_64) { + tcg_gen_qemu_st_i32(TCGV_LOW(val), addr, idx, memop); + return; + } + + memop = tcg_canonicalize_memop(memop, 1, 1); + gen_ldst_i64(INDEX_op_qemu_st_i64, val, addr, memop, idx); +} diff --git a/tcg/tcg-op.h b/tcg/tcg-op.h index 019dd9b..d1d763f 100644 --- a/tcg/tcg-op.h +++ b/tcg/tcg-op.h @@ -21,359 +21,306 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ + #include "tcg.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" -int gen_new_label(void); +/* Basic output routines. Not for general consumption. */ + +void tcg_gen_op1(TCGContext *, TCGOpcode, TCGArg); +void tcg_gen_op2(TCGContext *, TCGOpcode, TCGArg, TCGArg); +void tcg_gen_op3(TCGContext *, TCGOpcode, TCGArg, TCGArg, TCGArg); +void tcg_gen_op4(TCGContext *, TCGOpcode, TCGArg, TCGArg, TCGArg, TCGArg); +void tcg_gen_op5(TCGContext *, TCGOpcode, TCGArg, TCGArg, TCGArg, + TCGArg, TCGArg); +void tcg_gen_op6(TCGContext *, TCGOpcode, TCGArg, TCGArg, TCGArg, + TCGArg, TCGArg, TCGArg); -static inline void tcg_gen_op0(TCGOpcode opc) -{ - *tcg_ctx.gen_opc_ptr++ = opc; -} -static inline void tcg_gen_op1_i32(TCGOpcode opc, TCGv_i32 arg1) +static inline void tcg_gen_op1_i32(TCGOpcode opc, TCGv_i32 a1) { - *tcg_ctx.gen_opc_ptr++ = opc; - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg1); + tcg_gen_op1(&tcg_ctx, opc, GET_TCGV_I32(a1)); } -static inline void tcg_gen_op1_i64(TCGOpcode opc, TCGv_i64 arg1) +static inline void tcg_gen_op1_i64(TCGOpcode opc, TCGv_i64 a1) { - *tcg_ctx.gen_opc_ptr++ = opc; - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg1); + tcg_gen_op1(&tcg_ctx, opc, GET_TCGV_I64(a1)); } -static inline void tcg_gen_op1i(TCGOpcode opc, TCGArg arg1) +static inline void tcg_gen_op1i(TCGOpcode opc, TCGArg a1) { - *tcg_ctx.gen_opc_ptr++ = opc; - *tcg_ctx.gen_opparam_ptr++ = arg1; + tcg_gen_op1(&tcg_ctx, opc, a1); } -static inline void tcg_gen_op2_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2) +static inline void tcg_gen_op2_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2) { - *tcg_ctx.gen_opc_ptr++ = opc; - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg1); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg2); + tcg_gen_op2(&tcg_ctx, opc, GET_TCGV_I32(a1), GET_TCGV_I32(a2)); } -static inline void tcg_gen_op2_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2) +static inline void tcg_gen_op2_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2) { - *tcg_ctx.gen_opc_ptr++ = opc; - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg1); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg2); + tcg_gen_op2(&tcg_ctx, opc, GET_TCGV_I64(a1), GET_TCGV_I64(a2)); } -static inline void tcg_gen_op2i_i32(TCGOpcode opc, TCGv_i32 arg1, TCGArg arg2) +static inline void tcg_gen_op2i_i32(TCGOpcode opc, TCGv_i32 a1, TCGArg a2) { - *tcg_ctx.gen_opc_ptr++ = opc; - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg1); - *tcg_ctx.gen_opparam_ptr++ = arg2; + tcg_gen_op2(&tcg_ctx, opc, GET_TCGV_I32(a1), a2); } -static inline void tcg_gen_op2i_i64(TCGOpcode opc, TCGv_i64 arg1, TCGArg arg2) +static inline void tcg_gen_op2i_i64(TCGOpcode opc, TCGv_i64 a1, TCGArg a2) { - *tcg_ctx.gen_opc_ptr++ = opc; - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg1); - *tcg_ctx.gen_opparam_ptr++ = arg2; + tcg_gen_op2(&tcg_ctx, opc, GET_TCGV_I64(a1), a2); } -static inline void tcg_gen_op2ii(TCGOpcode opc, TCGArg arg1, TCGArg arg2) +static inline void tcg_gen_op2ii(TCGOpcode opc, TCGArg a1, TCGArg a2) { - *tcg_ctx.gen_opc_ptr++ = opc; - *tcg_ctx.gen_opparam_ptr++ = arg1; - *tcg_ctx.gen_opparam_ptr++ = arg2; + tcg_gen_op2(&tcg_ctx, opc, a1, a2); } -static inline void tcg_gen_op3_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2, - TCGv_i32 arg3) +static inline void tcg_gen_op3_i32(TCGOpcode opc, TCGv_i32 a1, + TCGv_i32 a2, TCGv_i32 a3) { - *tcg_ctx.gen_opc_ptr++ = opc; - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg1); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg2); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg3); + tcg_gen_op3(&tcg_ctx, opc, GET_TCGV_I32(a1), + GET_TCGV_I32(a2), GET_TCGV_I32(a3)); } -static inline void tcg_gen_op3_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2, - TCGv_i64 arg3) +static inline void tcg_gen_op3_i64(TCGOpcode opc, TCGv_i64 a1, + TCGv_i64 a2, TCGv_i64 a3) { - *tcg_ctx.gen_opc_ptr++ = opc; - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg1); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg2); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg3); + tcg_gen_op3(&tcg_ctx, opc, GET_TCGV_I64(a1), + GET_TCGV_I64(a2), GET_TCGV_I64(a3)); } -static inline void tcg_gen_op3i_i32(TCGOpcode opc, TCGv_i32 arg1, - TCGv_i32 arg2, TCGArg arg3) +static inline void tcg_gen_op3i_i32(TCGOpcode opc, TCGv_i32 a1, + TCGv_i32 a2, TCGArg a3) { - *tcg_ctx.gen_opc_ptr++ = opc; - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg1); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg2); - *tcg_ctx.gen_opparam_ptr++ = arg3; + tcg_gen_op3(&tcg_ctx, opc, GET_TCGV_I32(a1), GET_TCGV_I32(a2), a3); } -static inline void tcg_gen_op3i_i64(TCGOpcode opc, TCGv_i64 arg1, - TCGv_i64 arg2, TCGArg arg3) +static inline void tcg_gen_op3i_i64(TCGOpcode opc, TCGv_i64 a1, + TCGv_i64 a2, TCGArg a3) { - *tcg_ctx.gen_opc_ptr++ = opc; - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg1); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg2); - *tcg_ctx.gen_opparam_ptr++ = arg3; + tcg_gen_op3(&tcg_ctx, opc, GET_TCGV_I64(a1), GET_TCGV_I64(a2), a3); } static inline void tcg_gen_ldst_op_i32(TCGOpcode opc, TCGv_i32 val, TCGv_ptr base, TCGArg offset) { - *tcg_ctx.gen_opc_ptr++ = opc; - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(val); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_PTR(base); - *tcg_ctx.gen_opparam_ptr++ = offset; + tcg_gen_op3(&tcg_ctx, opc, GET_TCGV_I32(val), GET_TCGV_PTR(base), offset); } static inline void tcg_gen_ldst_op_i64(TCGOpcode opc, TCGv_i64 val, TCGv_ptr base, TCGArg offset) { - *tcg_ctx.gen_opc_ptr++ = opc; - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(val); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_PTR(base); - *tcg_ctx.gen_opparam_ptr++ = offset; + tcg_gen_op3(&tcg_ctx, opc, GET_TCGV_I64(val), GET_TCGV_PTR(base), offset); } -static inline void tcg_gen_op4_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2, - TCGv_i32 arg3, TCGv_i32 arg4) +static inline void tcg_gen_op4_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGv_i32 a4) { - *tcg_ctx.gen_opc_ptr++ = opc; - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg1); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg2); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg3); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg4); + tcg_gen_op4(&tcg_ctx, opc, GET_TCGV_I32(a1), GET_TCGV_I32(a2), + GET_TCGV_I32(a3), GET_TCGV_I32(a4)); } -static inline void tcg_gen_op4_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2, - TCGv_i64 arg3, TCGv_i64 arg4) +static inline void tcg_gen_op4_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGv_i64 a3, TCGv_i64 a4) { - *tcg_ctx.gen_opc_ptr++ = opc; - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg1); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg2); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg3); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg4); + tcg_gen_op4(&tcg_ctx, opc, GET_TCGV_I64(a1), GET_TCGV_I64(a2), + GET_TCGV_I64(a3), GET_TCGV_I64(a4)); } -static inline void tcg_gen_op4i_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2, - TCGv_i32 arg3, TCGArg arg4) +static inline void tcg_gen_op4i_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGArg a4) { - *tcg_ctx.gen_opc_ptr++ = opc; - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg1); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg2); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg3); - *tcg_ctx.gen_opparam_ptr++ = arg4; + tcg_gen_op4(&tcg_ctx, opc, GET_TCGV_I32(a1), GET_TCGV_I32(a2), + GET_TCGV_I32(a3), a4); } -static inline void tcg_gen_op4i_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2, - TCGv_i64 arg3, TCGArg arg4) +static inline void tcg_gen_op4i_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGv_i64 a3, TCGArg a4) { - *tcg_ctx.gen_opc_ptr++ = opc; - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg1); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg2); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg3); - *tcg_ctx.gen_opparam_ptr++ = arg4; + tcg_gen_op4(&tcg_ctx, opc, GET_TCGV_I64(a1), GET_TCGV_I64(a2), + GET_TCGV_I64(a3), a4); } -static inline void tcg_gen_op4ii_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2, - TCGArg arg3, TCGArg arg4) +static inline void tcg_gen_op4ii_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGArg a3, TCGArg a4) { - *tcg_ctx.gen_opc_ptr++ = opc; - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg1); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg2); - *tcg_ctx.gen_opparam_ptr++ = arg3; - *tcg_ctx.gen_opparam_ptr++ = arg4; + tcg_gen_op4(&tcg_ctx, opc, GET_TCGV_I32(a1), GET_TCGV_I32(a2), a3, a4); } -static inline void tcg_gen_op4ii_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2, - TCGArg arg3, TCGArg arg4) +static inline void tcg_gen_op4ii_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGArg a3, TCGArg a4) { - *tcg_ctx.gen_opc_ptr++ = opc; - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg1); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg2); - *tcg_ctx.gen_opparam_ptr++ = arg3; - *tcg_ctx.gen_opparam_ptr++ = arg4; + tcg_gen_op4(&tcg_ctx, opc, GET_TCGV_I64(a1), GET_TCGV_I64(a2), a3, a4); } -static inline void tcg_gen_op5_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2, - TCGv_i32 arg3, TCGv_i32 arg4, TCGv_i32 arg5) +static inline void tcg_gen_op5_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGv_i32 a4, TCGv_i32 a5) { - *tcg_ctx.gen_opc_ptr++ = opc; - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg1); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg2); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg3); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg4); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg5); + tcg_gen_op5(&tcg_ctx, opc, GET_TCGV_I32(a1), GET_TCGV_I32(a2), + GET_TCGV_I32(a3), GET_TCGV_I32(a4), GET_TCGV_I32(a5)); } -static inline void tcg_gen_op5_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2, - TCGv_i64 arg3, TCGv_i64 arg4, TCGv_i64 arg5) +static inline void tcg_gen_op5_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGv_i64 a3, TCGv_i64 a4, TCGv_i64 a5) { - *tcg_ctx.gen_opc_ptr++ = opc; - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg1); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg2); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg3); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg4); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg5); + tcg_gen_op5(&tcg_ctx, opc, GET_TCGV_I64(a1), GET_TCGV_I64(a2), + GET_TCGV_I64(a3), GET_TCGV_I64(a4), GET_TCGV_I64(a5)); } -static inline void tcg_gen_op5i_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2, - TCGv_i32 arg3, TCGv_i32 arg4, TCGArg arg5) +static inline void tcg_gen_op5i_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGv_i32 a4, TCGArg a5) { - *tcg_ctx.gen_opc_ptr++ = opc; - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg1); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg2); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg3); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg4); - *tcg_ctx.gen_opparam_ptr++ = arg5; + tcg_gen_op5(&tcg_ctx, opc, GET_TCGV_I32(a1), GET_TCGV_I32(a2), + GET_TCGV_I32(a3), GET_TCGV_I32(a4), a5); } -static inline void tcg_gen_op5i_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2, - TCGv_i64 arg3, TCGv_i64 arg4, TCGArg arg5) +static inline void tcg_gen_op5i_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGv_i64 a3, TCGv_i64 a4, TCGArg a5) { - *tcg_ctx.gen_opc_ptr++ = opc; - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg1); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg2); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg3); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg4); - *tcg_ctx.gen_opparam_ptr++ = arg5; + tcg_gen_op5(&tcg_ctx, opc, GET_TCGV_I64(a1), GET_TCGV_I64(a2), + GET_TCGV_I64(a3), GET_TCGV_I64(a4), a5); } -static inline void tcg_gen_op5ii_i32(TCGOpcode opc, TCGv_i32 arg1, - TCGv_i32 arg2, TCGv_i32 arg3, - TCGArg arg4, TCGArg arg5) +static inline void tcg_gen_op5ii_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGArg a4, TCGArg a5) { - *tcg_ctx.gen_opc_ptr++ = opc; - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg1); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg2); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg3); - *tcg_ctx.gen_opparam_ptr++ = arg4; - *tcg_ctx.gen_opparam_ptr++ = arg5; + tcg_gen_op5(&tcg_ctx, opc, GET_TCGV_I32(a1), GET_TCGV_I32(a2), + GET_TCGV_I32(a3), a4, a5); } -static inline void tcg_gen_op5ii_i64(TCGOpcode opc, TCGv_i64 arg1, - TCGv_i64 arg2, TCGv_i64 arg3, - TCGArg arg4, TCGArg arg5) +static inline void tcg_gen_op5ii_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGv_i64 a3, TCGArg a4, TCGArg a5) { - *tcg_ctx.gen_opc_ptr++ = opc; - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg1); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg2); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg3); - *tcg_ctx.gen_opparam_ptr++ = arg4; - *tcg_ctx.gen_opparam_ptr++ = arg5; + tcg_gen_op5(&tcg_ctx, opc, GET_TCGV_I64(a1), GET_TCGV_I64(a2), + GET_TCGV_I64(a3), a4, a5); } -static inline void tcg_gen_op6_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2, - TCGv_i32 arg3, TCGv_i32 arg4, TCGv_i32 arg5, - TCGv_i32 arg6) +static inline void tcg_gen_op6_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGv_i32 a4, + TCGv_i32 a5, TCGv_i32 a6) { - *tcg_ctx.gen_opc_ptr++ = opc; - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg1); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg2); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg3); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg4); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg5); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg6); + tcg_gen_op6(&tcg_ctx, opc, GET_TCGV_I32(a1), GET_TCGV_I32(a2), + GET_TCGV_I32(a3), GET_TCGV_I32(a4), GET_TCGV_I32(a5), + GET_TCGV_I32(a6)); } -static inline void tcg_gen_op6_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2, - TCGv_i64 arg3, TCGv_i64 arg4, TCGv_i64 arg5, - TCGv_i64 arg6) +static inline void tcg_gen_op6_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGv_i64 a3, TCGv_i64 a4, + TCGv_i64 a5, TCGv_i64 a6) { - *tcg_ctx.gen_opc_ptr++ = opc; - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg1); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg2); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg3); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg4); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg5); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg6); + tcg_gen_op6(&tcg_ctx, opc, GET_TCGV_I64(a1), GET_TCGV_I64(a2), + GET_TCGV_I64(a3), GET_TCGV_I64(a4), GET_TCGV_I64(a5), + GET_TCGV_I64(a6)); } -static inline void tcg_gen_op6i_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2, - TCGv_i32 arg3, TCGv_i32 arg4, - TCGv_i32 arg5, TCGArg arg6) +static inline void tcg_gen_op6i_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGv_i32 a4, + TCGv_i32 a5, TCGArg a6) { - *tcg_ctx.gen_opc_ptr++ = opc; - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg1); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg2); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg3); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg4); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg5); - *tcg_ctx.gen_opparam_ptr++ = arg6; + tcg_gen_op6(&tcg_ctx, opc, GET_TCGV_I32(a1), GET_TCGV_I32(a2), + GET_TCGV_I32(a3), GET_TCGV_I32(a4), GET_TCGV_I32(a5), a6); } -static inline void tcg_gen_op6i_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2, - TCGv_i64 arg3, TCGv_i64 arg4, - TCGv_i64 arg5, TCGArg arg6) +static inline void tcg_gen_op6i_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGv_i64 a3, TCGv_i64 a4, + TCGv_i64 a5, TCGArg a6) { - *tcg_ctx.gen_opc_ptr++ = opc; - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg1); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg2); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg3); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg4); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg5); - *tcg_ctx.gen_opparam_ptr++ = arg6; + tcg_gen_op6(&tcg_ctx, opc, GET_TCGV_I64(a1), GET_TCGV_I64(a2), + GET_TCGV_I64(a3), GET_TCGV_I64(a4), GET_TCGV_I64(a5), a6); } -static inline void tcg_gen_op6ii_i32(TCGOpcode opc, TCGv_i32 arg1, - TCGv_i32 arg2, TCGv_i32 arg3, - TCGv_i32 arg4, TCGArg arg5, TCGArg arg6) +static inline void tcg_gen_op6ii_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGv_i32 a4, + TCGArg a5, TCGArg a6) { - *tcg_ctx.gen_opc_ptr++ = opc; - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg1); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg2); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg3); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg4); - *tcg_ctx.gen_opparam_ptr++ = arg5; - *tcg_ctx.gen_opparam_ptr++ = arg6; + tcg_gen_op6(&tcg_ctx, opc, GET_TCGV_I32(a1), GET_TCGV_I32(a2), + GET_TCGV_I32(a3), GET_TCGV_I32(a4), a5, a6); } -static inline void tcg_gen_op6ii_i64(TCGOpcode opc, TCGv_i64 arg1, - TCGv_i64 arg2, TCGv_i64 arg3, - TCGv_i64 arg4, TCGArg arg5, TCGArg arg6) +static inline void tcg_gen_op6ii_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGv_i64 a3, TCGv_i64 a4, + TCGArg a5, TCGArg a6) { - *tcg_ctx.gen_opc_ptr++ = opc; - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg1); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg2); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg3); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(arg4); - *tcg_ctx.gen_opparam_ptr++ = arg5; - *tcg_ctx.gen_opparam_ptr++ = arg6; + tcg_gen_op6(&tcg_ctx, opc, GET_TCGV_I64(a1), GET_TCGV_I64(a2), + GET_TCGV_I64(a3), GET_TCGV_I64(a4), a5, a6); } -static inline void tcg_add_param_i32(TCGv_i32 val) -{ - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(val); -} -static inline void tcg_add_param_i64(TCGv_i64 val) +/* Generic ops. */ + +static inline void gen_set_label(TCGLabel *l) { -#if TCG_TARGET_REG_BITS == 32 - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(TCGV_LOW(val)); - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(TCGV_HIGH(val)); -#else - *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I64(val); -#endif + tcg_gen_op1(&tcg_ctx, INDEX_op_set_label, label_arg(l)); } -static inline void gen_set_label(int n) +static inline void tcg_gen_br(TCGLabel *l) { - tcg_gen_op1i(INDEX_op_set_label, n); + tcg_gen_op1(&tcg_ctx, INDEX_op_br, label_arg(l)); } -static inline void tcg_gen_br(int label) +/* Helper calls. */ + +/* 32 bit ops */ + +void tcg_gen_addi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); +void tcg_gen_subfi_i32(TCGv_i32 ret, int32_t arg1, TCGv_i32 arg2); +void tcg_gen_subi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); +void tcg_gen_andi_i32(TCGv_i32 ret, TCGv_i32 arg1, uint32_t arg2); +void tcg_gen_ori_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); +void tcg_gen_xori_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); +void tcg_gen_shli_i32(TCGv_i32 ret, TCGv_i32 arg1, unsigned arg2); +void tcg_gen_shri_i32(TCGv_i32 ret, TCGv_i32 arg1, unsigned arg2); +void tcg_gen_sari_i32(TCGv_i32 ret, TCGv_i32 arg1, unsigned arg2); +void tcg_gen_muli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); +void tcg_gen_div_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_rem_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_divu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_remu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_andc_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_eqv_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_nand_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_nor_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_orc_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_rotl_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_rotli_i32(TCGv_i32 ret, TCGv_i32 arg1, unsigned arg2); +void tcg_gen_rotr_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_rotri_i32(TCGv_i32 ret, TCGv_i32 arg1, unsigned arg2); +void tcg_gen_deposit_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2, + unsigned int ofs, unsigned int len); +void tcg_gen_brcond_i32(TCGCond cond, TCGv_i32 arg1, TCGv_i32 arg2, TCGLabel *); +void tcg_gen_brcondi_i32(TCGCond cond, TCGv_i32 arg1, int32_t arg2, TCGLabel *); +void tcg_gen_setcond_i32(TCGCond cond, TCGv_i32 ret, + TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_setcondi_i32(TCGCond cond, TCGv_i32 ret, + TCGv_i32 arg1, int32_t arg2); +void tcg_gen_movcond_i32(TCGCond cond, TCGv_i32 ret, TCGv_i32 c1, + TCGv_i32 c2, TCGv_i32 v1, TCGv_i32 v2); +void tcg_gen_add2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 al, + TCGv_i32 ah, TCGv_i32 bl, TCGv_i32 bh); +void tcg_gen_sub2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 al, + TCGv_i32 ah, TCGv_i32 bl, TCGv_i32 bh); +void tcg_gen_mulu2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_muls2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_ext8s_i32(TCGv_i32 ret, TCGv_i32 arg); +void tcg_gen_ext16s_i32(TCGv_i32 ret, TCGv_i32 arg); +void tcg_gen_ext8u_i32(TCGv_i32 ret, TCGv_i32 arg); +void tcg_gen_ext16u_i32(TCGv_i32 ret, TCGv_i32 arg); +void tcg_gen_bswap16_i32(TCGv_i32 ret, TCGv_i32 arg); +void tcg_gen_bswap32_i32(TCGv_i32 ret, TCGv_i32 arg); + +static inline void tcg_gen_discard_i32(TCGv_i32 arg) { - tcg_gen_op1i(INDEX_op_br, label); + tcg_gen_op1_i32(INDEX_op_discard, arg); } static inline void tcg_gen_mov_i32(TCGv_i32 ret, TCGv_i32 arg) { - if (!TCGV_EQUAL_I32(ret, arg)) + if (!TCGV_EQUAL_I32(ret, arg)) { tcg_gen_op2_i32(INDEX_op_mov_i32, ret, arg); + } } static inline void tcg_gen_movi_i32(TCGv_i32 ret, int32_t arg) @@ -381,44 +328,50 @@ static inline void tcg_gen_movi_i32(TCGv_i32 ret, int32_t arg) tcg_gen_op2i_i32(INDEX_op_movi_i32, ret, arg); } -/* 32 bit ops */ - -static inline void tcg_gen_ld8u_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset) +static inline void tcg_gen_ld8u_i32(TCGv_i32 ret, TCGv_ptr arg2, + tcg_target_long offset) { tcg_gen_ldst_op_i32(INDEX_op_ld8u_i32, ret, arg2, offset); } -static inline void tcg_gen_ld8s_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset) +static inline void tcg_gen_ld8s_i32(TCGv_i32 ret, TCGv_ptr arg2, + tcg_target_long offset) { tcg_gen_ldst_op_i32(INDEX_op_ld8s_i32, ret, arg2, offset); } -static inline void tcg_gen_ld16u_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset) +static inline void tcg_gen_ld16u_i32(TCGv_i32 ret, TCGv_ptr arg2, + tcg_target_long offset) { tcg_gen_ldst_op_i32(INDEX_op_ld16u_i32, ret, arg2, offset); } -static inline void tcg_gen_ld16s_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset) +static inline void tcg_gen_ld16s_i32(TCGv_i32 ret, TCGv_ptr arg2, + tcg_target_long offset) { tcg_gen_ldst_op_i32(INDEX_op_ld16s_i32, ret, arg2, offset); } -static inline void tcg_gen_ld_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset) +static inline void tcg_gen_ld_i32(TCGv_i32 ret, TCGv_ptr arg2, + tcg_target_long offset) { tcg_gen_ldst_op_i32(INDEX_op_ld_i32, ret, arg2, offset); } -static inline void tcg_gen_st8_i32(TCGv_i32 arg1, TCGv_ptr arg2, tcg_target_long offset) +static inline void tcg_gen_st8_i32(TCGv_i32 arg1, TCGv_ptr arg2, + tcg_target_long offset) { tcg_gen_ldst_op_i32(INDEX_op_st8_i32, arg1, arg2, offset); } -static inline void tcg_gen_st16_i32(TCGv_i32 arg1, TCGv_ptr arg2, tcg_target_long offset) +static inline void tcg_gen_st16_i32(TCGv_i32 arg1, TCGv_ptr arg2, + tcg_target_long offset) { tcg_gen_ldst_op_i32(INDEX_op_st16_i32, arg1, arg2, offset); } -static inline void tcg_gen_st_i32(TCGv_i32 arg1, TCGv_ptr arg2, tcg_target_long offset) +static inline void tcg_gen_st_i32(TCGv_i32 arg1, TCGv_ptr arg2, + tcg_target_long offset) { tcg_gen_ldst_op_i32(INDEX_op_st_i32, arg1, arg2, offset); } @@ -428,126 +381,24 @@ static inline void tcg_gen_add_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) tcg_gen_op3_i32(INDEX_op_add_i32, ret, arg1, arg2); } -static inline void tcg_gen_addi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) -{ - /* some cases can be optimized here */ - if (arg2 == 0) { - tcg_gen_mov_i32(ret, arg1); - } else { - TCGv_i32 t0 = tcg_const_i32(arg2); - tcg_gen_add_i32(ret, arg1, t0); - tcg_temp_free_i32(t0); - } -} - static inline void tcg_gen_sub_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { tcg_gen_op3_i32(INDEX_op_sub_i32, ret, arg1, arg2); } -static inline void tcg_gen_subfi_i32(TCGv_i32 ret, int32_t arg1, TCGv_i32 arg2) -{ - TCGv_i32 t0 = tcg_const_i32(arg1); - tcg_gen_sub_i32(ret, t0, arg2); - tcg_temp_free_i32(t0); -} - -static inline void tcg_gen_subi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) -{ - /* some cases can be optimized here */ - if (arg2 == 0) { - tcg_gen_mov_i32(ret, arg1); - } else { - TCGv_i32 t0 = tcg_const_i32(arg2); - tcg_gen_sub_i32(ret, arg1, t0); - tcg_temp_free_i32(t0); - } -} - static inline void tcg_gen_and_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - if (TCGV_EQUAL_I32(arg1, arg2)) { - tcg_gen_mov_i32(ret, arg1); - } else { - tcg_gen_op3_i32(INDEX_op_and_i32, ret, arg1, arg2); - } -} - -static inline void tcg_gen_andi_i32(TCGv_i32 ret, TCGv_i32 arg1, uint32_t arg2) -{ - TCGv_i32 t0; - /* Some cases can be optimized here. */ - switch (arg2) { - case 0: - tcg_gen_movi_i32(ret, 0); - return; - case 0xffffffffu: - tcg_gen_mov_i32(ret, arg1); - return; - case 0xffu: - /* Don't recurse with tcg_gen_ext8u_i32. */ - if (TCG_TARGET_HAS_ext8u_i32) { - tcg_gen_op2_i32(INDEX_op_ext8u_i32, ret, arg1); - return; - } - break; - case 0xffffu: - if (TCG_TARGET_HAS_ext16u_i32) { - tcg_gen_op2_i32(INDEX_op_ext16u_i32, ret, arg1); - return; - } - break; - } - t0 = tcg_const_i32(arg2); - tcg_gen_and_i32(ret, arg1, t0); - tcg_temp_free_i32(t0); + tcg_gen_op3_i32(INDEX_op_and_i32, ret, arg1, arg2); } static inline void tcg_gen_or_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - if (TCGV_EQUAL_I32(arg1, arg2)) { - tcg_gen_mov_i32(ret, arg1); - } else { - tcg_gen_op3_i32(INDEX_op_or_i32, ret, arg1, arg2); - } -} - -static inline void tcg_gen_ori_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) -{ - /* Some cases can be optimized here. */ - if (arg2 == -1) { - tcg_gen_movi_i32(ret, -1); - } else if (arg2 == 0) { - tcg_gen_mov_i32(ret, arg1); - } else { - TCGv_i32 t0 = tcg_const_i32(arg2); - tcg_gen_or_i32(ret, arg1, t0); - tcg_temp_free_i32(t0); - } + tcg_gen_op3_i32(INDEX_op_or_i32, ret, arg1, arg2); } static inline void tcg_gen_xor_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - if (TCGV_EQUAL_I32(arg1, arg2)) { - tcg_gen_movi_i32(ret, 0); - } else { - tcg_gen_op3_i32(INDEX_op_xor_i32, ret, arg1, arg2); - } -} - -static inline void tcg_gen_xori_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) -{ - /* Some cases can be optimized here. */ - if (arg2 == 0) { - tcg_gen_mov_i32(ret, arg1); - } else if (arg2 == -1 && TCG_TARGET_HAS_not_i32) { - /* Don't recurse with tcg_gen_not_i32. */ - tcg_gen_op2_i32(INDEX_op_not_i32, ret, arg1); - } else { - TCGv_i32 t0 = tcg_const_i32(arg2); - tcg_gen_xor_i32(ret, arg1, t0); - tcg_temp_free_i32(t0); - } + tcg_gen_op3_i32(INDEX_op_xor_i32, ret, arg1, arg2); } static inline void tcg_gen_shl_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) @@ -555,1913 +406,322 @@ static inline void tcg_gen_shl_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) tcg_gen_op3_i32(INDEX_op_shl_i32, ret, arg1, arg2); } -static inline void tcg_gen_shli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) -{ - if (arg2 == 0) { - tcg_gen_mov_i32(ret, arg1); - } else { - TCGv_i32 t0 = tcg_const_i32(arg2); - tcg_gen_shl_i32(ret, arg1, t0); - tcg_temp_free_i32(t0); - } -} - static inline void tcg_gen_shr_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { tcg_gen_op3_i32(INDEX_op_shr_i32, ret, arg1, arg2); } -static inline void tcg_gen_shri_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) -{ - if (arg2 == 0) { - tcg_gen_mov_i32(ret, arg1); - } else { - TCGv_i32 t0 = tcg_const_i32(arg2); - tcg_gen_shr_i32(ret, arg1, t0); - tcg_temp_free_i32(t0); - } -} - static inline void tcg_gen_sar_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { tcg_gen_op3_i32(INDEX_op_sar_i32, ret, arg1, arg2); } -static inline void tcg_gen_sari_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) -{ - if (arg2 == 0) { - tcg_gen_mov_i32(ret, arg1); - } else { - TCGv_i32 t0 = tcg_const_i32(arg2); - tcg_gen_sar_i32(ret, arg1, t0); - tcg_temp_free_i32(t0); - } -} - -static inline void tcg_gen_brcond_i32(TCGCond cond, TCGv_i32 arg1, - TCGv_i32 arg2, int label_index) -{ - if (cond == TCG_COND_ALWAYS) { - tcg_gen_br(label_index); - } else if (cond != TCG_COND_NEVER) { - tcg_gen_op4ii_i32(INDEX_op_brcond_i32, arg1, arg2, cond, label_index); - } -} - -static inline void tcg_gen_brcondi_i32(TCGCond cond, TCGv_i32 arg1, - int32_t arg2, int label_index) -{ - if (cond == TCG_COND_ALWAYS) { - tcg_gen_br(label_index); - } else if (cond != TCG_COND_NEVER) { - TCGv_i32 t0 = tcg_const_i32(arg2); - tcg_gen_brcond_i32(cond, arg1, t0, label_index); - tcg_temp_free_i32(t0); - } -} - -static inline void tcg_gen_setcond_i32(TCGCond cond, TCGv_i32 ret, - TCGv_i32 arg1, TCGv_i32 arg2) -{ - if (cond == TCG_COND_ALWAYS) { - tcg_gen_movi_i32(ret, 1); - } else if (cond == TCG_COND_NEVER) { - tcg_gen_movi_i32(ret, 0); - } else { - tcg_gen_op4i_i32(INDEX_op_setcond_i32, ret, arg1, arg2, cond); - } -} - -static inline void tcg_gen_setcondi_i32(TCGCond cond, TCGv_i32 ret, - TCGv_i32 arg1, int32_t arg2) -{ - if (cond == TCG_COND_ALWAYS) { - tcg_gen_movi_i32(ret, 1); - } else if (cond == TCG_COND_NEVER) { - tcg_gen_movi_i32(ret, 0); - } else { - TCGv_i32 t0 = tcg_const_i32(arg2); - tcg_gen_setcond_i32(cond, ret, arg1, t0); - tcg_temp_free_i32(t0); - } -} - static inline void tcg_gen_mul_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { tcg_gen_op3_i32(INDEX_op_mul_i32, ret, arg1, arg2); } -static inline void tcg_gen_muli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) -{ - TCGv_i32 t0 = tcg_const_i32(arg2); - tcg_gen_mul_i32(ret, arg1, t0); - tcg_temp_free_i32(t0); -} - -static inline void tcg_gen_div_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +static inline void tcg_gen_neg_i32(TCGv_i32 ret, TCGv_i32 arg) { - if (TCG_TARGET_HAS_div_i32) { - tcg_gen_op3_i32(INDEX_op_div_i32, ret, arg1, arg2); - } else if (TCG_TARGET_HAS_div2_i32) { - TCGv_i32 t0 = tcg_temp_new_i32(); - tcg_gen_sari_i32(t0, arg1, 31); - tcg_gen_op5_i32(INDEX_op_div2_i32, ret, t0, arg1, t0, arg2); - tcg_temp_free_i32(t0); - } else { - gen_helper_div_i32(ret, arg1, arg2); - } -} - -static inline void tcg_gen_rem_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - if (TCG_TARGET_HAS_rem_i32) { - tcg_gen_op3_i32(INDEX_op_rem_i32, ret, arg1, arg2); - } else if (TCG_TARGET_HAS_div_i32) { - TCGv_i32 t0 = tcg_temp_new_i32(); - tcg_gen_op3_i32(INDEX_op_div_i32, t0, arg1, arg2); - tcg_gen_mul_i32(t0, t0, arg2); - tcg_gen_sub_i32(ret, arg1, t0); - tcg_temp_free_i32(t0); - } else if (TCG_TARGET_HAS_div2_i32) { - TCGv_i32 t0 = tcg_temp_new_i32(); - tcg_gen_sari_i32(t0, arg1, 31); - tcg_gen_op5_i32(INDEX_op_div2_i32, t0, ret, arg1, t0, arg2); - tcg_temp_free_i32(t0); + if (TCG_TARGET_HAS_neg_i32) { + tcg_gen_op2_i32(INDEX_op_neg_i32, ret, arg); } else { - gen_helper_rem_i32(ret, arg1, arg2); + tcg_gen_subfi_i32(ret, 0, arg); } } -static inline void tcg_gen_divu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +static inline void tcg_gen_not_i32(TCGv_i32 ret, TCGv_i32 arg) { - if (TCG_TARGET_HAS_div_i32) { - tcg_gen_op3_i32(INDEX_op_divu_i32, ret, arg1, arg2); - } else if (TCG_TARGET_HAS_div2_i32) { - TCGv_i32 t0 = tcg_temp_new_i32(); - tcg_gen_movi_i32(t0, 0); - tcg_gen_op5_i32(INDEX_op_divu2_i32, ret, t0, arg1, t0, arg2); - tcg_temp_free_i32(t0); + if (TCG_TARGET_HAS_not_i32) { + tcg_gen_op2_i32(INDEX_op_not_i32, ret, arg); } else { - gen_helper_divu_i32(ret, arg1, arg2); + tcg_gen_xori_i32(ret, arg, -1); } } -static inline void tcg_gen_remu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - if (TCG_TARGET_HAS_rem_i32) { - tcg_gen_op3_i32(INDEX_op_remu_i32, ret, arg1, arg2); - } else if (TCG_TARGET_HAS_div_i32) { - TCGv_i32 t0 = tcg_temp_new_i32(); - tcg_gen_op3_i32(INDEX_op_divu_i32, t0, arg1, arg2); - tcg_gen_mul_i32(t0, t0, arg2); - tcg_gen_sub_i32(ret, arg1, t0); - tcg_temp_free_i32(t0); - } else if (TCG_TARGET_HAS_div2_i32) { - TCGv_i32 t0 = tcg_temp_new_i32(); - tcg_gen_movi_i32(t0, 0); - tcg_gen_op5_i32(INDEX_op_divu2_i32, t0, ret, arg1, t0, arg2); - tcg_temp_free_i32(t0); - } else { - gen_helper_remu_i32(ret, arg1, arg2); - } -} +/* 64 bit ops */ + +void tcg_gen_addi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); +void tcg_gen_subfi_i64(TCGv_i64 ret, int64_t arg1, TCGv_i64 arg2); +void tcg_gen_subi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); +void tcg_gen_andi_i64(TCGv_i64 ret, TCGv_i64 arg1, uint64_t arg2); +void tcg_gen_ori_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); +void tcg_gen_xori_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); +void tcg_gen_shli_i64(TCGv_i64 ret, TCGv_i64 arg1, unsigned arg2); +void tcg_gen_shri_i64(TCGv_i64 ret, TCGv_i64 arg1, unsigned arg2); +void tcg_gen_sari_i64(TCGv_i64 ret, TCGv_i64 arg1, unsigned arg2); +void tcg_gen_muli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); +void tcg_gen_div_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_rem_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_divu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_remu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_andc_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_eqv_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_nand_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_nor_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_orc_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_rotl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_rotli_i64(TCGv_i64 ret, TCGv_i64 arg1, unsigned arg2); +void tcg_gen_rotr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_rotri_i64(TCGv_i64 ret, TCGv_i64 arg1, unsigned arg2); +void tcg_gen_deposit_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2, + unsigned int ofs, unsigned int len); +void tcg_gen_brcond_i64(TCGCond cond, TCGv_i64 arg1, TCGv_i64 arg2, TCGLabel *); +void tcg_gen_brcondi_i64(TCGCond cond, TCGv_i64 arg1, int64_t arg2, TCGLabel *); +void tcg_gen_setcond_i64(TCGCond cond, TCGv_i64 ret, + TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_setcondi_i64(TCGCond cond, TCGv_i64 ret, + TCGv_i64 arg1, int64_t arg2); +void tcg_gen_movcond_i64(TCGCond cond, TCGv_i64 ret, TCGv_i64 c1, + TCGv_i64 c2, TCGv_i64 v1, TCGv_i64 v2); +void tcg_gen_add2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 al, + TCGv_i64 ah, TCGv_i64 bl, TCGv_i64 bh); +void tcg_gen_sub2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 al, + TCGv_i64 ah, TCGv_i64 bl, TCGv_i64 bh); +void tcg_gen_mulu2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_muls2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_not_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_ext8s_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_ext16s_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_ext32s_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_ext8u_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_ext16u_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_ext32u_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_bswap16_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_bswap32_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_bswap64_i64(TCGv_i64 ret, TCGv_i64 arg); -#if TCG_TARGET_REG_BITS == 32 +#if TCG_TARGET_REG_BITS == 64 +static inline void tcg_gen_discard_i64(TCGv_i64 arg) +{ + tcg_gen_op1_i64(INDEX_op_discard, arg); +} static inline void tcg_gen_mov_i64(TCGv_i64 ret, TCGv_i64 arg) { if (!TCGV_EQUAL_I64(ret, arg)) { - tcg_gen_mov_i32(TCGV_LOW(ret), TCGV_LOW(arg)); - tcg_gen_mov_i32(TCGV_HIGH(ret), TCGV_HIGH(arg)); + tcg_gen_op2_i64(INDEX_op_mov_i64, ret, arg); } } static inline void tcg_gen_movi_i64(TCGv_i64 ret, int64_t arg) { - tcg_gen_movi_i32(TCGV_LOW(ret), arg); - tcg_gen_movi_i32(TCGV_HIGH(ret), arg >> 32); + tcg_gen_op2i_i64(INDEX_op_movi_i64, ret, arg); } static inline void tcg_gen_ld8u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { - tcg_gen_ld8u_i32(TCGV_LOW(ret), arg2, offset); - tcg_gen_movi_i32(TCGV_HIGH(ret), 0); + tcg_gen_ldst_op_i64(INDEX_op_ld8u_i64, ret, arg2, offset); } static inline void tcg_gen_ld8s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { - tcg_gen_ld8s_i32(TCGV_LOW(ret), arg2, offset); - tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_HIGH(ret), 31); + tcg_gen_ldst_op_i64(INDEX_op_ld8s_i64, ret, arg2, offset); } static inline void tcg_gen_ld16u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { - tcg_gen_ld16u_i32(TCGV_LOW(ret), arg2, offset); - tcg_gen_movi_i32(TCGV_HIGH(ret), 0); + tcg_gen_ldst_op_i64(INDEX_op_ld16u_i64, ret, arg2, offset); } static inline void tcg_gen_ld16s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { - tcg_gen_ld16s_i32(TCGV_LOW(ret), arg2, offset); - tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); + tcg_gen_ldst_op_i64(INDEX_op_ld16s_i64, ret, arg2, offset); } static inline void tcg_gen_ld32u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { - tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset); - tcg_gen_movi_i32(TCGV_HIGH(ret), 0); + tcg_gen_ldst_op_i64(INDEX_op_ld32u_i64, ret, arg2, offset); } static inline void tcg_gen_ld32s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { - tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset); - tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); + tcg_gen_ldst_op_i64(INDEX_op_ld32s_i64, ret, arg2, offset); } static inline void tcg_gen_ld_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { - /* since arg2 and ret have different types, they cannot be the - same temporary */ -#ifdef HOST_WORDS_BIGENDIAN - tcg_gen_ld_i32(TCGV_HIGH(ret), arg2, offset); - tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset + 4); -#else - tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset); - tcg_gen_ld_i32(TCGV_HIGH(ret), arg2, offset + 4); -#endif + tcg_gen_ldst_op_i64(INDEX_op_ld_i64, ret, arg2, offset); } static inline void tcg_gen_st8_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset) { - tcg_gen_st8_i32(TCGV_LOW(arg1), arg2, offset); + tcg_gen_ldst_op_i64(INDEX_op_st8_i64, arg1, arg2, offset); } static inline void tcg_gen_st16_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset) { - tcg_gen_st16_i32(TCGV_LOW(arg1), arg2, offset); + tcg_gen_ldst_op_i64(INDEX_op_st16_i64, arg1, arg2, offset); } static inline void tcg_gen_st32_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset) { - tcg_gen_st_i32(TCGV_LOW(arg1), arg2, offset); + tcg_gen_ldst_op_i64(INDEX_op_st32_i64, arg1, arg2, offset); } static inline void tcg_gen_st_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset) { -#ifdef HOST_WORDS_BIGENDIAN - tcg_gen_st_i32(TCGV_HIGH(arg1), arg2, offset); - tcg_gen_st_i32(TCGV_LOW(arg1), arg2, offset + 4); -#else - tcg_gen_st_i32(TCGV_LOW(arg1), arg2, offset); - tcg_gen_st_i32(TCGV_HIGH(arg1), arg2, offset + 4); -#endif + tcg_gen_ldst_op_i64(INDEX_op_st_i64, arg1, arg2, offset); } static inline void tcg_gen_add_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - tcg_gen_op6_i32(INDEX_op_add2_i32, TCGV_LOW(ret), TCGV_HIGH(ret), - TCGV_LOW(arg1), TCGV_HIGH(arg1), TCGV_LOW(arg2), - TCGV_HIGH(arg2)); - /* Allow the optimizer room to replace add2 with two moves. */ - tcg_gen_op0(INDEX_op_nop); + tcg_gen_op3_i64(INDEX_op_add_i64, ret, arg1, arg2); } static inline void tcg_gen_sub_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - tcg_gen_op6_i32(INDEX_op_sub2_i32, TCGV_LOW(ret), TCGV_HIGH(ret), - TCGV_LOW(arg1), TCGV_HIGH(arg1), TCGV_LOW(arg2), - TCGV_HIGH(arg2)); - /* Allow the optimizer room to replace sub2 with two moves. */ - tcg_gen_op0(INDEX_op_nop); + tcg_gen_op3_i64(INDEX_op_sub_i64, ret, arg1, arg2); } static inline void tcg_gen_and_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - tcg_gen_and_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); - tcg_gen_and_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); -} - -static inline void tcg_gen_andi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) -{ - tcg_gen_andi_i32(TCGV_LOW(ret), TCGV_LOW(arg1), arg2); - tcg_gen_andi_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), arg2 >> 32); + tcg_gen_op3_i64(INDEX_op_and_i64, ret, arg1, arg2); } static inline void tcg_gen_or_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - tcg_gen_or_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); - tcg_gen_or_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); -} - -static inline void tcg_gen_ori_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) -{ - tcg_gen_ori_i32(TCGV_LOW(ret), TCGV_LOW(arg1), arg2); - tcg_gen_ori_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), arg2 >> 32); + tcg_gen_op3_i64(INDEX_op_or_i64, ret, arg1, arg2); } static inline void tcg_gen_xor_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - tcg_gen_xor_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); - tcg_gen_xor_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); -} - -static inline void tcg_gen_xori_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) -{ - tcg_gen_xori_i32(TCGV_LOW(ret), TCGV_LOW(arg1), arg2); - tcg_gen_xori_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), arg2 >> 32); + tcg_gen_op3_i64(INDEX_op_xor_i64, ret, arg1, arg2); } -/* XXX: use generic code when basic block handling is OK or CPU - specific code (x86) */ static inline void tcg_gen_shl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - gen_helper_shl_i64(ret, arg1, arg2); -} - -static inline void tcg_gen_shli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) -{ - tcg_gen_shifti_i64(ret, arg1, arg2, 0, 0); + tcg_gen_op3_i64(INDEX_op_shl_i64, ret, arg1, arg2); } static inline void tcg_gen_shr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - gen_helper_shr_i64(ret, arg1, arg2); -} - -static inline void tcg_gen_shri_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) -{ - tcg_gen_shifti_i64(ret, arg1, arg2, 1, 0); + tcg_gen_op3_i64(INDEX_op_shr_i64, ret, arg1, arg2); } static inline void tcg_gen_sar_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - gen_helper_sar_i64(ret, arg1, arg2); -} - -static inline void tcg_gen_sari_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) -{ - tcg_gen_shifti_i64(ret, arg1, arg2, 1, 1); -} - -static inline void tcg_gen_brcond_i64(TCGCond cond, TCGv_i64 arg1, - TCGv_i64 arg2, int label_index) -{ - if (cond == TCG_COND_ALWAYS) { - tcg_gen_br(label_index); - } else if (cond != TCG_COND_NEVER) { - tcg_gen_op6ii_i32(INDEX_op_brcond2_i32, - TCGV_LOW(arg1), TCGV_HIGH(arg1), TCGV_LOW(arg2), - TCGV_HIGH(arg2), cond, label_index); - } -} - -static inline void tcg_gen_setcond_i64(TCGCond cond, TCGv_i64 ret, - TCGv_i64 arg1, TCGv_i64 arg2) -{ - if (cond == TCG_COND_ALWAYS) { - tcg_gen_movi_i32(TCGV_LOW(ret), 1); - } else if (cond == TCG_COND_NEVER) { - tcg_gen_movi_i32(TCGV_LOW(ret), 0); - } else { - tcg_gen_op6i_i32(INDEX_op_setcond2_i32, TCGV_LOW(ret), - TCGV_LOW(arg1), TCGV_HIGH(arg1), - TCGV_LOW(arg2), TCGV_HIGH(arg2), cond); - } - tcg_gen_movi_i32(TCGV_HIGH(ret), 0); + tcg_gen_op3_i64(INDEX_op_sar_i64, ret, arg1, arg2); } static inline void tcg_gen_mul_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - TCGv_i64 t0; - TCGv_i32 t1; - - t0 = tcg_temp_new_i64(); - t1 = tcg_temp_new_i32(); - - if (TCG_TARGET_HAS_mulu2_i32) { - tcg_gen_op4_i32(INDEX_op_mulu2_i32, TCGV_LOW(t0), TCGV_HIGH(t0), - TCGV_LOW(arg1), TCGV_LOW(arg2)); - /* Allow the optimizer room to replace mulu2 with two moves. */ - tcg_gen_op0(INDEX_op_nop); - } else { - tcg_debug_assert(TCG_TARGET_HAS_muluh_i32); - tcg_gen_op3_i32(INDEX_op_mul_i32, TCGV_LOW(t0), - TCGV_LOW(arg1), TCGV_LOW(arg2)); - tcg_gen_op3_i32(INDEX_op_muluh_i32, TCGV_HIGH(t0), - TCGV_LOW(arg1), TCGV_LOW(arg2)); - } - - tcg_gen_mul_i32(t1, TCGV_LOW(arg1), TCGV_HIGH(arg2)); - tcg_gen_add_i32(TCGV_HIGH(t0), TCGV_HIGH(t0), t1); - tcg_gen_mul_i32(t1, TCGV_HIGH(arg1), TCGV_LOW(arg2)); - tcg_gen_add_i32(TCGV_HIGH(t0), TCGV_HIGH(t0), t1); - - tcg_gen_mov_i64(ret, t0); - tcg_temp_free_i64(t0); - tcg_temp_free_i32(t1); -} - -static inline void tcg_gen_div_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - gen_helper_div_i64(ret, arg1, arg2); + tcg_gen_op3_i64(INDEX_op_mul_i64, ret, arg1, arg2); } - -static inline void tcg_gen_rem_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +#else /* TCG_TARGET_REG_BITS == 32 */ +static inline void tcg_gen_st8_i64(TCGv_i64 arg1, TCGv_ptr arg2, + tcg_target_long offset) { - gen_helper_rem_i64(ret, arg1, arg2); + tcg_gen_st8_i32(TCGV_LOW(arg1), arg2, offset); } -static inline void tcg_gen_divu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +static inline void tcg_gen_st16_i64(TCGv_i64 arg1, TCGv_ptr arg2, + tcg_target_long offset) { - gen_helper_divu_i64(ret, arg1, arg2); + tcg_gen_st16_i32(TCGV_LOW(arg1), arg2, offset); } -static inline void tcg_gen_remu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +static inline void tcg_gen_st32_i64(TCGv_i64 arg1, TCGv_ptr arg2, + tcg_target_long offset) { - gen_helper_remu_i64(ret, arg1, arg2); + tcg_gen_st_i32(TCGV_LOW(arg1), arg2, offset); } -#else - -static inline void tcg_gen_mov_i64(TCGv_i64 ret, TCGv_i64 arg) +static inline void tcg_gen_add_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - if (!TCGV_EQUAL_I64(ret, arg)) - tcg_gen_op2_i64(INDEX_op_mov_i64, ret, arg); + tcg_gen_add2_i32(TCGV_LOW(ret), TCGV_HIGH(ret), TCGV_LOW(arg1), + TCGV_HIGH(arg1), TCGV_LOW(arg2), TCGV_HIGH(arg2)); } -static inline void tcg_gen_movi_i64(TCGv_i64 ret, int64_t arg) +static inline void tcg_gen_sub_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - tcg_gen_op2i_i64(INDEX_op_movi_i64, ret, arg); -} + tcg_gen_sub2_i32(TCGV_LOW(ret), TCGV_HIGH(ret), TCGV_LOW(arg1), + TCGV_HIGH(arg1), TCGV_LOW(arg2), TCGV_HIGH(arg2)); +} + +void tcg_gen_discard_i64(TCGv_i64 arg); +void tcg_gen_mov_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_movi_i64(TCGv_i64 ret, int64_t arg); +void tcg_gen_ld8u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_ld8s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_ld16u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_ld16s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_ld32u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_ld32s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_ld_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_st_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_and_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_or_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_xor_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_shl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_shr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_sar_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_mul_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +#endif /* TCG_TARGET_REG_BITS */ -static inline void tcg_gen_ld8u_i64(TCGv_i64 ret, TCGv_ptr arg2, - tcg_target_long offset) +static inline void tcg_gen_neg_i64(TCGv_i64 ret, TCGv_i64 arg) { - tcg_gen_ldst_op_i64(INDEX_op_ld8u_i64, ret, arg2, offset); + if (TCG_TARGET_HAS_neg_i64) { + tcg_gen_op2_i64(INDEX_op_neg_i64, ret, arg); + } else { + tcg_gen_subfi_i64(ret, 0, arg); + } } -static inline void tcg_gen_ld8s_i64(TCGv_i64 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_ld8s_i64, ret, arg2, offset); -} +/* Size changing operations. */ -static inline void tcg_gen_ld16u_i64(TCGv_i64 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_ld16u_i64, ret, arg2, offset); -} +void tcg_gen_extu_i32_i64(TCGv_i64 ret, TCGv_i32 arg); +void tcg_gen_ext_i32_i64(TCGv_i64 ret, TCGv_i32 arg); +void tcg_gen_concat_i32_i64(TCGv_i64 dest, TCGv_i32 low, TCGv_i32 high); +void tcg_gen_trunc_shr_i64_i32(TCGv_i32 ret, TCGv_i64 arg, unsigned int c); +void tcg_gen_extr_i64_i32(TCGv_i32 lo, TCGv_i32 hi, TCGv_i64 arg); +void tcg_gen_extr32_i64(TCGv_i64 lo, TCGv_i64 hi, TCGv_i64 arg); -static inline void tcg_gen_ld16s_i64(TCGv_i64 ret, TCGv_ptr arg2, - tcg_target_long offset) +static inline void tcg_gen_concat32_i64(TCGv_i64 ret, TCGv_i64 lo, TCGv_i64 hi) { - tcg_gen_ldst_op_i64(INDEX_op_ld16s_i64, ret, arg2, offset); + tcg_gen_deposit_i64(ret, lo, hi, 32, 32); } -static inline void tcg_gen_ld32u_i64(TCGv_i64 ret, TCGv_ptr arg2, - tcg_target_long offset) +static inline void tcg_gen_trunc_i64_i32(TCGv_i32 ret, TCGv_i64 arg) { - tcg_gen_ldst_op_i64(INDEX_op_ld32u_i64, ret, arg2, offset); + tcg_gen_trunc_shr_i64_i32(ret, arg, 0); } -static inline void tcg_gen_ld32s_i64(TCGv_i64 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_ld32s_i64, ret, arg2, offset); -} +/* QEMU specific operations. */ -static inline void tcg_gen_ld_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_ld_i64, ret, arg2, offset); -} +#ifndef TARGET_LONG_BITS +#error must include QEMU headers +#endif -static inline void tcg_gen_st8_i64(TCGv_i64 arg1, TCGv_ptr arg2, - tcg_target_long offset) +/* debug info: write the PC of the corresponding QEMU CPU instruction */ +static inline void tcg_gen_debug_insn_start(uint64_t pc) { - tcg_gen_ldst_op_i64(INDEX_op_st8_i64, arg1, arg2, offset); + /* XXX: must really use a 32 bit size for TCGArg in all cases */ +#if TARGET_LONG_BITS > TCG_TARGET_REG_BITS + tcg_gen_op2ii(INDEX_op_debug_insn_start, + (uint32_t)(pc), (uint32_t)(pc >> 32)); +#else + tcg_gen_op1i(INDEX_op_debug_insn_start, pc); +#endif } -static inline void tcg_gen_st16_i64(TCGv_i64 arg1, TCGv_ptr arg2, - tcg_target_long offset) +static inline void tcg_gen_exit_tb(uintptr_t val) { - tcg_gen_ldst_op_i64(INDEX_op_st16_i64, arg1, arg2, offset); + tcg_gen_op1i(INDEX_op_exit_tb, val); } -static inline void tcg_gen_st32_i64(TCGv_i64 arg1, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_st32_i64, arg1, arg2, offset); -} - -static inline void tcg_gen_st_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_st_i64, arg1, arg2, offset); -} - -static inline void tcg_gen_add_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_op3_i64(INDEX_op_add_i64, ret, arg1, arg2); -} - -static inline void tcg_gen_sub_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_op3_i64(INDEX_op_sub_i64, ret, arg1, arg2); -} - -static inline void tcg_gen_and_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - if (TCGV_EQUAL_I64(arg1, arg2)) { - tcg_gen_mov_i64(ret, arg1); - } else { - tcg_gen_op3_i64(INDEX_op_and_i64, ret, arg1, arg2); - } -} - -static inline void tcg_gen_andi_i64(TCGv_i64 ret, TCGv_i64 arg1, uint64_t arg2) -{ - TCGv_i64 t0; - /* Some cases can be optimized here. */ - switch (arg2) { - case 0: - tcg_gen_movi_i64(ret, 0); - return; - case 0xffffffffffffffffull: - tcg_gen_mov_i64(ret, arg1); - return; - case 0xffull: - /* Don't recurse with tcg_gen_ext8u_i32. */ - if (TCG_TARGET_HAS_ext8u_i64) { - tcg_gen_op2_i64(INDEX_op_ext8u_i64, ret, arg1); - return; - } - break; - case 0xffffu: - if (TCG_TARGET_HAS_ext16u_i64) { - tcg_gen_op2_i64(INDEX_op_ext16u_i64, ret, arg1); - return; - } - break; - case 0xffffffffull: - if (TCG_TARGET_HAS_ext32u_i64) { - tcg_gen_op2_i64(INDEX_op_ext32u_i64, ret, arg1); - return; - } - break; - } - t0 = tcg_const_i64(arg2); - tcg_gen_and_i64(ret, arg1, t0); - tcg_temp_free_i64(t0); -} - -static inline void tcg_gen_or_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - if (TCGV_EQUAL_I64(arg1, arg2)) { - tcg_gen_mov_i64(ret, arg1); - } else { - tcg_gen_op3_i64(INDEX_op_or_i64, ret, arg1, arg2); - } -} - -static inline void tcg_gen_ori_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) -{ - /* Some cases can be optimized here. */ - if (arg2 == -1) { - tcg_gen_movi_i64(ret, -1); - } else if (arg2 == 0) { - tcg_gen_mov_i64(ret, arg1); - } else { - TCGv_i64 t0 = tcg_const_i64(arg2); - tcg_gen_or_i64(ret, arg1, t0); - tcg_temp_free_i64(t0); - } -} - -static inline void tcg_gen_xor_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - if (TCGV_EQUAL_I64(arg1, arg2)) { - tcg_gen_movi_i64(ret, 0); - } else { - tcg_gen_op3_i64(INDEX_op_xor_i64, ret, arg1, arg2); - } -} - -static inline void tcg_gen_xori_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) -{ - /* Some cases can be optimized here. */ - if (arg2 == 0) { - tcg_gen_mov_i64(ret, arg1); - } else if (arg2 == -1 && TCG_TARGET_HAS_not_i64) { - /* Don't recurse with tcg_gen_not_i64. */ - tcg_gen_op2_i64(INDEX_op_not_i64, ret, arg1); - } else { - TCGv_i64 t0 = tcg_const_i64(arg2); - tcg_gen_xor_i64(ret, arg1, t0); - tcg_temp_free_i64(t0); - } -} - -static inline void tcg_gen_shl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_op3_i64(INDEX_op_shl_i64, ret, arg1, arg2); -} - -static inline void tcg_gen_shli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) -{ - if (arg2 == 0) { - tcg_gen_mov_i64(ret, arg1); - } else { - TCGv_i64 t0 = tcg_const_i64(arg2); - tcg_gen_shl_i64(ret, arg1, t0); - tcg_temp_free_i64(t0); - } -} - -static inline void tcg_gen_shr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_op3_i64(INDEX_op_shr_i64, ret, arg1, arg2); -} - -static inline void tcg_gen_shri_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) -{ - if (arg2 == 0) { - tcg_gen_mov_i64(ret, arg1); - } else { - TCGv_i64 t0 = tcg_const_i64(arg2); - tcg_gen_shr_i64(ret, arg1, t0); - tcg_temp_free_i64(t0); - } -} - -static inline void tcg_gen_sar_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_op3_i64(INDEX_op_sar_i64, ret, arg1, arg2); -} - -static inline void tcg_gen_sari_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) -{ - if (arg2 == 0) { - tcg_gen_mov_i64(ret, arg1); - } else { - TCGv_i64 t0 = tcg_const_i64(arg2); - tcg_gen_sar_i64(ret, arg1, t0); - tcg_temp_free_i64(t0); - } -} - -static inline void tcg_gen_brcond_i64(TCGCond cond, TCGv_i64 arg1, - TCGv_i64 arg2, int label_index) -{ - if (cond == TCG_COND_ALWAYS) { - tcg_gen_br(label_index); - } else if (cond != TCG_COND_NEVER) { - tcg_gen_op4ii_i64(INDEX_op_brcond_i64, arg1, arg2, cond, label_index); - } -} - -static inline void tcg_gen_setcond_i64(TCGCond cond, TCGv_i64 ret, - TCGv_i64 arg1, TCGv_i64 arg2) -{ - if (cond == TCG_COND_ALWAYS) { - tcg_gen_movi_i64(ret, 1); - } else if (cond == TCG_COND_NEVER) { - tcg_gen_movi_i64(ret, 0); - } else { - tcg_gen_op4i_i64(INDEX_op_setcond_i64, ret, arg1, arg2, cond); - } -} - -static inline void tcg_gen_mul_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_op3_i64(INDEX_op_mul_i64, ret, arg1, arg2); -} - -static inline void tcg_gen_div_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - if (TCG_TARGET_HAS_div_i64) { - tcg_gen_op3_i64(INDEX_op_div_i64, ret, arg1, arg2); - } else if (TCG_TARGET_HAS_div2_i64) { - TCGv_i64 t0 = tcg_temp_new_i64(); - tcg_gen_sari_i64(t0, arg1, 63); - tcg_gen_op5_i64(INDEX_op_div2_i64, ret, t0, arg1, t0, arg2); - tcg_temp_free_i64(t0); - } else { - gen_helper_div_i64(ret, arg1, arg2); - } -} - -static inline void tcg_gen_rem_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - if (TCG_TARGET_HAS_rem_i64) { - tcg_gen_op3_i64(INDEX_op_rem_i64, ret, arg1, arg2); - } else if (TCG_TARGET_HAS_div_i64) { - TCGv_i64 t0 = tcg_temp_new_i64(); - tcg_gen_op3_i64(INDEX_op_div_i64, t0, arg1, arg2); - tcg_gen_mul_i64(t0, t0, arg2); - tcg_gen_sub_i64(ret, arg1, t0); - tcg_temp_free_i64(t0); - } else if (TCG_TARGET_HAS_div2_i64) { - TCGv_i64 t0 = tcg_temp_new_i64(); - tcg_gen_sari_i64(t0, arg1, 63); - tcg_gen_op5_i64(INDEX_op_div2_i64, t0, ret, arg1, t0, arg2); - tcg_temp_free_i64(t0); - } else { - gen_helper_rem_i64(ret, arg1, arg2); - } -} - -static inline void tcg_gen_divu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - if (TCG_TARGET_HAS_div_i64) { - tcg_gen_op3_i64(INDEX_op_divu_i64, ret, arg1, arg2); - } else if (TCG_TARGET_HAS_div2_i64) { - TCGv_i64 t0 = tcg_temp_new_i64(); - tcg_gen_movi_i64(t0, 0); - tcg_gen_op5_i64(INDEX_op_divu2_i64, ret, t0, arg1, t0, arg2); - tcg_temp_free_i64(t0); - } else { - gen_helper_divu_i64(ret, arg1, arg2); - } -} - -static inline void tcg_gen_remu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - if (TCG_TARGET_HAS_rem_i64) { - tcg_gen_op3_i64(INDEX_op_remu_i64, ret, arg1, arg2); - } else if (TCG_TARGET_HAS_div_i64) { - TCGv_i64 t0 = tcg_temp_new_i64(); - tcg_gen_op3_i64(INDEX_op_divu_i64, t0, arg1, arg2); - tcg_gen_mul_i64(t0, t0, arg2); - tcg_gen_sub_i64(ret, arg1, t0); - tcg_temp_free_i64(t0); - } else if (TCG_TARGET_HAS_div2_i64) { - TCGv_i64 t0 = tcg_temp_new_i64(); - tcg_gen_movi_i64(t0, 0); - tcg_gen_op5_i64(INDEX_op_divu2_i64, t0, ret, arg1, t0, arg2); - tcg_temp_free_i64(t0); - } else { - gen_helper_remu_i64(ret, arg1, arg2); - } -} -#endif /* TCG_TARGET_REG_BITS == 32 */ - -static inline void tcg_gen_addi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) -{ - /* some cases can be optimized here */ - if (arg2 == 0) { - tcg_gen_mov_i64(ret, arg1); - } else { - TCGv_i64 t0 = tcg_const_i64(arg2); - tcg_gen_add_i64(ret, arg1, t0); - tcg_temp_free_i64(t0); - } -} - -static inline void tcg_gen_subfi_i64(TCGv_i64 ret, int64_t arg1, TCGv_i64 arg2) -{ - TCGv_i64 t0 = tcg_const_i64(arg1); - tcg_gen_sub_i64(ret, t0, arg2); - tcg_temp_free_i64(t0); -} - -static inline void tcg_gen_subi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) -{ - /* some cases can be optimized here */ - if (arg2 == 0) { - tcg_gen_mov_i64(ret, arg1); - } else { - TCGv_i64 t0 = tcg_const_i64(arg2); - tcg_gen_sub_i64(ret, arg1, t0); - tcg_temp_free_i64(t0); - } -} -static inline void tcg_gen_brcondi_i64(TCGCond cond, TCGv_i64 arg1, - int64_t arg2, int label_index) -{ - if (cond == TCG_COND_ALWAYS) { - tcg_gen_br(label_index); - } else if (cond != TCG_COND_NEVER) { - TCGv_i64 t0 = tcg_const_i64(arg2); - tcg_gen_brcond_i64(cond, arg1, t0, label_index); - tcg_temp_free_i64(t0); - } -} - -static inline void tcg_gen_setcondi_i64(TCGCond cond, TCGv_i64 ret, - TCGv_i64 arg1, int64_t arg2) -{ - TCGv_i64 t0 = tcg_const_i64(arg2); - tcg_gen_setcond_i64(cond, ret, arg1, t0); - tcg_temp_free_i64(t0); -} - -static inline void tcg_gen_muli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) -{ - TCGv_i64 t0 = tcg_const_i64(arg2); - tcg_gen_mul_i64(ret, arg1, t0); - tcg_temp_free_i64(t0); -} - - -/***************************************/ -/* optional operations */ - -static inline void tcg_gen_ext8s_i32(TCGv_i32 ret, TCGv_i32 arg) -{ - if (TCG_TARGET_HAS_ext8s_i32) { - tcg_gen_op2_i32(INDEX_op_ext8s_i32, ret, arg); - } else { - tcg_gen_shli_i32(ret, arg, 24); - tcg_gen_sari_i32(ret, ret, 24); - } -} - -static inline void tcg_gen_ext16s_i32(TCGv_i32 ret, TCGv_i32 arg) -{ - if (TCG_TARGET_HAS_ext16s_i32) { - tcg_gen_op2_i32(INDEX_op_ext16s_i32, ret, arg); - } else { - tcg_gen_shli_i32(ret, arg, 16); - tcg_gen_sari_i32(ret, ret, 16); - } -} - -static inline void tcg_gen_ext8u_i32(TCGv_i32 ret, TCGv_i32 arg) -{ - if (TCG_TARGET_HAS_ext8u_i32) { - tcg_gen_op2_i32(INDEX_op_ext8u_i32, ret, arg); - } else { - tcg_gen_andi_i32(ret, arg, 0xffu); - } -} - -static inline void tcg_gen_ext16u_i32(TCGv_i32 ret, TCGv_i32 arg) -{ - if (TCG_TARGET_HAS_ext16u_i32) { - tcg_gen_op2_i32(INDEX_op_ext16u_i32, ret, arg); - } else { - tcg_gen_andi_i32(ret, arg, 0xffffu); - } -} - -/* Note: we assume the two high bytes are set to zero */ -static inline void tcg_gen_bswap16_i32(TCGv_i32 ret, TCGv_i32 arg) -{ - if (TCG_TARGET_HAS_bswap16_i32) { - tcg_gen_op2_i32(INDEX_op_bswap16_i32, ret, arg); - } else { - TCGv_i32 t0 = tcg_temp_new_i32(); - - tcg_gen_ext8u_i32(t0, arg); - tcg_gen_shli_i32(t0, t0, 8); - tcg_gen_shri_i32(ret, arg, 8); - tcg_gen_or_i32(ret, ret, t0); - tcg_temp_free_i32(t0); - } -} - -static inline void tcg_gen_bswap32_i32(TCGv_i32 ret, TCGv_i32 arg) -{ - if (TCG_TARGET_HAS_bswap32_i32) { - tcg_gen_op2_i32(INDEX_op_bswap32_i32, ret, arg); - } else { - TCGv_i32 t0, t1; - t0 = tcg_temp_new_i32(); - t1 = tcg_temp_new_i32(); - - tcg_gen_shli_i32(t0, arg, 24); - - tcg_gen_andi_i32(t1, arg, 0x0000ff00); - tcg_gen_shli_i32(t1, t1, 8); - tcg_gen_or_i32(t0, t0, t1); - - tcg_gen_shri_i32(t1, arg, 8); - tcg_gen_andi_i32(t1, t1, 0x0000ff00); - tcg_gen_or_i32(t0, t0, t1); - - tcg_gen_shri_i32(t1, arg, 24); - tcg_gen_or_i32(ret, t0, t1); - tcg_temp_free_i32(t0); - tcg_temp_free_i32(t1); - } -} - -#if TCG_TARGET_REG_BITS == 32 -static inline void tcg_gen_ext8s_i64(TCGv_i64 ret, TCGv_i64 arg) -{ - tcg_gen_ext8s_i32(TCGV_LOW(ret), TCGV_LOW(arg)); - tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); -} - -static inline void tcg_gen_ext16s_i64(TCGv_i64 ret, TCGv_i64 arg) -{ - tcg_gen_ext16s_i32(TCGV_LOW(ret), TCGV_LOW(arg)); - tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); -} - -static inline void tcg_gen_ext32s_i64(TCGv_i64 ret, TCGv_i64 arg) -{ - tcg_gen_mov_i32(TCGV_LOW(ret), TCGV_LOW(arg)); - tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); -} - -static inline void tcg_gen_ext8u_i64(TCGv_i64 ret, TCGv_i64 arg) -{ - tcg_gen_ext8u_i32(TCGV_LOW(ret), TCGV_LOW(arg)); - tcg_gen_movi_i32(TCGV_HIGH(ret), 0); -} - -static inline void tcg_gen_ext16u_i64(TCGv_i64 ret, TCGv_i64 arg) -{ - tcg_gen_ext16u_i32(TCGV_LOW(ret), TCGV_LOW(arg)); - tcg_gen_movi_i32(TCGV_HIGH(ret), 0); -} - -static inline void tcg_gen_ext32u_i64(TCGv_i64 ret, TCGv_i64 arg) -{ - tcg_gen_mov_i32(TCGV_LOW(ret), TCGV_LOW(arg)); - tcg_gen_movi_i32(TCGV_HIGH(ret), 0); -} - -static inline void tcg_gen_trunc_shr_i64_i32(TCGv_i32 ret, TCGv_i64 arg, - unsigned int count) -{ - tcg_debug_assert(count < 64); - if (count >= 32) { - tcg_gen_shri_i32(ret, TCGV_HIGH(arg), count - 32); - } else if (count == 0) { - tcg_gen_mov_i32(ret, TCGV_LOW(arg)); - } else { - TCGv_i64 t = tcg_temp_new_i64(); - tcg_gen_shri_i64(t, arg, count); - tcg_gen_mov_i32(ret, TCGV_LOW(t)); - tcg_temp_free_i64(t); - } -} - -static inline void tcg_gen_extu_i32_i64(TCGv_i64 ret, TCGv_i32 arg) -{ - tcg_gen_mov_i32(TCGV_LOW(ret), arg); - tcg_gen_movi_i32(TCGV_HIGH(ret), 0); -} - -static inline void tcg_gen_ext_i32_i64(TCGv_i64 ret, TCGv_i32 arg) -{ - tcg_gen_mov_i32(TCGV_LOW(ret), arg); - tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); -} - -/* Note: we assume the six high bytes are set to zero */ -static inline void tcg_gen_bswap16_i64(TCGv_i64 ret, TCGv_i64 arg) -{ - tcg_gen_mov_i32(TCGV_HIGH(ret), TCGV_HIGH(arg)); - tcg_gen_bswap16_i32(TCGV_LOW(ret), TCGV_LOW(arg)); -} - -/* Note: we assume the four high bytes are set to zero */ -static inline void tcg_gen_bswap32_i64(TCGv_i64 ret, TCGv_i64 arg) -{ - tcg_gen_mov_i32(TCGV_HIGH(ret), TCGV_HIGH(arg)); - tcg_gen_bswap32_i32(TCGV_LOW(ret), TCGV_LOW(arg)); -} - -static inline void tcg_gen_bswap64_i64(TCGv_i64 ret, TCGv_i64 arg) -{ - TCGv_i32 t0, t1; - t0 = tcg_temp_new_i32(); - t1 = tcg_temp_new_i32(); - - tcg_gen_bswap32_i32(t0, TCGV_LOW(arg)); - tcg_gen_bswap32_i32(t1, TCGV_HIGH(arg)); - tcg_gen_mov_i32(TCGV_LOW(ret), t1); - tcg_gen_mov_i32(TCGV_HIGH(ret), t0); - tcg_temp_free_i32(t0); - tcg_temp_free_i32(t1); -} -#else - -static inline void tcg_gen_ext8s_i64(TCGv_i64 ret, TCGv_i64 arg) -{ - if (TCG_TARGET_HAS_ext8s_i64) { - tcg_gen_op2_i64(INDEX_op_ext8s_i64, ret, arg); - } else { - tcg_gen_shli_i64(ret, arg, 56); - tcg_gen_sari_i64(ret, ret, 56); - } -} - -static inline void tcg_gen_ext16s_i64(TCGv_i64 ret, TCGv_i64 arg) -{ - if (TCG_TARGET_HAS_ext16s_i64) { - tcg_gen_op2_i64(INDEX_op_ext16s_i64, ret, arg); - } else { - tcg_gen_shli_i64(ret, arg, 48); - tcg_gen_sari_i64(ret, ret, 48); - } -} - -static inline void tcg_gen_ext32s_i64(TCGv_i64 ret, TCGv_i64 arg) -{ - if (TCG_TARGET_HAS_ext32s_i64) { - tcg_gen_op2_i64(INDEX_op_ext32s_i64, ret, arg); - } else { - tcg_gen_shli_i64(ret, arg, 32); - tcg_gen_sari_i64(ret, ret, 32); - } -} - -static inline void tcg_gen_ext8u_i64(TCGv_i64 ret, TCGv_i64 arg) -{ - if (TCG_TARGET_HAS_ext8u_i64) { - tcg_gen_op2_i64(INDEX_op_ext8u_i64, ret, arg); - } else { - tcg_gen_andi_i64(ret, arg, 0xffu); - } -} - -static inline void tcg_gen_ext16u_i64(TCGv_i64 ret, TCGv_i64 arg) -{ - if (TCG_TARGET_HAS_ext16u_i64) { - tcg_gen_op2_i64(INDEX_op_ext16u_i64, ret, arg); - } else { - tcg_gen_andi_i64(ret, arg, 0xffffu); - } -} - -static inline void tcg_gen_ext32u_i64(TCGv_i64 ret, TCGv_i64 arg) -{ - if (TCG_TARGET_HAS_ext32u_i64) { - tcg_gen_op2_i64(INDEX_op_ext32u_i64, ret, arg); - } else { - tcg_gen_andi_i64(ret, arg, 0xffffffffu); - } -} - -static inline void tcg_gen_trunc_shr_i64_i32(TCGv_i32 ret, TCGv_i64 arg, - unsigned int count) -{ - tcg_debug_assert(count < 64); - if (TCG_TARGET_HAS_trunc_shr_i32) { - tcg_gen_op3i_i32(INDEX_op_trunc_shr_i32, ret, - MAKE_TCGV_I32(GET_TCGV_I64(arg)), count); - } else if (count == 0) { - tcg_gen_mov_i32(ret, MAKE_TCGV_I32(GET_TCGV_I64(arg))); - } else { - TCGv_i64 t = tcg_temp_new_i64(); - tcg_gen_shri_i64(t, arg, count); - tcg_gen_mov_i32(ret, MAKE_TCGV_I32(GET_TCGV_I64(t))); - tcg_temp_free_i64(t); - } -} - -/* Note: we assume the target supports move between 32 and 64 bit - registers */ -static inline void tcg_gen_extu_i32_i64(TCGv_i64 ret, TCGv_i32 arg) -{ - tcg_gen_ext32u_i64(ret, MAKE_TCGV_I64(GET_TCGV_I32(arg))); -} - -/* Note: we assume the target supports move between 32 and 64 bit - registers */ -static inline void tcg_gen_ext_i32_i64(TCGv_i64 ret, TCGv_i32 arg) -{ - tcg_gen_ext32s_i64(ret, MAKE_TCGV_I64(GET_TCGV_I32(arg))); -} - -/* Note: we assume the six high bytes are set to zero */ -static inline void tcg_gen_bswap16_i64(TCGv_i64 ret, TCGv_i64 arg) -{ - if (TCG_TARGET_HAS_bswap16_i64) { - tcg_gen_op2_i64(INDEX_op_bswap16_i64, ret, arg); - } else { - TCGv_i64 t0 = tcg_temp_new_i64(); - - tcg_gen_ext8u_i64(t0, arg); - tcg_gen_shli_i64(t0, t0, 8); - tcg_gen_shri_i64(ret, arg, 8); - tcg_gen_or_i64(ret, ret, t0); - tcg_temp_free_i64(t0); - } -} - -/* Note: we assume the four high bytes are set to zero */ -static inline void tcg_gen_bswap32_i64(TCGv_i64 ret, TCGv_i64 arg) -{ - if (TCG_TARGET_HAS_bswap32_i64) { - tcg_gen_op2_i64(INDEX_op_bswap32_i64, ret, arg); - } else { - TCGv_i64 t0, t1; - t0 = tcg_temp_new_i64(); - t1 = tcg_temp_new_i64(); - - tcg_gen_shli_i64(t0, arg, 24); - tcg_gen_ext32u_i64(t0, t0); - - tcg_gen_andi_i64(t1, arg, 0x0000ff00); - tcg_gen_shli_i64(t1, t1, 8); - tcg_gen_or_i64(t0, t0, t1); - - tcg_gen_shri_i64(t1, arg, 8); - tcg_gen_andi_i64(t1, t1, 0x0000ff00); - tcg_gen_or_i64(t0, t0, t1); - - tcg_gen_shri_i64(t1, arg, 24); - tcg_gen_or_i64(ret, t0, t1); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - } -} - -static inline void tcg_gen_bswap64_i64(TCGv_i64 ret, TCGv_i64 arg) -{ - if (TCG_TARGET_HAS_bswap64_i64) { - tcg_gen_op2_i64(INDEX_op_bswap64_i64, ret, arg); - } else { - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); - - tcg_gen_shli_i64(t0, arg, 56); - - tcg_gen_andi_i64(t1, arg, 0x0000ff00); - tcg_gen_shli_i64(t1, t1, 40); - tcg_gen_or_i64(t0, t0, t1); - - tcg_gen_andi_i64(t1, arg, 0x00ff0000); - tcg_gen_shli_i64(t1, t1, 24); - tcg_gen_or_i64(t0, t0, t1); - - tcg_gen_andi_i64(t1, arg, 0xff000000); - tcg_gen_shli_i64(t1, t1, 8); - tcg_gen_or_i64(t0, t0, t1); - - tcg_gen_shri_i64(t1, arg, 8); - tcg_gen_andi_i64(t1, t1, 0xff000000); - tcg_gen_or_i64(t0, t0, t1); - - tcg_gen_shri_i64(t1, arg, 24); - tcg_gen_andi_i64(t1, t1, 0x00ff0000); - tcg_gen_or_i64(t0, t0, t1); - - tcg_gen_shri_i64(t1, arg, 40); - tcg_gen_andi_i64(t1, t1, 0x0000ff00); - tcg_gen_or_i64(t0, t0, t1); - - tcg_gen_shri_i64(t1, arg, 56); - tcg_gen_or_i64(ret, t0, t1); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - } -} - -#endif - -static inline void tcg_gen_neg_i32(TCGv_i32 ret, TCGv_i32 arg) -{ - if (TCG_TARGET_HAS_neg_i32) { - tcg_gen_op2_i32(INDEX_op_neg_i32, ret, arg); - } else { - TCGv_i32 t0 = tcg_const_i32(0); - tcg_gen_sub_i32(ret, t0, arg); - tcg_temp_free_i32(t0); - } -} - -static inline void tcg_gen_neg_i64(TCGv_i64 ret, TCGv_i64 arg) -{ - if (TCG_TARGET_HAS_neg_i64) { - tcg_gen_op2_i64(INDEX_op_neg_i64, ret, arg); - } else { - TCGv_i64 t0 = tcg_const_i64(0); - tcg_gen_sub_i64(ret, t0, arg); - tcg_temp_free_i64(t0); - } -} - -static inline void tcg_gen_not_i32(TCGv_i32 ret, TCGv_i32 arg) -{ - if (TCG_TARGET_HAS_not_i32) { - tcg_gen_op2_i32(INDEX_op_not_i32, ret, arg); - } else { - tcg_gen_xori_i32(ret, arg, -1); - } -} - -static inline void tcg_gen_not_i64(TCGv_i64 ret, TCGv_i64 arg) -{ -#if TCG_TARGET_REG_BITS == 64 - if (TCG_TARGET_HAS_not_i64) { - tcg_gen_op2_i64(INDEX_op_not_i64, ret, arg); - } else { - tcg_gen_xori_i64(ret, arg, -1); - } -#else - tcg_gen_not_i32(TCGV_LOW(ret), TCGV_LOW(arg)); - tcg_gen_not_i32(TCGV_HIGH(ret), TCGV_HIGH(arg)); -#endif -} - -static inline void tcg_gen_discard_i32(TCGv_i32 arg) -{ - tcg_gen_op1_i32(INDEX_op_discard, arg); -} - -static inline void tcg_gen_discard_i64(TCGv_i64 arg) -{ -#if TCG_TARGET_REG_BITS == 32 - tcg_gen_discard_i32(TCGV_LOW(arg)); - tcg_gen_discard_i32(TCGV_HIGH(arg)); -#else - tcg_gen_op1_i64(INDEX_op_discard, arg); -#endif -} - -static inline void tcg_gen_andc_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - if (TCG_TARGET_HAS_andc_i32) { - tcg_gen_op3_i32(INDEX_op_andc_i32, ret, arg1, arg2); - } else { - TCGv_i32 t0 = tcg_temp_new_i32(); - tcg_gen_not_i32(t0, arg2); - tcg_gen_and_i32(ret, arg1, t0); - tcg_temp_free_i32(t0); - } -} - -static inline void tcg_gen_andc_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ -#if TCG_TARGET_REG_BITS == 64 - if (TCG_TARGET_HAS_andc_i64) { - tcg_gen_op3_i64(INDEX_op_andc_i64, ret, arg1, arg2); - } else { - TCGv_i64 t0 = tcg_temp_new_i64(); - tcg_gen_not_i64(t0, arg2); - tcg_gen_and_i64(ret, arg1, t0); - tcg_temp_free_i64(t0); - } -#else - tcg_gen_andc_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); - tcg_gen_andc_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); -#endif -} - -static inline void tcg_gen_eqv_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - if (TCG_TARGET_HAS_eqv_i32) { - tcg_gen_op3_i32(INDEX_op_eqv_i32, ret, arg1, arg2); - } else { - tcg_gen_xor_i32(ret, arg1, arg2); - tcg_gen_not_i32(ret, ret); - } -} - -static inline void tcg_gen_eqv_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ -#if TCG_TARGET_REG_BITS == 64 - if (TCG_TARGET_HAS_eqv_i64) { - tcg_gen_op3_i64(INDEX_op_eqv_i64, ret, arg1, arg2); - } else { - tcg_gen_xor_i64(ret, arg1, arg2); - tcg_gen_not_i64(ret, ret); - } -#else - tcg_gen_eqv_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); - tcg_gen_eqv_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); -#endif -} - -static inline void tcg_gen_nand_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - if (TCG_TARGET_HAS_nand_i32) { - tcg_gen_op3_i32(INDEX_op_nand_i32, ret, arg1, arg2); - } else { - tcg_gen_and_i32(ret, arg1, arg2); - tcg_gen_not_i32(ret, ret); - } -} - -static inline void tcg_gen_nand_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ -#if TCG_TARGET_REG_BITS == 64 - if (TCG_TARGET_HAS_nand_i64) { - tcg_gen_op3_i64(INDEX_op_nand_i64, ret, arg1, arg2); - } else { - tcg_gen_and_i64(ret, arg1, arg2); - tcg_gen_not_i64(ret, ret); - } -#else - tcg_gen_nand_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); - tcg_gen_nand_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); -#endif -} - -static inline void tcg_gen_nor_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - if (TCG_TARGET_HAS_nor_i32) { - tcg_gen_op3_i32(INDEX_op_nor_i32, ret, arg1, arg2); - } else { - tcg_gen_or_i32(ret, arg1, arg2); - tcg_gen_not_i32(ret, ret); - } -} - -static inline void tcg_gen_nor_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ -#if TCG_TARGET_REG_BITS == 64 - if (TCG_TARGET_HAS_nor_i64) { - tcg_gen_op3_i64(INDEX_op_nor_i64, ret, arg1, arg2); - } else { - tcg_gen_or_i64(ret, arg1, arg2); - tcg_gen_not_i64(ret, ret); - } -#else - tcg_gen_nor_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); - tcg_gen_nor_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); -#endif -} - -static inline void tcg_gen_orc_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - if (TCG_TARGET_HAS_orc_i32) { - tcg_gen_op3_i32(INDEX_op_orc_i32, ret, arg1, arg2); - } else { - TCGv_i32 t0 = tcg_temp_new_i32(); - tcg_gen_not_i32(t0, arg2); - tcg_gen_or_i32(ret, arg1, t0); - tcg_temp_free_i32(t0); - } -} - -static inline void tcg_gen_orc_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ -#if TCG_TARGET_REG_BITS == 64 - if (TCG_TARGET_HAS_orc_i64) { - tcg_gen_op3_i64(INDEX_op_orc_i64, ret, arg1, arg2); - } else { - TCGv_i64 t0 = tcg_temp_new_i64(); - tcg_gen_not_i64(t0, arg2); - tcg_gen_or_i64(ret, arg1, t0); - tcg_temp_free_i64(t0); - } -#else - tcg_gen_orc_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); - tcg_gen_orc_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); -#endif -} - -static inline void tcg_gen_rotl_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - if (TCG_TARGET_HAS_rot_i32) { - tcg_gen_op3_i32(INDEX_op_rotl_i32, ret, arg1, arg2); - } else { - TCGv_i32 t0, t1; - - t0 = tcg_temp_new_i32(); - t1 = tcg_temp_new_i32(); - tcg_gen_shl_i32(t0, arg1, arg2); - tcg_gen_subfi_i32(t1, 32, arg2); - tcg_gen_shr_i32(t1, arg1, t1); - tcg_gen_or_i32(ret, t0, t1); - tcg_temp_free_i32(t0); - tcg_temp_free_i32(t1); - } -} - -static inline void tcg_gen_rotl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - if (TCG_TARGET_HAS_rot_i64) { - tcg_gen_op3_i64(INDEX_op_rotl_i64, ret, arg1, arg2); - } else { - TCGv_i64 t0, t1; - t0 = tcg_temp_new_i64(); - t1 = tcg_temp_new_i64(); - tcg_gen_shl_i64(t0, arg1, arg2); - tcg_gen_subfi_i64(t1, 64, arg2); - tcg_gen_shr_i64(t1, arg1, t1); - tcg_gen_or_i64(ret, t0, t1); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - } -} - -static inline void tcg_gen_rotli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) -{ - /* some cases can be optimized here */ - if (arg2 == 0) { - tcg_gen_mov_i32(ret, arg1); - } else if (TCG_TARGET_HAS_rot_i32) { - TCGv_i32 t0 = tcg_const_i32(arg2); - tcg_gen_rotl_i32(ret, arg1, t0); - tcg_temp_free_i32(t0); - } else { - TCGv_i32 t0, t1; - t0 = tcg_temp_new_i32(); - t1 = tcg_temp_new_i32(); - tcg_gen_shli_i32(t0, arg1, arg2); - tcg_gen_shri_i32(t1, arg1, 32 - arg2); - tcg_gen_or_i32(ret, t0, t1); - tcg_temp_free_i32(t0); - tcg_temp_free_i32(t1); - } -} - -static inline void tcg_gen_rotli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) -{ - /* some cases can be optimized here */ - if (arg2 == 0) { - tcg_gen_mov_i64(ret, arg1); - } else if (TCG_TARGET_HAS_rot_i64) { - TCGv_i64 t0 = tcg_const_i64(arg2); - tcg_gen_rotl_i64(ret, arg1, t0); - tcg_temp_free_i64(t0); - } else { - TCGv_i64 t0, t1; - t0 = tcg_temp_new_i64(); - t1 = tcg_temp_new_i64(); - tcg_gen_shli_i64(t0, arg1, arg2); - tcg_gen_shri_i64(t1, arg1, 64 - arg2); - tcg_gen_or_i64(ret, t0, t1); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - } -} - -static inline void tcg_gen_rotr_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - if (TCG_TARGET_HAS_rot_i32) { - tcg_gen_op3_i32(INDEX_op_rotr_i32, ret, arg1, arg2); - } else { - TCGv_i32 t0, t1; - - t0 = tcg_temp_new_i32(); - t1 = tcg_temp_new_i32(); - tcg_gen_shr_i32(t0, arg1, arg2); - tcg_gen_subfi_i32(t1, 32, arg2); - tcg_gen_shl_i32(t1, arg1, t1); - tcg_gen_or_i32(ret, t0, t1); - tcg_temp_free_i32(t0); - tcg_temp_free_i32(t1); - } -} - -static inline void tcg_gen_rotr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - if (TCG_TARGET_HAS_rot_i64) { - tcg_gen_op3_i64(INDEX_op_rotr_i64, ret, arg1, arg2); - } else { - TCGv_i64 t0, t1; - t0 = tcg_temp_new_i64(); - t1 = tcg_temp_new_i64(); - tcg_gen_shr_i64(t0, arg1, arg2); - tcg_gen_subfi_i64(t1, 64, arg2); - tcg_gen_shl_i64(t1, arg1, t1); - tcg_gen_or_i64(ret, t0, t1); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - } -} - -static inline void tcg_gen_rotri_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) -{ - /* some cases can be optimized here */ - if (arg2 == 0) { - tcg_gen_mov_i32(ret, arg1); - } else { - tcg_gen_rotli_i32(ret, arg1, 32 - arg2); - } -} - -static inline void tcg_gen_rotri_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) -{ - /* some cases can be optimized here */ - if (arg2 == 0) { - tcg_gen_mov_i64(ret, arg1); - } else { - tcg_gen_rotli_i64(ret, arg1, 64 - arg2); - } -} - -static inline void tcg_gen_deposit_i32(TCGv_i32 ret, TCGv_i32 arg1, - TCGv_i32 arg2, unsigned int ofs, - unsigned int len) -{ - uint32_t mask; - TCGv_i32 t1; - - tcg_debug_assert(ofs < 32); - tcg_debug_assert(len <= 32); - tcg_debug_assert(ofs + len <= 32); - - if (ofs == 0 && len == 32) { - tcg_gen_mov_i32(ret, arg2); - return; - } - if (TCG_TARGET_HAS_deposit_i32 && TCG_TARGET_deposit_i32_valid(ofs, len)) { - tcg_gen_op5ii_i32(INDEX_op_deposit_i32, ret, arg1, arg2, ofs, len); - return; - } - - mask = (1u << len) - 1; - t1 = tcg_temp_new_i32(); - - if (ofs + len < 32) { - tcg_gen_andi_i32(t1, arg2, mask); - tcg_gen_shli_i32(t1, t1, ofs); - } else { - tcg_gen_shli_i32(t1, arg2, ofs); - } - tcg_gen_andi_i32(ret, arg1, ~(mask << ofs)); - tcg_gen_or_i32(ret, ret, t1); - - tcg_temp_free_i32(t1); -} - -static inline void tcg_gen_deposit_i64(TCGv_i64 ret, TCGv_i64 arg1, - TCGv_i64 arg2, unsigned int ofs, - unsigned int len) -{ - uint64_t mask; - TCGv_i64 t1; - - tcg_debug_assert(ofs < 64); - tcg_debug_assert(len <= 64); - tcg_debug_assert(ofs + len <= 64); - - if (ofs == 0 && len == 64) { - tcg_gen_mov_i64(ret, arg2); - return; - } - if (TCG_TARGET_HAS_deposit_i64 && TCG_TARGET_deposit_i64_valid(ofs, len)) { - tcg_gen_op5ii_i64(INDEX_op_deposit_i64, ret, arg1, arg2, ofs, len); - return; - } - -#if TCG_TARGET_REG_BITS == 32 - if (ofs >= 32) { - tcg_gen_deposit_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), - TCGV_LOW(arg2), ofs - 32, len); - tcg_gen_mov_i32(TCGV_LOW(ret), TCGV_LOW(arg1)); - return; - } - if (ofs + len <= 32) { - tcg_gen_deposit_i32(TCGV_LOW(ret), TCGV_LOW(arg1), - TCGV_LOW(arg2), ofs, len); - tcg_gen_mov_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1)); - return; - } -#endif - - mask = (1ull << len) - 1; - t1 = tcg_temp_new_i64(); - - if (ofs + len < 64) { - tcg_gen_andi_i64(t1, arg2, mask); - tcg_gen_shli_i64(t1, t1, ofs); - } else { - tcg_gen_shli_i64(t1, arg2, ofs); - } - tcg_gen_andi_i64(ret, arg1, ~(mask << ofs)); - tcg_gen_or_i64(ret, ret, t1); - - tcg_temp_free_i64(t1); -} - -static inline void tcg_gen_concat_i32_i64(TCGv_i64 dest, TCGv_i32 low, - TCGv_i32 high) -{ -#if TCG_TARGET_REG_BITS == 32 - tcg_gen_mov_i32(TCGV_LOW(dest), low); - tcg_gen_mov_i32(TCGV_HIGH(dest), high); -#else - TCGv_i64 tmp = tcg_temp_new_i64(); - /* These extensions are only needed for type correctness. - We may be able to do better given target specific information. */ - tcg_gen_extu_i32_i64(tmp, high); - tcg_gen_extu_i32_i64(dest, low); - /* If deposit is available, use it. Otherwise use the extra - knowledge that we have of the zero-extensions above. */ - if (TCG_TARGET_HAS_deposit_i64 && TCG_TARGET_deposit_i64_valid(32, 32)) { - tcg_gen_deposit_i64(dest, dest, tmp, 32, 32); - } else { - tcg_gen_shli_i64(tmp, tmp, 32); - tcg_gen_or_i64(dest, dest, tmp); - } - tcg_temp_free_i64(tmp); -#endif -} - -static inline void tcg_gen_concat32_i64(TCGv_i64 dest, TCGv_i64 low, - TCGv_i64 high) -{ - tcg_gen_deposit_i64(dest, low, high, 32, 32); -} - -static inline void tcg_gen_trunc_i64_i32(TCGv_i32 ret, TCGv_i64 arg) -{ - tcg_gen_trunc_shr_i64_i32(ret, arg, 0); -} - -static inline void tcg_gen_extr_i64_i32(TCGv_i32 lo, TCGv_i32 hi, TCGv_i64 arg) -{ - tcg_gen_trunc_shr_i64_i32(lo, arg, 0); - tcg_gen_trunc_shr_i64_i32(hi, arg, 32); -} - -static inline void tcg_gen_extr32_i64(TCGv_i64 lo, TCGv_i64 hi, TCGv_i64 arg) -{ - tcg_gen_ext32u_i64(lo, arg); - tcg_gen_shri_i64(hi, arg, 32); -} - -static inline void tcg_gen_movcond_i32(TCGCond cond, TCGv_i32 ret, - TCGv_i32 c1, TCGv_i32 c2, - TCGv_i32 v1, TCGv_i32 v2) -{ - if (TCG_TARGET_HAS_movcond_i32) { - tcg_gen_op6i_i32(INDEX_op_movcond_i32, ret, c1, c2, v1, v2, cond); - } else { - TCGv_i32 t0 = tcg_temp_new_i32(); - TCGv_i32 t1 = tcg_temp_new_i32(); - tcg_gen_setcond_i32(cond, t0, c1, c2); - tcg_gen_neg_i32(t0, t0); - tcg_gen_and_i32(t1, v1, t0); - tcg_gen_andc_i32(ret, v2, t0); - tcg_gen_or_i32(ret, ret, t1); - tcg_temp_free_i32(t0); - tcg_temp_free_i32(t1); - } -} - -static inline void tcg_gen_movcond_i64(TCGCond cond, TCGv_i64 ret, - TCGv_i64 c1, TCGv_i64 c2, - TCGv_i64 v1, TCGv_i64 v2) -{ -#if TCG_TARGET_REG_BITS == 32 - TCGv_i32 t0 = tcg_temp_new_i32(); - TCGv_i32 t1 = tcg_temp_new_i32(); - tcg_gen_op6i_i32(INDEX_op_setcond2_i32, t0, - TCGV_LOW(c1), TCGV_HIGH(c1), - TCGV_LOW(c2), TCGV_HIGH(c2), cond); - - if (TCG_TARGET_HAS_movcond_i32) { - tcg_gen_movi_i32(t1, 0); - tcg_gen_movcond_i32(TCG_COND_NE, TCGV_LOW(ret), t0, t1, - TCGV_LOW(v1), TCGV_LOW(v2)); - tcg_gen_movcond_i32(TCG_COND_NE, TCGV_HIGH(ret), t0, t1, - TCGV_HIGH(v1), TCGV_HIGH(v2)); - } else { - tcg_gen_neg_i32(t0, t0); - - tcg_gen_and_i32(t1, TCGV_LOW(v1), t0); - tcg_gen_andc_i32(TCGV_LOW(ret), TCGV_LOW(v2), t0); - tcg_gen_or_i32(TCGV_LOW(ret), TCGV_LOW(ret), t1); - - tcg_gen_and_i32(t1, TCGV_HIGH(v1), t0); - tcg_gen_andc_i32(TCGV_HIGH(ret), TCGV_HIGH(v2), t0); - tcg_gen_or_i32(TCGV_HIGH(ret), TCGV_HIGH(ret), t1); - } - tcg_temp_free_i32(t0); - tcg_temp_free_i32(t1); -#else - if (TCG_TARGET_HAS_movcond_i64) { - tcg_gen_op6i_i64(INDEX_op_movcond_i64, ret, c1, c2, v1, v2, cond); - } else { - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); - tcg_gen_setcond_i64(cond, t0, c1, c2); - tcg_gen_neg_i64(t0, t0); - tcg_gen_and_i64(t1, v1, t0); - tcg_gen_andc_i64(ret, v2, t0); - tcg_gen_or_i64(ret, ret, t1); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - } -#endif -} - -static inline void tcg_gen_add2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 al, - TCGv_i32 ah, TCGv_i32 bl, TCGv_i32 bh) -{ - if (TCG_TARGET_HAS_add2_i32) { - tcg_gen_op6_i32(INDEX_op_add2_i32, rl, rh, al, ah, bl, bh); - /* Allow the optimizer room to replace add2 with two moves. */ - tcg_gen_op0(INDEX_op_nop); - } else { - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); - tcg_gen_concat_i32_i64(t0, al, ah); - tcg_gen_concat_i32_i64(t1, bl, bh); - tcg_gen_add_i64(t0, t0, t1); - tcg_gen_extr_i64_i32(rl, rh, t0); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - } -} - -static inline void tcg_gen_sub2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 al, - TCGv_i32 ah, TCGv_i32 bl, TCGv_i32 bh) -{ - if (TCG_TARGET_HAS_sub2_i32) { - tcg_gen_op6_i32(INDEX_op_sub2_i32, rl, rh, al, ah, bl, bh); - /* Allow the optimizer room to replace sub2 with two moves. */ - tcg_gen_op0(INDEX_op_nop); - } else { - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); - tcg_gen_concat_i32_i64(t0, al, ah); - tcg_gen_concat_i32_i64(t1, bl, bh); - tcg_gen_sub_i64(t0, t0, t1); - tcg_gen_extr_i64_i32(rl, rh, t0); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - } -} - -static inline void tcg_gen_mulu2_i32(TCGv_i32 rl, TCGv_i32 rh, - TCGv_i32 arg1, TCGv_i32 arg2) -{ - if (TCG_TARGET_HAS_mulu2_i32) { - tcg_gen_op4_i32(INDEX_op_mulu2_i32, rl, rh, arg1, arg2); - /* Allow the optimizer room to replace mulu2 with two moves. */ - tcg_gen_op0(INDEX_op_nop); - } else if (TCG_TARGET_HAS_muluh_i32) { - TCGv_i32 t = tcg_temp_new_i32(); - tcg_gen_op3_i32(INDEX_op_mul_i32, t, arg1, arg2); - tcg_gen_op3_i32(INDEX_op_muluh_i32, rh, arg1, arg2); - tcg_gen_mov_i32(rl, t); - tcg_temp_free_i32(t); - } else { - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); - tcg_gen_extu_i32_i64(t0, arg1); - tcg_gen_extu_i32_i64(t1, arg2); - tcg_gen_mul_i64(t0, t0, t1); - tcg_gen_extr_i64_i32(rl, rh, t0); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - } -} - -static inline void tcg_gen_muls2_i32(TCGv_i32 rl, TCGv_i32 rh, - TCGv_i32 arg1, TCGv_i32 arg2) -{ - if (TCG_TARGET_HAS_muls2_i32) { - tcg_gen_op4_i32(INDEX_op_muls2_i32, rl, rh, arg1, arg2); - /* Allow the optimizer room to replace muls2 with two moves. */ - tcg_gen_op0(INDEX_op_nop); - } else if (TCG_TARGET_HAS_mulsh_i32) { - TCGv_i32 t = tcg_temp_new_i32(); - tcg_gen_op3_i32(INDEX_op_mul_i32, t, arg1, arg2); - tcg_gen_op3_i32(INDEX_op_mulsh_i32, rh, arg1, arg2); - tcg_gen_mov_i32(rl, t); - tcg_temp_free_i32(t); - } else if (TCG_TARGET_REG_BITS == 32) { - TCGv_i32 t0 = tcg_temp_new_i32(); - TCGv_i32 t1 = tcg_temp_new_i32(); - TCGv_i32 t2 = tcg_temp_new_i32(); - TCGv_i32 t3 = tcg_temp_new_i32(); - tcg_gen_mulu2_i32(t0, t1, arg1, arg2); - /* Adjust for negative inputs. */ - tcg_gen_sari_i32(t2, arg1, 31); - tcg_gen_sari_i32(t3, arg2, 31); - tcg_gen_and_i32(t2, t2, arg2); - tcg_gen_and_i32(t3, t3, arg1); - tcg_gen_sub_i32(rh, t1, t2); - tcg_gen_sub_i32(rh, rh, t3); - tcg_gen_mov_i32(rl, t0); - tcg_temp_free_i32(t0); - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t2); - tcg_temp_free_i32(t3); - } else { - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); - tcg_gen_ext_i32_i64(t0, arg1); - tcg_gen_ext_i32_i64(t1, arg2); - tcg_gen_mul_i64(t0, t0, t1); - tcg_gen_extr_i64_i32(rl, rh, t0); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - } -} - -static inline void tcg_gen_add2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 al, - TCGv_i64 ah, TCGv_i64 bl, TCGv_i64 bh) -{ - if (TCG_TARGET_HAS_add2_i64) { - tcg_gen_op6_i64(INDEX_op_add2_i64, rl, rh, al, ah, bl, bh); - /* Allow the optimizer room to replace add2 with two moves. */ - tcg_gen_op0(INDEX_op_nop); - } else { - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); - tcg_gen_add_i64(t0, al, bl); - tcg_gen_setcond_i64(TCG_COND_LTU, t1, t0, al); - tcg_gen_add_i64(rh, ah, bh); - tcg_gen_add_i64(rh, rh, t1); - tcg_gen_mov_i64(rl, t0); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - } -} - -static inline void tcg_gen_sub2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 al, - TCGv_i64 ah, TCGv_i64 bl, TCGv_i64 bh) -{ - if (TCG_TARGET_HAS_sub2_i64) { - tcg_gen_op6_i64(INDEX_op_sub2_i64, rl, rh, al, ah, bl, bh); - /* Allow the optimizer room to replace sub2 with two moves. */ - tcg_gen_op0(INDEX_op_nop); - } else { - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); - tcg_gen_sub_i64(t0, al, bl); - tcg_gen_setcond_i64(TCG_COND_LTU, t1, al, bl); - tcg_gen_sub_i64(rh, ah, bh); - tcg_gen_sub_i64(rh, rh, t1); - tcg_gen_mov_i64(rl, t0); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - } -} - -static inline void tcg_gen_mulu2_i64(TCGv_i64 rl, TCGv_i64 rh, - TCGv_i64 arg1, TCGv_i64 arg2) -{ - if (TCG_TARGET_HAS_mulu2_i64) { - tcg_gen_op4_i64(INDEX_op_mulu2_i64, rl, rh, arg1, arg2); - /* Allow the optimizer room to replace mulu2 with two moves. */ - tcg_gen_op0(INDEX_op_nop); - } else if (TCG_TARGET_HAS_muluh_i64) { - TCGv_i64 t = tcg_temp_new_i64(); - tcg_gen_op3_i64(INDEX_op_mul_i64, t, arg1, arg2); - tcg_gen_op3_i64(INDEX_op_muluh_i64, rh, arg1, arg2); - tcg_gen_mov_i64(rl, t); - tcg_temp_free_i64(t); - } else { - TCGv_i64 t0 = tcg_temp_new_i64(); - tcg_gen_mul_i64(t0, arg1, arg2); - gen_helper_muluh_i64(rh, arg1, arg2); - tcg_gen_mov_i64(rl, t0); - tcg_temp_free_i64(t0); - } -} - -static inline void tcg_gen_muls2_i64(TCGv_i64 rl, TCGv_i64 rh, - TCGv_i64 arg1, TCGv_i64 arg2) -{ - if (TCG_TARGET_HAS_muls2_i64) { - tcg_gen_op4_i64(INDEX_op_muls2_i64, rl, rh, arg1, arg2); - /* Allow the optimizer room to replace muls2 with two moves. */ - tcg_gen_op0(INDEX_op_nop); - } else if (TCG_TARGET_HAS_mulsh_i64) { - TCGv_i64 t = tcg_temp_new_i64(); - tcg_gen_op3_i64(INDEX_op_mul_i64, t, arg1, arg2); - tcg_gen_op3_i64(INDEX_op_mulsh_i64, rh, arg1, arg2); - tcg_gen_mov_i64(rl, t); - tcg_temp_free_i64(t); - } else if (TCG_TARGET_HAS_mulu2_i64 || TCG_TARGET_HAS_muluh_i64) { - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); - TCGv_i64 t2 = tcg_temp_new_i64(); - TCGv_i64 t3 = tcg_temp_new_i64(); - tcg_gen_mulu2_i64(t0, t1, arg1, arg2); - /* Adjust for negative inputs. */ - tcg_gen_sari_i64(t2, arg1, 63); - tcg_gen_sari_i64(t3, arg2, 63); - tcg_gen_and_i64(t2, t2, arg2); - tcg_gen_and_i64(t3, t3, arg1); - tcg_gen_sub_i64(rh, t1, t2); - tcg_gen_sub_i64(rh, rh, t3); - tcg_gen_mov_i64(rl, t0); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); - tcg_temp_free_i64(t3); - } else { - TCGv_i64 t0 = tcg_temp_new_i64(); - tcg_gen_mul_i64(t0, arg1, arg2); - gen_helper_mulsh_i64(rh, arg1, arg2); - tcg_gen_mov_i64(rl, t0); - tcg_temp_free_i64(t0); - } -} - -/***************************************/ -/* QEMU specific operations. Their type depend on the QEMU CPU - type. */ -#ifndef TARGET_LONG_BITS -#error must include QEMU headers -#endif +void tcg_gen_goto_tb(unsigned idx); #if TARGET_LONG_BITS == 32 #define TCGv TCGv_i32 @@ -2473,7 +733,6 @@ static inline void tcg_gen_muls2_i64(TCGv_i64 rl, TCGv_i64 rh, #define TCGV_UNUSED(x) TCGV_UNUSED_I32(x) #define TCGV_IS_UNUSED(x) TCGV_IS_UNUSED_I32(x) #define TCGV_EQUAL(a, b) TCGV_EQUAL_I32(a, b) -#define tcg_add_param_tl tcg_add_param_i32 #define tcg_gen_qemu_ld_tl tcg_gen_qemu_ld_i32 #define tcg_gen_qemu_st_tl tcg_gen_qemu_st_i32 #else @@ -2486,41 +745,10 @@ static inline void tcg_gen_muls2_i64(TCGv_i64 rl, TCGv_i64 rh, #define TCGV_UNUSED(x) TCGV_UNUSED_I64(x) #define TCGV_IS_UNUSED(x) TCGV_IS_UNUSED_I64(x) #define TCGV_EQUAL(a, b) TCGV_EQUAL_I64(a, b) -#define tcg_add_param_tl tcg_add_param_i64 #define tcg_gen_qemu_ld_tl tcg_gen_qemu_ld_i64 #define tcg_gen_qemu_st_tl tcg_gen_qemu_st_i64 #endif -/* debug info: write the PC of the corresponding QEMU CPU instruction */ -static inline void tcg_gen_debug_insn_start(uint64_t pc) -{ - /* XXX: must really use a 32 bit size for TCGArg in all cases */ -#if TARGET_LONG_BITS > TCG_TARGET_REG_BITS - tcg_gen_op2ii(INDEX_op_debug_insn_start, - (uint32_t)(pc), (uint32_t)(pc >> 32)); -#else - tcg_gen_op1i(INDEX_op_debug_insn_start, pc); -#endif -} - -static inline void tcg_gen_exit_tb(uintptr_t val) -{ - tcg_gen_op1i(INDEX_op_exit_tb, val); -} - -static inline void tcg_gen_goto_tb(unsigned idx) -{ - /* We only support two chained exits. */ - tcg_debug_assert(idx <= 1); -#ifdef CONFIG_DEBUG_TCG - /* Verify that we havn't seen this numbered exit before. */ - tcg_debug_assert((tcg_ctx.goto_tb_issue_mask & (1 << idx)) == 0); - tcg_ctx.goto_tb_issue_mask |= 1 << idx; -#endif - tcg_gen_op1i(INDEX_op_goto_tb, idx); -} - - void tcg_gen_qemu_ld_i32(TCGv_i32, TCGv, TCGArg, TCGMemOp); void tcg_gen_qemu_st_i32(TCGv_i32, TCGv, TCGArg, TCGMemOp); void tcg_gen_qemu_ld_i64(TCGv_i64, TCGv, TCGArg, TCGMemOp); diff --git a/tcg/tcg-opc.h b/tcg/tcg-opc.h index 042d442..42d0cfe 100644 --- a/tcg/tcg-opc.h +++ b/tcg/tcg-opc.h @@ -27,15 +27,6 @@ */ /* predefined ops */ -DEF(end, 0, 0, 0, TCG_OPF_NOT_PRESENT) /* must be kept first */ -DEF(nop, 0, 0, 0, TCG_OPF_NOT_PRESENT) -DEF(nop1, 0, 0, 1, TCG_OPF_NOT_PRESENT) -DEF(nop2, 0, 0, 2, TCG_OPF_NOT_PRESENT) -DEF(nop3, 0, 0, 3, TCG_OPF_NOT_PRESENT) - -/* variable number of parameters */ -DEF(nopn, 0, 0, 1, TCG_OPF_NOT_PRESENT) - DEF(discard, 1, 0, 0, TCG_OPF_NOT_PRESENT) DEF(set_label, 0, 0, 1, TCG_OPF_BB_END | TCG_OPF_NOT_PRESENT) diff --git a/tcg/tcg.c b/tcg/tcg.c index 7a84b87..f1558b7 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -208,12 +208,10 @@ static __attribute__((unused)) inline void tcg_patch64(tcg_insn_unit *p, /* label relocation processing */ static void tcg_out_reloc(TCGContext *s, tcg_insn_unit *code_ptr, int type, - int label_index, intptr_t addend) + TCGLabel *l, intptr_t addend) { - TCGLabel *l; TCGRelocation *r; - l = &s->labels[label_index]; if (l->has_value) { /* FIXME: This may break relocations on RISC targets that modify instruction fields in place. The caller may not have @@ -230,9 +228,8 @@ static void tcg_out_reloc(TCGContext *s, tcg_insn_unit *code_ptr, int type, } } -static void tcg_out_label(TCGContext *s, int label_index, tcg_insn_unit *ptr) +static void tcg_out_label(TCGContext *s, TCGLabel *l, tcg_insn_unit *ptr) { - TCGLabel *l = &s->labels[label_index]; intptr_t value = (intptr_t)ptr; TCGRelocation *r; @@ -246,19 +243,16 @@ static void tcg_out_label(TCGContext *s, int label_index, tcg_insn_unit *ptr) l->u.value_ptr = ptr; } -int gen_new_label(void) +TCGLabel *gen_new_label(void) { TCGContext *s = &tcg_ctx; - int idx; - TCGLabel *l; + TCGLabel *l = tcg_malloc(sizeof(TCGLabel)); - if (s->nb_labels >= TCG_MAX_LABELS) - tcg_abort(); - idx = s->nb_labels++; - l = &s->labels[idx]; - l->has_value = 0; - l->u.first_reloc = NULL; - return idx; + *l = (TCGLabel){ + .id = s->nb_labels++ + }; + + return l; } #include "tcg-target.c" @@ -407,7 +401,6 @@ void tcg_func_start(TCGContext *s) /* No temps have been previously allocated for size or locality. */ memset(s->free_temps, 0, sizeof(s->free_temps)); - s->labels = tcg_malloc(sizeof(TCGLabel) * TCG_MAX_LABELS); s->nb_labels = 0; s->current_frame_offset = s->frame_start; @@ -415,8 +408,10 @@ void tcg_func_start(TCGContext *s) s->goto_tb_issue_mask = 0; #endif - s->gen_opc_ptr = s->gen_opc_buf; - s->gen_opparam_ptr = s->gen_opparam_buf; + s->gen_first_op_idx = 0; + s->gen_last_op_idx = -1; + s->gen_next_op_idx = 0; + s->gen_next_parm_idx = 0; s->be = tcg_malloc(sizeof(TCGBackendData)); } @@ -703,9 +698,8 @@ int tcg_check_temp_count(void) void tcg_gen_callN(TCGContext *s, void *func, TCGArg ret, int nargs, TCGArg *args) { - int i, real_args, nb_rets; + int i, real_args, nb_rets, pi, pi_first; unsigned sizemask, flags; - TCGArg *nparam; TCGHelperInfo *info; info = g_hash_table_lookup(s->helpers, (gpointer)func); @@ -758,8 +752,7 @@ void tcg_gen_callN(TCGContext *s, void *func, TCGArg ret, } #endif /* TCG_TARGET_EXTEND_ARGS */ - *s->gen_opc_ptr++ = INDEX_op_call; - nparam = s->gen_opparam_ptr++; + pi_first = pi = s->gen_next_parm_idx; if (ret != TCG_CALL_DUMMY_ARG) { #if defined(__sparc__) && !defined(__arch64__) \ && !defined(CONFIG_TCG_INTERPRETER) @@ -769,25 +762,25 @@ void tcg_gen_callN(TCGContext *s, void *func, TCGArg ret, two return temporaries, and reassemble below. */ retl = tcg_temp_new_i64(); reth = tcg_temp_new_i64(); - *s->gen_opparam_ptr++ = GET_TCGV_I64(reth); - *s->gen_opparam_ptr++ = GET_TCGV_I64(retl); + s->gen_opparam_buf[pi++] = GET_TCGV_I64(reth); + s->gen_opparam_buf[pi++] = GET_TCGV_I64(retl); nb_rets = 2; } else { - *s->gen_opparam_ptr++ = ret; + s->gen_opparam_buf[pi++] = ret; nb_rets = 1; } #else if (TCG_TARGET_REG_BITS < 64 && (sizemask & 1)) { #ifdef HOST_WORDS_BIGENDIAN - *s->gen_opparam_ptr++ = ret + 1; - *s->gen_opparam_ptr++ = ret; + s->gen_opparam_buf[pi++] = ret + 1; + s->gen_opparam_buf[pi++] = ret; #else - *s->gen_opparam_ptr++ = ret; - *s->gen_opparam_ptr++ = ret + 1; + s->gen_opparam_buf[pi++] = ret; + s->gen_opparam_buf[pi++] = ret + 1; #endif nb_rets = 2; } else { - *s->gen_opparam_ptr++ = ret; + s->gen_opparam_buf[pi++] = ret; nb_rets = 1; } #endif @@ -801,7 +794,7 @@ void tcg_gen_callN(TCGContext *s, void *func, TCGArg ret, #ifdef TCG_TARGET_CALL_ALIGN_ARGS /* some targets want aligned 64 bit args */ if (real_args & 1) { - *s->gen_opparam_ptr++ = TCG_CALL_DUMMY_ARG; + s->gen_opparam_buf[pi++] = TCG_CALL_DUMMY_ARG; real_args++; } #endif @@ -816,26 +809,42 @@ void tcg_gen_callN(TCGContext *s, void *func, TCGArg ret, have to get more complicated to differentiate between stack arguments and register arguments. */ #if defined(HOST_WORDS_BIGENDIAN) != defined(TCG_TARGET_STACK_GROWSUP) - *s->gen_opparam_ptr++ = args[i] + 1; - *s->gen_opparam_ptr++ = args[i]; + s->gen_opparam_buf[pi++] = args[i] + 1; + s->gen_opparam_buf[pi++] = args[i]; #else - *s->gen_opparam_ptr++ = args[i]; - *s->gen_opparam_ptr++ = args[i] + 1; + s->gen_opparam_buf[pi++] = args[i]; + s->gen_opparam_buf[pi++] = args[i] + 1; #endif real_args += 2; continue; } - *s->gen_opparam_ptr++ = args[i]; + s->gen_opparam_buf[pi++] = args[i]; real_args++; } - *s->gen_opparam_ptr++ = (uintptr_t)func; - *s->gen_opparam_ptr++ = flags; + s->gen_opparam_buf[pi++] = (uintptr_t)func; + s->gen_opparam_buf[pi++] = flags; + + i = s->gen_next_op_idx; + tcg_debug_assert(i < OPC_BUF_SIZE); + tcg_debug_assert(pi <= OPPARAM_BUF_SIZE); + + /* Set links for sequential allocation during translation. */ + s->gen_op_buf[i] = (TCGOp){ + .opc = INDEX_op_call, + .callo = nb_rets, + .calli = real_args, + .args = pi_first, + .prev = i - 1, + .next = i + 1 + }; - *nparam = (nb_rets << 16) | real_args; + /* Make sure the calli field didn't overflow. */ + tcg_debug_assert(s->gen_op_buf[i].calli == real_args); - /* total parameters, needed to go backward in the instruction stream */ - *s->gen_opparam_ptr++ = 1 + nb_rets + real_args + 3; + s->gen_last_op_idx = i; + s->gen_next_op_idx = i + 1; + s->gen_next_parm_idx = pi; #if defined(__sparc__) && !defined(__arch64__) \ && !defined(CONFIG_TCG_INTERPRETER) @@ -870,143 +879,6 @@ void tcg_gen_callN(TCGContext *s, void *func, TCGArg ret, #endif /* TCG_TARGET_EXTEND_ARGS */ } -#if TCG_TARGET_REG_BITS == 32 -void tcg_gen_shifti_i64(TCGv_i64 ret, TCGv_i64 arg1, - int c, int right, int arith) -{ - if (c == 0) { - tcg_gen_mov_i32(TCGV_LOW(ret), TCGV_LOW(arg1)); - tcg_gen_mov_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1)); - } else if (c >= 32) { - c -= 32; - if (right) { - if (arith) { - tcg_gen_sari_i32(TCGV_LOW(ret), TCGV_HIGH(arg1), c); - tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), 31); - } else { - tcg_gen_shri_i32(TCGV_LOW(ret), TCGV_HIGH(arg1), c); - tcg_gen_movi_i32(TCGV_HIGH(ret), 0); - } - } else { - tcg_gen_shli_i32(TCGV_HIGH(ret), TCGV_LOW(arg1), c); - tcg_gen_movi_i32(TCGV_LOW(ret), 0); - } - } else { - TCGv_i32 t0, t1; - - t0 = tcg_temp_new_i32(); - t1 = tcg_temp_new_i32(); - if (right) { - tcg_gen_shli_i32(t0, TCGV_HIGH(arg1), 32 - c); - if (arith) - tcg_gen_sari_i32(t1, TCGV_HIGH(arg1), c); - else - tcg_gen_shri_i32(t1, TCGV_HIGH(arg1), c); - tcg_gen_shri_i32(TCGV_LOW(ret), TCGV_LOW(arg1), c); - tcg_gen_or_i32(TCGV_LOW(ret), TCGV_LOW(ret), t0); - tcg_gen_mov_i32(TCGV_HIGH(ret), t1); - } else { - tcg_gen_shri_i32(t0, TCGV_LOW(arg1), 32 - c); - /* Note: ret can be the same as arg1, so we use t1 */ - tcg_gen_shli_i32(t1, TCGV_LOW(arg1), c); - tcg_gen_shli_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), c); - tcg_gen_or_i32(TCGV_HIGH(ret), TCGV_HIGH(ret), t0); - tcg_gen_mov_i32(TCGV_LOW(ret), t1); - } - tcg_temp_free_i32(t0); - tcg_temp_free_i32(t1); - } -} -#endif - -static inline TCGMemOp tcg_canonicalize_memop(TCGMemOp op, bool is64, bool st) -{ - switch (op & MO_SIZE) { - case MO_8: - op &= ~MO_BSWAP; - break; - case MO_16: - break; - case MO_32: - if (!is64) { - op &= ~MO_SIGN; - } - break; - case MO_64: - if (!is64) { - tcg_abort(); - } - break; - } - if (st) { - op &= ~MO_SIGN; - } - return op; -} - -void tcg_gen_qemu_ld_i32(TCGv_i32 val, TCGv addr, TCGArg idx, TCGMemOp memop) -{ - memop = tcg_canonicalize_memop(memop, 0, 0); - - *tcg_ctx.gen_opc_ptr++ = INDEX_op_qemu_ld_i32; - tcg_add_param_i32(val); - tcg_add_param_tl(addr); - *tcg_ctx.gen_opparam_ptr++ = memop; - *tcg_ctx.gen_opparam_ptr++ = idx; -} - -void tcg_gen_qemu_st_i32(TCGv_i32 val, TCGv addr, TCGArg idx, TCGMemOp memop) -{ - memop = tcg_canonicalize_memop(memop, 0, 1); - - *tcg_ctx.gen_opc_ptr++ = INDEX_op_qemu_st_i32; - tcg_add_param_i32(val); - tcg_add_param_tl(addr); - *tcg_ctx.gen_opparam_ptr++ = memop; - *tcg_ctx.gen_opparam_ptr++ = idx; -} - -void tcg_gen_qemu_ld_i64(TCGv_i64 val, TCGv addr, TCGArg idx, TCGMemOp memop) -{ - memop = tcg_canonicalize_memop(memop, 1, 0); - -#if TCG_TARGET_REG_BITS == 32 - if ((memop & MO_SIZE) < MO_64) { - tcg_gen_qemu_ld_i32(TCGV_LOW(val), addr, idx, memop); - if (memop & MO_SIGN) { - tcg_gen_sari_i32(TCGV_HIGH(val), TCGV_LOW(val), 31); - } else { - tcg_gen_movi_i32(TCGV_HIGH(val), 0); - } - return; - } -#endif - - *tcg_ctx.gen_opc_ptr++ = INDEX_op_qemu_ld_i64; - tcg_add_param_i64(val); - tcg_add_param_tl(addr); - *tcg_ctx.gen_opparam_ptr++ = memop; - *tcg_ctx.gen_opparam_ptr++ = idx; -} - -void tcg_gen_qemu_st_i64(TCGv_i64 val, TCGv addr, TCGArg idx, TCGMemOp memop) -{ - memop = tcg_canonicalize_memop(memop, 1, 1); - -#if TCG_TARGET_REG_BITS == 32 - if ((memop & MO_SIZE) < MO_64) { - tcg_gen_qemu_st_i32(TCGV_LOW(val), addr, idx, memop); - return; - } -#endif - - *tcg_ctx.gen_opc_ptr++ = INDEX_op_qemu_st_i64; - tcg_add_param_i64(val); - tcg_add_param_tl(addr); - *tcg_ctx.gen_opparam_ptr++ = memop; - *tcg_ctx.gen_opparam_ptr++ = idx; -} - static void tcg_reg_alloc_start(TCGContext *s) { int i; @@ -1109,20 +981,21 @@ static const char * const ldst_name[] = void tcg_dump_ops(TCGContext *s) { - const uint16_t *opc_ptr; - const TCGArg *args; - TCGArg arg; - TCGOpcode c; - int i, k, nb_oargs, nb_iargs, nb_cargs, first_insn; - const TCGOpDef *def; char buf[128]; + TCGOp *op; + int oi; + + for (oi = s->gen_first_op_idx; oi >= 0; oi = op->next) { + int i, k, nb_oargs, nb_iargs, nb_cargs; + const TCGOpDef *def; + const TCGArg *args; + TCGOpcode c; - first_insn = 1; - opc_ptr = s->gen_opc_buf; - args = s->gen_opparam_buf; - while (opc_ptr < s->gen_opc_ptr) { - c = *opc_ptr++; + op = &s->gen_op_buf[oi]; + c = op->opc; def = &tcg_op_defs[c]; + args = &s->gen_opparam_buf[op->args]; + if (c == INDEX_op_debug_insn_start) { uint64_t pc; #if TARGET_LONG_BITS > TCG_TARGET_REG_BITS @@ -1130,21 +1003,14 @@ void tcg_dump_ops(TCGContext *s) #else pc = args[0]; #endif - if (!first_insn) { + if (oi != s->gen_first_op_idx) { qemu_log("\n"); } qemu_log(" ---- 0x%" PRIx64, pc); - first_insn = 0; - nb_oargs = def->nb_oargs; - nb_iargs = def->nb_iargs; - nb_cargs = def->nb_cargs; } else if (c == INDEX_op_call) { - TCGArg arg; - /* variable number of arguments */ - arg = *args++; - nb_oargs = arg >> 16; - nb_iargs = arg & 0xffff; + nb_oargs = op->callo; + nb_iargs = op->calli; nb_cargs = def->nb_cargs; /* function name, flags, out args */ @@ -1165,26 +1031,20 @@ void tcg_dump_ops(TCGContext *s) } } else { qemu_log(" %s ", def->name); - if (c == INDEX_op_nopn) { - /* variable number of arguments */ - nb_cargs = *args; - nb_oargs = 0; - nb_iargs = 0; - } else { - nb_oargs = def->nb_oargs; - nb_iargs = def->nb_iargs; - nb_cargs = def->nb_cargs; - } - + + nb_oargs = def->nb_oargs; + nb_iargs = def->nb_iargs; + nb_cargs = def->nb_cargs; + k = 0; - for(i = 0; i < nb_oargs; i++) { + for (i = 0; i < nb_oargs; i++) { if (k != 0) { qemu_log(","); } qemu_log("%s", tcg_get_arg_str_idx(s, buf, sizeof(buf), args[k++])); } - for(i = 0; i < nb_iargs; i++) { + for (i = 0; i < nb_iargs; i++) { if (k != 0) { qemu_log(","); } @@ -1222,16 +1082,23 @@ void tcg_dump_ops(TCGContext *s) i = 0; break; } - for(; i < nb_cargs; i++) { - if (k != 0) { - qemu_log(","); - } - arg = args[k++]; - qemu_log("$0x%" TCG_PRIlx, arg); + switch (c) { + case INDEX_op_set_label: + case INDEX_op_br: + case INDEX_op_brcond_i32: + case INDEX_op_brcond_i64: + case INDEX_op_brcond2_i32: + qemu_log("%s$L%d", k ? "," : "", arg_label(args[k])->id); + i++, k++; + break; + default: + break; + } + for (; i < nb_cargs; i++, k++) { + qemu_log("%s$0x%" TCG_PRIlx, k ? "," : "", args[k]); } } qemu_log("\n"); - args += nb_iargs + nb_oargs + nb_cargs; } } @@ -1380,21 +1247,30 @@ void tcg_add_target_add_op_defs(const TCGTargetOpDef *tdefs) #endif } -#ifdef USE_LIVENESS_ANALYSIS - -/* set a nop for an operation using 'nb_args' */ -static inline void tcg_set_nop(TCGContext *s, uint16_t *opc_ptr, - TCGArg *args, int nb_args) +void tcg_op_remove(TCGContext *s, TCGOp *op) { - if (nb_args == 0) { - *opc_ptr = INDEX_op_nop; + int next = op->next; + int prev = op->prev; + + if (next >= 0) { + s->gen_op_buf[next].prev = prev; + } else { + s->gen_last_op_idx = prev; + } + if (prev >= 0) { + s->gen_op_buf[prev].next = next; } else { - *opc_ptr = INDEX_op_nopn; - args[0] = nb_args; - args[nb_args - 1] = nb_args; + s->gen_first_op_idx = next; } + + memset(op, -1, sizeof(*op)); + +#ifdef CONFIG_PROFILER + s->del_op_count++; +#endif } +#ifdef USE_LIVENESS_ANALYSIS /* liveness analysis: end of function: all temps are dead, and globals should be in memory. */ static inline void tcg_la_func_end(TCGContext *s, uint8_t *dead_temps, @@ -1424,19 +1300,10 @@ static inline void tcg_la_bb_end(TCGContext *s, uint8_t *dead_temps, temporaries are removed. */ static void tcg_liveness_analysis(TCGContext *s) { - int i, op_index, nb_args, nb_iargs, nb_oargs, nb_ops; - TCGOpcode op, op_new, op_new2; - TCGArg *args, arg; - const TCGOpDef *def; uint8_t *dead_temps, *mem_temps; - uint16_t dead_args; - uint8_t sync_args; - bool have_op_new2; - - s->gen_opc_ptr++; /* skip end */ - - nb_ops = s->gen_opc_ptr - s->gen_opc_buf; + int oi, oi_prev, nb_ops; + nb_ops = s->gen_next_op_idx; s->op_dead_args = tcg_malloc(nb_ops * sizeof(uint16_t)); s->op_sync_args = tcg_malloc(nb_ops * sizeof(uint8_t)); @@ -1444,25 +1311,31 @@ static void tcg_liveness_analysis(TCGContext *s) mem_temps = tcg_malloc(s->nb_temps); tcg_la_func_end(s, dead_temps, mem_temps); - args = s->gen_opparam_ptr; - op_index = nb_ops - 1; - while (op_index >= 0) { - op = s->gen_opc_buf[op_index]; - def = &tcg_op_defs[op]; - switch(op) { + for (oi = s->gen_last_op_idx; oi >= 0; oi = oi_prev) { + int i, nb_iargs, nb_oargs; + TCGOpcode opc_new, opc_new2; + bool have_opc_new2; + uint16_t dead_args; + uint8_t sync_args; + TCGArg arg; + + TCGOp * const op = &s->gen_op_buf[oi]; + TCGArg * const args = &s->gen_opparam_buf[op->args]; + TCGOpcode opc = op->opc; + const TCGOpDef *def = &tcg_op_defs[opc]; + + oi_prev = op->prev; + + switch (opc) { case INDEX_op_call: { int call_flags; - nb_args = args[-1]; - args -= nb_args; - arg = *args++; - nb_iargs = arg & 0xffff; - nb_oargs = arg >> 16; + nb_oargs = op->callo; + nb_iargs = op->calli; call_flags = args[nb_oargs + nb_iargs + 1]; - /* pure functions can be removed if their result is not - used */ + /* pure functions can be removed if their result is unused */ if (call_flags & TCG_CALL_NO_SIDE_EFFECTS) { for (i = 0; i < nb_oargs; i++) { arg = args[i]; @@ -1470,8 +1343,7 @@ static void tcg_liveness_analysis(TCGContext *s) goto do_not_remove_call; } } - tcg_set_nop(s, s->gen_opc_buf + op_index, - args - 1, nb_args); + goto do_remove; } else { do_not_remove_call: @@ -1510,41 +1382,31 @@ static void tcg_liveness_analysis(TCGContext *s) dead_temps[arg] = 0; } } - s->op_dead_args[op_index] = dead_args; - s->op_sync_args[op_index] = sync_args; + s->op_dead_args[oi] = dead_args; + s->op_sync_args[oi] = sync_args; } - args--; } break; case INDEX_op_debug_insn_start: - args -= def->nb_args; - break; - case INDEX_op_nopn: - nb_args = args[-1]; - args -= nb_args; break; case INDEX_op_discard: - args--; /* mark the temporary as dead */ dead_temps[args[0]] = 1; mem_temps[args[0]] = 0; break; - case INDEX_op_end: - break; case INDEX_op_add2_i32: - op_new = INDEX_op_add_i32; + opc_new = INDEX_op_add_i32; goto do_addsub2; case INDEX_op_sub2_i32: - op_new = INDEX_op_sub_i32; + opc_new = INDEX_op_sub_i32; goto do_addsub2; case INDEX_op_add2_i64: - op_new = INDEX_op_add_i64; + opc_new = INDEX_op_add_i64; goto do_addsub2; case INDEX_op_sub2_i64: - op_new = INDEX_op_sub_i64; + opc_new = INDEX_op_sub_i64; do_addsub2: - args -= 6; nb_iargs = 4; nb_oargs = 2; /* Test if the high part of the operation is dead, but not @@ -1555,12 +1417,11 @@ static void tcg_liveness_analysis(TCGContext *s) if (dead_temps[args[0]] && !mem_temps[args[0]]) { goto do_remove; } - /* Create the single operation plus nop. */ - s->gen_opc_buf[op_index] = op = op_new; + /* Replace the opcode and adjust the args in place, + leaving 3 unused args at the end. */ + op->opc = opc = opc_new; args[1] = args[2]; args[2] = args[4]; - assert(s->gen_opc_buf[op_index + 1] == INDEX_op_nop); - tcg_set_nop(s, s->gen_opc_buf + op_index + 1, args + 3, 3); /* Fall through and mark the single-word operation live. */ nb_iargs = 2; nb_oargs = 1; @@ -1568,27 +1429,26 @@ static void tcg_liveness_analysis(TCGContext *s) goto do_not_remove; case INDEX_op_mulu2_i32: - op_new = INDEX_op_mul_i32; - op_new2 = INDEX_op_muluh_i32; - have_op_new2 = TCG_TARGET_HAS_muluh_i32; + opc_new = INDEX_op_mul_i32; + opc_new2 = INDEX_op_muluh_i32; + have_opc_new2 = TCG_TARGET_HAS_muluh_i32; goto do_mul2; case INDEX_op_muls2_i32: - op_new = INDEX_op_mul_i32; - op_new2 = INDEX_op_mulsh_i32; - have_op_new2 = TCG_TARGET_HAS_mulsh_i32; + opc_new = INDEX_op_mul_i32; + opc_new2 = INDEX_op_mulsh_i32; + have_opc_new2 = TCG_TARGET_HAS_mulsh_i32; goto do_mul2; case INDEX_op_mulu2_i64: - op_new = INDEX_op_mul_i64; - op_new2 = INDEX_op_muluh_i64; - have_op_new2 = TCG_TARGET_HAS_muluh_i64; + opc_new = INDEX_op_mul_i64; + opc_new2 = INDEX_op_muluh_i64; + have_opc_new2 = TCG_TARGET_HAS_muluh_i64; goto do_mul2; case INDEX_op_muls2_i64: - op_new = INDEX_op_mul_i64; - op_new2 = INDEX_op_mulsh_i64; - have_op_new2 = TCG_TARGET_HAS_mulsh_i64; + opc_new = INDEX_op_mul_i64; + opc_new2 = INDEX_op_mulsh_i64; + have_opc_new2 = TCG_TARGET_HAS_mulsh_i64; goto do_mul2; do_mul2: - args -= 4; nb_iargs = 2; nb_oargs = 2; if (dead_temps[args[1]] && !mem_temps[args[1]]) { @@ -1597,28 +1457,25 @@ static void tcg_liveness_analysis(TCGContext *s) goto do_remove; } /* The high part of the operation is dead; generate the low. */ - s->gen_opc_buf[op_index] = op = op_new; + op->opc = opc = opc_new; args[1] = args[2]; args[2] = args[3]; - } else if (have_op_new2 && dead_temps[args[0]] + } else if (have_opc_new2 && dead_temps[args[0]] && !mem_temps[args[0]]) { - /* The low part of the operation is dead; generate the high. */ - s->gen_opc_buf[op_index] = op = op_new2; + /* The low part of the operation is dead; generate the high. */ + op->opc = opc = opc_new2; args[0] = args[1]; args[1] = args[2]; args[2] = args[3]; } else { goto do_not_remove; } - assert(s->gen_opc_buf[op_index + 1] == INDEX_op_nop); - tcg_set_nop(s, s->gen_opc_buf + op_index + 1, args + 3, 1); /* Mark the single-word operation live. */ nb_oargs = 1; goto do_not_remove; default: /* XXX: optimize by hardcoding common cases (e.g. triadic ops) */ - args -= def->nb_args; nb_iargs = def->nb_iargs; nb_oargs = def->nb_oargs; @@ -1626,24 +1483,20 @@ static void tcg_liveness_analysis(TCGContext *s) its outputs are dead. We assume that nb_oargs == 0 implies side effects */ if (!(def->flags & TCG_OPF_SIDE_EFFECTS) && nb_oargs != 0) { - for(i = 0; i < nb_oargs; i++) { + for (i = 0; i < nb_oargs; i++) { arg = args[i]; if (!dead_temps[arg] || mem_temps[arg]) { goto do_not_remove; } } do_remove: - tcg_set_nop(s, s->gen_opc_buf + op_index, args, def->nb_args); -#ifdef CONFIG_PROFILER - s->del_op_count++; -#endif + tcg_op_remove(s, op); } else { do_not_remove: - /* output args are dead */ dead_args = 0; sync_args = 0; - for(i = 0; i < nb_oargs; i++) { + for (i = 0; i < nb_oargs; i++) { arg = args[i]; if (dead_temps[arg]) { dead_args |= (1 << i); @@ -1664,23 +1517,18 @@ static void tcg_liveness_analysis(TCGContext *s) } /* input args are live */ - for(i = nb_oargs; i < nb_oargs + nb_iargs; i++) { + for (i = nb_oargs; i < nb_oargs + nb_iargs; i++) { arg = args[i]; if (dead_temps[arg]) { dead_args |= (1 << i); } dead_temps[arg] = 0; } - s->op_dead_args[op_index] = dead_args; - s->op_sync_args[op_index] = sync_args; + s->op_dead_args[oi] = dead_args; + s->op_sync_args[oi] = sync_args; } break; } - op_index--; - } - - if (args != s->gen_opparam_buf) { - tcg_abort(); } } #else @@ -2247,11 +2095,11 @@ static void tcg_reg_alloc_op(TCGContext *s, #define STACK_DIR(x) (x) #endif -static int tcg_reg_alloc_call(TCGContext *s, const TCGOpDef *def, - TCGOpcode opc, const TCGArg *args, - uint16_t dead_args, uint8_t sync_args) +static void tcg_reg_alloc_call(TCGContext *s, int nb_oargs, int nb_iargs, + const TCGArg * const args, uint16_t dead_args, + uint8_t sync_args) { - int nb_iargs, nb_oargs, flags, nb_regs, i, reg, nb_params; + int flags, nb_regs, i, reg; TCGArg arg; TCGTemp *ts; intptr_t stack_offset; @@ -2260,22 +2108,16 @@ static int tcg_reg_alloc_call(TCGContext *s, const TCGOpDef *def, int allocate_args; TCGRegSet allocated_regs; - arg = *args++; - - nb_oargs = arg >> 16; - nb_iargs = arg & 0xffff; - nb_params = nb_iargs; - func_addr = (tcg_insn_unit *)(intptr_t)args[nb_oargs + nb_iargs]; flags = args[nb_oargs + nb_iargs + 1]; nb_regs = ARRAY_SIZE(tcg_target_call_iarg_regs); - if (nb_regs > nb_params) { - nb_regs = nb_params; + if (nb_regs > nb_iargs) { + nb_regs = nb_iargs; } /* assign stack slots first */ - call_stack_size = (nb_params - nb_regs) * sizeof(tcg_target_long); + call_stack_size = (nb_iargs - nb_regs) * sizeof(tcg_target_long); call_stack_size = (call_stack_size + TCG_TARGET_STACK_ALIGN - 1) & ~(TCG_TARGET_STACK_ALIGN - 1); allocate_args = (call_stack_size > TCG_STATIC_CALL_ARGS_SIZE); @@ -2286,7 +2128,7 @@ static int tcg_reg_alloc_call(TCGContext *s, const TCGOpDef *def, } stack_offset = TCG_TARGET_CALL_STACK_OFFSET; - for(i = nb_regs; i < nb_params; i++) { + for(i = nb_regs; i < nb_iargs; i++) { arg = args[nb_oargs + i]; #ifdef TCG_TARGET_STACK_GROWSUP stack_offset -= sizeof(tcg_target_long); @@ -2393,22 +2235,26 @@ static int tcg_reg_alloc_call(TCGContext *s, const TCGOpDef *def, } } } - - return nb_iargs + nb_oargs + def->nb_cargs + 1; } #ifdef CONFIG_PROFILER static int64_t tcg_table_op_count[NB_OPS]; -static void dump_op_count(void) +void tcg_dump_op_count(FILE *f, fprintf_function cpu_fprintf) { int i; - for(i = INDEX_op_end; i < NB_OPS; i++) { - qemu_log("%s %" PRId64 "\n", tcg_op_defs[i].name, tcg_table_op_count[i]); + for (i = 0; i < NB_OPS; i++) { + cpu_fprintf(f, "%s %" PRId64 "\n", tcg_op_defs[i].name, + tcg_table_op_count[i]); } } +#else +void tcg_dump_op_count(FILE *f, fprintf_function cpu_fprintf) +{ + cpu_fprintf(f, "[TCG profiler not compiled]\n"); +} #endif @@ -2416,10 +2262,7 @@ static inline int tcg_gen_code_common(TCGContext *s, tcg_insn_unit *gen_code_buf, long search_pc) { - TCGOpcode opc; - int op_index; - const TCGOpDef *def; - const TCGArg *args; + int oi, oi_next; #ifdef DEBUG_DISAS if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) { @@ -2434,8 +2277,7 @@ static inline int tcg_gen_code_common(TCGContext *s, #endif #ifdef USE_TCG_OPTIMIZATIONS - s->gen_opparam_ptr = - tcg_optimize(s, s->gen_opc_ptr, s->gen_opparam_buf, tcg_op_defs); + tcg_optimize(s); #endif #ifdef CONFIG_PROFILER @@ -2464,56 +2306,41 @@ static inline int tcg_gen_code_common(TCGContext *s, tcg_out_tb_init(s); - args = s->gen_opparam_buf; - op_index = 0; + for (oi = s->gen_first_op_idx; oi >= 0; oi = oi_next) { + TCGOp * const op = &s->gen_op_buf[oi]; + TCGArg * const args = &s->gen_opparam_buf[op->args]; + TCGOpcode opc = op->opc; + const TCGOpDef *def = &tcg_op_defs[opc]; + uint16_t dead_args = s->op_dead_args[oi]; + uint8_t sync_args = s->op_sync_args[oi]; - for(;;) { - opc = s->gen_opc_buf[op_index]; + oi_next = op->next; #ifdef CONFIG_PROFILER tcg_table_op_count[opc]++; #endif - def = &tcg_op_defs[opc]; -#if 0 - printf("%s: %d %d %d\n", def->name, - def->nb_oargs, def->nb_iargs, def->nb_cargs); - // dump_regs(s); -#endif - switch(opc) { + + switch (opc) { case INDEX_op_mov_i32: case INDEX_op_mov_i64: - tcg_reg_alloc_mov(s, def, args, s->op_dead_args[op_index], - s->op_sync_args[op_index]); + tcg_reg_alloc_mov(s, def, args, dead_args, sync_args); break; case INDEX_op_movi_i32: case INDEX_op_movi_i64: - tcg_reg_alloc_movi(s, args, s->op_dead_args[op_index], - s->op_sync_args[op_index]); + tcg_reg_alloc_movi(s, args, dead_args, sync_args); break; case INDEX_op_debug_insn_start: - /* debug instruction */ break; - case INDEX_op_nop: - case INDEX_op_nop1: - case INDEX_op_nop2: - case INDEX_op_nop3: - break; - case INDEX_op_nopn: - args += args[0]; - goto next; case INDEX_op_discard: temp_dead(s, args[0]); break; case INDEX_op_set_label: tcg_reg_alloc_bb_end(s, s->reserved_regs); - tcg_out_label(s, args[0], s->code_ptr); + tcg_out_label(s, arg_label(args[0]), s->code_ptr); break; case INDEX_op_call: - args += tcg_reg_alloc_call(s, def, opc, args, - s->op_dead_args[op_index], - s->op_sync_args[op_index]); - goto next; - case INDEX_op_end: - goto the_end; + tcg_reg_alloc_call(s, op->callo, op->calli, args, + dead_args, sync_args); + break; default: /* Sanity check that we've not introduced any unhandled opcodes. */ if (def->flags & TCG_OPF_NOT_PRESENT) { @@ -2522,21 +2349,17 @@ static inline int tcg_gen_code_common(TCGContext *s, /* Note: in order to speed up the code, it would be much faster to have specialized register allocator functions for some common argument patterns */ - tcg_reg_alloc_op(s, def, opc, args, s->op_dead_args[op_index], - s->op_sync_args[op_index]); + tcg_reg_alloc_op(s, def, opc, args, dead_args, sync_args); break; } - args += def->nb_args; - next: if (search_pc >= 0 && search_pc < tcg_current_code_size(s)) { - return op_index; + return oi; } - op_index++; #ifndef NDEBUG check_regs(s); #endif } - the_end: + /* Generate TB finalization at the end of block */ tcg_out_tb_finalize(s); return -1; @@ -2547,14 +2370,18 @@ int tcg_gen_code(TCGContext *s, tcg_insn_unit *gen_code_buf) #ifdef CONFIG_PROFILER { int n; - n = (s->gen_opc_ptr - s->gen_opc_buf); + + n = s->gen_last_op_idx + 1; s->op_count += n; - if (n > s->op_count_max) + if (n > s->op_count_max) { s->op_count_max = n; + } - s->temp_count += s->nb_temps; - if (s->nb_temps > s->temp_count_max) - s->temp_count_max = s->nb_temps; + n = s->nb_temps; + s->temp_count += n; + if (n > s->temp_count_max) { + s->temp_count_max = n; + } } #endif @@ -2620,8 +2447,6 @@ void tcg_dump_info(FILE *f, fprintf_function cpu_fprintf) s->restore_count); cpu_fprintf(f, " avg cycles %0.1f\n", s->restore_count ? (double)s->restore_time / s->restore_count : 0); - - dump_op_count(); } #else void tcg_dump_info(FILE *f, fprintf_function cpu_fprintf) diff --git a/tcg/tcg.h b/tcg/tcg.h index 7285f71..add7f75 100644 --- a/tcg/tcg.h +++ b/tcg/tcg.h @@ -167,7 +167,8 @@ typedef struct TCGRelocation { } TCGRelocation; typedef struct TCGLabel { - int has_value; + unsigned has_value : 1; + unsigned id : 31; union { uintptr_t value; tcg_insn_unit *value_ptr; @@ -183,8 +184,6 @@ typedef struct TCGPool { #define TCG_POOL_CHUNK_SIZE 32768 -#define TCG_MAX_LABELS 512 - #define TCG_MAX_TEMPS 512 /* when the size of the arguments of a called function is smaller than @@ -448,10 +447,28 @@ typedef struct TCGTempSet { unsigned long l[BITS_TO_LONGS(TCG_MAX_TEMPS)]; } TCGTempSet; +typedef struct TCGOp { + TCGOpcode opc : 8; + + /* The number of out and in parameter for a call. */ + unsigned callo : 2; + unsigned calli : 6; + + /* Index of the arguments for this op, or -1 for zero-operand ops. */ + signed args : 16; + + /* Index of the prex/next op, or -1 for the end of the list. */ + signed prev : 16; + signed next : 16; +} TCGOp; + +QEMU_BUILD_BUG_ON(NB_OPS > 0xff); +QEMU_BUILD_BUG_ON(OPC_BUF_SIZE >= 0x7fff); +QEMU_BUILD_BUG_ON(OPPARAM_BUF_SIZE >= 0x7fff); + struct TCGContext { uint8_t *pool_cur, *pool_end; TCGPool *pool_first, *pool_current, *pool_first_large; - TCGLabel *labels; int nb_labels; int nb_globals; int nb_temps; @@ -469,9 +486,6 @@ struct TCGContext { corresponding output argument needs to be sync to memory. */ - /* tells in which temporary a given register is. It does not take - into account fixed registers */ - int reg_to_temp[TCG_TARGET_NB_REGS]; TCGRegSet reserved_regs; intptr_t current_frame_offset; intptr_t frame_start; @@ -479,8 +493,6 @@ struct TCGContext { int frame_reg; tcg_insn_unit *code_ptr; - TCGTemp temps[TCG_MAX_TEMPS]; /* globals first, temps after */ - TCGTempSet free_temps[TCG_TYPE_COUNT * 2]; GHashTable *helpers; @@ -508,14 +520,10 @@ struct TCGContext { int goto_tb_issue_mask; #endif - uint16_t gen_opc_buf[OPC_BUF_SIZE]; - TCGArg gen_opparam_buf[OPPARAM_BUF_SIZE]; - - uint16_t *gen_opc_ptr; - TCGArg *gen_opparam_ptr; - target_ulong gen_opc_pc[OPC_BUF_SIZE]; - uint16_t gen_opc_icount[OPC_BUF_SIZE]; - uint8_t gen_opc_instr_start[OPC_BUF_SIZE]; + int gen_first_op_idx; + int gen_last_op_idx; + int gen_next_op_idx; + int gen_next_parm_idx; /* Code generation. Note that we specifically do not use tcg_insn_unit here, because there's too much arithmetic throughout that relies @@ -533,10 +541,36 @@ struct TCGContext { /* The TCGBackendData structure is private to tcg-target.c. */ struct TCGBackendData *be; + + TCGTempSet free_temps[TCG_TYPE_COUNT * 2]; + TCGTemp temps[TCG_MAX_TEMPS]; /* globals first, temps after */ + + /* tells in which temporary a given register is. It does not take + into account fixed registers */ + int reg_to_temp[TCG_TARGET_NB_REGS]; + + TCGOp gen_op_buf[OPC_BUF_SIZE]; + TCGArg gen_opparam_buf[OPPARAM_BUF_SIZE]; + + target_ulong gen_opc_pc[OPC_BUF_SIZE]; + uint16_t gen_opc_icount[OPC_BUF_SIZE]; + uint8_t gen_opc_instr_start[OPC_BUF_SIZE]; }; extern TCGContext tcg_ctx; +/* The number of opcodes emitted so far. */ +static inline int tcg_op_buf_count(void) +{ + return tcg_ctx.gen_next_op_idx; +} + +/* Test for whether to terminate the TB for using too many opcodes. */ +static inline bool tcg_op_buf_full(void) +{ + return tcg_op_buf_count() >= OPC_MAX_SIZE; +} + /* pool based memory allocation */ void *tcg_malloc_internal(TCGContext *s, int size); @@ -610,6 +644,7 @@ int tcg_check_temp_count(void); #endif void tcg_dump_info(FILE *f, fprintf_function cpu_fprintf); +void tcg_dump_op_count(FILE *f, fprintf_function cpu_fprintf); #define TCG_CT_ALIAS 0x80 #define TCG_CT_IALIAS 0x40 @@ -705,11 +740,8 @@ void tcg_add_target_add_op_defs(const TCGTargetOpDef *tdefs); void tcg_gen_callN(TCGContext *s, void *func, TCGArg ret, int nargs, TCGArg *args); -void tcg_gen_shifti_i64(TCGv_i64 ret, TCGv_i64 arg1, - int c, int right, int arith); - -TCGArg *tcg_optimize(TCGContext *s, uint16_t *tcg_opc_ptr, TCGArg *args, - TCGOpDef *tcg_op_def); +void tcg_op_remove(TCGContext *s, TCGOp *op); +void tcg_optimize(TCGContext *s); /* only used for debugging purposes */ void tcg_dump_ops(TCGContext *s); @@ -720,6 +752,33 @@ TCGv_i64 tcg_const_i64(int64_t val); TCGv_i32 tcg_const_local_i32(int32_t val); TCGv_i64 tcg_const_local_i64(int64_t val); +TCGLabel *gen_new_label(void); + +/** + * label_arg + * @l: label + * + * Encode a label for storage in the TCG opcode stream. + */ + +static inline TCGArg label_arg(TCGLabel *l) +{ + return (uintptr_t)l; +} + +/** + * arg_label + * @i: value + * + * The opposite of label_arg. Retrieve a label from the + * encoding of the TCG opcode stream. + */ + +static inline TCGLabel *arg_label(TCGArg i) +{ + return (TCGLabel *)(uintptr_t)i; +} + /** * tcg_ptr_byte_diff * @a, @b: addresses to be differenced diff --git a/tcg/tci/tcg-target.c b/tcg/tci/tcg-target.c index 03a7b46..fb2339d 100644 --- a/tcg/tci/tcg-target.c +++ b/tcg/tci/tcg-target.c @@ -460,14 +460,13 @@ static void tcg_out_ri64(TCGContext *s, int const_arg, TCGArg arg) #endif /* Write label. */ -static void tci_out_label(TCGContext *s, TCGArg arg) +static void tci_out_label(TCGContext *s, TCGLabel *label) { - TCGLabel *label = &s->labels[arg]; if (label->has_value) { tcg_out_i(s, label->u.value); assert(label->u.value); } else { - tcg_out_reloc(s, s->code_ptr, sizeof(tcg_target_ulong), arg, 0); + tcg_out_reloc(s, s->code_ptr, sizeof(tcg_target_ulong), label, 0); s->code_ptr += sizeof(tcg_target_ulong); } } @@ -565,7 +564,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, s->tb_next_offset[args[0]] = tcg_current_code_size(s); break; case INDEX_op_br: - tci_out_label(s, args[0]); + tci_out_label(s, arg_label(args[0])); break; case INDEX_op_setcond_i32: tcg_out_r(s, args[0]); @@ -689,7 +688,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, tcg_out_r(s, args[0]); tcg_out_ri64(s, const_args[1], args[1]); tcg_out8(s, args[2]); /* condition */ - tci_out_label(s, args[3]); + tci_out_label(s, arg_label(args[3])); break; case INDEX_op_bswap16_i64: /* Optional (TCG_TARGET_HAS_bswap16_i64). */ case INDEX_op_bswap32_i64: /* Optional (TCG_TARGET_HAS_bswap32_i64). */ @@ -742,7 +741,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, tcg_out_ri32(s, const_args[2], args[2]); tcg_out_ri32(s, const_args[3], args[3]); tcg_out8(s, args[4]); /* condition */ - tci_out_label(s, args[5]); + tci_out_label(s, arg_label(args[5])); break; case INDEX_op_mulu2_i32: tcg_out_r(s, args[0]); @@ -755,7 +754,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, tcg_out_r(s, args[0]); tcg_out_ri32(s, const_args[1], args[1]); tcg_out8(s, args[2]); /* condition */ - tci_out_label(s, args[3]); + tci_out_label(s, arg_label(args[3])); break; case INDEX_op_qemu_ld_i32: tcg_out_r(s, *args++); diff --git a/tci.c b/tci.c index 4711ee4..28292b3 100644 --- a/tci.c +++ b/tci.c @@ -506,19 +506,6 @@ uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr) tb_ptr += 2; switch (opc) { - case INDEX_op_end: - case INDEX_op_nop: - break; - case INDEX_op_nop1: - case INDEX_op_nop2: - case INDEX_op_nop3: - case INDEX_op_nopn: - case INDEX_op_discard: - TODO(); - break; - case INDEX_op_set_label: - TODO(); - break; case INDEX_op_call: t0 = tci_read_ri(&tb_ptr); #if TCG_TARGET_REG_BITS == 32 diff --git a/tests/.gitignore b/tests/.gitignore index e2e4957..0dcb618 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -5,6 +5,7 @@ check-qjson check-qlist check-qstring check-qom-interface +rcutorture test-aio test-bitops test-coroutine @@ -26,6 +27,7 @@ test-qmp-input-strict test-qmp-input-visitor test-qmp-marshal.c test-qmp-output-visitor +test-rcu-list test-rfifolock test-string-input-visitor test-string-output-visitor @@ -33,6 +35,7 @@ test-thread-pool test-throttle test-visitor-serialization test-vmstate +test-write-threshold test-x86-cpuid test-xbzrle *-test diff --git a/tests/Makefile b/tests/Makefile index 16f0e4c..55aa745 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -48,8 +48,11 @@ check-unit-y += tests/test-hbitmap$(EXESUF) check-unit-y += tests/test-x86-cpuid$(EXESUF) # all code tested by test-x86-cpuid is inside topology.h gcov-files-test-x86-cpuid-y = +ifeq ($(CONFIG_SOFTMMU),y) check-unit-y += tests/test-xbzrle$(EXESUF) -gcov-files-test-xbzrle-y = xbzrle.c +gcov-files-test-xbzrle-y = migration/xbzrle.c +check-unit-$(CONFIG_POSIX) += tests/test-vmstate$(EXESUF) +endif check-unit-y += tests/test-cutils$(EXESUF) gcov-files-test-cutils-y += util/cutils.c check-unit-y += tests/test-mul64$(EXESUF) @@ -57,13 +60,18 @@ gcov-files-test-mul64-y = util/host-utils.c check-unit-y += tests/test-int128$(EXESUF) # all code tested by test-int128 is inside int128.h gcov-files-test-int128-y = +check-unit-y += tests/rcutorture$(EXESUF) +gcov-files-rcutorture-y = util/rcu.c +check-unit-y += tests/test-rcu-list$(EXESUF) +gcov-files-test-rcu-list-y = util/rcu.c check-unit-y += tests/test-bitops$(EXESUF) check-unit-$(CONFIG_HAS_GLIB_SUBPROCESS_TESTS) += tests/test-qdev-global-props$(EXESUF) check-unit-y += tests/check-qom-interface$(EXESUF) gcov-files-check-qom-interface-y = qom/object.c -check-unit-$(CONFIG_POSIX) += tests/test-vmstate$(EXESUF) check-unit-y += tests/test-qemu-opts$(EXESUF) gcov-files-test-qemu-opts-y = qom/test-qemu-opts.c +check-unit-y += tests/test-write-threshold$(EXESUF) +gcov-files-test-write-threshold-y = block/write-threshold.c check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh @@ -165,6 +173,7 @@ gcov-files-i386-y += hw/usb/dev-hid.c gcov-files-i386-y += hw/usb/dev-storage.c check-qtest-i386-y += tests/usb-hcd-xhci-test$(EXESUF) gcov-files-i386-y += hw/usb/hcd-xhci.c +check-qtest-i386-y += tests/pc-cpu-test$(EXESUF) check-qtest-i386-$(CONFIG_LINUX) += tests/vhost-user-test$(EXESUF) check-qtest-x86_64-y = $(check-qtest-i386-y) gcov-files-i386-y += i386-softmmu/hw/timer/mc146818rtc.c @@ -183,6 +192,8 @@ gcov-files-sparc-y += hw/timer/m48t59.c gcov-files-sparc64-y += hw/timer/m48t59.c check-qtest-arm-y = tests/tmp105-test$(EXESUF) gcov-files-arm-y += hw/misc/tmp105.c +check-qtest-arm-y += tests/virtio-blk-test$(EXESUF) +gcov-files-arm-y += arm-softmmu/hw/block/virtio-blk.c check-qtest-ppc-y += tests/boot-order-test$(EXESUF) check-qtest-ppc64-y += tests/boot-order-test$(EXESUF) check-qtest-ppc64-y += tests/spapr-phb-test$(EXESUF) @@ -221,7 +232,8 @@ test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \ tests/test-qmp-input-visitor.o tests/test-qmp-input-strict.o \ tests/test-qmp-commands.o tests/test-visitor-serialization.o \ tests/test-x86-cpuid.o tests/test-mul64.o tests/test-int128.o \ - tests/test-opts-visitor.o tests/test-qmp-event.o + tests/test-opts-visitor.o tests/test-qmp-event.o \ + tests/rcutorture.o tests/test-rcu-list.o test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o \ tests/test-qapi-event.o @@ -230,8 +242,6 @@ $(test-obj-y): QEMU_INCLUDES += -Itests QEMU_CFLAGS += -I$(SRC_PATH)/tests qom-core-obj = qom/object.o qom/qom-qobject.o qom/container.o -tests/test-x86-cpuid.o: QEMU_INCLUDES += -I$(SRC_PATH)/target-i386 - tests/check-qint$(EXESUF): tests/check-qint.o libqemuutil.a tests/check-qstring$(EXESUF): tests/check-qstring.o libqemuutil.a tests/check-qdict$(EXESUF): tests/check-qdict.o libqemuutil.a @@ -247,9 +257,12 @@ tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(block-obj-y) libqemu tests/test-iov$(EXESUF): tests/test-iov.o libqemuutil.a tests/test-hbitmap$(EXESUF): tests/test-hbitmap.o libqemuutil.a libqemustub.a tests/test-x86-cpuid$(EXESUF): tests/test-x86-cpuid.o -tests/test-xbzrle$(EXESUF): tests/test-xbzrle.o xbzrle.o page_cache.o libqemuutil.a +tests/test-xbzrle$(EXESUF): tests/test-xbzrle.o migration/xbzrle.o page_cache.o libqemuutil.a tests/test-cutils$(EXESUF): tests/test-cutils.o util/cutils.o tests/test-int128$(EXESUF): tests/test-int128.o +tests/rcutorture$(EXESUF): tests/rcutorture.o libqemuutil.a libqemustub.a +tests/test-rcu-list$(EXESUF): tests/test-rcu-list.o libqemuutil.a libqemustub.a + tests/test-qdev-global-props$(EXESUF): tests/test-qdev-global-props.o \ hw/core/qdev.o hw/core/qdev-properties.o hw/core/hotplug.o\ hw/core/irq.o \ @@ -258,7 +271,9 @@ tests/test-qdev-global-props$(EXESUF): tests/test-qdev-global-props.o \ $(test-qapi-obj-y) \ libqemuutil.a libqemustub.a tests/test-vmstate$(EXESUF): tests/test-vmstate.o \ - vmstate.o qemu-file.o qemu-file-unix.o \ + migration/vmstate.o migration/qemu-file.o migration/qemu-file-buf.o \ + migration/qemu-file-unix.o qjson.o \ + $(qom-core-obj) \ libqemuutil.a libqemustub.a tests/test-qapi-types.c tests/test-qapi-types.h :\ @@ -295,13 +310,14 @@ tests/test-opts-visitor$(EXESUF): tests/test-opts-visitor.o $(test-qapi-obj-y) l tests/test-mul64$(EXESUF): tests/test-mul64.o libqemuutil.a tests/test-bitops$(EXESUF): tests/test-bitops.o libqemuutil.a -libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o -libqos-obj-y += tests/libqos/i2c.o +libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o +libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o libqos-pc-obj-y = $(libqos-obj-y) tests/libqos/pci-pc.o -libqos-pc-obj-y += tests/libqos/malloc-pc.o +libqos-pc-obj-y += tests/libqos/malloc-pc.o tests/libqos/libqos-pc.o +libqos-pc-obj-y += tests/libqos/ahci.o libqos-omap-obj-y = $(libqos-obj-y) tests/libqos/i2c-omap.o -libqos-virtio-obj-y = $(libqos-obj-y) $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o libqos-usb-obj-y = $(libqos-pc-obj-y) tests/libqos/usb.o +libqos-virtio-obj-y = $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o tests/libqos/virtio-mmio.o tests/libqos/malloc-generic.o tests/rtc-test$(EXESUF): tests/rtc-test.o tests/m48t59-test$(EXESUF): tests/m48t59-test.o @@ -317,7 +333,7 @@ tests/tmp105-test$(EXESUF): tests/tmp105-test.o $(libqos-omap-obj-y) tests/i440fx-test$(EXESUF): tests/i440fx-test.o $(libqos-pc-obj-y) tests/fw_cfg-test$(EXESUF): tests/fw_cfg-test.o $(libqos-pc-obj-y) tests/e1000-test$(EXESUF): tests/e1000-test.o -tests/rtl8139-test$(EXESUF): tests/rtl8139-test.o +tests/rtl8139-test$(EXESUF): tests/rtl8139-test.o $(libqos-pc-obj-y) tests/pcnet-test$(EXESUF): tests/pcnet-test.o tests/eepro100-test$(EXESUF): tests/eepro100-test.o tests/vmxnet3-test$(EXESUF): tests/vmxnet3-test.o @@ -348,9 +364,11 @@ tests/usb-hcd-ohci-test$(EXESUF): tests/usb-hcd-ohci-test.o $(libqos-usb-obj-y) tests/usb-hcd-uhci-test$(EXESUF): tests/usb-hcd-uhci-test.o $(libqos-usb-obj-y) tests/usb-hcd-ehci-test$(EXESUF): tests/usb-hcd-ehci-test.o $(libqos-usb-obj-y) tests/usb-hcd-xhci-test$(EXESUF): tests/usb-hcd-xhci-test.o $(libqos-usb-obj-y) +tests/pc-cpu-test$(EXESUF): tests/pc-cpu-test.o tests/vhost-user-test$(EXESUF): tests/vhost-user-test.o qemu-char.o qemu-timer.o $(qtest-obj-y) tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o tests/test-qemu-opts$(EXESUF): tests/test-qemu-opts.o libqemuutil.a libqemustub.a +tests/test-write-threshold$(EXESUF): tests/test-write-threshold.o $(block-obj-y) libqemuutil.a libqemustub.a ifeq ($(CONFIG_POSIX),y) LIBS += -lutil diff --git a/tests/acpi-test-data/pc/DSDT b/tests/acpi-test-data/pc/DSDT index ee9cc6781cea3a9515b9a176eea3459f8e4d8655..1693c3783b34cfb5473e7ef65ba6f8deee390898 100644 GIT binary patch delta 93 zcmeB>nI+EU66_K(i<^OgF=8TD$7V$)ZbktgX?^DSV5j&1XHNsqV8{5)Sn_=@yE_r4EU*;Gp delta 697 zcmYk4&ubGw6vy9Wu^lIqn4mUMO1gLu1hGjQauFn(&C*17>#!4wu!qUPo0m{}2_BjR zp+N*Kdl3(!P><~*^=|(J{{ubv4|p;s*EhRv@GZRkzF(i&*_n5h`8w;$?tm5n$kSss zKisX}Q?jFk-LF&IcVxN4*!y__avZe9+L0Zy;0sPD<_R}+>Nl1bd>I$wQ%ru$;wxwI z8Ne^dVl(<40bDHOpHYn9b#YW_0i-|x263F%Y_uF4Rj@+R2!6!z*FUMgsEvWzVe#^f z!wF_$ZU4Hmm(Gvjn#GjuOL(bw#a;=yy_L#0!pY=Y;cjn@L|rOfM&T%39PJ+@67xVn z_k7@?XzVFC(XiLH-)gd=C1k6a>NVkH^0shW&5>wmWFj;&5w6mu7n+~}!;|6XV@@7C ztZf?E3G}6keTm*=PC)1_Re91d28 zH-m}r<$&ZNVrDSqNq|nZOW~K*XZM5I9f6CdWa}GSNs$G{5E%a_`X<+TWA zc0Jsrjao423C~A7W7Yph86I7A94fjdryIJ(@cfQzqU)h^wQ^C3CknsnYQlgv^Bk#& f*WZ;SAf9VKf?#?*9hMG`Ja+cbN!vVsPB)CdFB+w> diff --git a/tests/acpi-test-data/pc/SSDT b/tests/acpi-test-data/pc/SSDT index 558e4c85b031ec081045aec6cc382a680e9c6009..59be315237a835d886965a069c4c19ef9c0b293f 100644 GIT binary patch delta 689 zcmZuvL2J}N6n>Lk);OEMu8mp{G-r{TB6w~xNjJOhq(deZC51UiX$5hmwTGV4ZHsy> z{R2JpR%j0X0KIziC-mkI5Ohz*H;D?C&f)RC_uhPb^S=4kKbxachS}M50Ae4}J4MuA zTTn7kf(<=Wu~hbhYxMO88w?BdsdOL%GP7nDF|kgpmM;Co_RRW+?Me@Re}5aG=>t%x zs;(ZzV?f;@4Fa>OKK-im0zUuPg?yj22O>fN@i4SSB0S%F45nH|r0ee1`u1dJ>&5yu z7G>tsZX?yEbU-f3i;}c8$*3)2kzPfDuXE$c+c&RvHvZ?jb%_kl;hZ?TM|%&7Dydb|nm^!=A66D!G$79l-mu03r1 zb(Y5>G{@l2uaxq2Ly zD37Efe3(d*Pj5EYWQ4OT(NhePxlV^`eLX&@zgK`=w5cmSPnpUOQv~nFP-1qqNC{m delta 464 zcmZ8du};G<6nrjiWjSfnmJCSz0ut_o*hmutshZTprWB=;GnTCzVk#7g0VdQRC{i{? zK7frsLH!Ggopb?D@9wO7_wIdn-6lc=a*Y8{^OfwUgW3czr$X5Pfc3#a7T%r;(ka&;i+^H5za;utMf2uT(Ibp zQrgvvn2;7gde7M~K?b4p!P){YvAd^)^k8O`Y*k-oki%OwCyv`p0xpffWtL~IeU)&D zW>bUlT=|J;HwZaHCO<-V@M0)Z35QW!j#WL5wS5?erM;1gJw|9f--5}XKK5!pr*vCG z7${RuafIjdP#6U)=<%F0?!2|OA5 j8|?F8+zE8lmNzq{;htavNEeQvRAJ=>MY$hemGAm5%;#$; diff --git a/tests/acpi-test-data/pc/SSDT.bridge b/tests/acpi-test-data/pc/SSDT.bridge new file mode 100644 index 0000000000000000000000000000000000000000..fa6136935caa32981bc14879af3ae9378cf01bcf GIT binary patch literal 4334 zcmeH}KX2Pc7{>1;#%4}bOv!ZYIC0FOL*2op<3DjXNhD+0G3kR&GE&eeha#u~6iJYO zK;Xqz0JVF+fQQV{M`%AnKSGAiT?(|Gs=hC&SN7lwI%bdt>K;BnK6ghR@1;^f`%jxO ze%Ltho62oGlD>j-oH2%N{I|o((Q2w1sq}X}Gxb}l(wG)ce=f6v1vXJl7iMxITdK`VY#`j^)XD@L(wd#TTlb~s<#*1p z(x~2M?|m8783?#BC{&v{C1z-TIAgJ&7-f2 z^!THu49qBqTQFZ-2-)q0$0*nW4yq}m77R*=h2(S@MKNkM_FXtM;joXbeQ3!?DU5nH zqghQvp-OqQ8!Mii&gECB5ZHt85Dv-e1-e-+_)L6$x(mQyg*0%F20WLBV19{T>A`euZ7-omDPBptp(hv6#Nx?~a zUn&nThBnVAh~QU~EBTm!6a_+IXjg>-ItmpQm|hp$l4+Qg0D@ONR!Ox=84v7NykNgx=PnA*J3S z^o|y#)Fncfbm#+0y-Vm_ElR1&gf3r&I`L9)7`F>&rQoE(&sLAYk9hof8!3l#jb5*pvGTHlLBgo;JoFb_H9=$iu!i#~yYD z8^OrK-nPabb_cKX$ix2bqc?Nh&58hMcr8i8R{-b>AHD_p3SKbs%}ihT@D2UKyGXDWj_4+OV literal 0 HcmV?d00001 diff --git a/tests/acpi-test-data/q35/DSDT b/tests/acpi-test-data/q35/DSDT index ef0c75f4236de3e6f727e21991533f2bf8279dee..e9ac11c38ff98c36ba0c06abfd6b01fcb94b4452 100644 GIT binary patch delta 94 zcmexnzr&i#CDq_(c7iaS2 tnQSDTFnNu1wt#?(mL5z&bd$U0N}N0T=DQB^!7816;T>OMienp%fR6@6MRwUEFu>J?EbH^=bEVk9B2tcv%C$-xo~b z@&4K)B|A!ZgSzEKzN~a^vRCg)U~r&FDPa$<_!UP>_mP{_`mX6EzphIu34gua0LXa& z25DL(sSX1&2J;Gjr0M5BIWByuPQdaKb$9D{s#__=KiO)b{siv0OnH%n_jF2jDCEv| z4oAXY!6OmuY>!1&<&jP-EImmD1Zw%KOIa=UT;&+*)=CsVsoji-GPMpy*KHvb#dU&ml`lAPP? za#kaW8AhGJ(W$RfKq$~iyiod7w=)lDv(T3c!@<797{kDO2JvhyAC|rzdF~(RBt2*G I)&6Px1+A=|7XSbN diff --git a/tests/acpi-test-data/q35/SSDT b/tests/acpi-test-data/q35/SSDT index 4e45510181c4038505ae1815b76799a2cd0e8808..e87f5a35c15b9fc27dcab39f384b5c0f235ec93c 100644 GIT binary patch literal 680 zcmaJ9|7z zdVYW+1w9`CpFny4LBUm+8JkeD#rVCMS-p9));g)a^Z?k`crG&?#0N@h%rO8UOkZy* z7TZS0M#*XD7+LDA_zHjZaR5DmWq^hPo3M@82M+XCIeNdFbW@lWA>3zJ~%l%Jw7{mc6f?O zT}9mOCLM4-VSydk%sU6~=7OqYGqf#+LWu8~p=0qL#~mQMPZbjN<|A@kuO3_5WLuS~ zZly)^gWH{~RQo}8{O0w`v*-U+UyHS10a|#gN?kE`EqG8bMN2T49j|^jmMRQ8x4tlW zRKbN*c?3w%3=v&{KQQTD$dIIaaO7_l)$Y^YXa4C3-qZCLj#Tqv*k{a>&f<3e^2zc{ zB~G3cB|4cLL*tkzDe*0D>F{Ak@E2j=Fpx+2@VOItU6uHE<*{NUdg_rAd z==x|)rZX2~HOZAtPrby$ELk{C^AoLgkLF38Zvj-dHXW9NnW*ks-^6+uEu-@>T(dKJ zZa$4*3NOJ_wWGx{v7Qa%>oIeQrgTdSN*!<~AjAzkvZNZ>FfV2>Ab3vc zS&_}s*{Z)`qjMhuk6+#Q=Z)2xV`#-JK`yc0AvQLn9d;mUV;*~$8k@)bfH;p#-1%>w z4zw^f9WWh7-NNr*G@e^eV+9YKekF|^7Ttsm1|upi_zH*O5Y7=PdV=$jvF9hcuM0QP(|lc^3ydrE5bF#sS;UvDDj zTSkwJl9SLevd|l&%lysf9#{gy05uslW^1t)%t1dB9diA~@LK%iy_yn#eYy_N#sN6g zwM(h^ENCL6Nbg5ofB$n1ADu$Al-;pPP(Pi-J(a643Lk)Dc9D@kINCoxJUM!?e~eCD zM%-;X?QuS4fgRY)I|c97oT3BMx4R655Z^O>r^_vlJ3w}yA|&b!Z_vcm@=14###Tk5 z8)*^!P18SN`PyK2rHBN2*!g?=a>`XMU%1ak4B` zv6IDlffLg4nw8+{Ic!o>smcoG0_U66;wF(!R*a?rniS2H&=ir{GuZXjCL4x4OSECx zB-gB3Tx+Xzg1NKgDH<&U-_EE#Z+waNS1nx~hQ={bT;MHl>+oSq@E2j= #include "libqtest.h" +#include "libqos/libqos-pc.h" +#include "libqos/ahci.h" #include "libqos/pci-pc.h" -#include "libqos/malloc-pc.h" #include "qemu-common.h" #include "qemu/host-utils.h" @@ -41,361 +42,18 @@ /* Test-specific defines. */ #define TEST_IMAGE_SIZE (64 * 1024 * 1024) -/*** Supplementary PCI Config Space IDs & Masks ***/ -#define PCI_DEVICE_ID_INTEL_Q35_AHCI (0x2922) -#define PCI_MSI_FLAGS_RESERVED (0xFF00) -#define PCI_PM_CTRL_RESERVED (0xFC) -#define PCI_BCC(REG32) ((REG32) >> 24) -#define PCI_PI(REG32) (((REG32) >> 8) & 0xFF) -#define PCI_SCC(REG32) (((REG32) >> 16) & 0xFF) - -/*** Recognized AHCI Device Types ***/ -#define AHCI_INTEL_ICH9 (PCI_DEVICE_ID_INTEL_Q35_AHCI << 16 | \ - PCI_VENDOR_ID_INTEL) - -/*** AHCI/HBA Register Offsets and Bitmasks ***/ -#define AHCI_CAP (0) -#define AHCI_CAP_NP (0x1F) -#define AHCI_CAP_SXS (0x20) -#define AHCI_CAP_EMS (0x40) -#define AHCI_CAP_CCCS (0x80) -#define AHCI_CAP_NCS (0x1F00) -#define AHCI_CAP_PSC (0x2000) -#define AHCI_CAP_SSC (0x4000) -#define AHCI_CAP_PMD (0x8000) -#define AHCI_CAP_FBSS (0x10000) -#define AHCI_CAP_SPM (0x20000) -#define AHCI_CAP_SAM (0x40000) -#define AHCI_CAP_RESERVED (0x80000) -#define AHCI_CAP_ISS (0xF00000) -#define AHCI_CAP_SCLO (0x1000000) -#define AHCI_CAP_SAL (0x2000000) -#define AHCI_CAP_SALP (0x4000000) -#define AHCI_CAP_SSS (0x8000000) -#define AHCI_CAP_SMPS (0x10000000) -#define AHCI_CAP_SSNTF (0x20000000) -#define AHCI_CAP_SNCQ (0x40000000) -#define AHCI_CAP_S64A (0x80000000) - -#define AHCI_GHC (1) -#define AHCI_GHC_HR (0x01) -#define AHCI_GHC_IE (0x02) -#define AHCI_GHC_MRSM (0x04) -#define AHCI_GHC_RESERVED (0x7FFFFFF8) -#define AHCI_GHC_AE (0x80000000) - -#define AHCI_IS (2) -#define AHCI_PI (3) -#define AHCI_VS (4) - -#define AHCI_CCCCTL (5) -#define AHCI_CCCCTL_EN (0x01) -#define AHCI_CCCCTL_RESERVED (0x06) -#define AHCI_CCCCTL_CC (0xFF00) -#define AHCI_CCCCTL_TV (0xFFFF0000) - -#define AHCI_CCCPORTS (6) -#define AHCI_EMLOC (7) - -#define AHCI_EMCTL (8) -#define AHCI_EMCTL_STSMR (0x01) -#define AHCI_EMCTL_CTLTM (0x100) -#define AHCI_EMCTL_CTLRST (0x200) -#define AHCI_EMCTL_RESERVED (0xF0F0FCFE) - -#define AHCI_CAP2 (9) -#define AHCI_CAP2_BOH (0x01) -#define AHCI_CAP2_NVMP (0x02) -#define AHCI_CAP2_APST (0x04) -#define AHCI_CAP2_RESERVED (0xFFFFFFF8) - -#define AHCI_BOHC (10) -#define AHCI_RESERVED (11) -#define AHCI_NVMHCI (24) -#define AHCI_VENDOR (40) -#define AHCI_PORTS (64) - -/*** Port Memory Offsets & Bitmasks ***/ -#define AHCI_PX_CLB (0) -#define AHCI_PX_CLB_RESERVED (0x1FF) - -#define AHCI_PX_CLBU (1) - -#define AHCI_PX_FB (2) -#define AHCI_PX_FB_RESERVED (0xFF) - -#define AHCI_PX_FBU (3) - -#define AHCI_PX_IS (4) -#define AHCI_PX_IS_DHRS (0x1) -#define AHCI_PX_IS_PSS (0x2) -#define AHCI_PX_IS_DSS (0x4) -#define AHCI_PX_IS_SDBS (0x8) -#define AHCI_PX_IS_UFS (0x10) -#define AHCI_PX_IS_DPS (0x20) -#define AHCI_PX_IS_PCS (0x40) -#define AHCI_PX_IS_DMPS (0x80) -#define AHCI_PX_IS_RESERVED (0x23FFF00) -#define AHCI_PX_IS_PRCS (0x400000) -#define AHCI_PX_IS_IPMS (0x800000) -#define AHCI_PX_IS_OFS (0x1000000) -#define AHCI_PX_IS_INFS (0x4000000) -#define AHCI_PX_IS_IFS (0x8000000) -#define AHCI_PX_IS_HBDS (0x10000000) -#define AHCI_PX_IS_HBFS (0x20000000) -#define AHCI_PX_IS_TFES (0x40000000) -#define AHCI_PX_IS_CPDS (0x80000000) - -#define AHCI_PX_IE (5) -#define AHCI_PX_IE_DHRE (0x1) -#define AHCI_PX_IE_PSE (0x2) -#define AHCI_PX_IE_DSE (0x4) -#define AHCI_PX_IE_SDBE (0x8) -#define AHCI_PX_IE_UFE (0x10) -#define AHCI_PX_IE_DPE (0x20) -#define AHCI_PX_IE_PCE (0x40) -#define AHCI_PX_IE_DMPE (0x80) -#define AHCI_PX_IE_RESERVED (0x23FFF00) -#define AHCI_PX_IE_PRCE (0x400000) -#define AHCI_PX_IE_IPME (0x800000) -#define AHCI_PX_IE_OFE (0x1000000) -#define AHCI_PX_IE_INFE (0x4000000) -#define AHCI_PX_IE_IFE (0x8000000) -#define AHCI_PX_IE_HBDE (0x10000000) -#define AHCI_PX_IE_HBFE (0x20000000) -#define AHCI_PX_IE_TFEE (0x40000000) -#define AHCI_PX_IE_CPDE (0x80000000) - -#define AHCI_PX_CMD (6) -#define AHCI_PX_CMD_ST (0x1) -#define AHCI_PX_CMD_SUD (0x2) -#define AHCI_PX_CMD_POD (0x4) -#define AHCI_PX_CMD_CLO (0x8) -#define AHCI_PX_CMD_FRE (0x10) -#define AHCI_PX_CMD_RESERVED (0xE0) -#define AHCI_PX_CMD_CCS (0x1F00) -#define AHCI_PX_CMD_MPSS (0x2000) -#define AHCI_PX_CMD_FR (0x4000) -#define AHCI_PX_CMD_CR (0x8000) -#define AHCI_PX_CMD_CPS (0x10000) -#define AHCI_PX_CMD_PMA (0x20000) -#define AHCI_PX_CMD_HPCP (0x40000) -#define AHCI_PX_CMD_MPSP (0x80000) -#define AHCI_PX_CMD_CPD (0x100000) -#define AHCI_PX_CMD_ESP (0x200000) -#define AHCI_PX_CMD_FBSCP (0x400000) -#define AHCI_PX_CMD_APSTE (0x800000) -#define AHCI_PX_CMD_ATAPI (0x1000000) -#define AHCI_PX_CMD_DLAE (0x2000000) -#define AHCI_PX_CMD_ALPE (0x4000000) -#define AHCI_PX_CMD_ASP (0x8000000) -#define AHCI_PX_CMD_ICC (0xF0000000) - -#define AHCI_PX_RES1 (7) - -#define AHCI_PX_TFD (8) -#define AHCI_PX_TFD_STS (0xFF) -#define AHCI_PX_TFD_STS_ERR (0x01) -#define AHCI_PX_TFD_STS_CS1 (0x06) -#define AHCI_PX_TFD_STS_DRQ (0x08) -#define AHCI_PX_TFD_STS_CS2 (0x70) -#define AHCI_PX_TFD_STS_BSY (0x80) -#define AHCI_PX_TFD_ERR (0xFF00) -#define AHCI_PX_TFD_RESERVED (0xFFFF0000) - -#define AHCI_PX_SIG (9) -#define AHCI_PX_SIG_SECTOR_COUNT (0xFF) -#define AHCI_PX_SIG_LBA_LOW (0xFF00) -#define AHCI_PX_SIG_LBA_MID (0xFF0000) -#define AHCI_PX_SIG_LBA_HIGH (0xFF000000) - -#define AHCI_PX_SSTS (10) -#define AHCI_PX_SSTS_DET (0x0F) -#define AHCI_PX_SSTS_SPD (0xF0) -#define AHCI_PX_SSTS_IPM (0xF00) -#define AHCI_PX_SSTS_RESERVED (0xFFFFF000) -#define SSTS_DET_NO_DEVICE (0x00) -#define SSTS_DET_PRESENT (0x01) -#define SSTS_DET_ESTABLISHED (0x03) -#define SSTS_DET_OFFLINE (0x04) - -#define AHCI_PX_SCTL (11) - -#define AHCI_PX_SERR (12) -#define AHCI_PX_SERR_ERR (0xFFFF) -#define AHCI_PX_SERR_DIAG (0xFFFF0000) -#define AHCI_PX_SERR_DIAG_X (0x04000000) - -#define AHCI_PX_SACT (13) -#define AHCI_PX_CI (14) -#define AHCI_PX_SNTF (15) - -#define AHCI_PX_FBS (16) -#define AHCI_PX_FBS_EN (0x1) -#define AHCI_PX_FBS_DEC (0x2) -#define AHCI_PX_FBS_SDE (0x4) -#define AHCI_PX_FBS_DEV (0xF00) -#define AHCI_PX_FBS_ADO (0xF000) -#define AHCI_PX_FBS_DWE (0xF0000) -#define AHCI_PX_FBS_RESERVED (0xFFF000F8) - -#define AHCI_PX_RES2 (17) -#define AHCI_PX_VS (28) - -#define HBA_DATA_REGION_SIZE (256) -#define HBA_PORT_DATA_SIZE (128) -#define HBA_PORT_NUM_REG (HBA_PORT_DATA_SIZE/4) - -#define AHCI_VERSION_0_95 (0x00000905) -#define AHCI_VERSION_1_0 (0x00010000) -#define AHCI_VERSION_1_1 (0x00010100) -#define AHCI_VERSION_1_2 (0x00010200) -#define AHCI_VERSION_1_3 (0x00010300) - -/*** Structures ***/ - -/** - * Generic FIS structure. - */ -typedef struct FIS { - uint8_t fis_type; - uint8_t flags; - char data[0]; -} __attribute__((__packed__)) FIS; - -/** - * Register device-to-host FIS structure. - */ -typedef struct RegD2HFIS { - /* DW0 */ - uint8_t fis_type; - uint8_t flags; - uint8_t status; - uint8_t error; - /* DW1 */ - uint8_t lba_low; - uint8_t lba_mid; - uint8_t lba_high; - uint8_t device; - /* DW2 */ - uint8_t lba3; - uint8_t lba4; - uint8_t lba5; - uint8_t res1; - /* DW3 */ - uint16_t count; - uint8_t res2; - uint8_t res3; - /* DW4 */ - uint16_t res4; - uint16_t res5; -} __attribute__((__packed__)) RegD2HFIS; - -/** - * Register host-to-device FIS structure. - */ -typedef struct RegH2DFIS { - /* DW0 */ - uint8_t fis_type; - uint8_t flags; - uint8_t command; - uint8_t feature_low; - /* DW1 */ - uint8_t lba_low; - uint8_t lba_mid; - uint8_t lba_high; - uint8_t device; - /* DW2 */ - uint8_t lba3; - uint8_t lba4; - uint8_t lba5; - uint8_t feature_high; - /* DW3 */ - uint16_t count; - uint8_t icc; - uint8_t control; - /* DW4 */ - uint32_t aux; -} __attribute__((__packed__)) RegH2DFIS; - -/** - * Command List entry structure. - * The command list contains between 1-32 of these structures. - */ -typedef struct AHCICommand { - uint8_t b1; - uint8_t b2; - uint16_t prdtl; /* Phys Region Desc. Table Length */ - uint32_t prdbc; /* Phys Region Desc. Byte Count */ - uint32_t ctba; /* Command Table Descriptor Base Address */ - uint32_t ctbau; /* '' Upper */ - uint32_t res[4]; -} __attribute__((__packed__)) AHCICommand; - -/** - * Physical Region Descriptor; pointed to by the Command List Header, - * struct ahci_command. - */ -typedef struct PRD { - uint32_t dba; /* Data Base Address */ - uint32_t dbau; /* Data Base Address Upper */ - uint32_t res; /* Reserved */ - uint32_t dbc; /* Data Byte Count (0-indexed) & Interrupt Flag (bit 2^31) */ -} PRD; - -typedef struct HBACap { - uint32_t cap; - uint32_t cap2; -} HBACap; - /*** Globals ***/ -static QGuestAllocator *guest_malloc; -static QPCIBus *pcibus; -static uint64_t barsize; static char tmp_path[] = "/tmp/qtest.XXXXXX"; static bool ahci_pedantic; -static uint32_t ahci_fingerprint; - -/*** Macro Utilities ***/ -#define BITANY(data, mask) (((data) & (mask)) != 0) -#define BITSET(data, mask) (((data) & (mask)) == (mask)) -#define BITCLR(data, mask) (((data) & (mask)) == 0) -#define ASSERT_BIT_SET(data, mask) g_assert_cmphex((data) & (mask), ==, (mask)) -#define ASSERT_BIT_CLEAR(data, mask) g_assert_cmphex((data) & (mask), ==, 0) - -/*** IO macros for the AHCI memory registers. ***/ -#define AHCI_READ(OFST) qpci_io_readl(ahci, hba_base + (OFST)) -#define AHCI_WRITE(OFST, VAL) qpci_io_writel(ahci, hba_base + (OFST), (VAL)) -#define AHCI_RREG(regno) AHCI_READ(4 * (regno)) -#define AHCI_WREG(regno, val) AHCI_WRITE(4 * (regno), (val)) -#define AHCI_SET(regno, mask) AHCI_WREG((regno), AHCI_RREG(regno) | (mask)) -#define AHCI_CLR(regno, mask) AHCI_WREG((regno), AHCI_RREG(regno) & ~(mask)) - -/*** IO macros for port-specific offsets inside of AHCI memory. ***/ -#define PX_OFST(port, regno) (HBA_PORT_NUM_REG * (port) + AHCI_PORTS + (regno)) -#define PX_RREG(port, regno) AHCI_RREG(PX_OFST((port), (regno))) -#define PX_WREG(port, regno, val) AHCI_WREG(PX_OFST((port), (regno)), (val)) -#define PX_SET(port, reg, mask) PX_WREG((port), (reg), \ - PX_RREG((port), (reg)) | (mask)); -#define PX_CLR(port, reg, mask) PX_WREG((port), (reg), \ - PX_RREG((port), (reg)) & ~(mask)); - -/* For calculating how big the PRD table needs to be: */ -#define CMD_TBL_SIZ(n) ((0x80 + ((n) * sizeof(PRD)) + 0x7F) & ~0x7F) - /*** Function Declarations ***/ -static QPCIDevice *get_ahci_device(void); -static QPCIDevice *start_ahci_device(QPCIDevice *dev, void **hba_base); -static void free_ahci_device(QPCIDevice *dev); -static void ahci_test_port_spec(QPCIDevice *ahci, void *hba_base, - HBACap *hcap, uint8_t port); -static void ahci_test_pci_spec(QPCIDevice *ahci); -static void ahci_test_pci_caps(QPCIDevice *ahci, uint16_t header, +static void ahci_test_port_spec(AHCIQState *ahci, uint8_t port); +static void ahci_test_pci_spec(AHCIQState *ahci); +static void ahci_test_pci_caps(AHCIQState *ahci, uint16_t header, uint8_t offset); -static void ahci_test_satacap(QPCIDevice *ahci, uint8_t offset); -static void ahci_test_msicap(QPCIDevice *ahci, uint8_t offset); -static void ahci_test_pmcap(QPCIDevice *ahci, uint8_t offset); +static void ahci_test_satacap(AHCIQState *ahci, uint8_t offset); +static void ahci_test_msicap(AHCIQState *ahci, uint8_t offset); +static void ahci_test_pmcap(AHCIQState *ahci, uint8_t offset); /*** Utilities ***/ @@ -410,288 +68,99 @@ static void string_bswap16(uint16_t *s, size_t bytes) } } -/** - * Locate, verify, and return a handle to the AHCI device. - */ -static QPCIDevice *get_ahci_device(void) +static void generate_pattern(void *buffer, size_t len, size_t cycle_len) { - QPCIDevice *ahci; - - pcibus = qpci_init_pc(); - - /* Find the AHCI PCI device and verify it's the right one. */ - ahci = qpci_device_find(pcibus, QPCI_DEVFN(0x1F, 0x02)); - g_assert(ahci != NULL); - - ahci_fingerprint = qpci_config_readl(ahci, PCI_VENDOR_ID); - - switch (ahci_fingerprint) { - case AHCI_INTEL_ICH9: - break; - default: - /* Unknown device. */ - g_assert_not_reached(); + int i, j; + unsigned char *tx = (unsigned char *)buffer; + unsigned char p; + size_t *sx; + + /* Write an indicative pattern that varies and is unique per-cycle */ + p = rand() % 256; + for (i = j = 0; i < len; i++, j++) { + tx[i] = p; + if (j % cycle_len == 0) { + p = rand() % 256; + } } - return ahci; -} - -static void free_ahci_device(QPCIDevice *ahci) -{ - /* libqos doesn't have a function for this, so free it manually */ - g_free(ahci); - - if (pcibus) { - qpci_free_pc(pcibus); - pcibus = NULL; + /* force uniqueness by writing an id per-cycle */ + for (i = 0; i < len / cycle_len; i++) { + j = i * cycle_len; + if (j + sizeof(*sx) <= len) { + sx = (size_t *)&tx[j]; + *sx = i; + } } - - /* Clear our cached barsize information. */ - barsize = 0; } /*** Test Setup & Teardown ***/ /** - * Launch QEMU with the given command line, - * and then set up interrupts and our guest malloc interface. + * Start a Q35 machine and bookmark a handle to the AHCI device. */ -static void qtest_boot(const char *cmdline_fmt, ...) +static AHCIQState *ahci_boot(void) { - va_list ap; - char *cmdline; + AHCIQState *s; + const char *cli; - va_start(ap, cmdline_fmt); - cmdline = g_strdup_vprintf(cmdline_fmt, ap); - va_end(ap); + s = g_malloc0(sizeof(AHCIQState)); - qtest_start(cmdline); - qtest_irq_intercept_in(global_qtest, "ioapic"); - guest_malloc = pc_alloc_init(); - - g_free(cmdline); -} - -/** - * Tear down the QEMU instance. - */ -static void qtest_shutdown(void) -{ - g_free(guest_malloc); - guest_malloc = NULL; - qtest_end(); -} - -/** - * Start a Q35 machine and bookmark a handle to the AHCI device. - */ -static QPCIDevice *ahci_boot(void) -{ - qtest_boot("-drive if=none,id=drive0,file=%s,cache=writeback,serial=%s" - " -M q35 " - "-device ide-hd,drive=drive0 " - "-global ide-hd.ver=%s", - tmp_path, "testdisk", "version"); + cli = "-drive if=none,id=drive0,file=%s,cache=writeback,serial=%s" + ",format=raw" + " -M q35 " + "-device ide-hd,drive=drive0 " + "-global ide-hd.ver=%s"; + s->parent = qtest_pc_boot(cli, tmp_path, "testdisk", "version"); + alloc_set_flags(s->parent->alloc, ALLOC_LEAK_ASSERT); /* Verify that we have an AHCI device present. */ - return get_ahci_device(); -} + s->dev = get_ahci_device(&s->fingerprint); -/** - * Clean up the PCI device, then terminate the QEMU instance. - */ -static void ahci_shutdown(QPCIDevice *ahci) -{ - free_ahci_device(ahci); - qtest_shutdown(); + return s; } -/*** Logical Device Initialization ***/ - /** - * Start the PCI device and sanity-check default operation. + * Clean up the PCI device, then terminate the QEMU instance. */ -static void ahci_pci_enable(QPCIDevice *ahci, void **hba_base) +static void ahci_shutdown(AHCIQState *ahci) { - uint8_t reg; - - start_ahci_device(ahci, hba_base); - - switch (ahci_fingerprint) { - case AHCI_INTEL_ICH9: - /* ICH9 has a register at PCI 0x92 that - * acts as a master port enabler mask. */ - reg = qpci_config_readb(ahci, 0x92); - reg |= 0x3F; - qpci_config_writeb(ahci, 0x92, reg); - /* 0...0111111b -- bit significant, ports 0-5 enabled. */ - ASSERT_BIT_SET(qpci_config_readb(ahci, 0x92), 0x3F); - break; - } + QOSState *qs = ahci->parent; + ahci_clean_mem(ahci); + free_ahci_device(ahci->dev); + g_free(ahci); + qtest_shutdown(qs); } /** - * Map BAR5/ABAR, and engage the PCI device. + * Boot and fully enable the HBA device. + * @see ahci_boot, ahci_pci_enable and ahci_hba_enable. */ -static QPCIDevice *start_ahci_device(QPCIDevice *ahci, void **hba_base) +static AHCIQState *ahci_boot_and_enable(void) { - /* Map AHCI's ABAR (BAR5) */ - *hba_base = qpci_iomap(ahci, 5, &barsize); + AHCIQState *ahci; + ahci = ahci_boot(); - /* turns on pci.cmd.iose, pci.cmd.mse and pci.cmd.bme */ - qpci_device_enable(ahci); + ahci_pci_enable(ahci); + ahci_hba_enable(ahci); return ahci; } -/** - * Test and initialize the AHCI's HBA memory areas. - * Initialize and start any ports with devices attached. - * Bring the HBA into the idle state. - */ -static void ahci_hba_enable(QPCIDevice *ahci, void *hba_base) -{ - /* Bits of interest in this section: - * GHC.AE Global Host Control / AHCI Enable - * PxCMD.ST Port Command: Start - * PxCMD.SUD "Spin Up Device" - * PxCMD.POD "Power On Device" - * PxCMD.FRE "FIS Receive Enable" - * PxCMD.FR "FIS Receive Running" - * PxCMD.CR "Command List Running" - */ - - g_assert(ahci != NULL); - g_assert(hba_base != NULL); - - uint32_t reg, ports_impl, clb, fb; - uint16_t i; - uint8_t num_cmd_slots; - - g_assert(hba_base != 0); - - /* Set GHC.AE to 1 */ - AHCI_SET(AHCI_GHC, AHCI_GHC_AE); - reg = AHCI_RREG(AHCI_GHC); - ASSERT_BIT_SET(reg, AHCI_GHC_AE); - - /* Read CAP.NCS, how many command slots do we have? */ - reg = AHCI_RREG(AHCI_CAP); - num_cmd_slots = ((reg & AHCI_CAP_NCS) >> ctzl(AHCI_CAP_NCS)) + 1; - g_test_message("Number of Command Slots: %u", num_cmd_slots); - - /* Determine which ports are implemented. */ - ports_impl = AHCI_RREG(AHCI_PI); - - for (i = 0; ports_impl; ports_impl >>= 1, ++i) { - if (!(ports_impl & 0x01)) { - continue; - } - - g_test_message("Initializing port %u", i); - - reg = PX_RREG(i, AHCI_PX_CMD); - if (BITCLR(reg, AHCI_PX_CMD_ST | AHCI_PX_CMD_CR | - AHCI_PX_CMD_FRE | AHCI_PX_CMD_FR)) { - g_test_message("port is idle"); - } else { - g_test_message("port needs to be idled"); - PX_CLR(i, AHCI_PX_CMD, (AHCI_PX_CMD_ST | AHCI_PX_CMD_FRE)); - /* The port has 500ms to disengage. */ - usleep(500000); - reg = PX_RREG(i, AHCI_PX_CMD); - ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_CR); - ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_FR); - g_test_message("port is now idle"); - /* The spec does allow for possibly needing a PORT RESET - * or HBA reset if we fail to idle the port. */ - } - - /* Allocate Memory for the Command List Buffer & FIS Buffer */ - /* PxCLB space ... 0x20 per command, as in 4.2.2 p 36 */ - clb = guest_alloc(guest_malloc, num_cmd_slots * 0x20); - g_test_message("CLB: 0x%08x", clb); - PX_WREG(i, AHCI_PX_CLB, clb); - g_assert_cmphex(clb, ==, PX_RREG(i, AHCI_PX_CLB)); - - /* PxFB space ... 0x100, as in 4.2.1 p 35 */ - fb = guest_alloc(guest_malloc, 0x100); - g_test_message("FB: 0x%08x", fb); - PX_WREG(i, AHCI_PX_FB, fb); - g_assert_cmphex(fb, ==, PX_RREG(i, AHCI_PX_FB)); - - /* Clear PxSERR, PxIS, then IS.IPS[x] by writing '1's. */ - PX_WREG(i, AHCI_PX_SERR, 0xFFFFFFFF); - PX_WREG(i, AHCI_PX_IS, 0xFFFFFFFF); - AHCI_WREG(AHCI_IS, (1 << i)); - - /* Verify Interrupts Cleared */ - reg = PX_RREG(i, AHCI_PX_SERR); - g_assert_cmphex(reg, ==, 0); - - reg = PX_RREG(i, AHCI_PX_IS); - g_assert_cmphex(reg, ==, 0); - - reg = AHCI_RREG(AHCI_IS); - ASSERT_BIT_CLEAR(reg, (1 << i)); - - /* Enable All Interrupts: */ - PX_WREG(i, AHCI_PX_IE, 0xFFFFFFFF); - reg = PX_RREG(i, AHCI_PX_IE); - g_assert_cmphex(reg, ==, ~((uint32_t)AHCI_PX_IE_RESERVED)); - - /* Enable the FIS Receive Engine. */ - PX_SET(i, AHCI_PX_CMD, AHCI_PX_CMD_FRE); - reg = PX_RREG(i, AHCI_PX_CMD); - ASSERT_BIT_SET(reg, AHCI_PX_CMD_FR); - - /* AHCI 1.3 spec: if !STS.BSY, !STS.DRQ and PxSSTS.DET indicates - * physical presence, a device is present and may be started. However, - * PxSERR.DIAG.X /may/ need to be cleared a priori. */ - reg = PX_RREG(i, AHCI_PX_SERR); - if (BITSET(reg, AHCI_PX_SERR_DIAG_X)) { - PX_SET(i, AHCI_PX_SERR, AHCI_PX_SERR_DIAG_X); - } - - reg = PX_RREG(i, AHCI_PX_TFD); - if (BITCLR(reg, AHCI_PX_TFD_STS_BSY | AHCI_PX_TFD_STS_DRQ)) { - reg = PX_RREG(i, AHCI_PX_SSTS); - if ((reg & AHCI_PX_SSTS_DET) == SSTS_DET_ESTABLISHED) { - /* Device Found: set PxCMD.ST := 1 */ - PX_SET(i, AHCI_PX_CMD, AHCI_PX_CMD_ST); - ASSERT_BIT_SET(PX_RREG(i, AHCI_PX_CMD), AHCI_PX_CMD_CR); - g_test_message("Started Device %u", i); - } else if ((reg & AHCI_PX_SSTS_DET)) { - /* Device present, but in some unknown state. */ - g_assert_not_reached(); - } - } - } - - /* Enable GHC.IE */ - AHCI_SET(AHCI_GHC, AHCI_GHC_IE); - reg = AHCI_RREG(AHCI_GHC); - ASSERT_BIT_SET(reg, AHCI_GHC_IE); - - /* TODO: The device should now be idling and waiting for commands. - * In the future, a small test-case to inspect the Register D2H FIS - * and clear the initial interrupts might be good. */ -} - /*** Specification Adherence Tests ***/ /** * Implementation for test_pci_spec. Ensures PCI configuration space is sane. */ -static void ahci_test_pci_spec(QPCIDevice *ahci) +static void ahci_test_pci_spec(AHCIQState *ahci) { uint8_t datab; uint16_t data; uint32_t datal; /* Most of these bits should start cleared until we turn them on. */ - data = qpci_config_readw(ahci, PCI_COMMAND); + data = qpci_config_readw(ahci->dev, PCI_COMMAND); ASSERT_BIT_CLEAR(data, PCI_COMMAND_MEMORY); ASSERT_BIT_CLEAR(data, PCI_COMMAND_MASTER); ASSERT_BIT_CLEAR(data, PCI_COMMAND_SPECIAL); /* Reserved */ @@ -703,7 +172,7 @@ static void ahci_test_pci_spec(QPCIDevice *ahci) ASSERT_BIT_CLEAR(data, PCI_COMMAND_INTX_DISABLE); ASSERT_BIT_CLEAR(data, 0xF800); /* Reserved */ - data = qpci_config_readw(ahci, PCI_STATUS); + data = qpci_config_readw(ahci->dev, PCI_STATUS); ASSERT_BIT_CLEAR(data, 0x01 | 0x02 | 0x04); /* Reserved */ ASSERT_BIT_CLEAR(data, PCI_STATUS_INTERRUPT); ASSERT_BIT_SET(data, PCI_STATUS_CAP_LIST); /* must be set */ @@ -716,7 +185,7 @@ static void ahci_test_pci_spec(QPCIDevice *ahci) ASSERT_BIT_CLEAR(data, PCI_STATUS_DETECTED_PARITY); /* RID occupies the low byte, CCs occupy the high three. */ - datal = qpci_config_readl(ahci, PCI_CLASS_REVISION); + datal = qpci_config_readl(ahci->dev, PCI_CLASS_REVISION); if (ahci_pedantic) { /* AHCI 1.3 specifies that at-boot, the RID should reset to 0x00, * Though in practice this is likely seldom true. */ @@ -739,40 +208,40 @@ static void ahci_test_pci_spec(QPCIDevice *ahci) g_assert_not_reached(); } - datab = qpci_config_readb(ahci, PCI_CACHE_LINE_SIZE); + datab = qpci_config_readb(ahci->dev, PCI_CACHE_LINE_SIZE); g_assert_cmphex(datab, ==, 0); - datab = qpci_config_readb(ahci, PCI_LATENCY_TIMER); + datab = qpci_config_readb(ahci->dev, PCI_LATENCY_TIMER); g_assert_cmphex(datab, ==, 0); /* Only the bottom 7 bits must be off. */ - datab = qpci_config_readb(ahci, PCI_HEADER_TYPE); + datab = qpci_config_readb(ahci->dev, PCI_HEADER_TYPE); ASSERT_BIT_CLEAR(datab, 0x7F); /* BIST is optional, but the low 7 bits must always start off regardless. */ - datab = qpci_config_readb(ahci, PCI_BIST); + datab = qpci_config_readb(ahci->dev, PCI_BIST); ASSERT_BIT_CLEAR(datab, 0x7F); /* BARS 0-4 do not have a boot spec, but ABAR/BAR5 must be clean. */ - datal = qpci_config_readl(ahci, PCI_BASE_ADDRESS_5); + datal = qpci_config_readl(ahci->dev, PCI_BASE_ADDRESS_5); g_assert_cmphex(datal, ==, 0); - qpci_config_writel(ahci, PCI_BASE_ADDRESS_5, 0xFFFFFFFF); - datal = qpci_config_readl(ahci, PCI_BASE_ADDRESS_5); + qpci_config_writel(ahci->dev, PCI_BASE_ADDRESS_5, 0xFFFFFFFF); + datal = qpci_config_readl(ahci->dev, PCI_BASE_ADDRESS_5); /* ABAR must be 32-bit, memory mapped, non-prefetchable and * must be >= 512 bytes. To that end, bits 0-8 must be off. */ ASSERT_BIT_CLEAR(datal, 0xFF); /* Capability list MUST be present, */ - datal = qpci_config_readl(ahci, PCI_CAPABILITY_LIST); + datal = qpci_config_readl(ahci->dev, PCI_CAPABILITY_LIST); /* But these bits are reserved. */ ASSERT_BIT_CLEAR(datal, ~0xFF); g_assert_cmphex(datal, !=, 0); /* Check specification adherence for capability extenstions. */ - data = qpci_config_readw(ahci, datal); + data = qpci_config_readw(ahci->dev, datal); - switch (ahci_fingerprint) { + switch (ahci->fingerprint) { case AHCI_INTEL_ICH9: /* Intel ICH9 Family Datasheet 14.1.19 p.550 */ g_assert_cmphex((data & 0xFF), ==, PCI_CAP_ID_MSI); @@ -785,18 +254,18 @@ static void ahci_test_pci_spec(QPCIDevice *ahci) ahci_test_pci_caps(ahci, data, (uint8_t)datal); /* Reserved. */ - datal = qpci_config_readl(ahci, PCI_CAPABILITY_LIST + 4); + datal = qpci_config_readl(ahci->dev, PCI_CAPABILITY_LIST + 4); g_assert_cmphex(datal, ==, 0); /* IPIN might vary, but ILINE must be off. */ - datab = qpci_config_readb(ahci, PCI_INTERRUPT_LINE); + datab = qpci_config_readb(ahci->dev, PCI_INTERRUPT_LINE); g_assert_cmphex(datab, ==, 0); } /** * Test PCI capabilities for AHCI specification adherence. */ -static void ahci_test_pci_caps(QPCIDevice *ahci, uint16_t header, +static void ahci_test_pci_caps(AHCIQState *ahci, uint16_t header, uint8_t offset) { uint8_t cid = header & 0xFF; @@ -820,14 +289,14 @@ static void ahci_test_pci_caps(QPCIDevice *ahci, uint16_t header, } if (next) { - ahci_test_pci_caps(ahci, qpci_config_readw(ahci, next), next); + ahci_test_pci_caps(ahci, qpci_config_readw(ahci->dev, next), next); } } /** * Test SATA PCI capabilitity for AHCI specification adherence. */ -static void ahci_test_satacap(QPCIDevice *ahci, uint8_t offset) +static void ahci_test_satacap(AHCIQState *ahci, uint8_t offset) { uint16_t dataw; uint32_t datal; @@ -835,11 +304,11 @@ static void ahci_test_satacap(QPCIDevice *ahci, uint8_t offset) g_test_message("Verifying SATACAP"); /* Assert that the SATACAP version is 1.0, And reserved bits are empty. */ - dataw = qpci_config_readw(ahci, offset + 2); + dataw = qpci_config_readw(ahci->dev, offset + 2); g_assert_cmphex(dataw, ==, 0x10); /* Grab the SATACR1 register. */ - datal = qpci_config_readw(ahci, offset + 4); + datal = qpci_config_readw(ahci->dev, offset + 4); switch (datal & 0x0F) { case 0x04: /* BAR0 */ @@ -862,30 +331,30 @@ static void ahci_test_satacap(QPCIDevice *ahci, uint8_t offset) /** * Test MSI PCI capability for AHCI specification adherence. */ -static void ahci_test_msicap(QPCIDevice *ahci, uint8_t offset) +static void ahci_test_msicap(AHCIQState *ahci, uint8_t offset) { uint16_t dataw; uint32_t datal; g_test_message("Verifying MSICAP"); - dataw = qpci_config_readw(ahci, offset + PCI_MSI_FLAGS); + dataw = qpci_config_readw(ahci->dev, offset + PCI_MSI_FLAGS); ASSERT_BIT_CLEAR(dataw, PCI_MSI_FLAGS_ENABLE); ASSERT_BIT_CLEAR(dataw, PCI_MSI_FLAGS_QSIZE); ASSERT_BIT_CLEAR(dataw, PCI_MSI_FLAGS_RESERVED); - datal = qpci_config_readl(ahci, offset + PCI_MSI_ADDRESS_LO); + datal = qpci_config_readl(ahci->dev, offset + PCI_MSI_ADDRESS_LO); g_assert_cmphex(datal, ==, 0); if (dataw & PCI_MSI_FLAGS_64BIT) { g_test_message("MSICAP is 64bit"); - datal = qpci_config_readl(ahci, offset + PCI_MSI_ADDRESS_HI); + datal = qpci_config_readl(ahci->dev, offset + PCI_MSI_ADDRESS_HI); g_assert_cmphex(datal, ==, 0); - dataw = qpci_config_readw(ahci, offset + PCI_MSI_DATA_64); + dataw = qpci_config_readw(ahci->dev, offset + PCI_MSI_DATA_64); g_assert_cmphex(dataw, ==, 0); } else { g_test_message("MSICAP is 32bit"); - dataw = qpci_config_readw(ahci, offset + PCI_MSI_DATA_32); + dataw = qpci_config_readw(ahci->dev, offset + PCI_MSI_DATA_32); g_assert_cmphex(dataw, ==, 0); } } @@ -893,36 +362,34 @@ static void ahci_test_msicap(QPCIDevice *ahci, uint8_t offset) /** * Test Power Management PCI capability for AHCI specification adherence. */ -static void ahci_test_pmcap(QPCIDevice *ahci, uint8_t offset) +static void ahci_test_pmcap(AHCIQState *ahci, uint8_t offset) { uint16_t dataw; g_test_message("Verifying PMCAP"); - dataw = qpci_config_readw(ahci, offset + PCI_PM_PMC); + dataw = qpci_config_readw(ahci->dev, offset + PCI_PM_PMC); ASSERT_BIT_CLEAR(dataw, PCI_PM_CAP_PME_CLOCK); ASSERT_BIT_CLEAR(dataw, PCI_PM_CAP_RESERVED); ASSERT_BIT_CLEAR(dataw, PCI_PM_CAP_D1); ASSERT_BIT_CLEAR(dataw, PCI_PM_CAP_D2); - dataw = qpci_config_readw(ahci, offset + PCI_PM_CTRL); + dataw = qpci_config_readw(ahci->dev, offset + PCI_PM_CTRL); ASSERT_BIT_CLEAR(dataw, PCI_PM_CTRL_STATE_MASK); ASSERT_BIT_CLEAR(dataw, PCI_PM_CTRL_RESERVED); ASSERT_BIT_CLEAR(dataw, PCI_PM_CTRL_DATA_SEL_MASK); ASSERT_BIT_CLEAR(dataw, PCI_PM_CTRL_DATA_SCALE_MASK); } -static void ahci_test_hba_spec(QPCIDevice *ahci, void *hba_base) +static void ahci_test_hba_spec(AHCIQState *ahci) { - HBACap hcap; unsigned i; - uint32_t cap, cap2, reg; + uint32_t reg; uint32_t ports; uint8_t nports_impl; uint8_t maxports; - g_assert(ahci != 0); - g_assert(hba_base != 0); + g_assert(ahci != NULL); /* * Note that the AHCI spec does expect the BIOS to set up a few things: @@ -945,15 +412,15 @@ static void ahci_test_hba_spec(QPCIDevice *ahci, void *hba_base) */ /* 1 CAP - Capabilities Register */ - cap = AHCI_RREG(AHCI_CAP); - ASSERT_BIT_CLEAR(cap, AHCI_CAP_RESERVED); + ahci->cap = ahci_rreg(ahci, AHCI_CAP); + ASSERT_BIT_CLEAR(ahci->cap, AHCI_CAP_RESERVED); /* 2 GHC - Global Host Control */ - reg = AHCI_RREG(AHCI_GHC); + reg = ahci_rreg(ahci, AHCI_GHC); ASSERT_BIT_CLEAR(reg, AHCI_GHC_HR); ASSERT_BIT_CLEAR(reg, AHCI_GHC_IE); ASSERT_BIT_CLEAR(reg, AHCI_GHC_MRSM); - if (BITSET(cap, AHCI_CAP_SAM)) { + if (BITSET(ahci->cap, AHCI_CAP_SAM)) { g_test_message("Supports AHCI-Only Mode: GHC_AE is Read-Only."); ASSERT_BIT_SET(reg, AHCI_GHC_AE); } else { @@ -962,27 +429,27 @@ static void ahci_test_hba_spec(QPCIDevice *ahci, void *hba_base) } /* 3 IS - Interrupt Status */ - reg = AHCI_RREG(AHCI_IS); + reg = ahci_rreg(ahci, AHCI_IS); g_assert_cmphex(reg, ==, 0); /* 4 PI - Ports Implemented */ - ports = AHCI_RREG(AHCI_PI); + ports = ahci_rreg(ahci, AHCI_PI); /* Ports Implemented must be non-zero. */ g_assert_cmphex(ports, !=, 0); /* Ports Implemented must be <= Number of Ports. */ nports_impl = ctpopl(ports); - g_assert_cmpuint(((AHCI_CAP_NP & cap) + 1), >=, nports_impl); + g_assert_cmpuint(((AHCI_CAP_NP & ahci->cap) + 1), >=, nports_impl); - g_assert_cmphex(barsize, >, 0); /* Ports must be within the proper range. Given a mapping of SIZE, * 256 bytes are used for global HBA control, and the rest is used * for ports data, at 0x80 bytes each. */ - maxports = (barsize - HBA_DATA_REGION_SIZE) / HBA_PORT_DATA_SIZE; + g_assert_cmphex(ahci->barsize, >, 0); + maxports = (ahci->barsize - HBA_DATA_REGION_SIZE) / HBA_PORT_DATA_SIZE; /* e.g, 30 ports for 4K of memory. (4096 - 256) / 128 = 30 */ g_assert_cmphex((reg >> maxports), ==, 0); /* 5 AHCI Version */ - reg = AHCI_RREG(AHCI_VS); + reg = ahci_rreg(ahci, AHCI_VS); switch (reg) { case AHCI_VERSION_0_95: case AHCI_VERSION_1_0: @@ -995,8 +462,8 @@ static void ahci_test_hba_spec(QPCIDevice *ahci, void *hba_base) } /* 6 Command Completion Coalescing Control: depends on CAP.CCCS. */ - reg = AHCI_RREG(AHCI_CCCCTL); - if (BITSET(cap, AHCI_CAP_CCCS)) { + reg = ahci_rreg(ahci, AHCI_CCCCTL); + if (BITSET(ahci->cap, AHCI_CAP_CCCS)) { ASSERT_BIT_CLEAR(reg, AHCI_CCCCTL_EN); ASSERT_BIT_CLEAR(reg, AHCI_CCCCTL_RESERVED); ASSERT_BIT_SET(reg, AHCI_CCCCTL_CC); @@ -1006,19 +473,19 @@ static void ahci_test_hba_spec(QPCIDevice *ahci, void *hba_base) } /* 7 CCC_PORTS */ - reg = AHCI_RREG(AHCI_CCCPORTS); + reg = ahci_rreg(ahci, AHCI_CCCPORTS); /* Must be zeroes initially regardless of CAP.CCCS */ g_assert_cmphex(reg, ==, 0); /* 8 EM_LOC */ - reg = AHCI_RREG(AHCI_EMLOC); - if (BITCLR(cap, AHCI_CAP_EMS)) { + reg = ahci_rreg(ahci, AHCI_EMLOC); + if (BITCLR(ahci->cap, AHCI_CAP_EMS)) { g_assert_cmphex(reg, ==, 0); } /* 9 EM_CTL */ - reg = AHCI_RREG(AHCI_EMCTL); - if (BITSET(cap, AHCI_CAP_EMS)) { + reg = ahci_rreg(ahci, AHCI_EMCTL); + if (BITSET(ahci->cap, AHCI_CAP_EMS)) { ASSERT_BIT_CLEAR(reg, AHCI_EMCTL_STSMR); ASSERT_BIT_CLEAR(reg, AHCI_EMCTL_CTLTM); ASSERT_BIT_CLEAR(reg, AHCI_EMCTL_CTLRST); @@ -1028,25 +495,25 @@ static void ahci_test_hba_spec(QPCIDevice *ahci, void *hba_base) } /* 10 CAP2 -- Capabilities Extended */ - cap2 = AHCI_RREG(AHCI_CAP2); - ASSERT_BIT_CLEAR(cap2, AHCI_CAP2_RESERVED); + ahci->cap2 = ahci_rreg(ahci, AHCI_CAP2); + ASSERT_BIT_CLEAR(ahci->cap2, AHCI_CAP2_RESERVED); /* 11 BOHC -- Bios/OS Handoff Control */ - reg = AHCI_RREG(AHCI_BOHC); + reg = ahci_rreg(ahci, AHCI_BOHC); g_assert_cmphex(reg, ==, 0); /* 12 -- 23: Reserved */ g_test_message("Verifying HBA reserved area is empty."); for (i = AHCI_RESERVED; i < AHCI_NVMHCI; ++i) { - reg = AHCI_RREG(i); + reg = ahci_rreg(ahci, i); g_assert_cmphex(reg, ==, 0); } /* 24 -- 39: NVMHCI */ - if (BITCLR(cap2, AHCI_CAP2_NVMP)) { + if (BITCLR(ahci->cap2, AHCI_CAP2_NVMP)) { g_test_message("Verifying HBA/NVMHCI area is empty."); for (i = AHCI_NVMHCI; i < AHCI_VENDOR; ++i) { - reg = AHCI_RREG(i); + reg = ahci_rreg(ahci, i); g_assert_cmphex(reg, ==, 0); } } @@ -1054,17 +521,15 @@ static void ahci_test_hba_spec(QPCIDevice *ahci, void *hba_base) /* 40 -- 63: Vendor */ g_test_message("Verifying HBA/Vendor area is empty."); for (i = AHCI_VENDOR; i < AHCI_PORTS; ++i) { - reg = AHCI_RREG(i); + reg = ahci_rreg(ahci, i); g_assert_cmphex(reg, ==, 0); } /* 64 -- XX: Port Space */ - hcap.cap = cap; - hcap.cap2 = cap2; for (i = 0; ports || (i < maxports); ports >>= 1, ++i) { if (BITSET(ports, 0x1)) { g_test_message("Testing port %u for spec", i); - ahci_test_port_spec(ahci, hba_base, &hcap, i); + ahci_test_port_spec(ahci, i); } else { uint16_t j; uint16_t low = AHCI_PORTS + (32 * i); @@ -1073,7 +538,7 @@ static void ahci_test_hba_spec(QPCIDevice *ahci, void *hba_base) "(reg [%u-%u]) is empty.", i, low, high - 1); for (j = low; j < high; ++j) { - reg = AHCI_RREG(j); + reg = ahci_rreg(ahci, j); g_assert_cmphex(reg, ==, 0); } } @@ -1083,42 +548,41 @@ static void ahci_test_hba_spec(QPCIDevice *ahci, void *hba_base) /** * Test the memory space for one port for specification adherence. */ -static void ahci_test_port_spec(QPCIDevice *ahci, void *hba_base, - HBACap *hcap, uint8_t port) +static void ahci_test_port_spec(AHCIQState *ahci, uint8_t port) { uint32_t reg; unsigned i; /* (0) CLB */ - reg = PX_RREG(port, AHCI_PX_CLB); + reg = ahci_px_rreg(ahci, port, AHCI_PX_CLB); ASSERT_BIT_CLEAR(reg, AHCI_PX_CLB_RESERVED); /* (1) CLBU */ - if (BITCLR(hcap->cap, AHCI_CAP_S64A)) { - reg = PX_RREG(port, AHCI_PX_CLBU); + if (BITCLR(ahci->cap, AHCI_CAP_S64A)) { + reg = ahci_px_rreg(ahci, port, AHCI_PX_CLBU); g_assert_cmphex(reg, ==, 0); } /* (2) FB */ - reg = PX_RREG(port, AHCI_PX_FB); + reg = ahci_px_rreg(ahci, port, AHCI_PX_FB); ASSERT_BIT_CLEAR(reg, AHCI_PX_FB_RESERVED); /* (3) FBU */ - if (BITCLR(hcap->cap, AHCI_CAP_S64A)) { - reg = PX_RREG(port, AHCI_PX_FBU); + if (BITCLR(ahci->cap, AHCI_CAP_S64A)) { + reg = ahci_px_rreg(ahci, port, AHCI_PX_FBU); g_assert_cmphex(reg, ==, 0); } /* (4) IS */ - reg = PX_RREG(port, AHCI_PX_IS); + reg = ahci_px_rreg(ahci, port, AHCI_PX_IS); g_assert_cmphex(reg, ==, 0); /* (5) IE */ - reg = PX_RREG(port, AHCI_PX_IE); + reg = ahci_px_rreg(ahci, port, AHCI_PX_IE); g_assert_cmphex(reg, ==, 0); /* (6) CMD */ - reg = PX_RREG(port, AHCI_PX_CMD); + reg = ahci_px_rreg(ahci, port, AHCI_PX_CMD); ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_FRE); ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_RESERVED); ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_CCS); @@ -1140,27 +604,27 @@ static void ahci_test_port_spec(QPCIDevice *ahci, void *hba_base, ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_MPSS); } /* If we do not support MPS, MPSS and MPSP must be off. */ - if (BITCLR(hcap->cap, AHCI_CAP_SMPS)) { + if (BITCLR(ahci->cap, AHCI_CAP_SMPS)) { ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_MPSS); ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_MPSP); } /* If, via CPD or MPSP we detect a drive, HPCP must be on. */ - if (BITANY(reg, AHCI_PX_CMD_CPD || AHCI_PX_CMD_MPSP)) { + if (BITANY(reg, AHCI_PX_CMD_CPD | AHCI_PX_CMD_MPSP)) { ASSERT_BIT_SET(reg, AHCI_PX_CMD_HPCP); } /* HPCP and ESP cannot both be active. */ g_assert(!BITSET(reg, AHCI_PX_CMD_HPCP | AHCI_PX_CMD_ESP)); /* If CAP.FBSS is not set, FBSCP must not be set. */ - if (BITCLR(hcap->cap, AHCI_CAP_FBSS)) { + if (BITCLR(ahci->cap, AHCI_CAP_FBSS)) { ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_FBSCP); } /* (7) RESERVED */ - reg = PX_RREG(port, AHCI_PX_RES1); + reg = ahci_px_rreg(ahci, port, AHCI_PX_RES1); g_assert_cmphex(reg, ==, 0); /* (8) TFD */ - reg = PX_RREG(port, AHCI_PX_TFD); + reg = ahci_px_rreg(ahci, port, AHCI_PX_TFD); /* At boot, prior to an FIS being received, the TFD register should be 0x7F, * which breaks down as follows, as seen in AHCI 1.3 sec 3.3.8, p. 27. */ ASSERT_BIT_SET(reg, AHCI_PX_TFD_STS_ERR); @@ -1178,53 +642,53 @@ static void ahci_test_port_spec(QPCIDevice *ahci, void *hba_base, * so we cannot expect a value here. AHCI 1.3, sec 3.3.9, pp 27-28 */ /* (10) SSTS / SCR0: SStatus */ - reg = PX_RREG(port, AHCI_PX_SSTS); + reg = ahci_px_rreg(ahci, port, AHCI_PX_SSTS); ASSERT_BIT_CLEAR(reg, AHCI_PX_SSTS_RESERVED); /* Even though the register should be 0 at boot, it is asynchronous and * prone to change, so we cannot test any well known value. */ /* (11) SCTL / SCR2: SControl */ - reg = PX_RREG(port, AHCI_PX_SCTL); + reg = ahci_px_rreg(ahci, port, AHCI_PX_SCTL); g_assert_cmphex(reg, ==, 0); /* (12) SERR / SCR1: SError */ - reg = PX_RREG(port, AHCI_PX_SERR); + reg = ahci_px_rreg(ahci, port, AHCI_PX_SERR); g_assert_cmphex(reg, ==, 0); /* (13) SACT / SCR3: SActive */ - reg = PX_RREG(port, AHCI_PX_SACT); + reg = ahci_px_rreg(ahci, port, AHCI_PX_SACT); g_assert_cmphex(reg, ==, 0); /* (14) CI */ - reg = PX_RREG(port, AHCI_PX_CI); + reg = ahci_px_rreg(ahci, port, AHCI_PX_CI); g_assert_cmphex(reg, ==, 0); /* (15) SNTF */ - reg = PX_RREG(port, AHCI_PX_SNTF); + reg = ahci_px_rreg(ahci, port, AHCI_PX_SNTF); g_assert_cmphex(reg, ==, 0); /* (16) FBS */ - reg = PX_RREG(port, AHCI_PX_FBS); + reg = ahci_px_rreg(ahci, port, AHCI_PX_FBS); ASSERT_BIT_CLEAR(reg, AHCI_PX_FBS_EN); ASSERT_BIT_CLEAR(reg, AHCI_PX_FBS_DEC); ASSERT_BIT_CLEAR(reg, AHCI_PX_FBS_SDE); ASSERT_BIT_CLEAR(reg, AHCI_PX_FBS_DEV); ASSERT_BIT_CLEAR(reg, AHCI_PX_FBS_DWE); ASSERT_BIT_CLEAR(reg, AHCI_PX_FBS_RESERVED); - if (BITSET(hcap->cap, AHCI_CAP_FBSS)) { + if (BITSET(ahci->cap, AHCI_CAP_FBSS)) { /* if Port-Multiplier FIS-based switching avail, ADO must >= 2 */ g_assert((reg & AHCI_PX_FBS_ADO) >> ctzl(AHCI_PX_FBS_ADO) >= 2); } /* [17 -- 27] RESERVED */ for (i = AHCI_PX_RES2; i < AHCI_PX_VS; ++i) { - reg = PX_RREG(port, i); + reg = ahci_px_rreg(ahci, port, i); g_assert_cmphex(reg, ==, 0); } /* [28 -- 31] Vendor-Specific */ for (i = AHCI_PX_VS; i < 32; ++i) { - reg = PX_RREG(port, i); + reg = ahci_px_rreg(ahci, port, i); if (reg) { g_test_message("INFO: Vendor register %u non-empty", i); } @@ -1235,164 +699,46 @@ static void ahci_test_port_spec(QPCIDevice *ahci, void *hba_base, * Utilizing an initialized AHCI HBA, issue an IDENTIFY command to the first * device we see, then read and check the response. */ -static void ahci_test_identify(QPCIDevice *ahci, void *hba_base) +static void ahci_test_identify(AHCIQState *ahci) { - RegD2HFIS *d2h = g_malloc0(0x20); - RegD2HFIS *pio = g_malloc0(0x20); - RegH2DFIS fis; - AHCICommand cmd; - PRD prd; - uint32_t ports, reg, clb, table, fb, data_ptr; uint16_t buff[256]; - unsigned i; + unsigned px; int rc; + uint16_t sect_size; + const size_t buffsize = 512; g_assert(ahci != NULL); - g_assert(hba_base != NULL); - - /* We need to: - * (1) Create a Command Table Buffer and update the Command List Slot #0 - * to point to this buffer. - * (2) Construct an FIS host-to-device command structure, and write it to - * the top of the command table buffer. - * (3) Create a data buffer for the IDENTIFY response to be sent to - * (4) Create a Physical Region Descriptor that points to the data buffer, - * and write it to the bottom (offset 0x80) of the command table. - * (5) Now, PxCLB points to the command list, command 0 points to - * our table, and our table contains an FIS instruction and a - * PRD that points to our rx buffer. - * (6) We inform the HBA via PxCI that there is a command ready in slot #0. + + /** + * This serves as a bit of a tutorial on AHCI device programming: + * + * (1) Create a data buffer for the IDENTIFY response to be sent to + * (2) Create a Command Table buffer, where we will store the + * command and PRDT (Physical Region Descriptor Table) + * (3) Construct an FIS host-to-device command structure, and write it to + * the top of the Command Table buffer. + * (4) Create one or more Physical Region Descriptors (PRDs) that describe + * a location in memory where data may be stored/retrieved. + * (5) Write these PRDTs to the bottom (offset 0x80) of the Command Table. + * (6) Each AHCI port has up to 32 command slots. Each slot contains a + * header that points to a Command Table buffer. Pick an unused slot + * and update it to point to the Command Table we have built. + * (7) Now: Command #n points to our Command Table, and our Command Table + * contains the FIS (that describes our command) and the PRDTL, which + * describes our buffer. + * (8) We inform the HBA via PxCI (Command Issue) that the command in slot + * #n is ready for processing. */ /* Pick the first implemented and running port */ - ports = AHCI_RREG(AHCI_PI); - for (i = 0; i < 32; ports >>= 1, ++i) { - if (ports == 0) { - i = 32; - } + px = ahci_port_select(ahci); + g_test_message("Selected port %u for test", px); - if (!(ports & 0x01)) { - continue; - } + /* Clear out the FIS Receive area and any pending interrupts. */ + ahci_port_clear(ahci, px); - reg = PX_RREG(i, AHCI_PX_CMD); - if (BITSET(reg, AHCI_PX_CMD_ST)) { - break; - } - } - g_assert_cmphex(i, <, 32); - g_test_message("Selected port %u for test", i); - - /* Clear out this port's interrupts (ignore the init register d2h fis) */ - reg = PX_RREG(i, AHCI_PX_IS); - PX_WREG(i, AHCI_PX_IS, reg); - g_assert_cmphex(PX_RREG(i, AHCI_PX_IS), ==, 0); - - /* Wipe the FIS-Recieve Buffer */ - fb = PX_RREG(i, AHCI_PX_FB); - g_assert_cmphex(fb, !=, 0); - qmemset(fb, 0x00, 0x100); - - /* Create a Command Table buffer. 0x80 is the smallest with a PRDTL of 0. */ - /* We need at least one PRD, so round up to the nearest 0x80 multiple. */ - table = guest_alloc(guest_malloc, CMD_TBL_SIZ(1)); - g_assert(table); - ASSERT_BIT_CLEAR(table, 0x7F); - - /* Create a data buffer ... where we will dump the IDENTIFY data to. */ - data_ptr = guest_alloc(guest_malloc, 512); - g_assert(data_ptr); - - /* Grab the Command List Buffer pointer */ - clb = PX_RREG(i, AHCI_PX_CLB); - g_assert(clb); - - /* Copy the existing Command #0 structure from the CLB into local memory, - * and build a new command #0. */ - memread(clb, &cmd, sizeof(cmd)); - cmd.b1 = 5; /* reg_h2d_fis is 5 double-words long */ - cmd.b2 = 0x04; /* clear PxTFD.STS.BSY when done */ - cmd.prdtl = cpu_to_le16(1); /* One PRD table entry. */ - cmd.prdbc = 0; - cmd.ctba = cpu_to_le32(table); - cmd.ctbau = 0; - - /* Construct our PRD, noting that DBC is 0-indexed. */ - prd.dba = cpu_to_le32(data_ptr); - prd.dbau = 0; - prd.res = 0; - /* 511+1 bytes, request DPS interrupt */ - prd.dbc = cpu_to_le32(511 | 0x80000000); - - /* Construct our Command FIS, Based on http://wiki.osdev.org/AHCI */ - memset(&fis, 0x00, sizeof(fis)); - fis.fis_type = 0x27; /* Register Host-to-Device FIS */ - fis.command = 0xEC; /* IDENTIFY */ - fis.device = 0; - fis.flags = 0x80; /* Indicate this is a command FIS */ - - /* We've committed nothing yet, no interrupts should be posted yet. */ - g_assert_cmphex(PX_RREG(i, AHCI_PX_IS), ==, 0); - - /* Commit the Command FIS to the Command Table */ - memwrite(table, &fis, sizeof(fis)); - - /* Commit the PRD entry to the Command Table */ - memwrite(table + 0x80, &prd, sizeof(prd)); - - /* Commit Command #0, pointing to the Table, to the Command List Buffer. */ - memwrite(clb, &cmd, sizeof(cmd)); - - /* Everything is in place, but we haven't given the go-ahead yet. */ - g_assert_cmphex(PX_RREG(i, AHCI_PX_IS), ==, 0); - - /* Issue Command #0 via PxCI */ - PX_WREG(i, AHCI_PX_CI, (1 << 0)); - while (BITSET(PX_RREG(i, AHCI_PX_TFD), AHCI_PX_TFD_STS_BSY)) { - usleep(50); - } - - /* Check for expected interrupts */ - reg = PX_RREG(i, AHCI_PX_IS); - ASSERT_BIT_SET(reg, AHCI_PX_IS_DHRS); - ASSERT_BIT_SET(reg, AHCI_PX_IS_PSS); - /* BUG: we expect AHCI_PX_IS_DPS to be set. */ - ASSERT_BIT_CLEAR(reg, AHCI_PX_IS_DPS); - - /* Clear expected interrupts and assert all interrupts now cleared. */ - PX_WREG(i, AHCI_PX_IS, AHCI_PX_IS_DHRS | AHCI_PX_IS_PSS | AHCI_PX_IS_DPS); - g_assert_cmphex(PX_RREG(i, AHCI_PX_IS), ==, 0); - - /* Check for errors. */ - reg = PX_RREG(i, AHCI_PX_SERR); - g_assert_cmphex(reg, ==, 0); - reg = PX_RREG(i, AHCI_PX_TFD); - ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_ERR); - ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_ERR); - - /* Investigate CMD #0, assert that we read 512 bytes */ - memread(clb, &cmd, sizeof(cmd)); - g_assert_cmphex(512, ==, le32_to_cpu(cmd.prdbc)); - - /* Investigate FIS responses */ - memread(fb + 0x20, pio, 0x20); - memread(fb + 0x40, d2h, 0x20); - g_assert_cmphex(pio->fis_type, ==, 0x5f); - g_assert_cmphex(d2h->fis_type, ==, 0x34); - g_assert_cmphex(pio->flags, ==, d2h->flags); - g_assert_cmphex(pio->status, ==, d2h->status); - g_assert_cmphex(pio->error, ==, d2h->error); - - reg = PX_RREG(i, AHCI_PX_TFD); - g_assert_cmphex((reg & AHCI_PX_TFD_ERR), ==, pio->error); - g_assert_cmphex((reg & AHCI_PX_TFD_STS), ==, pio->status); - /* The PIO Setup FIS contains a "bytes read" field, which is a - * 16-bit value. The Physical Region Descriptor Byte Count is - * 32-bit, but for small transfers using one PRD, it should match. */ - g_assert_cmphex(le16_to_cpu(pio->res4), ==, le32_to_cpu(cmd.prdbc)); - - /* Last, but not least: Investigate the IDENTIFY response data. */ - memread(data_ptr, &buff, 512); + /* "Read" 512 bytes using CMD_IDENTIFY into the host buffer. */ + ahci_io(ahci, px, CMD_IDENTIFY, &buff, buffsize); /* Check serial number/version in the buffer */ /* NB: IDENTIFY strings are packed in 16bit little endian chunks. @@ -1407,8 +753,45 @@ static void ahci_test_identify(QPCIDevice *ahci, void *hba_base) rc = memcmp(&buff[23], "version ", 8); g_assert_cmphex(rc, ==, 0); - g_free(d2h); - g_free(pio); + sect_size = le16_to_cpu(*((uint16_t *)(&buff[5]))); + g_assert_cmphex(sect_size, ==, 0x200); +} + +static void ahci_test_io_rw_simple(AHCIQState *ahci, unsigned bufsize, + uint8_t read_cmd, uint8_t write_cmd) +{ + uint64_t ptr; + uint8_t port; + unsigned char *tx = g_malloc(bufsize); + unsigned char *rx = g_malloc0(bufsize); + + g_assert(ahci != NULL); + + /* Pick the first running port and clear it. */ + port = ahci_port_select(ahci); + ahci_port_clear(ahci, port); + + /*** Create pattern and transfer to guest ***/ + /* Data buffer in the guest */ + ptr = ahci_alloc(ahci, bufsize); + g_assert(ptr); + + /* Write some indicative pattern to our buffer. */ + generate_pattern(tx, bufsize, AHCI_SECTOR_SIZE); + memwrite(ptr, tx, bufsize); + + /* Write this buffer to disk, then read it back to the DMA buffer. */ + ahci_guest_io(ahci, port, write_cmd, ptr, bufsize); + qmemset(ptr, 0x00, bufsize); + ahci_guest_io(ahci, port, read_cmd, ptr, bufsize); + + /*** Read back the Data ***/ + memread(ptr, rx, bufsize); + g_assert_cmphex(memcmp(tx, rx, bufsize), ==, 0); + + ahci_free(ahci, ptr); + g_free(tx); + g_free(rx); } /******************************************************************************/ @@ -1420,7 +803,7 @@ static void ahci_test_identify(QPCIDevice *ahci, void *hba_base) */ static void test_sanity(void) { - QPCIDevice *ahci; + AHCIQState *ahci; ahci = ahci_boot(); ahci_shutdown(ahci); } @@ -1431,7 +814,7 @@ static void test_sanity(void) */ static void test_pci_spec(void) { - QPCIDevice *ahci; + AHCIQState *ahci; ahci = ahci_boot(); ahci_test_pci_spec(ahci); ahci_shutdown(ahci); @@ -1443,10 +826,10 @@ static void test_pci_spec(void) */ static void test_pci_enable(void) { - QPCIDevice *ahci; - void *hba_base; + AHCIQState *ahci; + ahci = ahci_boot(); - ahci_pci_enable(ahci, &hba_base); + ahci_pci_enable(ahci); ahci_shutdown(ahci); } @@ -1456,12 +839,11 @@ static void test_pci_enable(void) */ static void test_hba_spec(void) { - QPCIDevice *ahci; - void *hba_base; + AHCIQState *ahci; ahci = ahci_boot(); - ahci_pci_enable(ahci, &hba_base); - ahci_test_hba_spec(ahci, hba_base); + ahci_pci_enable(ahci); + ahci_test_hba_spec(ahci); ahci_shutdown(ahci); } @@ -1471,12 +853,11 @@ static void test_hba_spec(void) */ static void test_hba_enable(void) { - QPCIDevice *ahci; - void *hba_base; + AHCIQState *ahci; ahci = ahci_boot(); - ahci_pci_enable(ahci, &hba_base); - ahci_hba_enable(ahci, hba_base); + ahci_pci_enable(ahci); + ahci_hba_enable(ahci); ahci_shutdown(ahci); } @@ -1486,14 +867,203 @@ static void test_hba_enable(void) */ static void test_identify(void) { - QPCIDevice *ahci; - void *hba_base; + AHCIQState *ahci; + + ahci = ahci_boot_and_enable(); + ahci_test_identify(ahci); + ahci_shutdown(ahci); +} + +/** + * Fragmented DMA test: Perform a standard 4K DMA read/write + * test, but make sure the physical regions are fragmented to + * be very small, each just 32 bytes, to see how AHCI performs + * with chunks defined to be much less than a sector. + */ +static void test_dma_fragmented(void) +{ + AHCIQState *ahci; + AHCICommand *cmd; + uint8_t px; + size_t bufsize = 4096; + unsigned char *tx = g_malloc(bufsize); + unsigned char *rx = g_malloc0(bufsize); + uint64_t ptr; + + ahci = ahci_boot_and_enable(); + px = ahci_port_select(ahci); + ahci_port_clear(ahci, px); + + /* create pattern */ + generate_pattern(tx, bufsize, AHCI_SECTOR_SIZE); + + /* Create a DMA buffer in guest memory, and write our pattern to it. */ + ptr = guest_alloc(ahci->parent->alloc, bufsize); + g_assert(ptr); + memwrite(ptr, tx, bufsize); + + cmd = ahci_command_create(CMD_WRITE_DMA); + ahci_command_adjust(cmd, 0, ptr, bufsize, 32); + ahci_command_commit(ahci, cmd, px); + ahci_command_issue(ahci, cmd); + ahci_command_verify(ahci, cmd); + g_free(cmd); + + cmd = ahci_command_create(CMD_READ_DMA); + ahci_command_adjust(cmd, 0, ptr, bufsize, 32); + ahci_command_commit(ahci, cmd, px); + ahci_command_issue(ahci, cmd); + ahci_command_verify(ahci, cmd); + g_free(cmd); + + /* Read back the guest's receive buffer into local memory */ + memread(ptr, rx, bufsize); + guest_free(ahci->parent->alloc, ptr); + + g_assert_cmphex(memcmp(tx, rx, bufsize), ==, 0); - ahci = ahci_boot(); - ahci_pci_enable(ahci, &hba_base); - ahci_hba_enable(ahci, hba_base); - ahci_test_identify(ahci, hba_base); ahci_shutdown(ahci); + + g_free(rx); + g_free(tx); +} + +/******************************************************************************/ +/* AHCI I/O Test Matrix Definitions */ + +enum BuffLen { + LEN_BEGIN = 0, + LEN_SIMPLE = LEN_BEGIN, + LEN_DOUBLE, + LEN_LONG, + LEN_SHORT, + NUM_LENGTHS +}; + +static const char *buff_len_str[NUM_LENGTHS] = { "simple", "double", + "long", "short" }; + +enum AddrMode { + ADDR_MODE_BEGIN = 0, + ADDR_MODE_LBA28 = ADDR_MODE_BEGIN, + ADDR_MODE_LBA48, + NUM_ADDR_MODES +}; + +static const char *addr_mode_str[NUM_ADDR_MODES] = { "lba28", "lba48" }; + +enum IOMode { + MODE_BEGIN = 0, + MODE_PIO = MODE_BEGIN, + MODE_DMA, + NUM_MODES +}; + +static const char *io_mode_str[NUM_MODES] = { "pio", "dma" }; + +enum IOOps { + IO_BEGIN = 0, + IO_READ = IO_BEGIN, + IO_WRITE, + NUM_IO_OPS +}; + +typedef struct AHCIIOTestOptions { + enum BuffLen length; + enum AddrMode address_type; + enum IOMode io_type; +} AHCIIOTestOptions; + +/** + * Table of possible I/O ATA commands given a set of enumerations. + */ +static const uint8_t io_cmds[NUM_MODES][NUM_ADDR_MODES][NUM_IO_OPS] = { + [MODE_PIO] = { + [ADDR_MODE_LBA28] = { + [IO_READ] = CMD_READ_PIO, + [IO_WRITE] = CMD_WRITE_PIO }, + [ADDR_MODE_LBA48] = { + [IO_READ] = CMD_READ_PIO_EXT, + [IO_WRITE] = CMD_WRITE_PIO_EXT } + }, + [MODE_DMA] = { + [ADDR_MODE_LBA28] = { + [IO_READ] = CMD_READ_DMA, + [IO_WRITE] = CMD_WRITE_DMA }, + [ADDR_MODE_LBA48] = { + [IO_READ] = CMD_READ_DMA_EXT, + [IO_WRITE] = CMD_WRITE_DMA_EXT } + } +}; + +/** + * Test a Read/Write pattern using various commands, addressing modes, + * transfer modes, and buffer sizes. + */ +static void test_io_rw_interface(enum AddrMode lba48, enum IOMode dma, + unsigned bufsize) +{ + AHCIQState *ahci; + + ahci = ahci_boot_and_enable(); + ahci_test_io_rw_simple(ahci, bufsize, + io_cmds[dma][lba48][IO_READ], + io_cmds[dma][lba48][IO_WRITE]); + ahci_shutdown(ahci); +} + +/** + * Demultiplex the test data and invoke the actual test routine. + */ +static void test_io_interface(gconstpointer opaque) +{ + AHCIIOTestOptions *opts = (AHCIIOTestOptions *)opaque; + unsigned bufsize; + + switch (opts->length) { + case LEN_SIMPLE: + bufsize = 4096; + break; + case LEN_DOUBLE: + bufsize = 8192; + break; + case LEN_LONG: + bufsize = 4096 * 64; + break; + case LEN_SHORT: + bufsize = 512; + break; + default: + g_assert_not_reached(); + } + + test_io_rw_interface(opts->address_type, opts->io_type, bufsize); + g_free(opts); + return; +} + +static void create_ahci_io_test(enum IOMode type, enum AddrMode addr, + enum BuffLen len) +{ + static const char *arch; + char *name; + AHCIIOTestOptions *opts = g_malloc(sizeof(AHCIIOTestOptions)); + + opts->length = len; + opts->address_type = addr; + opts->io_type = type; + + if (!arch) { + arch = qtest_get_arch(); + } + + name = g_strdup_printf("/%s/ahci/io/%s/%s/%s", arch, + io_mode_str[type], + addr_mode_str[addr], + buff_len_str[len]); + + g_test_add_data_func(name, opts, test_io_interface); + g_free(name); } /******************************************************************************/ @@ -1504,6 +1074,7 @@ int main(int argc, char **argv) int fd; int ret; int c; + int i, j, k; static struct option long_options[] = { {"pedantic", no_argument, 0, 'p' }, @@ -1552,6 +1123,16 @@ int main(int argc, char **argv) qtest_add_func("/ahci/hba_enable", test_hba_enable); qtest_add_func("/ahci/identify", test_identify); + for (i = MODE_BEGIN; i < NUM_MODES; i++) { + for (j = ADDR_MODE_BEGIN; j < NUM_ADDR_MODES; j++) { + for (k = LEN_BEGIN; k < NUM_LENGTHS; k++) { + create_ahci_io_test(i, j, k); + } + } + } + + qtest_add_func("/ahci/io/dma/lba28/fragmented", test_dma_fragmented); + ret = g_test_run(); /* Cleanup */ diff --git a/tests/bios-tables-test.c b/tests/bios-tables-test.c index 9e4d205..735ac61 100644 --- a/tests/bios-tables-test.c +++ b/tests/bios-tables-test.c @@ -40,6 +40,7 @@ typedef struct { typedef struct { const char *machine; + const char *variant; uint32_t rsdp_addr; AcpiRsdpDescriptor rsdp_table; AcpiRsdtDescriptorRev1 rsdt_table; @@ -396,13 +397,14 @@ static void dump_aml_files(test_data *data, bool rebuild) int i; for (i = 0; i < data->tables->len; ++i) { + const char *ext = data->variant ? data->variant : ""; sdt = &g_array_index(data->tables, AcpiSdtTable, i); g_assert(sdt->aml); if (rebuild) { uint32_t signature = cpu_to_le32(sdt->header.signature); - aml_file = g_strdup_printf("%s/%s/%.4s", data_dir, data->machine, - (gchar *)&signature); + aml_file = g_strdup_printf("%s/%s/%.4s%s", data_dir, data->machine, + (gchar *)&signature, ext); fd = g_open(aml_file, O_WRONLY|O_TRUNC|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH); } else { @@ -509,7 +511,7 @@ static GArray *load_expected_aml(test_data *data) { int i; AcpiSdtTable *sdt; - gchar *aml_file; + gchar *aml_file = NULL; GError *error = NULL; gboolean ret; @@ -517,6 +519,7 @@ static GArray *load_expected_aml(test_data *data) for (i = 0; i < data->tables->len; ++i) { AcpiSdtTable exp_sdt; uint32_t signature; + const char *ext = data->variant ? data->variant : ""; sdt = &g_array_index(data->tables, AcpiSdtTable, i); @@ -524,8 +527,15 @@ static GArray *load_expected_aml(test_data *data) exp_sdt.header.signature = sdt->header.signature; signature = cpu_to_le32(sdt->header.signature); - aml_file = g_strdup_printf("%s/%s/%.4s", data_dir, data->machine, - (gchar *)&signature); + +try_again: + aml_file = g_strdup_printf("%s/%s/%.4s%s", data_dir, data->machine, + (gchar *)&signature, ext); + if (data->variant && !g_file_test(aml_file, G_FILE_TEST_EXISTS)) { + g_free(aml_file); + ext = ""; + goto try_again; + } exp_sdt.aml_file = aml_file; g_assert(g_file_test(aml_file, G_FILE_TEST_EXISTS)); ret = g_file_get_contents(aml_file, &exp_sdt.aml, @@ -716,7 +726,7 @@ static void test_acpi_one(const char *params, test_data *data) int i; args = g_strdup_printf("-net none -display none %s " - "-drive id=hd0,if=none,file=%s " + "-drive id=hd0,if=none,file=%s,format=raw " "-device ide-hd,drive=hd0 ", params ? params : "", disk); @@ -765,7 +775,7 @@ static void test_acpi_one(const char *params, test_data *data) g_free(args); } -static void test_acpi_tcg(void) +static void test_acpi_piix4_tcg(void) { test_data data; @@ -776,6 +786,22 @@ static void test_acpi_tcg(void) data.machine = MACHINE_PC; test_acpi_one("-machine accel=tcg", &data); free_test_data(&data); +} + +static void test_acpi_piix4_tcg_bridge(void) +{ + test_data data; + + memset(&data, 0, sizeof(data)); + data.machine = MACHINE_PC; + data.variant = ".bridge"; + test_acpi_one("-machine accel=tcg -device pci-bridge,chassis_nr=1", &data); + free_test_data(&data); +} + +static void test_acpi_q35_tcg(void) +{ + test_data data; memset(&data, 0, sizeof(data)); data.machine = MACHINE_Q35; @@ -783,6 +809,18 @@ static void test_acpi_tcg(void) free_test_data(&data); } +static void test_acpi_q35_tcg_bridge(void) +{ + test_data data; + + memset(&data, 0, sizeof(data)); + data.machine = MACHINE_Q35; + data.variant = ".bridge"; + test_acpi_one("-machine q35,accel=tcg -device pci-bridge,chassis_nr=1", + &data); + free_test_data(&data); +} + int main(int argc, char *argv[]) { const char *arch = qtest_get_arch(); @@ -799,7 +837,10 @@ int main(int argc, char *argv[]) g_test_init(&argc, &argv, NULL); if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - qtest_add_func("acpi/tcg", test_acpi_tcg); + qtest_add_func("acpi/piix4/tcg", test_acpi_piix4_tcg); + qtest_add_func("acpi/piix4/tcg/bridge", test_acpi_piix4_tcg_bridge); + qtest_add_func("acpi/q35/tcg", test_acpi_q35_tcg); + qtest_add_func("acpi/q35/tcg/bridge", test_acpi_q35_tcg_bridge); } ret = g_test_run(); unlink(disk); diff --git a/tests/drive_del-test.c b/tests/drive_del-test.c index 53fa969..8951f6f 100644 --- a/tests/drive_del-test.c +++ b/tests/drive_del-test.c @@ -103,7 +103,7 @@ static void test_after_failed_device_add(void) static void test_drive_del_device_del(void) { /* Start with a drive used by a device that unplugs instantaneously */ - qtest_start("-drive if=none,id=drive0,file=/dev/null" + qtest_start("-drive if=none,id=drive0,file=/dev/null,format=raw" " -device virtio-scsi-pci" " -device scsi-hd,drive=drive0,id=dev0"); diff --git a/tests/fdc-test.c b/tests/fdc-test.c index 203074c..3c6c83c 100644 --- a/tests/fdc-test.c +++ b/tests/fdc-test.c @@ -291,7 +291,7 @@ static void test_media_insert(void) /* Insert media in drive. DSKCHK should not be reset until a step pulse * is sent. */ qmp_discard_response("{'execute':'change', 'arguments':{" - " 'device':'floppy0', 'target': %s }}", + " 'device':'floppy0', 'target': %s, 'arg': 'raw' }}", test_image); qmp_discard_response(""); /* ignore event (FIXME open -> open transition?!) */ diff --git a/tests/fw_cfg-test.c b/tests/fw_cfg-test.c index 5c8f8d6..9be78e9 100644 --- a/tests/fw_cfg-test.c +++ b/tests/fw_cfg-test.c @@ -109,21 +109,21 @@ int main(int argc, char **argv) fw_cfg = pc_fw_cfg_init(); - g_test_add_func("/fw_cfg/signature", test_fw_cfg_signature); - g_test_add_func("/fw_cfg/id", test_fw_cfg_id); - g_test_add_func("/fw_cfg/uuid", test_fw_cfg_uuid); - g_test_add_func("/fw_cfg/ram_size", test_fw_cfg_ram_size); - g_test_add_func("/fw_cfg/nographic", test_fw_cfg_nographic); - g_test_add_func("/fw_cfg/nb_cpus", test_fw_cfg_nb_cpus); + qtest_add_func("fw_cfg/signature", test_fw_cfg_signature); + qtest_add_func("fw_cfg/id", test_fw_cfg_id); + qtest_add_func("fw_cfg/uuid", test_fw_cfg_uuid); + qtest_add_func("fw_cfg/ram_size", test_fw_cfg_ram_size); + qtest_add_func("fw_cfg/nographic", test_fw_cfg_nographic); + qtest_add_func("fw_cfg/nb_cpus", test_fw_cfg_nb_cpus); #if 0 - g_test_add_func("/fw_cfg/machine_id", test_fw_cfg_machine_id); - g_test_add_func("/fw_cfg/kernel", test_fw_cfg_kernel); - g_test_add_func("/fw_cfg/initrd", test_fw_cfg_initrd); - g_test_add_func("/fw_cfg/boot_device", test_fw_cfg_boot_device); + qtest_add_func("fw_cfg/machine_id", test_fw_cfg_machine_id); + qtest_add_func("fw_cfg/kernel", test_fw_cfg_kernel); + qtest_add_func("fw_cfg/initrd", test_fw_cfg_initrd); + qtest_add_func("fw_cfg/boot_device", test_fw_cfg_boot_device); #endif - g_test_add_func("/fw_cfg/max_cpus", test_fw_cfg_max_cpus); - g_test_add_func("/fw_cfg/numa", test_fw_cfg_numa); - g_test_add_func("/fw_cfg/boot_menu", test_fw_cfg_boot_menu); + qtest_add_func("fw_cfg/max_cpus", test_fw_cfg_max_cpus); + qtest_add_func("fw_cfg/numa", test_fw_cfg_numa); + qtest_add_func("fw_cfg/boot_menu", test_fw_cfg_boot_menu); cmdline = g_strdup_printf("-uuid 4600cb32-38ec-4b2f-8acb-81c6ea54f2d8 "); s = qtest_start(cmdline); diff --git a/tests/hd-geo-test.c b/tests/hd-geo-test.c index c84d1e7..00afc20 100644 --- a/tests/hd-geo-test.c +++ b/tests/hd-geo-test.c @@ -22,8 +22,6 @@ #include "qemu-common.h" #include "libqtest.h" -static const char test_image[] = "/tmp/qtest.XXXXXX"; - static char *create_test_img(int secs) { char *template = strdup("/tmp/qtest.XXXXXX"); @@ -208,7 +206,7 @@ static int setup_ide(int argc, char *argv[], int argv_sz, { char *s1, *s2, *s3; - s1 = g_strdup_printf("-drive id=drive%d,if=%s", + s1 = g_strdup_printf("-drive id=drive%d,if=%s,format=raw", ide_idx, dev ? "none" : "ide"); s2 = dev ? g_strdup("") : g_strdup_printf(",index=%d", ide_idx); diff --git a/tests/i440fx-test.c b/tests/i440fx-test.c index ad232b5..d0bc8de 100644 --- a/tests/i440fx-test.c +++ b/tests/i440fx-test.c @@ -342,8 +342,9 @@ static void test_i440fx_firmware(FirmwareTestFixture *fixture, g_assert(fw_pathname != NULL); /* Better hope the user didn't put metacharacters in TMPDIR and co. */ - cmdline = g_strdup_printf("-S %s %s", - fixture->is_bios ? "-bios" : "-pflash", + cmdline = g_strdup_printf("-S %s%s", fixture->is_bios + ? "-bios " + : "-drive if=pflash,format=raw,file=", fw_pathname); g_test_message("qemu cmdline: %s", cmdline); qtest_start(cmdline); @@ -382,8 +383,8 @@ static void add_firmware_test(const char *testpath, void (*setup_fixture)(FirmwareTestFixture *f, gconstpointer test_data)) { - g_test_add(testpath, FirmwareTestFixture, NULL, setup_fixture, - test_i440fx_firmware, NULL); + qtest_add(testpath, FirmwareTestFixture, NULL, setup_fixture, + test_i440fx_firmware, NULL); } static void request_bios(FirmwareTestFixture *fixture, @@ -407,10 +408,10 @@ int main(int argc, char **argv) data.num_cpus = 1; - g_test_add_data_func("/i440fx/defaults", &data, test_i440fx_defaults); - g_test_add_data_func("/i440fx/pam", &data, test_i440fx_pam); - add_firmware_test("/i440fx/firmware/bios", request_bios); - add_firmware_test("/i440fx/firmware/pflash", request_pflash); + qtest_add_data_func("i440fx/defaults", &data, test_i440fx_defaults); + qtest_add_data_func("i440fx/pam", &data, test_i440fx_pam); + add_firmware_test("i440fx/firmware/bios", request_bios); + add_firmware_test("i440fx/firmware/pflash", request_pflash); ret = g_test_run(); return ret; diff --git a/tests/ide-test.c b/tests/ide-test.c index b7a97e9..b28a302 100644 --- a/tests/ide-test.c +++ b/tests/ide-test.c @@ -118,7 +118,6 @@ static void ide_test_start(const char *cmdline_fmt, ...) va_end(ap); qtest_start(cmdline); - qtest_irq_intercept_in(global_qtest, "ioapic"); guest_malloc = pc_alloc_init(); g_free(cmdline); @@ -385,9 +384,10 @@ static void test_bmdma_no_busmaster(void) static void test_bmdma_setup(void) { ide_test_start( - "-drive file=%s,if=ide,serial=%s,cache=writeback " + "-drive file=%s,if=ide,serial=%s,cache=writeback,format=raw " "-global ide-hd.ver=%s", tmp_path, "testdisk", "version"); + qtest_irq_intercept_in(global_qtest, "ioapic"); } static void test_bmdma_teardown(void) @@ -414,7 +414,7 @@ static void test_identify(void) int ret; ide_test_start( - "-drive file=%s,if=ide,serial=%s,cache=writeback " + "-drive file=%s,if=ide,serial=%s,cache=writeback,format=raw " "-global ide-hd.ver=%s", tmp_path, "testdisk", "version"); @@ -458,7 +458,7 @@ static void test_flush(void) uint8_t data; ide_test_start( - "-drive file=blkdebug::%s,if=ide,cache=writeback", + "-drive file=blkdebug::%s,if=ide,cache=writeback,format=raw", tmp_path); /* Delay the completion of the flush request until we explicitly do it */ @@ -516,7 +516,7 @@ static void prepare_blkdebug_script(const char *debug_fn, const char *event) g_assert(ret == 0); } -static void test_retry_flush(void) +static void test_retry_flush(const char *machine) { uint8_t data; const char *s; @@ -526,7 +526,8 @@ static void test_retry_flush(void) ide_test_start( "-vnc none " - "-drive file=blkdebug:%s:%s,if=ide,cache=writeback,rerror=stop,werror=stop", + "-drive file=blkdebug:%s:%s,if=ide,cache=writeback,format=raw," + "rerror=stop,werror=stop", debug_path, tmp_path); /* FLUSH CACHE command on device 0*/ @@ -579,6 +580,16 @@ static void test_flush_nodev(void) ide_test_quit(); } +static void test_pci_retry_flush(const char *machine) +{ + test_retry_flush("pc"); +} + +static void test_isa_retry_flush(const char *machine) +{ + test_retry_flush("isapc"); +} + int main(int argc, char **argv) { const char *arch = qtest_get_arch(); @@ -616,9 +627,9 @@ int main(int argc, char **argv) qtest_add_func("/ide/bmdma/teardown", test_bmdma_teardown); qtest_add_func("/ide/flush", test_flush); - qtest_add_func("/ide/flush_nodev", test_flush_nodev); - - qtest_add_func("/ide/retry/flush", test_retry_flush); + qtest_add_func("/ide/flush/nodev", test_flush_nodev); + qtest_add_func("/ide/flush/retry_pci", test_pci_retry_flush); + qtest_add_func("/ide/flush/retry_isa", test_isa_retry_flush); ret = g_test_run(); diff --git a/tests/libqos/ahci.c b/tests/libqos/ahci.c new file mode 100644 index 0000000..b0f39a5 --- /dev/null +++ b/tests/libqos/ahci.c @@ -0,0 +1,880 @@ +/* + * libqos AHCI functions + * + * Copyright (c) 2014 John Snow + * + * 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 + +#include "libqtest.h" +#include "libqos/ahci.h" +#include "libqos/pci-pc.h" + +#include "qemu-common.h" +#include "qemu/host-utils.h" + +#include "hw/pci/pci_ids.h" +#include "hw/pci/pci_regs.h" + +typedef struct AHCICommandProp { + uint8_t cmd; /* Command Code */ + bool data; /* Data transfer command? */ + bool pio; + bool dma; + bool lba28; + bool lba48; + bool read; + bool write; + bool atapi; + bool ncq; + uint64_t size; /* Static transfer size, for commands like IDENTIFY. */ + uint32_t interrupts; /* Expected interrupts for this command. */ +} AHCICommandProp; + +AHCICommandProp ahci_command_properties[] = { + { .cmd = CMD_READ_PIO, .data = true, .pio = true, + .lba28 = true, .read = true }, + { .cmd = CMD_WRITE_PIO, .data = true, .pio = true, + .lba28 = true, .write = true }, + { .cmd = CMD_READ_PIO_EXT, .data = true, .pio = true, + .lba48 = true, .read = true }, + { .cmd = CMD_WRITE_PIO_EXT, .data = true, .pio = true, + .lba48 = true, .write = true }, + { .cmd = CMD_READ_DMA, .data = true, .dma = true, + .lba28 = true, .read = true }, + { .cmd = CMD_WRITE_DMA, .data = true, .dma = true, + .lba28 = true, .write = true }, + { .cmd = CMD_READ_DMA_EXT, .data = true, .dma = true, + .lba48 = true, .read = true }, + { .cmd = CMD_WRITE_DMA_EXT, .data = true, .dma = true, + .lba48 = true, .write = true }, + { .cmd = CMD_IDENTIFY, .data = true, .pio = true, + .size = 512, .read = true }, + { .cmd = CMD_READ_MAX, .lba28 = true }, + { .cmd = CMD_READ_MAX_EXT, .lba48 = true }, + { .cmd = CMD_FLUSH_CACHE, .data = false } +}; + +/** + * Allocate space in the guest using information in the AHCIQState object. + */ +uint64_t ahci_alloc(AHCIQState *ahci, size_t bytes) +{ + g_assert(ahci); + g_assert(ahci->parent); + return qmalloc(ahci->parent, bytes); +} + +void ahci_free(AHCIQState *ahci, uint64_t addr) +{ + g_assert(ahci); + g_assert(ahci->parent); + qfree(ahci->parent, addr); +} + +/** + * Locate, verify, and return a handle to the AHCI device. + */ +QPCIDevice *get_ahci_device(uint32_t *fingerprint) +{ + QPCIDevice *ahci; + uint32_t ahci_fingerprint; + QPCIBus *pcibus; + + pcibus = qpci_init_pc(); + + /* Find the AHCI PCI device and verify it's the right one. */ + ahci = qpci_device_find(pcibus, QPCI_DEVFN(0x1F, 0x02)); + g_assert(ahci != NULL); + + ahci_fingerprint = qpci_config_readl(ahci, PCI_VENDOR_ID); + + switch (ahci_fingerprint) { + case AHCI_INTEL_ICH9: + break; + default: + /* Unknown device. */ + g_assert_not_reached(); + } + + if (fingerprint) { + *fingerprint = ahci_fingerprint; + } + return ahci; +} + +void free_ahci_device(QPCIDevice *dev) +{ + QPCIBus *pcibus = dev ? dev->bus : NULL; + + /* libqos doesn't have a function for this, so free it manually */ + g_free(dev); + qpci_free_pc(pcibus); +} + +/* Free all memory in-use by the AHCI device. */ +void ahci_clean_mem(AHCIQState *ahci) +{ + uint8_t port, slot; + + for (port = 0; port < 32; ++port) { + if (ahci->port[port].fb) { + ahci_free(ahci, ahci->port[port].fb); + } + if (ahci->port[port].clb) { + for (slot = 0; slot < 32; slot++) { + ahci_destroy_command(ahci, port, slot); + } + ahci_free(ahci, ahci->port[port].clb); + } + } +} + +/*** Logical Device Initialization ***/ + +/** + * Start the PCI device and sanity-check default operation. + */ +void ahci_pci_enable(AHCIQState *ahci) +{ + uint8_t reg; + + start_ahci_device(ahci); + + switch (ahci->fingerprint) { + case AHCI_INTEL_ICH9: + /* ICH9 has a register at PCI 0x92 that + * acts as a master port enabler mask. */ + reg = qpci_config_readb(ahci->dev, 0x92); + reg |= 0x3F; + qpci_config_writeb(ahci->dev, 0x92, reg); + /* 0...0111111b -- bit significant, ports 0-5 enabled. */ + ASSERT_BIT_SET(qpci_config_readb(ahci->dev, 0x92), 0x3F); + break; + } + +} + +/** + * Map BAR5/ABAR, and engage the PCI device. + */ +void start_ahci_device(AHCIQState *ahci) +{ + /* Map AHCI's ABAR (BAR5) */ + ahci->hba_base = qpci_iomap(ahci->dev, 5, &ahci->barsize); + g_assert(ahci->hba_base); + + /* turns on pci.cmd.iose, pci.cmd.mse and pci.cmd.bme */ + qpci_device_enable(ahci->dev); +} + +/** + * Test and initialize the AHCI's HBA memory areas. + * Initialize and start any ports with devices attached. + * Bring the HBA into the idle state. + */ +void ahci_hba_enable(AHCIQState *ahci) +{ + /* Bits of interest in this section: + * GHC.AE Global Host Control / AHCI Enable + * PxCMD.ST Port Command: Start + * PxCMD.SUD "Spin Up Device" + * PxCMD.POD "Power On Device" + * PxCMD.FRE "FIS Receive Enable" + * PxCMD.FR "FIS Receive Running" + * PxCMD.CR "Command List Running" + */ + uint32_t reg, ports_impl; + uint16_t i; + uint8_t num_cmd_slots; + + g_assert(ahci != NULL); + + /* Set GHC.AE to 1 */ + ahci_set(ahci, AHCI_GHC, AHCI_GHC_AE); + reg = ahci_rreg(ahci, AHCI_GHC); + ASSERT_BIT_SET(reg, AHCI_GHC_AE); + + /* Cache CAP and CAP2. */ + ahci->cap = ahci_rreg(ahci, AHCI_CAP); + ahci->cap2 = ahci_rreg(ahci, AHCI_CAP2); + + /* Read CAP.NCS, how many command slots do we have? */ + num_cmd_slots = ((ahci->cap & AHCI_CAP_NCS) >> ctzl(AHCI_CAP_NCS)) + 1; + g_test_message("Number of Command Slots: %u", num_cmd_slots); + + /* Determine which ports are implemented. */ + ports_impl = ahci_rreg(ahci, AHCI_PI); + + for (i = 0; ports_impl; ports_impl >>= 1, ++i) { + if (!(ports_impl & 0x01)) { + continue; + } + + g_test_message("Initializing port %u", i); + + reg = ahci_px_rreg(ahci, i, AHCI_PX_CMD); + if (BITCLR(reg, AHCI_PX_CMD_ST | AHCI_PX_CMD_CR | + AHCI_PX_CMD_FRE | AHCI_PX_CMD_FR)) { + g_test_message("port is idle"); + } else { + g_test_message("port needs to be idled"); + ahci_px_clr(ahci, i, AHCI_PX_CMD, + (AHCI_PX_CMD_ST | AHCI_PX_CMD_FRE)); + /* The port has 500ms to disengage. */ + usleep(500000); + reg = ahci_px_rreg(ahci, i, AHCI_PX_CMD); + ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_CR); + ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_FR); + g_test_message("port is now idle"); + /* The spec does allow for possibly needing a PORT RESET + * or HBA reset if we fail to idle the port. */ + } + + /* Allocate Memory for the Command List Buffer & FIS Buffer */ + /* PxCLB space ... 0x20 per command, as in 4.2.2 p 36 */ + ahci->port[i].clb = ahci_alloc(ahci, num_cmd_slots * 0x20); + qmemset(ahci->port[i].clb, 0x00, 0x100); + g_test_message("CLB: 0x%08" PRIx64, ahci->port[i].clb); + ahci_px_wreg(ahci, i, AHCI_PX_CLB, ahci->port[i].clb); + g_assert_cmphex(ahci->port[i].clb, ==, + ahci_px_rreg(ahci, i, AHCI_PX_CLB)); + + /* PxFB space ... 0x100, as in 4.2.1 p 35 */ + ahci->port[i].fb = ahci_alloc(ahci, 0x100); + qmemset(ahci->port[i].fb, 0x00, 0x100); + g_test_message("FB: 0x%08" PRIx64, ahci->port[i].fb); + ahci_px_wreg(ahci, i, AHCI_PX_FB, ahci->port[i].fb); + g_assert_cmphex(ahci->port[i].fb, ==, + ahci_px_rreg(ahci, i, AHCI_PX_FB)); + + /* Clear PxSERR, PxIS, then IS.IPS[x] by writing '1's. */ + ahci_px_wreg(ahci, i, AHCI_PX_SERR, 0xFFFFFFFF); + ahci_px_wreg(ahci, i, AHCI_PX_IS, 0xFFFFFFFF); + ahci_wreg(ahci, AHCI_IS, (1 << i)); + + /* Verify Interrupts Cleared */ + reg = ahci_px_rreg(ahci, i, AHCI_PX_SERR); + g_assert_cmphex(reg, ==, 0); + + reg = ahci_px_rreg(ahci, i, AHCI_PX_IS); + g_assert_cmphex(reg, ==, 0); + + reg = ahci_rreg(ahci, AHCI_IS); + ASSERT_BIT_CLEAR(reg, (1 << i)); + + /* Enable All Interrupts: */ + ahci_px_wreg(ahci, i, AHCI_PX_IE, 0xFFFFFFFF); + reg = ahci_px_rreg(ahci, i, AHCI_PX_IE); + g_assert_cmphex(reg, ==, ~((uint32_t)AHCI_PX_IE_RESERVED)); + + /* Enable the FIS Receive Engine. */ + ahci_px_set(ahci, i, AHCI_PX_CMD, AHCI_PX_CMD_FRE); + reg = ahci_px_rreg(ahci, i, AHCI_PX_CMD); + ASSERT_BIT_SET(reg, AHCI_PX_CMD_FR); + + /* AHCI 1.3 spec: if !STS.BSY, !STS.DRQ and PxSSTS.DET indicates + * physical presence, a device is present and may be started. However, + * PxSERR.DIAG.X /may/ need to be cleared a priori. */ + reg = ahci_px_rreg(ahci, i, AHCI_PX_SERR); + if (BITSET(reg, AHCI_PX_SERR_DIAG_X)) { + ahci_px_set(ahci, i, AHCI_PX_SERR, AHCI_PX_SERR_DIAG_X); + } + + reg = ahci_px_rreg(ahci, i, AHCI_PX_TFD); + if (BITCLR(reg, AHCI_PX_TFD_STS_BSY | AHCI_PX_TFD_STS_DRQ)) { + reg = ahci_px_rreg(ahci, i, AHCI_PX_SSTS); + if ((reg & AHCI_PX_SSTS_DET) == SSTS_DET_ESTABLISHED) { + /* Device Found: set PxCMD.ST := 1 */ + ahci_px_set(ahci, i, AHCI_PX_CMD, AHCI_PX_CMD_ST); + ASSERT_BIT_SET(ahci_px_rreg(ahci, i, AHCI_PX_CMD), + AHCI_PX_CMD_CR); + g_test_message("Started Device %u", i); + } else if ((reg & AHCI_PX_SSTS_DET)) { + /* Device present, but in some unknown state. */ + g_assert_not_reached(); + } + } + } + + /* Enable GHC.IE */ + ahci_set(ahci, AHCI_GHC, AHCI_GHC_IE); + reg = ahci_rreg(ahci, AHCI_GHC); + ASSERT_BIT_SET(reg, AHCI_GHC_IE); + + /* TODO: The device should now be idling and waiting for commands. + * In the future, a small test-case to inspect the Register D2H FIS + * and clear the initial interrupts might be good. */ +} + +/** + * Pick the first implemented and running port + */ +unsigned ahci_port_select(AHCIQState *ahci) +{ + uint32_t ports, reg; + unsigned i; + + ports = ahci_rreg(ahci, AHCI_PI); + for (i = 0; i < 32; ports >>= 1, ++i) { + if (ports == 0) { + i = 32; + } + + if (!(ports & 0x01)) { + continue; + } + + reg = ahci_px_rreg(ahci, i, AHCI_PX_CMD); + if (BITSET(reg, AHCI_PX_CMD_ST)) { + break; + } + } + g_assert(i < 32); + return i; +} + +/** + * Clear a port's interrupts and status information prior to a test. + */ +void ahci_port_clear(AHCIQState *ahci, uint8_t port) +{ + uint32_t reg; + + /* Clear out this port's interrupts (ignore the init register d2h fis) */ + reg = ahci_px_rreg(ahci, port, AHCI_PX_IS); + ahci_px_wreg(ahci, port, AHCI_PX_IS, reg); + g_assert_cmphex(ahci_px_rreg(ahci, port, AHCI_PX_IS), ==, 0); + + /* Wipe the FIS-Recieve Buffer */ + qmemset(ahci->port[port].fb, 0x00, 0x100); +} + +/** + * Check a port for errors. + */ +void ahci_port_check_error(AHCIQState *ahci, uint8_t port) +{ + uint32_t reg; + + /* The upper 9 bits of the IS register all indicate errors. */ + reg = ahci_px_rreg(ahci, port, AHCI_PX_IS); + reg >>= 23; + g_assert_cmphex(reg, ==, 0); + + /* The Sata Error Register should be empty. */ + reg = ahci_px_rreg(ahci, port, AHCI_PX_SERR); + g_assert_cmphex(reg, ==, 0); + + /* The TFD also has two error sections. */ + reg = ahci_px_rreg(ahci, port, AHCI_PX_TFD); + ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_ERR); + ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_ERR); +} + +void ahci_port_check_interrupts(AHCIQState *ahci, uint8_t port, + uint32_t intr_mask) +{ + uint32_t reg; + + /* Check for expected interrupts */ + reg = ahci_px_rreg(ahci, port, AHCI_PX_IS); + ASSERT_BIT_SET(reg, intr_mask); + + /* Clear expected interrupts and assert all interrupts now cleared. */ + ahci_px_wreg(ahci, port, AHCI_PX_IS, intr_mask); + g_assert_cmphex(ahci_px_rreg(ahci, port, AHCI_PX_IS), ==, 0); +} + +void ahci_port_check_nonbusy(AHCIQState *ahci, uint8_t port, uint8_t slot) +{ + uint32_t reg; + + /* Assert that the command slot is no longer busy (NCQ) */ + reg = ahci_px_rreg(ahci, port, AHCI_PX_SACT); + ASSERT_BIT_CLEAR(reg, (1 << slot)); + + /* Non-NCQ */ + reg = ahci_px_rreg(ahci, port, AHCI_PX_CI); + ASSERT_BIT_CLEAR(reg, (1 << slot)); + + /* And assert that we are generally not busy. */ + reg = ahci_px_rreg(ahci, port, AHCI_PX_TFD); + ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_BSY); + ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_DRQ); +} + +void ahci_port_check_d2h_sanity(AHCIQState *ahci, uint8_t port, uint8_t slot) +{ + RegD2HFIS *d2h = g_malloc0(0x20); + uint32_t reg; + + memread(ahci->port[port].fb + 0x40, d2h, 0x20); + g_assert_cmphex(d2h->fis_type, ==, 0x34); + + reg = ahci_px_rreg(ahci, port, AHCI_PX_TFD); + g_assert_cmphex((reg & AHCI_PX_TFD_ERR) >> 8, ==, d2h->error); + g_assert_cmphex((reg & AHCI_PX_TFD_STS), ==, d2h->status); + + g_free(d2h); +} + +void ahci_port_check_pio_sanity(AHCIQState *ahci, uint8_t port, + uint8_t slot, size_t buffsize) +{ + PIOSetupFIS *pio = g_malloc0(0x20); + + /* We cannot check the Status or E_Status registers, becuase + * the status may have again changed between the PIO Setup FIS + * and the conclusion of the command with the D2H Register FIS. */ + memread(ahci->port[port].fb + 0x20, pio, 0x20); + g_assert_cmphex(pio->fis_type, ==, 0x5f); + + /* BUG: PIO Setup FIS as utilized by QEMU tries to fit the entire + * transfer size in a uint16_t field. The maximum transfer size can + * eclipse this; the field is meant to convey the size of data per + * each Data FIS, not the entire operation as a whole. For now, + * we will sanity check the broken case where applicable. */ + if (buffsize <= UINT16_MAX) { + g_assert_cmphex(le16_to_cpu(pio->tx_count), ==, buffsize); + } + + g_free(pio); +} + +void ahci_port_check_cmd_sanity(AHCIQState *ahci, uint8_t port, + uint8_t slot, size_t buffsize) +{ + AHCICommandHeader cmd; + + ahci_get_command_header(ahci, port, slot, &cmd); + g_assert_cmphex(buffsize, ==, cmd.prdbc); +} + +/* Get the command in #slot of port #port. */ +void ahci_get_command_header(AHCIQState *ahci, uint8_t port, + uint8_t slot, AHCICommandHeader *cmd) +{ + uint64_t ba = ahci->port[port].clb; + ba += slot * sizeof(AHCICommandHeader); + memread(ba, cmd, sizeof(AHCICommandHeader)); + + cmd->flags = le16_to_cpu(cmd->flags); + cmd->prdtl = le16_to_cpu(cmd->prdtl); + cmd->prdbc = le32_to_cpu(cmd->prdbc); + cmd->ctba = le64_to_cpu(cmd->ctba); +} + +/* Set the command in #slot of port #port. */ +void ahci_set_command_header(AHCIQState *ahci, uint8_t port, + uint8_t slot, AHCICommandHeader *cmd) +{ + AHCICommandHeader tmp = { .flags = 0 }; + uint64_t ba = ahci->port[port].clb; + ba += slot * sizeof(AHCICommandHeader); + + tmp.flags = cpu_to_le16(cmd->flags); + tmp.prdtl = cpu_to_le16(cmd->prdtl); + tmp.prdbc = cpu_to_le32(cmd->prdbc); + tmp.ctba = cpu_to_le64(cmd->ctba); + + memwrite(ba, &tmp, sizeof(AHCICommandHeader)); +} + +void ahci_destroy_command(AHCIQState *ahci, uint8_t port, uint8_t slot) +{ + AHCICommandHeader cmd; + + /* Obtain the Nth Command Header */ + ahci_get_command_header(ahci, port, slot, &cmd); + if (cmd.ctba == 0) { + /* No address in it, so just return -- it's empty. */ + goto tidy; + } + + /* Free the Table */ + ahci_free(ahci, cmd.ctba); + + tidy: + /* NULL the header. */ + memset(&cmd, 0x00, sizeof(cmd)); + ahci_set_command_header(ahci, port, slot, &cmd); + ahci->port[port].ctba[slot] = 0; + ahci->port[port].prdtl[slot] = 0; +} + +void ahci_write_fis(AHCIQState *ahci, RegH2DFIS *fis, uint64_t addr) +{ + RegH2DFIS tmp = *fis; + + /* The auxiliary FIS fields are defined per-command and are not + * currently implemented in libqos/ahci.o, but may or may not need + * to be flipped. */ + + /* All other FIS fields are 8 bit and do not need to be flipped. */ + tmp.count = cpu_to_le16(tmp.count); + + memwrite(addr, &tmp, sizeof(tmp)); +} + +unsigned ahci_pick_cmd(AHCIQState *ahci, uint8_t port) +{ + unsigned i; + unsigned j; + uint32_t reg; + + reg = ahci_px_rreg(ahci, port, AHCI_PX_CI); + + /* Pick the least recently used command slot that's available */ + for (i = 0; i < 32; ++i) { + j = ((ahci->port[port].next + i) % 32); + if (reg & (1 << j)) { + continue; + } + ahci_destroy_command(ahci, port, i); + ahci->port[port].next = (j + 1) % 32; + return j; + } + + g_test_message("All command slots were busy."); + g_assert_not_reached(); +} + +inline unsigned size_to_prdtl(unsigned bytes, unsigned bytes_per_prd) +{ + /* Each PRD can describe up to 4MiB */ + g_assert_cmphex(bytes_per_prd, <=, 4096 * 1024); + g_assert_cmphex(bytes_per_prd & 0x01, ==, 0x00); + return (bytes + bytes_per_prd - 1) / bytes_per_prd; +} + +/* Given a guest buffer address, perform an IO operation */ +void ahci_guest_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd, + uint64_t buffer, size_t bufsize) +{ + AHCICommand *cmd; + + cmd = ahci_command_create(ide_cmd); + ahci_command_set_buffer(cmd, buffer); + ahci_command_set_size(cmd, bufsize); + ahci_command_commit(ahci, cmd, port); + ahci_command_issue(ahci, cmd); + ahci_command_verify(ahci, cmd); + ahci_command_free(cmd); +} + +struct AHCICommand { + /* Test Management Data */ + uint8_t name; + uint8_t port; + uint8_t slot; + uint32_t interrupts; + uint64_t xbytes; + uint32_t prd_size; + uint64_t buffer; + AHCICommandProp *props; + /* Data to be transferred to the guest */ + AHCICommandHeader header; + RegH2DFIS fis; + void *atapi_cmd; +}; + +static AHCICommandProp *ahci_command_find(uint8_t command_name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ahci_command_properties); i++) { + if (ahci_command_properties[i].cmd == command_name) { + return &ahci_command_properties[i]; + } + } + + return NULL; +} + +/* Given a HOST buffer, create a buffer address and perform an IO operation. */ +void ahci_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd, + void *buffer, size_t bufsize) +{ + uint64_t ptr; + AHCICommandProp *props; + + props = ahci_command_find(ide_cmd); + g_assert(props); + ptr = ahci_alloc(ahci, bufsize); + g_assert(ptr); + + if (props->write) { + memwrite(ptr, buffer, bufsize); + } + + ahci_guest_io(ahci, port, ide_cmd, ptr, bufsize); + + if (props->read) { + memread(ptr, buffer, bufsize); + } + + ahci_free(ahci, ptr); +} + +/** + * Initializes a basic command header in memory. + * We assume that this is for an ATA command using RegH2DFIS. + */ +static void command_header_init(AHCICommand *cmd) +{ + AHCICommandHeader *hdr = &cmd->header; + AHCICommandProp *props = cmd->props; + + hdr->flags = 5; /* RegH2DFIS is 5 DW long. Must be < 32 */ + hdr->flags |= CMDH_CLR_BSY; /* Clear the BSY bit when done */ + if (props->write) { + hdr->flags |= CMDH_WRITE; + } + if (props->atapi) { + hdr->flags |= CMDH_ATAPI; + } + /* Other flags: PREFETCH, RESET, and BIST */ + hdr->prdtl = size_to_prdtl(cmd->xbytes, cmd->prd_size); + hdr->prdbc = 0; + hdr->ctba = 0; +} + +static void command_table_init(AHCICommand *cmd) +{ + RegH2DFIS *fis = &(cmd->fis); + + fis->fis_type = REG_H2D_FIS; + fis->flags = REG_H2D_FIS_CMD; /* "Command" bit */ + fis->command = cmd->name; + cmd->fis.feature_low = 0x00; + cmd->fis.feature_high = 0x00; + if (cmd->props->lba28 || cmd->props->lba48) { + cmd->fis.device = ATA_DEVICE_LBA; + } + cmd->fis.count = (cmd->xbytes / AHCI_SECTOR_SIZE); + cmd->fis.icc = 0x00; + cmd->fis.control = 0x00; + memset(cmd->fis.aux, 0x00, ARRAY_SIZE(cmd->fis.aux)); +} + +AHCICommand *ahci_command_create(uint8_t command_name) +{ + AHCICommandProp *props = ahci_command_find(command_name); + AHCICommand *cmd; + + g_assert(props); + cmd = g_malloc0(sizeof(AHCICommand)); + g_assert(!(props->dma && props->pio)); + g_assert(!(props->lba28 && props->lba48)); + g_assert(!(props->read && props->write)); + g_assert(!props->size || props->data); + + /* Defaults and book-keeping */ + cmd->props = props; + cmd->name = command_name; + cmd->xbytes = props->size; + cmd->prd_size = 4096; + cmd->buffer = 0xabad1dea; + + cmd->interrupts = AHCI_PX_IS_DHRS; + /* BUG: We expect the DPS interrupt for data commands */ + /* cmd->interrupts |= props->data ? AHCI_PX_IS_DPS : 0; */ + /* BUG: We expect the DMA Setup interrupt for DMA commands */ + /* cmd->interrupts |= props->dma ? AHCI_PX_IS_DSS : 0; */ + cmd->interrupts |= props->pio ? AHCI_PX_IS_PSS : 0; + + command_header_init(cmd); + command_table_init(cmd); + + return cmd; +} + +void ahci_command_free(AHCICommand *cmd) +{ + g_free(cmd); +} + +void ahci_command_set_flags(AHCICommand *cmd, uint16_t cmdh_flags) +{ + cmd->header.flags |= cmdh_flags; +} + +void ahci_command_clr_flags(AHCICommand *cmd, uint16_t cmdh_flags) +{ + cmd->header.flags &= ~cmdh_flags; +} + +void ahci_command_set_offset(AHCICommand *cmd, uint64_t lba_sect) +{ + RegH2DFIS *fis = &(cmd->fis); + if (cmd->props->lba28) { + g_assert_cmphex(lba_sect, <=, 0xFFFFFFF); + } else if (cmd->props->lba48) { + g_assert_cmphex(lba_sect, <=, 0xFFFFFFFFFFFF); + } else { + /* Can't set offset if we don't know the format. */ + g_assert_not_reached(); + } + + /* LBA28 uses the low nibble of the device/control register for LBA24:27 */ + fis->lba_lo[0] = (lba_sect & 0xFF); + fis->lba_lo[1] = (lba_sect >> 8) & 0xFF; + fis->lba_lo[2] = (lba_sect >> 16) & 0xFF; + if (cmd->props->lba28) { + fis->device = (fis->device & 0xF0) || (lba_sect >> 24) & 0x0F; + } + fis->lba_hi[0] = (lba_sect >> 24) & 0xFF; + fis->lba_hi[1] = (lba_sect >> 32) & 0xFF; + fis->lba_hi[2] = (lba_sect >> 40) & 0xFF; +} + +void ahci_command_set_buffer(AHCICommand *cmd, uint64_t buffer) +{ + cmd->buffer = buffer; +} + +void ahci_command_set_sizes(AHCICommand *cmd, uint64_t xbytes, + unsigned prd_size) +{ + /* Each PRD can describe up to 4MiB, and must not be odd. */ + g_assert_cmphex(prd_size, <=, 4096 * 1024); + g_assert_cmphex(prd_size & 0x01, ==, 0x00); + cmd->prd_size = prd_size; + cmd->xbytes = xbytes; + cmd->fis.count = (cmd->xbytes / AHCI_SECTOR_SIZE); + cmd->header.prdtl = size_to_prdtl(cmd->xbytes, cmd->prd_size); +} + +void ahci_command_set_size(AHCICommand *cmd, uint64_t xbytes) +{ + ahci_command_set_sizes(cmd, xbytes, cmd->prd_size); +} + +void ahci_command_set_prd_size(AHCICommand *cmd, unsigned prd_size) +{ + ahci_command_set_sizes(cmd, cmd->xbytes, prd_size); +} + +void ahci_command_adjust(AHCICommand *cmd, uint64_t offset, uint64_t buffer, + uint64_t xbytes, unsigned prd_size) +{ + ahci_command_set_sizes(cmd, xbytes, prd_size); + ahci_command_set_buffer(cmd, buffer); + ahci_command_set_offset(cmd, offset); +} + +void ahci_command_commit(AHCIQState *ahci, AHCICommand *cmd, uint8_t port) +{ + uint16_t i, prdtl; + uint64_t table_size, table_ptr, remaining; + PRD prd; + + /* This command is now tied to this port/command slot */ + cmd->port = port; + cmd->slot = ahci_pick_cmd(ahci, port); + + /* Create a buffer for the command table */ + prdtl = size_to_prdtl(cmd->xbytes, cmd->prd_size); + table_size = CMD_TBL_SIZ(prdtl); + table_ptr = ahci_alloc(ahci, table_size); + g_assert(table_ptr); + /* AHCI 1.3: Must be aligned to 0x80 */ + g_assert((table_ptr & 0x7F) == 0x00); + cmd->header.ctba = table_ptr; + + /* Commit the command header and command FIS */ + ahci_set_command_header(ahci, port, cmd->slot, &(cmd->header)); + ahci_write_fis(ahci, &(cmd->fis), table_ptr); + + /* Construct and write the PRDs to the command table */ + g_assert_cmphex(prdtl, ==, cmd->header.prdtl); + remaining = cmd->xbytes; + for (i = 0; i < prdtl; ++i) { + prd.dba = cpu_to_le64(cmd->buffer + (cmd->prd_size * i)); + prd.res = 0; + if (remaining > cmd->prd_size) { + /* Note that byte count is 0-based. */ + prd.dbc = cpu_to_le32(cmd->prd_size - 1); + remaining -= cmd->prd_size; + } else { + /* Again, dbc is 0-based. */ + prd.dbc = cpu_to_le32(remaining - 1); + remaining = 0; + } + prd.dbc |= cpu_to_le32(0x80000000); /* Request DPS Interrupt */ + + /* Commit the PRD entry to the Command Table */ + memwrite(table_ptr + 0x80 + (i * sizeof(PRD)), + &prd, sizeof(PRD)); + } + + /* Bookmark the PRDTL and CTBA values */ + ahci->port[port].ctba[cmd->slot] = table_ptr; + ahci->port[port].prdtl[cmd->slot] = prdtl; +} + +void ahci_command_issue_async(AHCIQState *ahci, AHCICommand *cmd) +{ + if (cmd->props->ncq) { + ahci_px_wreg(ahci, cmd->port, AHCI_PX_SACT, (1 << cmd->slot)); + } + + ahci_px_wreg(ahci, cmd->port, AHCI_PX_CI, (1 << cmd->slot)); +} + +void ahci_command_wait(AHCIQState *ahci, AHCICommand *cmd) +{ + /* We can't rely on STS_BSY until the command has started processing. + * Therefore, we also use the Command Issue bit as indication of + * a command in-flight. */ + while (BITSET(ahci_px_rreg(ahci, cmd->port, AHCI_PX_TFD), + AHCI_PX_TFD_STS_BSY) || + BITSET(ahci_px_rreg(ahci, cmd->port, AHCI_PX_CI), (1 << cmd->slot))) { + usleep(50); + } +} + +void ahci_command_issue(AHCIQState *ahci, AHCICommand *cmd) +{ + ahci_command_issue_async(ahci, cmd); + ahci_command_wait(ahci, cmd); +} + +void ahci_command_verify(AHCIQState *ahci, AHCICommand *cmd) +{ + uint8_t slot = cmd->slot; + uint8_t port = cmd->port; + + ahci_port_check_error(ahci, port); + ahci_port_check_interrupts(ahci, port, cmd->interrupts); + ahci_port_check_nonbusy(ahci, port, slot); + ahci_port_check_cmd_sanity(ahci, port, slot, cmd->xbytes); + ahci_port_check_d2h_sanity(ahci, port, slot); + if (cmd->props->pio) { + ahci_port_check_pio_sanity(ahci, port, slot, cmd->xbytes); + } +} + +uint8_t ahci_command_slot(AHCICommand *cmd) +{ + return cmd->slot; +} diff --git a/tests/libqos/ahci.h b/tests/libqos/ahci.h new file mode 100644 index 0000000..888545d --- /dev/null +++ b/tests/libqos/ahci.h @@ -0,0 +1,554 @@ +#ifndef __libqos_ahci_h +#define __libqos_ahci_h + +/* + * AHCI qtest library functions and definitions + * + * Copyright (c) 2014 John Snow + * + * 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 +#include +#include +#include "libqos/libqos.h" +#include "libqos/pci.h" +#include "libqos/malloc-pc.h" + +/*** Supplementary PCI Config Space IDs & Masks ***/ +#define PCI_DEVICE_ID_INTEL_Q35_AHCI (0x2922) +#define PCI_MSI_FLAGS_RESERVED (0xFF00) +#define PCI_PM_CTRL_RESERVED (0xFC) +#define PCI_BCC(REG32) ((REG32) >> 24) +#define PCI_PI(REG32) (((REG32) >> 8) & 0xFF) +#define PCI_SCC(REG32) (((REG32) >> 16) & 0xFF) + +/*** Recognized AHCI Device Types ***/ +#define AHCI_INTEL_ICH9 (PCI_DEVICE_ID_INTEL_Q35_AHCI << 16 | \ + PCI_VENDOR_ID_INTEL) + +/*** AHCI/HBA Register Offsets and Bitmasks ***/ +#define AHCI_CAP (0) +#define AHCI_CAP_NP (0x1F) +#define AHCI_CAP_SXS (0x20) +#define AHCI_CAP_EMS (0x40) +#define AHCI_CAP_CCCS (0x80) +#define AHCI_CAP_NCS (0x1F00) +#define AHCI_CAP_PSC (0x2000) +#define AHCI_CAP_SSC (0x4000) +#define AHCI_CAP_PMD (0x8000) +#define AHCI_CAP_FBSS (0x10000) +#define AHCI_CAP_SPM (0x20000) +#define AHCI_CAP_SAM (0x40000) +#define AHCI_CAP_RESERVED (0x80000) +#define AHCI_CAP_ISS (0xF00000) +#define AHCI_CAP_SCLO (0x1000000) +#define AHCI_CAP_SAL (0x2000000) +#define AHCI_CAP_SALP (0x4000000) +#define AHCI_CAP_SSS (0x8000000) +#define AHCI_CAP_SMPS (0x10000000) +#define AHCI_CAP_SSNTF (0x20000000) +#define AHCI_CAP_SNCQ (0x40000000) +#define AHCI_CAP_S64A (0x80000000) + +#define AHCI_GHC (1) +#define AHCI_GHC_HR (0x01) +#define AHCI_GHC_IE (0x02) +#define AHCI_GHC_MRSM (0x04) +#define AHCI_GHC_RESERVED (0x7FFFFFF8) +#define AHCI_GHC_AE (0x80000000) + +#define AHCI_IS (2) +#define AHCI_PI (3) +#define AHCI_VS (4) + +#define AHCI_CCCCTL (5) +#define AHCI_CCCCTL_EN (0x01) +#define AHCI_CCCCTL_RESERVED (0x06) +#define AHCI_CCCCTL_CC (0xFF00) +#define AHCI_CCCCTL_TV (0xFFFF0000) + +#define AHCI_CCCPORTS (6) +#define AHCI_EMLOC (7) + +#define AHCI_EMCTL (8) +#define AHCI_EMCTL_STSMR (0x01) +#define AHCI_EMCTL_CTLTM (0x100) +#define AHCI_EMCTL_CTLRST (0x200) +#define AHCI_EMCTL_RESERVED (0xF0F0FCFE) + +#define AHCI_CAP2 (9) +#define AHCI_CAP2_BOH (0x01) +#define AHCI_CAP2_NVMP (0x02) +#define AHCI_CAP2_APST (0x04) +#define AHCI_CAP2_RESERVED (0xFFFFFFF8) + +#define AHCI_BOHC (10) +#define AHCI_RESERVED (11) +#define AHCI_NVMHCI (24) +#define AHCI_VENDOR (40) +#define AHCI_PORTS (64) + +/*** Port Memory Offsets & Bitmasks ***/ +#define AHCI_PX_CLB (0) +#define AHCI_PX_CLB_RESERVED (0x1FF) + +#define AHCI_PX_CLBU (1) + +#define AHCI_PX_FB (2) +#define AHCI_PX_FB_RESERVED (0xFF) + +#define AHCI_PX_FBU (3) + +#define AHCI_PX_IS (4) +#define AHCI_PX_IS_DHRS (0x1) +#define AHCI_PX_IS_PSS (0x2) +#define AHCI_PX_IS_DSS (0x4) +#define AHCI_PX_IS_SDBS (0x8) +#define AHCI_PX_IS_UFS (0x10) +#define AHCI_PX_IS_DPS (0x20) +#define AHCI_PX_IS_PCS (0x40) +#define AHCI_PX_IS_DMPS (0x80) +#define AHCI_PX_IS_RESERVED (0x23FFF00) +#define AHCI_PX_IS_PRCS (0x400000) +#define AHCI_PX_IS_IPMS (0x800000) +#define AHCI_PX_IS_OFS (0x1000000) +#define AHCI_PX_IS_INFS (0x4000000) +#define AHCI_PX_IS_IFS (0x8000000) +#define AHCI_PX_IS_HBDS (0x10000000) +#define AHCI_PX_IS_HBFS (0x20000000) +#define AHCI_PX_IS_TFES (0x40000000) +#define AHCI_PX_IS_CPDS (0x80000000) + +#define AHCI_PX_IE (5) +#define AHCI_PX_IE_DHRE (0x1) +#define AHCI_PX_IE_PSE (0x2) +#define AHCI_PX_IE_DSE (0x4) +#define AHCI_PX_IE_SDBE (0x8) +#define AHCI_PX_IE_UFE (0x10) +#define AHCI_PX_IE_DPE (0x20) +#define AHCI_PX_IE_PCE (0x40) +#define AHCI_PX_IE_DMPE (0x80) +#define AHCI_PX_IE_RESERVED (0x23FFF00) +#define AHCI_PX_IE_PRCE (0x400000) +#define AHCI_PX_IE_IPME (0x800000) +#define AHCI_PX_IE_OFE (0x1000000) +#define AHCI_PX_IE_INFE (0x4000000) +#define AHCI_PX_IE_IFE (0x8000000) +#define AHCI_PX_IE_HBDE (0x10000000) +#define AHCI_PX_IE_HBFE (0x20000000) +#define AHCI_PX_IE_TFEE (0x40000000) +#define AHCI_PX_IE_CPDE (0x80000000) + +#define AHCI_PX_CMD (6) +#define AHCI_PX_CMD_ST (0x1) +#define AHCI_PX_CMD_SUD (0x2) +#define AHCI_PX_CMD_POD (0x4) +#define AHCI_PX_CMD_CLO (0x8) +#define AHCI_PX_CMD_FRE (0x10) +#define AHCI_PX_CMD_RESERVED (0xE0) +#define AHCI_PX_CMD_CCS (0x1F00) +#define AHCI_PX_CMD_MPSS (0x2000) +#define AHCI_PX_CMD_FR (0x4000) +#define AHCI_PX_CMD_CR (0x8000) +#define AHCI_PX_CMD_CPS (0x10000) +#define AHCI_PX_CMD_PMA (0x20000) +#define AHCI_PX_CMD_HPCP (0x40000) +#define AHCI_PX_CMD_MPSP (0x80000) +#define AHCI_PX_CMD_CPD (0x100000) +#define AHCI_PX_CMD_ESP (0x200000) +#define AHCI_PX_CMD_FBSCP (0x400000) +#define AHCI_PX_CMD_APSTE (0x800000) +#define AHCI_PX_CMD_ATAPI (0x1000000) +#define AHCI_PX_CMD_DLAE (0x2000000) +#define AHCI_PX_CMD_ALPE (0x4000000) +#define AHCI_PX_CMD_ASP (0x8000000) +#define AHCI_PX_CMD_ICC (0xF0000000) + +#define AHCI_PX_RES1 (7) + +#define AHCI_PX_TFD (8) +#define AHCI_PX_TFD_STS (0xFF) +#define AHCI_PX_TFD_STS_ERR (0x01) +#define AHCI_PX_TFD_STS_CS1 (0x06) +#define AHCI_PX_TFD_STS_DRQ (0x08) +#define AHCI_PX_TFD_STS_CS2 (0x70) +#define AHCI_PX_TFD_STS_BSY (0x80) +#define AHCI_PX_TFD_ERR (0xFF00) +#define AHCI_PX_TFD_RESERVED (0xFFFF0000) + +#define AHCI_PX_SIG (9) +#define AHCI_PX_SIG_SECTOR_COUNT (0xFF) +#define AHCI_PX_SIG_LBA_LOW (0xFF00) +#define AHCI_PX_SIG_LBA_MID (0xFF0000) +#define AHCI_PX_SIG_LBA_HIGH (0xFF000000) + +#define AHCI_PX_SSTS (10) +#define AHCI_PX_SSTS_DET (0x0F) +#define AHCI_PX_SSTS_SPD (0xF0) +#define AHCI_PX_SSTS_IPM (0xF00) +#define AHCI_PX_SSTS_RESERVED (0xFFFFF000) +#define SSTS_DET_NO_DEVICE (0x00) +#define SSTS_DET_PRESENT (0x01) +#define SSTS_DET_ESTABLISHED (0x03) +#define SSTS_DET_OFFLINE (0x04) + +#define AHCI_PX_SCTL (11) + +#define AHCI_PX_SERR (12) +#define AHCI_PX_SERR_ERR (0xFFFF) +#define AHCI_PX_SERR_DIAG (0xFFFF0000) +#define AHCI_PX_SERR_DIAG_X (0x04000000) + +#define AHCI_PX_SACT (13) +#define AHCI_PX_CI (14) +#define AHCI_PX_SNTF (15) + +#define AHCI_PX_FBS (16) +#define AHCI_PX_FBS_EN (0x1) +#define AHCI_PX_FBS_DEC (0x2) +#define AHCI_PX_FBS_SDE (0x4) +#define AHCI_PX_FBS_DEV (0xF00) +#define AHCI_PX_FBS_ADO (0xF000) +#define AHCI_PX_FBS_DWE (0xF0000) +#define AHCI_PX_FBS_RESERVED (0xFFF000F8) + +#define AHCI_PX_RES2 (17) +#define AHCI_PX_VS (28) + +#define HBA_DATA_REGION_SIZE (256) +#define HBA_PORT_DATA_SIZE (128) +#define HBA_PORT_NUM_REG (HBA_PORT_DATA_SIZE/4) + +#define AHCI_VERSION_0_95 (0x00000905) +#define AHCI_VERSION_1_0 (0x00010000) +#define AHCI_VERSION_1_1 (0x00010100) +#define AHCI_VERSION_1_2 (0x00010200) +#define AHCI_VERSION_1_3 (0x00010300) + +#define AHCI_SECTOR_SIZE (512) + +/* FIS types */ +enum { + REG_H2D_FIS = 0x27, + REG_D2H_FIS = 0x34, + DMA_ACTIVATE_FIS = 0x39, + DMA_SETUP_FIS = 0x41, + DATA_FIS = 0x46, + BIST_ACTIVATE_FIS = 0x58, + PIO_SETUP_FIS = 0x5F, + SDB_FIS = 0xA1 +}; + +/* FIS flags */ +#define REG_H2D_FIS_CMD 0x80 + +/* ATA Commands */ +enum { + /* DMA */ + CMD_READ_DMA = 0xC8, + CMD_READ_DMA_EXT = 0x25, + CMD_WRITE_DMA = 0xCA, + CMD_WRITE_DMA_EXT = 0x35, + /* PIO */ + CMD_READ_PIO = 0x20, + CMD_READ_PIO_EXT = 0x24, + CMD_WRITE_PIO = 0x30, + CMD_WRITE_PIO_EXT = 0x34, + /* Misc */ + CMD_READ_MAX = 0xF8, + CMD_READ_MAX_EXT = 0x27, + CMD_FLUSH_CACHE = 0xE7, + CMD_IDENTIFY = 0xEC +}; + +/* AHCI Command Header Flags & Masks*/ +#define CMDH_CFL (0x1F) +#define CMDH_ATAPI (0x20) +#define CMDH_WRITE (0x40) +#define CMDH_PREFETCH (0x80) +#define CMDH_RESET (0x100) +#define CMDH_BIST (0x200) +#define CMDH_CLR_BSY (0x400) +#define CMDH_RES (0x800) +#define CMDH_PMP (0xF000) + +/* ATA device register masks */ +#define ATA_DEVICE_MAGIC 0xA0 +#define ATA_DEVICE_LBA 0x40 +#define ATA_DEVICE_DRIVE 0x10 +#define ATA_DEVICE_HEAD 0x0F + +/*** Structures ***/ + +typedef struct AHCIPortQState { + uint64_t fb; + uint64_t clb; + uint64_t ctba[32]; + uint16_t prdtl[32]; + uint8_t next; /** Next Command Slot to Use **/ +} AHCIPortQState; + +typedef struct AHCIQState { + QOSState *parent; + QPCIDevice *dev; + void *hba_base; + uint64_t barsize; + uint32_t fingerprint; + uint32_t cap; + uint32_t cap2; + AHCIPortQState port[32]; +} AHCIQState; + +/** + * Generic FIS structure. + */ +typedef struct FIS { + uint8_t fis_type; + uint8_t flags; + char data[0]; +} __attribute__((__packed__)) FIS; + +/** + * Register device-to-host FIS structure. + */ +typedef struct RegD2HFIS { + /* DW0 */ + uint8_t fis_type; + uint8_t flags; + uint8_t status; + uint8_t error; + /* DW1 */ + uint8_t lba_lo[3]; + uint8_t device; + /* DW2 */ + uint8_t lba_hi[3]; + uint8_t res0; + /* DW3 */ + uint16_t count; + uint16_t res1; + /* DW4 */ + uint32_t res2; +} __attribute__((__packed__)) RegD2HFIS; + +/** + * Register device-to-host FIS structure; + * PIO Setup variety. + */ +typedef struct PIOSetupFIS { + /* DW0 */ + uint8_t fis_type; + uint8_t flags; + uint8_t status; + uint8_t error; + /* DW1 */ + uint8_t lba_lo[3]; + uint8_t device; + /* DW2 */ + uint8_t lba_hi[3]; + uint8_t res0; + /* DW3 */ + uint16_t count; + uint8_t res1; + uint8_t e_status; + /* DW4 */ + uint16_t tx_count; + uint16_t res2; +} __attribute__((__packed__)) PIOSetupFIS; + +/** + * Register host-to-device FIS structure. + */ +typedef struct RegH2DFIS { + /* DW0 */ + uint8_t fis_type; + uint8_t flags; + uint8_t command; + uint8_t feature_low; + /* DW1 */ + uint8_t lba_lo[3]; + uint8_t device; + /* DW2 */ + uint8_t lba_hi[3]; + uint8_t feature_high; + /* DW3 */ + uint16_t count; + uint8_t icc; + uint8_t control; + /* DW4 */ + uint8_t aux[4]; +} __attribute__((__packed__)) RegH2DFIS; + +/** + * Command List entry structure. + * The command list contains between 1-32 of these structures. + */ +typedef struct AHCICommandHeader { + uint16_t flags; /* Cmd-Fis-Len, PMP#, and flags. */ + uint16_t prdtl; /* Phys Region Desc. Table Length */ + uint32_t prdbc; /* Phys Region Desc. Byte Count */ + uint64_t ctba; /* Command Table Descriptor Base Address */ + uint32_t res[4]; +} __attribute__((__packed__)) AHCICommandHeader; + +/** + * Physical Region Descriptor; pointed to by the Command List Header, + * struct ahci_command. + */ +typedef struct PRD { + uint64_t dba; /* Data Base Address */ + uint32_t res; /* Reserved */ + uint32_t dbc; /* Data Byte Count (0-indexed) & Interrupt Flag (bit 2^31) */ +} __attribute__((__packed__)) PRD; + +/* Opaque, defined within ahci.c */ +typedef struct AHCICommand AHCICommand; + +/*** Macro Utilities ***/ +#define BITANY(data, mask) (((data) & (mask)) != 0) +#define BITSET(data, mask) (((data) & (mask)) == (mask)) +#define BITCLR(data, mask) (((data) & (mask)) == 0) +#define ASSERT_BIT_SET(data, mask) g_assert_cmphex((data) & (mask), ==, (mask)) +#define ASSERT_BIT_CLEAR(data, mask) g_assert_cmphex((data) & (mask), ==, 0) + +/* For calculating how big the PRD table needs to be: */ +#define CMD_TBL_SIZ(n) ((0x80 + ((n) * sizeof(PRD)) + 0x7F) & ~0x7F) + +/* Helpers for reading/writing AHCI HBA register values */ + +static inline uint32_t ahci_mread(AHCIQState *ahci, size_t offset) +{ + return qpci_io_readl(ahci->dev, ahci->hba_base + offset); +} + +static inline void ahci_mwrite(AHCIQState *ahci, size_t offset, uint32_t value) +{ + qpci_io_writel(ahci->dev, ahci->hba_base + offset, value); +} + +static inline uint32_t ahci_rreg(AHCIQState *ahci, uint32_t reg_num) +{ + return ahci_mread(ahci, 4 * reg_num); +} + +static inline void ahci_wreg(AHCIQState *ahci, uint32_t reg_num, uint32_t value) +{ + ahci_mwrite(ahci, 4 * reg_num, value); +} + +static inline void ahci_set(AHCIQState *ahci, uint32_t reg_num, uint32_t mask) +{ + ahci_wreg(ahci, reg_num, ahci_rreg(ahci, reg_num) | mask); +} + +static inline void ahci_clr(AHCIQState *ahci, uint32_t reg_num, uint32_t mask) +{ + ahci_wreg(ahci, reg_num, ahci_rreg(ahci, reg_num) & ~mask); +} + +static inline size_t ahci_px_offset(uint8_t port, uint32_t reg_num) +{ + return AHCI_PORTS + (HBA_PORT_NUM_REG * port) + reg_num; +} + +static inline uint32_t ahci_px_rreg(AHCIQState *ahci, uint8_t port, + uint32_t reg_num) +{ + return ahci_rreg(ahci, ahci_px_offset(port, reg_num)); +} + +static inline void ahci_px_wreg(AHCIQState *ahci, uint8_t port, + uint32_t reg_num, uint32_t value) +{ + ahci_wreg(ahci, ahci_px_offset(port, reg_num), value); +} + +static inline void ahci_px_set(AHCIQState *ahci, uint8_t port, + uint32_t reg_num, uint32_t mask) +{ + ahci_px_wreg(ahci, port, reg_num, + ahci_px_rreg(ahci, port, reg_num) | mask); +} + +static inline void ahci_px_clr(AHCIQState *ahci, uint8_t port, + uint32_t reg_num, uint32_t mask) +{ + ahci_px_wreg(ahci, port, reg_num, + ahci_px_rreg(ahci, port, reg_num) & ~mask); +} + +/*** Prototypes ***/ +uint64_t ahci_alloc(AHCIQState *ahci, size_t bytes); +void ahci_free(AHCIQState *ahci, uint64_t addr); +QPCIDevice *get_ahci_device(uint32_t *fingerprint); +void free_ahci_device(QPCIDevice *dev); +void ahci_clean_mem(AHCIQState *ahci); +void ahci_pci_enable(AHCIQState *ahci); +void start_ahci_device(AHCIQState *ahci); +void ahci_hba_enable(AHCIQState *ahci); +unsigned ahci_port_select(AHCIQState *ahci); +void ahci_port_clear(AHCIQState *ahci, uint8_t port); +void ahci_port_check_error(AHCIQState *ahci, uint8_t port); +void ahci_port_check_interrupts(AHCIQState *ahci, uint8_t port, + uint32_t intr_mask); +void ahci_port_check_nonbusy(AHCIQState *ahci, uint8_t port, uint8_t slot); +void ahci_port_check_d2h_sanity(AHCIQState *ahci, uint8_t port, uint8_t slot); +void ahci_port_check_pio_sanity(AHCIQState *ahci, uint8_t port, + uint8_t slot, size_t buffsize); +void ahci_port_check_cmd_sanity(AHCIQState *ahci, uint8_t port, + uint8_t slot, size_t buffsize); +void ahci_get_command_header(AHCIQState *ahci, uint8_t port, + uint8_t slot, AHCICommandHeader *cmd); +void ahci_set_command_header(AHCIQState *ahci, uint8_t port, + uint8_t slot, AHCICommandHeader *cmd); +void ahci_destroy_command(AHCIQState *ahci, uint8_t port, uint8_t slot); +void ahci_write_fis(AHCIQState *ahci, RegH2DFIS *fis, uint64_t addr); +unsigned ahci_pick_cmd(AHCIQState *ahci, uint8_t port); +unsigned size_to_prdtl(unsigned bytes, unsigned bytes_per_prd); +void ahci_guest_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd, + uint64_t gbuffer, size_t size); +void ahci_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd, + void *buffer, size_t bufsize); + +/* Command Lifecycle */ +AHCICommand *ahci_command_create(uint8_t command_name); +void ahci_command_commit(AHCIQState *ahci, AHCICommand *cmd, uint8_t port); +void ahci_command_issue(AHCIQState *ahci, AHCICommand *cmd); +void ahci_command_issue_async(AHCIQState *ahci, AHCICommand *cmd); +void ahci_command_wait(AHCIQState *ahci, AHCICommand *cmd); +void ahci_command_verify(AHCIQState *ahci, AHCICommand *cmd); +void ahci_command_free(AHCICommand *cmd); + +/* Command adjustments */ +void ahci_command_set_flags(AHCICommand *cmd, uint16_t cmdh_flags); +void ahci_command_clr_flags(AHCICommand *cmd, uint16_t cmdh_flags); +void ahci_command_set_offset(AHCICommand *cmd, uint64_t lba_sect); +void ahci_command_set_buffer(AHCICommand *cmd, uint64_t buffer); +void ahci_command_set_size(AHCICommand *cmd, uint64_t xbytes); +void ahci_command_set_prd_size(AHCICommand *cmd, unsigned prd_size); +void ahci_command_set_sizes(AHCICommand *cmd, uint64_t xbytes, + unsigned prd_size); +void ahci_command_adjust(AHCICommand *cmd, uint64_t lba_sect, uint64_t gbuffer, + uint64_t xbytes, unsigned prd_size); + +/* Command Misc */ +uint8_t ahci_command_slot(AHCICommand *cmd); + +#endif diff --git a/tests/libqos/libqos-pc.c b/tests/libqos/libqos-pc.c new file mode 100644 index 0000000..bbace89 --- /dev/null +++ b/tests/libqos/libqos-pc.c @@ -0,0 +1,24 @@ +#include "libqos/libqos-pc.h" +#include "libqos/malloc-pc.h" + +static QOSOps qos_ops = { + .init_allocator = pc_alloc_init_flags, + .uninit_allocator = pc_alloc_uninit +}; + +QOSState *qtest_pc_boot(const char *cmdline_fmt, ...) +{ + QOSState *qs; + va_list ap; + + va_start(ap, cmdline_fmt); + qs = qtest_vboot(&qos_ops, cmdline_fmt, ap); + va_end(ap); + + return qs; +} + +void qtest_pc_shutdown(QOSState *qs) +{ + return qtest_shutdown(qs); +} diff --git a/tests/libqos/libqos-pc.h b/tests/libqos/libqos-pc.h new file mode 100644 index 0000000..316857d --- /dev/null +++ b/tests/libqos/libqos-pc.h @@ -0,0 +1,9 @@ +#ifndef __libqos_pc_h +#define __libqos_pc_h + +#include "libqos/libqos.h" + +QOSState *qtest_pc_boot(const char *cmdline_fmt, ...); +void qtest_pc_shutdown(QOSState *qs); + +#endif diff --git a/tests/libqos/libqos.c b/tests/libqos/libqos.c new file mode 100644 index 0000000..bc8beb2 --- /dev/null +++ b/tests/libqos/libqos.c @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include +#include + +#include "libqtest.h" +#include "libqos/libqos.h" +#include "libqos/pci.h" + +/*** Test Setup & Teardown ***/ + +/** + * Launch QEMU with the given command line, + * and then set up interrupts and our guest malloc interface. + */ +QOSState *qtest_vboot(QOSOps *ops, const char *cmdline_fmt, va_list ap) +{ + char *cmdline; + + struct QOSState *qs = g_malloc(sizeof(QOSState)); + + cmdline = g_strdup_vprintf(cmdline_fmt, ap); + qs->qts = qtest_start(cmdline); + qs->ops = ops; + qtest_irq_intercept_in(global_qtest, "ioapic"); + if (ops && ops->init_allocator) { + qs->alloc = ops->init_allocator(ALLOC_NO_FLAGS); + } + + g_free(cmdline); + return qs; +} + +/** + * Launch QEMU with the given command line, + * and then set up interrupts and our guest malloc interface. + */ +QOSState *qtest_boot(QOSOps *ops, const char *cmdline_fmt, ...) +{ + QOSState *qs; + va_list ap; + + va_start(ap, cmdline_fmt); + qs = qtest_vboot(ops, cmdline_fmt, ap); + va_end(ap); + + return qs; +} + +/** + * Tear down the QEMU instance. + */ +void qtest_shutdown(QOSState *qs) +{ + if (qs->alloc && qs->ops && qs->ops->uninit_allocator) { + qs->ops->uninit_allocator(qs->alloc); + qs->alloc = NULL; + } + qtest_quit(qs->qts); + g_free(qs); +} diff --git a/tests/libqos/libqos.h b/tests/libqos/libqos.h new file mode 100644 index 0000000..612d41e --- /dev/null +++ b/tests/libqos/libqos.h @@ -0,0 +1,33 @@ +#ifndef __libqos_h +#define __libqos_h + +#include "libqtest.h" +#include "libqos/pci.h" +#include "libqos/malloc-pc.h" + +typedef struct QOSOps { + QGuestAllocator *(*init_allocator)(QAllocOpts); + void (*uninit_allocator)(QGuestAllocator *); +} QOSOps; + +typedef struct QOSState { + QTestState *qts; + QGuestAllocator *alloc; + QOSOps *ops; +} QOSState; + +QOSState *qtest_vboot(QOSOps *ops, const char *cmdline_fmt, va_list ap); +QOSState *qtest_boot(QOSOps *ops, const char *cmdline_fmt, ...); +void qtest_shutdown(QOSState *qs); + +static inline uint64_t qmalloc(QOSState *q, size_t bytes) +{ + return guest_alloc(q->alloc, bytes); +} + +static inline void qfree(QOSState *q, uint64_t addr) +{ + guest_free(q->alloc, addr); +} + +#endif diff --git a/tests/libqos/malloc-generic.c b/tests/libqos/malloc-generic.c new file mode 100644 index 0000000..d30a2f4 --- /dev/null +++ b/tests/libqos/malloc-generic.c @@ -0,0 +1,39 @@ +/* + * Basic libqos generic malloc support + * + * Copyright (c) 2014 Marc Marí + * + * 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 +#include "libqos/malloc-generic.h" +#include "libqos/malloc.h" + +/* + * Mostly for valgrind happiness, but it does offer + * a chokepoint for debugging guest memory leaks, too. + */ +void generic_alloc_uninit(QGuestAllocator *allocator) +{ + alloc_uninit(allocator); +} + +QGuestAllocator *generic_alloc_init_flags(uint64_t base_addr, uint64_t size, + uint32_t page_size, QAllocOpts flags) +{ + QGuestAllocator *s; + uint64_t start = base_addr + (1 << 20); /* Start at 1MB */ + + s = alloc_init_flags(flags, start, start + size); + alloc_set_page_size(s, page_size); + + return s; +} + +inline QGuestAllocator *generic_alloc_init(uint64_t base_addr, uint64_t size, + uint32_t page_size) +{ + return generic_alloc_init_flags(base_addr, size, page_size, ALLOC_NO_FLAGS); +} diff --git a/tests/libqos/malloc-generic.h b/tests/libqos/malloc-generic.h new file mode 100644 index 0000000..90104ec --- /dev/null +++ b/tests/libqos/malloc-generic.h @@ -0,0 +1,21 @@ +/* + * Basic libqos generic malloc support + * + * Copyright (c) 2014 Marc Marí + * + * 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 LIBQOS_MALLOC_GENERIC_H +#define LIBQOS_MALLOC_GENERIC_H + +#include "libqos/malloc.h" + +QGuestAllocator *generic_alloc_init(uint64_t base_addr, uint64_t size, + uint32_t page_size); +QGuestAllocator *generic_alloc_init_flags(uint64_t base_addr, uint64_t size, + uint32_t page_size, QAllocOpts flags); +void generic_alloc_uninit(QGuestAllocator *allocator); + +#endif diff --git a/tests/libqos/malloc-pc.c b/tests/libqos/malloc-pc.c index f4218c6..6e253b6 100644 --- a/tests/libqos/malloc-pc.c +++ b/tests/libqos/malloc-pc.c @@ -17,318 +17,36 @@ #include "hw/nvram/fw_cfg.h" #include "qemu-common.h" -#include "qemu/queue.h" #include #define PAGE_SIZE (4096) -#define MLIST_ENTNAME entries -typedef QTAILQ_HEAD(MemList, MemBlock) MemList; -typedef struct MemBlock { - QTAILQ_ENTRY(MemBlock) MLIST_ENTNAME; - uint64_t size; - uint64_t addr; -} MemBlock; - -typedef struct PCAlloc -{ - QGuestAllocator alloc; - PCAllocOpts opts; - uint64_t start; - uint64_t end; - - MemList used; - MemList free; -} PCAlloc; - -static MemBlock *mlist_new(uint64_t addr, uint64_t size) -{ - MemBlock *block; - - if (!size) { - return NULL; - } - block = g_malloc0(sizeof(MemBlock)); - - block->addr = addr; - block->size = size; - - return block; -} - -static void mlist_delete(MemList *list, MemBlock *node) -{ - g_assert(list && node); - QTAILQ_REMOVE(list, node, MLIST_ENTNAME); - g_free(node); -} - -static MemBlock *mlist_find_key(MemList *head, uint64_t addr) -{ - MemBlock *node; - QTAILQ_FOREACH(node, head, MLIST_ENTNAME) { - if (node->addr == addr) { - return node; - } - } - return NULL; -} - -static MemBlock *mlist_find_space(MemList *head, uint64_t size) -{ - MemBlock *node; - - QTAILQ_FOREACH(node, head, MLIST_ENTNAME) { - if (node->size >= size) { - return node; - } - } - return NULL; -} - -static MemBlock *mlist_sort_insert(MemList *head, MemBlock *insr) -{ - MemBlock *node; - g_assert(head && insr); - - QTAILQ_FOREACH(node, head, MLIST_ENTNAME) { - if (insr->addr < node->addr) { - QTAILQ_INSERT_BEFORE(node, insr, MLIST_ENTNAME); - return insr; - } - } - - QTAILQ_INSERT_TAIL(head, insr, MLIST_ENTNAME); - return insr; -} - -static inline uint64_t mlist_boundary(MemBlock *node) -{ - return node->size + node->addr; -} - -static MemBlock *mlist_join(MemList *head, MemBlock *left, MemBlock *right) -{ - g_assert(head && left && right); - - left->size += right->size; - mlist_delete(head, right); - return left; -} - -static void mlist_coalesce(MemList *head, MemBlock *node) -{ - g_assert(node); - MemBlock *left; - MemBlock *right; - char merge; - - do { - merge = 0; - left = QTAILQ_PREV(node, MemList, MLIST_ENTNAME); - right = QTAILQ_NEXT(node, MLIST_ENTNAME); - - /* clowns to the left of me */ - if (left && mlist_boundary(left) == node->addr) { - node = mlist_join(head, left, node); - merge = 1; - } - - /* jokers to the right */ - if (right && mlist_boundary(node) == right->addr) { - node = mlist_join(head, node, right); - merge = 1; - } - - } while (merge); -} - -static uint64_t pc_mlist_fulfill(PCAlloc *s, MemBlock *freenode, uint64_t size) -{ - uint64_t addr; - MemBlock *usednode; - - g_assert(freenode); - g_assert_cmpint(freenode->size, >=, size); - - addr = freenode->addr; - if (freenode->size == size) { - /* re-use this freenode as our used node */ - QTAILQ_REMOVE(&s->free, freenode, MLIST_ENTNAME); - usednode = freenode; - } else { - /* adjust the free node and create a new used node */ - freenode->addr += size; - freenode->size -= size; - usednode = mlist_new(addr, size); - } - - mlist_sort_insert(&s->used, usednode); - return addr; -} - -/* To assert the correctness of the list. - * Used only if PC_ALLOC_PARANOID is set. */ -static void pc_mlist_check(PCAlloc *s) -{ - MemBlock *node; - uint64_t addr = s->start > 0 ? s->start - 1 : 0; - uint64_t next = s->start; - - QTAILQ_FOREACH(node, &s->free, MLIST_ENTNAME) { - g_assert_cmpint(node->addr, >, addr); - g_assert_cmpint(node->addr, >=, next); - addr = node->addr; - next = node->addr + node->size; - } - - addr = s->start > 0 ? s->start - 1 : 0; - next = s->start; - QTAILQ_FOREACH(node, &s->used, MLIST_ENTNAME) { - g_assert_cmpint(node->addr, >, addr); - g_assert_cmpint(node->addr, >=, next); - addr = node->addr; - next = node->addr + node->size; - } -} - -static uint64_t pc_mlist_alloc(PCAlloc *s, uint64_t size) -{ - MemBlock *node; - - node = mlist_find_space(&s->free, size); - if (!node) { - fprintf(stderr, "Out of guest memory.\n"); - g_assert_not_reached(); - } - return pc_mlist_fulfill(s, node, size); -} - -static void pc_mlist_free(PCAlloc *s, uint64_t addr) -{ - MemBlock *node; - - if (addr == 0) { - return; - } - - node = mlist_find_key(&s->used, addr); - if (!node) { - fprintf(stderr, "Error: no record found for an allocation at " - "0x%016" PRIx64 ".\n", - addr); - g_assert_not_reached(); - } - - /* Rip it out of the used list and re-insert back into the free list. */ - QTAILQ_REMOVE(&s->used, node, MLIST_ENTNAME); - mlist_sort_insert(&s->free, node); - mlist_coalesce(&s->free, node); -} - -static uint64_t pc_alloc(QGuestAllocator *allocator, size_t size) -{ - PCAlloc *s = container_of(allocator, PCAlloc, alloc); - uint64_t rsize = size; - uint64_t naddr; - - rsize += (PAGE_SIZE - 1); - rsize &= -PAGE_SIZE; - g_assert_cmpint((s->start + rsize), <=, s->end); - g_assert_cmpint(rsize, >=, size); - - naddr = pc_mlist_alloc(s, rsize); - if (s->opts & PC_ALLOC_PARANOID) { - pc_mlist_check(s); - } - - return naddr; -} - -static void pc_free(QGuestAllocator *allocator, uint64_t addr) -{ - PCAlloc *s = container_of(allocator, PCAlloc, alloc); - - pc_mlist_free(s, addr); - if (s->opts & PC_ALLOC_PARANOID) { - pc_mlist_check(s); - } -} - /* * Mostly for valgrind happiness, but it does offer * a chokepoint for debugging guest memory leaks, too. */ void pc_alloc_uninit(QGuestAllocator *allocator) { - PCAlloc *s = container_of(allocator, PCAlloc, alloc); - MemBlock *node; - MemBlock *tmp; - PCAllocOpts mask; - - /* Check for guest leaks, and destroy the list. */ - QTAILQ_FOREACH_SAFE(node, &s->used, MLIST_ENTNAME, tmp) { - if (s->opts & (PC_ALLOC_LEAK_WARN | PC_ALLOC_LEAK_ASSERT)) { - fprintf(stderr, "guest malloc leak @ 0x%016" PRIx64 "; " - "size 0x%016" PRIx64 ".\n", - node->addr, node->size); - } - if (s->opts & (PC_ALLOC_LEAK_ASSERT)) { - g_assert_not_reached(); - } - g_free(node); - } - - /* If we have previously asserted that there are no leaks, then there - * should be only one node here with a specific address and size. */ - mask = PC_ALLOC_LEAK_ASSERT | PC_ALLOC_PARANOID; - QTAILQ_FOREACH_SAFE(node, &s->free, MLIST_ENTNAME, tmp) { - if ((s->opts & mask) == mask) { - if ((node->addr != s->start) || - (node->size != s->end - s->start)) { - fprintf(stderr, "Free list is corrupted.\n"); - g_assert_not_reached(); - } - } - - g_free(node); - } - - g_free(s); + alloc_uninit(allocator); } -QGuestAllocator *pc_alloc_init_flags(PCAllocOpts flags) +QGuestAllocator *pc_alloc_init_flags(QAllocOpts flags) { - PCAlloc *s = g_malloc0(sizeof(*s)); + QGuestAllocator *s; uint64_t ram_size; QFWCFG *fw_cfg = pc_fw_cfg_init(); - MemBlock *node; - - s->opts = flags; - s->alloc.alloc = pc_alloc; - s->alloc.free = pc_free; ram_size = qfw_cfg_get_u64(fw_cfg, FW_CFG_RAM_SIZE); - - /* Start at 1MB */ - s->start = 1 << 20; - - /* Respect PCI hole */ - s->end = MIN(ram_size, 0xE0000000); + s = alloc_init_flags(flags, 1 << 20, MIN(ram_size, 0xE0000000)); + alloc_set_page_size(s, PAGE_SIZE); /* clean-up */ g_free(fw_cfg); - QTAILQ_INIT(&s->used); - QTAILQ_INIT(&s->free); - - node = mlist_new(s->start, s->end - s->start); - QTAILQ_INSERT_HEAD(&s->free, node, MLIST_ENTNAME); - - return &s->alloc; + return s; } inline QGuestAllocator *pc_alloc_init(void) { - return pc_alloc_init_flags(PC_ALLOC_NO_FLAGS); + return pc_alloc_init_flags(ALLOC_NO_FLAGS); } diff --git a/tests/libqos/malloc-pc.h b/tests/libqos/malloc-pc.h index 9f525e3..86ab9f0 100644 --- a/tests/libqos/malloc-pc.h +++ b/tests/libqos/malloc-pc.h @@ -15,15 +15,8 @@ #include "libqos/malloc.h" -typedef enum { - PC_ALLOC_NO_FLAGS = 0x00, - PC_ALLOC_LEAK_WARN = 0x01, - PC_ALLOC_LEAK_ASSERT = 0x02, - PC_ALLOC_PARANOID = 0x04 -} PCAllocOpts; - QGuestAllocator *pc_alloc_init(void); -QGuestAllocator *pc_alloc_init_flags(PCAllocOpts flags); -void pc_alloc_uninit(QGuestAllocator *allocator); +QGuestAllocator *pc_alloc_init_flags(QAllocOpts flags); +void pc_alloc_uninit(QGuestAllocator *allocator); #endif diff --git a/tests/libqos/malloc.c b/tests/libqos/malloc.c new file mode 100644 index 0000000..67f3190 --- /dev/null +++ b/tests/libqos/malloc.c @@ -0,0 +1,331 @@ +/* + * libqos malloc support + * + * Copyright (c) 2014 + * + * Author: + * John Snow + * + * 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 "libqos/malloc.h" +#include "qemu-common.h" +#include +#include +#include + +typedef QTAILQ_HEAD(MemList, MemBlock) MemList; + +typedef struct MemBlock { + QTAILQ_ENTRY(MemBlock) MLIST_ENTNAME; + uint64_t size; + uint64_t addr; +} MemBlock; + +struct QGuestAllocator { + QAllocOpts opts; + uint64_t start; + uint64_t end; + uint32_t page_size; + + MemList used; + MemList free; +}; + +#define DEFAULT_PAGE_SIZE 4096 + +static void mlist_delete(MemList *list, MemBlock *node) +{ + g_assert(list && node); + QTAILQ_REMOVE(list, node, MLIST_ENTNAME); + g_free(node); +} + +static MemBlock *mlist_find_key(MemList *head, uint64_t addr) +{ + MemBlock *node; + QTAILQ_FOREACH(node, head, MLIST_ENTNAME) { + if (node->addr == addr) { + return node; + } + } + return NULL; +} + +static MemBlock *mlist_find_space(MemList *head, uint64_t size) +{ + MemBlock *node; + + QTAILQ_FOREACH(node, head, MLIST_ENTNAME) { + if (node->size >= size) { + return node; + } + } + return NULL; +} + +static MemBlock *mlist_sort_insert(MemList *head, MemBlock *insr) +{ + MemBlock *node; + g_assert(head && insr); + + QTAILQ_FOREACH(node, head, MLIST_ENTNAME) { + if (insr->addr < node->addr) { + QTAILQ_INSERT_BEFORE(node, insr, MLIST_ENTNAME); + return insr; + } + } + + QTAILQ_INSERT_TAIL(head, insr, MLIST_ENTNAME); + return insr; +} + +static inline uint64_t mlist_boundary(MemBlock *node) +{ + return node->size + node->addr; +} + +static MemBlock *mlist_join(MemList *head, MemBlock *left, MemBlock *right) +{ + g_assert(head && left && right); + + left->size += right->size; + mlist_delete(head, right); + return left; +} + +static void mlist_coalesce(MemList *head, MemBlock *node) +{ + g_assert(node); + MemBlock *left; + MemBlock *right; + char merge; + + do { + merge = 0; + left = QTAILQ_PREV(node, MemList, MLIST_ENTNAME); + right = QTAILQ_NEXT(node, MLIST_ENTNAME); + + /* clowns to the left of me */ + if (left && mlist_boundary(left) == node->addr) { + node = mlist_join(head, left, node); + merge = 1; + } + + /* jokers to the right */ + if (right && mlist_boundary(node) == right->addr) { + node = mlist_join(head, node, right); + merge = 1; + } + + } while (merge); +} + +static MemBlock *mlist_new(uint64_t addr, uint64_t size) +{ + MemBlock *block; + + if (!size) { + return NULL; + } + block = g_malloc0(sizeof(MemBlock)); + + block->addr = addr; + block->size = size; + + return block; +} + +static uint64_t mlist_fulfill(QGuestAllocator *s, MemBlock *freenode, + uint64_t size) +{ + uint64_t addr; + MemBlock *usednode; + + g_assert(freenode); + g_assert_cmpint(freenode->size, >=, size); + + addr = freenode->addr; + if (freenode->size == size) { + /* re-use this freenode as our used node */ + QTAILQ_REMOVE(&s->free, freenode, MLIST_ENTNAME); + usednode = freenode; + } else { + /* adjust the free node and create a new used node */ + freenode->addr += size; + freenode->size -= size; + usednode = mlist_new(addr, size); + } + + mlist_sort_insert(&s->used, usednode); + return addr; +} + +/* To assert the correctness of the list. + * Used only if ALLOC_PARANOID is set. */ +static void mlist_check(QGuestAllocator *s) +{ + MemBlock *node; + uint64_t addr = s->start > 0 ? s->start - 1 : 0; + uint64_t next = s->start; + + QTAILQ_FOREACH(node, &s->free, MLIST_ENTNAME) { + g_assert_cmpint(node->addr, >, addr); + g_assert_cmpint(node->addr, >=, next); + addr = node->addr; + next = node->addr + node->size; + } + + addr = s->start > 0 ? s->start - 1 : 0; + next = s->start; + QTAILQ_FOREACH(node, &s->used, MLIST_ENTNAME) { + g_assert_cmpint(node->addr, >, addr); + g_assert_cmpint(node->addr, >=, next); + addr = node->addr; + next = node->addr + node->size; + } +} + +static uint64_t mlist_alloc(QGuestAllocator *s, uint64_t size) +{ + MemBlock *node; + + node = mlist_find_space(&s->free, size); + if (!node) { + fprintf(stderr, "Out of guest memory.\n"); + g_assert_not_reached(); + } + return mlist_fulfill(s, node, size); +} + +static void mlist_free(QGuestAllocator *s, uint64_t addr) +{ + MemBlock *node; + + if (addr == 0) { + return; + } + + node = mlist_find_key(&s->used, addr); + if (!node) { + fprintf(stderr, "Error: no record found for an allocation at " + "0x%016" PRIx64 ".\n", + addr); + g_assert_not_reached(); + } + + /* Rip it out of the used list and re-insert back into the free list. */ + QTAILQ_REMOVE(&s->used, node, MLIST_ENTNAME); + mlist_sort_insert(&s->free, node); + mlist_coalesce(&s->free, node); +} + +/* + * Mostly for valgrind happiness, but it does offer + * a chokepoint for debugging guest memory leaks, too. + */ +void alloc_uninit(QGuestAllocator *allocator) +{ + MemBlock *node; + MemBlock *tmp; + QAllocOpts mask; + + /* Check for guest leaks, and destroy the list. */ + QTAILQ_FOREACH_SAFE(node, &allocator->used, MLIST_ENTNAME, tmp) { + if (allocator->opts & (ALLOC_LEAK_WARN | ALLOC_LEAK_ASSERT)) { + fprintf(stderr, "guest malloc leak @ 0x%016" PRIx64 "; " + "size 0x%016" PRIx64 ".\n", + node->addr, node->size); + } + if (allocator->opts & (ALLOC_LEAK_ASSERT)) { + g_assert_not_reached(); + } + g_free(node); + } + + /* If we have previously asserted that there are no leaks, then there + * should be only one node here with a specific address and size. */ + mask = ALLOC_LEAK_ASSERT | ALLOC_PARANOID; + QTAILQ_FOREACH_SAFE(node, &allocator->free, MLIST_ENTNAME, tmp) { + if ((allocator->opts & mask) == mask) { + if ((node->addr != allocator->start) || + (node->size != allocator->end - allocator->start)) { + fprintf(stderr, "Free list is corrupted.\n"); + g_assert_not_reached(); + } + } + + g_free(node); + } + + g_free(allocator); +} + +uint64_t guest_alloc(QGuestAllocator *allocator, size_t size) +{ + uint64_t rsize = size; + uint64_t naddr; + + rsize += (allocator->page_size - 1); + rsize &= -allocator->page_size; + g_assert_cmpint((allocator->start + rsize), <=, allocator->end); + g_assert_cmpint(rsize, >=, size); + + naddr = mlist_alloc(allocator, rsize); + if (allocator->opts & ALLOC_PARANOID) { + mlist_check(allocator); + } + + return naddr; +} + +void guest_free(QGuestAllocator *allocator, uint64_t addr) +{ + mlist_free(allocator, addr); + if (allocator->opts & ALLOC_PARANOID) { + mlist_check(allocator); + } +} + +QGuestAllocator *alloc_init(uint64_t start, uint64_t end) +{ + QGuestAllocator *s = g_malloc0(sizeof(*s)); + MemBlock *node; + + s->start = start; + s->end = end; + + QTAILQ_INIT(&s->used); + QTAILQ_INIT(&s->free); + + node = mlist_new(s->start, s->end - s->start); + QTAILQ_INSERT_HEAD(&s->free, node, MLIST_ENTNAME); + + s->page_size = DEFAULT_PAGE_SIZE; + + return s; +} + +QGuestAllocator *alloc_init_flags(QAllocOpts opts, + uint64_t start, uint64_t end) +{ + QGuestAllocator *s = alloc_init(start, end); + s->opts = opts; + return s; +} + +void alloc_set_page_size(QGuestAllocator *allocator, size_t page_size) +{ + /* Can't alter the page_size for an allocator in-use */ + g_assert(QTAILQ_EMPTY(&allocator->used)); + + g_assert(is_power_of_2(page_size)); + allocator->page_size = page_size; +} + +void alloc_set_flags(QGuestAllocator *allocator, QAllocOpts opts) +{ + allocator->opts |= opts; +} diff --git a/tests/libqos/malloc.h b/tests/libqos/malloc.h index 5565381..71ac407 100644 --- a/tests/libqos/malloc.h +++ b/tests/libqos/malloc.h @@ -15,24 +15,27 @@ #include #include +#include "qemu/queue.h" + +typedef enum { + ALLOC_NO_FLAGS = 0x00, + ALLOC_LEAK_WARN = 0x01, + ALLOC_LEAK_ASSERT = 0x02, + ALLOC_PARANOID = 0x04 +} QAllocOpts; typedef struct QGuestAllocator QGuestAllocator; -struct QGuestAllocator -{ - uint64_t (*alloc)(QGuestAllocator *allocator, size_t size); - void (*free)(QGuestAllocator *allocator, uint64_t addr); -}; +void alloc_uninit(QGuestAllocator *allocator); /* Always returns page aligned values */ -static inline uint64_t guest_alloc(QGuestAllocator *allocator, size_t size) -{ - return allocator->alloc(allocator, size); -} - -static inline void guest_free(QGuestAllocator *allocator, uint64_t addr) -{ - allocator->free(allocator, addr); -} +uint64_t guest_alloc(QGuestAllocator *allocator, size_t size); +void guest_free(QGuestAllocator *allocator, uint64_t addr); + +QGuestAllocator *alloc_init(uint64_t start, uint64_t end); +QGuestAllocator *alloc_init_flags(QAllocOpts flags, + uint64_t start, uint64_t end); +void alloc_set_page_size(QGuestAllocator *allocator, size_t page_size); +void alloc_set_flags(QGuestAllocator *allocator, QAllocOpts opts); #endif diff --git a/tests/libqos/virtio-mmio.c b/tests/libqos/virtio-mmio.c new file mode 100644 index 0000000..b3e62e7 --- /dev/null +++ b/tests/libqos/virtio-mmio.c @@ -0,0 +1,198 @@ +/* + * libqos virtio MMIO driver + * + * Copyright (c) 2014 Marc Marí + * + * 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 +#include +#include "libqtest.h" +#include "libqos/virtio.h" +#include "libqos/virtio-mmio.h" +#include "libqos/malloc.h" +#include "libqos/malloc-generic.h" + +static uint8_t qvirtio_mmio_config_readb(QVirtioDevice *d, uint64_t addr) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + return readb(dev->addr + addr); +} + +static uint16_t qvirtio_mmio_config_readw(QVirtioDevice *d, uint64_t addr) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + return readw(dev->addr + addr); +} + +static uint32_t qvirtio_mmio_config_readl(QVirtioDevice *d, uint64_t addr) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + return readl(dev->addr + addr); +} + +static uint64_t qvirtio_mmio_config_readq(QVirtioDevice *d, uint64_t addr) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + return readq(dev->addr + addr); +} + +static uint32_t qvirtio_mmio_get_features(QVirtioDevice *d) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + writel(dev->addr + QVIRTIO_MMIO_HOST_FEATURES_SEL, 0); + return readl(dev->addr + QVIRTIO_MMIO_HOST_FEATURES); +} + +static void qvirtio_mmio_set_features(QVirtioDevice *d, uint32_t features) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + dev->features = features; + writel(dev->addr + QVIRTIO_MMIO_GUEST_FEATURES_SEL, 0); + writel(dev->addr + QVIRTIO_MMIO_GUEST_FEATURES, features); +} + +static uint32_t qvirtio_mmio_get_guest_features(QVirtioDevice *d) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + return dev->features; +} + +static uint8_t qvirtio_mmio_get_status(QVirtioDevice *d) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + return (uint8_t)readl(dev->addr + QVIRTIO_MMIO_DEVICE_STATUS); +} + +static void qvirtio_mmio_set_status(QVirtioDevice *d, uint8_t status) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + writel(dev->addr + QVIRTIO_MMIO_DEVICE_STATUS, (uint32_t)status); +} + +static bool qvirtio_mmio_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + uint32_t isr; + + isr = readl(dev->addr + QVIRTIO_MMIO_INTERRUPT_STATUS) & 1; + if (isr != 0) { + writel(dev->addr + QVIRTIO_MMIO_INTERRUPT_ACK, 1); + return true; + } + + return false; +} + +static bool qvirtio_mmio_get_config_isr_status(QVirtioDevice *d) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + uint32_t isr; + + isr = readl(dev->addr + QVIRTIO_MMIO_INTERRUPT_STATUS) & 2; + if (isr != 0) { + writel(dev->addr + QVIRTIO_MMIO_INTERRUPT_ACK, 2); + return true; + } + + return false; +} + +static void qvirtio_mmio_queue_select(QVirtioDevice *d, uint16_t index) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + writel(dev->addr + QVIRTIO_MMIO_QUEUE_SEL, (uint32_t)index); + + g_assert_cmphex(readl(dev->addr + QVIRTIO_MMIO_QUEUE_PFN), ==, 0); +} + +static uint16_t qvirtio_mmio_get_queue_size(QVirtioDevice *d) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + return (uint16_t)readl(dev->addr + QVIRTIO_MMIO_QUEUE_NUM_MAX); +} + +static void qvirtio_mmio_set_queue_address(QVirtioDevice *d, uint32_t pfn) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + writel(dev->addr + QVIRTIO_MMIO_QUEUE_PFN, pfn); +} + +static QVirtQueue *qvirtio_mmio_virtqueue_setup(QVirtioDevice *d, + QGuestAllocator *alloc, uint16_t index) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + QVirtQueue *vq; + uint64_t addr; + + vq = g_malloc0(sizeof(*vq)); + qvirtio_mmio_queue_select(d, index); + writel(dev->addr + QVIRTIO_MMIO_QUEUE_ALIGN, dev->page_size); + + vq->index = index; + vq->size = qvirtio_mmio_get_queue_size(d); + vq->free_head = 0; + vq->num_free = vq->size; + vq->align = dev->page_size; + vq->indirect = (dev->features & QVIRTIO_F_RING_INDIRECT_DESC) != 0; + vq->event = (dev->features & QVIRTIO_F_RING_EVENT_IDX) != 0; + + writel(dev->addr + QVIRTIO_MMIO_QUEUE_NUM, vq->size); + + /* Check different than 0 */ + g_assert_cmpint(vq->size, !=, 0); + + /* Check power of 2 */ + g_assert_cmpint(vq->size & (vq->size - 1), ==, 0); + + addr = guest_alloc(alloc, qvring_size(vq->size, dev->page_size)); + qvring_init(alloc, vq, addr); + qvirtio_mmio_set_queue_address(d, vq->desc / dev->page_size); + + return vq; +} + +static void qvirtio_mmio_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + writel(dev->addr + QVIRTIO_MMIO_QUEUE_NOTIFY, vq->index); +} + +const QVirtioBus qvirtio_mmio = { + .config_readb = qvirtio_mmio_config_readb, + .config_readw = qvirtio_mmio_config_readw, + .config_readl = qvirtio_mmio_config_readl, + .config_readq = qvirtio_mmio_config_readq, + .get_features = qvirtio_mmio_get_features, + .set_features = qvirtio_mmio_set_features, + .get_guest_features = qvirtio_mmio_get_guest_features, + .get_status = qvirtio_mmio_get_status, + .set_status = qvirtio_mmio_set_status, + .get_queue_isr_status = qvirtio_mmio_get_queue_isr_status, + .get_config_isr_status = qvirtio_mmio_get_config_isr_status, + .queue_select = qvirtio_mmio_queue_select, + .get_queue_size = qvirtio_mmio_get_queue_size, + .set_queue_address = qvirtio_mmio_set_queue_address, + .virtqueue_setup = qvirtio_mmio_virtqueue_setup, + .virtqueue_kick = qvirtio_mmio_virtqueue_kick, +}; + +QVirtioMMIODevice *qvirtio_mmio_init_device(uint64_t addr, uint32_t page_size) +{ + QVirtioMMIODevice *dev; + uint32_t magic; + dev = g_malloc0(sizeof(*dev)); + + magic = readl(addr + QVIRTIO_MMIO_MAGIC_VALUE); + g_assert(magic == ('v' | 'i' << 8 | 'r' << 16 | 't' << 24)); + + dev->addr = addr; + dev->page_size = page_size; + dev->vdev.device_type = readl(addr + QVIRTIO_MMIO_DEVICE_ID); + + writel(addr + QVIRTIO_MMIO_GUEST_PAGE_SIZE, page_size); + + return dev; +} diff --git a/tests/libqos/virtio-mmio.h b/tests/libqos/virtio-mmio.h new file mode 100644 index 0000000..e3e52b9 --- /dev/null +++ b/tests/libqos/virtio-mmio.h @@ -0,0 +1,46 @@ +/* + * libqos virtio MMIO definitions + * + * Copyright (c) 2014 Marc Marí + * + * 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 LIBQOS_VIRTIO_MMIO_H +#define LIBQOS_VIRTIO_MMIO_H + +#include "libqos/virtio.h" + +#define QVIRTIO_MMIO_MAGIC_VALUE 0x000 +#define QVIRTIO_MMIO_VERSION 0x004 +#define QVIRTIO_MMIO_DEVICE_ID 0x008 +#define QVIRTIO_MMIO_VENDOR_ID 0x00C +#define QVIRTIO_MMIO_HOST_FEATURES 0x010 +#define QVIRTIO_MMIO_HOST_FEATURES_SEL 0x014 +#define QVIRTIO_MMIO_GUEST_FEATURES 0x020 +#define QVIRTIO_MMIO_GUEST_FEATURES_SEL 0x024 +#define QVIRTIO_MMIO_GUEST_PAGE_SIZE 0x028 +#define QVIRTIO_MMIO_QUEUE_SEL 0x030 +#define QVIRTIO_MMIO_QUEUE_NUM_MAX 0x034 +#define QVIRTIO_MMIO_QUEUE_NUM 0x038 +#define QVIRTIO_MMIO_QUEUE_ALIGN 0x03C +#define QVIRTIO_MMIO_QUEUE_PFN 0x040 +#define QVIRTIO_MMIO_QUEUE_NOTIFY 0x050 +#define QVIRTIO_MMIO_INTERRUPT_STATUS 0x060 +#define QVIRTIO_MMIO_INTERRUPT_ACK 0x064 +#define QVIRTIO_MMIO_DEVICE_STATUS 0x070 +#define QVIRTIO_MMIO_DEVICE_SPECIFIC 0x100 + +typedef struct QVirtioMMIODevice { + QVirtioDevice vdev; + uint64_t addr; + uint32_t page_size; + uint32_t features; /* As it cannot be read later, save it */ +} QVirtioMMIODevice; + +extern const QVirtioBus qvirtio_mmio; + +QVirtioMMIODevice *qvirtio_mmio_init_device(uint64_t addr, uint32_t page_size); + +#endif diff --git a/tests/libqos/virtio-pci.c b/tests/libqos/virtio-pci.c index 788ebaf..f9fb924 100644 --- a/tests/libqos/virtio-pci.c +++ b/tests/libqos/virtio-pci.c @@ -60,25 +60,25 @@ static void qvirtio_pci_assign_device(QVirtioDevice *d, void *data) *vpcidev = (QVirtioPCIDevice *)d; } -static uint8_t qvirtio_pci_config_readb(QVirtioDevice *d, void *addr) +static uint8_t qvirtio_pci_config_readb(QVirtioDevice *d, uint64_t addr) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readb(dev->pdev, addr); + return qpci_io_readb(dev->pdev, (void *)(uintptr_t)addr); } -static uint16_t qvirtio_pci_config_readw(QVirtioDevice *d, void *addr) +static uint16_t qvirtio_pci_config_readw(QVirtioDevice *d, uint64_t addr) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readw(dev->pdev, addr); + return qpci_io_readw(dev->pdev, (void *)(uintptr_t)addr); } -static uint32_t qvirtio_pci_config_readl(QVirtioDevice *d, void *addr) +static uint32_t qvirtio_pci_config_readl(QVirtioDevice *d, uint64_t addr) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readl(dev->pdev, addr); + return qpci_io_readl(dev->pdev, (void *)(uintptr_t)addr); } -static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, void *addr) +static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, uint64_t addr) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; int i; @@ -86,11 +86,13 @@ static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, void *addr) if (qtest_big_endian()) { for (i = 0; i < 8; ++i) { - u64 |= (uint64_t)qpci_io_readb(dev->pdev, addr + i) << (7 - i) * 8; + u64 |= (uint64_t)qpci_io_readb(dev->pdev, + (void *)(uintptr_t)addr + i) << (7 - i) * 8; } } else { for (i = 0; i < 8; ++i) { - u64 |= (uint64_t)qpci_io_readb(dev->pdev, addr + i) << i * 8; + u64 |= (uint64_t)qpci_io_readb(dev->pdev, + (void *)(uintptr_t)addr + i) << i * 8; } } @@ -100,31 +102,31 @@ static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, void *addr) static uint32_t qvirtio_pci_get_features(QVirtioDevice *d) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readl(dev->pdev, dev->addr + QVIRTIO_DEVICE_FEATURES); + return qpci_io_readl(dev->pdev, dev->addr + QVIRTIO_PCI_DEVICE_FEATURES); } static void qvirtio_pci_set_features(QVirtioDevice *d, uint32_t features) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - qpci_io_writel(dev->pdev, dev->addr + QVIRTIO_GUEST_FEATURES, features); + qpci_io_writel(dev->pdev, dev->addr + QVIRTIO_PCI_GUEST_FEATURES, features); } static uint32_t qvirtio_pci_get_guest_features(QVirtioDevice *d) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readl(dev->pdev, dev->addr + QVIRTIO_GUEST_FEATURES); + return qpci_io_readl(dev->pdev, dev->addr + QVIRTIO_PCI_GUEST_FEATURES); } static uint8_t qvirtio_pci_get_status(QVirtioDevice *d) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_DEVICE_STATUS); + return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_PCI_DEVICE_STATUS); } static void qvirtio_pci_set_status(QVirtioDevice *d, uint8_t status) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - qpci_io_writeb(dev->pdev, dev->addr + QVIRTIO_DEVICE_STATUS, status); + qpci_io_writeb(dev->pdev, dev->addr + QVIRTIO_PCI_DEVICE_STATUS, status); } static bool qvirtio_pci_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq) @@ -140,11 +142,15 @@ static bool qvirtio_pci_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq) return qpci_msix_pending(dev->pdev, vqpci->msix_entry); } else { data = readl(vqpci->msix_addr); - writel(vqpci->msix_addr, 0); - return data == vqpci->msix_data; + if (data == vqpci->msix_data) { + writel(vqpci->msix_addr, 0); + return true; + } else { + return false; + } } } else { - return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_ISR_STATUS) & 1; + return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_PCI_ISR_STATUS) & 1; } } @@ -160,30 +166,34 @@ static bool qvirtio_pci_get_config_isr_status(QVirtioDevice *d) return qpci_msix_pending(dev->pdev, dev->config_msix_entry); } else { data = readl(dev->config_msix_addr); - writel(dev->config_msix_addr, 0); - return data == dev->config_msix_data; + if (data == dev->config_msix_data) { + writel(dev->config_msix_addr, 0); + return true; + } else { + return false; + } } } else { - return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_ISR_STATUS) & 2; + return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_PCI_ISR_STATUS) & 2; } } static void qvirtio_pci_queue_select(QVirtioDevice *d, uint16_t index) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - qpci_io_writeb(dev->pdev, dev->addr + QVIRTIO_QUEUE_SELECT, index); + qpci_io_writeb(dev->pdev, dev->addr + QVIRTIO_PCI_QUEUE_SELECT, index); } static uint16_t qvirtio_pci_get_queue_size(QVirtioDevice *d) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readw(dev->pdev, dev->addr + QVIRTIO_QUEUE_SIZE); + return qpci_io_readw(dev->pdev, dev->addr + QVIRTIO_PCI_QUEUE_SIZE); } static void qvirtio_pci_set_queue_address(QVirtioDevice *d, uint32_t pfn) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - qpci_io_writel(dev->pdev, dev->addr + QVIRTIO_QUEUE_ADDRESS, pfn); + qpci_io_writel(dev->pdev, dev->addr + QVIRTIO_PCI_QUEUE_ADDRESS, pfn); } static QVirtQueue *qvirtio_pci_virtqueue_setup(QVirtioDevice *d, @@ -225,7 +235,7 @@ static QVirtQueue *qvirtio_pci_virtqueue_setup(QVirtioDevice *d, static void qvirtio_pci_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - qpci_io_writew(dev->pdev, dev->addr + QVIRTIO_QUEUE_NOTIFY, vq->index); + qpci_io_writew(dev->pdev, dev->addr + QVIRTIO_PCI_QUEUE_NOTIFY, vq->index); } const QVirtioBus qvirtio_pci = { @@ -305,8 +315,8 @@ void qvirtqueue_pci_msix_setup(QVirtioPCIDevice *d, QVirtQueuePCI *vqpci, control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT); qvirtio_pci_queue_select(&d->vdev, vqpci->vq.index); - qpci_io_writew(d->pdev, d->addr + QVIRTIO_MSIX_QUEUE_VECTOR, entry); - vector = qpci_io_readw(d->pdev, d->addr + QVIRTIO_MSIX_QUEUE_VECTOR); + qpci_io_writew(d->pdev, d->addr + QVIRTIO_PCI_MSIX_QUEUE_VECTOR, entry); + vector = qpci_io_readw(d->pdev, d->addr + QVIRTIO_PCI_MSIX_QUEUE_VECTOR); g_assert_cmphex(vector, !=, QVIRTIO_MSI_NO_VECTOR); } @@ -337,7 +347,7 @@ void qvirtio_pci_set_msix_configuration_vector(QVirtioPCIDevice *d, qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_VECTOR_CTRL, control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT); - qpci_io_writew(d->pdev, d->addr + QVIRTIO_MSIX_CONF_VECTOR, entry); - vector = qpci_io_readw(d->pdev, d->addr + QVIRTIO_MSIX_CONF_VECTOR); + qpci_io_writew(d->pdev, d->addr + QVIRTIO_PCI_MSIX_CONF_VECTOR, entry); + vector = qpci_io_readw(d->pdev, d->addr + QVIRTIO_PCI_MSIX_CONF_VECTOR); g_assert_cmphex(vector, !=, QVIRTIO_MSI_NO_VECTOR); } diff --git a/tests/libqos/virtio-pci.h b/tests/libqos/virtio-pci.h index 883f7ff..8f0e52a 100644 --- a/tests/libqos/virtio-pci.h +++ b/tests/libqos/virtio-pci.h @@ -13,18 +13,18 @@ #include "libqos/virtio.h" #include "libqos/pci.h" -#define QVIRTIO_DEVICE_FEATURES 0x00 -#define QVIRTIO_GUEST_FEATURES 0x04 -#define QVIRTIO_QUEUE_ADDRESS 0x08 -#define QVIRTIO_QUEUE_SIZE 0x0C -#define QVIRTIO_QUEUE_SELECT 0x0E -#define QVIRTIO_QUEUE_NOTIFY 0x10 -#define QVIRTIO_DEVICE_STATUS 0x12 -#define QVIRTIO_ISR_STATUS 0x13 -#define QVIRTIO_MSIX_CONF_VECTOR 0x14 -#define QVIRTIO_MSIX_QUEUE_VECTOR 0x16 -#define QVIRTIO_DEVICE_SPECIFIC_MSIX 0x18 -#define QVIRTIO_DEVICE_SPECIFIC_NO_MSIX 0x14 +#define QVIRTIO_PCI_DEVICE_FEATURES 0x00 +#define QVIRTIO_PCI_GUEST_FEATURES 0x04 +#define QVIRTIO_PCI_QUEUE_ADDRESS 0x08 +#define QVIRTIO_PCI_QUEUE_SIZE 0x0C +#define QVIRTIO_PCI_QUEUE_SELECT 0x0E +#define QVIRTIO_PCI_QUEUE_NOTIFY 0x10 +#define QVIRTIO_PCI_DEVICE_STATUS 0x12 +#define QVIRTIO_PCI_ISR_STATUS 0x13 +#define QVIRTIO_PCI_MSIX_CONF_VECTOR 0x14 +#define QVIRTIO_PCI_MSIX_QUEUE_VECTOR 0x16 +#define QVIRTIO_PCI_DEVICE_SPECIFIC_MSIX 0x18 +#define QVIRTIO_PCI_DEVICE_SPECIFIC_NO_MSIX 0x14 #define QVIRTIO_PCI_ALIGN 4096 diff --git a/tests/libqos/virtio.c b/tests/libqos/virtio.c index a061289..3205b88 100644 --- a/tests/libqos/virtio.c +++ b/tests/libqos/virtio.c @@ -12,25 +12,25 @@ #include "libqos/virtio.h" uint8_t qvirtio_config_readb(const QVirtioBus *bus, QVirtioDevice *d, - void *addr) + uint64_t addr) { return bus->config_readb(d, addr); } uint16_t qvirtio_config_readw(const QVirtioBus *bus, QVirtioDevice *d, - void *addr) + uint64_t addr) { return bus->config_readw(d, addr); } uint32_t qvirtio_config_readl(const QVirtioBus *bus, QVirtioDevice *d, - void *addr) + uint64_t addr) { return bus->config_readl(d, addr); } uint64_t qvirtio_config_readq(const QVirtioBus *bus, QVirtioDevice *d, - void *addr) + uint64_t addr) { return bus->config_readq(d, addr); } diff --git a/tests/libqos/virtio.h b/tests/libqos/virtio.h index 29fbacb..2449fee 100644 --- a/tests/libqos/virtio.h +++ b/tests/libqos/virtio.h @@ -93,10 +93,10 @@ typedef struct QVRingIndirectDesc { } QVRingIndirectDesc; typedef struct QVirtioBus { - uint8_t (*config_readb)(QVirtioDevice *d, void *addr); - uint16_t (*config_readw)(QVirtioDevice *d, void *addr); - uint32_t (*config_readl)(QVirtioDevice *d, void *addr); - uint64_t (*config_readq)(QVirtioDevice *d, void *addr); + uint8_t (*config_readb)(QVirtioDevice *d, uint64_t addr); + uint16_t (*config_readw)(QVirtioDevice *d, uint64_t addr); + uint32_t (*config_readl)(QVirtioDevice *d, uint64_t addr); + uint64_t (*config_readq)(QVirtioDevice *d, uint64_t addr); /* Get features of the device */ uint32_t (*get_features)(QVirtioDevice *d); @@ -144,13 +144,13 @@ static inline uint32_t qvring_size(uint32_t num, uint32_t align) } uint8_t qvirtio_config_readb(const QVirtioBus *bus, QVirtioDevice *d, - void *addr); + uint64_t addr); uint16_t qvirtio_config_readw(const QVirtioBus *bus, QVirtioDevice *d, - void *addr); + uint64_t addr); uint32_t qvirtio_config_readl(const QVirtioBus *bus, QVirtioDevice *d, - void *addr); + uint64_t addr); uint64_t qvirtio_config_readq(const QVirtioBus *bus, QVirtioDevice *d, - void *addr); + uint64_t addr); uint32_t qvirtio_get_features(const QVirtioBus *bus, QVirtioDevice *d); void qvirtio_set_features(const QVirtioBus *bus, QVirtioDevice *d, uint32_t features); diff --git a/tests/libqtest.c b/tests/libqtest.c index 9a92aa7..12d65bd 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -652,6 +652,13 @@ void qtest_add_func(const char *str, void (*fn)) g_free(path); } +void qtest_add_data_func(const char *str, const void *data, void (*fn)) +{ + gchar *path = g_strdup_printf("/%s/%s", qtest_get_arch(), str); + g_test_add_data_func(path, data, fn); + g_free(path); +} + void qtest_memwrite(QTestState *s, uint64_t addr, const void *data, size_t size) { const uint8_t *ptr = data; diff --git a/tests/libqtest.h b/tests/libqtest.h index e7413d5..03469b8 100644 --- a/tests/libqtest.h +++ b/tests/libqtest.h @@ -345,6 +345,38 @@ const char *qtest_get_arch(void); void qtest_add_func(const char *str, void (*fn)); /** + * qtest_add_data_func: + * @str: Test case path. + * @data: Test case data + * @fn: Test case function + * + * Add a GTester testcase with the given name, data and function. + * The path is prefixed with the architecture under test, as + * returned by qtest_get_arch(). + */ +void qtest_add_data_func(const char *str, const void *data, void (*fn)); + +/** + * qtest_add: + * @testpath: Test case path + * @Fixture: Fixture type + * @tdata: Test case data + * @fsetup: Test case setup function + * @ftest: Test case function + * @fteardown: Test case teardown function + * + * Add a GTester testcase with the given name, data and functions. + * The path is prefixed with the architecture under test, as + * returned by qtest_get_arch(). + */ +#define qtest_add(testpath, Fixture, tdata, fsetup, ftest, fteardown) \ + do { \ + char *path = g_strdup_printf("/%s/%s", qtest_get_arch(), testpath); \ + g_test_add(path, Fixture, tdata, fsetup, ftest, fteardown); \ + g_free(path); \ + } while (0) + +/** * qtest_start: * @args: other arguments to pass to QEMU * diff --git a/tests/multiboot/Makefile b/tests/multiboot/Makefile index 34cdd81..36f01dc 100644 --- a/tests/multiboot/Makefile +++ b/tests/multiboot/Makefile @@ -6,11 +6,14 @@ LD=ld LDFLAGS=-melf_i386 -T link.ld LIBS=$(shell $(CC) $(CCFLAGS) -print-libgcc-file-name) -all: mmap.elf +all: mmap.elf modules.elf mmap.elf: start.o mmap.o libc.o $(LD) $(LDFLAGS) -o $@ $^ $(LIBS) +modules.elf: start.o modules.o libc.o + $(LD) $(LDFLAGS) -o $@ $^ $(LIBS) + %.o: %.c $(CC) $(CCFLAGS) -c -o $@ $^ diff --git a/tests/multiboot/libc.c b/tests/multiboot/libc.c index 05abbd9..6df9bda 100644 --- a/tests/multiboot/libc.c +++ b/tests/multiboot/libc.c @@ -22,6 +22,18 @@ #include "libc.h" +void* memcpy(void *dest, const void *src, int n) +{ + char *d = dest; + const char *s = src; + + while (n--) { + *d++ = *s++; + } + + return dest; +} + static void print_char(char c) { outb(0xe9, c); diff --git a/tests/multiboot/libc.h b/tests/multiboot/libc.h index 80eec5b..04c9922 100644 --- a/tests/multiboot/libc.h +++ b/tests/multiboot/libc.h @@ -57,5 +57,6 @@ static inline void outb(uint16_t port, uint8_t data) /* Misc functions */ void printf(const char *fmt, ...); +void* memcpy(void *dest, const void *src, int n); #endif diff --git a/tests/multiboot/mmap.out b/tests/multiboot/mmap.out index e70b6eb..003e109 100644 --- a/tests/multiboot/mmap.out +++ b/tests/multiboot/mmap.out @@ -4,14 +4,14 @@ === Running test case: mmap.elf === Lower memory: 639k -Upper memory: 130040k +Upper memory: 129920k e820 memory map: 0x0 - 0x9fc00: type 1 [entry size: 20] 0x9fc00 - 0xa0000: type 2 [entry size: 20] 0xf0000 - 0x100000: type 2 [entry size: 20] -0x100000 - 0x7ffe000: type 1 [entry size: 20] -0x7ffe000 - 0x8000000: type 2 [entry size: 20] +0x100000 - 0x7fe0000: type 1 [entry size: 20] +0x7fe0000 - 0x8000000: type 2 [entry size: 20] 0xfffc0000 - 0x100000000: type 2 [entry size: 20] mmap start: 0x9000 @@ -22,32 +22,31 @@ real mmap end: 0x9090 === Running test case: mmap.elf -m 1.1M === Lower memory: 639k -Upper memory: 96k +Upper memory: 104k e820 memory map: 0x0 - 0x9fc00: type 1 [entry size: 20] 0x9fc00 - 0xa0000: type 2 [entry size: 20] 0xf0000 - 0x100000: type 2 [entry size: 20] -0x100000 - 0x118000: type 1 [entry size: 20] -0x118000 - 0x11a000: type 2 [entry size: 20] +0x100000 - 0x11a000: type 1 [entry size: 20] 0xfffc0000 - 0x100000000: type 2 [entry size: 20] mmap start: 0x9000 -mmap end: 0x9090 -real mmap end: 0x9090 +mmap end: 0x9078 +real mmap end: 0x9078 === Running test case: mmap.elf -m 2G === Lower memory: 639k -Upper memory: 2096120k +Upper memory: 2096000k e820 memory map: 0x0 - 0x9fc00: type 1 [entry size: 20] 0x9fc00 - 0xa0000: type 2 [entry size: 20] 0xf0000 - 0x100000: type 2 [entry size: 20] -0x100000 - 0x7fffe000: type 1 [entry size: 20] -0x7fffe000 - 0x80000000: type 2 [entry size: 20] +0x100000 - 0x7ffe0000: type 1 [entry size: 20] +0x7ffe0000 - 0x80000000: type 2 [entry size: 20] 0xfffc0000 - 0x100000000: type 2 [entry size: 20] mmap start: 0x9000 @@ -58,16 +57,16 @@ real mmap end: 0x9090 === Running test case: mmap.elf -m 4G === Lower memory: 639k -Upper memory: 3668984k +Upper memory: 3144576k e820 memory map: 0x0 - 0x9fc00: type 1 [entry size: 20] 0x9fc00 - 0xa0000: type 2 [entry size: 20] 0xf0000 - 0x100000: type 2 [entry size: 20] -0x100000 - 0xdfffe000: type 1 [entry size: 20] -0xdfffe000 - 0xe0000000: type 2 [entry size: 20] +0x100000 - 0xbffe0000: type 1 [entry size: 20] +0xbffe0000 - 0xc0000000: type 2 [entry size: 20] 0xfffc0000 - 0x100000000: type 2 [entry size: 20] -0x100000000 - 0x120000000: type 1 [entry size: 20] +0x100000000 - 0x140000000: type 1 [entry size: 20] mmap start: 0x9000 mmap end: 0x90a8 @@ -77,16 +76,16 @@ real mmap end: 0x90a8 === Running test case: mmap.elf -m 8G === Lower memory: 639k -Upper memory: 3668984k +Upper memory: 3144576k e820 memory map: 0x0 - 0x9fc00: type 1 [entry size: 20] 0x9fc00 - 0xa0000: type 2 [entry size: 20] 0xf0000 - 0x100000: type 2 [entry size: 20] -0x100000 - 0xdfffe000: type 1 [entry size: 20] -0xdfffe000 - 0xe0000000: type 2 [entry size: 20] +0x100000 - 0xbffe0000: type 1 [entry size: 20] +0xbffe0000 - 0xc0000000: type 2 [entry size: 20] 0xfffc0000 - 0x100000000: type 2 [entry size: 20] -0x100000000 - 0x220000000: type 1 [entry size: 20] +0x100000000 - 0x240000000: type 1 [entry size: 20] mmap start: 0x9000 mmap end: 0x90a8 diff --git a/tests/multiboot/module.txt b/tests/multiboot/module.txt new file mode 100644 index 0000000..54c1d27 --- /dev/null +++ b/tests/multiboot/module.txt @@ -0,0 +1 @@ +This is a test file that is used as a multiboot module. diff --git a/tests/multiboot/modules.c b/tests/multiboot/modules.c new file mode 100644 index 0000000..531601f --- /dev/null +++ b/tests/multiboot/modules.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2015 Kevin Wolf + * + * 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 "libc.h" +#include "multiboot.h" + +int test_main(uint32_t magic, struct mb_info *mbi) +{ + struct mb_module *mod; + unsigned int i; + + (void) magic; + + printf("Module list with %d entries at %x\n", + mbi->mods_count, mbi->mods_addr); + + for (i = 0, mod = (struct mb_module*) mbi->mods_addr; + i < mbi->mods_count; + i++, mod++) + { + char buf[1024]; + unsigned int size = mod->mod_end - mod->mod_start; + + printf("[%p] Module: %x - %x (%d bytes) '%s'\n", + mod, mod->mod_start, mod->mod_end, size, mod->string); + + /* Print test file, but remove the newline at the end */ + if (size < sizeof(buf)) { + memcpy(buf, (void*) mod->mod_start, size); + buf[size - 1] = '\0'; + printf(" Content: '%s'\n", buf); + } + } + + return 0; +} diff --git a/tests/multiboot/modules.out b/tests/multiboot/modules.out new file mode 100644 index 0000000..1636708 --- /dev/null +++ b/tests/multiboot/modules.out @@ -0,0 +1,38 @@ + + + +=== Running test case: modules.elf === + +Module list with 0 entries at 102000 + + +=== Running test case: modules.elf -initrd module.txt === + +Module list with 1 entries at 102000 +[102000] Module: 103000 - 103038 (56 bytes) 'module.txt' + Content: 'This is a test file that is used as a multiboot module.' + + +=== Running test case: modules.elf -initrd module.txt argument === + +Module list with 1 entries at 102000 +[102000] Module: 103000 - 103038 (56 bytes) 'module.txt argument' + Content: 'This is a test file that is used as a multiboot module.' + + +=== Running test case: modules.elf -initrd module.txt argument,,with,,commas === + +Module list with 1 entries at 102000 +[102000] Module: 103000 - 103038 (56 bytes) 'module.txt argument,with,commas' + Content: 'This is a test file that is used as a multiboot module.' + + +=== Running test case: modules.elf -initrd module.txt,module.txt argument,module.txt === + +Module list with 3 entries at 102000 +[102000] Module: 103000 - 103038 (56 bytes) 'module.txt' + Content: 'This is a test file that is used as a multiboot module.' +[102010] Module: 104000 - 104038 (56 bytes) 'module.txt argument' + Content: 'This is a test file that is used as a multiboot module.' +[102020] Module: 105000 - 105038 (56 bytes) 'module.txt' + Content: 'This is a test file that is used as a multiboot module.' diff --git a/tests/multiboot/run_test.sh b/tests/multiboot/run_test.sh index 97a9a49..78d7edf 100755 --- a/tests/multiboot/run_test.sh +++ b/tests/multiboot/run_test.sh @@ -48,10 +48,17 @@ mmap() { run_qemu mmap.elf -m 8G } +modules() { + run_qemu modules.elf + run_qemu modules.elf -initrd module.txt + run_qemu modules.elf -initrd "module.txt argument" + run_qemu modules.elf -initrd "module.txt argument,,with,,commas" + run_qemu modules.elf -initrd "module.txt,module.txt argument,module.txt" +} make all -for t in mmap; do +for t in mmap modules; do echo > test.log $t diff --git a/tests/nvme-test.c b/tests/nvme-test.c index 85768e8..ff38b5e 100644 --- a/tests/nvme-test.c +++ b/tests/nvme-test.c @@ -24,7 +24,7 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); qtest_add_func("/nvme/nop", nop); - qtest_start("-drive id=drv0,if=none,file=/dev/null " + qtest_start("-drive id=drv0,if=none,file=/dev/null,format=raw " "-device nvme,drive=drv0,serial=foo"); ret = g_test_run(); diff --git a/tests/pc-cpu-test.c b/tests/pc-cpu-test.c new file mode 100644 index 0000000..a0122d3 --- /dev/null +++ b/tests/pc-cpu-test.c @@ -0,0 +1,147 @@ +/* + * QTest testcase for PC CPUs + * + * Copyright (c) 2015 SUSE Linux GmbH + * + * 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 +#include + +#include "qemu-common.h" +#include "libqtest.h" +#include "qemu/osdep.h" +#include "qapi/qmp/types.h" + +struct PCTestData { + const char *machine; + const char *cpu_model; + unsigned sockets; + unsigned cores; + unsigned threads; + unsigned maxcpus; +}; +typedef struct PCTestData PCTestData; + +static void test_pc_with_cpu_add(gconstpointer data) +{ + const PCTestData *s = data; + char *args; + QDict *response; + unsigned int i; + + args = g_strdup_printf("-machine %s -cpu %s " + "-smp sockets=%u,cores=%u,threads=%u,maxcpus=%u", + s->machine, s->cpu_model, + s->sockets, s->cores, s->threads, s->maxcpus); + qtest_start(args); + + for (i = s->sockets * s->cores * s->threads; i < s->maxcpus; i++) { + response = qmp("{ 'execute': 'cpu-add'," + " 'arguments': { 'id': %d } }", i); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + QDECREF(response); + } + + qtest_end(); + g_free(args); +} + +static void test_pc_without_cpu_add(gconstpointer data) +{ + const PCTestData *s = data; + char *args; + QDict *response; + + args = g_strdup_printf("-machine %s -cpu %s " + "-smp sockets=%u,cores=%u,threads=%u,maxcpus=%u", + s->machine, s->cpu_model, + s->sockets, s->cores, s->threads, s->maxcpus); + qtest_start(args); + + response = qmp("{ 'execute': 'cpu-add'," + " 'arguments': { 'id': %d } }", + s->sockets * s->cores * s->threads); + g_assert(response); + g_assert(qdict_haskey(response, "error")); + QDECREF(response); + + qtest_end(); + g_free(args); +} + +static void add_pc_test_cases(void) +{ + const char *arch = qtest_get_arch(); + QDict *response, *minfo; + QList *list; + const QListEntry *p; + QObject *qobj; + QString *qstr; + const char *mname, *path; + PCTestData *data; + + qtest_start("-machine none"); + response = qmp("{ 'execute': 'query-machines' }"); + g_assert(response); + list = qdict_get_qlist(response, "return"); + g_assert(list); + + for (p = qlist_first(list); p; p = qlist_next(p)) { + minfo = qobject_to_qdict(qlist_entry_obj(p)); + g_assert(minfo); + qobj = qdict_get(minfo, "name"); + g_assert(qobj); + qstr = qobject_to_qstring(qobj); + g_assert(qstr); + mname = qstring_get_str(qstr); + if (!g_str_has_prefix(mname, "pc-")) { + continue; + } + data = g_malloc(sizeof(PCTestData)); + data->machine = mname; + data->cpu_model = "Haswell"; /* 1.3+ theoretically */ + data->sockets = 1; + data->cores = 3; + data->threads = 2; + data->maxcpus = data->sockets * data->cores * data->threads * 2; + if (g_str_has_suffix(mname, "-1.4") || + (strcmp(mname, "pc-1.3") == 0) || + (strcmp(mname, "pc-1.2") == 0) || + (strcmp(mname, "pc-1.1") == 0) || + (strcmp(mname, "pc-1.0") == 0) || + (strcmp(mname, "pc-0.15") == 0) || + (strcmp(mname, "pc-0.14") == 0) || + (strcmp(mname, "pc-0.13") == 0) || + (strcmp(mname, "pc-0.12") == 0) || + (strcmp(mname, "pc-0.11") == 0) || + (strcmp(mname, "pc-0.10") == 0)) { + path = g_strdup_printf("/%s/cpu/%s/init/%ux%ux%u&maxcpus=%u", + arch, mname, data->sockets, data->cores, + data->threads, data->maxcpus); + g_test_add_data_func(path, data, test_pc_without_cpu_add); + } else { + path = g_strdup_printf("/%s/cpu/%s/add/%ux%ux%u&maxcpus=%u", + arch, mname, data->sockets, data->cores, + data->threads, data->maxcpus); + g_test_add_data_func(path, data, test_pc_with_cpu_add); + } + } + qtest_end(); +} + +int main(int argc, char **argv) +{ + const char *arch = qtest_get_arch(); + + g_test_init(&argc, &argv, NULL); + + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + add_pc_test_cases(); + } + + return g_test_run(); +} diff --git a/tests/qemu-iotests-quick.sh b/tests/qemu-iotests-quick.sh index 12af731..0e554bb 100755 --- a/tests/qemu-iotests-quick.sh +++ b/tests/qemu-iotests-quick.sh @@ -3,6 +3,6 @@ cd tests/qemu-iotests ret=0 -./check -T -qcow2 -g quick || ret=1 +TEST_DIR=${TEST_DIR:-/tmp/qemu-iotests-quick-$$} ./check -T -qcow2 -g quick || ret=1 exit $ret diff --git a/tests/qemu-iotests/.gitignore b/tests/qemu-iotests/.gitignore index 0541f80..0711cbd 100644 --- a/tests/qemu-iotests/.gitignore +++ b/tests/qemu-iotests/.gitignore @@ -1,5 +1,6 @@ check.log check.time +common.env *.out.bad *.notrun socket_scm_helper diff --git a/tests/qemu-iotests/001.out b/tests/qemu-iotests/001.out index 330f5c9..b80e098 100644 --- a/tests/qemu-iotests/001.out +++ b/tests/qemu-iotests/001.out @@ -1,5 +1,5 @@ QA output created by 001 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 == reading whole image == read 134217728/134217728 bytes at offset 0 diff --git a/tests/qemu-iotests/002.out b/tests/qemu-iotests/002.out index cd6aa0f..1b46b34 100644 --- a/tests/qemu-iotests/002.out +++ b/tests/qemu-iotests/002.out @@ -1,5 +1,5 @@ QA output created by 002 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 == reading whole image == read 134217728/134217728 bytes at offset 0 diff --git a/tests/qemu-iotests/003.out b/tests/qemu-iotests/003.out index 4942482..e764123 100644 --- a/tests/qemu-iotests/003.out +++ b/tests/qemu-iotests/003.out @@ -1,5 +1,5 @@ QA output created by 003 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 == reading whole image == read 134217728/134217728 bytes at offset 0 diff --git a/tests/qemu-iotests/004 b/tests/qemu-iotests/004 index 651072e..2ad77ed 100755 --- a/tests/qemu-iotests/004 +++ b/tests/qemu-iotests/004 @@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.rc . ./common.filter -_supported_fmt generic +_supported_fmt raw qcow qcow2 qed vdi vmdk vhdx _supported_proto generic _supported_os Linux diff --git a/tests/qemu-iotests/004.out b/tests/qemu-iotests/004.out index f1ed4d8..fb85932 100644 --- a/tests/qemu-iotests/004.out +++ b/tests/qemu-iotests/004.out @@ -1,5 +1,5 @@ QA output created by 004 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 write before image boundary wrote 1048576/1048576 bytes at offset 133169152 diff --git a/tests/qemu-iotests/005.out b/tests/qemu-iotests/005.out index 2d3e7df..10044c3 100644 --- a/tests/qemu-iotests/005.out +++ b/tests/qemu-iotests/005.out @@ -1,7 +1,7 @@ QA output created by 005 creating large image -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=5368709120000 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=5368709120000 small read read 4096/4096 bytes at offset 1024 diff --git a/tests/qemu-iotests/006.out b/tests/qemu-iotests/006.out deleted file mode 100644 index f82fbfb..0000000 --- a/tests/qemu-iotests/006.out +++ /dev/null @@ -1,6 +0,0 @@ -QA output created by 006 - -creating 128GB image -qemu-img: The image size is too large for file format 'vpc' -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=137438953472 -*** done diff --git a/tests/qemu-iotests/007 b/tests/qemu-iotests/007 index fe1a743..7b5aff5 100755 --- a/tests/qemu-iotests/007 +++ b/tests/qemu-iotests/007 @@ -43,6 +43,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto generic _supported_os Linux +# refcount_bits must be at least 4 so we can create ten internal snapshots +# (1 bit supports none, 2 bits support two, 4 bits support 14) +_unsupported_imgopts 'refcount_bits=\(1\|2\)[^0-9]' echo echo "creating image" diff --git a/tests/qemu-iotests/007.out b/tests/qemu-iotests/007.out index cf3d17d..5715053 100644 --- a/tests/qemu-iotests/007.out +++ b/tests/qemu-iotests/007.out @@ -1,7 +1,7 @@ QA output created by 007 creating image -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 savevm 1 savevm 2 savevm 3 diff --git a/tests/qemu-iotests/008.out b/tests/qemu-iotests/008.out index 0acbb86..08475c8 100644 --- a/tests/qemu-iotests/008.out +++ b/tests/qemu-iotests/008.out @@ -1,5 +1,5 @@ QA output created by 008 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 == reading whole image == read 134217728/134217728 bytes at offset 0 diff --git a/tests/qemu-iotests/009.out b/tests/qemu-iotests/009.out index 180e451..19e15d0 100644 --- a/tests/qemu-iotests/009.out +++ b/tests/qemu-iotests/009.out @@ -1,7 +1,7 @@ QA output created by 009 creating image -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 creating pattern wrote 4096/4096 bytes at offset 2097152 diff --git a/tests/qemu-iotests/010.out b/tests/qemu-iotests/010.out index cc5c941..1bee037 100644 --- a/tests/qemu-iotests/010.out +++ b/tests/qemu-iotests/010.out @@ -1,7 +1,7 @@ QA output created by 010 creating image -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 creating pattern wrote 4096/4096 bytes at offset 2097152 diff --git a/tests/qemu-iotests/011.out b/tests/qemu-iotests/011.out index dfec17d..65c998e 100644 --- a/tests/qemu-iotests/011.out +++ b/tests/qemu-iotests/011.out @@ -1,7 +1,7 @@ QA output created by 011 creating image -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 overlapping I/O wrote 1048576/1048576 bytes at offset XXX diff --git a/tests/qemu-iotests/012.out b/tests/qemu-iotests/012.out index 5f9b990..c4ffa8f 100644 --- a/tests/qemu-iotests/012.out +++ b/tests/qemu-iotests/012.out @@ -1,5 +1,5 @@ QA output created by 012 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 == mark image read-only diff --git a/tests/qemu-iotests/013.out b/tests/qemu-iotests/013.out index 43a414c..317cdf4 100644 --- a/tests/qemu-iotests/013.out +++ b/tests/qemu-iotests/013.out @@ -1,5 +1,5 @@ QA output created by 013 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 Testing empty image At offset 0: diff --git a/tests/qemu-iotests/014.out b/tests/qemu-iotests/014.out index 4744b4b..f19341c 100644 --- a/tests/qemu-iotests/014.out +++ b/tests/qemu-iotests/014.out @@ -1,5 +1,5 @@ QA output created by 014 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 Testing empty image: test2: With offset 0 === Clusters to be compressed [1] diff --git a/tests/qemu-iotests/015 b/tests/qemu-iotests/015 index 099d757..6f26095 100755 --- a/tests/qemu-iotests/015 +++ b/tests/qemu-iotests/015 @@ -43,6 +43,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto generic _supported_os Linux +# Internal snapshots are (currently) impossible with refcount_bits=1 +_unsupported_imgopts 'refcount_bits=1[^0-9]' echo echo "creating image" diff --git a/tests/qemu-iotests/015.out b/tests/qemu-iotests/015.out index d4b961c..3414afd 100644 --- a/tests/qemu-iotests/015.out +++ b/tests/qemu-iotests/015.out @@ -1,7 +1,7 @@ QA output created by 015 creating image -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=37748736 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=37748736 creating first snapshot wrote 37748736/37748736 bytes at offset 0 36 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/tests/qemu-iotests/016.out b/tests/qemu-iotests/016.out deleted file mode 100644 index 1867978..0000000 --- a/tests/qemu-iotests/016.out +++ /dev/null @@ -1,23 +0,0 @@ -QA output created by 016 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 - -== reading at EOF == -read 512/512 bytes at offset 134217728 -512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) - -== reading far past EOF == -read 512/512 bytes at offset 268435456 -512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) - -== writing at EOF == -wrote 512/512 bytes at offset 134217728 -512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -read 512/512 bytes at offset 134217728 -512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) - -== writing far past EOF == -wrote 512/512 bytes at offset 268435456 -512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -read 512/512 bytes at offset 268435456 -512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -*** done diff --git a/tests/qemu-iotests/017.out b/tests/qemu-iotests/017.out index 75ea614..7c409fc 100644 --- a/tests/qemu-iotests/017.out +++ b/tests/qemu-iotests/017.out @@ -269,7 +269,7 @@ wrote 65536/65536 bytes at offset 4295032832 No errors were found on the image. Creating test image with backing file -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base' Filling test image === IO: pattern 1 diff --git a/tests/qemu-iotests/018.out b/tests/qemu-iotests/018.out index 25e7b95..39a6011 100644 --- a/tests/qemu-iotests/018.out +++ b/tests/qemu-iotests/018.out @@ -269,7 +269,7 @@ wrote 65536/65536 bytes at offset 4295032832 No errors were found on the image. Creating test image with backing file -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base' Filling test image === IO: pattern 1 diff --git a/tests/qemu-iotests/019.out b/tests/qemu-iotests/019.out index f0c6e63..4695b97 100644 --- a/tests/qemu-iotests/019.out +++ b/tests/qemu-iotests/019.out @@ -1,5 +1,5 @@ QA output created by 019 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 Filling base image === IO: pattern 42 @@ -269,7 +269,7 @@ wrote 65536/65536 bytes at offset 4296015872 No errors were found on the image. Creating test image with backing file -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base' Filling test image === IO: pattern 43 diff --git a/tests/qemu-iotests/020.out b/tests/qemu-iotests/020.out index fc9a63c..71aab1c 100644 --- a/tests/qemu-iotests/020.out +++ b/tests/qemu-iotests/020.out @@ -1,5 +1,5 @@ QA output created by 020 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 Filling base image === IO: pattern 0 @@ -269,7 +269,7 @@ wrote 65536/65536 bytes at offset 4295032832 No errors were found on the image. Creating test image with backing file -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base' Filling test image === IO: pattern 1 diff --git a/tests/qemu-iotests/021.out b/tests/qemu-iotests/021.out index ed4448c..8533f8e 100644 --- a/tests/qemu-iotests/021.out +++ b/tests/qemu-iotests/021.out @@ -1,5 +1,5 @@ QA output created by 021 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 == testing writev -P -1 == -1 is not a valid pattern byte diff --git a/tests/qemu-iotests/022.out b/tests/qemu-iotests/022.out index 5a729e0..dbe9cdb 100644 --- a/tests/qemu-iotests/022.out +++ b/tests/qemu-iotests/022.out @@ -1,5 +1,5 @@ QA output created by 022 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 Testing empty image At offset 10485760: diff --git a/tests/qemu-iotests/023.out b/tests/qemu-iotests/023.out index ec32341..d4e9be2 100644 --- a/tests/qemu-iotests/023.out +++ b/tests/qemu-iotests/023.out @@ -1,7 +1,7 @@ QA output created by 023 Creating new image; cluster size: 1024 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 Testing empty image At offset 0: @@ -5664,7 +5664,7 @@ read 3072/3072 bytes at offset 4295491072 No errors were found on the image. Creating another new image -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 More complex patterns test2: With offset 0 @@ -5887,7 +5887,7 @@ read 2048/2048 bytes at offset 4295001088 No errors were found on the image. Creating new image; cluster size: 4096 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 Testing empty image At offset 0: @@ -12270,7 +12270,7 @@ read 12288/12288 bytes at offset 4301256704 No errors were found on the image. Creating another new image -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 More complex patterns test2: With offset 0 @@ -12493,7 +12493,7 @@ read 8192/8192 bytes at offset 4295102464 No errors were found on the image. Creating new image; cluster size: 16384 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 Testing empty image At offset 0: @@ -18876,7 +18876,7 @@ read 49152/49152 bytes at offset 4395622400 No errors were found on the image. Creating another new image -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 More complex patterns test2: With offset 0 @@ -19099,7 +19099,7 @@ read 32768/32768 bytes at offset 4295507968 No errors were found on the image. Creating new image; cluster size: 65536 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 Testing empty image At offset 0: @@ -25482,7 +25482,7 @@ read 196608/196608 bytes at offset 5905547264 No errors were found on the image. Creating another new image -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 More complex patterns test2: With offset 0 diff --git a/tests/qemu-iotests/024.out b/tests/qemu-iotests/024.out index e84b973..521d469 100644 --- a/tests/qemu-iotests/024.out +++ b/tests/qemu-iotests/024.out @@ -1,7 +1,7 @@ QA output created by 024 Creating backing file -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 === IO: pattern 0x11 wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -21,7 +21,7 @@ wrote 65536/65536 bytes at offset 917504 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) Creating new backing file -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 === IO: pattern 0x22 wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -33,7 +33,7 @@ wrote 131072/131072 bytes at offset 786432 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) Creating COW image -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 backing_file='TEST_DIR/t.IMGFMT.base_old' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 backing_file='TEST_DIR/t.IMGFMT.base_old' === IO: pattern 0x33 wrote 262144/262144 bytes at offset 0 256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/tests/qemu-iotests/025.out b/tests/qemu-iotests/025.out index 8c695e6..f13fc28 100644 --- a/tests/qemu-iotests/025.out +++ b/tests/qemu-iotests/025.out @@ -1,7 +1,7 @@ QA output created by 025 === Creating image -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 === Writing whole image === IO: pattern 0xc5 diff --git a/tests/qemu-iotests/026 b/tests/qemu-iotests/026 index df2884b..0fc3244 100755 --- a/tests/qemu-iotests/026 +++ b/tests/qemu-iotests/026 @@ -46,6 +46,13 @@ _supported_proto file _supported_os Linux _default_cache_mode "writethrough" _supported_cache_modes "writethrough" "none" +# The refcount table tests expect a certain minimum width for refcount entries +# (so that the refcount table actually needs to grow); that minimum is 16 bits, +# being the default refcount entry width. +# 32 and 64 bits do not work either, however, due to different leaked cluster +# count on error. +# Thus, the only remaining option is refcount_bits=16. +_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' echo "Errors while writing 128 kB" echo diff --git a/tests/qemu-iotests/026.out b/tests/qemu-iotests/026.out index f7c78e7..5e964fb 100644 --- a/tests/qemu-iotests/026.out +++ b/tests/qemu-iotests/026.out @@ -1,63 +1,71 @@ QA output created by 026 Errors while writing 128 kB -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l1_update; errno: 5; imm: off; once: on; write +Event: l1_update; errno: 5; imm: off; once: on; write write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 5; imm: off; once: on; write -b write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l1_update; errno: 5; imm: off; once: off; write +Event: l1_update; errno: 5; imm: off; once: off; write +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error 1 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 5; imm: off; once: off; write -b +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error 1 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l1_update; errno: 28; imm: off; once: on; write +Event: l1_update; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l1_update; errno: 28; imm: off; once: off; write +Event: l1_update; errno: 28; imm: off; once: off; write +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device 1 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 28; imm: off; once: off; write -b +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device 1 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l2_load; errno: 5; imm: off; once: on; write +Event: l2_load; errno: 5; imm: off; once: on; write wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) write failed: Input/output error read failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_load; errno: 5; imm: off; once: on; write -b wrote 131072/131072 bytes at offset 0 @@ -65,31 +73,39 @@ wrote 131072/131072 bytes at offset 0 write failed: Input/output error read failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l2_load; errno: 5; imm: off; once: off; write +Event: l2_load; errno: 5; imm: off; once: off; write wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error read failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_load; errno: 5; imm: off; once: off; write -b wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error read failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l2_load; errno: 28; imm: off; once: on; write +Event: l2_load; errno: 28; imm: off; once: on; write wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) write failed: No space left on device read failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_load; errno: 28; imm: off; once: on; write -b wrote 131072/131072 bytes at offset 0 @@ -97,437 +113,513 @@ wrote 131072/131072 bytes at offset 0 write failed: No space left on device read failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l2_load; errno: 28; imm: off; once: off; write +Event: l2_load; errno: 28; imm: off; once: off; write wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device read failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_load; errno: 28; imm: off; once: off; write -b wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device read failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l2_update; errno: 5; imm: off; once: on; write +Event: l2_update; errno: 5; imm: off; once: on; write write failed: Input/output error - -127 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 5; imm: off; once: on; write -b write failed: Input/output error +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -127 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 - -Event: l2_update; errno: 5; imm: off; once: off; write +Event: l2_update; errno: 5; imm: off; once: off; write +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error 127 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 5; imm: off; once: off; write -b +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error 127 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l2_update; errno: 28; imm: off; once: on; write +Event: l2_update; errno: 28; imm: off; once: on; write write failed: No space left on device - -127 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 28; imm: off; once: on; write -b write failed: No space left on device +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -127 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 - -Event: l2_update; errno: 28; imm: off; once: off; write +Event: l2_update; errno: 28; imm: off; once: off; write +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device 127 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 28; imm: off; once: off; write -b +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device 127 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l2_alloc.write; errno: 5; imm: off; once: on; write +Event: l2_alloc.write; errno: 5; imm: off; once: on; write write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_alloc.write; errno: 5; imm: off; once: on; write -b write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l2_alloc.write; errno: 5; imm: off; once: off; write +Event: l2_alloc.write; errno: 5; imm: off; once: off; write +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_alloc.write; errno: 5; imm: off; once: off; write -b +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error 1 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l2_alloc.write; errno: 28; imm: off; once: on; write +Event: l2_alloc.write; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_alloc.write; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: l2_alloc.write; errno: 28; imm: off; once: off; write +Event: l2_alloc.write; errno: 28; imm: off; once: off; write +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_alloc.write; errno: 28; imm: off; once: off; write -b +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device 1 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: write_aio; errno: 5; imm: off; once: on; write +Event: write_aio; errno: 5; imm: off; once: on; write write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: write_aio; errno: 5; imm: off; once: on; write -b write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: write_aio; errno: 5; imm: off; once: off; write +Event: write_aio; errno: 5; imm: off; once: off; write +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: write_aio; errno: 5; imm: off; once: off; write -b +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: write_aio; errno: 28; imm: off; once: on; write +Event: write_aio; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: write_aio; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: write_aio; errno: 28; imm: off; once: off; write +Event: write_aio; errno: 28; imm: off; once: off; write +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: write_aio; errno: 28; imm: off; once: off; write -b +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_load; errno: 5; imm: off; once: on; write +Event: refblock_load; errno: 5; imm: off; once: on; write write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_load; errno: 5; imm: off; once: on; write -b write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_load; errno: 5; imm: off; once: off; write +Event: refblock_load; errno: 5; imm: off; once: off; write +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_load; errno: 5; imm: off; once: off; write -b +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_load; errno: 28; imm: off; once: on; write +Event: refblock_load; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_load; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_load; errno: 28; imm: off; once: off; write +Event: refblock_load; errno: 28; imm: off; once: off; write +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_load; errno: 28; imm: off; once: off; write -b +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_update_part; errno: 5; imm: off; once: on; write +Event: refblock_update_part; errno: 5; imm: off; once: on; write write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_update_part; errno: 5; imm: off; once: on; write -b write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_update_part; errno: 5; imm: off; once: off; write +Event: refblock_update_part; errno: 5; imm: off; once: off; write +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_update_part; errno: 5; imm: off; once: off; write -b +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_update_part; errno: 28; imm: off; once: on; write +Event: refblock_update_part; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_update_part; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_update_part; errno: 28; imm: off; once: off; write +Event: refblock_update_part; errno: 28; imm: off; once: off; write +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_update_part; errno: 28; imm: off; once: off; write -b +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc; errno: 5; imm: off; once: on; write +Event: refblock_alloc; errno: 5; imm: off; once: on; write write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc; errno: 5; imm: off; once: on; write -b write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc; errno: 5; imm: off; once: off; write +Event: refblock_alloc; errno: 5; imm: off; once: off; write +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc; errno: 5; imm: off; once: off; write -b +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc; errno: 28; imm: off; once: on; write +Event: refblock_alloc; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc; errno: 28; imm: off; once: off; write +Event: refblock_alloc; errno: 28; imm: off; once: off; write +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc; errno: 28; imm: off; once: off; write -b +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: cluster_alloc; errno: 5; imm: off; once: on; write +Event: cluster_alloc; errno: 5; imm: off; once: on; write write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: cluster_alloc; errno: 5; imm: off; once: on; write -b write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: cluster_alloc; errno: 5; imm: off; once: off; write +Event: cluster_alloc; errno: 5; imm: off; once: off; write +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: cluster_alloc; errno: 5; imm: off; once: off; write -b +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: cluster_alloc; errno: 28; imm: off; once: on; write +Event: cluster_alloc; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: cluster_alloc; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: cluster_alloc; errno: 28; imm: off; once: off; write +Event: cluster_alloc; errno: 28; imm: off; once: off; write +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: cluster_alloc; errno: 28; imm: off; once: off; write -b +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. === Refcout table growth tests === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.hookup; errno: 28; imm: off; once: on; write +Event: refblock_alloc.hookup; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.hookup; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.hookup; errno: 28; imm: off; once: off; write +Event: refblock_alloc.hookup; errno: 28; imm: off; once: off; write +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device 55 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.hookup; errno: 28; imm: off; once: off; write -b +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device 251 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.write; errno: 28; imm: off; once: on; write +Event: refblock_alloc.write; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.write; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.write; errno: 28; imm: off; once: off; write +Event: refblock_alloc.write; errno: 28; imm: off; once: off; write +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.write; errno: 28; imm: off; once: off; write -b +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.write_blocks; errno: 28; imm: off; once: on; write +Event: refblock_alloc.write_blocks; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.write_blocks; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.write_blocks; errno: 28; imm: off; once: off; write +Event: refblock_alloc.write_blocks; errno: 28; imm: off; once: off; write +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device 11 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.write_blocks; errno: 28; imm: off; once: off; write -b +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device 23 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.write_table; errno: 28; imm: off; once: on; write +Event: refblock_alloc.write_table; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.write_table; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.write_table; errno: 28; imm: off; once: off; write +Event: refblock_alloc.write_table; errno: 28; imm: off; once: off; write +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device 11 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.write_table; errno: 28; imm: off; once: off; write -b +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device 23 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.switch_table; errno: 28; imm: off; once: on; write +Event: refblock_alloc.switch_table; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.switch_table; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 -Event: refblock_alloc.switch_table; errno: 28; imm: off; once: off; write +Event: refblock_alloc.switch_table; errno: 28; imm: off; once: off; write +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device 11 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.switch_table; errno: 28; imm: off; once: off; write -b +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device 23 leaked clusters were found on the image. @@ -535,66 +627,78 @@ This means waste of disk space, but no harm to data. === L1 growth tests === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.alloc_table; errno: 5; imm: off; once: on write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.alloc_table; errno: 5; imm: off; once: off +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.alloc_table; errno: 28; imm: off; once: on write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.alloc_table; errno: 28; imm: off; once: off +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.write_table; errno: 5; imm: off; once: on write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.write_table; errno: 5; imm: off; once: off +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.write_table; errno: 28; imm: off; once: on write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.write_table; errno: 28; imm: off; once: off +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.activate_table; errno: 5; imm: off; once: on write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.activate_table; errno: 5; imm: off; once: off +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error write failed: Input/output error 96 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.activate_table; errno: 28; imm: off; once: on write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.activate_table; errno: 28; imm: off; once: off +Failed to flush the L2 table cache: No space left on device +Failed to flush the refcount block cache: No space left on device write failed: No space left on device 96 leaked clusters were found on the image. diff --git a/tests/qemu-iotests/027.out b/tests/qemu-iotests/027.out index 4fcb416..c7bca71 100644 --- a/tests/qemu-iotests/027.out +++ b/tests/qemu-iotests/027.out @@ -1,5 +1,5 @@ QA output created by 027 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 == writing first cluster to populate metadata == wrote 65536/65536 bytes at offset 65536 diff --git a/tests/qemu-iotests/028.out b/tests/qemu-iotests/028.out index e8d0245..5db167c 100644 --- a/tests/qemu-iotests/028.out +++ b/tests/qemu-iotests/028.out @@ -1,5 +1,5 @@ QA output created by 028 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=3221227008 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=3221227008 Filling base image === IO: pattern 195 @@ -70,7 +70,7 @@ wrote 512/512 bytes at offset 3221225984 No errors were found on the image. Creating test image with backing file -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294968832 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294968832 backing_file='TEST_DIR/t.IMGFMT.base' Filling test image === IO: pattern 196 @@ -469,7 +469,7 @@ No errors were found on the image. block-backup Formatting 'TEST_DIR/t.IMGFMT.copy', fmt=IMGFMT size=4294968832 backing_file='TEST_DIR/t.IMGFMT.base' backing_fmt='IMGFMT' -(qemu) +(qemu) (qemu) iininfinfoinfo info binfo blinfo bloinfo blocinfo blockinfo block-info block-jinfo block-joinfo block-jobinfo block-jobs Type backup, device disk: Completed 0 of 4294968832 bytes, speed limit 0 bytes/s iininfinfoinfo info binfo blinfo bloinfo blocinfo blockinfo block-info block-jinfo block-joinfo block-jobinfo block-jobs diff --git a/tests/qemu-iotests/029 b/tests/qemu-iotests/029 index fa46ace..b9cd826 100755 --- a/tests/qemu-iotests/029 +++ b/tests/qemu-iotests/029 @@ -44,6 +44,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto generic _supported_os Linux +# Internal snapshots are (currently) impossible with refcount_bits=1 +_unsupported_imgopts 'refcount_bits=1[^0-9]' offset_size=24 offset_l1_size=36 diff --git a/tests/qemu-iotests/029.out b/tests/qemu-iotests/029.out index ce0e64d..5bc93e0 100644 --- a/tests/qemu-iotests/029.out +++ b/tests/qemu-iotests/029.out @@ -3,18 +3,18 @@ QA output created by 029 Test loading internal snapshots where the L1 table of the snapshot is smaller than the current L1 table. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=16777216 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=16777216 wrote 4194304/4194304 bytes at offset 0 4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) No errors were found on the image. Try using a huge VM state -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 4096/4096 bytes at offset 1099511627776 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 4096/4096 bytes at offset 1099511627776 @@ -23,5 +23,5 @@ No errors were found on the image. qcow2_snapshot_load_tmp() should take the L1 size from the snapshot -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 *** done diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030 index 8ce2373..952a524 100755 --- a/tests/qemu-iotests/030 +++ b/tests/qemu-iotests/030 @@ -34,7 +34,7 @@ class TestSingleDrive(iotests.QMPTestCase): iotests.create_image(backing_img, TestSingleDrive.image_len) qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img) qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img) - qemu_io('-c', 'write -P 0x1 0 512', backing_img) + qemu_io('-f', 'raw', '-c', 'write -P 0x1 0 512', backing_img) self.vm = iotests.VM().add_drive("blkdebug::" + test_img) self.vm.launch() @@ -55,8 +55,8 @@ class TestSingleDrive(iotests.QMPTestCase): self.assert_no_active_block_jobs() self.vm.shutdown() - self.assertEqual(qemu_io('-c', 'map', backing_img), - qemu_io('-c', 'map', test_img), + self.assertEqual(qemu_io('-f', 'raw', '-c', 'map', backing_img), + qemu_io('-f', iotests.imgfmt, '-c', 'map', test_img), 'image file map does not match backing file after streaming') def test_stream_pause(self): @@ -86,8 +86,8 @@ class TestSingleDrive(iotests.QMPTestCase): self.assert_no_active_block_jobs() self.vm.shutdown() - self.assertEqual(qemu_io('-c', 'map', backing_img), - qemu_io('-c', 'map', test_img), + self.assertEqual(qemu_io('-f', 'raw', '-c', 'map', backing_img), + qemu_io('-f', iotests.imgfmt, '-c', 'map', test_img), 'image file map does not match backing file after streaming') def test_stream_partial(self): @@ -101,8 +101,8 @@ class TestSingleDrive(iotests.QMPTestCase): self.assert_no_active_block_jobs() self.vm.shutdown() - self.assertEqual(qemu_io('-c', 'map', mid_img), - qemu_io('-c', 'map', test_img), + self.assertEqual(qemu_io('-f', iotests.imgfmt, '-c', 'map', mid_img), + qemu_io('-f', iotests.imgfmt, '-c', 'map', test_img), 'image file map does not match backing file after streaming') def test_device_not_found(self): @@ -359,9 +359,9 @@ class TestStreamStop(iotests.QMPTestCase): def setUp(self): qemu_img('create', backing_img, str(TestStreamStop.image_len)) - qemu_io('-c', 'write -P 0x1 0 32M', backing_img) + qemu_io('-f', 'raw', '-c', 'write -P 0x1 0 32M', backing_img) qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) - qemu_io('-c', 'write -P 0x1 32M 32M', test_img) + qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x1 32M 32M', test_img) self.vm = iotests.VM().add_drive("blkdebug::" + test_img) self.vm.launch() @@ -388,9 +388,9 @@ class TestSetSpeed(iotests.QMPTestCase): def setUp(self): qemu_img('create', backing_img, str(TestSetSpeed.image_len)) - qemu_io('-c', 'write -P 0x1 0 32M', backing_img) + qemu_io('-f', 'raw', '-c', 'write -P 0x1 0 32M', backing_img) qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) - qemu_io('-c', 'write -P 0x1 32M 32M', test_img) + qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x1 32M 32M', test_img) self.vm = iotests.VM().add_drive('blkdebug::' + test_img) self.vm.launch() diff --git a/tests/qemu-iotests/031.out b/tests/qemu-iotests/031.out index a943344..fce3ce0 100644 --- a/tests/qemu-iotests/031.out +++ b/tests/qemu-iotests/031.out @@ -4,7 +4,7 @@ QA output created by 031 === Create image with unknown header extension === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 magic 0x514649fb version 2 backing_file_offset 0x0 @@ -105,7 +105,7 @@ data 'This is a test header extension' === Create image with unknown header extension === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 magic 0x514649fb version 3 backing_file_offset 0x0 diff --git a/tests/qemu-iotests/032.out b/tests/qemu-iotests/032.out index ca20de6..ce27d5d 100644 --- a/tests/qemu-iotests/032.out +++ b/tests/qemu-iotests/032.out @@ -2,7 +2,7 @@ QA output created by 032 === Prepare image === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 65536/65536 bytes at offset 131072 diff --git a/tests/qemu-iotests/033 b/tests/qemu-iotests/033 index ea3351c..a61d8ce 100755 --- a/tests/qemu-iotests/033 +++ b/tests/qemu-iotests/033 @@ -46,26 +46,52 @@ _supported_os Linux size=128M _make_test_img $size -echo -echo "== preparing image ==" -$QEMU_IO -c "write -P 0xa 0x200 0x400" "$TEST_IMG" | _filter_qemu_io -$QEMU_IO -c "write -P 0xa 0x20000 0x600" "$TEST_IMG" | _filter_qemu_io -$QEMU_IO -c "write -z 0x400 0x20000" "$TEST_IMG" | _filter_qemu_io - -echo -echo "== verifying patterns (1) ==" -$QEMU_IO -c "read -P 0xa 0x200 0x200" "$TEST_IMG" | _filter_qemu_io -$QEMU_IO -c "read -P 0x0 0x400 0x20000" "$TEST_IMG" | _filter_qemu_io -$QEMU_IO -c "read -P 0xa 0x20400 0x200" "$TEST_IMG" | _filter_qemu_io - -echo -echo "== rewriting zeroes ==" -$QEMU_IO -c "write -P 0xb 0x10000 0x10000" "$TEST_IMG" | _filter_qemu_io -$QEMU_IO -c "write -z 0x10000 0x10000" "$TEST_IMG" | _filter_qemu_io - -echo -echo "== verifying patterns (2) ==" -$QEMU_IO -c "read -P 0x0 0x400 0x20000" "$TEST_IMG" | _filter_qemu_io +do_test() +{ + local align=$1 + local iocmd=$2 + local img=$3 + { + echo "open -o driver=$IMGFMT,file.align=$align blkdebug::$img" + echo $iocmd + } | $QEMU_IO +} + +for align in 512 4k; do + echo + echo "== preparing image ==" + do_test $align "write -P 0xa 0x200 0x400" "$TEST_IMG" | _filter_qemu_io + do_test $align "write -P 0xa 0x20000 0x600" "$TEST_IMG" | _filter_qemu_io + do_test $align "write -z 0x400 0x20000" "$TEST_IMG" | _filter_qemu_io + + echo + echo "== verifying patterns (1) ==" + do_test $align "read -P 0xa 0x200 0x200" "$TEST_IMG" | _filter_qemu_io + do_test $align "read -P 0x0 0x400 0x20000" "$TEST_IMG" | _filter_qemu_io + do_test $align "read -P 0xa 0x20400 0x200" "$TEST_IMG" | _filter_qemu_io + + echo + echo "== rewriting zeroes ==" + do_test $align "write -P 0xb 0x10000 0x10000" "$TEST_IMG" | _filter_qemu_io + do_test $align "write -z 0x10000 0x10000" "$TEST_IMG" | _filter_qemu_io + + echo + echo "== verifying patterns (2) ==" + do_test $align "read -P 0x0 0x400 0x20000" "$TEST_IMG" | _filter_qemu_io + + echo + echo "== rewriting unaligned zeroes ==" + do_test $align "write -P 0xb 0x0 0x1000" "$TEST_IMG" | _filter_qemu_io + do_test $align "write -z 0x200 0x200" "$TEST_IMG" | _filter_qemu_io + + echo + echo "== verifying patterns (3) ==" + do_test $align "read -P 0xb 0x0 0x200" "$TEST_IMG" | _filter_qemu_io + do_test $align "read -P 0x0 0x200 0x200" "$TEST_IMG" | _filter_qemu_io + do_test $align "read -P 0xb 0x400 0xc00" "$TEST_IMG" | _filter_qemu_io + + echo +done # success, all done echo "*** done" diff --git a/tests/qemu-iotests/033.out b/tests/qemu-iotests/033.out index 2fe74df..c3d18aa 100644 --- a/tests/qemu-iotests/033.out +++ b/tests/qemu-iotests/033.out @@ -1,5 +1,5 @@ QA output created by 033 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 == preparing image == wrote 1024/1024 bytes at offset 512 @@ -26,4 +26,60 @@ wrote 65536/65536 bytes at offset 65536 == verifying patterns (2) == read 131072/131072 bytes at offset 1024 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== rewriting unaligned zeroes == +wrote 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 512/512 bytes at offset 512 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== verifying patterns (3) == +read 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 512/512 bytes at offset 512 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 3072/3072 bytes at offset 1024 +3 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + + +== preparing image == +wrote 1024/1024 bytes at offset 512 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 1536/1536 bytes at offset 131072 +1.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 131072/131072 bytes at offset 1024 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== verifying patterns (1) == +read 512/512 bytes at offset 512 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 131072/131072 bytes at offset 1024 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 512/512 bytes at offset 132096 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== rewriting zeroes == +wrote 65536/65536 bytes at offset 65536 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 65536 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== verifying patterns (2) == +read 131072/131072 bytes at offset 1024 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== rewriting unaligned zeroes == +wrote 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 512/512 bytes at offset 512 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== verifying patterns (3) == +read 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 512/512 bytes at offset 512 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 3072/3072 bytes at offset 1024 +3 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + *** done diff --git a/tests/qemu-iotests/034.out b/tests/qemu-iotests/034.out index e82dae5..d12daf2 100644 --- a/tests/qemu-iotests/034.out +++ b/tests/qemu-iotests/034.out @@ -1,10 +1,10 @@ QA output created by 034 == creating backing file for COW tests == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 1048576/1048576 bytes at offset 0 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base' == zero write with backing file == wrote 196608/196608 bytes at offset 65536 diff --git a/tests/qemu-iotests/035.out b/tests/qemu-iotests/035.out index cde21d8..dd3bd20 100644 --- a/tests/qemu-iotests/035.out +++ b/tests/qemu-iotests/035.out @@ -1,7 +1,7 @@ QA output created by 035 creating image -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 wrote 512/512 bytes at offset XXX 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 512/512 bytes at offset XXX diff --git a/tests/qemu-iotests/036.out b/tests/qemu-iotests/036.out index 720bd89..5616e37 100644 --- a/tests/qemu-iotests/036.out +++ b/tests/qemu-iotests/036.out @@ -2,7 +2,7 @@ QA output created by 036 === Image with unknown incompatible feature bit === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 magic 0x514649fb version 3 backing_file_offset 0x0 @@ -27,7 +27,7 @@ qemu-img: Could not open 'TEST_DIR/t.IMGFMT': 'image' uses a IMGFMT feature whic === Image with multiple incompatible feature bits === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 qemu-img: Could not open 'TEST_DIR/t.IMGFMT': 'image' uses a IMGFMT feature which is not supported by this qemu version: Unknown incompatible feature: e000000000000000 qemu-img: Could not open 'TEST_DIR/t.IMGFMT': 'image' uses a IMGFMT feature which is not supported by this qemu version: Test feature, Unknown incompatible feature: 6000000000000000 qemu-img: Could not open 'TEST_DIR/t.IMGFMT': 'image' uses a IMGFMT feature which is not supported by this qemu version: Test feature, Unknown incompatible feature: c000000000000000 @@ -36,7 +36,7 @@ qemu-img: Could not open 'TEST_DIR/t.IMGFMT': 'image' uses a IMGFMT feature whic qemu-img: Could not open 'TEST_DIR/t.IMGFMT': 'image' uses a IMGFMT feature which is not supported by this qemu version: test2, Unknown incompatible feature: a000000000000000 === Create image with unknown autoclear feature bit === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 magic 0x514649fb version 3 backing_file_offset 0x0 diff --git a/tests/qemu-iotests/037.out b/tests/qemu-iotests/037.out index 4eb84ed..dc40a02 100644 --- a/tests/qemu-iotests/037.out +++ b/tests/qemu-iotests/037.out @@ -1,7 +1,7 @@ QA output created by 037 == creating backing file for COW tests == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 512/512 bytes at offset 512 @@ -514,7 +514,7 @@ wrote 512/512 bytes at offset 130048 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 512/512 bytes at offset 130560 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base' == COW in a single cluster == wrote 2048/2048 bytes at offset 0 diff --git a/tests/qemu-iotests/038.out b/tests/qemu-iotests/038.out index a71c3fa..e1a7e94 100644 --- a/tests/qemu-iotests/038.out +++ b/tests/qemu-iotests/038.out @@ -1,7 +1,7 @@ QA output created by 038 == creating backing file for COW tests == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 65536/65536 bytes at offset 65536 @@ -514,7 +514,7 @@ wrote 65536/65536 bytes at offset 16646144 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 65536/65536 bytes at offset 16711680 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base' == Some concurrent requests touching the same cluster == wrote 65536/65536 bytes at offset XXX diff --git a/tests/qemu-iotests/039 b/tests/qemu-iotests/039 index 84c9167..859705f 100755 --- a/tests/qemu-iotests/039 +++ b/tests/qemu-iotests/039 @@ -47,9 +47,11 @@ _supported_os Linux _default_cache_mode "writethrough" _supported_cache_modes "writethrough" -_no_dump_exec() +_subshell_exec() { - (ulimit -c 0; exec "$@") + # Executing crashing commands in a subshell prevents information like the + # "Killed" line from being lost + (exec "$@") } size=128M @@ -72,7 +74,9 @@ echo "== Creating a dirty image file ==" IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img $size -_no_dump_exec $QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" 2>&1 | _filter_qemu_io +_subshell_exec $QEMU_IO -c "write -P 0x5a 0 512" \ + -c "sigraise $(kill -l KILL)" "$TEST_IMG" 2>&1 \ + | _filter_qemu_io # The dirty bit must be set $PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features @@ -105,7 +109,9 @@ echo "== Opening a dirty image read/write should repair it ==" IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img $size -_no_dump_exec $QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" 2>&1 | _filter_qemu_io +_subshell_exec $QEMU_IO -c "write -P 0x5a 0 512" \ + -c "sigraise $(kill -l KILL)" "$TEST_IMG" 2>&1 \ + | _filter_qemu_io # The dirty bit must be set $PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features @@ -121,7 +127,9 @@ echo "== Creating an image file with lazy_refcounts=off ==" IMGOPTS="compat=1.1,lazy_refcounts=off" _make_test_img $size -_no_dump_exec $QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" 2>&1 | _filter_qemu_io +_subshell_exec $QEMU_IO -c "write -P 0x5a 0 512" \ + -c "sigraise $(kill -l KILL)" "$TEST_IMG" 2>&1 \ + | _filter_qemu_io # The dirty bit must not be set since lazy_refcounts=off $PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features diff --git a/tests/qemu-iotests/039.out b/tests/qemu-iotests/039.out index 0adf153..d09751f 100644 --- a/tests/qemu-iotests/039.out +++ b/tests/qemu-iotests/039.out @@ -1,17 +1,17 @@ QA output created by 039 == Checking that image is clean on shutdown == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) incompatible_features 0x0 No errors were found on the image. == Creating a dirty image file == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -./039: Aborted ( ulimit -c 0; exec "$@" ) +./039: Killed ( exec "$@" ) incompatible_features 0x1 ERROR cluster 5 refcount=0 reference=1 ERROR OFLAG_COPIED data cluster: l2_entry=8000000000050000 refcount=0 @@ -43,10 +43,10 @@ read 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) == Opening a dirty image read/write should repair it == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -./039: Aborted ( ulimit -c 0; exec "$@" ) +./039: Killed ( exec "$@" ) incompatible_features 0x1 ERROR cluster 5 refcount=0 reference=1 Rebuilding refcount structure @@ -57,16 +57,16 @@ wrote 512/512 bytes at offset 0 incompatible_features 0x0 == Creating an image file with lazy_refcounts=off == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -./039: Aborted ( ulimit -c 0; exec "$@" ) +./039: Killed ( exec "$@" ) incompatible_features 0x0 No errors were found on the image. == Committing to a backing file with lazy_refcounts=on == -Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR/t.IMGFMT.base' wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) Image committed. diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040 index 2b432ad..ea2f98e 100755 --- a/tests/qemu-iotests/040 +++ b/tests/qemu-iotests/040 @@ -76,8 +76,8 @@ class TestSingleDrive(ImageCommitTestCase): iotests.create_image(backing_img, self.image_len) qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img) qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img) - qemu_io('-c', 'write -P 0xab 0 524288', backing_img) - qemu_io('-c', 'write -P 0xef 524288 524288', mid_img) + qemu_io('-f', 'raw', '-c', 'write -P 0xab 0 524288', backing_img) + qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xef 524288 524288', mid_img) self.vm = iotests.VM().add_drive(test_img) self.vm.launch() @@ -89,8 +89,8 @@ class TestSingleDrive(ImageCommitTestCase): def test_commit(self): self.run_commit_test(mid_img, backing_img) - self.assertEqual(-1, qemu_io('-c', 'read -P 0xab 0 524288', backing_img).find("verification failed")) - self.assertEqual(-1, qemu_io('-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed")) + self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img).find("verification failed")) + self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed")) def test_device_not_found(self): result = self.vm.qmp('block-commit', device='nonexistent', top='%s' % mid_img) @@ -116,13 +116,13 @@ class TestSingleDrive(ImageCommitTestCase): def test_top_is_active(self): self.run_commit_test(test_img, backing_img, need_ready=True) - self.assertEqual(-1, qemu_io('-c', 'read -P 0xab 0 524288', backing_img).find("verification failed")) - self.assertEqual(-1, qemu_io('-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed")) + self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img).find("verification failed")) + self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed")) def test_top_is_default_active(self): self.run_default_commit_test() - self.assertEqual(-1, qemu_io('-c', 'read -P 0xab 0 524288', backing_img).find("verification failed")) - self.assertEqual(-1, qemu_io('-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed")) + self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img).find("verification failed")) + self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed")) def test_top_and_base_reversed(self): self.assert_no_active_block_jobs() @@ -159,8 +159,8 @@ class TestRelativePaths(ImageCommitTestCase): qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % self.mid_img_abs, self.test_img) qemu_img('rebase', '-u', '-b', self.backing_img, self.mid_img_abs) qemu_img('rebase', '-u', '-b', self.mid_img, self.test_img) - qemu_io('-c', 'write -P 0xab 0 524288', self.backing_img_abs) - qemu_io('-c', 'write -P 0xef 524288 524288', self.mid_img_abs) + qemu_io('-f', 'raw', '-c', 'write -P 0xab 0 524288', self.backing_img_abs) + qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xef 524288 524288', self.mid_img_abs) self.vm = iotests.VM().add_drive(self.test_img) self.vm.launch() @@ -179,8 +179,8 @@ class TestRelativePaths(ImageCommitTestCase): def test_commit(self): self.run_commit_test(self.mid_img, self.backing_img) - self.assertEqual(-1, qemu_io('-c', 'read -P 0xab 0 524288', self.backing_img_abs).find("verification failed")) - self.assertEqual(-1, qemu_io('-c', 'read -P 0xef 524288 524288', self.backing_img_abs).find("verification failed")) + self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', self.backing_img_abs).find("verification failed")) + self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', self.backing_img_abs).find("verification failed")) def test_device_not_found(self): result = self.vm.qmp('block-commit', device='nonexistent', top='%s' % self.mid_img) @@ -206,8 +206,8 @@ class TestRelativePaths(ImageCommitTestCase): def test_top_is_active(self): self.run_commit_test(self.test_img, self.backing_img) - self.assertEqual(-1, qemu_io('-c', 'read -P 0xab 0 524288', self.backing_img_abs).find("verification failed")) - self.assertEqual(-1, qemu_io('-c', 'read -P 0xef 524288 524288', self.backing_img_abs).find("verification failed")) + self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', self.backing_img_abs).find("verification failed")) + self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', self.backing_img_abs).find("verification failed")) def test_top_and_base_reversed(self): self.assert_no_active_block_jobs() @@ -223,8 +223,8 @@ class TestSetSpeed(ImageCommitTestCase): qemu_img('create', backing_img, str(TestSetSpeed.image_len)) qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img) qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img) - qemu_io('-c', 'write -P 0x1 0 512', test_img) - qemu_io('-c', 'write -P 0xef 524288 524288', mid_img) + qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x1 0 512', test_img) + qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xef 524288 524288', mid_img) self.vm = iotests.VM().add_drive(test_img) self.vm.launch() diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 index 59a8f73..3d46ed7 100755 --- a/tests/qemu-iotests/041 +++ b/tests/qemu-iotests/041 @@ -34,38 +34,8 @@ quorum_img3 = os.path.join(iotests.test_dir, 'quorum3.img') quorum_repair_img = os.path.join(iotests.test_dir, 'quorum_repair.img') quorum_snapshot_file = os.path.join(iotests.test_dir, 'quorum_snapshot.img') -class ImageMirroringTestCase(iotests.QMPTestCase): - '''Abstract base class for image mirroring test cases''' - def wait_ready(self, drive='drive0'): - '''Wait until a block job BLOCK_JOB_READY event''' - ready = False - while not ready: - for event in self.vm.get_qmp_events(wait=True): - if event['event'] == 'BLOCK_JOB_READY': - self.assert_qmp(event, 'data/type', 'mirror') - self.assert_qmp(event, 'data/device', drive) - ready = True - - def wait_ready_and_cancel(self, drive='drive0'): - self.wait_ready(drive=drive) - event = self.cancel_and_wait(drive=drive) - self.assertEquals(event['event'], 'BLOCK_JOB_COMPLETED') - self.assert_qmp(event, 'data/type', 'mirror') - self.assert_qmp(event, 'data/offset', event['data']['len']) - - def complete_and_wait(self, drive='drive0', wait_ready=True): - '''Complete a block job and wait for it to finish''' - if wait_ready: - self.wait_ready(drive=drive) - - result = self.vm.qmp('block-job-complete', device=drive) - self.assert_qmp(result, 'return', {}) - - event = self.wait_until_completed(drive=drive) - self.assert_qmp(event, 'data/type', 'mirror') - -class TestSingleDrive(ImageMirroringTestCase): +class TestSingleDrive(iotests.QMPTestCase): image_len = 1 * 1024 * 1024 # MB def setUp(self): @@ -221,17 +191,9 @@ class TestSingleDriveUnalignedLength(TestSingleDrive): test_small_buffer2 = None test_large_cluster = None -class TestMirrorNoBacking(ImageMirroringTestCase): +class TestMirrorNoBacking(iotests.QMPTestCase): image_len = 2 * 1024 * 1024 # MB - def complete_and_wait(self, drive='drive0', wait_ready=True): - iotests.create_image(target_backing_img, TestMirrorNoBacking.image_len) - return ImageMirroringTestCase.complete_and_wait(self, drive, wait_ready) - - def compare_images(self, img1, img2): - iotests.create_image(target_backing_img, TestMirrorNoBacking.image_len) - return iotests.compare_images(img1, img2) - def setUp(self): iotests.create_image(backing_img, TestMirrorNoBacking.image_len) qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) @@ -242,7 +204,10 @@ class TestMirrorNoBacking(ImageMirroringTestCase): self.vm.shutdown() os.remove(test_img) os.remove(backing_img) - os.remove(target_backing_img) + try: + os.remove(target_backing_img) + except: + pass os.remove(target_img) def test_complete(self): @@ -257,7 +222,7 @@ class TestMirrorNoBacking(ImageMirroringTestCase): result = self.vm.qmp('query-block') self.assert_qmp(result, 'return[0]/inserted/file', target_img) self.vm.shutdown() - self.assertTrue(self.compare_images(test_img, target_img), + self.assertTrue(iotests.compare_images(test_img, target_img), 'target image does not match source after mirroring') def test_cancel(self): @@ -272,7 +237,7 @@ class TestMirrorNoBacking(ImageMirroringTestCase): result = self.vm.qmp('query-block') self.assert_qmp(result, 'return[0]/inserted/file', test_img) self.vm.shutdown() - self.assertTrue(self.compare_images(test_img, target_img), + self.assertTrue(iotests.compare_images(test_img, target_img), 'target image does not match source after mirroring') def test_large_cluster(self): @@ -283,7 +248,6 @@ class TestMirrorNoBacking(ImageMirroringTestCase): %(TestMirrorNoBacking.image_len), target_backing_img) qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s' % (TestMirrorNoBacking.image_len, target_backing_img), target_img) - os.remove(target_backing_img) result = self.vm.qmp('drive-mirror', device='drive0', sync='full', mode='existing', target=target_img) @@ -293,10 +257,10 @@ class TestMirrorNoBacking(ImageMirroringTestCase): result = self.vm.qmp('query-block') self.assert_qmp(result, 'return[0]/inserted/file', target_img) self.vm.shutdown() - self.assertTrue(self.compare_images(test_img, target_img), + self.assertTrue(iotests.compare_images(test_img, target_img), 'target image does not match source after mirroring') -class TestMirrorResized(ImageMirroringTestCase): +class TestMirrorResized(iotests.QMPTestCase): backing_len = 1 * 1024 * 1024 # MB image_len = 2 * 1024 * 1024 # MB @@ -344,7 +308,7 @@ class TestMirrorResized(ImageMirroringTestCase): self.assertTrue(iotests.compare_images(test_img, target_img), 'target image does not match source after mirroring') -class TestReadErrors(ImageMirroringTestCase): +class TestReadErrors(iotests.QMPTestCase): image_len = 2 * 1024 * 1024 # MB # this should be a multiple of twice the default granularity @@ -498,7 +462,7 @@ new_state = "1" self.assert_no_active_block_jobs() self.vm.shutdown() -class TestWriteErrors(ImageMirroringTestCase): +class TestWriteErrors(iotests.QMPTestCase): image_len = 2 * 1024 * 1024 # MB # this should be a multiple of twice the default granularity @@ -624,7 +588,7 @@ new_state = "1" self.assert_no_active_block_jobs() self.vm.shutdown() -class TestSetSpeed(ImageMirroringTestCase): +class TestSetSpeed(iotests.QMPTestCase): image_len = 80 * 1024 * 1024 # MB def setUp(self): @@ -690,7 +654,7 @@ class TestSetSpeed(ImageMirroringTestCase): self.wait_ready_and_cancel() -class TestUnbackedSource(ImageMirroringTestCase): +class TestUnbackedSource(iotests.QMPTestCase): image_len = 2 * 1024 * 1024 # MB def setUp(self): @@ -731,7 +695,7 @@ class TestUnbackedSource(ImageMirroringTestCase): self.complete_and_wait() self.assert_no_active_block_jobs() -class TestRepairQuorum(ImageMirroringTestCase): +class TestRepairQuorum(iotests.QMPTestCase): """ This class test quorum file repair using drive-mirror. It's mostly a fork of TestSingleDrive """ image_len = 1 * 1024 * 1024 # MB diff --git a/tests/qemu-iotests/042.out b/tests/qemu-iotests/042.out index dc80f4b..8ab1520 100644 --- a/tests/qemu-iotests/042.out +++ b/tests/qemu-iotests/042.out @@ -1,7 +1,7 @@ QA output created by 042 == Creating zero size image == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=0 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=0 No errors were found on the image. == Converting the image == diff --git a/tests/qemu-iotests/043.out b/tests/qemu-iotests/043.out index ad23337..012cc00 100644 --- a/tests/qemu-iotests/043.out +++ b/tests/qemu-iotests/043.out @@ -1,23 +1,23 @@ QA output created by 043 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 == backing file references self == qemu-img: Backing file 'TEST_DIR/t.IMGFMT' creates an infinite loop. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR/t.IMGFMT.base' == parent references self == qemu-img: Backing file 'TEST_DIR/t.IMGFMT' creates an infinite loop. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR/t.IMGFMT.1.base' -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR/t.IMGFMT.2.base' -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR/t.IMGFMT.3.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR/t.IMGFMT.1.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR/t.IMGFMT.2.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR/t.IMGFMT.3.base' == ancestor references another ancestor == qemu-img: Backing file 'TEST_DIR/t.IMGFMT.2.base' creates an infinite loop. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR/t.IMGFMT.1.base' -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR/t.IMGFMT.2.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR/t.IMGFMT.1.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR/t.IMGFMT.2.base' == finite chain of length 3 (human) == image: TEST_DIR/t.IMGFMT @@ -40,26 +40,26 @@ cluster_size: 65536 == finite chain of length 3 (json) == [ { - "virtual-size": 134217728, - "filename": "TEST_DIR/t.IMGFMT", - "cluster-size": 65536, - "format": "IMGFMT", - "backing-filename": "TEST_DIR/t.IMGFMT.2.base", + "virtual-size": 134217728, + "filename": "TEST_DIR/t.IMGFMT", + "cluster-size": 65536, + "format": "IMGFMT", + "backing-filename": "TEST_DIR/t.IMGFMT.2.base", "dirty-flag": false - }, + }, { - "virtual-size": 134217728, - "filename": "TEST_DIR/t.IMGFMT.2.base", - "cluster-size": 65536, - "format": "IMGFMT", - "backing-filename": "TEST_DIR/t.IMGFMT.1.base", + "virtual-size": 134217728, + "filename": "TEST_DIR/t.IMGFMT.2.base", + "cluster-size": 65536, + "format": "IMGFMT", + "backing-filename": "TEST_DIR/t.IMGFMT.1.base", "dirty-flag": false - }, + }, { - "virtual-size": 134217728, - "filename": "TEST_DIR/t.IMGFMT.1.base", - "cluster-size": 65536, - "format": "IMGFMT", + "virtual-size": 134217728, + "filename": "TEST_DIR/t.IMGFMT.1.base", + "cluster-size": 65536, + "format": "IMGFMT", "dirty-flag": false } ] diff --git a/tests/qemu-iotests/046.out b/tests/qemu-iotests/046.out index 65d584b..9d18af5 100644 --- a/tests/qemu-iotests/046.out +++ b/tests/qemu-iotests/046.out @@ -1,7 +1,7 @@ QA output created by 046 == creating backing file for COW tests == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 65536/65536 bytes at offset 65536 @@ -66,7 +66,7 @@ wrote 65536/65536 bytes at offset 1966080 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 65536/65536 bytes at offset 2031616 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base' == Some concurrent requests touching the same cluster == blkdebug: Suspended request 'A' diff --git a/tests/qemu-iotests/047.out b/tests/qemu-iotests/047.out index 959f2af..856a2bc 100644 --- a/tests/qemu-iotests/047.out +++ b/tests/qemu-iotests/047.out @@ -1,5 +1,5 @@ QA output created by 047 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 327680/327680 bytes at offset 0 320 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 131072/131072 bytes at offset 327680 diff --git a/tests/qemu-iotests/048 b/tests/qemu-iotests/048 index 65da46d..e1eeac2 100755 --- a/tests/qemu-iotests/048 +++ b/tests/qemu-iotests/048 @@ -64,7 +64,7 @@ _compare _compare -q # Compare images with different size -$QEMU_IMG resize "$TEST_IMG" +512M +$QEMU_IMG resize -f $IMGFMT "$TEST_IMG" +512M _compare _compare -s diff --git a/tests/qemu-iotests/048.out b/tests/qemu-iotests/048.out index c0f380d..57100dc 100644 --- a/tests/qemu-iotests/048.out +++ b/tests/qemu-iotests/048.out @@ -1,5 +1,5 @@ QA output created by 048 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 === IO: pattern 45 wrote 4096/4096 bytes at offset 524288 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -28,7 +28,7 @@ wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) Content mismatch at offset 0! 1 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 === IO: pattern 100 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/tests/qemu-iotests/049.out b/tests/qemu-iotests/049.out index 09ca0ae..9f93666 100644 --- a/tests/qemu-iotests/049.out +++ b/tests/qemu-iotests/049.out @@ -4,90 +4,90 @@ QA output created by 049 == 1. Traditional size parameter == qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024b -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1k -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1K -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1048576 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1048576 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1G -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1073741824 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1073741824 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1T -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1099511627776 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1099511627776 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024.0 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024.0b -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5k -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5K -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1572864 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1572864 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5G -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1610612736 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1610612736 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5T -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1649267441664 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1649267441664 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 == 2. Specifying size via -o == qemu-img create -f qcow2 -o size=1024 TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1024b TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1k TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1K TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1M TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1048576 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1048576 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1G TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1073741824 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1073741824 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1T TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1099511627776 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1099511627776 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1024.0 TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1024.0b TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1.5k TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1.5K TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1.5M TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1572864 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1572864 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1.5G TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1610612736 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1610612736 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1.5T TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1649267441664 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1649267441664 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 == 3. Invalid sizes == @@ -97,7 +97,7 @@ qemu-img: Image size must be less than 8 EiB! qemu-img create -f qcow2 -o size=-1024 TEST_DIR/t.qcow2 qemu-img: qcow2 doesn't support shrinking images yet qemu-img: TEST_DIR/t.qcow2: Could not resize image: Operation not supported -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=-1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=-1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- -1k qemu-img: Image size must be less than 8 EiB! @@ -105,17 +105,17 @@ qemu-img: Image size must be less than 8 EiB! qemu-img create -f qcow2 -o size=-1k TEST_DIR/t.qcow2 qemu-img: qcow2 doesn't support shrinking images yet qemu-img: TEST_DIR/t.qcow2: Could not resize image: Operation not supported -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=-1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=-1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- 1kilobyte -qemu-img: Invalid image size specified! You may use k, M, G, T, P or E suffixes for +qemu-img: Invalid image size specified! You may use k, M, G, T, P or E suffixes for qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes. qemu-img create -f qcow2 -o size=1kilobyte TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- foobar -qemu-img: Invalid image size specified! You may use k, M, G, T, P or E suffixes for +qemu-img: Invalid image size specified! You may use k, M, G, T, P or E suffixes for qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes. qemu-img create -f qcow2 -o size=foobar TEST_DIR/t.qcow2 @@ -125,84 +125,90 @@ qemu-img: TEST_DIR/t.qcow2: Invalid options for file format 'qcow2' == Check correct interpretation of suffixes for cluster size == qemu-img create -f qcow2 -o cluster_size=1024 TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=1024b TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=1k TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=1K TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=1M TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1048576 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1048576 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=1024.0 TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=1024.0b TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=0.5k TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=512 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=512 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=0.5K TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=512 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=512 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=0.5M TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=524288 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=524288 lazy_refcounts=off refcount_bits=16 == Check compat level option == qemu-img create -f qcow2 -o compat=0.10 TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o compat=1.1 TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='1.1' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='1.1' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o compat=0.42 TEST_DIR/t.qcow2 64M qemu-img: TEST_DIR/t.qcow2: Invalid compatibility level: '0.42' -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.42' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.42' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o compat=foobar TEST_DIR/t.qcow2 64M qemu-img: TEST_DIR/t.qcow2: Invalid compatibility level: 'foobar' -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='foobar' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='foobar' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 == Check preallocation option == qemu-img create -f qcow2 -o preallocation=off TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='off' lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='off' lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o preallocation=metadata TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='metadata' lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='metadata' lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o preallocation=1234 TEST_DIR/t.qcow2 64M qemu-img: TEST_DIR/t.qcow2: invalid parameter value: 1234 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='1234' lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='1234' lazy_refcounts=off refcount_bits=16 == Check encryption option == qemu-img create -f qcow2 -o encryption=off TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o encryption=on TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=on cluster_size=65536 lazy_refcounts=off +qemu-img: Encrypted images are deprecated +Support for them will be removed in a future release. +You can use 'qemu-img convert' to convert your image to an unencrypted one. +qemu-img: Encrypted images are deprecated +Support for them will be removed in a future release. +You can use 'qemu-img convert' to convert your image to an unencrypted one. +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=on cluster_size=65536 lazy_refcounts=off refcount_bits=16 == Check lazy_refcounts option (only with v3) == qemu-img create -f qcow2 -o compat=1.1,lazy_refcounts=off TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='1.1' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='1.1' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o compat=1.1,lazy_refcounts=on TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='1.1' encryption=off cluster_size=65536 lazy_refcounts=on +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='1.1' encryption=off cluster_size=65536 lazy_refcounts=on refcount_bits=16 qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=off TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=on TEST_DIR/t.qcow2 64M qemu-img: TEST_DIR/t.qcow2: Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater) -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=on +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=on refcount_bits=16 *** done diff --git a/tests/qemu-iotests/050.out b/tests/qemu-iotests/050.out index 3f5f7e1..a6cb2e6 100644 --- a/tests/qemu-iotests/050.out +++ b/tests/qemu-iotests/050.out @@ -1,13 +1,13 @@ QA output created by 050 == Creating images == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=10485760 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=10485760 wrote 1048576/1048576 bytes at offset 0 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=10485760 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=10485760 wrote 1048576/1048576 bytes at offset 0 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=10485760 backing_file='TEST_DIR/t.IMGFMT.old' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=10485760 backing_file='TEST_DIR/t.IMGFMT.old' wrote 1048576/1048576 bytes at offset 0 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051 index 11c858f..0360f37 100755 --- a/tests/qemu-iotests/051 +++ b/tests/qemu-iotests/051 @@ -41,6 +41,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file _supported_os Linux +# A compat=0.10 image is created in this test which does not support anything +# other than refcount_bits=16 +_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' function do_run_qemu() { @@ -93,6 +96,13 @@ echo run_qemu -drive file="$TEST_IMG",format=foo run_qemu -drive file="$TEST_IMG",driver=foo run_qemu -drive file="$TEST_IMG",driver=raw,format=qcow2 +run_qemu -drive file="$TEST_IMG",driver=qcow2,format=qcow2 + +echo +echo === Device without drive === +echo + +run_qemu -device virtio-scsi-pci -device scsi-hd echo echo === Overriding backing file === diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out index 2c7e808..2890eac 100644 --- a/tests/qemu-iotests/051.out +++ b/tests/qemu-iotests/051.out @@ -1,47 +1,58 @@ QA output created by 051 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR/t.IMGFMT.base' === Unknown option === Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt= -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=: could not open disk image TEST_DIR/t.qcow2: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt' +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt' Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on: could not open disk image TEST_DIR/t.qcow2: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt' +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt' Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234 -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234: could not open disk image TEST_DIR/t.qcow2: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt' +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt' Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo: could not open disk image TEST_DIR/t.qcow2: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt' +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt' === Unknown protocol option === Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt= -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=: could not open disk image TEST_DIR/t.qcow2: Block protocol 'file' doesn't support the option 'unknown_opt' +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=: Block protocol 'file' doesn't support the option 'unknown_opt' Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=on -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=on: could not open disk image TEST_DIR/t.qcow2: Block protocol 'file' doesn't support the option 'unknown_opt' +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=on: Block protocol 'file' doesn't support the option 'unknown_opt' Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=1234 -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=1234: could not open disk image TEST_DIR/t.qcow2: Block protocol 'file' doesn't support the option 'unknown_opt' +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=1234: Block protocol 'file' doesn't support the option 'unknown_opt' Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=foo -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=foo: could not open disk image TEST_DIR/t.qcow2: Block protocol 'file' doesn't support the option 'unknown_opt' +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=foo: Block protocol 'file' doesn't support the option 'unknown_opt' === Invalid format === Testing: -drive file=TEST_DIR/t.qcow2,format=foo -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=foo: 'foo' invalid format +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=foo: Unknown driver 'foo' Testing: -drive file=TEST_DIR/t.qcow2,driver=foo -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=foo: could not open disk image TEST_DIR/t.qcow2: Unknown driver 'foo' +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=foo: Unknown driver 'foo' Testing: -drive file=TEST_DIR/t.qcow2,driver=raw,format=qcow2 -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=raw,format=qcow2: could not open disk image TEST_DIR/t.qcow2: Driver specified twice +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=raw,format=qcow2: Cannot specify both 'driver' and 'format' + +Testing: -drive file=TEST_DIR/t.qcow2,driver=qcow2,format=qcow2 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=qcow2,format=qcow2: Cannot specify both 'driver' and 'format' + + +=== Device without drive === + +Testing: -device virtio-scsi-pci -device scsi-hd +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) QEMU_PROG: -device scsi-hd: drive property not set +QEMU_PROG: -device scsi-hd: Device 'scsi-hd' could not be initialized === Overriding backing file === @@ -50,17 +61,18 @@ Testing: -drive file=TEST_DIR/t.qcow2,driver=qcow2,backing.file.filename=TEST_DI QEMU X.Y.Z monitor - type 'help' for more information (qemu) iininfinfoinfo info binfo blinfo bloinfo blocinfo block ide0-hd0: TEST_DIR/t.qcow2 (qcow2) + Cache mode: writeback Backing file: TEST_DIR/t.qcow2.orig (chain depth: 1) (qemu) qququiquit Testing: -drive file=TEST_DIR/t.qcow2,driver=raw,backing.file.filename=TEST_DIR/t.qcow2.orig -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=raw,backing.file.filename=TEST_DIR/t.qcow2.orig: could not open disk image TEST_DIR/t.qcow2: Driver doesn't support backing files +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=raw,backing.file.filename=TEST_DIR/t.qcow2.orig: Driver doesn't support backing files Testing: -drive file=TEST_DIR/t.qcow2,file.backing.driver=file,file.backing.filename=TEST_DIR/t.qcow2.orig -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.backing.driver=file,file.backing.filename=TEST_DIR/t.qcow2.orig: could not open disk image TEST_DIR/t.qcow2: Driver doesn't support backing files +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.backing.driver=file,file.backing.filename=TEST_DIR/t.qcow2.orig: Driver doesn't support backing files Testing: -drive file=TEST_DIR/t.qcow2,file.backing.driver=qcow2,file.backing.file.filename=TEST_DIR/t.qcow2.orig -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.backing.driver=qcow2,file.backing.file.filename=TEST_DIR/t.qcow2.orig: could not open disk image TEST_DIR/t.qcow2: Driver doesn't support backing files +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.backing.driver=qcow2,file.backing.file.filename=TEST_DIR/t.qcow2.orig: Driver doesn't support backing files === Enable and disable lazy refcounting on the command line, plus some invalid values === @@ -74,20 +86,20 @@ QEMU X.Y.Z monitor - type 'help' for more information (qemu) qququiquit Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts= -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=: could not open disk image TEST_DIR/t.qcow2: Parameter 'lazy-refcounts' expects 'on' or 'off' +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=: Parameter 'lazy-refcounts' expects 'on' or 'off' Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=42 -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=42: could not open disk image TEST_DIR/t.qcow2: Parameter 'lazy-refcounts' expects 'on' or 'off' +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=42: Parameter 'lazy-refcounts' expects 'on' or 'off' Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=foo -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=foo: could not open disk image TEST_DIR/t.qcow2: Parameter 'lazy-refcounts' expects 'on' or 'off' +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=foo: Parameter 'lazy-refcounts' expects 'on' or 'off' === With version 2 images enabling lazy refcounts must fail === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=on -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=on: could not open disk image TEST_DIR/t.qcow2: Lazy refcounts require a qcow2 image with at least qemu 1.1 compatibility level +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=on: Lazy refcounts require a qcow2 image with at least qemu 1.1 compatibility level Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=off QEMU X.Y.Z monitor - type 'help' for more information @@ -111,20 +123,16 @@ QEMU X.Y.Z monitor - type 'help' for more information Testing: -drive if=ide QEMU X.Y.Z monitor - type 'help' for more information (qemu) QEMU_PROG: Device needs media, but drive is empty -QEMU_PROG: Device initialization failed. -QEMU_PROG: Initialization of device ide-hd failed +QEMU_PROG: Initialization of device ide-hd failed: Device initialization failed. Testing: -drive if=virtio QEMU X.Y.Z monitor - type 'help' for more information (qemu) QEMU_PROG: -drive if=virtio: Device needs media, but drive is empty -QEMU_PROG: -drive if=virtio: Device initialization failed. QEMU_PROG: -drive if=virtio: Device 'virtio-blk-pci' could not be initialized Testing: -drive if=scsi QEMU X.Y.Z monitor - type 'help' for more information -(qemu) QEMU_PROG: -drive if=scsi: Device needs media, but drive is empty -QEMU_PROG: Device initialization failed. -QEMU_PROG: Initialization of device lsi53c895a failed +(qemu) QEMU_PROG: Initialization of device lsi53c895a failed: Device needs media, but drive is empty Testing: -drive if=none,id=disk -device ide-cd,drive=disk QEMU X.Y.Z monitor - type 'help' for more information @@ -174,8 +182,7 @@ QEMU X.Y.Z monitor - type 'help' for more information Testing: -drive file=TEST_DIR/t.qcow2,if=ide,readonly=on QEMU X.Y.Z monitor - type 'help' for more information (qemu) QEMU_PROG: Can't use a read-only drive -QEMU_PROG: Device initialization failed. -QEMU_PROG: Initialization of device ide-hd failed +QEMU_PROG: Initialization of device ide-hd failed: Device initialization failed. Testing: -drive file=TEST_DIR/t.qcow2,if=virtio,readonly=on QEMU X.Y.Z monitor - type 'help' for more information @@ -247,31 +254,31 @@ QEMU X.Y.Z monitor - type 'help' for more information (qemu) qququiquit Testing: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2 -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2: could not open disk image TEST_DIR/t.qcow2: Block format 'qcow2' used by device '' doesn't support the option 'filename' +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2: Block format 'qcow2' used by device '' doesn't support the option 'filename' === Leaving out required options === Testing: -drive driver=file -QEMU_PROG: -drive driver=file: could not open disk image ide0-hd0: The 'file' block driver requires a file name +QEMU_PROG: -drive driver=file: The 'file' block driver requires a file name Testing: -drive driver=nbd -QEMU_PROG: -drive driver=nbd: could not open disk image ide0-hd0: one of path and host must be specified. +QEMU_PROG: -drive driver=nbd: one of path and host must be specified. Testing: -drive driver=raw -QEMU_PROG: -drive driver=raw: could not open disk image ide0-hd0: Can't use 'raw' as a block driver for the protocol level +QEMU_PROG: -drive driver=raw: Can't use 'raw' as a block driver for the protocol level Testing: -drive file.driver=file -QEMU_PROG: -drive file.driver=file: could not open disk image ide0-hd0: The 'file' block driver requires a file name +QEMU_PROG: -drive file.driver=file: The 'file' block driver requires a file name Testing: -drive file.driver=nbd -QEMU_PROG: -drive file.driver=nbd: could not open disk image ide0-hd0: one of path and host must be specified. +QEMU_PROG: -drive file.driver=nbd: one of path and host must be specified. Testing: -drive file.driver=raw -QEMU_PROG: -drive file.driver=raw: could not open disk image ide0-hd0: Can't use 'raw' as a block driver for the protocol level +QEMU_PROG: -drive file.driver=raw: Can't use 'raw' as a block driver for the protocol level Testing: -drive foo=bar -QEMU_PROG: -drive foo=bar: could not open disk image ide0-hd0: Must specify either driver or file +QEMU_PROG: -drive foo=bar: Must specify either driver or file === Specifying both an option and its legacy alias === @@ -322,13 +329,13 @@ QEMU_PROG: -drive file=TEST_DIR/t.qcow2,readonly=on,read-only=off: 'read-only' a === Parsing protocol from file name === Testing: -hda foo:bar -QEMU_PROG: -hda foo:bar: could not open disk image foo:bar: Unknown protocol +QEMU_PROG: -hda foo:bar: Unknown protocol 'foo' Testing: -drive file=foo:bar -QEMU_PROG: -drive file=foo:bar: could not open disk image foo:bar: Unknown protocol +QEMU_PROG: -drive file=foo:bar: Unknown protocol 'foo' Testing: -drive file.filename=foo:bar -QEMU_PROG: -drive file.filename=foo:bar: could not open disk image ide0-hd0: Could not open 'foo:bar': No such file or directory +QEMU_PROG: -drive file.filename=foo:bar: Could not open 'foo:bar': No such file or directory Testing: -hda file:TEST_DIR/t.qcow2 QEMU X.Y.Z monitor - type 'help' for more information @@ -339,7 +346,7 @@ QEMU X.Y.Z monitor - type 'help' for more information (qemu) qququiquit Testing: -drive file.filename=file:TEST_DIR/t.qcow2 -QEMU_PROG: -drive file.filename=file:TEST_DIR/t.qcow2: could not open disk image ide0-hd0: Could not open 'file:TEST_DIR/t.qcow2': No such file or directory +QEMU_PROG: -drive file.filename=file:TEST_DIR/t.qcow2: Could not open 'file:TEST_DIR/t.qcow2': No such file or directory === Snapshot mode === diff --git a/tests/qemu-iotests/052.out b/tests/qemu-iotests/052.out index 8617aa2..9dab51c 100644 --- a/tests/qemu-iotests/052.out +++ b/tests/qemu-iotests/052.out @@ -1,5 +1,5 @@ QA output created by 052 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 == reading whole image == read 134217728/134217728 bytes at offset 0 diff --git a/tests/qemu-iotests/053.out b/tests/qemu-iotests/053.out index 16464e6..8e793b6 100644 --- a/tests/qemu-iotests/053.out +++ b/tests/qemu-iotests/053.out @@ -1,7 +1,7 @@ QA output created by 053 == Creating single sector image == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=512 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/tests/qemu-iotests/054.out b/tests/qemu-iotests/054.out index 7161d6e..e6ec430 100644 --- a/tests/qemu-iotests/054.out +++ b/tests/qemu-iotests/054.out @@ -2,9 +2,9 @@ QA output created by 054 creating too large image (1 EB) qemu-img: TEST_DIR/t.IMGFMT: The image size is too large for file format 'IMGFMT' (try using a larger cluster size) -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1152921504606846976 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1152921504606846976 creating too large image (1 EB) using qcow2.py -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 qemu-img: Could not open 'TEST_DIR/t.qcow2': Image is too big *** done diff --git a/tests/qemu-iotests/055 b/tests/qemu-iotests/055 index 451b67d..017a609 100755 --- a/tests/qemu-iotests/055 +++ b/tests/qemu-iotests/055 @@ -1,8 +1,8 @@ #!/usr/bin/env python # -# Tests for drive-backup +# Tests for drive-backup and blockdev-backup # -# Copyright (C) 2013 Red Hat, Inc. +# Copyright (C) 2013, 2014 Red Hat, Inc. # # Based on 041. # @@ -27,6 +27,7 @@ from iotests import qemu_img, qemu_io test_img = os.path.join(iotests.test_dir, 'test.img') target_img = os.path.join(iotests.test_dir, 'target.img') +blockdev_target_img = os.path.join(iotests.test_dir, 'blockdev-target.img') class TestSingleDrive(iotests.QMPTestCase): image_len = 64 * 1024 * 1024 # MB @@ -34,38 +35,45 @@ class TestSingleDrive(iotests.QMPTestCase): def setUp(self): # Write data to the image so we can compare later qemu_img('create', '-f', iotests.imgfmt, test_img, str(TestSingleDrive.image_len)) - qemu_io('-c', 'write -P0x5d 0 64k', test_img) - qemu_io('-c', 'write -P0xd5 1M 32k', test_img) - qemu_io('-c', 'write -P0xdc 32M 124k', test_img) - qemu_io('-c', 'write -P0xdc 67043328 64k', test_img) + qemu_io('-f', iotests.imgfmt, '-c', 'write -P0x5d 0 64k', test_img) + qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xd5 1M 32k', test_img) + qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xdc 32M 124k', test_img) + qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xdc 67043328 64k', test_img) + qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(TestSingleDrive.image_len)) - self.vm = iotests.VM().add_drive(test_img) + self.vm = iotests.VM().add_drive(test_img).add_drive(blockdev_target_img) self.vm.launch() def tearDown(self): self.vm.shutdown() os.remove(test_img) + os.remove(blockdev_target_img) try: os.remove(target_img) except OSError: pass - def test_cancel(self): + def do_test_cancel(self, cmd, target): self.assert_no_active_block_jobs() - result = self.vm.qmp('drive-backup', device='drive0', - target=target_img, sync='full') + result = self.vm.qmp(cmd, device='drive0', target=target, sync='full') self.assert_qmp(result, 'return', {}) event = self.cancel_and_wait() self.assert_qmp(event, 'data/type', 'backup') - def test_pause(self): + def test_cancel_drive_backup(self): + self.do_test_cancel('drive-backup', target_img) + + def test_cancel_blockdev_backup(self): + self.do_test_cancel('blockdev-backup', 'drive1') + + def do_test_pause(self, cmd, target, image): self.assert_no_active_block_jobs() self.vm.pause_drive('drive0') - result = self.vm.qmp('drive-backup', device='drive0', - target=target_img, sync='full') + result = self.vm.qmp(cmd, device='drive0', + target=target, sync='full') self.assert_qmp(result, 'return', {}) result = self.vm.qmp('block-job-pause', device='drive0') @@ -86,14 +94,25 @@ class TestSingleDrive(iotests.QMPTestCase): self.wait_until_completed() self.vm.shutdown() - self.assertTrue(iotests.compare_images(test_img, target_img), + self.assertTrue(iotests.compare_images(test_img, image), 'target image does not match source after backup') + def test_pause_drive_backup(self): + self.do_test_pause('drive-backup', target_img, target_img) + + def test_pause_blockdev_backup(self): + self.do_test_pause('blockdev-backup', 'drive1', blockdev_target_img) + def test_medium_not_found(self): result = self.vm.qmp('drive-backup', device='ide1-cd0', target=target_img, sync='full') self.assert_qmp(result, 'error/class', 'GenericError') + def test_medium_not_found_blockdev_backup(self): + result = self.vm.qmp('blockdev-backup', device='ide1-cd0', + target='drive1', sync='full') + self.assert_qmp(result, 'error/class', 'GenericError') + def test_image_not_found(self): result = self.vm.qmp('drive-backup', device='drive0', target=target_img, sync='full', mode='existing') @@ -105,31 +124,56 @@ class TestSingleDrive(iotests.QMPTestCase): format='spaghetti-noodles') self.assert_qmp(result, 'error/class', 'GenericError') + def do_test_device_not_found(self, cmd, **args): + result = self.vm.qmp(cmd, **args) + if cmd == 'drive-backup': + self.assert_qmp(result, 'error/class', 'DeviceNotFound') + else: + self.assert_qmp(result, 'error/class', 'GenericError') + def test_device_not_found(self): - result = self.vm.qmp('drive-backup', device='nonexistent', - target=target_img, sync='full') - self.assert_qmp(result, 'error/class', 'DeviceNotFound') + self.do_test_device_not_found('drive-backup', device='nonexistent', + target=target_img, sync='full') + + self.do_test_device_not_found('blockdev-backup', device='nonexistent', + target='drive0', sync='full') + + self.do_test_device_not_found('blockdev-backup', device='drive0', + target='nonexistent', sync='full') + + self.do_test_device_not_found('blockdev-backup', device='nonexistent', + target='nonexistent', sync='full') + + def test_target_is_source(self): + result = self.vm.qmp('blockdev-backup', device='drive0', + target='drive0', sync='full') + self.assert_qmp(result, 'error/class', 'GenericError') class TestSetSpeed(iotests.QMPTestCase): image_len = 80 * 1024 * 1024 # MB def setUp(self): qemu_img('create', '-f', iotests.imgfmt, test_img, str(TestSetSpeed.image_len)) - qemu_io('-c', 'write -P1 0 512', test_img) - self.vm = iotests.VM().add_drive(test_img) + qemu_io('-f', iotests.imgfmt, '-c', 'write -P1 0 512', test_img) + qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(TestSingleDrive.image_len)) + + self.vm = iotests.VM().add_drive(test_img).add_drive(blockdev_target_img) self.vm.launch() def tearDown(self): self.vm.shutdown() os.remove(test_img) - os.remove(target_img) + os.remove(blockdev_target_img) + try: + os.remove(target_img) + except OSError: + pass - def test_set_speed(self): + def do_test_set_speed(self, cmd, target): self.assert_no_active_block_jobs() self.vm.pause_drive('drive0') - result = self.vm.qmp('drive-backup', device='drive0', - target=target_img, sync='full') + result = self.vm.qmp(cmd, device='drive0', target=target, sync='full') self.assert_qmp(result, 'return', {}) # Default speed is 0 @@ -148,10 +192,10 @@ class TestSetSpeed(iotests.QMPTestCase): event = self.cancel_and_wait(resume=True) self.assert_qmp(event, 'data/type', 'backup') - # Check setting speed in drive-backup works + # Check setting speed option works self.vm.pause_drive('drive0') - result = self.vm.qmp('drive-backup', device='drive0', - target=target_img, sync='full', speed=4*1024*1024) + result = self.vm.qmp(cmd, device='drive0', + target=target, sync='full', speed=4*1024*1024) self.assert_qmp(result, 'return', {}) result = self.vm.qmp('query-block-jobs') @@ -161,18 +205,24 @@ class TestSetSpeed(iotests.QMPTestCase): event = self.cancel_and_wait(resume=True) self.assert_qmp(event, 'data/type', 'backup') - def test_set_speed_invalid(self): + def test_set_speed_drive_backup(self): + self.do_test_set_speed('drive-backup', target_img) + + def test_set_speed_blockdev_backup(self): + self.do_test_set_speed('blockdev-backup', 'drive1') + + def do_test_set_speed_invalid(self, cmd, target): self.assert_no_active_block_jobs() - result = self.vm.qmp('drive-backup', device='drive0', - target=target_img, sync='full', speed=-1) + result = self.vm.qmp(cmd, device='drive0', + target=target, sync='full', speed=-1) self.assert_qmp(result, 'error/class', 'GenericError') self.assert_no_active_block_jobs() self.vm.pause_drive('drive0') - result = self.vm.qmp('drive-backup', device='drive0', - target=target_img, sync='full') + result = self.vm.qmp(cmd, device='drive0', + target=target, sync='full') self.assert_qmp(result, 'return', {}) result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1) @@ -181,50 +231,65 @@ class TestSetSpeed(iotests.QMPTestCase): event = self.cancel_and_wait(resume=True) self.assert_qmp(event, 'data/type', 'backup') + def test_set_speed_invalid_drive_backup(self): + self.do_test_set_speed_invalid('drive-backup', target_img) + + def test_set_speed_invalid_blockdev_backup(self): + self.do_test_set_speed_invalid('blockdev-backup', 'drive1') + class TestSingleTransaction(iotests.QMPTestCase): image_len = 64 * 1024 * 1024 # MB def setUp(self): qemu_img('create', '-f', iotests.imgfmt, test_img, str(TestSingleTransaction.image_len)) - qemu_io('-c', 'write -P0x5d 0 64k', test_img) - qemu_io('-c', 'write -P0xd5 1M 32k', test_img) - qemu_io('-c', 'write -P0xdc 32M 124k', test_img) - qemu_io('-c', 'write -P0xdc 67043328 64k', test_img) + qemu_io('-f', iotests.imgfmt, '-c', 'write -P0x5d 0 64k', test_img) + qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xd5 1M 32k', test_img) + qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xdc 32M 124k', test_img) + qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xdc 67043328 64k', test_img) + qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(TestSingleDrive.image_len)) - self.vm = iotests.VM().add_drive(test_img) + self.vm = iotests.VM().add_drive(test_img).add_drive(blockdev_target_img) self.vm.launch() def tearDown(self): self.vm.shutdown() os.remove(test_img) + os.remove(blockdev_target_img) try: os.remove(target_img) except OSError: pass - def test_cancel(self): + def do_test_cancel(self, cmd, target): self.assert_no_active_block_jobs() result = self.vm.qmp('transaction', actions=[{ - 'type': 'drive-backup', + 'type': cmd, 'data': { 'device': 'drive0', - 'target': target_img, + 'target': target, 'sync': 'full' }, } ]) + self.assert_qmp(result, 'return', {}) event = self.cancel_and_wait() self.assert_qmp(event, 'data/type', 'backup') - def test_pause(self): + def test_cancel_drive_backup(self): + self.do_test_cancel('drive-backup', target_img) + + def test_cancel_blockdev_backup(self): + self.do_test_cancel('blockdev-backup', 'drive1') + + def do_test_pause(self, cmd, target, image): self.assert_no_active_block_jobs() self.vm.pause_drive('drive0') result = self.vm.qmp('transaction', actions=[{ - 'type': 'drive-backup', + 'type': cmd, 'data': { 'device': 'drive0', - 'target': target_img, + 'target': target, 'sync': 'full' }, } ]) @@ -248,19 +313,31 @@ class TestSingleTransaction(iotests.QMPTestCase): self.wait_until_completed() self.vm.shutdown() - self.assertTrue(iotests.compare_images(test_img, target_img), + self.assertTrue(iotests.compare_images(test_img, image), 'target image does not match source after backup') - def test_medium_not_found(self): + def test_pause_drive_backup(self): + self.do_test_pause('drive-backup', target_img, target_img) + + def test_pause_blockdev_backup(self): + self.do_test_pause('blockdev-backup', 'drive1', blockdev_target_img) + + def do_test_medium_not_found(self, cmd, target): result = self.vm.qmp('transaction', actions=[{ - 'type': 'drive-backup', + 'type': cmd, 'data': { 'device': 'ide1-cd0', - 'target': target_img, + 'target': target, 'sync': 'full' }, } ]) self.assert_qmp(result, 'error/class', 'GenericError') + def test_medium_not_found_drive_backup(self): + self.do_test_medium_not_found('drive-backup', target_img) + + def test_medium_not_found_blockdev_backup(self): + self.do_test_medium_not_found('blockdev-backup', 'drive1') + def test_image_not_found(self): result = self.vm.qmp('transaction', actions=[{ 'type': 'drive-backup', @@ -283,6 +360,43 @@ class TestSingleTransaction(iotests.QMPTestCase): ]) self.assert_qmp(result, 'error/class', 'DeviceNotFound') + result = self.vm.qmp('transaction', actions=[{ + 'type': 'blockdev-backup', + 'data': { 'device': 'nonexistent', + 'target': 'drive1', + 'sync': 'full' }, + } + ]) + self.assert_qmp(result, 'error/class', 'GenericError') + + result = self.vm.qmp('transaction', actions=[{ + 'type': 'blockdev-backup', + 'data': { 'device': 'drive0', + 'target': 'nonexistent', + 'sync': 'full' }, + } + ]) + self.assert_qmp(result, 'error/class', 'GenericError') + + result = self.vm.qmp('transaction', actions=[{ + 'type': 'blockdev-backup', + 'data': { 'device': 'nonexistent', + 'target': 'nonexistent', + 'sync': 'full' }, + } + ]) + self.assert_qmp(result, 'error/class', 'GenericError') + + def test_target_is_source(self): + result = self.vm.qmp('transaction', actions=[{ + 'type': 'blockdev-backup', + 'data': { 'device': 'drive0', + 'target': 'drive0', + 'sync': 'full' }, + } + ]) + self.assert_qmp(result, 'error/class', 'GenericError') + def test_abort(self): result = self.vm.qmp('transaction', actions=[{ 'type': 'drive-backup', @@ -298,5 +412,31 @@ class TestSingleTransaction(iotests.QMPTestCase): self.assert_qmp(result, 'error/class', 'GenericError') self.assert_no_active_block_jobs() + result = self.vm.qmp('transaction', actions=[{ + 'type': 'blockdev-backup', + 'data': { 'device': 'nonexistent', + 'target': 'drive1', + 'sync': 'full' }, + }, { + 'type': 'Abort', + 'data': {}, + } + ]) + self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_no_active_block_jobs() + + result = self.vm.qmp('transaction', actions=[{ + 'type': 'blockdev-backup', + 'data': { 'device': 'drive0', + 'target': 'nonexistent', + 'sync': 'full' }, + }, { + 'type': 'Abort', + 'data': {}, + } + ]) + self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_no_active_block_jobs() + if __name__ == '__main__': iotests.main(supported_fmts=['raw', 'qcow2']) diff --git a/tests/qemu-iotests/055.out b/tests/qemu-iotests/055.out index 6323079..42314e9 100644 --- a/tests/qemu-iotests/055.out +++ b/tests/qemu-iotests/055.out @@ -1,5 +1,5 @@ -.............. +........................ ---------------------------------------------------------------------- -Ran 14 tests +Ran 24 tests OK diff --git a/tests/qemu-iotests/058 b/tests/qemu-iotests/058 index 14584cd..f2bdd0b 100755 --- a/tests/qemu-iotests/058 +++ b/tests/qemu-iotests/058 @@ -87,7 +87,13 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file +_supported_os Linux _require_command QEMU_NBD +# Internal snapshots are (currently) impossible with refcount_bits=1 +_unsupported_imgopts 'refcount_bits=1[^0-9]' + +# Use -f raw instead of -f $IMGFMT for the NBD connection +QEMU_IO_NBD="$QEMU_IO -f raw --cache=$CACHEMODE" echo echo "== preparing image ==" @@ -108,15 +114,15 @@ _export_nbd_snapshot sn1 echo echo "== verifying the exported snapshot with patterns, method 1 ==" -$QEMU_IO -c 'read -P 0xa 0x1000 0x1000' "$nbd_snapshot_img" | _filter_qemu_io -$QEMU_IO -c 'read -P 0xb 0x2000 0x1000' "$nbd_snapshot_img" | _filter_qemu_io +$QEMU_IO_NBD -c 'read -P 0xa 0x1000 0x1000' "$nbd_snapshot_img" | _filter_qemu_io +$QEMU_IO_NBD -c 'read -P 0xb 0x2000 0x1000' "$nbd_snapshot_img" | _filter_qemu_io _export_nbd_snapshot1 sn1 echo echo "== verifying the exported snapshot with patterns, method 2 ==" -$QEMU_IO -c 'read -P 0xa 0x1000 0x1000' "$nbd_snapshot_img" | _filter_qemu_io -$QEMU_IO -c 'read -P 0xb 0x2000 0x1000' "$nbd_snapshot_img" | _filter_qemu_io +$QEMU_IO_NBD -c 'read -P 0xa 0x1000 0x1000' "$nbd_snapshot_img" | _filter_qemu_io +$QEMU_IO_NBD -c 'read -P 0xb 0x2000 0x1000' "$nbd_snapshot_img" | _filter_qemu_io $QEMU_IMG convert "$TEST_IMG" -l sn1 -O qcow2 "$converted_image" diff --git a/tests/qemu-iotests/059 b/tests/qemu-iotests/059 index 3c053c2..50ca5ce 100755 --- a/tests/qemu-iotests/059 +++ b/tests/qemu-iotests/059 @@ -106,11 +106,17 @@ _img_info echo echo "=== Converting to streamOptimized from image with small cluster size===" TEST_IMG="$TEST_IMG.qcow2" IMGFMT=qcow2 IMGOPTS="cluster_size=4096" _make_test_img 1G -$QEMU_IO -c "write -P 0xa 0 512" "$TEST_IMG.qcow2" | _filter_qemu_io -$QEMU_IO -c "write -P 0xb 10240 512" "$TEST_IMG.qcow2" | _filter_qemu_io +$QEMU_IO -f qcow2 -c "write -P 0xa 0 512" "$TEST_IMG.qcow2" | _filter_qemu_io +$QEMU_IO -f qcow2 -c "write -P 0xb 10240 512" "$TEST_IMG.qcow2" | _filter_qemu_io $QEMU_IMG convert -f qcow2 -O vmdk -o subformat=streamOptimized "$TEST_IMG.qcow2" "$TEST_IMG" 2>&1 echo +echo "=== Testing monolithicFlat with internally generated JSON file name ===" +IMGOPTS="subformat=monolithicFlat" _make_test_img 64M +$QEMU_IO -c "open -o driver=$IMGFMT,file.driver=blkdebug,file.image.filename=$TEST_IMG,file.inject-error.0.event=read_aio" 2>&1 \ + | _filter_testdir | _filter_imgfmt + +echo echo "=== Testing version 3 ===" _use_sample_img iotest-version3.vmdk.bz2 _img_info diff --git a/tests/qemu-iotests/059.out b/tests/qemu-iotests/059.out index 0dadba6..cbb0de4 100644 --- a/tests/qemu-iotests/059.out +++ b/tests/qemu-iotests/059.out @@ -2053,6 +2053,10 @@ wrote 512/512 bytes at offset 0 wrote 512/512 bytes at offset 10240 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +=== Testing monolithicFlat with internally generated JSON file name === +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qemu-io: can't open: Cannot use relative extent paths with VMDK descriptor file 'json:{"image": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "driver": "blkdebug", "inject-error.0.event": "read_aio"}' + === Testing version 3 === image: TEST_DIR/iotest-version3.IMGFMT file format: IMGFMT diff --git a/tests/qemu-iotests/060 b/tests/qemu-iotests/060 index 9772d36..c81319c 100755 --- a/tests/qemu-iotests/060 +++ b/tests/qemu-iotests/060 @@ -77,7 +77,7 @@ $QEMU_IO -c "$OPEN_RW" -c "write -P 0x2a 0 512" | _filter_qemu_io $PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features # This information should be available through qemu-img info -$QEMU_IMG info "$TEST_IMG" | _filter_testdir +_img_info --format-specific # Try to open the image R/W (which should fail) $QEMU_IO -c "$OPEN_RW" -c "read 0 512" 2>&1 | _filter_qemu_io \ @@ -186,6 +186,12 @@ $QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io poke_file "$TEST_IMG" "$l1_offset" "\x80\x00\x00\x00\x00\x04\x2a\x00" $QEMU_IO -c "read 0 64k" "$TEST_IMG" | _filter_qemu_io +# Test how well zero cluster expansion can cope with this +_make_test_img 64M +$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io +poke_file "$TEST_IMG" "$l1_offset" "\x80\x00\x00\x00\x00\x04\x2a\x00" +$QEMU_IMG amend -o compat=0.10 "$TEST_IMG" + echo echo "=== Testing unaligned L2 entry ===" echo @@ -195,6 +201,15 @@ poke_file "$TEST_IMG" "$l2_offset" "\x80\x00\x00\x00\x00\x05\x2a\x00" $QEMU_IO -c "read 0 64k" "$TEST_IMG" | _filter_qemu_io echo +echo "=== Testing unaligned pre-allocated zero cluster ===" +echo +_make_test_img 64M +$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io +poke_file "$TEST_IMG" "$l2_offset" "\x80\x00\x00\x00\x00\x05\x2a\x01" +# zero cluster expansion +$QEMU_IMG amend -o compat=0.10 "$TEST_IMG" + +echo echo "=== Testing unaligned reftable entry ===" echo _make_test_img 64M diff --git a/tests/qemu-iotests/060.out b/tests/qemu-iotests/060.out index 9419da1..7511189 100644 --- a/tests/qemu-iotests/060.out +++ b/tests/qemu-iotests/060.out @@ -2,7 +2,7 @@ QA output created by 060 === Testing L2 reference into L1 === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 ERROR cluster 3 refcount=1 reference=3 1 errors were found on the image. @@ -11,14 +11,14 @@ incompatible_features 0x0 qcow2: Marking image as corrupt: Preventing invalid write on metadata (overlaps with active L1 table); further corruption events will be suppressed write failed: Input/output error incompatible_features 0x2 -image: TEST_DIR/t.qcow2 -file format: qcow2 +image: TEST_DIR/t.IMGFMT +file format: IMGFMT virtual size: 64M (67108864 bytes) -disk size: 196K cluster_size: 65536 Format specific information: compat: 1.1 lazy refcounts: false + refcount bits: 16 corrupt: true qemu-io: can't open device TEST_DIR/t.IMGFMT: IMGFMT: Image is corrupt; cannot be opened read/write read 512/512 bytes at offset 0 @@ -26,7 +26,7 @@ read 512/512 bytes at offset 0 === Testing cluster data reference into refcount block === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 ERROR refcount block 0 refcount=2 ERROR cluster 2 refcount=1 reference=2 @@ -55,7 +55,7 @@ incompatible_features 0x0 === Testing cluster data reference into inactive L2 table === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 512/512 bytes at offset 0 @@ -96,7 +96,7 @@ read 512/512 bytes at offset 0 === Testing overlap while COW is in flight === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 65536/65536 bytes at offset 536870912 @@ -111,7 +111,7 @@ aio_write failed: No medium found === Testing unallocated image header === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qcow2: Marking image as corrupt: Preventing invalid write on metadata (overlaps with qcow2_header); further corruption events will be suppressed @@ -119,29 +119,42 @@ write failed: Input/output error === Testing unaligned L1 entry === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qcow2: Marking image as corrupt: L2 table offset 0x42a00 unaligned (L1 index: 0); further corruption events will be suppressed read failed: Input/output error +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +wrote 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qcow2: Marking image as corrupt: L2 table offset 0x42a00 unaligned (L1 index: 0); further corruption events will be suppressed +qemu-img: Error while amending options: Input/output error === Testing unaligned L2 entry === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qcow2: Marking image as corrupt: Data cluster offset 0x52a00 unaligned (L2 offset: 0x40000, L2 index: 0); further corruption events will be suppressed read failed: Input/output error +=== Testing unaligned pre-allocated zero cluster === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +wrote 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qcow2: Marking image as corrupt: Data cluster offset 0x52a00 unaligned (L2 offset: 0x40000, L2 index: 0); further corruption events will be suppressed +qemu-img: Error while amending options: Input/output error + === Testing unaligned reftable entry === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 qcow2: Marking image as corrupt: Refblock offset 0x22a00 unaligned (reftable index: 0); further corruption events will be suppressed write failed: Input/output error === Testing non-fatal corruption on freeing === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qcow2: Image is corrupt: Cannot free unaligned cluster 0x52a00; further non-fatal corruption events will be suppressed @@ -150,7 +163,7 @@ discard 65536/65536 bytes at offset 0 === Testing read-only corruption report === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qcow2: Image is corrupt: Data cluster offset 0x52a00 unaligned (L2 offset: 0x40000, L2 index: 0); further non-fatal corruption events will be suppressed @@ -159,7 +172,7 @@ read failed: Input/output error === Testing non-fatal and then fatal corruption report === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qcow2: Image is corrupt: Cannot free unaligned cluster 0x52a00; further non-fatal corruption events will be suppressed diff --git a/tests/qemu-iotests/061.out b/tests/qemu-iotests/061.out index 9045544..5ec248f 100644 --- a/tests/qemu-iotests/061.out +++ b/tests/qemu-iotests/061.out @@ -2,7 +2,7 @@ QA output created by 061 === Testing version downgrade with zero expansion === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) magic 0x514649fb @@ -54,7 +54,7 @@ No errors were found on the image. === Testing dirty version downgrade === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) magic 0x514649fb @@ -111,7 +111,7 @@ No errors were found on the image. === Testing version downgrade with unknown compat/autoclear flags === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 magic 0x514649fb version 3 backing_file_offset 0x0 @@ -159,7 +159,7 @@ No errors were found on the image. === Testing version upgrade and resize === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 65536/65536 bytes at offset 44040192 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) magic 0x514649fb @@ -211,7 +211,7 @@ No errors were found on the image. === Testing dirty lazy_refcounts=off === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) magic 0x514649fb @@ -268,8 +268,8 @@ No errors were found on the image. === Testing backing file === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 131072/131072 bytes at offset 0 @@ -280,7 +280,7 @@ No errors were found on the image. === Testing invalid configurations === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater) qemu-img: Error while amending options: Invalid argument Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater) @@ -288,7 +288,6 @@ qemu-img: Error while amending options: Invalid argument Unknown compatibility level 0.42. qemu-img: Error while amending options: Invalid argument qemu-img: Invalid parameter 'foo' -qemu-img: Invalid options for file format 'qcow2' Changing the cluster size is not supported. qemu-img: Error while amending options: Operation not supported Changing the encryption flag is not supported. @@ -298,7 +297,7 @@ qemu-img: Error while amending options: Operation not supported === Testing correct handling of unset value === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Should work: Should not work: Changing the cluster size is not supported. @@ -306,7 +305,7 @@ qemu-img: Error while amending options: Operation not supported === Testing zero expansion on inactive clusters === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 131072/131072 bytes at offset 0 @@ -320,7 +319,7 @@ read 131072/131072 bytes at offset 0 === Testing zero expansion on shared L2 table === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) No errors were found on the image. @@ -332,10 +331,10 @@ read 131072/131072 bytes at offset 0 === Testing zero expansion on backed image === -Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' read 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 65536/65536 bytes at offset 0 @@ -348,10 +347,10 @@ read 65536/65536 bytes at offset 65536 === Testing zero expansion on backed inactive clusters === -Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 131072/131072 bytes at offset 0 @@ -367,10 +366,10 @@ read 65536/65536 bytes at offset 65536 === Testing zero expansion on backed image with shared L2 table === -Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) No errors were found on the image. @@ -382,7 +381,7 @@ read 131072/131072 bytes at offset 0 === Testing preallocated zero expansion on full image === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 67108864/67108864 bytes at offset 0 64 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 67108864/67108864 bytes at offset 0 @@ -393,8 +392,8 @@ read 67108864/67108864 bytes at offset 0 === Testing progress report without snapshot === -Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=4294967296 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=4294967296 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 backing_file='TEST_DIR/t.IMGFMT.base' wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 65536/65536 bytes at offset 1073741824 @@ -408,8 +407,8 @@ No errors were found on the image. === Testing progress report with snapshot === -Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=4294967296 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=4294967296 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 backing_file='TEST_DIR/t.IMGFMT.base' wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 65536/65536 bytes at offset 1073741824 diff --git a/tests/qemu-iotests/062.out b/tests/qemu-iotests/062.out index 442d761..9ddf22b 100644 --- a/tests/qemu-iotests/062.out +++ b/tests/qemu-iotests/062.out @@ -2,7 +2,7 @@ QA output created by 062 === Testing snapshotting an image with zero clusters === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 262144/262144 bytes at offset 0 256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) No errors were found on the image. diff --git a/tests/qemu-iotests/064 b/tests/qemu-iotests/064 index 1c74c31..7564563 100755 --- a/tests/qemu-iotests/064 +++ b/tests/qemu-iotests/064 @@ -54,7 +54,15 @@ $QEMU_IO -r -c "read -pP 0x96 33M 33M" "$TEST_IMG" | _filter_qemu_io echo echo "=== Verify pattern 0x00, 66M - 1024M ===" -$QEMU_IO -r -c "read -pP 0x00 66M 958M" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -r -c "read -pP 0x00 66M 62M" \ + -c "read -pP 0x00 128M 128M" \ + -c "read -pP 0x00 256M 128M" \ + -c "read -pP 0x00 384M 128M" \ + -c "read -pP 0x00 512M 128M" \ + -c "read -pP 0x00 640M 128M" \ + -c "read -pP 0x00 768M 128M" \ + -c "read -pP 0x00 896M 128M" \ + "$TEST_IMG" | _filter_qemu_io echo echo "=== Verify pattern write, 0xc3 99M-157M ===" @@ -63,7 +71,14 @@ $QEMU_IO -c "write -pP 0xc3 99M 58M" "$TEST_IMG" | _filter_qemu_io $QEMU_IO -c "read -pP 0xa5 0 33M" "$TEST_IMG" | _filter_qemu_io $QEMU_IO -c "read -pP 0x96 33M 33M" "$TEST_IMG" | _filter_qemu_io $QEMU_IO -c "read -pP 0x00 66M 33M" "$TEST_IMG" | _filter_qemu_io -$QEMU_IO -c "read -pP 0x00 157MM 867MM" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -pP 0x00 157M 99M" \ + -c "read -pP 0x00 256M 128M" \ + -c "read -pP 0x00 384M 128M" \ + -c "read -pP 0x00 512M 128M" \ + -c "read -pP 0x00 640M 128M" \ + -c "read -pP 0x00 768M 128M" \ + -c "read -pP 0x00 896M 128M" \ + "$TEST_IMG" | _filter_qemu_io # now verify what we should have actually written $QEMU_IO -c "read -pP 0xc3 99M 58M" "$TEST_IMG" | _filter_qemu_io diff --git a/tests/qemu-iotests/064.out b/tests/qemu-iotests/064.out index 5346a4e..1a5b9e2 100644 --- a/tests/qemu-iotests/064.out +++ b/tests/qemu-iotests/064.out @@ -9,8 +9,22 @@ read 34603008/34603008 bytes at offset 34603008 33 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) === Verify pattern 0x00, 66M - 1024M === -read 1004535808/1004535808 bytes at offset 69206016 -958 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65011712/65011712 bytes at offset 69206016 +62 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 134217728/134217728 bytes at offset 134217728 +128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 134217728/134217728 bytes at offset 268435456 +128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 134217728/134217728 bytes at offset 402653184 +128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 134217728/134217728 bytes at offset 536870912 +128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 134217728/134217728 bytes at offset 671088640 +128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 134217728/134217728 bytes at offset 805306368 +128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 134217728/134217728 bytes at offset 939524096 +128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) === Verify pattern write, 0xc3 99M-157M === wrote 60817408/60817408 bytes at offset 103809024 @@ -21,8 +35,20 @@ read 34603008/34603008 bytes at offset 34603008 33 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 34603008/34603008 bytes at offset 69206016 33 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -read 909115392/909115392 bytes at offset 164626432 -867 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 103809024/103809024 bytes at offset 164626432 +99 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 134217728/134217728 bytes at offset 268435456 +128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 134217728/134217728 bytes at offset 402653184 +128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 134217728/134217728 bytes at offset 536870912 +128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 134217728/134217728 bytes at offset 671088640 +128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 134217728/134217728 bytes at offset 805306368 +128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 134217728/134217728 bytes at offset 939524096 +128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 60817408/60817408 bytes at offset 103809024 58 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) *** done diff --git a/tests/qemu-iotests/065 b/tests/qemu-iotests/065 index 8d3a9c9..72aa970 100755 --- a/tests/qemu-iotests/065 +++ b/tests/qemu-iotests/065 @@ -88,34 +88,41 @@ class TestQMP(TestImageInfoSpecific): class TestQCow2(TestQemuImgInfo): '''Testing a qcow2 version 2 image''' img_options = 'compat=0.10' - json_compare = { 'compat': '0.10' } - human_compare = [ 'compat: 0.10' ] + json_compare = { 'compat': '0.10', 'refcount-bits': 16 } + human_compare = [ 'compat: 0.10', 'refcount bits: 16' ] class TestQCow3NotLazy(TestQemuImgInfo): '''Testing a qcow2 version 3 image with lazy refcounts disabled''' img_options = 'compat=1.1,lazy_refcounts=off' - json_compare = { 'compat': '1.1', 'lazy-refcounts': False, 'corrupt': False } - human_compare = [ 'compat: 1.1', 'lazy refcounts: false', 'corrupt: false' ] + json_compare = { 'compat': '1.1', 'lazy-refcounts': False, + 'refcount-bits': 16, 'corrupt': False } + human_compare = [ 'compat: 1.1', 'lazy refcounts: false', + 'refcount bits: 16', 'corrupt: false' ] class TestQCow3Lazy(TestQemuImgInfo): '''Testing a qcow2 version 3 image with lazy refcounts enabled''' img_options = 'compat=1.1,lazy_refcounts=on' - json_compare = { 'compat': '1.1', 'lazy-refcounts': True, 'corrupt': False } - human_compare = [ 'compat: 1.1', 'lazy refcounts: true', 'corrupt: false' ] + json_compare = { 'compat': '1.1', 'lazy-refcounts': True, + 'refcount-bits': 16, 'corrupt': False } + human_compare = [ 'compat: 1.1', 'lazy refcounts: true', + 'refcount bits: 16', 'corrupt: false' ] class TestQCow3NotLazyQMP(TestQMP): '''Testing a qcow2 version 3 image with lazy refcounts disabled, opening with lazy refcounts enabled''' img_options = 'compat=1.1,lazy_refcounts=off' qemu_options = 'lazy-refcounts=on' - compare = { 'compat': '1.1', 'lazy-refcounts': False, 'corrupt': False } + compare = { 'compat': '1.1', 'lazy-refcounts': False, + 'refcount-bits': 16, 'corrupt': False } + class TestQCow3LazyQMP(TestQMP): '''Testing a qcow2 version 3 image with lazy refcounts enabled, opening with lazy refcounts disabled''' img_options = 'compat=1.1,lazy_refcounts=on' qemu_options = 'lazy-refcounts=off' - compare = { 'compat': '1.1', 'lazy-refcounts': True, 'corrupt': False } + compare = { 'compat': '1.1', 'lazy-refcounts': True, + 'refcount-bits': 16, 'corrupt': False } TestImageInfoSpecific = None TestQemuImgInfo = None diff --git a/tests/qemu-iotests/066.out b/tests/qemu-iotests/066.out index 9139780..7bc9a10 100644 --- a/tests/qemu-iotests/066.out +++ b/tests/qemu-iotests/066.out @@ -2,7 +2,7 @@ QA output created by 066 === Testing snapshotting an image with zero clusters === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 262144/262144 bytes at offset 0 256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 262144/262144 bytes at offset 0 diff --git a/tests/qemu-iotests/067 b/tests/qemu-iotests/067 index d025192..83eefa3 100755 --- a/tests/qemu-iotests/067 +++ b/tests/qemu-iotests/067 @@ -35,17 +35,20 @@ status=1 # failure is the default! _supported_fmt qcow2 _supported_proto file _supported_os Linux +# Because anything other than 16 would change the output of query-block +_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' function do_run_qemu() { echo Testing: "$@" - $QEMU -nographic -qmp stdio -serial none "$@" + $QEMU -nographic -qmp-pretty stdio -serial none "$@" echo } function run_qemu() { - do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp | sed -e 's/\("actual-size":\s*\)[0-9]\+/\1SIZE/g' + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp | _filter_qemu \ + | sed -e 's/\("actual-size":\s*\)[0-9]\+/\1SIZE/g' } size=128M diff --git a/tests/qemu-iotests/067.out b/tests/qemu-iotests/067.out index 0f72dcf..6ff41bc 100644 --- a/tests/qemu-iotests/067.out +++ b/tests/qemu-iotests/067.out @@ -1,80 +1,780 @@ QA output created by 067 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 === -drive/-device and device_del === Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,if=none,id=disk -device virtio-blk-pci,drive=disk,id=virtio0 -QMP_VERSION -{"return": {}} -{"return": [{"io-status": "ok", "device": "disk", "locked": false, "removable": false, "inserted": {"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false, "corrupt": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "type": "unknown"}, {"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]} -{"return": {}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"path": "/machine/peripheral/virtio0/virtio-backend"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"device": "virtio0", "path": "/machine/peripheral/virtio0"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESET"} -{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "floppy0", "tray-open": true}} +{ + QMP_VERSION +} +{ + "return": { + } +} +{ + "return": [ + { + "io-status": "ok", + "device": "disk", + "locked": false, + "removable": false, + "inserted": { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 134217728, + "filename": "TEST_DIR/t.qcow2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": SIZE, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": false, + "backing_file_depth": 0, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "TEST_DIR/t.qcow2", + "encryption_key_missing": false + }, + "type": "unknown" + }, + { + "io-status": "ok", + "device": "ide1-cd0", + "locked": false, + "removable": true, + "tray_open": false, + "type": "unknown" + }, + { + "device": "floppy0", + "locked": false, + "removable": true, + "tray_open": false, + "type": "unknown" + }, + { + "device": "sd0", + "locked": false, + "removable": true, + "tray_open": false, + "type": "unknown" + } + ] +} +{ + "return": { + } +} +{ + "return": { + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "DEVICE_DELETED", + "data": { + "path": "/machine/peripheral/virtio0/virtio-backend" + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "DEVICE_DELETED", + "data": { + "device": "virtio0", + "path": "/machine/peripheral/virtio0" + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "RESET" +} +{ + "return": [ + { + "io-status": "ok", + "device": "ide1-cd0", + "locked": false, + "removable": true, + "tray_open": false, + "type": "unknown" + }, + { + "device": "floppy0", + "locked": false, + "removable": true, + "tray_open": false, + "type": "unknown" + }, + { + "device": "sd0", + "locked": false, + "removable": true, + "tray_open": false, + "type": "unknown" + } + ] +} +{ + "return": { + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "SHUTDOWN" +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "DEVICE_TRAY_MOVED", + "data": { + "device": "ide1-cd0", + "tray-open": true + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "DEVICE_TRAY_MOVED", + "data": { + "device": "floppy0", + "tray-open": true + } +} === -drive/device_add and device_del === Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,if=none,id=disk -QMP_VERSION -{"return": {}} -{"return": [{"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false, "corrupt": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}, {"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]} -{"return": {}} -{"return": {}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"path": "/machine/peripheral/virtio0/virtio-backend"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"device": "virtio0", "path": "/machine/peripheral/virtio0"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESET"} -{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "floppy0", "tray-open": true}} +{ + QMP_VERSION +} +{ + "return": { + } +} +{ + "return": [ + { + "device": "disk", + "locked": false, + "removable": true, + "inserted": { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 134217728, + "filename": "TEST_DIR/t.qcow2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": SIZE, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": false, + "backing_file_depth": 0, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "TEST_DIR/t.qcow2", + "encryption_key_missing": false + }, + "tray_open": false, + "type": "unknown" + }, + { + "io-status": "ok", + "device": "ide1-cd0", + "locked": false, + "removable": true, + "tray_open": false, + "type": "unknown" + }, + { + "device": "floppy0", + "locked": false, + "removable": true, + "tray_open": false, + "type": "unknown" + }, + { + "device": "sd0", + "locked": false, + "removable": true, + "tray_open": false, + "type": "unknown" + } + ] +} +{ + "return": { + } +} +{ + "return": { + } +} +{ + "return": { + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "DEVICE_DELETED", + "data": { + "path": "/machine/peripheral/virtio0/virtio-backend" + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "DEVICE_DELETED", + "data": { + "device": "virtio0", + "path": "/machine/peripheral/virtio0" + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "RESET" +} +{ + "return": [ + { + "io-status": "ok", + "device": "ide1-cd0", + "locked": false, + "removable": true, + "tray_open": false, + "type": "unknown" + }, + { + "device": "floppy0", + "locked": false, + "removable": true, + "tray_open": false, + "type": "unknown" + }, + { + "device": "sd0", + "locked": false, + "removable": true, + "tray_open": false, + "type": "unknown" + } + ] +} +{ + "return": { + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "SHUTDOWN" +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "DEVICE_TRAY_MOVED", + "data": { + "device": "ide1-cd0", + "tray-open": true + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "DEVICE_TRAY_MOVED", + "data": { + "device": "floppy0", + "tray-open": true + } +} === drive_add/device_add and device_del === Testing: -QMP_VERSION -{"return": {}} -{"return": "OK\r\n"} -{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false, "corrupt": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]} -{"return": {}} -{"return": {}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"path": "/machine/peripheral/virtio0/virtio-backend"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"device": "virtio0", "path": "/machine/peripheral/virtio0"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESET"} -{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "floppy0", "tray-open": true}} +{ + QMP_VERSION +} +{ + "return": { + } +} +{ + "return": "OK\r\n" +} +{ + "return": [ + { + "io-status": "ok", + "device": "ide1-cd0", + "locked": false, + "removable": true, + "tray_open": false, + "type": "unknown" + }, + { + "device": "floppy0", + "locked": false, + "removable": true, + "tray_open": false, + "type": "unknown" + }, + { + "device": "sd0", + "locked": false, + "removable": true, + "tray_open": false, + "type": "unknown" + }, + { + "device": "disk", + "locked": false, + "removable": true, + "inserted": { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 134217728, + "filename": "TEST_DIR/t.qcow2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": SIZE, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": false, + "backing_file_depth": 0, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "TEST_DIR/t.qcow2", + "encryption_key_missing": false + }, + "tray_open": false, + "type": "unknown" + } + ] +} +{ + "return": { + } +} +{ + "return": { + } +} +{ + "return": { + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "DEVICE_DELETED", + "data": { + "path": "/machine/peripheral/virtio0/virtio-backend" + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "DEVICE_DELETED", + "data": { + "device": "virtio0", + "path": "/machine/peripheral/virtio0" + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "RESET" +} +{ + "return": [ + { + "io-status": "ok", + "device": "ide1-cd0", + "locked": false, + "removable": true, + "tray_open": false, + "type": "unknown" + }, + { + "device": "floppy0", + "locked": false, + "removable": true, + "tray_open": false, + "type": "unknown" + }, + { + "device": "sd0", + "locked": false, + "removable": true, + "tray_open": false, + "type": "unknown" + } + ] +} +{ + "return": { + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "SHUTDOWN" +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "DEVICE_TRAY_MOVED", + "data": { + "device": "ide1-cd0", + "tray-open": true + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "DEVICE_TRAY_MOVED", + "data": { + "device": "floppy0", + "tray-open": true + } +} === blockdev_add/device_add and device_del === Testing: -QMP_VERSION -{"return": {}} -{"return": {}} -{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false, "corrupt": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]} -{"return": {}} -{"return": {}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"path": "/machine/peripheral/virtio0/virtio-backend"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"device": "virtio0", "path": "/machine/peripheral/virtio0"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESET"} -{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"io-status": "ok", "device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false, "corrupt": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "floppy0", "tray-open": true}} +{ + QMP_VERSION +} +{ + "return": { + } +} +{ + "return": { + } +} +{ + "return": [ + { + "io-status": "ok", + "device": "ide1-cd0", + "locked": false, + "removable": true, + "tray_open": false, + "type": "unknown" + }, + { + "device": "floppy0", + "locked": false, + "removable": true, + "tray_open": false, + "type": "unknown" + }, + { + "device": "sd0", + "locked": false, + "removable": true, + "tray_open": false, + "type": "unknown" + }, + { + "device": "disk", + "locked": false, + "removable": true, + "inserted": { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 134217728, + "filename": "TEST_DIR/t.qcow2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": SIZE, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": false, + "backing_file_depth": 0, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "TEST_DIR/t.qcow2", + "encryption_key_missing": false + }, + "tray_open": false, + "type": "unknown" + } + ] +} +{ + "return": { + } +} +{ + "return": { + } +} +{ + "return": { + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "DEVICE_DELETED", + "data": { + "path": "/machine/peripheral/virtio0/virtio-backend" + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "DEVICE_DELETED", + "data": { + "device": "virtio0", + "path": "/machine/peripheral/virtio0" + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "RESET" +} +{ + "return": [ + { + "io-status": "ok", + "device": "ide1-cd0", + "locked": false, + "removable": true, + "tray_open": false, + "type": "unknown" + }, + { + "device": "floppy0", + "locked": false, + "removable": true, + "tray_open": false, + "type": "unknown" + }, + { + "device": "sd0", + "locked": false, + "removable": true, + "tray_open": false, + "type": "unknown" + }, + { + "io-status": "ok", + "device": "disk", + "locked": false, + "removable": true, + "inserted": { + "iops_rd": 0, + "detect_zeroes": "off", + "image": { + "virtual-size": 134217728, + "filename": "TEST_DIR/t.qcow2", + "cluster-size": 65536, + "format": "qcow2", + "actual-size": SIZE, + "format-specific": { + "type": "qcow2", + "data": { + "compat": "1.1", + "lazy-refcounts": false, + "refcount-bits": 16, + "corrupt": false + } + }, + "dirty-flag": false + }, + "iops_wr": 0, + "ro": false, + "backing_file_depth": 0, + "drv": "qcow2", + "iops": 0, + "bps_wr": 0, + "write_threshold": 0, + "encrypted": false, + "bps": 0, + "bps_rd": 0, + "cache": { + "no-flush": false, + "direct": false, + "writeback": true + }, + "file": "TEST_DIR/t.qcow2", + "encryption_key_missing": false + }, + "tray_open": false, + "type": "unknown" + } + ] +} +{ + "return": { + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "SHUTDOWN" +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "DEVICE_TRAY_MOVED", + "data": { + "device": "ide1-cd0", + "tray-open": true + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "DEVICE_TRAY_MOVED", + "data": { + "device": "floppy0", + "tray-open": true + } +} *** done diff --git a/tests/qemu-iotests/068.out b/tests/qemu-iotests/068.out index abe35a9..84f19b4 100644 --- a/tests/qemu-iotests/068.out +++ b/tests/qemu-iotests/068.out @@ -2,7 +2,7 @@ QA output created by 068 === Saving and reloading a VM state to/from a qcow2 image === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072 QEMU X.Y.Z monitor - type 'help' for more information (qemu) ssasavsavesavevsavevmsavevm savevm 0 (qemu) qququiquit diff --git a/tests/qemu-iotests/069.out b/tests/qemu-iotests/069.out index b48306d..4d7e63c 100644 --- a/tests/qemu-iotests/069.out +++ b/tests/qemu-iotests/069.out @@ -2,7 +2,7 @@ QA output created by 069 === Creating an image with a backing file and deleting that file === -Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=131072 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=131072 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072 backing_file='TEST_DIR/t.IMGFMT.base' qemu-io: can't open device TEST_DIR/t.IMGFMT: Could not open backing file: Could not open 'TEST_DIR/t.IMGFMT.base': No such file or directory *** done diff --git a/tests/qemu-iotests/071 b/tests/qemu-iotests/071 index 3924e51..9eaa49b 100755 --- a/tests/qemu-iotests/071 +++ b/tests/qemu-iotests/071 @@ -51,7 +51,7 @@ function do_run_qemu() function run_qemu() { - do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp | _filter_qemu_io + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu | _filter_qmp | _filter_qemu_io } IMG_SIZE=64M @@ -63,12 +63,12 @@ echo TEST_IMG="$TEST_IMG.base" IMGOPTS="" IMGFMT="raw" _make_test_img $IMG_SIZE |\ _filter_imgfmt _make_test_img $IMG_SIZE -$QEMU_IO -c "open -o file.driver=blkverify,file.raw.filename=$TEST_IMG.base $TEST_IMG" \ +$QEMU_IO -c "open -o driver=raw,file.driver=blkverify,file.raw.filename=$TEST_IMG.base $TEST_IMG" \ -c 'read 0 512' -c 'write -P 42 0x38000 512' -c 'read -P 42 0x38000 512' | _filter_qemu_io $QEMU_IO -c 'write -P 42 0 512' "$TEST_IMG" | _filter_qemu_io -$QEMU_IO -c "open -o file.driver=blkverify,file.raw.filename=$TEST_IMG.base $TEST_IMG" \ +$QEMU_IO -c "open -o driver=raw,file.driver=blkverify,file.raw.filename=$TEST_IMG.base $TEST_IMG" \ -c 'read -P 42 0 512' | _filter_qemu_io echo @@ -78,12 +78,12 @@ echo TEST_IMG="$TEST_IMG.base" IMGOPTS="" IMGFMT="raw" _make_test_img $IMG_SIZE |\ _filter_imgfmt _make_test_img $IMG_SIZE -$QEMU_IO -c "open -o file.driver=blkverify,file.raw.filename=$TEST_IMG.base,file.test.driver=$IMGFMT,file.test.file.filename=$TEST_IMG" \ +$QEMU_IO -c "open -o driver=raw,file.driver=blkverify,file.raw.filename=$TEST_IMG.base,file.test.driver=$IMGFMT,file.test.file.filename=$TEST_IMG" \ -c 'read 0 512' -c 'write -P 42 0x38000 512' -c 'read -P 42 0x38000 512' | _filter_qemu_io $QEMU_IO -c 'write -P 42 0 512' "$TEST_IMG" | _filter_qemu_io -$QEMU_IO -c "open -o file.driver=blkverify,file.raw.filename=$TEST_IMG.base $TEST_IMG" \ +$QEMU_IO -c "open -o driver=raw,file.driver=blkverify,file.raw.filename=$TEST_IMG.base $TEST_IMG" \ -c 'read -P 42 0 512' | _filter_qemu_io echo @@ -163,7 +163,7 @@ echo echo "=== Testing blkverify on existing raw block device ===" echo -run_qemu -drive "file=$TEST_IMG.base,if=none,id=drive0" <&1 | _filter_testdir - echo -} - echo "=== Check option preallocation and cluster_size ===" echo cluster_sizes="16384 32768 65536 131072 262144 524288 1048576 2097152 4194304" for s in $cluster_sizes; do - test_qemu_img create -f $IMGFMT -o preallocation=metadata,cluster_size=$s "$TEST_IMG" 4G + IMGOPTS=$(_optstr_add "$IMGOPTS" "preallocation=metadata,cluster_size=$s") \ + _make_test_img 4G done # success, all done diff --git a/tests/qemu-iotests/079.out b/tests/qemu-iotests/079.out index ef4b8c9..6dc5d57 100644 --- a/tests/qemu-iotests/079.out +++ b/tests/qemu-iotests/079.out @@ -1,32 +1,14 @@ QA output created by 079 === Check option preallocation and cluster_size === -qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=16384 TEST_DIR/t.qcow2 4G -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=16384 preallocation='metadata' lazy_refcounts=off - -qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=32768 TEST_DIR/t.qcow2 4G -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=32768 preallocation='metadata' lazy_refcounts=off - -qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=65536 TEST_DIR/t.qcow2 4G -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=65536 preallocation='metadata' lazy_refcounts=off - -qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=131072 TEST_DIR/t.qcow2 4G -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=131072 preallocation='metadata' lazy_refcounts=off - -qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=262144 TEST_DIR/t.qcow2 4G -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=262144 preallocation='metadata' lazy_refcounts=off - -qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=524288 TEST_DIR/t.qcow2 4G -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=524288 preallocation='metadata' lazy_refcounts=off - -qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=1048576 TEST_DIR/t.qcow2 4G -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=1048576 preallocation='metadata' lazy_refcounts=off - -qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=2097152 TEST_DIR/t.qcow2 4G -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=2097152 preallocation='metadata' lazy_refcounts=off - -qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=4194304 TEST_DIR/t.qcow2 4G -qemu-img: TEST_DIR/t.qcow2: Cluster size must be a power of two between 512 and 2048k -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=4194304 preallocation='metadata' lazy_refcounts=off - +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata' +qemu-img: TEST_DIR/t.IMGFMT: Cluster size must be a power of two between 512 and 2048k +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata' *** done diff --git a/tests/qemu-iotests/080 b/tests/qemu-iotests/080 index 9de337c..a2c58ae 100755 --- a/tests/qemu-iotests/080 +++ b/tests/qemu-iotests/080 @@ -42,6 +42,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file _supported_os Linux +# Internal snapshots are (currently) impossible with refcount_bits=1 +_unsupported_imgopts 'refcount_bits=1[^0-9]' header_size=104 @@ -78,6 +80,8 @@ poke_file "$TEST_IMG" "$offset_backing_file_offset" "\xff\xff\xff\xff\xff\xff\xf poke_file "$TEST_IMG" "$offset_ext_magic" "\x12\x34\x56\x78" poke_file "$TEST_IMG" "$offset_ext_size" "\x7f\xff\xff\xff" { $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir +poke_file "$TEST_IMG" "$offset_backing_file_offset" "\x00\x00\x00\x00\x00\x00\x00\x$(printf %x $offset_ext_size)" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir poke_file "$TEST_IMG" "$offset_backing_file_offset" "\x00\x00\x00\x00\x00\x00\x00\x00" { $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir diff --git a/tests/qemu-iotests/080.out b/tests/qemu-iotests/080.out index f7a943c..6061d84 100644 --- a/tests/qemu-iotests/080.out +++ b/tests/qemu-iotests/080.out @@ -1,38 +1,40 @@ QA output created by 080 == Huge header size == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 qemu-io: can't open device TEST_DIR/t.qcow2: qcow2 header exceeds cluster size no file open, try 'help open' qemu-io: can't open device TEST_DIR/t.qcow2: qcow2 header exceeds cluster size no file open, try 'help open' == Huge unknown header extension == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 qemu-io: can't open device TEST_DIR/t.qcow2: Invalid backing file offset no file open, try 'help open' qemu-io: can't open device TEST_DIR/t.qcow2: Header extension too large no file open, try 'help open' +qemu-io: can't open device TEST_DIR/t.qcow2: Header extension too large +no file open, try 'help open' == Huge refcount table size == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 qemu-io: can't open device TEST_DIR/t.qcow2: Reference count table too large no file open, try 'help open' qemu-io: can't open device TEST_DIR/t.qcow2: Reference count table too large no file open, try 'help open' == Misaligned refcount table == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 qemu-io: can't open device TEST_DIR/t.qcow2: Invalid reference count table offset no file open, try 'help open' == Huge refcount offset == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 qemu-io: can't open device TEST_DIR/t.qcow2: Invalid reference count table offset no file open, try 'help open' == Invalid snapshot table == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 qemu-io: can't open device TEST_DIR/t.qcow2: Too many snapshots no file open, try 'help open' qemu-io: can't open device TEST_DIR/t.qcow2: Too many snapshots @@ -43,13 +45,13 @@ qemu-io: can't open device TEST_DIR/t.qcow2: Invalid snapshot table offset no file open, try 'help open' == Hitting snapshot table size limit == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 qemu-img: Could not create snapshot 'test': -27 (File too large) read 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) == Invalid L1 table == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 qemu-io: can't open device TEST_DIR/t.qcow2: Active L1 table too large no file open, try 'help open' qemu-io: can't open device TEST_DIR/t.qcow2: Active L1 table too large @@ -60,23 +62,23 @@ qemu-io: can't open device TEST_DIR/t.qcow2: Invalid L1 table offset no file open, try 'help open' == Invalid L1 table (with internal snapshot in the image) == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 qemu-img: Could not open 'TEST_DIR/t.IMGFMT': L1 table is too small == Invalid backing file size == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 qemu-io: can't open device TEST_DIR/t.qcow2: Backing file name too long no file open, try 'help open' == Invalid L2 entry (huge physical offset) == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qemu-img: Could not create snapshot 'test': -27 (File too large) qemu-img: Could not create snapshot 'test': -11 (Resource temporarily unavailable) == Invalid snapshot L1 table == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qemu-img: Failed to load snapshot: Snapshot L1 table too large diff --git a/tests/qemu-iotests/081 b/tests/qemu-iotests/081 index ed3c29e..d9b042c 100755 --- a/tests/qemu-iotests/081 +++ b/tests/qemu-iotests/081 @@ -53,15 +53,19 @@ function do_run_qemu() function run_qemu() { - do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp | _filter_qemu_io + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu | _filter_qmp | _filter_qemu_io } test_quorum=$($QEMU_IMG --help|grep quorum) [ "$test_quorum" = "" ] && _supported_fmt quorum -quorum="file.driver=quorum,file.children.0.file.filename=$TEST_DIR/1.raw" +quorum="driver=raw,file.driver=quorum,file.vote-threshold=2" +quorum="$quorum,file.children.0.file.filename=$TEST_DIR/1.raw" quorum="$quorum,file.children.1.file.filename=$TEST_DIR/2.raw" -quorum="$quorum,file.children.2.file.filename=$TEST_DIR/3.raw,file.vote-threshold=2" +quorum="$quorum,file.children.2.file.filename=$TEST_DIR/3.raw" +quorum="$quorum,file.children.0.driver=raw" +quorum="$quorum,file.children.1.driver=raw" +quorum="$quorum,file.children.2.driver=raw" echo echo "== creating quorum files ==" diff --git a/tests/qemu-iotests/081.out b/tests/qemu-iotests/081.out index 073515e..9f57d9d 100644 --- a/tests/qemu-iotests/081.out +++ b/tests/qemu-iotests/081.out @@ -1,9 +1,9 @@ QA output created by 081 == creating quorum files == -Formatting 'TEST_DIR/1.IMGFMT', fmt=IMGFMT size=10485760 -Formatting 'TEST_DIR/2.IMGFMT', fmt=IMGFMT size=10485760 -Formatting 'TEST_DIR/3.IMGFMT', fmt=IMGFMT size=10485760 +Formatting 'TEST_DIR/1.IMGFMT', fmt=IMGFMT size=10485760 +Formatting 'TEST_DIR/2.IMGFMT', fmt=IMGFMT size=10485760 +Formatting 'TEST_DIR/3.IMGFMT', fmt=IMGFMT size=10485760 == writing images == wrote 10485760/10485760 bytes at offset 0 @@ -55,5 +55,5 @@ wrote 10485760/10485760 bytes at offset 0 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) == checking that quorum is broken == -qemu-io: can't open: Could not read image for determining its format: Input/output error +read failed: Input/output error *** done diff --git a/tests/qemu-iotests/082 b/tests/qemu-iotests/082 index e64de27..c83e01e 100755 --- a/tests/qemu-iotests/082 +++ b/tests/qemu-iotests/082 @@ -60,11 +60,11 @@ _img_info # Multiple -o should be merged run_qemu_img create -f $IMGFMT -o cluster_size=4k -o lazy_refcounts=on "$TEST_IMG" $size -run_qemu_img info "$TEST_IMG" +_img_info --format-specific # If the same -o key is specified more than once, the last one wins run_qemu_img create -f $IMGFMT -o cluster_size=4k -o lazy_refcounts=on -o cluster_size=8k "$TEST_IMG" $size -run_qemu_img info "$TEST_IMG" +_img_info --format-specific run_qemu_img create -f $IMGFMT -o cluster_size=4k,cluster_size=8k "$TEST_IMG" $size _img_info @@ -114,11 +114,11 @@ TEST_IMG="${TEST_IMG}.base" _img_info # Multiple -o should be merged run_qemu_img convert -O $IMGFMT -o cluster_size=4k -o lazy_refcounts=on "$TEST_IMG" "$TEST_IMG".base -run_qemu_img info "$TEST_IMG".base +TEST_IMG="${TEST_IMG}.base" _img_info --format-specific # If the same -o key is specified more than once, the last one wins run_qemu_img convert -O $IMGFMT -o cluster_size=4k -o lazy_refcounts=on -o cluster_size=8k "$TEST_IMG" "$TEST_IMG".base -run_qemu_img info "$TEST_IMG".base +TEST_IMG="${TEST_IMG}.base" _img_info --format-specific run_qemu_img convert -O $IMGFMT -o cluster_size=4k,cluster_size=8k "$TEST_IMG" "$TEST_IMG".base TEST_IMG="${TEST_IMG}.base" _img_info @@ -157,15 +157,15 @@ echo === amend: Options specified more than once === # Last -f should win run_qemu_img amend -f foo -f $IMGFMT -o lazy_refcounts=on "$TEST_IMG" -run_qemu_img info "$TEST_IMG" +_img_info --format-specific # Multiple -o should be merged run_qemu_img amend -f $IMGFMT -o size=130M -o lazy_refcounts=off "$TEST_IMG" -run_qemu_img info "$TEST_IMG" +_img_info --format-specific # If the same -o key is specified more than once, the last one wins run_qemu_img amend -f $IMGFMT -o size=8M -o lazy_refcounts=on -o size=132M "$TEST_IMG" -run_qemu_img info "$TEST_IMG" +_img_info --format-specific run_qemu_img amend -f $IMGFMT -o size=4M,size=148M "$TEST_IMG" _img_info diff --git a/tests/qemu-iotests/082.out b/tests/qemu-iotests/082.out index 0a3ab5a..3a749b8 100644 --- a/tests/qemu-iotests/082.out +++ b/tests/qemu-iotests/082.out @@ -3,42 +3,38 @@ QA output created by 082 === create: Options specified more than once === Testing: create -f foo -f qcow2 TEST_DIR/t.qcow2 128M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 128M (134217728 bytes) cluster_size: 65536 Testing: create -f qcow2 -o cluster_size=4k -o lazy_refcounts=on TEST_DIR/t.qcow2 128M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=4096 lazy_refcounts=on - -Testing: info TEST_DIR/t.qcow2 -image: TEST_DIR/t.qcow2 -file format: qcow2 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=4096 lazy_refcounts=on refcount_bits=16 +image: TEST_DIR/t.IMGFMT +file format: IMGFMT virtual size: 128M (134217728 bytes) -disk size: 16K cluster_size: 4096 Format specific information: compat: 1.1 lazy refcounts: true + refcount bits: 16 corrupt: false Testing: create -f qcow2 -o cluster_size=4k -o lazy_refcounts=on -o cluster_size=8k TEST_DIR/t.qcow2 128M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=8192 lazy_refcounts=on - -Testing: info TEST_DIR/t.qcow2 -image: TEST_DIR/t.qcow2 -file format: qcow2 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=8192 lazy_refcounts=on refcount_bits=16 +image: TEST_DIR/t.IMGFMT +file format: IMGFMT virtual size: 128M (134217728 bytes) -disk size: 28K cluster_size: 8192 Format specific information: compat: 1.1 lazy refcounts: true + refcount bits: 16 corrupt: false Testing: create -f qcow2 -o cluster_size=4k,cluster_size=8k TEST_DIR/t.qcow2 128M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=8192 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=8192 lazy_refcounts=off refcount_bits=16 image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 128M (134217728 bytes) @@ -56,6 +52,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: create -f qcow2 -o ? TEST_DIR/t.qcow2 128M @@ -68,6 +65,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: create -f qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2 128M @@ -80,6 +78,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: create -f qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2 128M @@ -92,6 +91,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: create -f qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2 128M @@ -104,6 +104,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: create -f qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2 128M @@ -116,6 +117,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: create -f qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2 128M @@ -128,6 +130,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: create -f qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2 128M @@ -140,13 +143,14 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: create -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 128M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2,help' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2,help' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 Testing: create -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,? TEST_DIR/t.qcow2 128M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2,?' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2,?' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 Testing: create -f qcow2 -o backing_file=TEST_DIR/t.qcow2, -o help TEST_DIR/t.qcow2 128M qemu-img: Invalid option list: backing_file=TEST_DIR/t.qcow2, @@ -167,6 +171,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits Testing: create -o help Supported options: @@ -175,7 +180,7 @@ size Virtual disk size === convert: Options specified more than once === Testing: create -f qcow2 TEST_DIR/t.qcow2 128M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 Testing: convert -f foo -f qcow2 TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base image: TEST_DIR/t.IMGFMT.base @@ -189,29 +194,25 @@ virtual size: 128M (134217728 bytes) cluster_size: 65536 Testing: convert -O qcow2 -o cluster_size=4k -o lazy_refcounts=on TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base - -Testing: info TEST_DIR/t.qcow2.base -image: TEST_DIR/t.qcow2.base -file format: qcow2 +image: TEST_DIR/t.IMGFMT.base +file format: IMGFMT virtual size: 128M (134217728 bytes) -disk size: 16K cluster_size: 4096 Format specific information: compat: 1.1 lazy refcounts: true + refcount bits: 16 corrupt: false Testing: convert -O qcow2 -o cluster_size=4k -o lazy_refcounts=on -o cluster_size=8k TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base - -Testing: info TEST_DIR/t.qcow2.base -image: TEST_DIR/t.qcow2.base -file format: qcow2 +image: TEST_DIR/t.IMGFMT.base +file format: IMGFMT virtual size: 128M (134217728 bytes) -disk size: 28K cluster_size: 8192 Format specific information: compat: 1.1 lazy refcounts: true + refcount bits: 16 corrupt: false Testing: convert -O qcow2 -o cluster_size=4k,cluster_size=8k TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base @@ -232,6 +233,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: convert -O qcow2 -o ? TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base @@ -244,6 +246,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: convert -O qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base @@ -256,6 +259,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: convert -O qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base @@ -268,6 +272,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: convert -O qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base @@ -280,6 +285,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: convert -O qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base @@ -292,6 +298,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: convert -O qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base @@ -304,6 +311,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: convert -O qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base @@ -316,6 +324,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: convert -O qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base @@ -343,6 +352,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits Testing: convert -o help Supported options: @@ -351,42 +361,36 @@ size Virtual disk size === amend: Options specified more than once === Testing: amend -f foo -f qcow2 -o lazy_refcounts=on TEST_DIR/t.qcow2 - -Testing: info TEST_DIR/t.qcow2 -image: TEST_DIR/t.qcow2 -file format: qcow2 +image: TEST_DIR/t.IMGFMT +file format: IMGFMT virtual size: 128M (134217728 bytes) -disk size: 196K cluster_size: 65536 Format specific information: compat: 1.1 lazy refcounts: true + refcount bits: 16 corrupt: false Testing: amend -f qcow2 -o size=130M -o lazy_refcounts=off TEST_DIR/t.qcow2 - -Testing: info TEST_DIR/t.qcow2 -image: TEST_DIR/t.qcow2 -file format: qcow2 +image: TEST_DIR/t.IMGFMT +file format: IMGFMT virtual size: 130M (136314880 bytes) -disk size: 196K cluster_size: 65536 Format specific information: compat: 1.1 lazy refcounts: false + refcount bits: 16 corrupt: false Testing: amend -f qcow2 -o size=8M -o lazy_refcounts=on -o size=132M TEST_DIR/t.qcow2 - -Testing: info TEST_DIR/t.qcow2 -image: TEST_DIR/t.qcow2 -file format: qcow2 +image: TEST_DIR/t.IMGFMT +file format: IMGFMT virtual size: 132M (138412032 bytes) -disk size: 196K cluster_size: 65536 Format specific information: compat: 1.1 lazy refcounts: true + refcount bits: 16 corrupt: false Testing: amend -f qcow2 -o size=4M,size=148M TEST_DIR/t.qcow2 @@ -407,6 +411,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: amend -f qcow2 -o ? TEST_DIR/t.qcow2 @@ -419,6 +424,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: amend -f qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2 @@ -431,6 +437,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: amend -f qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2 @@ -443,6 +450,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: amend -f qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2 @@ -455,6 +463,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: amend -f qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2 @@ -467,6 +476,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: amend -f qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2 @@ -479,6 +489,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: amend -f qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2 @@ -491,6 +502,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 @@ -520,6 +532,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits Testing: convert -o help Supported options: diff --git a/tests/qemu-iotests/083 b/tests/qemu-iotests/083 index 991a9d9..1b2d3f1 100755 --- a/tests/qemu-iotests/083 +++ b/tests/qemu-iotests/083 @@ -56,7 +56,8 @@ filter_nbd() { # # Filter out the TCP port number since this changes between runs. sed -e 's#^.*nbd\.c:.*##g' \ - -e 's#nbd:127\.0\.0\.1:[^:]*:#nbd:127\.0\.0\.1:PORT:#g' + -e 's#nbd:127\.0\.0\.1:[^:]*:#nbd:127\.0\.0\.1:PORT:#g' \ + -e 's#\(exportname=foo\|PORT\): Failed to .*$#\1#' } check_disconnect() { diff --git a/tests/qemu-iotests/083.out b/tests/qemu-iotests/083.out index 85ee8d6..8c1441b 100644 --- a/tests/qemu-iotests/083.out +++ b/tests/qemu-iotests/083.out @@ -1,163 +1,138 @@ QA output created by 083 === Check disconnect before neg1 === - -qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not open image: Invalid argument +qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo no file open, try 'help open' === Check disconnect after neg1 === - -qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not open image: Invalid argument +qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo no file open, try 'help open' === Check disconnect 8 neg1 === - -qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not open image: Invalid argument +qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo no file open, try 'help open' === Check disconnect 16 neg1 === - -qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not open image: Invalid argument +qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo no file open, try 'help open' === Check disconnect before export === - -qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not open image: Invalid argument +qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo no file open, try 'help open' === Check disconnect after export === - -qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not open image: Invalid argument +qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo no file open, try 'help open' === Check disconnect 4 export === - -qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not open image: Invalid argument +qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo no file open, try 'help open' === Check disconnect 12 export === - -qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not open image: Invalid argument +qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo no file open, try 'help open' === Check disconnect 16 export === - -qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not open image: Invalid argument +qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo no file open, try 'help open' === Check disconnect before neg2 === - -qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not open image: Invalid argument +qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo no file open, try 'help open' === Check disconnect after neg2 === -qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not read image for determining its format: Input/output error -no file open, try 'help open' +read failed: Input/output error === Check disconnect 8 neg2 === - -qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not open image: Invalid argument +qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo no file open, try 'help open' === Check disconnect 10 neg2 === - -qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not open image: Invalid argument +qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo no file open, try 'help open' === Check disconnect before request === -qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not read image for determining its format: Input/output error -no file open, try 'help open' +read failed: Input/output error === Check disconnect after request === -qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not read image for determining its format: Input/output error -no file open, try 'help open' +read failed: Input/output error === Check disconnect before reply === -qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not read image for determining its format: Input/output error -no file open, try 'help open' +read failed: Input/output error === Check disconnect after reply === -qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not read image for determining its format: Input/output error -no file open, try 'help open' +read failed: Input/output error === Check disconnect 4 reply === -qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not read image for determining its format: Input/output error -no file open, try 'help open' +read failed: Input/output error === Check disconnect 8 reply === -qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not read image for determining its format: Input/output error -no file open, try 'help open' +read failed: Input/output error === Check disconnect before data === -qemu-io: can't open device nbd:127.0.0.1:PORT:exportname=foo: Could not read image for determining its format: Input/output error -no file open, try 'help open' +read failed: Input/output error === Check disconnect after data === -read failed: Input/output error +read 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) === Check disconnect before neg-classic === - -qemu-io: can't open device nbd:127.0.0.1:PORT: Could not open image: Invalid argument +qemu-io: can't open device nbd:127.0.0.1:PORT no file open, try 'help open' === Check disconnect 8 neg-classic === - -qemu-io: can't open device nbd:127.0.0.1:PORT: Could not open image: Invalid argument +qemu-io: can't open device nbd:127.0.0.1:PORT no file open, try 'help open' === Check disconnect 16 neg-classic === - -qemu-io: can't open device nbd:127.0.0.1:PORT: Could not open image: Invalid argument +qemu-io: can't open device nbd:127.0.0.1:PORT no file open, try 'help open' === Check disconnect 24 neg-classic === - -qemu-io: can't open device nbd:127.0.0.1:PORT: Could not open image: Invalid argument +qemu-io: can't open device nbd:127.0.0.1:PORT no file open, try 'help open' === Check disconnect 28 neg-classic === - -qemu-io: can't open device nbd:127.0.0.1:PORT: Could not open image: Invalid argument +qemu-io: can't open device nbd:127.0.0.1:PORT no file open, try 'help open' === Check disconnect after neg-classic === -qemu-io: can't open device nbd:127.0.0.1:PORT: Could not read image for determining its format: Input/output error -no file open, try 'help open' +read failed: Input/output error *** done diff --git a/tests/qemu-iotests/084.out b/tests/qemu-iotests/084.out index 5ece829..5c5ab92 100644 --- a/tests/qemu-iotests/084.out +++ b/tests/qemu-iotests/084.out @@ -2,7 +2,7 @@ QA output created by 084 === Statically allocated image creation === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 64M (67108864 bytes) @@ -11,7 +11,7 @@ disk image file size in bytes: 67109888 === Testing image size bounds === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 64M (67108864 bytes) diff --git a/tests/qemu-iotests/085.out b/tests/qemu-iotests/085.out index 0f2b17f..5eb8b94 100644 --- a/tests/qemu-iotests/085.out +++ b/tests/qemu-iotests/085.out @@ -11,7 +11,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 === Create a single snapshot on virtio0 === -Formatting 'TEST_DIR/1-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2.orig' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/1-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2.orig' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} === Invalid command - missing device and nodename === @@ -25,31 +25,31 @@ Formatting 'TEST_DIR/1-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file === Create several transactional group snapshots === -Formatting 'TEST_DIR/2-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/1-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off -Formatting 'TEST_DIR/2-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/2-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/1-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/2-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} -Formatting 'TEST_DIR/3-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/2-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off -Formatting 'TEST_DIR/3-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/2-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/3-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/2-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/3-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/2-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} -Formatting 'TEST_DIR/4-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/3-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off -Formatting 'TEST_DIR/4-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/3-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/4-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/3-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/4-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/3-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} -Formatting 'TEST_DIR/5-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/4-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off -Formatting 'TEST_DIR/5-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/4-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/5-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/4-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/5-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/4-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} -Formatting 'TEST_DIR/6-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/5-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off -Formatting 'TEST_DIR/6-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/5-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/6-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/5-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/6-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/5-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} -Formatting 'TEST_DIR/7-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/6-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off -Formatting 'TEST_DIR/7-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/6-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/7-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/6-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/7-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/6-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} -Formatting 'TEST_DIR/8-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/7-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off -Formatting 'TEST_DIR/8-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/7-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/8-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/7-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/8-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/7-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} -Formatting 'TEST_DIR/9-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/8-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off -Formatting 'TEST_DIR/9-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/8-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/9-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/8-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/9-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/8-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} -Formatting 'TEST_DIR/10-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/9-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off -Formatting 'TEST_DIR/10-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/9-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/10-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/9-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/10-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/9-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} *** done diff --git a/tests/qemu-iotests/086.out b/tests/qemu-iotests/086.out index 9c0bf23..5ff9961 100644 --- a/tests/qemu-iotests/086.out +++ b/tests/qemu-iotests/086.out @@ -1,5 +1,5 @@ QA output created by 086 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 1048576/1048576 bytes at offset 0 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 1048576/1048576 bytes at offset 2097152 diff --git a/tests/qemu-iotests/087 b/tests/qemu-iotests/087 index d7454d1..8694749 100755 --- a/tests/qemu-iotests/087 +++ b/tests/qemu-iotests/087 @@ -45,7 +45,8 @@ function do_run_qemu() function run_qemu() { - do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp | sed -e 's/\("actual-size":\s*\)[0-9]\+/\1SIZE/g' + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp | _filter_qemu \ + | sed -e 's/\("actual-size":\s*\)[0-9]\+/\1SIZE/g' } size=128M diff --git a/tests/qemu-iotests/087.out b/tests/qemu-iotests/087.out index e8795b3..c71bb3a 100644 --- a/tests/qemu-iotests/087.out +++ b/tests/qemu-iotests/087.out @@ -1,5 +1,5 @@ QA output created by 087 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 === Missing ID === @@ -21,10 +21,9 @@ QMP_VERSION {"return": {}} {"error": {"class": "GenericError", "desc": "Device with id 'disk' already exists"}} {"error": {"class": "GenericError", "desc": "Device name 'test-node' conflicts with an existing node name"}} -main-loop: WARNING: I/O thread spun for 1000 iterations -{"error": {"class": "GenericError", "desc": "could not open disk image disk2: node-name=disk is conflicting with a device id"}} -{"error": {"class": "GenericError", "desc": "could not open disk image disk2: Duplicate node name"}} -{"error": {"class": "GenericError", "desc": "could not open disk image disk3: node-name=disk3 is conflicting with a device id"}} +{"error": {"class": "GenericError", "desc": "node-name=disk is conflicting with a device id"}} +{"error": {"class": "GenericError", "desc": "Duplicate node name"}} +{"error": {"class": "GenericError", "desc": "node-name=disk3 is conflicting with a device id"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}} @@ -45,10 +44,19 @@ QMP_VERSION === Encrypted image === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on +qemu-img: Encrypted images are deprecated +Support for them will be removed in a future release. +You can use 'qemu-img convert' to convert your image to an unencrypted one. +qemu-img: Encrypted images are deprecated +Support for them will be removed in a future release. +You can use 'qemu-img convert' to convert your image to an unencrypted one. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on Testing: -S QMP_VERSION {"return": {}} +Encrypted images are deprecated +Support for them will be removed in a future release. +You can use 'qemu-img convert' to convert your image to an unencrypted one. {"error": {"class": "GenericError", "desc": "blockdev-add doesn't support encrypted devices"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"} @@ -58,7 +66,10 @@ QMP_VERSION Testing: QMP_VERSION {"return": {}} -{"error": {"class": "GenericError", "desc": "could not open disk image disk: Guest must be stopped for opening of encrypted image"}} +Encrypted images are deprecated +Support for them will be removed in a future release. +You can use 'qemu-img convert' to convert your image to an unencrypted one. +{"error": {"class": "GenericError", "desc": "Guest must be stopped for opening of encrypted image"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}} @@ -67,7 +78,13 @@ QMP_VERSION === Missing driver === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on +qemu-img: Encrypted images are deprecated +Support for them will be removed in a future release. +You can use 'qemu-img convert' to convert your image to an unencrypted one. +qemu-img: Encrypted images are deprecated +Support for them will be removed in a future release. +You can use 'qemu-img convert' to convert your image to an unencrypted one. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on Testing: -S QMP_VERSION {"return": {}} diff --git a/tests/qemu-iotests/088.out b/tests/qemu-iotests/088.out index d961609..6e6bfca 100644 --- a/tests/qemu-iotests/088.out +++ b/tests/qemu-iotests/088.out @@ -1,7 +1,7 @@ QA output created by 088 == Invalid block size == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 qemu-io: can't open device TEST_DIR/t.vpc: Invalid block size 0 no file open, try 'help open' qemu-io: can't open device TEST_DIR/t.vpc: Invalid block size 0 diff --git a/tests/qemu-iotests/089 b/tests/qemu-iotests/089 index dffc977..3e0038d 100755 --- a/tests/qemu-iotests/089 +++ b/tests/qemu-iotests/089 @@ -41,6 +41,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file _supported_os Linux +# Because anything other than 16 would change the output of qemu_io -c info +_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' # Using an image filename containing quotation marks will render the JSON data # below invalid. In that case, we have little choice but simply not to run this @@ -65,7 +67,8 @@ $QEMU_IO -c 'write -P 42 0 512' -c 'write -P 23 512 512' \ $QEMU_IMG convert -f raw -O $IMGFMT "$TEST_IMG.base" "$TEST_IMG" -$QEMU_IO -c 'read -P 42 0 512' -c 'read -P 23 512 512' \ +$QEMU_IO_PROG --cache $CACHEMODE \ + -c 'read -P 42 0 512' -c 'read -P 23 512 512' \ -c 'read -P 66 1024 512' "json:{ \"driver\": \"$IMGFMT\", \"file\": { @@ -91,7 +94,8 @@ $QEMU_IO -c 'write -P 42 0x38000 512' "$TEST_IMG" | _filter_qemu_io # The "image.filename" part tests whether "a": { "b": "c" } and "a.b": "c" do # the same (which they should). -$QEMU_IO -c 'read -P 42 0x38000 512' "json:{ +$QEMU_IO_PROG --cache $CACHEMODE \ + -c 'read -P 42 0x38000 512' "json:{ \"driver\": \"$IMGFMT\", \"file\": { \"driver\": \"blkdebug\", diff --git a/tests/qemu-iotests/089.out b/tests/qemu-iotests/089.out index b2b0390..5b541a3 100644 --- a/tests/qemu-iotests/089.out +++ b/tests/qemu-iotests/089.out @@ -2,7 +2,7 @@ QA output created by 089 === Testing nested image formats === -Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 512/512 bytes at offset 512 @@ -21,9 +21,11 @@ read 512/512 bytes at offset 0 === Testing blkdebug === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 512/512 bytes at offset 229376 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Failed to flush the L2 table cache: Input/output error +Failed to flush the refcount block cache: Input/output error read failed: Input/output error === Testing qemu-img info output === @@ -41,6 +43,7 @@ vm state offset: 512 MiB Format specific information: compat: 1.1 lazy refcounts: false + refcount bits: 16 corrupt: false format name: IMGFMT cluster size: 64 KiB @@ -48,5 +51,6 @@ vm state offset: 512 MiB Format specific information: compat: 1.1 lazy refcounts: false + refcount bits: 16 corrupt: false *** done diff --git a/tests/qemu-iotests/090.out b/tests/qemu-iotests/090.out index 2df93e0..66ca5d5 100644 --- a/tests/qemu-iotests/090.out +++ b/tests/qemu-iotests/090.out @@ -1,5 +1,5 @@ QA output created by 090 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072 wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 65536/65536 bytes at offset 65536 diff --git a/tests/qemu-iotests/091.out b/tests/qemu-iotests/091.out index a2e0122..5017f8c 100644 --- a/tests/qemu-iotests/091.out +++ b/tests/qemu-iotests/091.out @@ -1,5 +1,5 @@ QA output created by 091 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 === Starting QEMU VM1 === diff --git a/tests/qemu-iotests/092.out b/tests/qemu-iotests/092.out index 496d8f0..c5c60f9 100644 --- a/tests/qemu-iotests/092.out +++ b/tests/qemu-iotests/092.out @@ -1,7 +1,7 @@ QA output created by 092 == Invalid cluster size == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 qemu-io: can't open device TEST_DIR/t.qcow: Cluster size must be between 512 and 64k no file open, try 'help open' qemu-io: can't open device TEST_DIR/t.qcow: Cluster size must be between 512 and 64k @@ -12,7 +12,7 @@ qemu-io: can't open device TEST_DIR/t.qcow: Cluster size must be between 512 and no file open, try 'help open' == Invalid L2 table size == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 qemu-io: can't open device TEST_DIR/t.qcow: L2 table size must be between 512 and 64k no file open, try 'help open' qemu-io: can't open device TEST_DIR/t.qcow: L2 table size must be between 512 and 64k @@ -23,14 +23,14 @@ qemu-io: can't open device TEST_DIR/t.qcow: L2 table size must be between 512 an no file open, try 'help open' == Invalid size == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 qemu-io: can't open device TEST_DIR/t.qcow: Image too large no file open, try 'help open' qemu-io: can't open device TEST_DIR/t.qcow: Image too large no file open, try 'help open' == Invalid backing file length == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 qemu-io: can't open device TEST_DIR/t.qcow: Backing file name too long no file open, try 'help open' qemu-io: can't open device TEST_DIR/t.qcow: Backing file name too long diff --git a/tests/qemu-iotests/093 b/tests/qemu-iotests/093 new file mode 100755 index 0000000..b9096a5 --- /dev/null +++ b/tests/qemu-iotests/093 @@ -0,0 +1,114 @@ +#!/usr/bin/env python +# +# Tests for IO throttling +# +# Copyright (C) 2015 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 . +# + +import iotests + +class ThrottleTestCase(iotests.QMPTestCase): + test_img = "null-aio://" + + def blockstats(self, device): + result = self.vm.qmp("query-blockstats") + for r in result['return']: + if r['device'] == device: + stat = r['stats'] + return stat['rd_bytes'], stat['rd_operations'], stat['wr_bytes'], stat['wr_operations'] + raise Exception("Device not found for blockstats: %s" % device) + + def setUp(self): + self.vm = iotests.VM().add_drive(self.test_img) + self.vm.launch() + + def tearDown(self): + self.vm.shutdown() + + def do_test_throttle(self, seconds, params): + def check_limit(limit, num): + # IO throttling algorithm is discrete, allow 10% error so the test + # is more robust + return limit == 0 or \ + (num < seconds * limit * 1.1 + and num > seconds * limit * 0.9) + + nsec_per_sec = 1000000000 + + params['device'] = 'drive0' + + result = self.vm.qmp("block_set_io_throttle", conv_keys=False, **params) + self.assert_qmp(result, 'return', {}) + + # Set vm clock to a known value + ns = seconds * nsec_per_sec + self.vm.qtest("clock_step %d" % ns) + + # Submit enough requests. They will drain bps_max and iops_max, but the + # rest requests won't get executed until we advance the virtual clock + # with qtest interface + rq_size = 512 + rd_nr = max(params['bps'] / rq_size / 2, + params['bps_rd'] / rq_size, + params['iops'] / 2, + params['iops_rd']) + rd_nr *= seconds * 2 + wr_nr = max(params['bps'] / rq_size / 2, + params['bps_wr'] / rq_size, + params['iops'] / 2, + params['iops_wr']) + wr_nr *= seconds * 2 + for i in range(rd_nr): + self.vm.hmp_qemu_io("drive0", "aio_read %d %d" % (i * rq_size, rq_size)) + for i in range(wr_nr): + self.vm.hmp_qemu_io("drive0", "aio_write %d %d" % (i * rq_size, rq_size)) + + start_rd_bytes, start_rd_iops, start_wr_bytes, start_wr_iops = self.blockstats('drive0') + + self.vm.qtest("clock_step %d" % ns) + end_rd_bytes, end_rd_iops, end_wr_bytes, end_wr_iops = self.blockstats('drive0') + + rd_bytes = end_rd_bytes - start_rd_bytes + rd_iops = end_rd_iops - start_rd_iops + wr_bytes = end_wr_bytes - start_wr_bytes + wr_iops = end_wr_iops - start_wr_iops + + self.assertTrue(check_limit(params['bps'], rd_bytes + wr_bytes)) + self.assertTrue(check_limit(params['bps_rd'], rd_bytes)) + self.assertTrue(check_limit(params['bps_wr'], wr_bytes)) + self.assertTrue(check_limit(params['iops'], rd_iops + wr_iops)) + self.assertTrue(check_limit(params['iops_rd'], rd_iops)) + self.assertTrue(check_limit(params['iops_wr'], wr_iops)) + + def test_all(self): + params = {"bps": 4096, + "bps_rd": 4096, + "bps_wr": 4096, + "iops": 10, + "iops_rd": 10, + "iops_wr": 10, + } + # Pick each out of all possible params and test + for tk in params: + limits = dict([(k, 0) for k in params]) + limits[tk] = params[tk] + self.do_test_throttle(5, limits) + +class ThrottleTestCoroutine(ThrottleTestCase): + test_img = "null-co://" + +if __name__ == '__main__': + iotests.main(supported_fmts=["raw"]) diff --git a/tests/qemu-iotests/093.out b/tests/qemu-iotests/093.out new file mode 100644 index 0000000..fbc63e6 --- /dev/null +++ b/tests/qemu-iotests/093.out @@ -0,0 +1,5 @@ +.. +---------------------------------------------------------------------- +Ran 2 tests + +OK diff --git a/tests/qemu-iotests/094 b/tests/qemu-iotests/094 new file mode 100755 index 0000000..27a2be2 --- /dev/null +++ b/tests/qemu-iotests/094 @@ -0,0 +1,81 @@ +#!/bin/bash +# +# Test case for drive-mirror to NBD (especially bdrv_swap() on NBD BDS) +# +# Copyright (C) 2015 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 . +# + +# creator +owner=mreitz@redhat.com + +seq="$(basename $0)" +echo "QA output created by $seq" + +here="$PWD" +tmp=/tmp/$$ +status=1 # failure is the default! + +trap "exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.qemu + +_supported_fmt generic +_supported_proto nbd +_supported_os Linux +_unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat" + +_make_test_img 64M +$QEMU_IMG create -f $IMGFMT "$TEST_DIR/source.$IMGFMT" 64M | _filter_img_create + +_launch_qemu -drive if=none,id=src,file="$TEST_DIR/source.$IMGFMT",format=raw \ + -nodefaults + +_send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'qmp_capabilities'}" \ + 'return' + +# 'format': 'nbd' is not actually "correct", but this is probably the only way +# to test bdrv_swap() on an NBD BDS +_send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'drive-mirror', + 'arguments': {'device': 'src', + 'target': '$TEST_IMG', + 'format': 'nbd', + 'sync':'full', + 'mode':'existing'}}" \ + 'BLOCK_JOB_READY' + +_send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'block-job-complete', + 'arguments': {'device': 'src'}}" \ + 'BLOCK_JOB_COMPLETE' + +_send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'quit'}" \ + 'return' + +wait=1 _cleanup_qemu + +_cleanup_test_img +rm -f "$TEST_DIR/source.$IMGFMT" + +# success, all done +echo '*** done' +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/094.out b/tests/qemu-iotests/094.out new file mode 100644 index 0000000..b66dc07 --- /dev/null +++ b/tests/qemu-iotests/094.out @@ -0,0 +1,11 @@ +QA output created by 094 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/source.IMGFMT', fmt=IMGFMT size=67108864 +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"} +*** done diff --git a/tests/qemu-iotests/095.out b/tests/qemu-iotests/095.out index cc86efa..267c483 100644 --- a/tests/qemu-iotests/095.out +++ b/tests/qemu-iotests/095.out @@ -1,7 +1,7 @@ QA output created by 095 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=5242880 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=104857600 backing_file='TEST_DIR/t.IMGFMT.base' -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=104857600 backing_file='TEST_DIR/t.IMGFMT.snp1' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=5242880 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=104857600 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=104857600 backing_file='TEST_DIR/t.IMGFMT.snp1' === Base image info before commit and resize === image: TEST_DIR/t.IMGFMT.base diff --git a/tests/qemu-iotests/097.out b/tests/qemu-iotests/097.out index 1cb7764..81651f4 100644 --- a/tests/qemu-iotests/097.out +++ b/tests/qemu-iotests/097.out @@ -2,9 +2,9 @@ QA output created by 097 === Test pass 0 === -Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 -Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.itmd' +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.itmd' wrote 196608/196608 bytes at offset 0 192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 131072/131072 bytes at offset 65536 @@ -31,9 +31,9 @@ Offset Length File === Test pass 1 === -Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 -Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.itmd' +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.itmd' wrote 196608/196608 bytes at offset 0 192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 131072/131072 bytes at offset 65536 @@ -61,9 +61,9 @@ Offset Length File === Test pass 2 === -Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 -Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.itmd' +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.itmd' wrote 196608/196608 bytes at offset 0 192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 131072/131072 bytes at offset 65536 @@ -91,9 +91,9 @@ Offset Length File === Test pass 3 === -Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 -Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.itmd' +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.itmd' wrote 196608/196608 bytes at offset 0 192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 131072/131072 bytes at offset 65536 diff --git a/tests/qemu-iotests/098.out b/tests/qemu-iotests/098.out index 586dfc8..e08a189 100644 --- a/tests/qemu-iotests/098.out +++ b/tests/qemu-iotests/098.out @@ -2,8 +2,8 @@ QA output created by 098 === l1_update === -Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qemu-img: Could not empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error @@ -11,8 +11,8 @@ No errors were found on the image. === empty_image_prepare === -Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qemu-img: Could not empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error @@ -24,8 +24,8 @@ No errors were found on the image. === reftable_update === -Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qemu-img: Could not empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error @@ -38,8 +38,8 @@ No errors were found on the image. === refblock_alloc === -Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qemu-img: Could not empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error diff --git a/tests/qemu-iotests/099 b/tests/qemu-iotests/099 index ffc7ea7..80f3d9a 100755 --- a/tests/qemu-iotests/099 +++ b/tests/qemu-iotests/099 @@ -44,7 +44,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow qcow2 qed vdi vhdx vmdk vpc _supported_proto file _supported_os Linux - +_unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat" \ + "subformat=twoGbMaxExtentSparse" function do_run_qemu() { @@ -56,7 +57,7 @@ function run_qemu() # Get the "file": "foo" entry ($foo may only contain escaped double quotes, # which is how we can extract it) do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_imgfmt | _filter_qmp \ - | grep "drv0" \ + | _filter_qemu | grep "drv0" \ | sed -e 's/^.*"file": "\(\(\\"\|[^"]\)*\)".*$/\1/' -e 's/\\"/"/g' } @@ -107,8 +108,21 @@ echo # generate a JSON object here as well test_qemu "file.driver=blkverify,file.raw.filename=$TEST_IMG.compare,file.test.file.driver=blkdebug,file.test.file.image.filename=$TEST_IMG,file.test.file.inject-error.0.event=l1_update" +echo +echo '=== Testing plain filename for blkdebug ===' +echo + +touch "$TEST_DIR/blkdebug.conf" +test_qemu "file.driver=blkdebug,file.config=$TEST_DIR/blkdebug.conf,file.image.filename=$TEST_IMG" + +echo +echo '=== Testing plain filename for blkdebug without configuration file ===' +echo + +test_qemu "file.driver=blkdebug,file.image.filename=$TEST_IMG" + -rm -f "$TEST_IMG.compare" +rm -f "$TEST_IMG.compare" "$TEST_DIR/blkdebug.conf" # success, all done echo "*** done" diff --git a/tests/qemu-iotests/099.out b/tests/qemu-iotests/099.out index 55be4d4..8cce627 100644 --- a/tests/qemu-iotests/099.out +++ b/tests/qemu-iotests/099.out @@ -1,6 +1,6 @@ QA output created by 099 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072 -Formatting 'TEST_DIR/t.IMGFMT.compare', fmt=raw size=131072 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072 +Formatting 'TEST_DIR/t.IMGFMT.compare', fmt=raw size=131072 === Testing simple filename for blkverify === @@ -12,9 +12,17 @@ blkverify:TEST_DIR/t.IMGFMT.compare:TEST_DIR/t.IMGFMT === Testing JSON filename for blkdebug === -json:{"driver": "IMGFMT", "file": {"inject-error": [{"immediately": false, "once": false, "state": 0, "sector": -1, "event": "l1_update", "errno": 5}], "image": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "driver": "blkdebug"}} +json:{"driver": "IMGFMT", "file": {"image": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "driver": "blkdebug", "inject-error.0.event": "l1_update"}} === Testing indirectly enforced JSON filename === -json:{"driver": "raw", "file": {"test": {"driver": "IMGFMT", "file": {"inject-error": [{"immediately": false, "once": false, "state": 0, "sector": -1, "event": "l1_update", "errno": 5}], "image": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "driver": "blkdebug"}}, "driver": "blkverify", "raw": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT.compare"}}} +json:{"driver": "raw", "file": {"test": {"driver": "IMGFMT", "file": {"image": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "driver": "blkdebug", "inject-error.0.event": "l1_update"}}, "driver": "blkverify", "raw": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT.compare"}}} + +=== Testing plain filename for blkdebug === + +blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT + +=== Testing plain filename for blkdebug without configuration file === + +blkdebug::TEST_DIR/t.IMGFMT *** done diff --git a/tests/qemu-iotests/100 b/tests/qemu-iotests/100 index 9124aba..7c1b235 100755 --- a/tests/qemu-iotests/100 +++ b/tests/qemu-iotests/100 @@ -55,6 +55,8 @@ echo "== verify pattern ==" $QEMU_IO -c "read -P 0xcd 0 4k" "$TEST_IMG" | _filter_qemu_io $QEMU_IO -c "read -P 0 4k 4k" "$TEST_IMG" | _filter_qemu_io +_cleanup_test_img + echo echo "== Sequential requests ==" _make_test_img $size @@ -66,6 +68,8 @@ $QEMU_IO -c "read -P 0xcd 0 4k" "$TEST_IMG" | _filter_qemu_io $QEMU_IO -c "read -P 0xce 4k 4k" "$TEST_IMG" | _filter_qemu_io $QEMU_IO -c "read -P 0 8k 4k" "$TEST_IMG" | _filter_qemu_io +_cleanup_test_img + echo echo "== Superset overlapping requests ==" _make_test_img $size @@ -79,6 +83,8 @@ $QEMU_IO -c "read -P 0xcd 0 1k" "$TEST_IMG" | _filter_qemu_io $QEMU_IO -c "read -P 0xcd 3k 1k" "$TEST_IMG" | _filter_qemu_io $QEMU_IO -c "read -P 0 4k 4k" "$TEST_IMG" | _filter_qemu_io +_cleanup_test_img + echo echo "== Subset overlapping requests ==" _make_test_img $size @@ -92,6 +98,8 @@ $QEMU_IO -c "read -P 0xce 0 1k" "$TEST_IMG" | _filter_qemu_io $QEMU_IO -c "read -P 0xce 3k 1k" "$TEST_IMG" | _filter_qemu_io $QEMU_IO -c "read -P 0 4k 4k" "$TEST_IMG" | _filter_qemu_io +_cleanup_test_img + echo echo "== Head overlapping requests ==" _make_test_img $size @@ -104,6 +112,8 @@ echo "== verify pattern ==" $QEMU_IO -c "read -P 0xce 2k 2k" "$TEST_IMG" | _filter_qemu_io $QEMU_IO -c "read -P 0 4k 4k" "$TEST_IMG" | _filter_qemu_io +_cleanup_test_img + echo echo "== Tail overlapping requests ==" _make_test_img $size @@ -116,6 +126,8 @@ echo "== verify pattern ==" $QEMU_IO -c "read -P 0xce 0k 2k" "$TEST_IMG" | _filter_qemu_io $QEMU_IO -c "read -P 0 4k 4k" "$TEST_IMG" | _filter_qemu_io +_cleanup_test_img + echo echo "== Disjoint requests ==" _make_test_img $size diff --git a/tests/qemu-iotests/100.out b/tests/qemu-iotests/100.out index 2d6e9f0..0564903 100644 --- a/tests/qemu-iotests/100.out +++ b/tests/qemu-iotests/100.out @@ -1,7 +1,7 @@ QA output created by 100 == Single request == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -12,7 +12,7 @@ read 4096/4096 bytes at offset 4096 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) == Sequential requests == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 8192/8192 bytes at offset 0 8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -25,7 +25,7 @@ read 4096/4096 bytes at offset 8192 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) == Superset overlapping requests == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 6144/6144 bytes at offset 0 6 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -38,7 +38,7 @@ read 4096/4096 bytes at offset 4096 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) == Subset overlapping requests == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 6144/6144 bytes at offset 1024 6 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -51,7 +51,7 @@ read 4096/4096 bytes at offset 4096 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) == Head overlapping requests == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 6144/6144 bytes at offset 0 6 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -62,7 +62,7 @@ read 4096/4096 bytes at offset 4096 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) == Tail overlapping requests == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 6144/6144 bytes at offset 2048 6 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -73,7 +73,7 @@ read 4096/4096 bytes at offset 4096 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) == Disjoint requests == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 8192/8192 bytes at offset 0 8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/tests/qemu-iotests/103 b/tests/qemu-iotests/103 index ccab551..fa9a3c1 100755 --- a/tests/qemu-iotests/103 +++ b/tests/qemu-iotests/103 @@ -93,6 +93,16 @@ $QEMU_IO -c "open -o l2-cache-size=1M,refcount-cache-size=0.25M $TEST_IMG" \ -c 'read -P 42 0 64k' \ | _filter_qemu_io +echo +echo '=== Testing minimal L2 cache and COW ===' +echo + +$QEMU_IMG snapshot -c foo "$TEST_IMG" +# This requires a COW operation, which accesses two L2 tables simultaneously +# (COW source and destination), so there must be enough space in the cache to +# place both tables there (and qemu should not crash) +$QEMU_IO -c "open -o cache-size=0 $TEST_IMG" -c 'write 0 64k' | _filter_qemu_io + # success, all done echo '*** done' rm -f $seq.full diff --git a/tests/qemu-iotests/103.out b/tests/qemu-iotests/103.out index ddf6b5a..d05f49f 100644 --- a/tests/qemu-iotests/103.out +++ b/tests/qemu-iotests/103.out @@ -1,5 +1,5 @@ QA output created by 103 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65536 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65536 wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -26,4 +26,9 @@ read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +=== Testing minimal L2 cache and COW === + +wrote 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) *** done diff --git a/tests/qemu-iotests/104 b/tests/qemu-iotests/104 index b471aa5..2e35ea8 100755 --- a/tests/qemu-iotests/104 +++ b/tests/qemu-iotests/104 @@ -28,17 +28,13 @@ here=`pwd` tmp=/tmp/$$ status=1 # failure is the default! -_cleanup() -{ - _cleanup_test_img -} -trap "_cleanup; exit \$status" 0 1 2 3 15 +trap "exit \$status" 0 1 2 3 15 # get standard environment, filters and checks . ./common.rc . ./common.filter -_supported_fmt generic +_supported_fmt raw qcow qcow2 qed vdi vmdk vhdx _supported_proto generic _supported_os Linux @@ -47,8 +43,9 @@ echo image_sizes="1024 1234" for s in $image_sizes; do - _make_test_img $s | _filter_img_create + _make_test_img $s _img_info | _filter_img_info + _cleanup_test_img done # success, all done diff --git a/tests/qemu-iotests/104.out b/tests/qemu-iotests/104.out index de27852..ab8d892 100644 --- a/tests/qemu-iotests/104.out +++ b/tests/qemu-iotests/104.out @@ -1,11 +1,11 @@ QA output created by 104 === Check qemu-img info output === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1024 image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 1.0K (1024 bytes) -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1234 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1234 image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 1.5K (1536 bytes) diff --git a/tests/qemu-iotests/107.out b/tests/qemu-iotests/107.out index 93445b7..24f85d8 100644 --- a/tests/qemu-iotests/107.out +++ b/tests/qemu-iotests/107.out @@ -2,7 +2,7 @@ QA output created by 107 === Updates should not write random data === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65536 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65536 wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 65528/65528 bytes at offset 196616 diff --git a/tests/qemu-iotests/108 b/tests/qemu-iotests/108 index 12fc92a..ce44749 100755 --- a/tests/qemu-iotests/108 +++ b/tests/qemu-iotests/108 @@ -43,6 +43,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file _supported_os Linux +# This test directly modifies a refblock so it relies on refcount_bits being 16 +_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' echo echo '=== Repairing an image without any refcount table ===' diff --git a/tests/qemu-iotests/108.out b/tests/qemu-iotests/108.out index 824d5cf..75bab8d 100644 --- a/tests/qemu-iotests/108.out +++ b/tests/qemu-iotests/108.out @@ -2,7 +2,7 @@ QA output created by 108 === Repairing an image without any refcount table === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ERROR cluster 0 refcount=0 reference=1 @@ -22,7 +22,7 @@ read 65536/65536 bytes at offset 0 === Repairing unreferenced data cluster in new refblock area === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 111104/111104 bytes at offset 0 108.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 131072 @@ -49,7 +49,7 @@ read 512/512 bytes at offset 111104 --- Otherwise clean --- -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Repairing refcount block 1 is outside image The following inconsistencies were found and repaired: @@ -61,7 +61,7 @@ No errors were found on the image. --- Refblock is unallocated --- -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Repairing refcount block 1 is outside image ERROR cluster 16 refcount=0 reference=1 Rebuilding refcount structure @@ -78,7 +78,7 @@ No errors were found on the image. --- Signed overflow after the refblock --- -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Repairing refcount block 1 is outside image ERROR could not resize image: Invalid argument Rebuilding refcount structure @@ -94,7 +94,7 @@ No errors were found on the image. --- Unsigned overflow after the refblock --- -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Repairing refcount block 1 is outside image ERROR could not resize image: Invalid argument Rebuilding refcount structure diff --git a/tests/qemu-iotests/109 b/tests/qemu-iotests/109 new file mode 100755 index 0000000..0b668da --- /dev/null +++ b/tests/qemu-iotests/109 @@ -0,0 +1,132 @@ +#!/bin/bash +# +# Test writing image headers of other formats into raw images +# +# Copyright (C) 2014 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 . +# + +# creator +owner=kwolf@redhat.com + +seq="$(basename $0)" +echo "QA output created by $seq" + +here="$PWD" +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + rm -f $TEST_IMG.src + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.qemu + +_supported_fmt raw +_supported_proto file +_supported_os Linux + +qemu_comm_method=qmp + +function run_qemu() +{ + local raw_img="$1" + local source_img="$2" + local qmp_format="$3" + local qmp_event="$4" + + _launch_qemu -drive file="${source_img}",format=raw,cache=${CACHEMODE},id=src + _send_qemu_cmd $QEMU_HANDLE "{ 'execute': 'qmp_capabilities' }" "return" + + _send_qemu_cmd $QEMU_HANDLE \ + "{'execute':'drive-mirror', 'arguments':{ + 'device': 'src', 'target': '$raw_img', $qmp_format + 'mode': 'existing', 'sync': 'full'}}" \ + "return" + + _send_qemu_cmd $QEMU_HANDLE '' "$qmp_event" + _send_qemu_cmd $QEMU_HANDLE '{"execute":"query-block-jobs"}' "return" + _cleanup_qemu +} + +for fmt in qcow qcow2 qed vdi vmdk vpc; do + + echo + echo "=== Writing a $fmt header into raw ===" + echo + + _make_test_img 64M + TEST_IMG="$TEST_IMG.src" IMGFMT=$fmt _make_test_img 64M + + # This first test should fail: The image format was probed, we may not + # write an image header at the start of the image + run_qemu "$TEST_IMG" "$TEST_IMG.src" "" "BLOCK_JOB_ERROR" + $QEMU_IO -c 'read -P 0 0 64k' "$TEST_IMG" | _filter_qemu_io + + + # When raw was explicitly specified, the same must succeed + run_qemu "$TEST_IMG" "$TEST_IMG.src" "'format': 'raw'," "BLOCK_JOB_READY" + $QEMU_IMG compare -f raw -F raw "$TEST_IMG" "$TEST_IMG.src" + +done + + +for sample_img in empty.bochs iotest-dirtylog-10G-4M.vhdx parallels-v1 \ + simple-pattern.cloop; do + + echo + echo "=== Copying sample image $sample_img into raw ===" + echo + + # Can't use _use_sample_img because that isn't designed to be used multiple + # times and it overwrites $TEST_IMG (both breaks cleanup) + _make_test_img 64M + bzcat "$SAMPLE_IMG_DIR/$sample_img.bz2" > "$TEST_IMG.src" + + run_qemu "$TEST_IMG" "$TEST_IMG.src" "" "BLOCK_JOB_ERROR" + $QEMU_IO -c 'read -P 0 0 64k' "$TEST_IMG" | _filter_qemu_io + + run_qemu "$TEST_IMG" "$TEST_IMG.src" "'format': 'raw'," "BLOCK_JOB_READY" + # qemu-img compare can't handle unaligned file sizes + $QEMU_IMG resize -f raw "$TEST_IMG.src" +0 + $QEMU_IMG compare -f raw -F raw "$TEST_IMG" "$TEST_IMG.src" +done + +echo +echo "=== Write legitimate MBR into raw ===" +echo + +for sample_img in grub_mbr.raw; do + _make_test_img 64M + bzcat "$SAMPLE_IMG_DIR/$sample_img.bz2" > "$TEST_IMG.src" + + run_qemu "$TEST_IMG" "$TEST_IMG.src" "" "BLOCK_JOB_READY" + $QEMU_IMG compare -f raw -F raw "$TEST_IMG" "$TEST_IMG.src" + + run_qemu "$TEST_IMG" "$TEST_IMG.src" "'format': 'raw'," "BLOCK_JOB_READY" + $QEMU_IMG compare -f raw -F raw "$TEST_IMG" "$TEST_IMG.src" +done + + +# success, all done +echo '*** done' +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/109.out b/tests/qemu-iotests/109.out new file mode 100644 index 0000000..7db92c9 --- /dev/null +++ b/tests/qemu-iotests/109.out @@ -0,0 +1,231 @@ +QA output created by 109 + +=== Writing a qcow header into raw === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 +{"return": {}} +WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. +Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. +Specify the 'raw' format explicitly to remove the restrictions. +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 1024, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"return": []} +read 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} +{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 1024, "offset": 1024, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +Warning: Image size mismatch! +Images are identical. + +=== Writing a qcow2 header into raw === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 +{"return": {}} +WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. +Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. +Specify the 'raw' format explicitly to remove the restrictions. +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 197120, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"return": []} +read 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}} +{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 197120, "offset": 197120, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +Warning: Image size mismatch! +Images are identical. + +=== Writing a qed header into raw === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 +{"return": {}} +WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. +Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. +Specify the 'raw' format explicitly to remove the restrictions. +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"return": []} +read 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} +{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 327680, "offset": 327680, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +Warning: Image size mismatch! +Images are identical. + +=== Writing a vdi header into raw === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 +{"return": {}} +WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. +Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. +Specify the 'raw' format explicitly to remove the restrictions. +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 1024, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"return": []} +read 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} +{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 1024, "offset": 1024, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +Warning: Image size mismatch! +Images are identical. + +=== Writing a vmdk header into raw === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 +{"return": {}} +WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. +Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. +Specify the 'raw' format explicitly to remove the restrictions. +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 65536, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"return": []} +read 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}} +{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 65536, "offset": 65536, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +Warning: Image size mismatch! +Images are identical. + +=== Writing a vpc header into raw === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 +{"return": {}} +WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. +Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. +Specify the 'raw' format explicitly to remove the restrictions. +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2560, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"return": []} +read 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} +{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 2560, "offset": 2560, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +Warning: Image size mismatch! +Images are identical. + +=== Copying sample image empty.bochs into raw === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +{"return": {}} +WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. +Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. +Specify the 'raw' format explicitly to remove the restrictions. +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2560, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"return": []} +read 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} +{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 2560, "offset": 2560, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +Image resized. +Warning: Image size mismatch! +Images are identical. + +=== Copying sample image iotest-dirtylog-10G-4M.vhdx into raw === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +{"return": {}} +WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. +Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. +Specify the 'raw' format explicitly to remove the restrictions. +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 31457280, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"return": []} +read 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}} +{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 31457280, "offset": 31457280, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +Image resized. +Warning: Image size mismatch! +Images are identical. + +=== Copying sample image parallels-v1 into raw === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +{"return": {}} +WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. +Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. +Specify the 'raw' format explicitly to remove the restrictions. +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"return": []} +read 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} +{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 327680, "offset": 327680, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +Image resized. +Warning: Image size mismatch! +Images are identical. + +=== Copying sample image simple-pattern.cloop into raw === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +{"return": {}} +WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. +Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. +Specify the 'raw' format explicitly to remove the restrictions. +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2048, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"return": []} +read 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}} +{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 2048, "offset": 2048, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +Image resized. +Warning: Image size mismatch! +Images are identical. + +=== Write legitimate MBR into raw === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +{"return": {}} +WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. +Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. +Specify the 'raw' format explicitly to remove the restrictions. +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} +{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 512, "offset": 512, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +Warning: Image size mismatch! +Images are identical. +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} +{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 512, "offset": 512, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +Warning: Image size mismatch! +Images are identical. +*** done diff --git a/tests/qemu-iotests/110 b/tests/qemu-iotests/110 new file mode 100755 index 0000000..a687f95 --- /dev/null +++ b/tests/qemu-iotests/110 @@ -0,0 +1,94 @@ +#!/bin/bash +# +# Test case for relative backing file names in complex BDS trees +# +# Copyright (C) 2014 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 . +# + +# creator +owner=mreitz@redhat.com + +seq="$(basename $0)" +echo "QA output created by $seq" + +here="$PWD" +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +# Any format supporting backing files +_supported_fmt qed qcow qcow2 vmdk +_supported_proto file +_supported_os Linux +_unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat" + +TEST_IMG_REL=$(basename "$TEST_IMG") + +echo +echo '=== Reconstructable filename ===' +echo + +TEST_IMG="$TEST_IMG.base" _make_test_img 64M +_make_test_img -b "$TEST_IMG_REL.base" 64M +# qemu should be able to reconstruct the filename, so relative backing names +# should work +TEST_IMG="json:{'driver':'$IMGFMT','file':{'driver':'file','filename':'$TEST_IMG'}}" \ + _img_info | _filter_img_info + +echo +echo '=== Non-reconstructable filename ===' +echo + +# Across blkdebug without a config file, you cannot reconstruct filenames, so +# qemu is incapable of knowing the directory of the top image +TEST_IMG="json:{ + 'driver': '$IMGFMT', + 'file': { + 'driver': 'blkdebug', + 'image': { + 'driver': 'file', + 'filename': '$TEST_IMG' + }, + 'set-state': [ + { + 'event': 'read_aio', + 'new_state': 42 + } + ] + } +}" _img_info | _filter_img_info + +echo +echo '=== Backing name is always relative to the backed image ===' +echo + +# omit the image size; it should work anyway +_make_test_img -b "$TEST_IMG_REL.base" + + +# success, all done +echo '*** done' +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/110.out b/tests/qemu-iotests/110.out new file mode 100644 index 0000000..152bacf --- /dev/null +++ b/tests/qemu-iotests/110.out @@ -0,0 +1,19 @@ +QA output created by 110 + +=== Reconstructable filename === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='t.IMGFMT.base' +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 64M (67108864 bytes) +backing file: t.IMGFMT.base (actual path: TEST_DIR/t.IMGFMT.base) + +=== Non-reconstructable filename === + +qemu-img: Cannot use relative backing file names for 'json:{"driver": "IMGFMT", "file": {"set-state.0.event": "read_aio", "image": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "driver": "blkdebug", "set-state.0.new_state": 42}}' + +=== Backing name is always relative to the backed image === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='t.IMGFMT.base' +*** done diff --git a/tests/qemu-iotests/112 b/tests/qemu-iotests/112 new file mode 100755 index 0000000..3f054a3 --- /dev/null +++ b/tests/qemu-iotests/112 @@ -0,0 +1,187 @@ +#!/bin/bash +# +# Test cases for different refcount_bits values +# +# Copyright (C) 2015 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 . +# + +# creator +owner=mreitz@redhat.com + +seq="$(basename $0)" +echo "QA output created by $seq" + +here="$PWD" +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +# This tests qcow2-specific low-level functionality +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux +# This test will set refcount_bits on its own which would conflict with the +# manual setting; compat will be overridden as well +_unsupported_imgopts refcount_bits 'compat=0.10' + +function print_refcount_bits() +{ + $QEMU_IMG info "$TEST_IMG" | sed -n '/refcount bits:/ s/^ *//p' +} + +echo +echo '=== refcount_bits limits ===' +echo + +# Must be positive (non-zero) +IMGOPTS="$IMGOPTS,refcount_bits=0" _make_test_img 64M +# Must be positive (non-negative) +IMGOPTS="$IMGOPTS,refcount_bits=-1" _make_test_img 64M +# May not exceed 64 +IMGOPTS="$IMGOPTS,refcount_bits=128" _make_test_img 64M +# Must be a power of two +IMGOPTS="$IMGOPTS,refcount_bits=42" _make_test_img 64M + +# 1 is the minimum +IMGOPTS="$IMGOPTS,refcount_bits=1" _make_test_img 64M +print_refcount_bits + +# 64 is the maximum +IMGOPTS="$IMGOPTS,refcount_bits=64" _make_test_img 64M +print_refcount_bits + +# 16 is the default +_make_test_img 64M +print_refcount_bits + +echo +echo '=== refcount_bits and compat=0.10 ===' +echo + +# Should work +IMGOPTS="$IMGOPTS,compat=0.10,refcount_bits=16" _make_test_img 64M +print_refcount_bits + +# Should not work +IMGOPTS="$IMGOPTS,compat=0.10,refcount_bits=1" _make_test_img 64M +IMGOPTS="$IMGOPTS,compat=0.10,refcount_bits=64" _make_test_img 64M + + +echo +echo '=== Snapshot limit on refcount_bits=1 ===' +echo + +IMGOPTS="$IMGOPTS,refcount_bits=1" _make_test_img 64M +print_refcount_bits + +$QEMU_IO -c 'write 0 512' "$TEST_IMG" | _filter_qemu_io + +# Should fail for now; in the future, this might be supported by automatically +# copying all clusters with overflowing refcount +$QEMU_IMG snapshot -c foo "$TEST_IMG" + +# The new L1 table could/should be leaked +_check_test_img + +echo +echo '=== Snapshot limit on refcount_bits=2 ===' +echo + +IMGOPTS="$IMGOPTS,refcount_bits=2" _make_test_img 64M +print_refcount_bits + +$QEMU_IO -c 'write 0 512' "$TEST_IMG" | _filter_qemu_io + +# Should succeed +$QEMU_IMG snapshot -c foo "$TEST_IMG" +$QEMU_IMG snapshot -c bar "$TEST_IMG" +# Should fail (4th reference) +$QEMU_IMG snapshot -c baz "$TEST_IMG" + +# The new L1 table could/should be leaked +_check_test_img + +echo +echo '=== Compressed clusters with refcount_bits=1 ===' +echo + +IMGOPTS="$IMGOPTS,refcount_bits=1" _make_test_img 64M +print_refcount_bits + +# Both should fit into a single host cluster; instead of failing to increase the +# refcount of that cluster, qemu should just allocate a new cluster and make +# this operation succeed +$QEMU_IO -c 'write -P 0 -c 0 64k' \ + -c 'write -P 1 -c 64k 64k' \ + "$TEST_IMG" | _filter_qemu_io + +_check_test_img + +echo +echo '=== MSb set in 64 bit refcount ===' +echo + +IMGOPTS="$IMGOPTS,refcount_bits=64" _make_test_img 64M +print_refcount_bits + +$QEMU_IO -c 'write 0 512' "$TEST_IMG" | _filter_qemu_io + +# Set the MSb in the refblock entry of the data cluster +poke_file "$TEST_IMG" $((0x20028)) "\x80\x00\x00\x00\x00\x00\x00\x00" + +# Clear OFLAG_COPIED in the L2 entry of the data cluster +poke_file "$TEST_IMG" $((0x40000)) "\x00\x00\x00\x00\x00\x05\x00\x00" + +# Try to write to that cluster (should work, even though the MSb is set) +$QEMU_IO -c 'write 0 512' "$TEST_IMG" | _filter_qemu_io + +echo +echo '=== Snapshot on maximum 64 bit refcount value ===' +echo + +IMGOPTS="$IMGOPTS,refcount_bits=64" _make_test_img 64M +print_refcount_bits + +$QEMU_IO -c 'write 0 512' "$TEST_IMG" | _filter_qemu_io + +# Set the refblock entry to the maximum value possible +poke_file "$TEST_IMG" $((0x20028)) "\xff\xff\xff\xff\xff\xff\xff\xff" + +# Clear OFLAG_COPIED in the L2 entry of the data cluster +poke_file "$TEST_IMG" $((0x40000)) "\x00\x00\x00\x00\x00\x05\x00\x00" + +# Try a snapshot (should correctly identify the overflow; may work in the future +# by falling back to COW) +$QEMU_IMG snapshot -c foo "$TEST_IMG" + +# The new L1 table could/should be leaked; and obviously the data cluster is +# leaked (refcount=UINT64_MAX reference=1) +_check_test_img + + +# success, all done +echo '*** done' +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/112.out b/tests/qemu-iotests/112.out new file mode 100644 index 0000000..9a98633 --- /dev/null +++ b/tests/qemu-iotests/112.out @@ -0,0 +1,84 @@ +QA output created by 112 + +=== refcount_bits limits === + +qemu-img: TEST_DIR/t.IMGFMT: Refcount width must be a power of two and may not exceed 64 bits +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qemu-img: TEST_DIR/t.IMGFMT: Refcount width must be a power of two and may not exceed 64 bits +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 refcount_bits=-1 +qemu-img: TEST_DIR/t.IMGFMT: Refcount width must be a power of two and may not exceed 64 bits +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qemu-img: TEST_DIR/t.IMGFMT: Refcount width must be a power of two and may not exceed 64 bits +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +refcount bits: 1 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +refcount bits: 64 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +refcount bits: 16 + +=== refcount_bits and compat=0.10 === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +refcount bits: 16 +qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use compat=1.1 or greater) +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use compat=1.1 or greater) +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 + +=== Snapshot limit on refcount_bits=1 === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +refcount bits: 1 +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-img: Could not create snapshot 'foo': -22 (Invalid argument) +Leaked cluster 6 refcount=1 reference=0 + +1 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. + +=== Snapshot limit on refcount_bits=2 === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +refcount bits: 2 +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-img: Could not create snapshot 'baz': -22 (Invalid argument) +Leaked cluster 7 refcount=1 reference=0 + +1 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. + +=== Compressed clusters with refcount_bits=1 === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +refcount bits: 1 +wrote 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 65536 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. + +=== MSb set in 64 bit refcount === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +refcount bits: 64 +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +=== Snapshot on maximum 64 bit refcount value === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +refcount bits: 64 +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-img: Could not create snapshot 'foo': -22 (Invalid argument) +Leaked cluster 5 refcount=18446744073709551615 reference=1 +Leaked cluster 6 refcount=1 reference=0 + +2 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. +*** done diff --git a/tests/qemu-iotests/113 b/tests/qemu-iotests/113 new file mode 100755 index 0000000..a2cd96b --- /dev/null +++ b/tests/qemu-iotests/113 @@ -0,0 +1,76 @@ +#!/bin/bash +# +# Test case for accessing creation options on image formats and +# protocols not supporting image creation +# +# Copyright (C) 2014 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 . +# + +# creator +owner=mreitz@redhat.com + +seq="$(basename $0)" +echo "QA output created by $seq" + +here="$PWD" +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +# We can only test one format here because we need its sample file +_supported_fmt bochs +_supported_proto nbd +_supported_os Linux + +echo +echo '=== Unsupported image creation in qemu-img create ===' +echo + +$QEMU_IMG create -f $IMGFMT nbd://example.com 2>&1 64M | _filter_imgfmt + +echo +echo '=== Unsupported image creation in qemu-img convert ===' +echo + +# We could use any input image format here, but this is a bochs test, so just +# use the bochs image +_use_sample_img empty.bochs.bz2 +$QEMU_IMG convert -f $IMGFMT -O $IMGFMT "$TEST_IMG" nbd://example.com 2>&1 \ + | _filter_imgfmt + +echo +echo '=== Unsupported format in qemu-img amend ===' +echo + +# The protocol does not matter here +_use_sample_img empty.bochs.bz2 +$QEMU_IMG amend -f $IMGFMT -o foo=bar "$TEST_IMG" 2>&1 | _filter_imgfmt + + +# success, all done +echo +echo '*** done' +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/113.out b/tests/qemu-iotests/113.out new file mode 100644 index 0000000..00bdfd6 --- /dev/null +++ b/tests/qemu-iotests/113.out @@ -0,0 +1,15 @@ +QA output created by 113 + +=== Unsupported image creation in qemu-img create === + +qemu-img: nbd://example.com: Format driver 'IMGFMT' does not support image creation + +=== Unsupported image creation in qemu-img convert === + +qemu-img: Format driver 'IMGFMT' does not support image creation + +=== Unsupported format in qemu-img amend === + +qemu-img: Format driver 'IMGFMT' does not support any options to amend + +*** done diff --git a/tests/qemu-iotests/114 b/tests/qemu-iotests/114 new file mode 100755 index 0000000..d02e7ff --- /dev/null +++ b/tests/qemu-iotests/114 @@ -0,0 +1,61 @@ +#!/bin/bash +# +# Test invalid backing file format in qcow2 images +# +# Copyright (C) 2014 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 . +# + +# creator +owner=kwolf@redhat.com + +seq="$(basename $0)" +echo "QA output created by $seq" + +here="$PWD" +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qcow2 +_supported_proto generic +_supported_os Linux + + +TEST_IMG="$TEST_IMG.base" _make_test_img 64M +_make_test_img -b "$TEST_IMG.base" 64M + +# Set an invalid backing file format +$PYTHON qcow2.py "$TEST_IMG" add-header-ext 0xE2792ACA "foo" +_img_info + +# Try opening the image. Should fail (and not probe) in the first case, but +# overriding the backing file format should be possible. +$QEMU_IO -c "open $TEST_IMG" -c "read 0 4k" 2>&1 | _filter_qemu_io | _filter_testdir +$QEMU_IO -c "open -o backing.driver=$IMGFMT $TEST_IMG" -c "read 0 4k" | _filter_qemu_io + +# success, all done +echo '*** done' +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/114.out b/tests/qemu-iotests/114.out new file mode 100644 index 0000000..6c6b210 --- /dev/null +++ b/tests/qemu-iotests/114.out @@ -0,0 +1,13 @@ +QA output created by 114 +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 64M (67108864 bytes) +cluster_size: 65536 +backing file: TEST_DIR/t.IMGFMT.base +backing file format: foo +qemu-io: can't open device TEST_DIR/t.qcow2: Could not open backing file: Unknown driver 'foo' +read 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +*** done diff --git a/tests/qemu-iotests/115 b/tests/qemu-iotests/115 new file mode 100755 index 0000000..a6be187 --- /dev/null +++ b/tests/qemu-iotests/115 @@ -0,0 +1,95 @@ +#!/bin/bash +# +# Test case for non-self-referential qcow2 refcount blocks +# +# Copyright (C) 2014 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 . +# + +# creator +owner=mreitz@redhat.com + +seq="$(basename $0)" +echo "QA output created by $seq" + +here="$PWD" +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux +# This test relies on refcounts being 64 bits wide (which does not work with +# compat=0.10) +_unsupported_imgopts 'refcount_bits=\([^6]\|.\([^4]\|$\)\)' 'compat=0.10' + +echo +echo '=== Testing large refcount and L1 table ===' +echo + +# Create an image with an L1 table and a refcount table that each span twice the +# number of clusters which can be described by a single refblock; therefore, at +# least two refblocks cannot count their own refcounts because all the clusters +# they describe are part of the L1 table or refcount table. + +# One refblock can describe (with cluster_size=512 and refcount_bits=64) +# 512/8 = 64 clusters, therefore the L1 table should cover 128 clusters, which +# equals 128 * (512/8) = 8192 entries (actually, 8192 - 512/8 = 8129 would +# suffice, but it does not really matter). 8192 L2 tables can in turn describe +# 8192 * 512/8 = 524,288 clusters which cover a space of 256 MB. + +# Since with refcount_bits=64 every refcount block entry is 64 bits wide (just +# like the L2 table entries), the same calculation applies to the refcount table +# as well; the difference is that while for the L1 table the guest disk size is +# concerned, for the refcount table it is the image length that has to be at +# least 256 MB. We can achieve that by using preallocation=metadata for an image +# which has a guest disk size of 256 MB. + +IMGOPTS="$IMGOPTS,refcount_bits=64,cluster_size=512,preallocation=metadata" \ + _make_test_img 256M + +# We know for sure that the L1 and refcount tables do not overlap with any other +# structure because the metadata overlap checks would have caught that case. + +# Because qemu refuses to open qcow2 files whose L1 table does not cover the +# whole guest disk size, it is definitely large enough. On the other hand, to +# test whether the refcount table is large enough, we simply have to verify that +# indeed all the clusters are allocated, which is done by qemu-img check. + +# The final thing we need to test is whether the tables are actually covered by +# refcount blocks; since all clusters of the tables are referenced, we can use +# qemu-img check for that purpose, too. + +$QEMU_IMG check "$TEST_IMG" | \ + sed -e 's/^.* = \([0-9]\+\.[0-9]\+% allocated\).*\(clusters\)$/\1 \2/' \ + -e '/^Image end offset/d' + +# (Note that we cannot use _check_test_img because that function filters out the +# allocation status) + +# success, all done +echo '*** done' +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/115.out b/tests/qemu-iotests/115.out new file mode 100644 index 0000000..7b2c5e0 --- /dev/null +++ b/tests/qemu-iotests/115.out @@ -0,0 +1,8 @@ +QA output created by 115 + +=== Testing large refcount and L1 table === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=268435456 preallocation='metadata' +No errors were found on the image. +100.00% allocated clusters +*** done diff --git a/tests/qemu-iotests/116 b/tests/qemu-iotests/116 new file mode 100755 index 0000000..713ed48 --- /dev/null +++ b/tests/qemu-iotests/116 @@ -0,0 +1,96 @@ +#!/bin/bash +# +# Test error code paths for invalid QED images +# +# The aim of this test is to exercise the error paths in qed_open() to ensure +# there are no crashes with invalid input files. +# +# Copyright (C) 2015 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 . +# + +# creator +owner=stefanha@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qed +_supported_proto generic +_supported_os Linux + + +size=128M + +echo +echo "== truncated header cluster ==" +_make_test_img $size +truncate -s 512 "$TEST_IMG" +$QEMU_IO -f "$IMGFMT" -c "read 0 $size" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== invalid header magic ==" +_make_test_img $size +poke_file "$TEST_IMG" "0" "QEDX" +$QEMU_IO -f "$IMGFMT" -c "read 0 $size" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== invalid cluster size ==" +_make_test_img $size +poke_file "$TEST_IMG" "4" "\xff\xff\xff\xff" +$QEMU_IO -f "$IMGFMT" -c "read 0 $size" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== invalid table size ==" +_make_test_img $size +poke_file "$TEST_IMG" "8" "\xff\xff\xff\xff" +$QEMU_IO -f "$IMGFMT" -c "read 0 $size" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== invalid header size ==" +_make_test_img $size +poke_file "$TEST_IMG" "12" "\xff\xff\xff\xff" +$QEMU_IO -f "$IMGFMT" -c "read 0 $size" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== invalid L1 table offset ==" +_make_test_img $size +poke_file "$TEST_IMG" "40" "\xff\xff\xff\xff\xff\xff\xff\xff" +$QEMU_IO -f "$IMGFMT" -c "read 0 $size" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== invalid image size ==" +_make_test_img $size +poke_file "$TEST_IMG" "48" "\xff\xff\xff\xff\xff\xff\xff\xff" +$QEMU_IO -f "$IMGFMT" -c "read 0 $size" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/116.out b/tests/qemu-iotests/116.out new file mode 100644 index 0000000..b679cee --- /dev/null +++ b/tests/qemu-iotests/116.out @@ -0,0 +1,37 @@ +QA output created by 116 + +== truncated header cluster == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +qemu-io: can't open device TEST_DIR/t.qed: Could not open 'TEST_DIR/t.qed': Invalid argument +no file open, try 'help open' + +== invalid header magic == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +qemu-io: can't open device TEST_DIR/t.qed: Image not in QED format +no file open, try 'help open' + +== invalid cluster size == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +qemu-io: can't open device TEST_DIR/t.qed: Could not open 'TEST_DIR/t.qed': Invalid argument +no file open, try 'help open' + +== invalid table size == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +qemu-io: can't open device TEST_DIR/t.qed: Could not open 'TEST_DIR/t.qed': Invalid argument +no file open, try 'help open' + +== invalid header size == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +qemu-io: can't open device TEST_DIR/t.qed: Could not open 'TEST_DIR/t.qed': Invalid argument +no file open, try 'help open' + +== invalid L1 table offset == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +qemu-io: can't open device TEST_DIR/t.qed: Could not open 'TEST_DIR/t.qed': Invalid argument +no file open, try 'help open' + +== invalid image size == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +qemu-io: can't open device TEST_DIR/t.qed: Could not open 'TEST_DIR/t.qed': Invalid argument +no file open, try 'help open' +*** done diff --git a/tests/qemu-iotests/121 b/tests/qemu-iotests/121 new file mode 100755 index 0000000..0912c3f --- /dev/null +++ b/tests/qemu-iotests/121 @@ -0,0 +1,102 @@ +#!/bin/bash +# +# Test cases for qcow2 refcount table growth +# +# Copyright (C) 2015 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 . +# + +# creator +owner=mreitz@redhat.com + +seq="$(basename $0)" +echo "QA output created by $seq" + +here="$PWD" +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux + +echo +echo '=== New refcount structures may not conflict with existing structures ===' + +echo +echo '--- Test 1 ---' +echo + +# Preallocation speeds up the write operation, but preallocating everything will +# destroy the purpose of the write; so preallocate one KB less than what would +# cause a reftable growth... +IMGOPTS='preallocation=metadata,cluster_size=1k' _make_test_img 64512K +# ...and make the image the desired size afterwards. +$QEMU_IMG resize "$TEST_IMG" 65M + +# The first write results in a growth of the refcount table during an allocation +# which has precisely the required size so that the new refcount block allocated +# in alloc_refcount_block() is right after cluster_index; this did lead to a +# different refcount block being written to disk (a zeroed cluster) than what is +# cached (a refblock with one entry having a refcount of 1), and the second +# write would then result in that cached cluster being marked dirty and then +# in it being written to disk. +# This should not happen, the new refcount structures may not conflict with +# new_block. +# (Note that for some reason, 'write 63M 1K' does not trigger the problem) +$QEMU_IO -c 'write 62M 1025K' -c 'write 64M 1M' "$TEST_IMG" | _filter_qemu_io + +_check_test_img + + +echo +echo '--- Test 2 ---' +echo + +IMGOPTS='preallocation=metadata,cluster_size=1k' _make_test_img 64513K +# This results in an L1 table growth which in turn results in some clusters at +# the start of the image becoming free +$QEMU_IMG resize "$TEST_IMG" 65M + +# This write results in a refcount table growth; but the refblock allocated +# immediately before that (new_block) takes cluster index 4 (which is now free) +# and is thus not self-describing (in contrast to test 1, where new_block was +# self-describing). The refcount table growth algorithm then used to place the +# new refcount structures at cluster index 65536 (which is the same as the +# cluster_index parameter in this case), allocating a new refcount block for +# that cluster while new_block already existed, leaking new_block. +# Therefore, the new refcount structures may not be put at cluster_index +# (because new_block already describes that cluster, and the new structures try +# to be self-describing). +$QEMU_IO -c 'write 63M 130K' "$TEST_IMG" | _filter_qemu_io + +_check_test_img + + +# success, all done +echo +echo '*** done' +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/121.out b/tests/qemu-iotests/121.out new file mode 100644 index 0000000..ff18e2c --- /dev/null +++ b/tests/qemu-iotests/121.out @@ -0,0 +1,23 @@ +QA output created by 121 + +=== New refcount structures may not conflict with existing structures === + +--- Test 1 --- + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=66060288 preallocation='metadata' +Image resized. +wrote 1049600/1049600 bytes at offset 65011712 +1.001 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 1048576/1048576 bytes at offset 67108864 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. + +--- Test 2 --- + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=66061312 preallocation='metadata' +Image resized. +wrote 133120/133120 bytes at offset 66060288 +130 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. + +*** done diff --git a/tests/qemu-iotests/016 b/tests/qemu-iotests/123 similarity index 56% rename from tests/qemu-iotests/016 rename to tests/qemu-iotests/123 index 7ea9e94..ad60803 100755 --- a/tests/qemu-iotests/016 +++ b/tests/qemu-iotests/123 @@ -1,8 +1,8 @@ #!/bin/bash # -# Test I/O after EOF for growable images. +# Test case for qemu-img convert to NBD # -# Copyright (C) 2009 Red Hat, Inc. +# Copyright (C) 2015 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 @@ -19,18 +19,19 @@ # # creator -owner=hch@lst.de +owner=mreitz@redhat.com -seq=`basename $0` +seq="$(basename $0)" echo "QA output created by $seq" -here=`pwd` +here="$PWD" tmp=/tmp/$$ status=1 # failure is the default! _cleanup() { - _cleanup_test_img + _cleanup_test_img + rm -f "$SRC_IMG" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -39,32 +40,23 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter _supported_fmt raw -_supported_proto file sheepdog nfs +_supported_proto nbd _supported_os Linux +SRC_IMG="$TEST_DIR/source.$IMGFMT" -size=128M -_make_test_img $size +_make_test_img 1M +$QEMU_IMG create -f $IMGFMT "$SRC_IMG" 1M | _filter_img_create -echo -echo "== reading at EOF ==" -$QEMU_IO -g -c "read -P 0 $size 512" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c 'write -P 42 0 1M' "$SRC_IMG" | _filter_qemu_io -echo -echo "== reading far past EOF ==" -$QEMU_IO -g -c "read -P 0 256M 512" "$TEST_IMG" | _filter_qemu_io +$QEMU_IMG convert -n -f $IMGFMT -O raw "$SRC_IMG" "$TEST_IMG" -echo -echo "== writing at EOF ==" -$QEMU_IO -g -c "write -P 66 $size 512" "$TEST_IMG" | _filter_qemu_io -$QEMU_IO -c "read -P 66 $size 512" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c 'read -P 42 0 1M' "$TEST_IMG" | _filter_qemu_io -echo -echo "== writing far past EOF ==" -$QEMU_IO -g -c "write -P 66 256M 512" "$TEST_IMG" | _filter_qemu_io -$QEMU_IO -c "read -P 66 256M 512" "$TEST_IMG" | _filter_qemu_io # success, all done -echo "*** done" +echo +echo '*** done' rm -f $seq.full status=0 diff --git a/tests/qemu-iotests/123.out b/tests/qemu-iotests/123.out new file mode 100644 index 0000000..0b818d3 --- /dev/null +++ b/tests/qemu-iotests/123.out @@ -0,0 +1,9 @@ +QA output created by 123 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 +Formatting 'TEST_DIR/source.IMGFMT', fmt=IMGFMT size=1048576 +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +*** done diff --git a/tests/qemu-iotests/128 b/tests/qemu-iotests/128 new file mode 100755 index 0000000..249a865 --- /dev/null +++ b/tests/qemu-iotests/128 @@ -0,0 +1,82 @@ +#!/bin/bash +# +# Test that opening O_DIRECT succeeds when image file I/O produces EIO +# +# Copyright (C) 2015 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 . +# + +# creator +owner=stefanha@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ +status=1 # failure is the default! + +devname="eiodev$$" + +_setup_eiodev() +{ + # This test should either be run as root or with passwordless sudo + for cmd in "" "sudo -n"; do + echo "0 $((1024 * 1024 * 1024 / 512)) error" | \ + $cmd dmsetup create "$devname" 2>/dev/null + if [ "$?" -eq 0 ]; then + return + fi + done + _notrun "root privileges required to run dmsetup" +} + +_cleanup_eiodev() +{ + for cmd in "" "sudo -n"; do + $cmd dmsetup remove "$devname" 2>/dev/null + if [ "$?" -eq 0 ]; then + return + fi + done +} + +_cleanup() +{ + _cleanup_eiodev +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt raw +_supported_proto file +_supported_os Linux + +_setup_eiodev + +TEST_IMG="/dev/mapper/$devname" + +echo +echo "== reading from error device ==" +# Opening image should succeed but the read operation should fail +$QEMU_IO --format "$IMGFMT" --nocache -c "read 0 65536" "$TEST_IMG" | _filter_qemu_io + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/128.out b/tests/qemu-iotests/128.out new file mode 100644 index 0000000..4e43f5f --- /dev/null +++ b/tests/qemu-iotests/128.out @@ -0,0 +1,5 @@ +QA output created by 128 + +== reading from error device == +read failed: Input/output error +*** done diff --git a/tests/qemu-iotests/130 b/tests/qemu-iotests/130 new file mode 100755 index 0000000..bc26247 --- /dev/null +++ b/tests/qemu-iotests/130 @@ -0,0 +1,95 @@ +#!/bin/bash +# +# Test that temporary backing file overrides (on the command line or in +# blockdev-add) don't replace the original path stored in the image during +# header updates. +# +# Copyright (C) 2015 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 . +# + +# creator +owner=kwolf@redhat.com + +seq="$(basename $0)" +echo "QA output created by $seq" + +here="$PWD" +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.qemu + +_supported_fmt qcow2 +_supported_proto generic +_supported_os Linux + +qemu_comm_method="monitor" + + +TEST_IMG="$TEST_IMG.orig" _make_test_img 64M +TEST_IMG="$TEST_IMG.base" _make_test_img 64M +_make_test_img 64M +_img_info | _filter_img_info + +echo +echo "=== HMP commit ===" +echo +# bdrv_make_empty() involves a header update for qcow2 + +# Test that a backing file isn't written +_launch_qemu -drive file="$TEST_IMG",backing.file.filename="$TEST_IMG.base" +_send_qemu_cmd $QEMU_HANDLE "commit ide0-hd0" "(qemu)" +_send_qemu_cmd $QEMU_HANDLE '' '(qemu)' +_cleanup_qemu +_img_info | _filter_img_info + +# Make sure that if there was a backing file that was just overridden on the +# command line, that backing file is retained, with the right format +_make_test_img -F raw -b "$TEST_IMG.orig" 64M +_launch_qemu -drive file="$TEST_IMG",backing.file.filename="$TEST_IMG.base",backing.driver=$IMGFMT +_send_qemu_cmd $QEMU_HANDLE "commit ide0-hd0" "(qemu)" +_send_qemu_cmd $QEMU_HANDLE '' '(qemu)' +_cleanup_qemu +_img_info | _filter_img_info + +echo +echo "=== Marking image dirty (lazy refcounts) ===" +echo + +# Test that a backing file isn't written +_make_test_img 64M +$QEMU_IO -c "open -o backing.file.filename=$TEST_IMG.base,lazy-refcounts=on $TEST_IMG" -c "write 0 4k" | _filter_qemu_io +_img_info | _filter_img_info + +# Make sure that if there was a backing file that was just overridden on the +# command line, that backing file is retained, with the right format +_make_test_img -F raw -b "$TEST_IMG.orig" 64M +$QEMU_IO -c "open -o backing.file.filename=$TEST_IMG.base,backing.driver=$IMGFMT,lazy-refcounts=on $TEST_IMG" -c "write 0 4k" | _filter_qemu_io +_img_info | _filter_img_info + +# success, all done +echo '*** done' +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/130.out b/tests/qemu-iotests/130.out new file mode 100644 index 0000000..ea68b5d --- /dev/null +++ b/tests/qemu-iotests/130.out @@ -0,0 +1,43 @@ +QA output created by 130 +Formatting 'TEST_DIR/t.IMGFMT.orig', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 64M (67108864 bytes) + +=== HMP commit === + +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) ccocomcommcommicommitcommit commit icommit idcommit idecommit ide0commit ide0-commit ide0-hcommit ide0-hdcommit ide0-hd0 +(qemu) +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 64M (67108864 bytes) +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.orig' backing_fmt='raw' +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) ccocomcommcommicommitcommit commit icommit idcommit idecommit ide0commit ide0-commit ide0-hcommit ide0-hdcommit ide0-hd0 +(qemu) +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 64M (67108864 bytes) +backing file: TEST_DIR/t.IMGFMT.orig +backing file format: raw + +=== Marking image dirty (lazy refcounts) === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +wrote 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 64M (67108864 bytes) +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.orig' backing_fmt='raw' +wrote 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 64M (67108864 bytes) +backing file: TEST_DIR/t.IMGFMT.orig +backing file format: raw +*** done diff --git a/tests/qemu-iotests/132 b/tests/qemu-iotests/132 new file mode 100644 index 0000000..f53ef6e --- /dev/null +++ b/tests/qemu-iotests/132 @@ -0,0 +1,59 @@ +#!/usr/bin/env python +# +# Test mirror with unmap +# +# Copyright (C) 2015 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 . +# + +import time +import os +import iotests +from iotests import qemu_img, qemu_io + +test_img = os.path.join(iotests.test_dir, 'test.img') +target_img = os.path.join(iotests.test_dir, 'target.img') + +class TestSingleDrive(iotests.QMPTestCase): + image_len = 2 * 1024 * 1024 # MB + + def setUp(self): + # Write data to the image so we can compare later + qemu_img('create', '-f', iotests.imgfmt, test_img, str(TestSingleDrive.image_len)) + qemu_io('-f', iotests.imgfmt, '-c', 'write -P0x5d 0 2M', test_img) + + self.vm = iotests.VM().add_drive(test_img, 'discard=unmap') + self.vm.launch() + + def tearDown(self): + self.vm.shutdown() + os.remove(test_img) + try: + os.remove(target_img) + except OSError: + pass + + def test_mirror_discard(self): + result = self.vm.qmp('drive-mirror', device='drive0', sync='full', + target=target_img) + self.assert_qmp(result, 'return', {}) + self.vm.hmp_qemu_io('drive0', 'discard 0 64k') + self.complete_and_wait('drive0') + self.vm.shutdown() + self.assertTrue(iotests.compare_images(test_img, target_img), + 'target image does not match source after mirroring') + +if __name__ == '__main__': + iotests.main(supported_fmts=['raw', 'qcow2']) diff --git a/tests/qemu-iotests/132.out b/tests/qemu-iotests/132.out new file mode 100644 index 0000000..ae1213e --- /dev/null +++ b/tests/qemu-iotests/132.out @@ -0,0 +1,5 @@ +. +---------------------------------------------------------------------- +Ran 1 tests + +OK diff --git a/tests/qemu-iotests/006 b/tests/qemu-iotests/135 similarity index 79% rename from tests/qemu-iotests/006 rename to tests/qemu-iotests/135 index 0c0cf5d..16bf736 100755 --- a/tests/qemu-iotests/006 +++ b/tests/qemu-iotests/135 @@ -1,9 +1,8 @@ #!/bin/bash # -# Make sure qemu-img rejects > 127GB images for the vpc format as the format -# doesn't support this. +# Test VPC open of image with large Max Table Entries value. # -# Copyright (C) 2009 Red Hat, Inc. +# Copyright (C) 2015 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 @@ -20,7 +19,7 @@ # # creator -owner=hch@lst.de +owner=jcody@redhat.com seq=`basename $0` echo "QA output created by $seq" @@ -31,7 +30,7 @@ status=1 # failure is the default! _cleanup() { - _cleanup_test_img + _cleanup_test_img } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -43,10 +42,11 @@ _supported_fmt vpc _supported_proto generic _supported_os Linux +_use_sample_img afl5.img.bz2 echo -echo "creating 128GB image" -_make_test_img 128G +echo "=== Verify image open and failure ====" +$QEMU_IMG info "$TEST_IMG" 2>&1| _filter_testdir # success, all done echo "*** done" diff --git a/tests/qemu-iotests/135.out b/tests/qemu-iotests/135.out new file mode 100644 index 0000000..793898b --- /dev/null +++ b/tests/qemu-iotests/135.out @@ -0,0 +1,5 @@ +QA output created by 135 + +=== Verify image open and failure ==== +qemu-img: Could not open 'TEST_DIR/afl5.img': Max Table Entries too large (1073741825) +*** done diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check index 8ca4011..baeae80 100755 --- a/tests/qemu-iotests/check +++ b/tests/qemu-iotests/check @@ -238,6 +238,7 @@ QEMU_NBD -- $QEMU_NBD IMGFMT -- $FULL_IMGFMT_DETAILS IMGPROTO -- $FULL_IMGPROTO_DETAILS PLATFORM -- $FULL_HOST_DETAILS +TEST_DIR -- $TEST_DIR SOCKET_SCM_HELPER -- $SOCKET_SCM_HELPER EOF diff --git a/tests/qemu-iotests/common b/tests/qemu-iotests/common index 9e12bec..1e556bb 100644 --- a/tests/qemu-iotests/common +++ b/tests/qemu-iotests/common @@ -289,10 +289,10 @@ testlist options if [ ! -z "$DISPLAY" ] then - which xdiff >/dev/null 2>&1 && diff=xdiff - which gdiff >/dev/null 2>&1 && diff=gdiff - which tkdiff >/dev/null 2>&1 && diff=tkdiff - which xxdiff >/dev/null 2>&1 && diff=xxdiff + command -v xdiff >/dev/null 2>&1 && diff=xdiff + command -v gdiff >/dev/null 2>&1 && diff=gdiff + command -v tkdiff >/dev/null 2>&1 && diff=tkdiff + command -v xxdiff >/dev/null 2>&1 && diff=xxdiff fi ;; @@ -391,7 +391,7 @@ BEGIN { for (t='$start'; t<='$end'; t++) printf "%03d\n",t }' \ done # Set qemu-io cache mode with $CACHEMODE we have -QEMU_IO_OPTIONS="$QEMU_IO_OPTIONS --cache $CACHEMODE" +QEMU_IO_OPTIONS="$QEMU_IO_OPTIONS -f $IMGFMT --cache $CACHEMODE" # Set default options for qemu-img create -o if they were not specified _set_default_imgopts diff --git a/tests/qemu-iotests/common.config b/tests/qemu-iotests/common.config index bd6790b..a1973ad 100644 --- a/tests/qemu-iotests/common.config +++ b/tests/qemu-iotests/common.config @@ -47,7 +47,7 @@ export PWD=`pwd` # $1 = prog to look for, $2* = default pathnames if not found in $PATH set_prog_path() { - p=`which $1 2> /dev/null` + p=`command -v $1 2> /dev/null` if [ -n "$p" -a -x "$p" ]; then echo $p return 0 @@ -155,4 +155,4 @@ _readlink() } # make sure this script returns success -/bin/true +true diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter index 3acdb30..012a812 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -150,7 +150,7 @@ _filter_win32() _filter_qemu_io() { _filter_win32 | sed -e "s/[0-9]* ops\; [0-9/:. sec]* ([0-9/.inf]* [EPTGMKiBbytes]*\/sec and [0-9/.inf]* ops\/sec)/X ops\; XX:XX:XX.X (XXX YYY\/sec and XXX ops\/sec)/" \ - -e "s/: line [0-9][0-9]*: *[0-9][0-9]*\( Aborted\)/:\1/" \ + -e "s/: line [0-9][0-9]*: *[0-9][0-9]*\( Aborted\| Killed\)/:\1/" \ -e "s/qemu-io> //g" } @@ -159,6 +159,7 @@ _filter_qemu() { sed -e "s#\\(^\\|(qemu) \\)$(basename $QEMU_PROG):#\1QEMU_PROG:#" \ -e 's#^QEMU [0-9]\+\.[0-9]\+\.[0-9]\+ monitor#QEMU X.Y.Z monitor#' \ + -e '/main-loop: WARNING: I\/O thread spun for [0-9]\+ iterations/d' \ -e $'s#\r##' # QEMU monitor uses \r\n line endings } @@ -167,7 +168,9 @@ _filter_qmp() { _filter_win32 | \ sed -e 's#\("\(micro\)\?seconds": \)[0-9]\+#\1 TIMESTAMP#g' \ - -e 's#^{"QMP":.*}$#QMP_VERSION#' + -e 's#^{"QMP":.*}$#QMP_VERSION#' \ + -e '/^ "QMP": {\s*$/, /^ }\s*$/ c\' \ + -e ' QMP_VERSION' } # replace driver-specific options in the "Formatting..." line @@ -189,7 +192,8 @@ _filter_img_create() -e "s# block_size=[0-9]\\+##g" \ -e "s# block_state_zero=\\(on\\|off\\)##g" \ -e "s# log_size=[0-9]\\+##g" \ - -e "s/archipelago:a/TEST_DIR\//g" + -e "s/archipelago:a/TEST_DIR\//g" \ + -e "s# refcount_bits=[0-9]\\+##g" } _filter_img_info() @@ -197,6 +201,7 @@ _filter_img_info() sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ -e "s#$TEST_DIR#TEST_DIR#g" \ -e "s#$IMGFMT#IMGFMT#g" \ + -e 's#nbd://127.0.0.1:10810$#TEST_DIR/t.IMGFMT#g' \ -e "/encrypted: yes/d" \ -e "/cluster_size: [0-9]\\+/d" \ -e "/table_size: [0-9]\\+/d" \ @@ -221,4 +226,4 @@ _filter_qemu_img_map() } # make sure this script returns success -/bin/true +true diff --git a/tests/qemu-iotests/common.qemu b/tests/qemu-iotests/common.qemu index ee7ebb4..4e1996c 100644 --- a/tests/qemu-iotests/common.qemu +++ b/tests/qemu-iotests/common.qemu @@ -153,8 +153,9 @@ function _launch_qemu() mkfifo "${fifo_out}" mkfifo "${fifo_in}" - "${QEMU}" -nographic -serial none ${comm} -machine accel=qtest "${@}" 2>&1 \ + "${QEMU}" -nographic -serial none ${comm} -machine accel=qtest "${@}" \ >"${fifo_out}" \ + 2>&1 \ <"${fifo_in}" & QEMU_PID[${_QEMU_HANDLE}]=$! @@ -186,13 +187,23 @@ function _launch_qemu() # Silenty kills the QEMU process +# +# If $wait is set to anything other than the empty string, the process will not +# be killed but only waited for, and any output will be forwarded to stdout. If +# $wait is empty, the process will be killed and all output will be suppressed. function _cleanup_qemu() { # QEMU_PID[], QEMU_IN[], QEMU_OUT[] all use same indices for i in "${!QEMU_OUT[@]}" do - kill -KILL ${QEMU_PID[$i]} 2>/dev/null + if [ -z "${wait}" ]; then + kill -KILL ${QEMU_PID[$i]} 2>/dev/null + fi wait ${QEMU_PID[$i]} 2>/dev/null # silent kill + if [ -n "${wait}" ]; then + cat <&${QEMU_OUT[$i]} | _filter_testdir | _filter_qemu \ + | _filter_qemu_io | _filter_qmp + fi rm -f "${QEMU_FIFO_IN}_${i}" "${QEMU_FIFO_OUT}_${i}" eval "exec ${QEMU_IN[$i]}<&-" # close file descriptors eval "exec ${QEMU_OUT[$i]}<&-" diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 9c49deb..22d3514 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -153,7 +153,7 @@ _make_test_img() # Start an NBD server on the image file, which is what we'll be talking to if [ $IMGPROTO = "nbd" ]; then - eval "$QEMU_NBD -v -t -b 127.0.0.1 -p 10810 $TEST_IMG_FILE &" + eval "$QEMU_NBD -v -t -b 127.0.0.1 -p 10810 -f $IMGFMT $TEST_IMG_FILE &" QEMU_NBD_PID=$! sleep 1 # FIXME: qemu-nbd needs to be listening before we continue fi @@ -175,7 +175,9 @@ _cleanup_test_img() case "$IMGPROTO" in nbd) - kill $QEMU_NBD_PID + if [ -n "$QEMU_NBD_PID" ]; then + kill $QEMU_NBD_PID + fi rm -f "$TEST_IMG_FILE" ;; file) @@ -213,6 +215,13 @@ _check_test_img() _img_info() { + if [[ "$1" == "--format-specific" ]]; then + local format_specific=1 + shift + else + local format_specific=0 + fi + discard=0 regex_json_spec_start='^ *"format-specific": \{' $QEMU_IMG info "$@" "$TEST_IMG" 2>&1 | \ @@ -222,7 +231,9 @@ _img_info() -e "/^disk size:/ D" \ -e "/actual-size/ D" | \ while IFS='' read line; do - if [[ $line == "Format specific information:" ]]; then + if [[ $format_specific == 1 ]]; then + discard=0 + elif [[ $line == "Format specific information:" ]]; then discard=1 elif [[ $line =~ $regex_json_spec_start ]]; then discard=2 @@ -479,4 +490,4 @@ _die() } # make sure this script returns success -/bin/true +true diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 7dfe469..4c6d9ef 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -12,7 +12,7 @@ 003 rw auto 004 rw auto quick 005 img auto quick -006 img auto +# 006 was removed, do not reuse 007 snapshot auto 008 rw auto quick 009 rw auto quick @@ -22,7 +22,7 @@ 013 rw auto 014 rw auto 015 rw snapshot auto -016 rw auto quick +# 016 was removed, do not reuse 017 rw backing auto quick 018 rw backing auto quick 019 rw backing auto quick @@ -97,8 +97,10 @@ 088 rw auto quick 089 rw auto quick 090 rw auto quick -091 rw auto quick +091 rw auto 092 rw auto quick +093 auto +094 rw auto quick 095 rw auto quick 097 rw auto backing 098 rw auto backing quick @@ -111,4 +113,17 @@ 105 rw auto quick 107 rw auto quick 108 rw auto quick +109 rw auto +110 rw auto backing quick 111 rw auto quick +112 rw auto +113 rw auto quick +114 rw auto quick +115 rw auto +116 rw auto quick +121 rw auto +123 rw auto quick +128 rw auto quick +130 rw auto quick +132 rw auto quick +135 rw auto diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index f57f154..0ddc513 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -21,8 +21,11 @@ import re import subprocess import string import unittest -import sys; sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'scripts', 'qmp')) +import sys +sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'scripts')) +sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'scripts', 'qmp')) import qmp +import qtest import struct __all__ = ['imgfmt', 'imgproto', 'test_dir' 'qemu_img', 'qemu_io', @@ -75,18 +78,38 @@ def create_image(name, size): i = i + 512 file.close() +# Test if 'match' is a recursive subset of 'event' +def event_match(event, match=None): + if match is None: + return True + + for key in match: + if key in event: + if isinstance(event[key], dict): + if not event_match(event[key], match[key]): + return False + elif event[key] != match[key]: + return False + else: + return False + + return True + class VM(object): '''A QEMU VM''' def __init__(self): self._monitor_path = os.path.join(test_dir, 'qemu-mon.%d' % os.getpid()) self._qemu_log_path = os.path.join(test_dir, 'qemu-log.%d' % os.getpid()) + self._qtest_path = os.path.join(test_dir, 'qemu-qtest.%d' % os.getpid()) self._args = qemu_args + ['-chardev', 'socket,id=mon,path=' + self._monitor_path, '-mon', 'chardev=mon,mode=control', - '-qtest', 'stdio', '-machine', 'accel=qtest', + '-qtest', 'unix:path=' + self._qtest_path, + '-machine', 'accel=qtest', '-display', 'none', '-vga', 'none'] self._num_drives = 0 + self._events = [] # This can be used to add an unused monitor instance. def add_monitor_telnet(self, ip, port): @@ -160,9 +183,11 @@ class VM(object): qemulog = open(self._qemu_log_path, 'wb') try: self._qmp = qmp.QEMUMonitorProtocol(self._monitor_path, server=True) + self._qtest = qtest.QEMUQtestProtocol(self._qtest_path, server=True) self._popen = subprocess.Popen(self._args, stdin=devnull, stdout=qemulog, stderr=subprocess.STDOUT) self._qmp.accept() + self._qtest.accept() except: os.remove(self._monitor_path) raise @@ -173,28 +198,56 @@ class VM(object): self._qmp.cmd('quit') self._popen.wait() os.remove(self._monitor_path) + os.remove(self._qtest_path) os.remove(self._qemu_log_path) self._popen = None underscore_to_dash = string.maketrans('_', '-') - def qmp(self, cmd, **args): + def qmp(self, cmd, conv_keys=True, **args): '''Invoke a QMP command and return the result dict''' qmp_args = dict() for k in args.keys(): - qmp_args[k.translate(self.underscore_to_dash)] = args[k] + if conv_keys: + qmp_args[k.translate(self.underscore_to_dash)] = args[k] + else: + qmp_args[k] = args[k] return self._qmp.cmd(cmd, args=qmp_args) + def qtest(self, cmd): + '''Send a qtest command to guest''' + return self._qtest.cmd(cmd) + def get_qmp_event(self, wait=False): '''Poll for one queued QMP events and return it''' + if len(self._events) > 0: + return self._events.pop(0) return self._qmp.pull_event(wait=wait) def get_qmp_events(self, wait=False): '''Poll for queued QMP events and return a list of dicts''' events = self._qmp.get_events(wait=wait) + events.extend(self._events) + del self._events[:] self._qmp.clear_events() return events + def event_wait(self, name='BLOCK_JOB_COMPLETED', timeout=60.0, match=None): + # Search cached events + for event in self._events: + if (event['event'] == name) and event_match(event, match): + self._events.remove(event) + return event + + # Poll for new events + while True: + event = self._qmp.pull_event(wait=timeout) + if (event['event'] == name) and event_match(event, match): + return event + self._events.append(event) + + return None + index_re = re.compile(r'([^\[]+)\[([^\]]+)\]') class QMPTestCase(unittest.TestCase): @@ -273,6 +326,29 @@ class QMPTestCase(unittest.TestCase): self.assert_no_active_block_jobs() return event + def wait_ready(self, drive='drive0'): + '''Wait until a block job BLOCK_JOB_READY event''' + f = {'data': {'type': 'mirror', 'device': drive } } + event = self.vm.event_wait(name='BLOCK_JOB_READY', match=f) + + def wait_ready_and_cancel(self, drive='drive0'): + self.wait_ready(drive=drive) + event = self.cancel_and_wait(drive=drive) + self.assertEquals(event['event'], 'BLOCK_JOB_COMPLETED') + self.assert_qmp(event, 'data/type', 'mirror') + self.assert_qmp(event, 'data/offset', event['data']['len']) + + def complete_and_wait(self, drive='drive0', wait_ready=True): + '''Complete a block job and wait for it to finish''' + if wait_ready: + self.wait_ready(drive=drive) + + result = self.vm.qmp('block-job-complete', device=drive) + self.assert_qmp(result, 'return', {}) + + event = self.wait_until_completed(drive=drive) + self.assert_qmp(event, 'data/type', 'mirror') + def notrun(reason): '''Skip this test suite''' # Each test in qemu-iotests has a number ("seq") @@ -282,12 +358,15 @@ def notrun(reason): print '%s not run: %s' % (seq, reason) sys.exit(0) -def main(supported_fmts=[]): +def main(supported_fmts=[], supported_oses=['linux']): '''Run tests''' if supported_fmts and (imgfmt not in supported_fmts): notrun('not suitable for this image format: %s' % imgfmt) + if True not in [sys.platform.startswith(x) for x in supported_oses]: + notrun('not suitable for this OS: %s' % sys.platform) + # We need to filter out the time taken from the output so that qemu-iotest # can reliably diff the results against master output. import StringIO diff --git a/tests/qemu-iotests/qcow2.py b/tests/qemu-iotests/qcow2.py index 2058596..9cc4cf7 100755 --- a/tests/qemu-iotests/qcow2.py +++ b/tests/qemu-iotests/qcow2.py @@ -7,6 +7,10 @@ import string class QcowHeaderExtension: def __init__(self, magic, length, data): + if length % 8 != 0: + padding = 8 - (length % 8) + data += "\0" * padding + self.magic = magic self.length = length self.data = data diff --git a/tests/qemu-iotests/sample_images/afl5.img.bz2 b/tests/qemu-iotests/sample_images/afl5.img.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..1614348865e5b2cfcb0340eab9474841717be2c5 GIT binary patch literal 175 zcmV;g08sxzT4*^jL0KkKSqT!KVgLXwfB*jgAVdfNFaTf(B!Frw|3pDR00;sy03ZSY z3IG5B1Sp^YbSh$=r=cgVwVQS9W$Kd2?dJ>H~Ej+J=Q^ dtom#MI_=bg;S5HeF^MqnF64@Ep&$|^KE!naKsf*a literal 0 HcmV?d00001 diff --git a/tests/qemu-iotests/sample_images/grub_mbr.raw.bz2 b/tests/qemu-iotests/sample_images/grub_mbr.raw.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..8585878bcdec2fdec18a3f31b360fa02450f9ff8 GIT binary patch literal 552 zcmV+@0@wXQT4*^jL0KkKSwNaEzW@N*fB*mg$;IITbk6q05>#(0-zI&1LXc)uRc-r% za7r+E4V|z7S+!=yQ}opudP7FiG&ZNCdYXEg21n^kMg-B3gwdLV5NKq2m?x<80Mzvd zsL(|;N#rt3l-imaWN5-@$jJ3D1ZWtVWMmj2iKd1l)X>S0X_F>|RQ9GqYI>fKJtNd; zGy_4VfCEF+&;WX#pa3*IN1_@5>S*+XH3aQ=P5?4O0ijGiL`>qe1qoLo5!)DYBptC@ z+Jubg%RxwjAIenMxq5t32G*?|XTt}}is%*Dg1s(t_P&J9ATn1pE#u)JV_k{N5y8C= zClpbWiblSE$!@~kYk)RpaO!T>1A0jG=Th}3Jwstf#F9_?-MYw+!zsls=Mye&e3gpn z&;YOw2VQbO5kni5;1oU(zuKD4&hMf|Oru8~8mo};Uh!h3dN~4v;5ZQayDQn6wA{gciKjOA6ny0++U~ zSP%~(`he1YAh80`JcgnkD$}Hl$**xH$q~|k6aePg1t67ianhAq=gsgf;|}p;onRF# z72qJ@0A8CoCq|#ci_vV#Po9#E15YrP#~bq2QB)$Rr2!^X7*Y&5go)T4sxo15|2ZMN qcE#a47jc3&PubI;fr_q6iA=@3Nn~{%uXwRP#oUoj6eJKPi|@d{(*wN# literal 0 HcmV?d00001 diff --git a/tests/rcutorture.c b/tests/rcutorture.c new file mode 100644 index 0000000..d6b304d --- /dev/null +++ b/tests/rcutorture.c @@ -0,0 +1,463 @@ +/* + * rcutorture.c: simple user-level performance/stress test of RCU. + * + * Usage: + * ./rcu rperf [ ] + * Run a read-side performance test with the specified + * number of readers for seconds. + * ./rcu uperf [ ] + * Run an update-side performance test with the specified + * number of updaters and specified duration. + * ./rcu perf [ ] + * Run a combined read/update performance test with the specified + * number of readers and one updater and specified duration. + * + * The above tests produce output as follows: + * + * n_reads: 46008000 n_updates: 146026 nreaders: 2 nupdaters: 1 duration: 1 + * ns/read: 43.4707 ns/update: 6848.1 + * + * The first line lists the total number of RCU reads and updates executed + * during the test, the number of reader threads, the number of updater + * threads, and the duration of the test in seconds. The second line + * lists the average duration of each type of operation in nanoseconds, + * or "nan" if the corresponding type of operation was not performed. + * + * ./rcu stress [ ] + * Run a stress test with the specified number of readers and + * one updater. + * + * This test produces output as follows: + * + * n_reads: 114633217 n_updates: 3903415 n_mberror: 0 + * rcu_stress_count: 114618391 14826 0 0 0 0 0 0 0 0 0 + * + * The first line lists the number of RCU read and update operations + * executed, followed by the number of memory-ordering violations + * (which will be zero in a correct RCU implementation). The second + * line lists the number of readers observing progressively more stale + * data. A correct RCU implementation will have all but the first two + * numbers non-zero. + * + * 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. + * + * Copyright (c) 2008 Paul E. McKenney, IBM Corporation. + */ + +/* + * Test variables. + */ + +#include +#include +#include +#include +#include "qemu/atomic.h" +#include "qemu/rcu.h" +#include "qemu/compiler.h" +#include "qemu/thread.h" + +long long n_reads = 0LL; +long n_updates = 0L; +int nthreadsrunning; + +#define GOFLAG_INIT 0 +#define GOFLAG_RUN 1 +#define GOFLAG_STOP 2 + +static volatile int goflag = GOFLAG_INIT; + +#define RCU_READ_RUN 1000 + +#define NR_THREADS 100 +static QemuMutex counts_mutex; +static QemuThread threads[NR_THREADS]; +static struct rcu_reader_data *data[NR_THREADS]; +static int n_threads; + +static void create_thread(void *(*func)(void *)) +{ + if (n_threads >= NR_THREADS) { + fprintf(stderr, "Thread limit of %d exceeded!\n", NR_THREADS); + exit(-1); + } + qemu_thread_create(&threads[n_threads], "test", func, &data[n_threads], + QEMU_THREAD_JOINABLE); + n_threads++; +} + +static void wait_all_threads(void) +{ + int i; + + for (i = 0; i < n_threads; i++) { + qemu_thread_join(&threads[i]); + } + n_threads = 0; +} + +/* + * Performance test. + */ + +static void *rcu_read_perf_test(void *arg) +{ + int i; + long long n_reads_local = 0; + + rcu_register_thread(); + + *(struct rcu_reader_data **)arg = &rcu_reader; + atomic_inc(&nthreadsrunning); + while (goflag == GOFLAG_INIT) { + g_usleep(1000); + } + while (goflag == GOFLAG_RUN) { + for (i = 0; i < RCU_READ_RUN; i++) { + rcu_read_lock(); + rcu_read_unlock(); + } + n_reads_local += RCU_READ_RUN; + } + qemu_mutex_lock(&counts_mutex); + n_reads += n_reads_local; + qemu_mutex_unlock(&counts_mutex); + + rcu_unregister_thread(); + return NULL; +} + +static void *rcu_update_perf_test(void *arg) +{ + long long n_updates_local = 0; + + rcu_register_thread(); + + *(struct rcu_reader_data **)arg = &rcu_reader; + atomic_inc(&nthreadsrunning); + while (goflag == GOFLAG_INIT) { + g_usleep(1000); + } + while (goflag == GOFLAG_RUN) { + synchronize_rcu(); + n_updates_local++; + } + qemu_mutex_lock(&counts_mutex); + n_updates += n_updates_local; + qemu_mutex_unlock(&counts_mutex); + + rcu_unregister_thread(); + return NULL; +} + +static void perftestinit(void) +{ + nthreadsrunning = 0; +} + +static void perftestrun(int nthreads, int duration, int nreaders, int nupdaters) +{ + while (atomic_read(&nthreadsrunning) < nthreads) { + g_usleep(1000); + } + goflag = GOFLAG_RUN; + g_usleep(duration * G_USEC_PER_SEC); + goflag = GOFLAG_STOP; + wait_all_threads(); + printf("n_reads: %lld n_updates: %ld nreaders: %d nupdaters: %d duration: %d\n", + n_reads, n_updates, nreaders, nupdaters, duration); + printf("ns/read: %g ns/update: %g\n", + ((duration * 1000*1000*1000.*(double)nreaders) / + (double)n_reads), + ((duration * 1000*1000*1000.*(double)nupdaters) / + (double)n_updates)); + exit(0); +} + +static void perftest(int nreaders, int duration) +{ + int i; + + perftestinit(); + for (i = 0; i < nreaders; i++) { + create_thread(rcu_read_perf_test); + } + create_thread(rcu_update_perf_test); + perftestrun(i + 1, duration, nreaders, 1); +} + +static void rperftest(int nreaders, int duration) +{ + int i; + + perftestinit(); + for (i = 0; i < nreaders; i++) { + create_thread(rcu_read_perf_test); + } + perftestrun(i, duration, nreaders, 0); +} + +static void uperftest(int nupdaters, int duration) +{ + int i; + + perftestinit(); + for (i = 0; i < nupdaters; i++) { + create_thread(rcu_update_perf_test); + } + perftestrun(i, duration, 0, nupdaters); +} + +/* + * Stress test. + */ + +#define RCU_STRESS_PIPE_LEN 10 + +struct rcu_stress { + int pipe_count; + int mbtest; +}; + +struct rcu_stress rcu_stress_array[RCU_STRESS_PIPE_LEN] = { { 0 } }; +struct rcu_stress *rcu_stress_current; +int rcu_stress_idx; + +int n_mberror; +long long rcu_stress_count[RCU_STRESS_PIPE_LEN + 1]; + + +static void *rcu_read_stress_test(void *arg) +{ + int i; + int itercnt = 0; + struct rcu_stress *p; + int pc; + long long n_reads_local = 0; + long long rcu_stress_local[RCU_STRESS_PIPE_LEN + 1] = { 0 }; + volatile int garbage = 0; + + rcu_register_thread(); + + *(struct rcu_reader_data **)arg = &rcu_reader; + while (goflag == GOFLAG_INIT) { + g_usleep(1000); + } + while (goflag == GOFLAG_RUN) { + rcu_read_lock(); + p = atomic_rcu_read(&rcu_stress_current); + if (p->mbtest == 0) { + n_mberror++; + } + rcu_read_lock(); + for (i = 0; i < 100; i++) { + garbage++; + } + rcu_read_unlock(); + pc = p->pipe_count; + rcu_read_unlock(); + if ((pc > RCU_STRESS_PIPE_LEN) || (pc < 0)) { + pc = RCU_STRESS_PIPE_LEN; + } + rcu_stress_local[pc]++; + n_reads_local++; + if ((++itercnt % 0x1000) == 0) { + synchronize_rcu(); + } + } + qemu_mutex_lock(&counts_mutex); + n_reads += n_reads_local; + for (i = 0; i <= RCU_STRESS_PIPE_LEN; i++) { + rcu_stress_count[i] += rcu_stress_local[i]; + } + qemu_mutex_unlock(&counts_mutex); + + rcu_unregister_thread(); + return NULL; +} + +static void *rcu_update_stress_test(void *arg) +{ + int i; + struct rcu_stress *p; + + rcu_register_thread(); + + *(struct rcu_reader_data **)arg = &rcu_reader; + while (goflag == GOFLAG_INIT) { + g_usleep(1000); + } + while (goflag == GOFLAG_RUN) { + i = rcu_stress_idx + 1; + if (i >= RCU_STRESS_PIPE_LEN) { + i = 0; + } + p = &rcu_stress_array[i]; + p->mbtest = 0; + smp_mb(); + p->pipe_count = 0; + p->mbtest = 1; + atomic_rcu_set(&rcu_stress_current, p); + rcu_stress_idx = i; + for (i = 0; i < RCU_STRESS_PIPE_LEN; i++) { + if (i != rcu_stress_idx) { + rcu_stress_array[i].pipe_count++; + } + } + synchronize_rcu(); + n_updates++; + } + + rcu_unregister_thread(); + return NULL; +} + +static void *rcu_fake_update_stress_test(void *arg) +{ + rcu_register_thread(); + + *(struct rcu_reader_data **)arg = &rcu_reader; + while (goflag == GOFLAG_INIT) { + g_usleep(1000); + } + while (goflag == GOFLAG_RUN) { + synchronize_rcu(); + g_usleep(1000); + } + + rcu_unregister_thread(); + return NULL; +} + +static void stresstest(int nreaders, int duration) +{ + int i; + + rcu_stress_current = &rcu_stress_array[0]; + rcu_stress_current->pipe_count = 0; + rcu_stress_current->mbtest = 1; + for (i = 0; i < nreaders; i++) { + create_thread(rcu_read_stress_test); + } + create_thread(rcu_update_stress_test); + for (i = 0; i < 5; i++) { + create_thread(rcu_fake_update_stress_test); + } + goflag = GOFLAG_RUN; + g_usleep(duration * G_USEC_PER_SEC); + goflag = GOFLAG_STOP; + wait_all_threads(); + printf("n_reads: %lld n_updates: %ld n_mberror: %d\n", + n_reads, n_updates, n_mberror); + printf("rcu_stress_count:"); + for (i = 0; i <= RCU_STRESS_PIPE_LEN; i++) { + printf(" %lld", rcu_stress_count[i]); + } + printf("\n"); + exit(0); +} + +/* GTest interface */ + +static void gtest_stress(int nreaders, int duration) +{ + int i; + + rcu_stress_current = &rcu_stress_array[0]; + rcu_stress_current->pipe_count = 0; + rcu_stress_current->mbtest = 1; + for (i = 0; i < nreaders; i++) { + create_thread(rcu_read_stress_test); + } + create_thread(rcu_update_stress_test); + for (i = 0; i < 5; i++) { + create_thread(rcu_fake_update_stress_test); + } + goflag = GOFLAG_RUN; + g_usleep(duration * G_USEC_PER_SEC); + goflag = GOFLAG_STOP; + wait_all_threads(); + g_assert_cmpint(n_mberror, ==, 0); + for (i = 2; i <= RCU_STRESS_PIPE_LEN; i++) { + g_assert_cmpint(rcu_stress_count[i], ==, 0); + } +} + +static void gtest_stress_1_1(void) +{ + gtest_stress(1, 1); +} + +static void gtest_stress_10_1(void) +{ + gtest_stress(10, 1); +} + +static void gtest_stress_1_5(void) +{ + gtest_stress(1, 5); +} + +static void gtest_stress_10_5(void) +{ + gtest_stress(10, 5); +} + +/* + * Mainprogram. + */ + +static void usage(int argc, char *argv[]) +{ + fprintf(stderr, "Usage: %s [nreaders [ perf | stress ] ]\n", argv[0]); + exit(-1); +} + +int main(int argc, char *argv[]) +{ + int nreaders = 1; + int duration = 1; + + qemu_mutex_init(&counts_mutex); + if (argc >= 2 && argv[1][0] == '-') { + g_test_init(&argc, &argv, NULL); + if (g_test_quick()) { + g_test_add_func("/rcu/torture/1reader", gtest_stress_1_1); + g_test_add_func("/rcu/torture/10readers", gtest_stress_10_1); + } else { + g_test_add_func("/rcu/torture/1reader", gtest_stress_1_5); + g_test_add_func("/rcu/torture/10readers", gtest_stress_10_5); + } + return g_test_run(); + } + + if (argc >= 2) { + nreaders = strtoul(argv[1], NULL, 0); + } + if (argc > 3) { + duration = strtoul(argv[3], NULL, 0); + } + if (argc < 3 || strcmp(argv[2], "stress") == 0) { + stresstest(nreaders, duration); + } else if (strcmp(argv[2], "rperf") == 0) { + rperftest(nreaders, duration); + } else if (strcmp(argv[2], "uperf") == 0) { + uperftest(nreaders, duration); + } else if (strcmp(argv[2], "perf") == 0) { + perftest(nreaders, duration); + } + usage(argc, argv); + return 0; +} diff --git a/tests/rtl8139-test.c b/tests/rtl8139-test.c index f6a1be3..4e0bf02 100644 --- a/tests/rtl8139-test.c +++ b/tests/rtl8139-test.c @@ -10,19 +10,200 @@ #include #include #include "libqtest.h" +#include "libqos/pci-pc.h" #include "qemu/osdep.h" +#include "qemu-common.h" /* Tests only initialization so far. TODO: Replace with functional tests */ static void nop(void) { } +#define CLK 33000000 +#define NS_PER_SEC 1000000000ULL + +static QPCIBus *pcibus; +static QPCIDevice *dev; +static void *dev_base; + +static void save_fn(QPCIDevice *dev, int devfn, void *data) +{ + QPCIDevice **pdev = (QPCIDevice **) data; + + *pdev = dev; +} + +static QPCIDevice *get_device(void) +{ + QPCIDevice *dev; + + pcibus = qpci_init_pc(); + qpci_device_foreach(pcibus, 0x10ec, 0x8139, save_fn, &dev); + g_assert(dev != NULL); + + return dev; +} + +#define PORT(name, len, val) \ +static unsigned __attribute__((unused)) in_##name(void) \ +{ \ + unsigned res = qpci_io_read##len(dev, dev_base+(val)); \ + g_test_message("*%s -> %x\n", #name, res); \ + return res; \ +} \ +static void out_##name(unsigned v) \ +{ \ + g_test_message("%x -> *%s\n", v, #name); \ + qpci_io_write##len(dev, dev_base+(val), v); \ +} + +PORT(Timer, l, 0x48) +PORT(IntrMask, w, 0x3c) +PORT(IntrStatus, w, 0x3E) +PORT(TimerInt, l, 0x54) + +#define fatal(...) do { g_test_message(__VA_ARGS__); g_assert(0); } while (0) + +static void test_timer(void) +{ + const unsigned from = 0.95 * CLK; + const unsigned to = 1.6 * CLK; + unsigned prev, curr, next; + unsigned cnt, diff; + + out_IntrMask(0); + + in_IntrStatus(); + in_Timer(); + in_Timer(); + + /* Test 1. test counter continue and continue */ + out_TimerInt(0); /* disable timer */ + out_IntrStatus(0x4000); + out_Timer(12345); /* reset timer to 0 */ + curr = in_Timer(); + if (curr > 0.1 * CLK) { + fatal("time too big %u\n", curr); + } + for (cnt = 0; ; ) { + clock_step(1 * NS_PER_SEC); + prev = curr; + curr = in_Timer(); + + /* test skip is in a specific range */ + diff = (curr-prev) & 0xffffffffu; + if (diff < from || diff > to) { + fatal("Invalid diff %u (%u-%u)\n", diff, from, to); + } + if (curr < prev && ++cnt == 3) { + break; + } + } + + /* Test 2. Check we didn't get an interrupt with TimerInt == 0 */ + if (in_IntrStatus() & 0x4000) { + fatal("got an interrupt\n"); + } + + /* Test 3. Setting TimerInt to 1 and Timer to 0 get interrupt */ + out_TimerInt(1); + out_Timer(0); + clock_step(40); + if ((in_IntrStatus() & 0x4000) == 0) { + fatal("we should have an interrupt here!\n"); + } + + /* Test 3. Check acknowledge */ + out_IntrStatus(0x4000); + if (in_IntrStatus() & 0x4000) { + fatal("got an interrupt\n"); + } + + /* Test. Status set after Timer reset */ + out_Timer(0); + out_TimerInt(0); + out_IntrStatus(0x4000); + curr = in_Timer(); + out_TimerInt(curr + 0.5 * CLK); + clock_step(1 * NS_PER_SEC); + out_Timer(0); + if ((in_IntrStatus() & 0x4000) == 0) { + fatal("we should have an interrupt here!\n"); + } + + /* Test. Status set after TimerInt reset */ + out_Timer(0); + out_TimerInt(0); + out_IntrStatus(0x4000); + curr = in_Timer(); + out_TimerInt(curr + 0.5 * CLK); + clock_step(1 * NS_PER_SEC); + out_TimerInt(0); + if ((in_IntrStatus() & 0x4000) == 0) { + fatal("we should have an interrupt here!\n"); + } + + /* Test 4. Increment TimerInt we should see an interrupt */ + curr = in_Timer(); + next = curr + 5.0 * CLK; + out_TimerInt(next); + for (cnt = 0; ; ) { + clock_step(1 * NS_PER_SEC); + prev = curr; + curr = in_Timer(); + diff = (curr-prev) & 0xffffffffu; + if (diff < from || diff > to) { + fatal("Invalid diff %u (%u-%u)\n", diff, from, to); + } + if (cnt < 3 && curr > next) { + if ((in_IntrStatus() & 0x4000) == 0) { + fatal("we should have an interrupt here!\n"); + } + out_IntrStatus(0x4000); + next = curr + 5.0 * CLK; + out_TimerInt(next); + if (++cnt == 3) { + out_TimerInt(1); + } + /* Test 5. Second time we pass from 0 should see an interrupt */ + } else if (cnt >= 3 && curr < prev) { + /* here we should have an interrupt */ + if ((in_IntrStatus() & 0x4000) == 0) { + fatal("we should have an interrupt here!\n"); + } + out_IntrStatus(0x4000); + if (++cnt == 5) { + break; + } + } + } + + g_test_message("Everythink is ok!\n"); +} + + +static void test_init(void) +{ + uint64_t barsize; + + dev = get_device(); + + dev_base = qpci_iomap(dev, 0, &barsize); + + g_assert(dev_base != NULL); + + qpci_device_enable(dev); + + test_timer(); +} + int main(int argc, char **argv) { int ret; g_test_init(&argc, &argv, NULL); qtest_add_func("/rtl8139/nop", nop); + qtest_add_func("/rtl8139/timer", test_init); qtest_start("-device rtl8139"); ret = g_test_run(); diff --git a/tests/tcg/xtensa/test_mmu.S b/tests/tcg/xtensa/test_mmu.S index 58c5bca..a15316f 100644 --- a/tests/tcg/xtensa/test_mmu.S +++ b/tests/tcg/xtensa/test_mmu.S @@ -641,7 +641,7 @@ test cross_page_tb witlb a2, a3 wdtlb a2, a3 - movi a2, 0x00007ffd + movi a2, 0x00007ffc movi a3, 20f movi a4, 21f sub a4, a4, a3 @@ -651,7 +651,7 @@ test cross_page_tb addi a2, a2, 1 addi a3, a3, 1 1: - movi a2, 0x00007ffd + movi a2, 0x00007ffc movi a3, 0x00008000 /* DTLB: OK, ITLB: OK */ jx a2 @@ -668,10 +668,10 @@ test cross_page_tb movi a3, 1 assert eq, a2, a3 rsr a2, epc1 - movi a3, 0x8000 + movi a3, 0x7fff assert eq, a2, a3 rsr a2, excsave1 - movi a3, 0x00007ffd + movi a3, 0x00007ffc assert ne, a2, a3 reset_ps @@ -680,7 +680,7 @@ test cross_page_tb movi a2, 0x0400000c /* PPN */ movi a3, 0x00008000 /* VPN */ wdtlb a2, a3 - movi a2, 0x00007ffd + movi a2, 0x00007ffc movi a3, 0x00008000 /* DTLB: FAIL, ITLB: OK */ jx a2 @@ -689,10 +689,10 @@ test cross_page_tb movi a3, 28 assert eq, a2, a3 rsr a2, epc1 - movi a3, 0x7ffd + movi a3, 0x7ffc assert eq, a2, a3 rsr a2, excsave1 - movi a3, 0x00007ffd + movi a3, 0x00007ffc assert eq, a2, a3 reset_ps @@ -703,7 +703,7 @@ test cross_page_tb witlb a2, a3 movi a2, 0x04000003 /* PPN */ wdtlb a2, a3 - movi a2, 0x00007ffd + movi a2, 0x00007ffc movi a3, 0x00008000 /* DTLB: OK, ITLB: FAIL */ jx a2 @@ -712,10 +712,10 @@ test cross_page_tb movi a3, 20 assert eq, a2, a3 rsr a2, epc1 - movi a3, 0x8000 + movi a3, 0x7fff assert eq, a2, a3 rsr a2, excsave1 - movi a3, 0x00007ffd + movi a3, 0x00007ffc assert ne, a2, a3 reset_ps @@ -724,7 +724,7 @@ test cross_page_tb movi a2, 0x0400000c /* PPN */ movi a3, 0x00008000 /* VPN */ wdtlb a2, a3 - movi a2, 0x00007ffd + movi a2, 0x00007ffc movi a3, 0x00008000 /* DTLB: FAIL, ITLB: FAIL */ jx a2 @@ -733,10 +733,10 @@ test cross_page_tb movi a3, 28 assert eq, a2, a3 rsr a2, epc1 - movi a3, 0x7ffd + movi a3, 0x7ffc assert eq, a2, a3 rsr a2, excsave1 - movi a3, 0x00007ffd + movi a3, 0x00007ffc assert eq, a2, a3 test_end diff --git a/tests/test-coroutine.c b/tests/test-coroutine.c index e22fae1..b552d9f 100644 --- a/tests/test-coroutine.c +++ b/tests/test-coroutine.c @@ -13,6 +13,7 @@ #include #include "block/coroutine.h" +#include "block/coroutine_int.h" /* * Check that qemu_in_coroutine() works @@ -122,6 +123,30 @@ static void test_yield(void) g_assert_cmpint(i, ==, 5); /* coroutine must yield 5 times */ } +static void coroutine_fn c2_fn(void *opaque) +{ + qemu_coroutine_yield(); +} + +static void coroutine_fn c1_fn(void *opaque) +{ + Coroutine *c2 = opaque; + qemu_coroutine_enter(c2, NULL); +} + +static void test_co_queue(void) +{ + Coroutine *c1; + Coroutine *c2; + + c1 = qemu_coroutine_create(c1_fn); + c2 = qemu_coroutine_create(c2_fn); + + qemu_coroutine_enter(c1, c2); + memset(c1, 0xff, sizeof(Coroutine)); + qemu_coroutine_enter(c2, NULL); +} + /* * Check that creation, enter, and return work */ @@ -337,12 +362,13 @@ static void perf_cost(void) "%luns per coroutine", maxcycles, duration, ops, - (unsigned long)(1000000000 * duration) / maxcycles); + (unsigned long)(1000000000.0 * duration / maxcycles)); } int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); + g_test_add_func("/basic/co_queue", test_co_queue); g_test_add_func("/basic/lifecycle", test_lifecycle); g_test_add_func("/basic/yield", test_yield); g_test_add_func("/basic/nesting", test_nesting); diff --git a/tests/test-qemu-opts.c b/tests/test-qemu-opts.c index ca08ac5..da56492 100644 --- a/tests/test-qemu-opts.c +++ b/tests/test-qemu-opts.c @@ -148,13 +148,13 @@ static void test_qemu_opt_get(void) opt = qemu_opt_get(opts, "str2"); g_assert(opt == NULL); - qemu_opt_set(opts, "str2", "value"); + qemu_opt_set(opts, "str2", "value", &error_abort); /* now we have set str2, should know about it */ opt = qemu_opt_get(opts, "str2"); g_assert_cmpstr(opt, ==, "value"); - qemu_opt_set(opts, "str2", "value2"); + qemu_opt_set(opts, "str2", "value2", &error_abort); /* having reset the value, the returned should be the reset one */ opt = qemu_opt_get(opts, "str2"); @@ -169,10 +169,10 @@ static void test_qemu_opt_get(void) static void test_qemu_opt_get_bool(void) { + Error *err = NULL; QemuOptsList *list; QemuOpts *opts; bool opt; - int ret; list = qemu_find_opts("opts_list_02"); g_assert(list != NULL); @@ -192,16 +192,16 @@ static void test_qemu_opt_get_bool(void) opt = qemu_opt_get_bool(opts, "bool1", false); g_assert(opt == false); - ret = qemu_opt_set_bool(opts, "bool1", true); - g_assert(ret == 0); + qemu_opt_set_bool(opts, "bool1", true, &err); + g_assert(!err); /* now we have set bool1, should know about it */ opt = qemu_opt_get_bool(opts, "bool1", false); g_assert(opt == true); /* having reset the value, opt should be the reset one not defval */ - ret = qemu_opt_set_bool(opts, "bool1", false); - g_assert(ret == 0); + qemu_opt_set_bool(opts, "bool1", false, &err); + g_assert(!err); opt = qemu_opt_get_bool(opts, "bool1", true); g_assert(opt == false); @@ -215,10 +215,10 @@ static void test_qemu_opt_get_bool(void) static void test_qemu_opt_get_number(void) { + Error *err = NULL; QemuOptsList *list; QemuOpts *opts; uint64_t opt; - int ret; list = qemu_find_opts("opts_list_01"); g_assert(list != NULL); @@ -238,16 +238,16 @@ static void test_qemu_opt_get_number(void) opt = qemu_opt_get_number(opts, "number1", 5); g_assert(opt == 5); - ret = qemu_opt_set_number(opts, "number1", 10); - g_assert(ret == 0); + qemu_opt_set_number(opts, "number1", 10, &err); + g_assert(!err); /* now we have set number1, should know about it */ opt = qemu_opt_get_number(opts, "number1", 5); g_assert(opt == 10); /* having reset it, the returned should be the reset one not defval */ - ret = qemu_opt_set_number(opts, "number1", 15); - g_assert(ret == 0); + qemu_opt_set_number(opts, "number1", 15, &err); + g_assert(!err); opt = qemu_opt_get_number(opts, "number1", 5); g_assert(opt == 15); @@ -331,7 +331,7 @@ static void test_qemu_opt_unset(void) g_assert_cmpstr(value, ==, "value"); /* reset it to value2 */ - qemu_opt_set(opts, "key", "value2"); + qemu_opt_set(opts, "key", "value2", &error_abort); value = qemu_opt_get(opts, "key"); g_assert_cmpstr(value, ==, "value2"); @@ -349,10 +349,10 @@ static void test_qemu_opt_unset(void) static void test_qemu_opts_reset(void) { + Error *err = NULL; QemuOptsList *list; QemuOpts *opts; uint64_t opt; - int ret; list = qemu_find_opts("opts_list_01"); g_assert(list != NULL); @@ -372,8 +372,8 @@ static void test_qemu_opts_reset(void) opt = qemu_opt_get_number(opts, "number1", 5); g_assert(opt == 5); - ret = qemu_opt_set_number(opts, "number1", 10); - g_assert(ret == 0); + qemu_opt_set_number(opts, "number1", 10, &err); + g_assert(!err); /* now we have set number1, should know about it */ opt = qemu_opt_get_number(opts, "number1", 5); @@ -388,9 +388,9 @@ static void test_qemu_opts_reset(void) static void test_qemu_opts_set(void) { + Error *err = NULL; QemuOptsList *list; QemuOpts *opts; - int ret; const char *opt; list = qemu_find_opts("opts_list_01"); @@ -403,8 +403,8 @@ static void test_qemu_opts_set(void) g_assert(opts == NULL); /* implicitly create opts and set str3 value */ - ret = qemu_opts_set(list, NULL, "str3", "value"); - g_assert(ret == 0); + qemu_opts_set(list, NULL, "str3", "value", &err); + g_assert(!err); g_assert(!QTAILQ_EMPTY(&list->head)); /* get the just created opts */ diff --git a/tests/test-rcu-list.c b/tests/test-rcu-list.c new file mode 100644 index 0000000..4c5f62e --- /dev/null +++ b/tests/test-rcu-list.c @@ -0,0 +1,312 @@ +/* + * rcuq_test.c + * + * usage: rcuq_test + * + * 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. + * + * Copyright (c) 2013 Mike D. Day, IBM Corporation. + */ + +#include +#include +#include +#include +#include "qemu/atomic.h" +#include "qemu/rcu.h" +#include "qemu/compiler.h" +#include "qemu/osdep.h" +#include "qemu/thread.h" +#include "qemu/rcu_queue.h" + +/* + * Test variables. + */ + +static QemuMutex counts_mutex; +static long long n_reads = 0LL; +static long long n_updates = 0LL; +static long long n_reclaims = 0LL; +static long long n_nodes_removed = 0LL; +static long long n_nodes = 0LL; +static int g_test_in_charge = 0; + +static int nthreadsrunning; + +#define GOFLAG_INIT 0 +#define GOFLAG_RUN 1 +#define GOFLAG_STOP 2 + +static volatile int goflag = GOFLAG_INIT; + +#define RCU_READ_RUN 1000 +#define RCU_UPDATE_RUN 10 +#define NR_THREADS 100 +#define RCU_Q_LEN 100 + +static QemuThread threads[NR_THREADS]; +static struct rcu_reader_data *data[NR_THREADS]; +static int n_threads; + +static int select_random_el(int max) +{ + return (rand() % max); +} + + +static void create_thread(void *(*func)(void *)) +{ + if (n_threads >= NR_THREADS) { + fprintf(stderr, "Thread limit of %d exceeded!\n", NR_THREADS); + exit(-1); + } + qemu_thread_create(&threads[n_threads], "test", func, &data[n_threads], + QEMU_THREAD_JOINABLE); + n_threads++; +} + +static void wait_all_threads(void) +{ + int i; + + for (i = 0; i < n_threads; i++) { + qemu_thread_join(&threads[i]); + } + n_threads = 0; +} + + +struct list_element { + QLIST_ENTRY(list_element) entry; + struct rcu_head rcu; +}; + +static void reclaim_list_el(struct rcu_head *prcu) +{ + struct list_element *el = container_of(prcu, struct list_element, rcu); + g_free(el); + /* Accessed only from call_rcu thread. */ + n_reclaims++; +} + +static QLIST_HEAD(q_list_head, list_element) Q_list_head; + +static void *rcu_q_reader(void *arg) +{ + long long n_reads_local = 0; + struct list_element *el; + + *(struct rcu_reader_data **)arg = &rcu_reader; + atomic_inc(&nthreadsrunning); + while (goflag == GOFLAG_INIT) { + g_usleep(1000); + } + + while (goflag == GOFLAG_RUN) { + rcu_read_lock(); + QLIST_FOREACH_RCU(el, &Q_list_head, entry) { + n_reads_local++; + if (goflag == GOFLAG_STOP) { + break; + } + } + rcu_read_unlock(); + + g_usleep(100); + } + qemu_mutex_lock(&counts_mutex); + n_reads += n_reads_local; + qemu_mutex_unlock(&counts_mutex); + return NULL; +} + + +static void *rcu_q_updater(void *arg) +{ + int j, target_el; + long long n_nodes_local = 0; + long long n_updates_local = 0; + long long n_removed_local = 0; + struct list_element *el, *prev_el; + + *(struct rcu_reader_data **)arg = &rcu_reader; + atomic_inc(&nthreadsrunning); + while (goflag == GOFLAG_INIT) { + g_usleep(1000); + } + + while (goflag == GOFLAG_RUN) { + target_el = select_random_el(RCU_Q_LEN); + j = 0; + /* FOREACH_RCU could work here but let's use both macros */ + QLIST_FOREACH_SAFE_RCU(prev_el, &Q_list_head, entry, el) { + j++; + if (target_el == j) { + QLIST_REMOVE_RCU(prev_el, entry); + /* may be more than one updater in the future */ + call_rcu1(&prev_el->rcu, reclaim_list_el); + n_removed_local++; + break; + } + } + if (goflag == GOFLAG_STOP) { + break; + } + target_el = select_random_el(RCU_Q_LEN); + j = 0; + QLIST_FOREACH_RCU(el, &Q_list_head, entry) { + j++; + if (target_el == j) { + prev_el = g_new(struct list_element, 1); + n_nodes += n_nodes_local; + QLIST_INSERT_BEFORE_RCU(el, prev_el, entry); + break; + } + } + + n_updates_local += 2; + synchronize_rcu(); + } + synchronize_rcu(); + qemu_mutex_lock(&counts_mutex); + n_nodes += n_nodes_local; + n_updates += n_updates_local; + n_nodes_removed += n_removed_local; + qemu_mutex_unlock(&counts_mutex); + return NULL; +} + +static void rcu_qtest_init(void) +{ + struct list_element *new_el; + int i; + nthreadsrunning = 0; + srand(time(0)); + for (i = 0; i < RCU_Q_LEN; i++) { + new_el = g_new(struct list_element, 1); + QLIST_INSERT_HEAD_RCU(&Q_list_head, new_el, entry); + } + qemu_mutex_lock(&counts_mutex); + n_nodes += RCU_Q_LEN; + qemu_mutex_unlock(&counts_mutex); +} + +static void rcu_qtest_run(int duration, int nreaders) +{ + int nthreads = nreaders + 1; + while (atomic_read(&nthreadsrunning) < nthreads) { + g_usleep(1000); + } + + goflag = GOFLAG_RUN; + sleep(duration); + goflag = GOFLAG_STOP; + wait_all_threads(); +} + + +static void rcu_qtest(const char *test, int duration, int nreaders) +{ + int i; + long long n_removed_local = 0; + + struct list_element *el, *prev_el; + + rcu_qtest_init(); + for (i = 0; i < nreaders; i++) { + create_thread(rcu_q_reader); + } + create_thread(rcu_q_updater); + rcu_qtest_run(duration, nreaders); + + QLIST_FOREACH_SAFE_RCU(prev_el, &Q_list_head, entry, el) { + QLIST_REMOVE_RCU(prev_el, entry); + call_rcu1(&prev_el->rcu, reclaim_list_el); + n_removed_local++; + } + qemu_mutex_lock(&counts_mutex); + n_nodes_removed += n_removed_local; + qemu_mutex_unlock(&counts_mutex); + synchronize_rcu(); + while (n_nodes_removed > n_reclaims) { + g_usleep(100); + synchronize_rcu(); + } + if (g_test_in_charge) { + g_assert_cmpint(n_nodes_removed, ==, n_reclaims); + } else { + printf("%s: %d readers; 1 updater; nodes read: " \ + "%lld, nodes removed: %lld; nodes reclaimed: %lld\n", + test, nthreadsrunning - 1, n_reads, n_nodes_removed, n_reclaims); + exit(0); + } +} + +static void usage(int argc, char *argv[]) +{ + fprintf(stderr, "Usage: %s duration nreaders\n", argv[0]); + exit(-1); +} + +static int gtest_seconds; + +static void gtest_rcuq_one(void) +{ + rcu_qtest("rcuqtest", gtest_seconds / 4, 1); +} + +static void gtest_rcuq_few(void) +{ + rcu_qtest("rcuqtest", gtest_seconds / 4, 5); +} + +static void gtest_rcuq_many(void) +{ + rcu_qtest("rcuqtest", gtest_seconds / 2, 20); +} + + +int main(int argc, char *argv[]) +{ + int duration = 0, readers = 0; + + qemu_mutex_init(&counts_mutex); + if (argc >= 2) { + if (argv[1][0] == '-') { + g_test_init(&argc, &argv, NULL); + if (g_test_quick()) { + gtest_seconds = 4; + } else { + gtest_seconds = 20; + } + g_test_add_func("/rcu/qlist/single-threaded", gtest_rcuq_one); + g_test_add_func("/rcu/qlist/short-few", gtest_rcuq_few); + g_test_add_func("/rcu/qlist/long-many", gtest_rcuq_many); + g_test_in_charge = 1; + return g_test_run(); + } + duration = strtoul(argv[1], NULL, 0); + } + if (argc >= 3) { + readers = strtoul(argv[2], NULL, 0); + } + if (duration && readers) { + rcu_qtest(argv[0], duration, readers); + return 0; + } + + usage(argc, argv); + return -1; +} diff --git a/tests/test-vmstate.c b/tests/test-vmstate.c index 5e0fd13..1d620e0 100644 --- a/tests/test-vmstate.c +++ b/tests/test-vmstate.c @@ -60,16 +60,6 @@ static QEMUFile *open_test_file(bool write) return qemu_fdopen(fd, write ? "wb" : "rb"); } -/* Open a read-only qemu-file from an existing memory block */ -static QEMUFile *open_mem_file_read(const void *data, size_t len) -{ - /* The qsb gets freed by qemu_fclose */ - QEMUSizedBuffer *qsb = qsb_create(data, len); - g_assert(qsb); - - return qemu_bufopen("r", qsb); -} - /* * Check that the contents of the memory-buffered file f match * the given size/data. @@ -95,7 +85,7 @@ static void save_vmstate(const VMStateDescription *desc, void *obj) QEMUFile *f = open_test_file(true); /* Save file with vmstate */ - vmstate_save_state(f, desc, obj); + vmstate_save_state(f, desc, obj, NULL); qemu_put_byte(f, QEMU_VM_EOF); g_assert(!qemu_file_get_error(f)); qemu_fclose(f); @@ -404,7 +394,7 @@ static void test_save_noskip(void) QEMUFile *fsave = qemu_bufopen("w", NULL); TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6, .skip_c_e = false }; - vmstate_save_state(fsave, &vmstate_skipping, &obj); + vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL); g_assert(!qemu_file_get_error(fsave)); uint8_t expected[] = { @@ -424,7 +414,7 @@ static void test_save_skip(void) QEMUFile *fsave = qemu_bufopen("w", NULL); TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6, .skip_c_e = true }; - vmstate_save_state(fsave, &vmstate_skipping, &obj); + vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL); g_assert(!qemu_file_get_error(fsave)); uint8_t expected[] = { @@ -450,7 +440,9 @@ static void test_load_noskip(void) QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ }; - QEMUFile *loading = open_mem_file_read(buf, sizeof(buf)); + QEMUSizedBuffer *qsb = qsb_create(buf, sizeof(buf)); + g_assert(qsb); + QEMUFile *loading = qemu_bufopen("r", qsb); TestStruct obj = { .skip_c_e = false }; vmstate_load_state(loading, &vmstate_skipping, &obj, 2); g_assert(!qemu_file_get_error(loading)); @@ -461,6 +453,7 @@ static void test_load_noskip(void) g_assert_cmpint(obj.e, ==, 50); g_assert_cmpint(obj.f, ==, 60); qemu_fclose(loading); + qsb_free(qsb); } static void test_load_skip(void) @@ -473,7 +466,9 @@ static void test_load_skip(void) QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ }; - QEMUFile *loading = open_mem_file_read(buf, sizeof(buf)); + QEMUSizedBuffer *qsb = qsb_create(buf, sizeof(buf)); + g_assert(qsb); + QEMUFile *loading = qemu_bufopen("r", qsb); TestStruct obj = { .skip_c_e = true, .c = 300, .e = 500 }; vmstate_load_state(loading, &vmstate_skipping, &obj, 2); g_assert(!qemu_file_get_error(loading)); @@ -484,6 +479,7 @@ static void test_load_skip(void) g_assert_cmpint(obj.e, ==, 500); g_assert_cmpint(obj.f, ==, 60); qemu_fclose(loading); + qsb_free(qsb); } int main(int argc, char **argv) diff --git a/tests/test-write-threshold.c b/tests/test-write-threshold.c new file mode 100644 index 0000000..faffa7b --- /dev/null +++ b/tests/test-write-threshold.c @@ -0,0 +1,119 @@ +/* + * Test block device write threshold + * + * 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 +#include +#include "block/block_int.h" +#include "block/write-threshold.h" + + +static void test_threshold_not_set_on_init(void) +{ + uint64_t res; + BlockDriverState bs; + memset(&bs, 0, sizeof(bs)); + + g_assert(!bdrv_write_threshold_is_set(&bs)); + + res = bdrv_write_threshold_get(&bs); + g_assert_cmpint(res, ==, 0); +} + +static void test_threshold_set_get(void) +{ + uint64_t threshold = 4 * 1024 * 1024; + uint64_t res; + BlockDriverState bs; + memset(&bs, 0, sizeof(bs)); + + bdrv_write_threshold_set(&bs, threshold); + + g_assert(bdrv_write_threshold_is_set(&bs)); + + res = bdrv_write_threshold_get(&bs); + g_assert_cmpint(res, ==, threshold); +} + +static void test_threshold_multi_set_get(void) +{ + uint64_t threshold1 = 4 * 1024 * 1024; + uint64_t threshold2 = 15 * 1024 * 1024; + uint64_t res; + BlockDriverState bs; + memset(&bs, 0, sizeof(bs)); + + bdrv_write_threshold_set(&bs, threshold1); + bdrv_write_threshold_set(&bs, threshold2); + res = bdrv_write_threshold_get(&bs); + g_assert_cmpint(res, ==, threshold2); +} + +static void test_threshold_not_trigger(void) +{ + uint64_t amount = 0; + uint64_t threshold = 4 * 1024 * 1024; + BlockDriverState bs; + BdrvTrackedRequest req; + + memset(&bs, 0, sizeof(bs)); + memset(&req, 0, sizeof(req)); + req.offset = 1024; + req.bytes = 1024; + + bdrv_write_threshold_set(&bs, threshold); + amount = bdrv_write_threshold_exceeded(&bs, &req); + g_assert_cmpuint(amount, ==, 0); +} + + +static void test_threshold_trigger(void) +{ + uint64_t amount = 0; + uint64_t threshold = 4 * 1024 * 1024; + BlockDriverState bs; + BdrvTrackedRequest req; + + memset(&bs, 0, sizeof(bs)); + memset(&req, 0, sizeof(req)); + req.offset = (4 * 1024 * 1024) - 1024; + req.bytes = 2 * 1024; + + bdrv_write_threshold_set(&bs, threshold); + amount = bdrv_write_threshold_exceeded(&bs, &req); + g_assert_cmpuint(amount, >=, 1024); +} + +typedef struct TestStruct { + const char *name; + void (*func)(void); +} TestStruct; + + +int main(int argc, char **argv) +{ + size_t i; + TestStruct tests[] = { + { "/write-threshold/not-set-on-init", + test_threshold_not_set_on_init }, + { "/write-threshold/set-get", + test_threshold_set_get }, + { "/write-threshold/multi-set-get", + test_threshold_multi_set_get }, + { "/write-threshold/not-trigger", + test_threshold_not_trigger }, + { "/write-threshold/trigger", + test_threshold_trigger }, + { NULL, NULL } + }; + + g_test_init(&argc, &argv, NULL); + for (i = 0; tests[i].name != NULL; i++) { + g_test_add_func(tests[i].name, tests[i].func); + } + return g_test_run(); +} diff --git a/tests/test-x86-cpuid.c b/tests/test-x86-cpuid.c index 8d9f96a..6cd20d4 100644 --- a/tests/test-x86-cpuid.c +++ b/tests/test-x86-cpuid.c @@ -24,7 +24,7 @@ #include -#include "topology.h" +#include "hw/i386/topology.h" static void test_topo_bits(void) { diff --git a/tests/usb-hcd-ohci-test.c b/tests/usb-hcd-ohci-test.c index 1160bde..fa592d4 100644 --- a/tests/usb-hcd-ohci-test.c +++ b/tests/usb-hcd-ohci-test.c @@ -1,7 +1,7 @@ /* * QTest testcase for USB OHCI controller * - * Copyright (c) 2014 HUAWEI TECHNOLOGIES CO.,LTD. + * Copyright (c) 2014 HUAWEI TECHNOLOGIES CO., LTD. * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. diff --git a/tests/usb-hcd-uhci-test.c b/tests/usb-hcd-uhci-test.c index 8cf2c5b..a96b716 100644 --- a/tests/usb-hcd-uhci-test.c +++ b/tests/usb-hcd-uhci-test.c @@ -1,7 +1,7 @@ /* * QTest testcase for USB UHCI controller * - * Copyright (c) 2014 HUAWEI TECHNOLOGIES CO.,LTD. + * Copyright (c) 2014 HUAWEI TECHNOLOGIES CO., LTD. * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. @@ -87,7 +87,7 @@ int main(int argc, char **argv) qtest_add_func("/uhci/pci/hotplug/usb-storage", test_usb_storage_hotplug); qtest_start("-device piix3-usb-uhci,id=uhci,addr=1d.0" - " -drive id=drive0,if=none,file=/dev/null" + " -drive id=drive0,if=none,file=/dev/null,format=raw" " -device usb-tablet,bus=uhci.0,port=1"); ret = g_test_run(); qtest_end(); diff --git a/tests/usb-hcd-xhci-test.c b/tests/usb-hcd-xhci-test.c index b1a7dec..56ab367 100644 --- a/tests/usb-hcd-xhci-test.c +++ b/tests/usb-hcd-xhci-test.c @@ -1,7 +1,7 @@ /* * QTest testcase for USB xHCI controller * - * Copyright (c) 2014 HUAWEI TECHNOLOGIES CO.,LTD. + * Copyright (c) 2014 HUAWEI TECHNOLOGIES CO., LTD. * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. @@ -91,7 +91,7 @@ int main(int argc, char **argv) qtest_add_func("/xhci/pci/hotplug/usb-uas", test_usb_uas_hotplug); qtest_start("-device nec-usb-xhci,id=xhci" - " -drive id=drive0,if=none,file=/dev/null"); + " -drive id=drive0,if=none,file=/dev/null,format=raw"); ret = g_test_run(); qtest_end(); diff --git a/tests/virtio-blk-test.c b/tests/virtio-blk-test.c index ead3911..4078321 100644 --- a/tests/virtio-blk-test.c +++ b/tests/virtio-blk-test.c @@ -16,9 +16,11 @@ #include "libqtest.h" #include "libqos/virtio.h" #include "libqos/virtio-pci.h" +#include "libqos/virtio-mmio.h" #include "libqos/pci-pc.h" #include "libqos/malloc.h" #include "libqos/malloc-pc.h" +#include "libqos/malloc-generic.h" #include "qemu/bswap.h" #define QVIRTIO_BLK_F_BARRIER 0x00000001 @@ -42,10 +44,14 @@ #define TEST_IMAGE_SIZE (64 * 1024 * 1024) #define QVIRTIO_BLK_TIMEOUT_US (30 * 1000 * 1000) +#define PCI_SLOT_HP 0x06 #define PCI_SLOT 0x04 #define PCI_FN 0x00 -#define PCI_SLOT_HP 0x06 +#define MMIO_PAGE_SIZE 4096 +#define MMIO_DEV_BASE_ADDR 0x0A003E00 +#define MMIO_RAM_ADDR 0x40000000 +#define MMIO_RAM_SIZE 0x20000000 typedef struct QVirtioBlkReq { uint32_t type; @@ -55,11 +61,10 @@ typedef struct QVirtioBlkReq { uint8_t status; } QVirtioBlkReq; -static QPCIBus *test_start(void) +static char *drive_create(void) { - char *cmdline; - char tmp_path[] = "/tmp/qtest.XXXXXX"; int fd, ret; + char *tmp_path = g_strdup("/tmp/qtest.XXXXXX"); /* Create a temporary raw image */ fd = mkstemp(tmp_path); @@ -68,24 +73,52 @@ static QPCIBus *test_start(void) g_assert_cmpint(ret, ==, 0); close(fd); - cmdline = g_strdup_printf("-drive if=none,id=drive0,file=%s " - "-drive if=none,id=drive1,file=/dev/null " - "-device virtio-blk-pci,id=drv0,drive=drive0," - "addr=%x.%x", - tmp_path, PCI_SLOT, PCI_FN); + return tmp_path; +} + +static QPCIBus *pci_test_start(void) +{ + char *cmdline; + char *tmp_path; + + tmp_path = drive_create(); + + cmdline = g_strdup_printf("-drive if=none,id=drive0,file=%s,format=raw " + "-drive if=none,id=drive1,file=/dev/null,format=raw " + "-device virtio-blk-pci,id=drv0,drive=drive0," + "addr=%x.%x", + tmp_path, PCI_SLOT, PCI_FN); qtest_start(cmdline); unlink(tmp_path); + g_free(tmp_path); g_free(cmdline); return qpci_init_pc(); } +static void arm_test_start(void) +{ + char *cmdline; + char *tmp_path; + + tmp_path = drive_create(); + + cmdline = g_strdup_printf("-machine virt " + "-drive if=none,id=drive0,file=%s,format=raw " + "-device virtio-blk-device,drive=drive0", + tmp_path); + qtest_start(cmdline); + unlink(tmp_path); + g_free(tmp_path); + g_free(cmdline); +} + static void test_end(void) { qtest_end(); } -static QVirtioPCIDevice *virtio_blk_init(QPCIBus *bus, int slot) +static QVirtioPCIDevice *virtio_blk_pci_init(QPCIBus *bus, int slot) { QVirtioPCIDevice *dev; @@ -135,14 +168,10 @@ static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioBlkReq *req, return addr; } -static void pci_basic(void) +static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev, + QGuestAllocator *alloc, QVirtQueue *vq, uint64_t device_specific) { - QVirtioPCIDevice *dev; - QPCIBus *bus; - QVirtQueuePCI *vqpci; - QGuestAllocator *alloc; QVirtioBlkReq req; - void *addr; uint64_t req_addr; uint64_t capacity; uint32_t features; @@ -150,29 +179,19 @@ static void pci_basic(void) uint8_t status; char *data; - bus = test_start(); + capacity = qvirtio_config_readq(bus, dev, device_specific); - dev = virtio_blk_init(bus, PCI_SLOT); - - /* MSI-X is not enabled */ - addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_NO_MSIX; - - capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); - features = qvirtio_get_features(&qvirtio_pci, &dev->vdev); + features = qvirtio_get_features(bus, dev); features = features & ~(QVIRTIO_F_BAD_FEATURE | QVIRTIO_F_RING_INDIRECT_DESC | QVIRTIO_F_RING_EVENT_IDX | QVIRTIO_BLK_F_SCSI); - qvirtio_set_features(&qvirtio_pci, &dev->vdev, features); - - alloc = pc_alloc_init(); - vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev, - alloc, 0); + qvirtio_set_features(bus, dev, features); - qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev); + qvirtio_set_driver_ok(bus, dev); - /* Write and read with 2 descriptor layout */ + /* Write and read with 3 descriptor layout */ /* Write request */ req.type = QVIRTIO_BLK_T_OUT; req.ioprio = 1; @@ -184,12 +203,13 @@ static void pci_basic(void) g_free(req.data); - free_head = qvirtqueue_add(&vqpci->vq, req_addr, 528, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); - qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); + free_head = qvirtqueue_add(vq, req_addr, 16, false, true); + qvirtqueue_add(vq, req_addr + 16, 512, false, true); + qvirtqueue_add(vq, req_addr + 528, 1, true, false); - qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, - QVIRTIO_BLK_TIMEOUT_US); + qvirtqueue_kick(bus, dev, vq, free_head); + + qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_BLK_TIMEOUT_US); status = readb(req_addr + 528); g_assert_cmpint(status, ==, 0); @@ -205,13 +225,13 @@ static void pci_basic(void) g_free(req.data); - free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 16, 513, true, false); + free_head = qvirtqueue_add(vq, req_addr, 16, false, true); + qvirtqueue_add(vq, req_addr + 16, 512, true, true); + qvirtqueue_add(vq, req_addr + 528, 1, true, false); - qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); + qvirtqueue_kick(bus, dev, vq, free_head); - qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, - QVIRTIO_BLK_TIMEOUT_US); + qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_BLK_TIMEOUT_US); status = readb(req_addr + 528); g_assert_cmpint(status, ==, 0); @@ -222,61 +242,84 @@ static void pci_basic(void) guest_free(alloc, req_addr); - /* Write and read with 3 descriptor layout */ - /* Write request */ - req.type = QVIRTIO_BLK_T_OUT; - req.ioprio = 1; - req.sector = 1; - req.data = g_malloc0(512); - strcpy(req.data, "TEST"); + if (features & QVIRTIO_F_ANY_LAYOUT) { + /* Write and read with 2 descriptor layout */ + /* Write request */ + req.type = QVIRTIO_BLK_T_OUT; + req.ioprio = 1; + req.sector = 1; + req.data = g_malloc0(512); + strcpy(req.data, "TEST"); - req_addr = virtio_blk_request(alloc, &req, 512); + req_addr = virtio_blk_request(alloc, &req, 512); - free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); + g_free(req.data); - qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); + free_head = qvirtqueue_add(vq, req_addr, 528, false, true); + qvirtqueue_add(vq, req_addr + 528, 1, true, false); + qvirtqueue_kick(bus, dev, vq, free_head); - qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, - QVIRTIO_BLK_TIMEOUT_US); - status = readb(req_addr + 528); - g_assert_cmpint(status, ==, 0); + qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_BLK_TIMEOUT_US); + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); - guest_free(alloc, req_addr); + guest_free(alloc, req_addr); - /* Read request */ - req.type = QVIRTIO_BLK_T_IN; - req.ioprio = 1; - req.sector = 1; - req.data = g_malloc0(512); + /* Read request */ + req.type = QVIRTIO_BLK_T_IN; + req.ioprio = 1; + req.sector = 1; + req.data = g_malloc0(512); - req_addr = virtio_blk_request(alloc, &req, 512); + req_addr = virtio_blk_request(alloc, &req, 512); - g_free(req.data); + g_free(req.data); - free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, true, true); - qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); + free_head = qvirtqueue_add(vq, req_addr, 16, false, true); + qvirtqueue_add(vq, req_addr + 16, 513, true, false); - qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); + qvirtqueue_kick(bus, dev, vq, free_head); - qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, - QVIRTIO_BLK_TIMEOUT_US); - status = readb(req_addr + 528); - g_assert_cmpint(status, ==, 0); + qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_BLK_TIMEOUT_US); + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); - data = g_malloc0(512); - memread(req_addr + 16, data, 512); - g_assert_cmpstr(data, ==, "TEST"); - g_free(data); + data = g_malloc0(512); + memread(req_addr + 16, data, 512); + g_assert_cmpstr(data, ==, "TEST"); + g_free(data); - guest_free(alloc, req_addr); + guest_free(alloc, req_addr); + } +} + +static void pci_basic(void) +{ + QVirtioPCIDevice *dev; + QPCIBus *bus; + QVirtQueuePCI *vqpci; + QGuestAllocator *alloc; + void *addr; + + bus = pci_test_start(); + dev = virtio_blk_pci_init(bus, PCI_SLOT); + + alloc = pc_alloc_init(); + vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev, + alloc, 0); + + /* MSI-X is not enabled */ + addr = dev->addr + QVIRTIO_PCI_DEVICE_SPECIFIC_NO_MSIX; + + test_basic(&qvirtio_pci, &dev->vdev, alloc, &vqpci->vq, + (uint64_t)(uintptr_t)addr); /* End test */ guest_free(alloc, vqpci->vq.desc); + pc_alloc_uninit(alloc); qvirtio_pci_device_disable(dev); g_free(dev); + qpci_free_pc(bus); test_end(); } @@ -296,14 +339,15 @@ static void pci_indirect(void) uint8_t status; char *data; - bus = test_start(); + bus = pci_test_start(); - dev = virtio_blk_init(bus, PCI_SLOT); + dev = virtio_blk_pci_init(bus, PCI_SLOT); /* MSI-X is not enabled */ - addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_NO_MSIX; + addr = dev->addr + QVIRTIO_PCI_DEVICE_SPECIFIC_NO_MSIX; - capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr); + capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, + (uint64_t)(uintptr_t)addr); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); features = qvirtio_get_features(&qvirtio_pci, &dev->vdev); @@ -374,8 +418,10 @@ static void pci_indirect(void) /* End test */ guest_free(alloc, vqpci->vq.desc); + pc_alloc_uninit(alloc); qvirtio_pci_device_disable(dev); g_free(dev); + qpci_free_pc(bus); test_end(); } @@ -387,14 +433,15 @@ static void pci_config(void) void *addr; uint64_t capacity; - bus = test_start(); + bus = pci_test_start(); - dev = virtio_blk_init(bus, PCI_SLOT); + dev = virtio_blk_pci_init(bus, PCI_SLOT); /* MSI-X is not enabled */ - addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_NO_MSIX; + addr = dev->addr + QVIRTIO_PCI_DEVICE_SPECIFIC_NO_MSIX; - capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr); + capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, + (uint64_t)(uintptr_t)addr); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev); @@ -403,11 +450,13 @@ static void pci_config(void) " 'size': %d } }", n_size); qvirtio_wait_config_isr(&qvirtio_pci, &dev->vdev, QVIRTIO_BLK_TIMEOUT_US); - capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr); + capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, + (uint64_t)(uintptr_t)addr); g_assert_cmpint(capacity, ==, n_size / 512); qvirtio_pci_device_disable(dev); g_free(dev); + qpci_free_pc(bus); test_end(); } @@ -427,18 +476,19 @@ static void pci_msix(void) uint8_t status; char *data; - bus = test_start(); + bus = pci_test_start(); alloc = pc_alloc_init(); - dev = virtio_blk_init(bus, PCI_SLOT); + dev = virtio_blk_pci_init(bus, PCI_SLOT); qpci_msix_enable(dev->pdev); qvirtio_pci_set_msix_configuration_vector(dev, alloc, 0); /* MSI-X is enabled */ - addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_MSIX; + addr = dev->addr + QVIRTIO_PCI_DEVICE_SPECIFIC_MSIX; - capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr); + capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, + (uint64_t)(uintptr_t)addr); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); features = qvirtio_get_features(&qvirtio_pci, &dev->vdev); @@ -458,7 +508,8 @@ static void pci_msix(void) qvirtio_wait_config_isr(&qvirtio_pci, &dev->vdev, QVIRTIO_BLK_TIMEOUT_US); - capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr); + capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, + (uint64_t)(uintptr_t)addr); g_assert_cmpint(capacity, ==, n_size / 512); /* Write request */ @@ -472,7 +523,8 @@ static void pci_msix(void) g_free(req.data); - free_head = qvirtqueue_add(&vqpci->vq, req_addr, 528, false, true); + free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); + qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true); qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); @@ -495,7 +547,8 @@ static void pci_msix(void) g_free(req.data); free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 16, 513, true, false); + qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, true, true); + qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); @@ -514,10 +567,12 @@ static void pci_msix(void) guest_free(alloc, req_addr); /* End test */ - guest_free(alloc, (uint64_t)vqpci->vq.desc); + guest_free(alloc, vqpci->vq.desc); + pc_alloc_uninit(alloc); qpci_msix_disable(dev->pdev); qvirtio_pci_device_disable(dev); g_free(dev); + qpci_free_pc(bus); test_end(); } @@ -536,18 +591,19 @@ static void pci_idx(void) uint8_t status; char *data; - bus = test_start(); + bus = pci_test_start(); alloc = pc_alloc_init(); - dev = virtio_blk_init(bus, PCI_SLOT); + dev = virtio_blk_pci_init(bus, PCI_SLOT); qpci_msix_enable(dev->pdev); qvirtio_pci_set_msix_configuration_vector(dev, alloc, 0); /* MSI-X is enabled */ - addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_MSIX; + addr = dev->addr + QVIRTIO_PCI_DEVICE_SPECIFIC_MSIX; - capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr); + capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, + (uint64_t)(uintptr_t)addr); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); features = qvirtio_get_features(&qvirtio_pci, &dev->vdev); @@ -573,7 +629,8 @@ static void pci_idx(void) g_free(req.data); - free_head = qvirtqueue_add(&vqpci->vq, req_addr, 528, false, true); + free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); + qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true); qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); @@ -593,7 +650,8 @@ static void pci_idx(void) /* Notify after processing the third request */ qvirtqueue_set_used_event(&vqpci->vq, 2); - free_head = qvirtqueue_add(&vqpci->vq, req_addr, 528, false, true); + free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); + qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true); qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); @@ -616,11 +674,11 @@ static void pci_idx(void) g_free(req.data); free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 16, 513, true, false); + qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, true, true); + qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); - qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, QVIRTIO_BLK_TIMEOUT_US); @@ -636,45 +694,94 @@ static void pci_idx(void) /* End test */ guest_free(alloc, vqpci->vq.desc); + pc_alloc_uninit(alloc); qpci_msix_disable(dev->pdev); qvirtio_pci_device_disable(dev); g_free(dev); + qpci_free_pc(bus); test_end(); } -static void hotplug(void) +static void pci_hotplug(void) { QPCIBus *bus; QVirtioPCIDevice *dev; - bus = test_start(); + bus = pci_test_start(); /* plug secondary disk */ qpci_plug_device_test("virtio-blk-pci", "drv1", PCI_SLOT_HP, "'drive': 'drive1'"); - dev = virtio_blk_init(bus, PCI_SLOT_HP); + dev = virtio_blk_pci_init(bus, PCI_SLOT_HP); g_assert(dev); qvirtio_pci_device_disable(dev); g_free(dev); /* unplug secondary disk */ qpci_unplug_acpi_device_test("drv1", PCI_SLOT_HP); + qpci_free_pc(bus); + test_end(); +} + +static void mmio_basic(void) +{ + QVirtioMMIODevice *dev; + QVirtQueue *vq; + QGuestAllocator *alloc; + int n_size = TEST_IMAGE_SIZE / 2; + uint64_t capacity; + + arm_test_start(); + + dev = qvirtio_mmio_init_device(MMIO_DEV_BASE_ADDR, MMIO_PAGE_SIZE); + g_assert(dev != NULL); + g_assert_cmphex(dev->vdev.device_type, ==, QVIRTIO_BLK_DEVICE_ID); + + qvirtio_reset(&qvirtio_mmio, &dev->vdev); + qvirtio_set_acknowledge(&qvirtio_mmio, &dev->vdev); + qvirtio_set_driver(&qvirtio_mmio, &dev->vdev); + + alloc = generic_alloc_init(MMIO_RAM_ADDR, MMIO_RAM_SIZE, MMIO_PAGE_SIZE); + vq = qvirtqueue_setup(&qvirtio_mmio, &dev->vdev, alloc, 0); + + test_basic(&qvirtio_mmio, &dev->vdev, alloc, vq, + QVIRTIO_MMIO_DEVICE_SPECIFIC); + + qmp("{ 'execute': 'block_resize', 'arguments': { 'device': 'drive0', " + " 'size': %d } }", n_size); + + qvirtio_wait_queue_isr(&qvirtio_mmio, &dev->vdev, vq, + QVIRTIO_BLK_TIMEOUT_US); + + capacity = qvirtio_config_readq(&qvirtio_mmio, &dev->vdev, + QVIRTIO_MMIO_DEVICE_SPECIFIC); + g_assert_cmpint(capacity, ==, n_size / 512); + + /* End test */ + guest_free(alloc, vq->desc); + generic_alloc_uninit(alloc); + g_free(dev); test_end(); } int main(int argc, char **argv) { int ret; + const char *arch = qtest_get_arch(); g_test_init(&argc, &argv, NULL); - g_test_add_func("/virtio/blk/pci/basic", pci_basic); - g_test_add_func("/virtio/blk/pci/indirect", pci_indirect); - g_test_add_func("/virtio/blk/pci/config", pci_config); - g_test_add_func("/virtio/blk/pci/msix", pci_msix); - g_test_add_func("/virtio/blk/pci/idx", pci_idx); - g_test_add_func("/virtio/blk/pci/hotplug", hotplug); + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + qtest_add_func("/virtio/blk/pci/basic", pci_basic); + qtest_add_func("/virtio/blk/pci/indirect", pci_indirect); + qtest_add_func("/virtio/blk/pci/config", pci_config); + qtest_add_func("/virtio/blk/pci/msix", pci_msix); + qtest_add_func("/virtio/blk/pci/idx", pci_idx); + qtest_add_func("/virtio/blk/pci/hotplug", pci_hotplug); + } else if (strcmp(arch, "arm") == 0) { + qtest_add_func("/virtio/blk/mmio/basic", mmio_basic); + } ret = g_test_run(); diff --git a/tests/virtio-scsi-test.c b/tests/virtio-scsi-test.c index 41f9602..989f825 100644 --- a/tests/virtio-scsi-test.c +++ b/tests/virtio-scsi-test.c @@ -52,8 +52,8 @@ int main(int argc, char **argv) qtest_add_func("/virtio/scsi/pci/nop", pci_nop); qtest_add_func("/virtio/scsi/pci/hotplug", hotplug); - qtest_start("-drive id=drv0,if=none,file=/dev/null " - "-drive id=drv1,if=none,file=/dev/null " + qtest_start("-drive id=drv0,if=none,file=/dev/null,format=raw " + "-drive id=drv1,if=none,file=/dev/null,format=raw " "-device virtio-scsi-pci,id=vscsi0 " "-device scsi-hd,bus=vscsi0.0,drive=drv0"); ret = g_test_run(); diff --git a/tpm.c b/tpm.c index c371023..963b7ee 100644 --- a/tpm.c +++ b/tpm.c @@ -134,7 +134,7 @@ static int configure_tpm(QemuOpts *opts) Error *local_err = NULL; if (!QLIST_EMPTY(&tpm_backends)) { - error_report("Only one TPM is allowed.\n"); + error_report("Only one TPM is allowed."); return 1; } @@ -162,8 +162,7 @@ static int configure_tpm(QemuOpts *opts) /* validate backend specific opts */ qemu_opts_validate(opts, be->opts, &local_err); if (local_err) { - qerror_report_err(local_err); - error_free(local_err); + error_report_err(local_err); return 1; } @@ -174,8 +173,7 @@ static int configure_tpm(QemuOpts *opts) tpm_backend_open(drv, &local_err); if (local_err) { - qerror_report_err(local_err); - error_free(local_err); + error_report_err(local_err); return 1; } diff --git a/trace-events b/trace-events index b5722ea..30eba92 100644 --- a/trace-events +++ b/trace-events @@ -116,6 +116,7 @@ virtio_blk_req_complete(void *req, int status) "req %p status %d" virtio_blk_rw_complete(void *req, int ret) "req %p ret %d" virtio_blk_handle_write(void *req, uint64_t sector, size_t nsectors) "req %p sector %"PRIu64" nsectors %zu" virtio_blk_handle_read(void *req, uint64_t sector, size_t nsectors) "req %p sector %"PRIu64" nsectors %zu" +virtio_blk_submit_multireq(void *mrb, int start, int num_reqs, uint64_t sector, size_t nsectors, bool is_write) "mrb %p start %d num_reqs %d sector %"PRIu64" nsectors %zu is_write %d" # hw/block/dataplane/virtio-blk.c virtio_blk_data_plane_start(void *s) "dataplane %p" @@ -142,6 +143,10 @@ cpu_out(unsigned int addr, unsigned int val) "addr %#x value %u" # balloon.c # Since requests are raised via monitor, not many tracepoints are needed. balloon_event(void *opaque, unsigned long addr) "opaque %p addr %lu" +virtio_balloon_handle_output(const char *name, uint64_t gpa) "setion name: %s gpa: %"PRIx64"" +virtio_balloon_get_config(uint32_t num_pages, uint32_t acutal) "num_pages: %d acutal: %d" +virtio_balloon_set_config(uint32_t acutal, uint32_t oldacutal) "acutal: %d oldacutal: %d" +virtio_balloon_to_target(uint64_t target, uint32_t num_pages) "balloon target: %"PRIx64" num_pages: %d" # hw/intc/apic_common.c cpu_set_apic_base(uint64_t val) "%016"PRIx64 @@ -202,6 +207,10 @@ hd_geometry_guess(void *blk, uint32_t cyls, uint32_t heads, uint32_t secs, int t jazz_led_read(uint64_t addr, uint8_t val) "read addr=0x%"PRIx64": 0x%x" jazz_led_write(uint64_t addr, uint8_t new) "write addr=0x%"PRIx64": 0x%x" +# hw/display/xenfb.c +xenfb_mouse_event(void *opaque, int dx, int dy, int dz, int button_state, int abs_pointer_wanted) "%p x %d y %d z %d bs %#x abs %d" +xenfb_input_connected(void *xendev, int abs_pointer_wanted) "%p abs %d" + # hw/net/lance.c lance_mem_readw(uint64_t addr, uint32_t ret) "addr=%"PRIx64"val=0x%04x" lance_mem_writew(uint64_t addr, uint32_t val) "addr=%"PRIx64"val=0x%04x" @@ -220,6 +229,23 @@ slavio_check_interrupts(uint32_t pending, uint32_t intregm_disabled) "pending %x slavio_set_irq(uint32_t target_cpu, int irq, uint32_t pil, int level) "Set cpu %d irq %d -> pil %d level %d" slavio_set_timer_irq_cpu(int cpu, int level) "Set cpu %d local timer level %d" +# hw/input/ps2.c +ps2_put_keycode(void *opaque, int keycode) "%p keycode %d" +ps2_read_data(void *opaque) "%p" +ps2_set_ledstate(void *s, int ledstate) "%p ledstate %d" +ps2_reset_keyboard(void *s) "%p" +ps2_write_keyboard(void *opaque, int val) "%p val %d" +ps2_keyboard_set_translation(void *opaque, int mode) "%p mode %d" +ps2_mouse_send_packet(void *s, int dx1, int dy1, int dz1, int b) "%p x %d y %d z %d bs %#x" +ps2_mouse_event_disabled(void *opaque, int dx, int dy, int dz, int buttons_state, int mouse_dx, int mouse_dy, int mouse_dz) "%p x %d y %d z %d bs %#x mx %d my %d mz %d " +ps2_mouse_event(void *opaque, int dx, int dy, int dz, int buttons_state, int mouse_dx, int mouse_dy, int mouse_dz) "%p x %d y %d z %d bs %#x mx %d my %d mz %d " +ps2_mouse_fake_event(void *opaque) "%p" +ps2_write_mouse(void *opaque, int val) "%p val %d" +ps2_kbd_reset(void *opaque) "%p" +ps2_mouse_reset(void *opaque) "%p" +ps2_kbd_init(void *s) "%p" +ps2_mouse_init(void *s) "%p" + # hw/misc/slavio_misc.c slavio_misc_update_irq_raise(void) "Raise IRQ" slavio_misc_update_irq_lower(void) "Lower IRQ" @@ -897,6 +923,15 @@ pvscsi_tx_rings_num_pages(const char* label, uint32_t num) "Number of %s pages: # xen-hvm.c xen_ram_alloc(unsigned long ram_addr, unsigned long size) "requested: %#lx, size %#lx" xen_client_set_memory(uint64_t start_addr, unsigned long size, bool log_dirty) "%#"PRIx64" size %#lx, log_dirty %i" +xen_ioreq_server_create(uint32_t id) "id: %u" +xen_ioreq_server_destroy(uint32_t id) "id: %u" +xen_ioreq_server_state(uint32_t id, bool enable) "id: %u: enable: %i" +xen_map_mmio_range(uint32_t id, uint64_t start_addr, uint64_t end_addr) "id: %u start: %#"PRIx64" end: %#"PRIx64 +xen_unmap_mmio_range(uint32_t id, uint64_t start_addr, uint64_t end_addr) "id: %u start: %#"PRIx64" end: %#"PRIx64 +xen_map_portio_range(uint32_t id, uint64_t start_addr, uint64_t end_addr) "id: %u start: %#"PRIx64" end: %#"PRIx64 +xen_unmap_portio_range(uint32_t id, uint64_t start_addr, uint64_t end_addr) "id: %u start: %#"PRIx64" end: %#"PRIx64 +xen_map_pcidev(uint32_t id, uint8_t bus, uint8_t dev, uint8_t func) "id: %u bdf: %02x.%02x.%02x" +xen_unmap_pcidev(uint32_t id, uint8_t bus, uint8_t dev, uint8_t func) "id: %u bdf: %02x.%02x.%02x" # xen-mapcache.c xen_map_cache(uint64_t phys_addr) "want %#"PRIx64 @@ -1133,8 +1168,11 @@ vmware_scratch_write(uint32_t index, uint32_t value) "index %d, value 0x%x" vmware_setmode(uint32_t w, uint32_t h, uint32_t bpp) "%dx%d @ %d bpp" # savevm.c +qemu_loadvm_state_section(unsigned int section_type) "%d" +qemu_loadvm_state_section_partend(uint32_t section_id) "%u" +qemu_loadvm_state_section_startfull(uint32_t section_id, const char *idstr, uint32_t instance_id, uint32_t version_id) "%u(%s) %u %u" savevm_section_start(const char *id, unsigned int section_id) "%s, section_id %u" -savevm_section_end(const char *id, unsigned int section_id) "%s, section_id %u" +savevm_section_end(const char *id, unsigned int section_id, int ret) "%s, section_id %u -> %d" savevm_state_begin(void) "" savevm_state_iterate(void) "" savevm_state_complete(void) "" @@ -1145,6 +1183,12 @@ qemu_announce_self_iter(const char *mac) "%s" # vmstate.c vmstate_load_field_error(const char *field, int ret) "field \"%s\" load failed, ret = %d" +vmstate_load_state(const char *name, int version_id) "%s v%d" +vmstate_load_state_end(const char *name, const char *reason, int val) "%s %s/%d" +vmstate_load_state_field(const char *name, const char *field) "%s:%s" +vmstate_subsection_load(const char *parent) "%s" +vmstate_subsection_load_bad(const char *parent, const char *sub) "%s: %s" +vmstate_subsection_load_good(const char *parent) "%s" # qemu-file.c qemu_file_fclose(void) "" @@ -1238,6 +1282,30 @@ spapr_pci_msi_write(uint64_t addr, uint64_t data, uint32_t dt_irq) "@%"PRIx64"<= spapr_pci_lsi_set(const char *busname, int pin, uint32_t irq) "%s PIN%d IRQ %u" spapr_pci_msi_retry(unsigned config_addr, unsigned req_num, unsigned max_irqs) "Guest device at %x asked %u, have only %u" +# hw/pci/pci.c +pci_update_mappings_del(void *d, uint32_t bus, uint32_t func, uint32_t slot, int bar, uint64_t addr, uint64_t size) "d=%p %02x:%02x.%x %d,%#"PRIx64"+%#"PRIx64 +pci_update_mappings_add(void *d, uint32_t bus, uint32_t func, uint32_t slot, int bar, uint64_t addr, uint64_t size) "d=%p %02x:%02x.%x %d,%#"PRIx64"+%#"PRIx64 + +# hw/net/pcnet.c +pcnet_s_reset(void *s) "s=%p" +pcnet_user_int(void *s) "s=%p" +pcnet_isr_change(void *s, uint32_t isr, uint32_t isr_old) "s=%p INTA=%d<=%d" +pcnet_init(void *s, uint64_t init_addr) "s=%p init_addr=%#"PRIx64 +pcnet_rlen_tlen(void *s, uint32_t rlen, uint32_t tlen) "s=%p rlen=%d tlen=%d" +pcnet_ss32_rdra_tdra(void *s, uint32_t ss32, uint32_t rdra, uint32_t rcvrl, uint32_t tdra, uint32_t xmtrl) "s=%p ss32=%d rdra=0x%08x[%d] tdra=0x%08x[%d]" + +# hw/net/pcnet-pci.c +pcnet_aprom_writeb(void *opaque, uint32_t addr, uint32_t val) "opaque=%p addr=0x%08x val=0x%02x" +pcnet_aprom_readb(void *opaque, uint32_t addr, uint32_t val) "opaque=%p addr=0x%08x val=0x%02x" +pcnet_ioport_read(void *opaque, uint64_t addr, unsigned size) "opaque=%p addr=%#"PRIx64" size=%d" +pcnet_ioport_write(void *opaque, uint64_t addr, uint64_t data, unsigned size) "opaque=%p addr=%#"PRIx64" data=%#"PRIx64" size=%d" +pcnet_mmio_writeb(void *opaque, uint64_t addr, uint32_t val) "opaque=%p addr=%#"PRIx64" val=0x%x" +pcnet_mmio_writew(void *opaque, uint64_t addr, uint32_t val) "opaque=%p addr=%#"PRIx64" val=0x%x" +pcnet_mmio_writel(void *opaque, uint64_t addr, uint32_t val) "opaque=%p addr=%#"PRIx64" val=0x%x" +pcnet_mmio_readb(void *opaque, uint64_t addr, uint32_t val) "opaque=%p addr=%#"PRIx64" val=0x%x" +pcnet_mmio_readw(void *opaque, uint64_t addr, uint32_t val) "opaque=%p addr=%#"PRIx64" val=0x%x" +pcnet_mmio_readl(void *opaque, uint64_t addr, uint32_t val) "opaque=%p addr=%#"PRIx64" val=0x%x" + # hw/intc/xics.c xics_icp_check_ipi(int server, uint8_t mfrr) "CPU %d can take IPI mfrr=%#x" xics_icp_accept(uint32_t old_xirr, uint32_t new_xirr) "icp_accept: XIRR %#"PRIx32"->%#"PRIx32 @@ -1313,6 +1381,68 @@ migrate_fd_cancel(void) "" migrate_pending(uint64_t size, uint64_t max) "pending size %" PRIu64 " max %" PRIu64 migrate_transferred(uint64_t tranferred, uint64_t time_spent, double bandwidth, uint64_t size) "transferred %" PRIu64 " time_spent %" PRIu64 " bandwidth %g max_size %" PRId64 +# migration/rdma.c +qemu_dma_accept_incoming_migration(void) "" +qemu_dma_accept_incoming_migration_accepted(void) "" +qemu_rdma_accept_pin_state(bool pin) "%d" +qemu_rdma_accept_pin_verbsc(void *verbs) "Verbs context after listen: %p" +qemu_rdma_block_for_wrid_miss(const char *wcompstr, int wcomp, const char *gcompstr, uint64_t req) "A Wanted wrid %s (%d) but got %s (%" PRIu64 ")" +qemu_rdma_block_for_wrid_miss_b(const char *wcompstr, int wcomp, const char *gcompstr, uint64_t req) "B Wanted wrid %s (%d) but got %s (%" PRIu64 ")" +qemu_rdma_cleanup_disconnect(void) "" +qemu_rdma_cleanup_waiting_for_disconnect(void) "" +qemu_rdma_close(void) "" +qemu_rdma_connect_pin_all_requested(void) "" +qemu_rdma_connect_pin_all_outcome(bool pin) "%d" +qemu_rdma_dest_init_trying(const char *host, const char *ip) "%s => %s" +qemu_rdma_dump_gid(const char *who, const char *src, const char *dst) "%s Source GID: %s, Dest GID: %s" +qemu_rdma_exchange_get_response_start(const char *desc) "CONTROL: %s receiving..." +qemu_rdma_exchange_get_response_none(const char *desc, int type) "Surprise: got %s (%d)" +qemu_rdma_exchange_send_issue_callback(void) "" +qemu_rdma_exchange_send_waiting(const char *desc) "Waiting for response %s" +qemu_rdma_exchange_send_received(const char *desc) "Response %s received." +qemu_rdma_fill(int64_t control_len, int size) "RDMA %" PRId64 " of %d bytes already in buffer" +qemu_rdma_init_ram_blocks(int blocks) "Allocated %d local ram block structures" +qemu_rdma_poll_recv(const char *compstr, int64_t comp, int64_t id, int sent) "completion %s #%" PRId64 " received (%" PRId64 ") left %d" +qemu_rdma_poll_write(const char *compstr, int64_t comp, int left, uint64_t block, uint64_t chunk, void *local, void *remote) "completions %s (%" PRId64 ") left %d, block %" PRIu64 ", chunk: %" PRIu64 " %p %p" +qemu_rdma_poll_other(const char *compstr, int64_t comp, int left) "other completion %s (%" PRId64 ") received left %d" +qemu_rdma_post_send_control(const char *desc) "CONTROL: sending %s.." +qemu_rdma_register_and_get_keys(uint64_t len, void *start) "Registering %" PRIu64 " bytes @ %p" +qemu_rdma_registration_handle_compress(int64_t length, int index, int64_t offset) "Zapping zero chunk: %" PRId64 " bytes, index %d, offset %" PRId64 +qemu_rdma_registration_handle_finished(void) "" +qemu_rdma_registration_handle_ram_blocks(void) "" +qemu_rdma_registration_handle_register(int requests) "%d requests" +qemu_rdma_registration_handle_register_loop(int req, int index, uint64_t addr, uint64_t chunks) "Registration request (%d): index %d, current_addr %" PRIu64 " chunks: %" PRIu64 +qemu_rdma_registration_handle_register_rkey(int rkey) "%x" +qemu_rdma_registration_handle_unregister(int requests) "%d requests" +qemu_rdma_registration_handle_unregister_loop(int count, int index, uint64_t chunk) "Unregistration request (%d): index %d, chunk %" PRIu64 +qemu_rdma_registration_handle_unregister_success(uint64_t chunk) "%" PRIu64 +qemu_rdma_registration_handle_wait(uint64_t flags) "Waiting for next request %" PRIu64 +qemu_rdma_registration_start(uint64_t flags) "%" PRIu64 +qemu_rdma_registration_stop(uint64_t flags) "%" PRIu64 +qemu_rdma_registration_stop_ram(void) "" +qemu_rdma_resolve_host_trying(const char *host, const char *ip) "Trying %s => %s" +qemu_rdma_signal_unregister_append(uint64_t chunk, int pos) "Appending unregister chunk %" PRIu64 " at position %d" +qemu_rdma_signal_unregister_already(uint64_t chunk) "Unregister chunk %" PRIu64 " already in queue" +qemu_rdma_unregister_waiting_inflight(uint64_t chunk) "Cannot unregister inflight chunk: %" PRIu64 +qemu_rdma_unregister_waiting_proc(uint64_t chunk, int pos) "Processing unregister for chunk: %" PRIu64 " at position %d" +qemu_rdma_unregister_waiting_send(uint64_t chunk) "Sending unregister for chunk: %" PRIu64 +qemu_rdma_unregister_waiting_complete(uint64_t chunk) "Unregister for chunk: %" PRIu64 " complete." +qemu_rdma_write_flush(int sent) "sent total: %d" +qemu_rdma_write_one_block(int count, int block, uint64_t chunk, uint64_t current, uint64_t len, int nb_sent, int nb_chunks) "(%d) Not clobbering: block: %d chunk %" PRIu64 " current %" PRIu64 " len %" PRIu64 " %d %d" +qemu_rdma_write_one_post(uint64_t chunk, long addr, long remote, uint32_t len) "Posting chunk: %" PRIu64 ", addr: %lx remote: %lx, bytes %" PRIu32 +qemu_rdma_write_one_queue_full(void) "" +qemu_rdma_write_one_recvregres(int mykey, int theirkey, uint64_t chunk) "Received registration result: my key: %x their key %x, chunk %" PRIu64 +qemu_rdma_write_one_sendreg(uint64_t chunk, int len, int index, int64_t offset) "Sending registration request chunk %" PRIu64 " for %d bytes, index: %d, offset: %" PRId64 +qemu_rdma_write_one_top(uint64_t chunks, uint64_t size) "Writing %" PRIu64 " chunks, (%" PRIu64 " MB)" +qemu_rdma_write_one_zero(uint64_t chunk, int len, int index, int64_t offset) "Entire chunk is zero, sending compress: %" PRIu64 " for %d bytes, index: %d, offset: %" PRId64 +rdma_add_block(int block, uint64_t addr, uint64_t offset, uint64_t len, uint64_t end, uint64_t bits, int chunks) "Added Block: %d, addr: %" PRIu64 ", offset: %" PRIu64 " length: %" PRIu64 " end: %" PRIu64 " bits %" PRIu64 " chunks %d" +rdma_delete_block(int block, uint64_t addr, uint64_t offset, uint64_t len, uint64_t end, uint64_t bits, int chunks) "Deleted Block: %d, addr: %" PRIu64 ", offset: %" PRIu64 " length: %" PRIu64 " end: %" PRIu64 " bits %" PRIu64 " chunks %d" +rdma_start_incoming_migration(void) "" +rdma_start_incoming_migration_after_dest_init(void) "" +rdma_start_incoming_migration_after_rdma_listen(void) "" +rdma_start_outgoing_migration_after_rdma_connect(void) "" +rdma_start_outgoing_migration_after_rdma_source_init(void) "" + # kvm-all.c kvm_ioctl(int type, void *arg) "type 0x%x, arg %p" kvm_vm_ioctl(int type, void *arg) "type 0x%x, arg %p" @@ -1351,7 +1481,86 @@ xen_pv_mmio_write(uint64_t addr) "WARNING: write to Xen PV Device MMIO space (ad pci_cfg_read(const char *dev, unsigned devid, unsigned fnid, unsigned offs, unsigned val) "%s %02u:%u @0x%x -> 0x%x" pci_cfg_write(const char *dev, unsigned devid, unsigned fnid, unsigned offs, unsigned val) "%s %02u:%u @0x%x <- 0x%x" -# hw/acpi/memory_hotplug.c +# hw/vfio/vfio-pci.c +vfio_intx_interrupt(const char *name, char line) " (%s) Pin %c" +vfio_eoi(const char *name) " (%s) EOI" +vfio_enable_intx_kvm(const char *name) " (%s) KVM INTx accel enabled" +vfio_disable_intx_kvm(const char *name) " (%s) KVM INTx accel disabled" +vfio_update_irq(const char *name, int new_irq, int target_irq) " (%s) IRQ moved %d -> %d" +vfio_enable_intx(const char *name) " (%s)" +vfio_disable_intx(const char *name) " (%s)" +vfio_msi_interrupt(const char *name, int index, uint64_t addr, int data) " (%s) vector %d 0x%"PRIx64"/0x%x" +vfio_msix_vector_do_use(const char *name, int index) " (%s) vector %d used" +vfio_msix_vector_release(const char *name, int index) " (%s) vector %d released" +vfio_enable_msix(const char *name) " (%s)" +vfio_enable_msi(const char *name, int nr_vectors) " (%s) Enabled %d MSI vectors" +vfio_disable_msix(const char *name) " (%s)" +vfio_disable_msi(const char *name) " (%s)" +vfio_pci_load_rom(const char *name, unsigned long size, unsigned long offset, unsigned long flags) "Device %s ROM:\n size: 0x%lx, offset: 0x%lx, flags: 0x%lx" +vfio_rom_read(const char *name, uint64_t addr, int size, uint64_t data) " (%s, 0x%"PRIx64", 0x%x) = 0x%"PRIx64 +vfio_pci_size_rom(const char *name, int size) "%s ROM size 0x%x" +vfio_vga_write(uint64_t addr, uint64_t data, int size) " (0x%"PRIx64", 0x%"PRIx64", %d)" +vfio_vga_read(uint64_t addr, int size, uint64_t data) " (0x%"PRIx64", %d) = 0x%"PRIx64 +# remove ) = +vfio_generic_window_quirk_read(const char * region_name, const char *name, int index, uint64_t addr, int size, uint64_t data) "%s read(%s:BAR%d+0x%"PRIx64", %d = 0x%"PRIx64 +## remove ) +vfio_generic_window_quirk_write(const char * region_name, const char *name, int index, uint64_t addr, uint64_t data, int size) "%s write(%s:BAR%d+0x%"PRIx64", 0x%"PRIx64", %d" +# remove ) = +vfio_generic_quirk_read(const char * region_name, const char *name, int index, uint64_t addr, int size, uint64_t data) "%s read(%s:BAR%d+0x%"PRIx64", %d = 0x%"PRIx64 +# remove ) +vfio_generic_quirk_write(const char * region_name, const char *name, int index, uint64_t addr, uint64_t data, int size) "%s write(%s:BAR%d+0x%"PRIx64", 0x%"PRIx64", %d" +vfio_ati_3c3_quirk_read(uint64_t data) " (0x3c3, 1) = 0x%"PRIx64 +vfio_vga_probe_ati_3c3_quirk(const char *name) "Enabled ATI/AMD quirk 0x3c3 BAR4for device %s" +vfio_probe_ati_bar4_window_quirk(const char *name) "Enabled ATI/AMD BAR4 window quirk for device %s" +#issue with ) +vfio_rtl8168_window_quirk_read_fake(const char *region_name, const char *name) "%s fake read(%s" +vfio_rtl8168_window_quirk_read_table(const char *region_name, const char *name) "%s MSI-X table read(%s" +vfio_rtl8168_window_quirk_read_direct(const char *region_name, const char *name) "%s direct read(%s" +vfio_rtl8168_window_quirk_write_table(const char *region_name, const char *name) "%s MSI-X table write(%s" +vfio_rtl8168_window_quirk_write_direct(const char *region_name, const char *name) "%s direct write(%s" +vfio_probe_rtl8168_bar2_window_quirk(const char *name) "Enabled RTL8168 BAR2 window quirk for device %s" +vfio_probe_ati_bar2_4000_quirk(const char *name) "Enabled ATI/AMD BAR2 0x4000 quirk for device %s" +vfio_nvidia_3d0_quirk_read(int size, uint64_t data) " (0x3d0, %d) = 0x%"PRIx64 +vfio_nvidia_3d0_quirk_write(uint64_t data, int size) " (0x3d0, 0x%"PRIx64", %d)" +vfio_vga_probe_nvidia_3d0_quirk(const char *name) "Enabled NVIDIA VGA 0x3d0 quirk for device %s" +vfio_probe_nvidia_bar5_window_quirk(const char *name) "Enabled NVIDIA BAR5 window quirk for device %s" +vfio_probe_nvidia_bar0_88000_quirk(const char *name) "Enabled NVIDIA BAR0 0x88000 quirk for device %s" +vfio_probe_nvidia_bar0_1800_quirk_id(int id) "Nvidia NV%02x" +vfio_probe_nvidia_bar0_1800_quirk(const char *name) "Enabled NVIDIA BAR0 0x1800 quirk for device %s" +vfio_pci_read_config(const char *name, int addr, int len, int val) " (%s, @0x%x, len=0x%x) %x" +vfio_pci_write_config(const char *name, int addr, int val, int len) " (%s, @0x%x, 0x%x, len=0x%x)" +vfio_setup_msi(const char *name, int pos) "%s PCI MSI CAP @0x%x" +vfio_early_setup_msix(const char *name, int pos, int table_bar, int offset, int entries) "%s PCI MSI-X CAP @0x%x, BAR %d, offset 0x%x, entries %d" +vfio_check_pcie_flr(const char *name) "%s Supports FLR via PCIe cap" +vfio_check_pm_reset(const char *name) "%s Supports PM reset" +vfio_check_af_flr(const char *name) "%s Supports FLR via AF cap" +vfio_pci_hot_reset(const char *name, const char *type) " (%s) %s" +vfio_pci_hot_reset_has_dep_devices(const char *name) "%s: hot reset dependent devices:" +vfio_pci_hot_reset_dep_devices(int domain, int bus, int slot, int function, int group_id) "\t%04x:%02x:%02x.%x group %d" +vfio_pci_hot_reset_result(const char *name, const char *result) "%s hot reset: %s" +vfio_populate_device_region(const char *region_name, int index, unsigned long size, unsigned long offset, unsigned long flags) "Device %s region %d:\n size: 0x%lx, offset: 0x%lx, flags: 0x%lx" +vfio_populate_device_config(const char *name, unsigned long size, unsigned long offset, unsigned long flags) "Device %s config:\n size: 0x%lx, offset: 0x%lx, flags: 0x%lx" +vfio_populate_device_get_irq_info_failure(void) "VFIO_DEVICE_GET_IRQ_INFO failure: %m" +vfio_initfn(const char *name, int group_id) " (%s) group %d" +vfio_pci_reset(const char *name) " (%s)" +vfio_pci_reset_flr(const char *name) "%s FLR/VFIO_DEVICE_RESET" +vfio_pci_reset_pm(const char *name) "%s PCI PM Reset" + +# hw/vfio/vfio-common.c +vfio_region_write(const char *name, int index, uint64_t addr, uint64_t data, unsigned size) " (%s:region%d+0x%"PRIx64", 0x%"PRIx64 ", %d)" +vfio_region_read(char *name, int index, uint64_t addr, unsigned size, uint64_t data) " (%s:region%d+0x%"PRIx64", %d) = 0x%"PRIx64 +vfio_iommu_map_notify(uint64_t iova_start, uint64_t iova_end) "iommu map @ %"PRIx64" - %"PRIx64 +vfio_listener_region_add_skip(uint64_t start, uint64_t end) "SKIPPING region_add %"PRIx64" - %"PRIx64 +vfio_listener_region_add_iommu(uint64_t start, uint64_t end) "region_add [iommu] %"PRIx64" - %"PRIx64 +vfio_listener_region_add_ram(uint64_t iova_start, uint64_t iova_end, void *vaddr) "region_add [ram] %"PRIx64" - %"PRIx64" [%p]" +vfio_listener_region_del_skip(uint64_t start, uint64_t end) "SKIPPING region_del %"PRIx64" - %"PRIx64 +vfio_listener_region_del(uint64_t start, uint64_t end) "region_del %"PRIx64" - %"PRIx64 +vfio_disconnect_container(int fd) "close container->fd=%d" +vfio_put_group(int fd) "close group->fd=%d" +vfio_get_device(const char * name, unsigned int flags, unsigned int num_regions, unsigned int num_irqs) "Device %s flags: %u, regions: %u, irqs: %u" +vfio_put_base_device(int fd) "close vdev->fd=%d" + +#hw/acpi/memory_hotplug.c mhp_acpi_invalid_slot_selected(uint32_t slot) "0x%"PRIx32 mhp_acpi_read_addr_lo(uint32_t slot, uint32_t addr) "slot[0x%"PRIx32"] addr lo: 0x%"PRIx32 mhp_acpi_read_addr_hi(uint32_t slot, uint32_t addr) "slot[0x%"PRIx32"] addr hi: 0x%"PRIx32 @@ -1372,6 +1581,7 @@ mhp_pc_dimm_assigned_address(uint64_t addr) "0x%"PRIx64 kvm_enable_cmma(int rc) "CMMA: enabling with result code %d" kvm_clear_cmma(int rc) "CMMA: clearing with result code %d" kvm_failed_cpu_state_set(int cpu_index, uint8_t state, const char *msg) "Warning: Unable to set cpu %d state %" PRIu8 " to KVM: %s" +kvm_sigp_finished(uint8_t order, int cpu_index, int dst_index, int cc) "SIGP: Finished order %u on cpu %d -> cpu %d with cc=%d" # hw/dma/i8257.c i8257_unregistered_dma(int nchan, int dma_pos, int dma_len) "unregistered DMA channel used nchan=%d dma_pos=%d dma_len=%d" diff --git a/trace/control.c b/trace/control.c index 0d30801..995beb3 100644 --- a/trace/control.c +++ b/trace/control.c @@ -126,7 +126,7 @@ static void trace_init_events(const char *fname) error_report("WARNING: trace event '%s' does not exist", line_ptr); } else if (!trace_event_get_state_static(ev)) { - error_report("WARNING: trace event '%s' is not traceable\n", + error_report("WARNING: trace event '%s' is not traceable", line_ptr); } else { trace_event_set_state_dynamic(ev, enable); diff --git a/translate-all.c b/translate-all.c index ba5c840..11763c6 100644 --- a/translate-all.c +++ b/translate-all.c @@ -218,7 +218,7 @@ static int cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb, gen_intermediate_code_pc(env, tb); - if (use_icount) { + if (tb->cflags & CF_USE_ICOUNT) { /* Reset the cycle counter to the start of the block. */ cpu->icount_decr.u16.low += tb->icount; /* Clear the IO flag. */ @@ -264,20 +264,26 @@ bool cpu_restore_state(CPUState *cpu, uintptr_t retaddr) tb = tb_find_pc(retaddr); if (tb) { cpu_restore_state_from_tb(cpu, tb, retaddr); + if (tb->cflags & CF_NOCACHE) { + /* one-shot translation, invalidate it immediately */ + cpu->current_tb = NULL; + tb_phys_invalidate(tb, -1); + tb_free(tb); + } return true; } return false; } #ifdef _WIN32 -static inline void map_exec(void *addr, long size) +static __attribute__((unused)) void map_exec(void *addr, long size) { DWORD old_protect; VirtualProtect(addr, size, PAGE_EXECUTE_READWRITE, &old_protect); } #else -static inline void map_exec(void *addr, long size) +static __attribute__((unused)) void map_exec(void *addr, long size) { unsigned long start, end, page_size; @@ -625,7 +631,7 @@ static inline void *alloc_code_gen_buffer(void) #else static inline void *alloc_code_gen_buffer(void) { - void *buf = g_malloc(tcg_ctx.code_gen_buffer_size); + void *buf = g_try_malloc(tcg_ctx.code_gen_buffer_size); if (buf == NULL) { return NULL; @@ -1039,6 +1045,9 @@ TranslationBlock *tb_gen_code(CPUState *cpu, int code_gen_size; phys_pc = get_page_addr_code(env, pc); + if (use_icount) { + cflags |= CF_USE_ICOUNT; + } tb = tb_alloc(pc); if (!tb) { /* flush must be done */ @@ -1325,8 +1334,6 @@ static inline void tb_alloc_page(TranslationBlock *tb, p->first_tb = (TranslationBlock *)((uintptr_t)tb | n); invalidate_page_bitmap(p); -#if defined(TARGET_HAS_SMC) || 1 - #if defined(CONFIG_USER_ONLY) if (p->flags & PAGE_WRITE) { target_ulong addr; @@ -1362,8 +1369,6 @@ static inline void tb_alloc_page(TranslationBlock *tb, tlb_protect_code(page_addr); } #endif - -#endif /* TARGET_HAS_SMC */ } /* add a new TB and link it to the physical page tables. phys_page2 is @@ -1442,7 +1447,7 @@ static TranslationBlock *tb_find_pc(uintptr_t tc_ptr) return &tcg_ctx.tb_ctx.tbs[m_max]; } -#if defined(TARGET_HAS_ICE) && !defined(CONFIG_USER_ONLY) +#if !defined(CONFIG_USER_ONLY) void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr) { ram_addr_t ram_addr; @@ -1458,7 +1463,7 @@ void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr) + addr; tb_invalidate_phys_page_range(ram_addr, ram_addr + 1, 0); } -#endif /* TARGET_HAS_ICE && !defined(CONFIG_USER_ONLY) */ +#endif /* !defined(CONFIG_USER_ONLY) */ void tb_check_watchpoint(CPUState *cpu) { @@ -1534,7 +1539,7 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr) branch. */ #if defined(TARGET_MIPS) if ((env->hflags & MIPS_HFLAG_BMASK) != 0 && n > 1) { - env->active_tc.PC -= 4; + env->active_tc.PC -= (env->hflags & MIPS_HFLAG_B16 ? 2 : 4); cpu->icount_decr.u16.low++; env->hflags &= ~MIPS_HFLAG_BMASK; } @@ -1645,6 +1650,11 @@ void dump_exec_info(FILE *f, fprintf_function cpu_fprintf) tcg_dump_info(f, cpu_fprintf); } +void dump_opcount_info(FILE *f, fprintf_function cpu_fprintf) +{ + tcg_dump_op_count(f, cpu_fprintf); +} + #else /* CONFIG_USER_ONLY */ void cpu_interrupt(CPUState *cpu, int mask) diff --git a/ui/Makefile.objs b/ui/Makefile.objs index 801cba2..13b5cfb 100644 --- a/ui/Makefile.objs +++ b/ui/Makefile.objs @@ -16,7 +16,12 @@ common-obj-$(CONFIG_CURSES) += curses.o common-obj-$(CONFIG_VNC) += $(vnc-obj-y) common-obj-$(CONFIG_GTK) += gtk.o x_keymap.o -sdl.mo-objs := sdl.o sdl_zoom.o sdl2.o +ifeq ($(CONFIG_SDLABI),1.2) +sdl.mo-objs := sdl.o sdl_zoom.o +endif +ifeq ($(CONFIG_SDLABI),2.0) +sdl.mo-objs := sdl2.o sdl2-input.o sdl2-2d.o +endif sdl.mo-cflags := $(SDL_CFLAGS) gtk.o-cflags := $(GTK_CFLAGS) $(VTE_CFLAGS) diff --git a/ui/console.c b/ui/console.c index 258af5d..b15ca87 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1285,9 +1285,9 @@ DisplaySurface *qemu_create_displaysurface_guestmem(int width, int height, linesize = width * PIXMAN_FORMAT_BPP(format) / 8; } - size = linesize * height; + size = (hwaddr)linesize * height; data = cpu_physical_memory_map(addr, &size, 0); - if (size != linesize * height) { + if (size != (hwaddr)linesize * height) { cpu_physical_memory_unmap(data, size, 0, 0); return NULL; } @@ -1439,6 +1439,31 @@ void dpy_gfx_replace_surface(QemuConsole *con, qemu_free_displaysurface(old_surface); } +bool dpy_gfx_check_format(QemuConsole *con, + pixman_format_code_t format) +{ + DisplayChangeListener *dcl; + DisplayState *s = con->ds; + + QLIST_FOREACH(dcl, &s->listeners, next) { + if (dcl->con && dcl->con != con) { + /* dcl bound to another console -> skip */ + continue; + } + if (dcl->ops->dpy_gfx_check_format) { + if (!dcl->ops->dpy_gfx_check_format(dcl, format)) { + return false; + } + } else { + /* default is to whitelist native 32 bpp only */ + if (format != qemu_default_pixman_format(32, true)) { + return false; + } + } + } + return true; +} + static void dpy_refresh(DisplayState *s) { DisplayChangeListener *dcl; @@ -1980,18 +2005,6 @@ DisplaySurface *qemu_console_surface(QemuConsole *console) return console->surface; } -DisplayState *qemu_console_displaystate(QemuConsole *console) -{ - return console->ds; -} - -PixelFormat qemu_different_endianness_pixelformat(int bpp) -{ - pixman_format_code_t fmt = qemu_default_pixman_format(bpp, false); - PixelFormat pf = qemu_pixelformat_from_pixman(fmt); - return pf; -} - PixelFormat qemu_default_pixelformat(int bpp) { pixman_format_code_t fmt = qemu_default_pixman_format(bpp, true); diff --git a/ui/d3des.c b/ui/d3des.c index 60c840e..5bc99b8 100644 --- a/ui/d3des.c +++ b/ui/d3des.c @@ -121,15 +121,6 @@ static void cookey(register unsigned long *raw1) return; } -void cpkey(register unsigned long *into) -{ - register unsigned long *from, *endp; - - from = KnL, endp = &KnL[32]; - while( from < endp ) *into++ = *from++; - return; - } - void usekey(register unsigned long *from) { register unsigned long *to, *endp; diff --git a/ui/d3des.h b/ui/d3des.h index 70cb6b5..773667e 100644 --- a/ui/d3des.h +++ b/ui/d3des.h @@ -36,12 +36,6 @@ void usekey(unsigned long *); * Loads the internal key register with the data in cookedkey. */ -void cpkey(unsigned long *); -/* cookedkey[32] - * Copies the contents of the internal key register into the storage - * located at &cookedkey[0]. - */ - void des(unsigned char *, unsigned char *); /* from[8] to[8] * Encrypts/Decrypts (according to the key currently loaded in the diff --git a/ui/gtk.c b/ui/gtk.c index 0385757..51abac9 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -294,6 +294,10 @@ static void gd_update_cursor(VirtualConsole *vc) return; } + if (!gtk_widget_get_realized(vc->gfx.drawing_area)) { + return; + } + window = gtk_widget_get_window(GTK_WIDGET(vc->gfx.drawing_area)); if (s->full_screen || qemu_input_is_absolute() || s->ptr_owner == vc) { gdk_window_set_cursor(window, s->null_cursor); @@ -458,6 +462,10 @@ static void gd_update(DisplayChangeListener *dcl, trace_gd_update(vc->label, x, y, w, h); + if (!gtk_widget_get_realized(vc->gfx.drawing_area)) { + return; + } + if (vc->gfx.convert) { pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image, NULL, vc->gfx.convert, @@ -540,6 +548,10 @@ static void gd_cursor_define(DisplayChangeListener *dcl, GdkPixbuf *pixbuf; GdkCursor *cursor; + if (!gtk_widget_get_realized(vc->gfx.drawing_area)) { + return; + } + pixbuf = gdk_pixbuf_new_from_data((guchar *)(c->data), GDK_COLORSPACE_RGB, true, 8, c->width, c->height, c->width * 4, @@ -1654,12 +1666,13 @@ static GtkWidget *gd_create_menu_machine(GtkDisplayState *s) } static const DisplayChangeListenerOps dcl_ops = { - .dpy_name = "gtk", - .dpy_gfx_update = gd_update, - .dpy_gfx_switch = gd_switch, - .dpy_refresh = gd_refresh, - .dpy_mouse_set = gd_mouse_set, - .dpy_cursor_define = gd_cursor_define, + .dpy_name = "gtk", + .dpy_gfx_update = gd_update, + .dpy_gfx_switch = gd_switch, + .dpy_gfx_check_format = qemu_pixman_check_format, + .dpy_refresh = gd_refresh, + .dpy_mouse_set = gd_mouse_set, + .dpy_cursor_define = gd_cursor_define, }; static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, diff --git a/ui/input-keymap.c b/ui/input-keymap.c index 5d29935..7635cb0 100644 --- a/ui/input-keymap.c +++ b/ui/input-keymap.c @@ -128,6 +128,10 @@ static const int qcode_to_number[] = { [Q_KEY_CODE_INSERT] = 0xd2, [Q_KEY_CODE_DELETE] = 0xd3, + + [Q_KEY_CODE_RO] = 0x73, + [Q_KEY_CODE_KP_COMMA] = 0x7e, + [Q_KEY_CODE_MAX] = 0, }; diff --git a/ui/input-legacy.c b/ui/input-legacy.c index a698a34..2d4ca19 100644 --- a/ui/input-legacy.c +++ b/ui/input-legacy.c @@ -143,12 +143,6 @@ QEMUPutKbdEntry *qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque) return entry; } -void qemu_remove_kbd_event_handler(QEMUPutKbdEntry *entry) -{ - qemu_input_handler_unregister(entry->s); - g_free(entry); -} - static void legacy_mouse_event(DeviceState *dev, QemuConsole *src, InputEvent *evt) { diff --git a/ui/input.c b/ui/input.c index 7ba99e5..eeeabe8 100644 --- a/ui/input.c +++ b/ui/input.c @@ -526,7 +526,7 @@ MouseInfoList *qmp_query_mice(Error **errp) return mice_list; } -void do_mouse_set(Monitor *mon, const QDict *qdict) +void hmp_mouse_set(Monitor *mon, const QDict *qdict) { QemuInputHandlerState *s; int index = qdict_get_int(qdict, "index"); diff --git a/ui/keymaps.c b/ui/keymaps.c index 80d658d..49410ae 100644 --- a/ui/keymaps.c +++ b/ui/keymaps.c @@ -26,18 +26,20 @@ #include "sysemu/sysemu.h" static int get_keysym(const name2keysym_t *table, - const char *name) + const char *name) { const name2keysym_t *p; for(p = table; p->name != NULL; p++) { - if (!strcmp(p->name, name)) + if (!strcmp(p->name, name)) { return p->keysym; + } } if (name[0] == 'U' && strlen(name) == 5) { /* try unicode Uxxxx */ char *end; int ret = (int)strtoul(name + 1, &end, 16); - if (*end == '\0' && ret > 0) - return ret; + if (*end == '\0' && ret > 0) { + return ret; + } } return 0; } @@ -46,19 +48,20 @@ static int get_keysym(const name2keysym_t *table, static void add_to_key_range(struct key_range **krp, int code) { struct key_range *kr; for (kr = *krp; kr; kr = kr->next) { - if (code >= kr->start && code <= kr->end) - break; - if (code == kr->start - 1) { - kr->start--; - break; - } - if (code == kr->end + 1) { - kr->end++; - break; - } + if (code >= kr->start && code <= kr->end) { + break; + } + if (code == kr->start - 1) { + kr->start--; + break; + } + if (code == kr->end + 1) { + kr->end++; + break; + } } if (kr == NULL) { - kr = g_malloc0(sizeof(*kr)); + kr = g_malloc0(sizeof(*kr)); kr->start = kr->end = code; kr->next = *krp; *krp = kr; @@ -67,30 +70,30 @@ static void add_to_key_range(struct key_range **krp, int code) { static void add_keysym(char *line, int keysym, int keycode, kbd_layout_t *k) { if (keysym < MAX_NORMAL_KEYCODE) { - //fprintf(stderr,"Setting keysym %s (%d) to %d\n",line,keysym,keycode); - k->keysym2keycode[keysym] = keycode; + /* fprintf(stderr,"Setting keysym %s (%d) to %d\n", + line, keysym, keycode); */ + k->keysym2keycode[keysym] = keycode; } else { - if (k->extra_count >= MAX_EXTRA_COUNT) { - fprintf(stderr, - "Warning: Could not assign keysym %s (0x%x) because of memory constraints.\n", - line, keysym); - } else { + if (k->extra_count >= MAX_EXTRA_COUNT) { + fprintf(stderr, "Warning: Could not assign keysym %s (0x%x)" + " because of memory constraints.\n", line, keysym); + } else { #if 0 - fprintf(stderr, "Setting %d: %d,%d\n", - k->extra_count, keysym, keycode); + fprintf(stderr, "Setting %d: %d,%d\n", + k->extra_count, keysym, keycode); #endif - k->keysym2keycode_extra[k->extra_count]. - keysym = keysym; - k->keysym2keycode_extra[k->extra_count]. - keycode = keycode; - k->extra_count++; - } + k->keysym2keycode_extra[k->extra_count]. + keysym = keysym; + k->keysym2keycode_extra[k->extra_count]. + keycode = keycode; + k->extra_count++; + } } } static kbd_layout_t *parse_keyboard_layout(const name2keysym_t *table, - const char *language, - kbd_layout_t * k) + const char *language, + kbd_layout_t *k) { FILE *f; char * filename; @@ -101,69 +104,78 @@ static kbd_layout_t *parse_keyboard_layout(const name2keysym_t *table, f = filename ? fopen(filename, "r") : NULL; g_free(filename); if (!f) { - fprintf(stderr, - "Could not read keymap file: '%s'\n", language); - return NULL; + fprintf(stderr, "Could not read keymap file: '%s'\n", language); + return NULL; } - if (!k) - k = g_malloc0(sizeof(kbd_layout_t)); + if (!k) { + k = g_malloc0(sizeof(kbd_layout_t)); + } for(;;) { - if (fgets(line, 1024, f) == NULL) + if (fgets(line, 1024, f) == NULL) { break; + } len = strlen(line); - if (len > 0 && line[len - 1] == '\n') + if (len > 0 && line[len - 1] == '\n') { line[len - 1] = '\0'; - if (line[0] == '#') - continue; - if (!strncmp(line, "map ", 4)) - continue; - if (!strncmp(line, "include ", 8)) { - parse_keyboard_layout(table, line + 8, k); + } + if (line[0] == '#') { + continue; + } + if (!strncmp(line, "map ", 4)) { + continue; + } + if (!strncmp(line, "include ", 8)) { + parse_keyboard_layout(table, line + 8, k); } else { - char *end_of_keysym = line; - while (*end_of_keysym != 0 && *end_of_keysym != ' ') - end_of_keysym++; - if (*end_of_keysym) { - int keysym; - *end_of_keysym = 0; - keysym = get_keysym(table, line); - if (keysym == 0) { - // fprintf(stderr, "Warning: unknown keysym %s\n", line); - } else { - const char *rest = end_of_keysym + 1; + char *end_of_keysym = line; + while (*end_of_keysym != 0 && *end_of_keysym != ' ') { + end_of_keysym++; + } + if (*end_of_keysym) { + int keysym; + *end_of_keysym = 0; + keysym = get_keysym(table, line); + if (keysym == 0) { + /* fprintf(stderr, "Warning: unknown keysym %s\n", line);*/ + } else { + const char *rest = end_of_keysym + 1; int keycode = strtol(rest, NULL, 0); if (strstr(rest, "numlock")) { - add_to_key_range(&k->keypad_range, keycode); - add_to_key_range(&k->numlock_range, keysym); - //fprintf(stderr, "keypad keysym %04x keycode %d\n", keysym, keycode); - } + add_to_key_range(&k->keypad_range, keycode); + add_to_key_range(&k->numlock_range, keysym); + /* fprintf(stderr, "keypad keysym %04x keycode %d\n", + keysym, keycode); */ + } if (strstr(rest, "shift")) { - keycode |= SCANCODE_SHIFT; + keycode |= SCANCODE_SHIFT; } if (strstr(rest, "altgr")) { - keycode |= SCANCODE_ALTGR; + keycode |= SCANCODE_ALTGR; } if (strstr(rest, "ctrl")) { - keycode |= SCANCODE_CTRL; + keycode |= SCANCODE_CTRL; } - add_keysym(line, keysym, keycode, k); + add_keysym(line, keysym, keycode, k); if (strstr(rest, "addupper")) { - char *c; - for (c = line; *c; c++) - *c = qemu_toupper(*c); - keysym = get_keysym(table, line); - if (keysym) - add_keysym(line, keysym, keycode | SCANCODE_SHIFT, k); - } - } - } - } + char *c; + for (c = line; *c; c++) { + *c = qemu_toupper(*c); + } + keysym = get_keysym(table, line); + if (keysym) { + add_keysym(line, keysym, + keycode | SCANCODE_SHIFT, k); + } + } + } + } + } } fclose(f); return k; @@ -180,19 +192,23 @@ int keysym2scancode(void *kbd_layout, int keysym) { kbd_layout_t *k = kbd_layout; if (keysym < MAX_NORMAL_KEYCODE) { - if (k->keysym2keycode[keysym] == 0) - fprintf(stderr, "Warning: no scancode found for keysym %d\n", - keysym); - return k->keysym2keycode[keysym]; + if (k->keysym2keycode[keysym] == 0) { + fprintf(stderr, "Warning: no scancode found for keysym %d\n", + keysym); + } + return k->keysym2keycode[keysym]; } else { - int i; + int i; #ifdef XK_ISO_Left_Tab - if (keysym == XK_ISO_Left_Tab) - keysym = XK_Tab; + if (keysym == XK_ISO_Left_Tab) { + keysym = XK_Tab; + } #endif - for (i = 0; i < k->extra_count; i++) - if (k->keysym2keycode_extra[i].keysym == keysym) - return k->keysym2keycode_extra[i].keycode; + for (i = 0; i < k->extra_count; i++) { + if (k->keysym2keycode_extra[i].keysym == keysym) { + return k->keysym2keycode_extra[i].keycode; + } + } } return 0; } @@ -202,9 +218,11 @@ int keycode_is_keypad(void *kbd_layout, int keycode) kbd_layout_t *k = kbd_layout; struct key_range *kr; - for (kr = k->keypad_range; kr; kr = kr->next) - if (keycode >= kr->start && keycode <= kr->end) + for (kr = k->keypad_range; kr; kr = kr->next) { + if (keycode >= kr->start && keycode <= kr->end) { return 1; + } + } return 0; } @@ -213,8 +231,10 @@ int keysym_is_numlock(void *kbd_layout, int keysym) kbd_layout_t *k = kbd_layout; struct key_range *kr; - for (kr = k->numlock_range; kr; kr = kr->next) - if (keysym >= kr->start && keysym <= kr->end) + for (kr = k->numlock_range; kr; kr = kr->next) { + if (keysym >= kr->start && keysym <= kr->end) { return 1; + } + } return 0; } diff --git a/ui/qemu-pixman.c b/ui/qemu-pixman.c index 1f6fea5..4116e15 100644 --- a/ui/qemu-pixman.c +++ b/ui/qemu-pixman.c @@ -84,7 +84,7 @@ pixman_format_code_t qemu_default_pixman_format(int bpp, bool native_endian) break; } } - g_assert_not_reached(); + return 0; } int qemu_pixman_get_type(int rshift, int gshift, int bshift) @@ -125,6 +125,33 @@ pixman_format_code_t qemu_pixman_get_format(PixelFormat *pf) return format; } +/* + * Return true for known-good pixman conversions. + * + * UIs using pixman for format conversion can hook this into + * DisplayChangeListenerOps->dpy_gfx_check_format + */ +bool qemu_pixman_check_format(DisplayChangeListener *dcl, + pixman_format_code_t format) +{ + switch (format) { + /* 32 bpp */ + case PIXMAN_x8r8g8b8: + case PIXMAN_a8r8g8b8: + case PIXMAN_b8g8r8x8: + case PIXMAN_b8g8r8a8: + /* 24 bpp */ + case PIXMAN_r8g8b8: + case PIXMAN_b8g8r8: + /* 16 bpp */ + case PIXMAN_x1r5g5b5: + case PIXMAN_r5g6b5: + return true; + default: + return false; + } +} + pixman_image_t *qemu_pixman_linebuf_create(pixman_format_code_t format, int width) { diff --git a/ui/sdl.c b/ui/sdl.c index 94c1d9d..8bdbf52 100644 --- a/ui/sdl.c +++ b/ui/sdl.c @@ -26,8 +26,6 @@ #undef WIN32_LEAN_AND_MEAN #include - -#if SDL_MAJOR_VERSION == 1 #include #include "qemu-common.h" @@ -63,16 +61,24 @@ static SDL_PixelFormat host_format; static int scaling_active = 0; static Notifier mouse_mode_notifier; +#if 0 +#define DEBUG_SDL +#endif + static void sdl_update(DisplayChangeListener *dcl, int x, int y, int w, int h) { - // printf("updating x=%d y=%d w=%d h=%d\n", x, y, w, h); SDL_Rect rec; rec.x = x; rec.y = y; rec.w = w; rec.h = h; +#ifdef DEBUG_SDL + printf("SDL: Updating x=%d y=%d w=%d h=%d (scaling: %d)\n", + x, y, w, h, scaling_active); +#endif + if (guest_screen) { if (!scaling_active) { SDL_BlitSurface(guest_screen, &rec, real_screen, &rec); @@ -91,7 +97,9 @@ static void do_sdl_resize(int width, int height, int bpp) int flags; SDL_Surface *tmp_screen; - // printf("resizing to %d %d\n", w, h); +#ifdef DEBUG_SDL + printf("SDL: Resizing to %dx%d bpp %d\n", width, height, bpp); +#endif flags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL; if (gui_fullscreen) { @@ -127,12 +135,13 @@ static void do_sdl_resize(int width, int height, int bpp) static void sdl_switch(DisplayChangeListener *dcl, DisplaySurface *new_surface) { - PixelFormat pf = qemu_pixelformat_from_pixman(new_surface->format); + PixelFormat pf; /* temporary hack: allows to call sdl_switch to handle scaling changes */ if (new_surface) { surface = new_surface; } + pf = qemu_pixelformat_from_pixman(surface->format); if (!scaling_active) { do_sdl_resize(surface_width(surface), surface_height(surface), 0); @@ -145,6 +154,12 @@ static void sdl_switch(DisplayChangeListener *dcl, if (guest_screen != NULL) { SDL_FreeSurface(guest_screen); } + +#ifdef DEBUG_SDL + printf("SDL: Creating surface with masks: %08x %08x %08x %08x\n", + pf.rmask, pf.gmask, pf.bmask, pf.amask); +#endif + guest_screen = SDL_CreateRGBSurfaceFrom (surface_data(surface), surface_width(surface), surface_height(surface), @@ -153,6 +168,19 @@ static void sdl_switch(DisplayChangeListener *dcl, pf.bmask, pf.amask); } +static bool sdl_check_format(DisplayChangeListener *dcl, + pixman_format_code_t format) +{ + /* + * We let SDL convert for us a few more formats than, + * the native ones. Thes are the ones I have tested. + */ + return (format == PIXMAN_x8r8g8b8 || + format == PIXMAN_b8g8r8x8 || + format == PIXMAN_x1r5g5b5 || + format == PIXMAN_r5g6b5); +} + /* generic keyboard conversion */ #include "sdl_keysym.h" @@ -475,6 +503,10 @@ static void sdl_scale(int width, int height) { int bpp = real_screen->format->BitsPerPixel; +#ifdef DEBUG_SDL + printf("SDL: Scaling to %dx%d bpp %d\n", width, height, bpp); +#endif + if (bpp != 16 && bpp != 32) { bpp = 32; } @@ -867,12 +899,13 @@ static void sdl_cleanup(void) } static const DisplayChangeListenerOps dcl_ops = { - .dpy_name = "sdl", - .dpy_gfx_update = sdl_update, - .dpy_gfx_switch = sdl_switch, - .dpy_refresh = sdl_refresh, - .dpy_mouse_set = sdl_mouse_warp, - .dpy_cursor_define = sdl_mouse_define, + .dpy_name = "sdl", + .dpy_gfx_update = sdl_update, + .dpy_gfx_switch = sdl_switch, + .dpy_gfx_check_format = sdl_check_format, + .dpy_refresh = sdl_refresh, + .dpy_mouse_set = sdl_mouse_warp, + .dpy_cursor_define = sdl_mouse_define, }; void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) @@ -958,4 +991,3 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) atexit(sdl_cleanup); } -#endif diff --git a/ui/sdl2-2d.c b/ui/sdl2-2d.c new file mode 100644 index 0000000..f907c21 --- /dev/null +++ b/ui/sdl2-2d.c @@ -0,0 +1,135 @@ +/* + * QEMU SDL display driver + * + * 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. + */ +/* Ported SDL 1.2 code to 2.0 by Dave Airlie. */ + +/* Avoid compiler warning because macro is redefined in SDL_syswm.h. */ +#undef WIN32_LEAN_AND_MEAN + +#include +#include + +#include "qemu-common.h" +#include "ui/console.h" +#include "ui/input.h" +#include "ui/sdl2.h" +#include "sysemu/sysemu.h" + +void sdl2_2d_update(DisplayChangeListener *dcl, + int x, int y, int w, int h) +{ + struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl); + DisplaySurface *surf = qemu_console_surface(dcl->con); + SDL_Rect rect; + + if (!surf) { + return; + } + if (!scon->texture) { + return; + } + + rect.x = x; + rect.y = y; + rect.w = w; + rect.h = h; + + SDL_UpdateTexture(scon->texture, NULL, surface_data(surf), + surface_stride(surf)); + SDL_RenderCopy(scon->real_renderer, scon->texture, &rect, &rect); + SDL_RenderPresent(scon->real_renderer); +} + +void sdl2_2d_switch(DisplayChangeListener *dcl, + DisplaySurface *new_surface) +{ + struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl); + DisplaySurface *old_surface = scon->surface; + int format = 0; + + scon->surface = new_surface; + + if (scon->texture) { + SDL_DestroyTexture(scon->texture); + scon->texture = NULL; + } + + if (!new_surface) { + sdl2_window_destroy(scon); + return; + } + + if (!scon->real_window) { + sdl2_window_create(scon); + } else if (old_surface && + ((surface_width(old_surface) != surface_width(new_surface)) || + (surface_height(old_surface) != surface_height(new_surface)))) { + sdl2_window_resize(scon); + } + + SDL_RenderSetLogicalSize(scon->real_renderer, + surface_width(new_surface), + surface_height(new_surface)); + + if (surface_bits_per_pixel(scon->surface) == 16) { + format = SDL_PIXELFORMAT_RGB565; + } else if (surface_bits_per_pixel(scon->surface) == 32) { + format = SDL_PIXELFORMAT_ARGB8888; + } + scon->texture = SDL_CreateTexture(scon->real_renderer, format, + SDL_TEXTUREACCESS_STREAMING, + surface_width(new_surface), + surface_height(new_surface)); + sdl2_2d_redraw(scon); +} + +void sdl2_2d_refresh(DisplayChangeListener *dcl) +{ + struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl); + + graphic_hw_update(dcl->con); + sdl2_poll_events(scon); +} + +void sdl2_2d_redraw(struct sdl2_console *scon) +{ + if (!scon->surface) { + return; + } + sdl2_2d_update(&scon->dcl, 0, 0, + surface_width(scon->surface), + surface_height(scon->surface)); +} + +bool sdl2_2d_check_format(DisplayChangeListener *dcl, + pixman_format_code_t format) +{ + /* + * We let SDL convert for us a few more formats than, + * the native ones. Thes are the ones I have tested. + */ + return (format == PIXMAN_x8r8g8b8 || + format == PIXMAN_b8g8r8x8 || + format == PIXMAN_x1r5g5b5 || + format == PIXMAN_r5g6b5); +} diff --git a/ui/sdl2-input.c b/ui/sdl2-input.c new file mode 100644 index 0000000..a1973fc --- /dev/null +++ b/ui/sdl2-input.c @@ -0,0 +1,106 @@ +/* + * QEMU SDL display driver + * + * 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. + */ +/* Ported SDL 1.2 code to 2.0 by Dave Airlie. */ + +/* Avoid compiler warning because macro is redefined in SDL_syswm.h. */ +#undef WIN32_LEAN_AND_MEAN + +#include +#include + +#include "qemu-common.h" +#include "ui/console.h" +#include "ui/input.h" +#include "ui/sdl2.h" +#include "sysemu/sysemu.h" + +#include "sdl2-keymap.h" + +static uint8_t modifiers_state[SDL_NUM_SCANCODES]; + +void sdl2_reset_keys(struct sdl2_console *scon) +{ + QemuConsole *con = scon ? scon->dcl.con : NULL; + int i; + + for (i = 0; i < SDL_NUM_SCANCODES; i++) { + if (modifiers_state[i]) { + int qcode = sdl2_scancode_to_qcode[i]; + qemu_input_event_send_key_qcode(con, qcode, false); + modifiers_state[i] = 0; + } + } +} + +void sdl2_process_key(struct sdl2_console *scon, + SDL_KeyboardEvent *ev) +{ + int qcode = sdl2_scancode_to_qcode[ev->keysym.scancode]; + QemuConsole *con = scon ? scon->dcl.con : NULL; + + if (!qemu_console_is_graphic(con)) { + if (ev->type == SDL_KEYDOWN) { + switch (ev->keysym.scancode) { + case SDL_SCANCODE_RETURN: + kbd_put_keysym_console(con, '\n'); + break; + case SDL_SCANCODE_BACKSPACE: + kbd_put_keysym_console(con, QEMU_KEY_BACKSPACE); + break; + default: + kbd_put_qcode_console(con, qcode); + break; + } + } + return; + } + + switch (ev->keysym.scancode) { +#if 0 + case SDL_SCANCODE_NUMLOCKCLEAR: + case SDL_SCANCODE_CAPSLOCK: + /* SDL does not send the key up event, so we generate it */ + qemu_input_event_send_key_qcode(con, qcode, true); + qemu_input_event_send_key_qcode(con, qcode, false); + return; +#endif + case SDL_SCANCODE_LCTRL: + case SDL_SCANCODE_LSHIFT: + case SDL_SCANCODE_LALT: + case SDL_SCANCODE_LGUI: + case SDL_SCANCODE_RCTRL: + case SDL_SCANCODE_RSHIFT: + case SDL_SCANCODE_RALT: + case SDL_SCANCODE_RGUI: + if (ev->type == SDL_KEYUP) { + modifiers_state[ev->keysym.scancode] = 0; + } else { + modifiers_state[ev->keysym.scancode] = 1; + } + /* fall though */ + default: + qemu_input_event_send_key_qcode(con, qcode, + ev->type == SDL_KEYDOWN); + } +} diff --git a/ui/sdl2.c b/ui/sdl2.c index 1ad74ba..f10c6a4 100644 --- a/ui/sdl2.c +++ b/ui/sdl2.c @@ -27,55 +27,37 @@ #undef WIN32_LEAN_AND_MEAN #include - -#if SDL_MAJOR_VERSION == 2 #include #include "qemu-common.h" #include "ui/console.h" #include "ui/input.h" +#include "ui/sdl2.h" #include "sysemu/sysemu.h" -#include "sdl2-keymap.h" - static int sdl2_num_outputs; -static struct sdl2_state { - DisplayChangeListener dcl; - DisplaySurface *surface; - SDL_Texture *texture; - SDL_Window *real_window; - SDL_Renderer *real_renderer; - int idx; - int last_vm_running; /* per console for caption reasons */ - int x, y; - int hidden; -} *sdl2_console; +static struct sdl2_console *sdl2_console; static SDL_Surface *guest_sprite_surface; static int gui_grab; /* if true, all keyboard/mouse events are grabbed */ -static bool gui_saved_scaling; -static int gui_saved_width; -static int gui_saved_height; static int gui_saved_grab; static int gui_fullscreen; static int gui_noframe; static int gui_key_modifier_pressed; static int gui_keysym; static int gui_grab_code = KMOD_LALT | KMOD_LCTRL; -static uint8_t modifiers_state[SDL_NUM_SCANCODES]; static SDL_Cursor *sdl_cursor_normal; static SDL_Cursor *sdl_cursor_hidden; static int absolute_enabled; static int guest_cursor; static int guest_x, guest_y; static SDL_Cursor *guest_sprite; -static int scaling_active; static Notifier mouse_mode_notifier; -static void sdl_update_caption(struct sdl2_state *scon); +static void sdl_update_caption(struct sdl2_console *scon); -static struct sdl2_state *get_scon_from_window(uint32_t window_id) +static struct sdl2_console *get_scon_from_window(uint32_t window_id) { int i; for (i = 0; i < sdl2_num_outputs; i++) { @@ -86,180 +68,57 @@ static struct sdl2_state *get_scon_from_window(uint32_t window_id) return NULL; } -static void sdl_update(DisplayChangeListener *dcl, - int x, int y, int w, int h) +void sdl2_window_create(struct sdl2_console *scon) { - struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl); - SDL_Rect rect; - DisplaySurface *surf = qemu_console_surface(dcl->con); + int flags = 0; - if (!surf) { + if (!scon->surface) { return; } - if (!scon->texture) { - return; - } - - rect.x = x; - rect.y = y; - rect.w = w; - rect.h = h; - - SDL_UpdateTexture(scon->texture, NULL, surface_data(surf), - surface_stride(surf)); - SDL_RenderCopy(scon->real_renderer, scon->texture, &rect, &rect); - SDL_RenderPresent(scon->real_renderer); -} - -static void do_sdl_resize(struct sdl2_state *scon, int width, int height, - int bpp) -{ - int flags; - - if (scon->real_window && scon->real_renderer) { - if (width && height) { - SDL_RenderSetLogicalSize(scon->real_renderer, width, height); - SDL_SetWindowSize(scon->real_window, width, height); - } else { - SDL_DestroyRenderer(scon->real_renderer); - SDL_DestroyWindow(scon->real_window); - scon->real_renderer = NULL; - scon->real_window = NULL; - } - } else { - if (!width || !height) { - return; - } - flags = 0; - if (gui_fullscreen) { - flags |= SDL_WINDOW_FULLSCREEN; - } else { - flags |= SDL_WINDOW_RESIZABLE; - } - if (scon->hidden) { - flags |= SDL_WINDOW_HIDDEN; - } - - scon->real_window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, - width, height, flags); - scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, 0); - sdl_update_caption(scon); - } -} - -static void sdl_switch(DisplayChangeListener *dcl, - DisplaySurface *new_surface) -{ - struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl); - int format = 0; - int idx = scon->idx; - DisplaySurface *old_surface = scon->surface; - - /* temporary hack: allows to call sdl_switch to handle scaling changes */ - if (new_surface) { - scon->surface = new_surface; - } + assert(!scon->real_window); - if (!new_surface && idx > 0) { - scon->surface = NULL; - } - - if (new_surface == NULL) { - do_sdl_resize(scon, 0, 0, 0); + if (gui_fullscreen) { + flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; } else { - do_sdl_resize(scon, surface_width(scon->surface), - surface_height(scon->surface), 0); + flags |= SDL_WINDOW_RESIZABLE; } - - if (old_surface && scon->texture) { - SDL_DestroyTexture(scon->texture); - scon->texture = NULL; + if (scon->hidden) { + flags |= SDL_WINDOW_HIDDEN; } - if (new_surface) { - if (!scon->texture) { - if (surface_bits_per_pixel(scon->surface) == 16) { - format = SDL_PIXELFORMAT_RGB565; - } else if (surface_bits_per_pixel(scon->surface) == 32) { - format = SDL_PIXELFORMAT_ARGB8888; - } - - scon->texture = SDL_CreateTexture(scon->real_renderer, format, - SDL_TEXTUREACCESS_STREAMING, - surface_width(new_surface), - surface_height(new_surface)); - } - } + scon->real_window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + surface_width(scon->surface), + surface_height(scon->surface), + flags); + scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, 0); + sdl_update_caption(scon); } -static void reset_keys(struct sdl2_state *scon) +void sdl2_window_destroy(struct sdl2_console *scon) { - QemuConsole *con = scon ? scon->dcl.con : NULL; - int i; - - for (i = 0; i < 256; i++) { - if (modifiers_state[i]) { - int qcode = sdl2_scancode_to_qcode[i]; - qemu_input_event_send_key_qcode(con, qcode, false); - modifiers_state[i] = 0; - } + if (!scon->real_window) { + return; } + + SDL_DestroyRenderer(scon->real_renderer); + scon->real_renderer = NULL; + SDL_DestroyWindow(scon->real_window); + scon->real_window = NULL; } -static void sdl_process_key(struct sdl2_state *scon, - SDL_KeyboardEvent *ev) +void sdl2_window_resize(struct sdl2_console *scon) { - int qcode = sdl2_scancode_to_qcode[ev->keysym.scancode]; - QemuConsole *con = scon ? scon->dcl.con : NULL; - - if (!qemu_console_is_graphic(con)) { - if (ev->type == SDL_KEYDOWN) { - switch (ev->keysym.scancode) { - case SDL_SCANCODE_RETURN: - kbd_put_keysym_console(con, '\n'); - break; - case SDL_SCANCODE_BACKSPACE: - kbd_put_keysym_console(con, QEMU_KEY_BACKSPACE); - break; - default: - kbd_put_qcode_console(con, qcode); - break; - } - } + if (!scon->real_window) { return; } - switch (ev->keysym.scancode) { -#if 0 - case SDL_SCANCODE_NUMLOCKCLEAR: - case SDL_SCANCODE_CAPSLOCK: - /* SDL does not send the key up event, so we generate it */ - qemu_input_event_send_key_qcode(con, qcode, true); - qemu_input_event_send_key_qcode(con, qcode, false); - return; -#endif - case SDL_SCANCODE_LCTRL: - case SDL_SCANCODE_LSHIFT: - case SDL_SCANCODE_LALT: - case SDL_SCANCODE_LGUI: - case SDL_SCANCODE_RCTRL: - case SDL_SCANCODE_RSHIFT: - case SDL_SCANCODE_RALT: - case SDL_SCANCODE_RGUI: - if (ev->type == SDL_KEYUP) { - modifiers_state[ev->keysym.scancode] = 0; - } else { - modifiers_state[ev->keysym.scancode] = 1; - } - /* fall though */ - default: - qemu_input_event_send_key_qcode(con, qcode, - ev->type == SDL_KEYDOWN); - } + SDL_SetWindowSize(scon->real_window, + surface_width(scon->surface), + surface_height(scon->surface)); } -static void sdl_update_caption(struct sdl2_state *scon) +static void sdl_update_caption(struct sdl2_console *scon) { char win_title[1024]; char icon_title[1024]; @@ -269,11 +128,11 @@ static void sdl_update_caption(struct sdl2_state *scon) status = " [Stopped]"; } else if (gui_grab) { if (alt_grab) { - status = " - Press Ctrl-Alt-Shift to exit mouse grab"; + status = " - Press Ctrl-Alt-Shift to exit grab"; } else if (ctrl_grab) { - status = " - Press Right-Ctrl to exit mouse grab"; + status = " - Press Right-Ctrl to exit grab"; } else { - status = " - Press Ctrl-Alt to exit mouse grab"; + status = " - Press Ctrl-Alt to exit grab"; } } @@ -323,7 +182,7 @@ static void sdl_show_cursor(void) } } -static void sdl_grab_start(struct sdl2_state *scon) +static void sdl_grab_start(struct sdl2_console *scon) { QemuConsole *con = scon ? scon->dcl.con : NULL; @@ -351,7 +210,7 @@ static void sdl_grab_start(struct sdl2_state *scon) sdl_update_caption(scon); } -static void sdl_grab_end(struct sdl2_state *scon) +static void sdl_grab_end(struct sdl2_console *scon) { SDL_SetWindowGrab(scon->real_window, SDL_FALSE); gui_grab = 0; @@ -359,7 +218,7 @@ static void sdl_grab_end(struct sdl2_state *scon) sdl_update_caption(scon); } -static void absolute_mouse_grab(struct sdl2_state *scon) +static void absolute_mouse_grab(struct sdl2_console *scon) { int mouse_x, mouse_y; int scr_w, scr_h; @@ -386,7 +245,7 @@ static void sdl_mouse_mode_change(Notifier *notify, void *data) } } -static void sdl_send_mouse_event(struct sdl2_state *scon, int dx, int dy, +static void sdl_send_mouse_event(struct sdl2_console *scon, int dx, int dy, int x, int y, int state) { static uint32_t bmap[INPUT_BUTTON_MAX] = { @@ -409,7 +268,7 @@ static void sdl_send_mouse_event(struct sdl2_state *scon, int dx, int dy, int i; for (i = 0; i < sdl2_num_outputs; i++) { - struct sdl2_state *thiscon = &sdl2_console[i]; + struct sdl2_console *thiscon = &sdl2_console[i]; if (thiscon->real_window && thiscon->surface) { SDL_GetWindowSize(thiscon->real_window, &scr_w, &scr_h); cur_off_x = thiscon->x; @@ -443,48 +302,27 @@ static void sdl_send_mouse_event(struct sdl2_state *scon, int dx, int dy, qemu_input_event_sync(); } -static void sdl_scale(struct sdl2_state *scon, int width, int height) +static void toggle_full_screen(struct sdl2_console *scon) { - int bpp = 0; - do_sdl_resize(scon, width, height, bpp); - scaling_active = 1; -} - -static void toggle_full_screen(struct sdl2_state *scon) -{ - int width = surface_width(scon->surface); - int height = surface_height(scon->surface); - int bpp = surface_bits_per_pixel(scon->surface); - gui_fullscreen = !gui_fullscreen; if (gui_fullscreen) { - SDL_GetWindowSize(scon->real_window, - &gui_saved_width, &gui_saved_height); - gui_saved_scaling = scaling_active; - - do_sdl_resize(scon, width, height, bpp); - scaling_active = 0; - + SDL_SetWindowFullscreen(scon->real_window, + SDL_WINDOW_FULLSCREEN_DESKTOP); gui_saved_grab = gui_grab; sdl_grab_start(scon); } else { - if (gui_saved_scaling) { - sdl_scale(scon, gui_saved_width, gui_saved_height); - } else { - do_sdl_resize(scon, width, height, 0); - } if (!gui_saved_grab) { sdl_grab_end(scon); } + SDL_SetWindowFullscreen(scon->real_window, 0); } - graphic_hw_invalidate(scon->dcl.con); - graphic_hw_update(scon->dcl.con); + sdl2_2d_redraw(scon); } static void handle_keydown(SDL_Event *ev) { int mod_state, win; - struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); + struct sdl2_console *scon = get_scon_from_window(ev->key.windowID); if (alt_grab) { mod_state = (SDL_GetModState() & (gui_grab_code | KMOD_LSHIFT)) == @@ -524,14 +362,13 @@ static void handle_keydown(SDL_Event *ev) gui_keysym = 1; break; case SDL_SCANCODE_U: - if (scaling_active) { - scaling_active = 0; - sdl_switch(&scon->dcl, NULL); - graphic_hw_invalidate(scon->dcl.con); - graphic_hw_update(scon->dcl.con); - } + sdl2_window_destroy(scon); + sdl2_window_create(scon); + /* re-create texture */ + sdl2_2d_switch(&scon->dcl, scon->surface); gui_keysym = 1; break; +#if 0 case SDL_SCANCODE_KP_PLUS: case SDL_SCANCODE_KP_MINUS: if (!gui_fullscreen) { @@ -544,25 +381,26 @@ static void handle_keydown(SDL_Event *ev) 160); height = (surface_height(scon->surface) * width) / surface_width(scon->surface); - + fprintf(stderr, "%s: scale to %dx%d\n", + __func__, width, height); sdl_scale(scon, width, height); - graphic_hw_invalidate(NULL); - graphic_hw_update(NULL); + sdl2_2d_redraw(scon); gui_keysym = 1; } +#endif default: break; } } if (!gui_keysym) { - sdl_process_key(scon, &ev->key); + sdl2_process_key(scon, &ev->key); } } static void handle_keyup(SDL_Event *ev) { int mod_state; - struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); + struct sdl2_console *scon = get_scon_from_window(ev->key.windowID); if (!alt_grab) { mod_state = (ev->key.keysym.mod & gui_grab_code); @@ -580,19 +418,19 @@ static void handle_keyup(SDL_Event *ev) } /* SDL does not send back all the modifiers key, so we must * correct it. */ - reset_keys(scon); + sdl2_reset_keys(scon); return; } gui_keysym = 0; } if (!gui_keysym) { - sdl_process_key(scon, &ev->key); + sdl2_process_key(scon, &ev->key); } } static void handle_textinput(SDL_Event *ev) { - struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); + struct sdl2_console *scon = get_scon_from_window(ev->key.windowID); QemuConsole *con = scon ? scon->dcl.con : NULL; if (qemu_console_is_graphic(con)) { @@ -604,7 +442,7 @@ static void handle_textinput(SDL_Event *ev) static void handle_mousemotion(SDL_Event *ev) { int max_x, max_y; - struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); + struct sdl2_console *scon = get_scon_from_window(ev->key.windowID); if (qemu_input_is_absolute() || absolute_enabled) { int scr_w, scr_h; @@ -631,7 +469,7 @@ static void handle_mousebutton(SDL_Event *ev) { int buttonstate = SDL_GetMouseState(NULL, NULL); SDL_MouseButtonEvent *bev; - struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); + struct sdl2_console *scon = get_scon_from_window(ev->key.windowID); bev = &ev->button; if (!gui_grab && !qemu_input_is_absolute()) { @@ -651,7 +489,7 @@ static void handle_mousebutton(SDL_Event *ev) static void handle_mousewheel(SDL_Event *ev) { - struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); + struct sdl2_console *scon = get_scon_from_window(ev->key.windowID); SDL_MouseWheelEvent *wev = &ev->wheel; InputButton btn; @@ -669,14 +507,16 @@ static void handle_mousewheel(SDL_Event *ev) qemu_input_event_sync(); } -static void handle_windowevent(DisplayChangeListener *dcl, SDL_Event *ev) +static void handle_windowevent(SDL_Event *ev) { - int w, h; - struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); + struct sdl2_console *scon = get_scon_from_window(ev->window.windowID); + + if (!scon) { + return; + } switch (ev->window.event) { case SDL_WINDOWEVENT_RESIZED: - sdl_scale(scon, ev->window.data1, ev->window.data2); { QemuUIInfo info; memset(&info, 0, sizeof(info)); @@ -684,12 +524,10 @@ static void handle_windowevent(DisplayChangeListener *dcl, SDL_Event *ev) info.height = ev->window.data2; dpy_set_ui_info(scon->dcl.con, &info); } - graphic_hw_invalidate(scon->dcl.con); - graphic_hw_update(scon->dcl.con); + sdl2_2d_redraw(scon); break; case SDL_WINDOWEVENT_EXPOSED: - SDL_GetWindowSize(SDL_GetWindowFromID(ev->window.windowID), &w, &h); - sdl_update(dcl, 0, 0, w, h); + sdl2_2d_redraw(scon); break; case SDL_WINDOWEVENT_FOCUS_GAINED: case SDL_WINDOWEVENT_ENTER: @@ -703,10 +541,10 @@ static void handle_windowevent(DisplayChangeListener *dcl, SDL_Event *ev) } break; case SDL_WINDOWEVENT_RESTORED: - update_displaychangelistener(dcl, GUI_REFRESH_INTERVAL_DEFAULT); + update_displaychangelistener(&scon->dcl, GUI_REFRESH_INTERVAL_DEFAULT); break; case SDL_WINDOWEVENT_MINIMIZED: - update_displaychangelistener(dcl, 500); + update_displaychangelistener(&scon->dcl, 500); break; case SDL_WINDOWEVENT_CLOSE: if (!no_quit) { @@ -714,12 +552,21 @@ static void handle_windowevent(DisplayChangeListener *dcl, SDL_Event *ev) qemu_system_shutdown_request(); } break; + case SDL_WINDOWEVENT_SHOWN: + if (scon->hidden) { + SDL_HideWindow(scon->real_window); + } + break; + case SDL_WINDOWEVENT_HIDDEN: + if (!scon->hidden) { + SDL_ShowWindow(scon->real_window); + } + break; } } -static void sdl_refresh(DisplayChangeListener *dcl) +void sdl2_poll_events(struct sdl2_console *scon) { - struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl); SDL_Event ev1, *ev = &ev1; if (scon->last_vm_running != runstate_is_running()) { @@ -727,8 +574,6 @@ static void sdl_refresh(DisplayChangeListener *dcl) sdl_update_caption(scon); } - graphic_hw_update(dcl->con); - while (SDL_PollEvent(ev)) { switch (ev->type) { case SDL_KEYDOWN: @@ -757,7 +602,7 @@ static void sdl_refresh(DisplayChangeListener *dcl) handle_mousewheel(ev); break; case SDL_WINDOWEVENT: - handle_windowevent(dcl, ev); + handle_windowevent(ev); break; default: break; @@ -768,7 +613,7 @@ static void sdl_refresh(DisplayChangeListener *dcl) static void sdl_mouse_warp(DisplayChangeListener *dcl, int x, int y, int on) { - struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl); + struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl); if (on) { if (!guest_cursor) { sdl_show_cursor(); @@ -826,13 +671,14 @@ static void sdl_cleanup(void) SDL_QuitSubSystem(SDL_INIT_VIDEO); } -static const DisplayChangeListenerOps dcl_ops = { - .dpy_name = "sdl", - .dpy_gfx_update = sdl_update, - .dpy_gfx_switch = sdl_switch, - .dpy_refresh = sdl_refresh, - .dpy_mouse_set = sdl_mouse_warp, - .dpy_cursor_define = sdl_mouse_define, +static const DisplayChangeListenerOps dcl_2d_ops = { + .dpy_name = "sdl2-2d", + .dpy_gfx_update = sdl2_2d_update, + .dpy_gfx_switch = sdl2_2d_switch, + .dpy_gfx_check_format = sdl2_2d_check_format, + .dpy_refresh = sdl2_2d_refresh, + .dpy_mouse_set = sdl_mouse_warp, + .dpy_cursor_define = sdl_mouse_define, }; void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) @@ -865,6 +711,7 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) SDL_GetError()); exit(1); } + SDL_SetHint(SDL_HINT_GRAB_KEYBOARD, "1"); for (i = 0;; i++) { QemuConsole *con = qemu_console_lookup_by_index(i); @@ -873,13 +720,13 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) } } sdl2_num_outputs = i; - sdl2_console = g_new0(struct sdl2_state, sdl2_num_outputs); + sdl2_console = g_new0(struct sdl2_console, sdl2_num_outputs); for (i = 0; i < sdl2_num_outputs; i++) { QemuConsole *con = qemu_console_lookup_by_index(i); if (!qemu_console_is_graphic(con)) { sdl2_console[i].hidden = true; } - sdl2_console[i].dcl.ops = &dcl_ops; + sdl2_console[i].dcl.ops = &dcl_2d_ops; sdl2_console[i].dcl.con = con; register_displaychangelistener(&sdl2_console[i].dcl); sdl2_console[i].idx = i; @@ -912,4 +759,3 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) atexit(sdl_cleanup); } -#endif diff --git a/ui/spice-core.c b/ui/spice-core.c index 6467fa4..c8f7f18 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -16,7 +16,6 @@ */ #include -#include #include #include "sysemu/sysemu.h" @@ -386,10 +385,7 @@ static SpiceChannelList *qmp_query_spice_channels(void) struct sockaddr *paddr; socklen_t plen; - if (!(item->info->flags & SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT)) { - error_report("invalid channel event"); - return NULL; - } + assert(item->info->flags & SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT); chan = g_malloc0(sizeof(*chan)); chan->value = g_malloc0(sizeof(*chan->value)); @@ -440,6 +436,11 @@ static QemuOptsList qemu_spice_opts = { },{ .name = "ipv6", .type = QEMU_OPT_BOOL, +#ifdef SPICE_ADDR_FLAG_UNIX_ONLY + },{ + .name = "unix", + .type = QEMU_OPT_BOOL, +#endif },{ .name = "password", .type = QEMU_OPT_STRING, @@ -661,10 +662,6 @@ void qemu_spice_init(void) } port = qemu_opt_get_number(opts, "port", 0); tls_port = qemu_opt_get_number(opts, "tls-port", 0); - if (!port && !tls_port) { - error_report("neither port nor tls-port specified for spice"); - exit(1); - } if (port < 0 || port > 65535) { error_report("spice port is out of range"); exit(1); @@ -716,6 +713,10 @@ void qemu_spice_init(void) addr_flags |= SPICE_ADDR_FLAG_IPV4_ONLY; } else if (qemu_opt_get_bool(opts, "ipv6", 0)) { addr_flags |= SPICE_ADDR_FLAG_IPV6_ONLY; +#ifdef SPICE_ADDR_FLAG_UNIX_ONLY + } else if (qemu_opt_get_bool(opts, "unix", 0)) { + addr_flags |= SPICE_ADDR_FLAG_UNIX_ONLY; +#endif } spice_server = spice_server_new(); diff --git a/ui/spice-display.c b/ui/spice-display.c index def7b52..5935564 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -199,7 +199,7 @@ static void qemu_spice_create_update(SimpleSpiceDisplay *ssd) static const int blksize = 32; int blocks = (surface_width(ssd->ds) + blksize - 1) / blksize; int dirty_top[blocks]; - int y, yoff, x, xoff, blk, bw; + int y, yoff1, yoff2, x, xoff, blk, bw; int bpp = surface_bytes_per_pixel(ssd->ds); uint8_t *guest, *mirror; @@ -207,12 +207,6 @@ static void qemu_spice_create_update(SimpleSpiceDisplay *ssd) return; }; - if (ssd->surface == NULL) { - ssd->surface = pixman_image_ref(ssd->ds->image); - ssd->mirror = qemu_pixman_mirror_create(ssd->ds->format, - ssd->ds->image); - } - for (blk = 0; blk < blocks; blk++) { dirty_top[blk] = -1; } @@ -220,13 +214,14 @@ static void qemu_spice_create_update(SimpleSpiceDisplay *ssd) guest = surface_data(ssd->ds); mirror = (void *)pixman_image_get_data(ssd->mirror); for (y = ssd->dirty.top; y < ssd->dirty.bottom; y++) { - yoff = y * surface_stride(ssd->ds); + yoff1 = y * surface_stride(ssd->ds); + yoff2 = y * pixman_image_get_stride(ssd->mirror); for (x = ssd->dirty.left; x < ssd->dirty.right; x += blksize) { xoff = x * bpp; blk = x / blksize; bw = MIN(blksize, ssd->dirty.right - x); - if (memcmp(guest + yoff + xoff, - mirror + yoff + xoff, + if (memcmp(guest + yoff1 + xoff, + mirror + yoff2 + xoff, bw * bpp) == 0) { if (dirty_top[blk] != -1) { QXLRect update = { @@ -409,7 +404,29 @@ void qemu_spice_display_switch(SimpleSpiceDisplay *ssd, SimpleSpiceUpdate *update; bool need_destroy; - dprint(1, "%s/%d:\n", __func__, ssd->qxl.id); + if (surface && ssd->surface && + surface_width(surface) == pixman_image_get_width(ssd->surface) && + surface_height(surface) == pixman_image_get_height(ssd->surface)) { + /* no-resize fast path: just swap backing store */ + dprint(1, "%s/%d: fast (%dx%d)\n", __func__, ssd->qxl.id, + surface_width(surface), surface_height(surface)); + qemu_mutex_lock(&ssd->lock); + ssd->ds = surface; + pixman_image_unref(ssd->surface); + ssd->surface = pixman_image_ref(ssd->ds->image); + qemu_mutex_unlock(&ssd->lock); + qemu_spice_display_update(ssd, 0, 0, + surface_width(surface), + surface_height(surface)); + return; + } + + /* full mode switch */ + dprint(1, "%s/%d: full (%dx%d -> %dx%d)\n", __func__, ssd->qxl.id, + ssd->surface ? pixman_image_get_width(ssd->surface) : 0, + ssd->surface ? pixman_image_get_height(ssd->surface) : 0, + surface ? surface_width(surface) : 0, + surface ? surface_height(surface) : 0); memset(&ssd->dirty, 0, sizeof(ssd->dirty)); if (ssd->surface) { @@ -431,6 +448,9 @@ void qemu_spice_display_switch(SimpleSpiceDisplay *ssd, qemu_spice_destroy_host_primary(ssd); } if (ssd->ds) { + ssd->surface = pixman_image_ref(ssd->ds->image); + ssd->mirror = qemu_pixman_mirror_create(ssd->ds->format, + ssd->ds->image); qemu_spice_create_host_primary(ssd); } @@ -438,7 +458,7 @@ void qemu_spice_display_switch(SimpleSpiceDisplay *ssd, ssd->notify++; } -void qemu_spice_cursor_refresh_unlocked(SimpleSpiceDisplay *ssd) +static void qemu_spice_cursor_refresh_unlocked(SimpleSpiceDisplay *ssd) { if (ssd->cursor) { assert(ssd->dcl.con); @@ -454,6 +474,15 @@ void qemu_spice_cursor_refresh_unlocked(SimpleSpiceDisplay *ssd) } } +void qemu_spice_cursor_refresh_bh(void *opaque) +{ + SimpleSpiceDisplay *ssd = opaque; + + qemu_mutex_lock(&ssd->lock); + qemu_spice_cursor_refresh_unlocked(ssd); + qemu_mutex_unlock(&ssd->lock); +} + void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd) { dprint(3, "%s/%d:\n", __func__, ssd->qxl.id); @@ -464,7 +493,6 @@ void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd) qemu_spice_create_update(ssd); ssd->notify++; } - qemu_spice_cursor_refresh_unlocked(ssd); qemu_mutex_unlock(&ssd->lock); if (ssd->notify) { @@ -733,12 +761,13 @@ static void display_mouse_define(DisplayChangeListener *dcl, } static const DisplayChangeListenerOps display_listener_ops = { - .dpy_name = "spice", - .dpy_gfx_update = display_update, - .dpy_gfx_switch = display_switch, - .dpy_refresh = display_refresh, - .dpy_mouse_set = display_mouse_set, - .dpy_cursor_define = display_mouse_define, + .dpy_name = "spice", + .dpy_gfx_update = display_update, + .dpy_gfx_switch = display_switch, + .dpy_gfx_check_format = qemu_pixman_check_format, + .dpy_refresh = display_refresh, + .dpy_mouse_set = display_mouse_set, + .dpy_cursor_define = display_mouse_define, }; static void qemu_spice_display_init_one(QemuConsole *con) diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c index f3ad75d..2ddd259 100644 --- a/ui/vnc-auth-sasl.c +++ b/ui/vnc-auth-sasl.c @@ -555,7 +555,7 @@ void start_auth_sasl(VncState *vs) memset (&secprops, 0, sizeof secprops); /* Inform SASL that we've got an external SSF layer from TLS */ - if (strncmp(vs->vd->display, "unix:", 5) == 0 + if (vs->vd->is_unix #ifdef CONFIG_VNC_TLS /* Disable SSF, if using TLS+x509+SASL only. TLS without x509 is not sufficiently strong */ diff --git a/ui/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c index bc7032e..a420ccb 100644 --- a/ui/vnc-auth-vencrypt.c +++ b/ui/vnc-auth-vencrypt.c @@ -93,7 +93,6 @@ static int vnc_start_vencrypt_handshake(struct VncState *vs) { } VNC_DEBUG("Handshake done, switching to TLS data mode\n"); - vs->tls.wiremode = VNC_WIREMODE_TLS; qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs); start_auth_vencrypt_subauth(vs); diff --git a/ui/vnc-enc-tight.c b/ui/vnc-enc-tight.c index 3d1b5cd..9a9ddf2 100644 --- a/ui/vnc-enc-tight.c +++ b/ui/vnc-enc-tight.c @@ -1489,7 +1489,7 @@ static int send_sub_rect(VncState *vs, int x, int y, int w, int h) } #endif - colors = tight_fill_palette(vs, x, y, w * h, &fg, &bg, &palette); + colors = tight_fill_palette(vs, x, y, w * h, &bg, &fg, &palette); #ifdef CONFIG_VNC_JPEG if (allow_jpeg && vs->tight.quality != (uint8_t)-1) { diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c index 68f3d77..c8ee203 100644 --- a/ui/vnc-jobs.c +++ b/ui/vnc-jobs.c @@ -342,16 +342,3 @@ void vnc_start_worker_thread(void) QEMU_THREAD_DETACHED); queue = q; /* Set global queue */ } - -void vnc_stop_worker_thread(void) -{ - if (!vnc_worker_thread_running()) - return ; - - /* Remove all jobs and wake up the thread */ - vnc_lock_queue(queue); - queue->exit = true; - vnc_unlock_queue(queue); - vnc_jobs_clear(NULL); - qemu_cond_broadcast(&queue->cond); -} diff --git a/ui/vnc-jobs.h b/ui/vnc-jobs.h index 31da103..044bf9f 100644 --- a/ui/vnc-jobs.h +++ b/ui/vnc-jobs.h @@ -40,7 +40,6 @@ void vnc_jobs_join(VncState *vs); void vnc_jobs_consume_buffer(VncState *vs); void vnc_start_worker_thread(void); -void vnc_stop_worker_thread(void); /* Locks */ static inline int vnc_trylock_display(VncDisplay *vd) diff --git a/ui/vnc-tls.c b/ui/vnc-tls.c index 0f59f9b..eddd39b 100644 --- a/ui/vnc-tls.c +++ b/ui/vnc-tls.c @@ -334,82 +334,77 @@ static int vnc_set_gnutls_priority(gnutls_session_t s, int x509) int vnc_tls_client_setup(struct VncState *vs, int needX509Creds) { - VncStateTLS *tls; - VNC_DEBUG("Do TLS setup\n"); -#ifdef CONFIG_VNC_WS - if (vs->websocket) { - tls = &vs->ws_tls; - } else -#endif /* CONFIG_VNC_WS */ - { - tls = &vs->tls; - } if (vnc_tls_initialize() < 0) { VNC_DEBUG("Failed to init TLS\n"); vnc_client_error(vs); return -1; } - if (tls->session == NULL) { - if (gnutls_init(&tls->session, GNUTLS_SERVER) < 0) { + if (vs->tls.session == NULL) { + if (gnutls_init(&vs->tls.session, GNUTLS_SERVER) < 0) { vnc_client_error(vs); return -1; } - if (gnutls_set_default_priority(tls->session) < 0) { - gnutls_deinit(tls->session); - tls->session = NULL; + if (gnutls_set_default_priority(vs->tls.session) < 0) { + gnutls_deinit(vs->tls.session); + vs->tls.session = NULL; vnc_client_error(vs); return -1; } - if (vnc_set_gnutls_priority(tls->session, needX509Creds) < 0) { - gnutls_deinit(tls->session); - tls->session = NULL; + if (vnc_set_gnutls_priority(vs->tls.session, needX509Creds) < 0) { + gnutls_deinit(vs->tls.session); + vs->tls.session = NULL; vnc_client_error(vs); return -1; } if (needX509Creds) { - gnutls_certificate_server_credentials x509_cred = vnc_tls_initialize_x509_cred(vs->vd); + gnutls_certificate_server_credentials x509_cred = + vnc_tls_initialize_x509_cred(vs->vd); if (!x509_cred) { - gnutls_deinit(tls->session); - tls->session = NULL; + gnutls_deinit(vs->tls.session); + vs->tls.session = NULL; vnc_client_error(vs); return -1; } - if (gnutls_credentials_set(tls->session, GNUTLS_CRD_CERTIFICATE, x509_cred) < 0) { - gnutls_deinit(tls->session); - tls->session = NULL; + if (gnutls_credentials_set(vs->tls.session, + GNUTLS_CRD_CERTIFICATE, x509_cred) < 0) { + gnutls_deinit(vs->tls.session); + vs->tls.session = NULL; gnutls_certificate_free_credentials(x509_cred); vnc_client_error(vs); return -1; } if (vs->vd->tls.x509verify) { VNC_DEBUG("Requesting a client certificate\n"); - gnutls_certificate_server_set_request (tls->session, GNUTLS_CERT_REQUEST); + gnutls_certificate_server_set_request(vs->tls.session, + GNUTLS_CERT_REQUEST); } } else { - gnutls_anon_server_credentials_t anon_cred = vnc_tls_initialize_anon_cred(); + gnutls_anon_server_credentials_t anon_cred = + vnc_tls_initialize_anon_cred(); if (!anon_cred) { - gnutls_deinit(tls->session); - tls->session = NULL; + gnutls_deinit(vs->tls.session); + vs->tls.session = NULL; vnc_client_error(vs); return -1; } - if (gnutls_credentials_set(tls->session, GNUTLS_CRD_ANON, anon_cred) < 0) { - gnutls_deinit(tls->session); - tls->session = NULL; + if (gnutls_credentials_set(vs->tls.session, + GNUTLS_CRD_ANON, anon_cred) < 0) { + gnutls_deinit(vs->tls.session); + vs->tls.session = NULL; gnutls_anon_free_server_credentials(anon_cred); vnc_client_error(vs); return -1; } } - gnutls_transport_set_ptr(tls->session, (gnutls_transport_ptr_t)vs); - gnutls_transport_set_push_function(tls->session, vnc_tls_push); - gnutls_transport_set_pull_function(tls->session, vnc_tls_pull); + gnutls_transport_set_ptr(vs->tls.session, (gnutls_transport_ptr_t)vs); + gnutls_transport_set_push_function(vs->tls.session, vnc_tls_push); + gnutls_transport_set_pull_function(vs->tls.session, vnc_tls_pull); } return 0; } @@ -421,16 +416,7 @@ void vnc_tls_client_cleanup(struct VncState *vs) gnutls_deinit(vs->tls.session); vs->tls.session = NULL; } - vs->tls.wiremode = VNC_WIREMODE_CLEAR; g_free(vs->tls.dname); -#ifdef CONFIG_VNC_WS - if (vs->ws_tls.session) { - gnutls_deinit(vs->ws_tls.session); - vs->ws_tls.session = NULL; - } - vs->ws_tls.wiremode = VNC_WIREMODE_CLEAR; - g_free(vs->ws_tls.dname); -#endif /* CONFIG_VNC_WS */ } diff --git a/ui/vnc-tls.h b/ui/vnc-tls.h index 36a2227..f9829c7 100644 --- a/ui/vnc-tls.h +++ b/ui/vnc-tls.h @@ -33,11 +33,6 @@ #include "qemu/acl.h" -enum { - VNC_WIREMODE_CLEAR, - VNC_WIREMODE_TLS, -}; - typedef struct VncDisplayTLS VncDisplayTLS; typedef struct VncStateTLS VncStateTLS; @@ -55,8 +50,6 @@ struct VncDisplayTLS { /* Per client state */ struct VncStateTLS { - /* Whether data is being TLS encrypted yet */ - int wiremode; gnutls_session_t session; /* Client's Distinguished Name from the x509 cert */ diff --git a/ui/vnc-ws.c b/ui/vnc-ws.c index e304baf..62eb97f 100644 --- a/ui/vnc-ws.c +++ b/ui/vnc-ws.c @@ -24,16 +24,14 @@ #ifdef CONFIG_VNC_TLS #include "qemu/sockets.h" -static void vncws_tls_handshake_io(void *opaque); - static int vncws_start_tls_handshake(struct VncState *vs) { - int ret = gnutls_handshake(vs->ws_tls.session); + int ret = gnutls_handshake(vs->tls.session); if (ret < 0) { if (!gnutls_error_is_fatal(ret)) { VNC_DEBUG("Handshake interrupted (blocking)\n"); - if (!gnutls_record_get_direction(vs->ws_tls.session)) { + if (!gnutls_record_get_direction(vs->tls.session)) { qemu_set_fd_handler(vs->csock, vncws_tls_handshake_io, NULL, vs); } else { @@ -47,40 +45,34 @@ static int vncws_start_tls_handshake(struct VncState *vs) return -1; } + if (vs->vd->tls.x509verify) { + if (vnc_tls_validate_certificate(vs) < 0) { + VNC_DEBUG("Client verification failed\n"); + vnc_client_error(vs); + return -1; + } else { + VNC_DEBUG("Client verification passed\n"); + } + } + VNC_DEBUG("Handshake done, switching to TLS data mode\n"); - vs->ws_tls.wiremode = VNC_WIREMODE_TLS; qemu_set_fd_handler2(vs->csock, NULL, vncws_handshake_read, NULL, vs); return 0; } -static void vncws_tls_handshake_io(void *opaque) +void vncws_tls_handshake_io(void *opaque) { struct VncState *vs = (struct VncState *)opaque; - VNC_DEBUG("Handshake IO continue\n"); - vncws_start_tls_handshake(vs); -} - -void vncws_tls_handshake_peek(void *opaque) -{ - VncState *vs = opaque; - long ret; - - if (!vs->ws_tls.session) { - char peek[4]; - ret = qemu_recv(vs->csock, peek, sizeof(peek), MSG_PEEK); - if (ret && (strncmp(peek, "\x16", 1) == 0 - || strncmp(peek, "\x80", 1) == 0)) { - VNC_DEBUG("TLS Websocket connection recognized"); - vnc_tls_client_setup(vs, 1); - vncws_start_tls_handshake(vs); - } else { - vncws_handshake_read(vs); + if (!vs->tls.session) { + VNC_DEBUG("TLS Websocket setup\n"); + if (vnc_tls_client_setup(vs, vs->vd->tls.x509cert != NULL) < 0) { + return; } - } else { - qemu_set_fd_handler2(vs->csock, NULL, vncws_handshake_read, NULL, vs); } + VNC_DEBUG("Handshake IO continue\n"); + vncws_start_tls_handshake(vs); } #endif /* CONFIG_VNC_TLS */ @@ -89,8 +81,11 @@ void vncws_handshake_read(void *opaque) VncState *vs = opaque; uint8_t *handshake_end; long ret; - buffer_reserve(&vs->ws_input, 4096); - ret = vnc_client_read_buf(vs, buffer_end(&vs->ws_input), 4096); + /* Typical HTTP headers from novnc are 512 bytes, so limiting + * total header size to 4096 is easily enough. */ + size_t want = 4096 - vs->ws_input.offset; + buffer_reserve(&vs->ws_input, want); + ret = vnc_client_read_buf(vs, buffer_end(&vs->ws_input), want); if (!ret) { if (vs->csock == -1) { @@ -107,6 +102,9 @@ void vncws_handshake_read(void *opaque) vncws_process_handshake(vs, vs->ws_input.buffer, vs->ws_input.offset); buffer_advance(&vs->ws_input, handshake_end - vs->ws_input.buffer + strlen(WS_HANDSHAKE_END)); + } else if (vs->ws_input.offset >= 4096) { + VNC_DEBUG("End of headers not found in first 4096 bytes\n"); + vnc_client_error(vs); } } @@ -115,7 +113,7 @@ long vnc_client_read_ws(VncState *vs) { int ret, err; uint8_t *payload; - size_t payload_size, frame_size; + size_t payload_size, header_size; VNC_DEBUG("Read websocket %p size %zd offset %zd\n", vs->ws_input.buffer, vs->ws_input.capacity, vs->ws_input.offset); buffer_reserve(&vs->ws_input, 4096); @@ -125,18 +123,39 @@ long vnc_client_read_ws(VncState *vs) } vs->ws_input.offset += ret; - /* make sure that nothing is left in the ws_input buffer */ + ret = 0; + /* consume as much of ws_input buffer as possible */ do { - err = vncws_decode_frame(&vs->ws_input, &payload, - &payload_size, &frame_size); - if (err <= 0) { - return err; + if (vs->ws_payload_remain == 0) { + err = vncws_decode_frame_header(&vs->ws_input, + &header_size, + &vs->ws_payload_remain, + &vs->ws_payload_mask); + if (err <= 0) { + return err; + } + + buffer_advance(&vs->ws_input, header_size); } + if (vs->ws_payload_remain != 0) { + err = vncws_decode_frame_payload(&vs->ws_input, + &vs->ws_payload_remain, + &vs->ws_payload_mask, + &payload, + &payload_size); + if (err < 0) { + return err; + } + if (err == 0) { + return ret; + } + ret += err; - buffer_reserve(&vs->input, payload_size); - buffer_append(&vs->input, payload, payload_size); + buffer_reserve(&vs->input, payload_size); + buffer_append(&vs->input, payload, payload_size); - buffer_advance(&vs->ws_input, frame_size); + buffer_advance(&vs->ws_input, payload_size); + } } while (vs->ws_input.offset > 0); return ret; @@ -207,8 +226,7 @@ static void vncws_send_handshake_response(VncState *vs, const char* key) } response = g_strdup_printf(WS_HANDSHAKE, accept); - vnc_write(vs, response, strlen(response)); - vnc_flush(vs); + vnc_client_write_buf(vs, (const uint8_t *)response, strlen(response)); g_free(accept); g_free(response); @@ -274,15 +292,14 @@ void vncws_encode_frame(Buffer *output, const void *payload, buffer_append(output, payload, payload_size); } -int vncws_decode_frame(Buffer *input, uint8_t **payload, - size_t *payload_size, size_t *frame_size) +int vncws_decode_frame_header(Buffer *input, + size_t *header_size, + size_t *payload_remain, + WsMask *payload_mask) { unsigned char opcode = 0, fin = 0, has_mask = 0; - size_t header_size = 0; - uint32_t *payload32; + size_t payload_len; WsHeader *header = (WsHeader *)input->buffer; - WsMask mask; - int i; if (input->offset < WS_HEAD_MIN_LEN + 4) { /* header not complete */ @@ -292,7 +309,7 @@ int vncws_decode_frame(Buffer *input, uint8_t **payload, fin = (header->b0 & 0x80) >> 7; opcode = header->b0 & 0x0f; has_mask = (header->b1 & 0x80) >> 7; - *payload_size = header->b1 & 0x7f; + payload_len = header->b1 & 0x7f; if (opcode == WS_OPCODE_CLOSE) { /* disconnect */ @@ -309,40 +326,57 @@ int vncws_decode_frame(Buffer *input, uint8_t **payload, return -2; } - if (*payload_size < 126) { - header_size = 6; - mask = header->u.m; - } else if (*payload_size == 126 && input->offset >= 8) { - *payload_size = be16_to_cpu(header->u.s16.l16); - header_size = 8; - mask = header->u.s16.m16; - } else if (*payload_size == 127 && input->offset >= 14) { - *payload_size = be64_to_cpu(header->u.s64.l64); - header_size = 14; - mask = header->u.s64.m64; + if (payload_len < 126) { + *payload_remain = payload_len; + *header_size = 6; + *payload_mask = header->u.m; + } else if (payload_len == 126 && input->offset >= 8) { + *payload_remain = be16_to_cpu(header->u.s16.l16); + *header_size = 8; + *payload_mask = header->u.s16.m16; + } else if (payload_len == 127 && input->offset >= 14) { + *payload_remain = be64_to_cpu(header->u.s64.l64); + *header_size = 14; + *payload_mask = header->u.s64.m64; } else { /* header not complete */ return 0; } - *frame_size = header_size + *payload_size; + return 1; +} + +int vncws_decode_frame_payload(Buffer *input, + size_t *payload_remain, WsMask *payload_mask, + uint8_t **payload, size_t *payload_size) +{ + size_t i; + uint32_t *payload32; - if (input->offset < *frame_size) { - /* frame not complete */ + *payload = input->buffer; + /* If we aren't at the end of the payload, then drop + * off the last bytes, so we're always multiple of 4 + * for purpose of unmasking, except at end of payload + */ + if (input->offset < *payload_remain) { + *payload_size = input->offset - (input->offset % 4); + } else { + *payload_size = *payload_remain; + } + if (*payload_size == 0) { return 0; } - - *payload = input->buffer + header_size; + *payload_remain -= *payload_size; /* unmask frame */ /* process 1 frame (32 bit op) */ payload32 = (uint32_t *)(*payload); for (i = 0; i < *payload_size / 4; i++) { - payload32[i] ^= mask.u; + payload32[i] ^= payload_mask->u; } /* process the remaining bytes (if any) */ for (i *= 4; i < *payload_size; i++) { - (*payload)[i] ^= mask.c[i % 4]; + (*payload)[i] ^= payload_mask->c[i % 4]; } return 1; diff --git a/ui/vnc-ws.h b/ui/vnc-ws.h index 95c1b0a..14d4230 100644 --- a/ui/vnc-ws.h +++ b/ui/vnc-ws.h @@ -75,7 +75,7 @@ enum { }; #ifdef CONFIG_VNC_TLS -void vncws_tls_handshake_peek(void *opaque); +void vncws_tls_handshake_io(void *opaque); #endif /* CONFIG_VNC_TLS */ void vncws_handshake_read(void *opaque); long vnc_client_write_ws(VncState *vs); @@ -83,7 +83,12 @@ long vnc_client_read_ws(VncState *vs); void vncws_process_handshake(VncState *vs, uint8_t *line, size_t size); void vncws_encode_frame(Buffer *output, const void *payload, const size_t payload_size); -int vncws_decode_frame(Buffer *input, uint8_t **payload, - size_t *payload_size, size_t *frame_size); +int vncws_decode_frame_header(Buffer *input, + size_t *header_size, + size_t *payload_remain, + WsMask *payload_mask); +int vncws_decode_frame_payload(Buffer *input, + size_t *payload_remain, WsMask *payload_mask, + uint8_t **payload, size_t *payload_size); #endif /* __QEMU_UI_VNC_WS_H */ diff --git a/ui/vnc.c b/ui/vnc.c index 5707015..f989dfb 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -27,10 +27,12 @@ #include "vnc.h" #include "vnc-jobs.h" #include "trace.h" +#include "hw/qdev.h" #include "sysemu/sysemu.h" #include "qemu/sockets.h" #include "qemu/timer.h" #include "qemu/acl.h" +#include "qemu/config-file.h" #include "qapi/qmp/types.h" #include "qmp-commands.h" #include "qemu/osdep.h" @@ -46,7 +48,8 @@ static const struct timeval VNC_REFRESH_LOSSY = { 2, 0 }; #include "vnc_keysym.h" #include "d3des.h" -static VncDisplay *vnc_display; /* needed for info vnc */ +static QTAILQ_HEAD(, VncDisplay) vnc_displays = + QTAILQ_HEAD_INITIALIZER(vnc_displays); static int vnc_cursor_define(VncState *vs); static void vnc_release_modifiers(VncState *vs); @@ -65,12 +68,34 @@ static void vnc_set_share_mode(VncState *vs, VncShareMode mode) vs->csock, mn[vs->share_mode], mn[mode]); #endif - if (vs->share_mode == VNC_SHARE_MODE_EXCLUSIVE) { + switch (vs->share_mode) { + case VNC_SHARE_MODE_CONNECTING: + vs->vd->num_connecting--; + break; + case VNC_SHARE_MODE_SHARED: + vs->vd->num_shared--; + break; + case VNC_SHARE_MODE_EXCLUSIVE: vs->vd->num_exclusive--; + break; + default: + break; } + vs->share_mode = mode; - if (vs->share_mode == VNC_SHARE_MODE_EXCLUSIVE) { + + switch (vs->share_mode) { + case VNC_SHARE_MODE_CONNECTING: + vs->vd->num_connecting++; + break; + case VNC_SHARE_MODE_SHARED: + vs->vd->num_shared++; + break; + case VNC_SHARE_MODE_EXCLUSIVE: vs->vd->num_exclusive++; + break; + default: + break; } } @@ -226,10 +251,10 @@ static const char *vnc_auth_name(VncDisplay *vd) { return "unknown"; } -static VncServerInfo *vnc_server_info_get(void) +static VncServerInfo *vnc_server_info_get(VncDisplay *vd) { VncServerInfo *info; - VncBasicInfo *bi = vnc_basic_info_get_from_server_addr(vnc_display->lsock); + VncBasicInfo *bi = vnc_basic_info_get_from_server_addr(vd->lsock); if (!bi) { return NULL; } @@ -237,7 +262,7 @@ static VncServerInfo *vnc_server_info_get(void) info = g_malloc(sizeof(*info)); info->base = bi; info->has_auth = true; - info->auth = g_strdup(vnc_auth_name(vnc_display)); + info->auth = g_strdup(vnc_auth_name(vd)); return info; } @@ -282,7 +307,7 @@ static void vnc_qmp_event(VncState *vs, QAPIEvent event) } g_assert(vs->info->base); - si = vnc_server_info_get(); + si = vnc_server_info_get(vs->vd); if (!si) { return; } @@ -328,6 +353,9 @@ static VncClientInfo *qmp_query_vnc_client(const VncState *client) info->base->host = g_strdup(host); info->base->service = g_strdup(serv); info->base->family = inet_netfamily(sa.ss_family); +#ifdef CONFIG_VNC_WS + info->base->websocket = client->websocket; +#endif #ifdef CONFIG_VNC_TLS if (client->tls.session && client->tls.dname) { @@ -345,43 +373,59 @@ static VncClientInfo *qmp_query_vnc_client(const VncState *client) return info; } +static VncDisplay *vnc_display_find(const char *id) +{ + VncDisplay *vd; + + if (id == NULL) { + return QTAILQ_FIRST(&vnc_displays); + } + QTAILQ_FOREACH(vd, &vnc_displays, next) { + if (strcmp(id, vd->id) == 0) { + return vd; + } + } + return NULL; +} + +static VncClientInfoList *qmp_query_client_list(VncDisplay *vd) +{ + VncClientInfoList *cinfo, *prev = NULL; + VncState *client; + + QTAILQ_FOREACH(client, &vd->clients, next) { + cinfo = g_new0(VncClientInfoList, 1); + cinfo->value = qmp_query_vnc_client(client); + cinfo->next = prev; + prev = cinfo; + } + return prev; +} + VncInfo *qmp_query_vnc(Error **errp) { VncInfo *info = g_malloc0(sizeof(*info)); + VncDisplay *vd = vnc_display_find(NULL); - if (vnc_display == NULL || vnc_display->display == NULL) { + if (vd == NULL || !vd->enabled) { info->enabled = false; } else { - VncClientInfoList *cur_item = NULL; struct sockaddr_storage sa; socklen_t salen = sizeof(sa); char host[NI_MAXHOST]; char serv[NI_MAXSERV]; - VncState *client; info->enabled = true; /* for compatibility with the original command */ info->has_clients = true; + info->clients = qmp_query_client_list(vd); - QTAILQ_FOREACH(client, &vnc_display->clients, next) { - VncClientInfoList *cinfo = g_malloc0(sizeof(*info)); - cinfo->value = qmp_query_vnc_client(client); - - /* XXX: waiting for the qapi to support GSList */ - if (!cur_item) { - info->clients = cur_item = cinfo; - } else { - cur_item->next = cinfo; - cur_item = cinfo; - } - } - - if (vnc_display->lsock == -1) { + if (vd->lsock == -1) { return info; } - if (getsockname(vnc_display->lsock, (struct sockaddr *)&sa, + if (getsockname(vd->lsock, (struct sockaddr *)&sa, &salen) == -1) { error_set(errp, QERR_UNDEFINED_ERROR); goto out_error; @@ -405,7 +449,7 @@ VncInfo *qmp_query_vnc(Error **errp) info->family = inet_netfamily(sa.ss_family); info->has_auth = true; - info->auth = g_strdup(vnc_auth_name(vnc_display)); + info->auth = g_strdup(vnc_auth_name(vd)); } return info; @@ -415,6 +459,142 @@ out_error: return NULL; } +static VncBasicInfoList *qmp_query_server_entry(int socket, + bool websocket, + VncBasicInfoList *prev) +{ + VncBasicInfoList *list; + VncBasicInfo *info; + struct sockaddr_storage sa; + socklen_t salen = sizeof(sa); + char host[NI_MAXHOST]; + char serv[NI_MAXSERV]; + + if (getsockname(socket, (struct sockaddr *)&sa, &salen) < 0 || + getnameinfo((struct sockaddr *)&sa, salen, + host, sizeof(host), serv, sizeof(serv), + NI_NUMERICHOST | NI_NUMERICSERV) < 0) { + return prev; + } + + info = g_new0(VncBasicInfo, 1); + info->host = g_strdup(host); + info->service = g_strdup(serv); + info->family = inet_netfamily(sa.ss_family); + info->websocket = websocket; + + list = g_new0(VncBasicInfoList, 1); + list->value = info; + list->next = prev; + return list; +} + +static void qmp_query_auth(VncDisplay *vd, VncInfo2 *info) +{ + switch (vd->auth) { + case VNC_AUTH_VNC: + info->auth = VNC_PRIMARY_AUTH_VNC; + break; + case VNC_AUTH_RA2: + info->auth = VNC_PRIMARY_AUTH_RA2; + break; + case VNC_AUTH_RA2NE: + info->auth = VNC_PRIMARY_AUTH_RA2NE; + break; + case VNC_AUTH_TIGHT: + info->auth = VNC_PRIMARY_AUTH_TIGHT; + break; + case VNC_AUTH_ULTRA: + info->auth = VNC_PRIMARY_AUTH_ULTRA; + break; + case VNC_AUTH_TLS: + info->auth = VNC_PRIMARY_AUTH_TLS; + break; + case VNC_AUTH_VENCRYPT: + info->auth = VNC_PRIMARY_AUTH_VENCRYPT; +#ifdef CONFIG_VNC_TLS + info->has_vencrypt = true; + switch (vd->subauth) { + case VNC_AUTH_VENCRYPT_PLAIN: + info->vencrypt = VNC_VENCRYPT_SUB_AUTH_PLAIN; + break; + case VNC_AUTH_VENCRYPT_TLSNONE: + info->vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_NONE; + break; + case VNC_AUTH_VENCRYPT_TLSVNC: + info->vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_VNC; + break; + case VNC_AUTH_VENCRYPT_TLSPLAIN: + info->vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_PLAIN; + break; + case VNC_AUTH_VENCRYPT_X509NONE: + info->vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_NONE; + break; + case VNC_AUTH_VENCRYPT_X509VNC: + info->vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_VNC; + break; + case VNC_AUTH_VENCRYPT_X509PLAIN: + info->vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_PLAIN; + break; + case VNC_AUTH_VENCRYPT_TLSSASL: + info->vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_SASL; + break; + case VNC_AUTH_VENCRYPT_X509SASL: + info->vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_SASL; + break; + default: + info->has_vencrypt = false; + break; + } +#endif + break; + case VNC_AUTH_SASL: + info->auth = VNC_PRIMARY_AUTH_SASL; + break; + case VNC_AUTH_NONE: + default: + info->auth = VNC_PRIMARY_AUTH_NONE; + break; + } +} + +VncInfo2List *qmp_query_vnc_servers(Error **errp) +{ + VncInfo2List *item, *prev = NULL; + VncInfo2 *info; + VncDisplay *vd; + DeviceState *dev; + + QTAILQ_FOREACH(vd, &vnc_displays, next) { + info = g_new0(VncInfo2, 1); + info->id = g_strdup(vd->id); + info->clients = qmp_query_client_list(vd); + qmp_query_auth(vd, info); + if (vd->dcl.con) { + dev = DEVICE(object_property_get_link(OBJECT(vd->dcl.con), + "device", NULL)); + info->has_display = true; + info->display = g_strdup(dev->id); + } + if (vd->lsock != -1) { + info->server = qmp_query_server_entry(vd->lsock, false, + info->server); + } +#ifdef CONFIG_VNC_WS + if (vd->lwebsock != -1) { + info->server = qmp_query_server_entry(vd->lwebsock, true, + info->server); + } +#endif + + item = g_new0(VncInfo2List, 1); + item->value = info; + item->next = prev; + prev = item; + } + return prev; +} + /* TODO 1) Get the queue working for IO. 2) there is some weirdness when using the -S option (the screen is grey @@ -479,10 +659,6 @@ void buffer_reserve(Buffer *buffer, size_t len) if ((buffer->capacity - buffer->offset) < len) { buffer->capacity += (len + 1024); buffer->buffer = g_realloc(buffer->buffer, buffer->capacity); - if (buffer->buffer == NULL) { - fprintf(stderr, "vnc: out of memory\n"); - exit(1); - } } } @@ -853,7 +1029,7 @@ static int vnc_cursor_define(VncState *vs) static void vnc_dpy_cursor_define(DisplayChangeListener *dcl, QEMUCursor *c) { - VncDisplay *vd = vnc_display; + VncDisplay *vd = container_of(dcl, VncDisplay, dcl); VncState *vs; cursor_put(vd->cursor); @@ -935,6 +1111,12 @@ static int vnc_update_client(VncState *vs, int has_dirty, bool sync) n += vnc_job_add_rect(job, x * VNC_DIRTY_PIXELS_PER_BIT, y, (x2 - x) * VNC_DIRTY_PIXELS_PER_BIT, h); } + if (!x && x2 == width / VNC_DIRTY_PIXELS_PER_BIT) { + y += h; + if (y == height) { + break; + } + } } vnc_job_push(job); @@ -1161,15 +1343,8 @@ long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen) if (vs->tls.session) { ret = vnc_client_write_tls(&vs->tls.session, data, datalen); } else { -#ifdef CONFIG_VNC_WS - if (vs->ws_tls.session) { - ret = vnc_client_write_tls(&vs->ws_tls.session, data, datalen); - } else -#endif /* CONFIG_VNC_WS */ #endif /* CONFIG_VNC_TLS */ - { - ret = send(vs->csock, (const void *)data, datalen, 0); - } + ret = send(vs->csock, (const void *)data, datalen, 0); #ifdef CONFIG_VNC_TLS } #endif /* CONFIG_VNC_TLS */ @@ -1309,15 +1484,8 @@ long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen) if (vs->tls.session) { ret = vnc_client_read_tls(&vs->tls.session, data, datalen); } else { -#ifdef CONFIG_VNC_WS - if (vs->ws_tls.session) { - ret = vnc_client_read_tls(&vs->ws_tls.session, data, datalen); - } else -#endif /* CONFIG_VNC_WS */ #endif /* CONFIG_VNC_TLS */ - { - ret = qemu_recv(vs->csock, data, datalen, 0); - } + ret = qemu_recv(vs->csock, data, datalen, 0); #ifdef CONFIG_VNC_TLS } #endif /* CONFIG_VNC_TLS */ @@ -1647,7 +1815,8 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym) vs->modifiers_state[keycode] = 0; break; case 0x02 ... 0x0a: /* '1' to '9' keys */ - if (down && vs->modifiers_state[0x1d] && vs->modifiers_state[0x38]) { + if (vs->vd->dcl.con == NULL && + down && vs->modifiers_state[0x1d] && vs->modifiers_state[0x38]) { /* Reset the modifiers sent to the current console */ reset_keys(vs); console_select(keycode - 0x02); @@ -2055,8 +2224,8 @@ static void set_pixel_format(VncState *vs, set_pixel_conversion(vs); - graphic_hw_invalidate(NULL); - graphic_hw_update(NULL); + graphic_hw_invalidate(vs->vd->dcl.con); + graphic_hw_update(vs->vd->dcl.con); } static void pixel_format_message (VncState *vs) { @@ -2217,34 +2386,34 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) case 4: vs->as.fmt = AUD_FMT_U32; break; case 5: vs->as.fmt = AUD_FMT_S32; break; default: - printf("Invalid audio format %d\n", read_u8(data, 4)); + VNC_DEBUG("Invalid audio format %d\n", read_u8(data, 4)); vnc_client_error(vs); break; } vs->as.nchannels = read_u8(data, 5); if (vs->as.nchannels != 1 && vs->as.nchannels != 2) { - printf("Invalid audio channel coount %d\n", - read_u8(data, 5)); + VNC_DEBUG("Invalid audio channel coount %d\n", + read_u8(data, 5)); vnc_client_error(vs); break; } vs->as.freq = read_u32(data, 6); break; default: - printf ("Invalid audio message %d\n", read_u8(data, 4)); + VNC_DEBUG("Invalid audio message %d\n", read_u8(data, 4)); vnc_client_error(vs); break; } break; default: - printf("Msg: %d\n", read_u16(data, 0)); + VNC_DEBUG("Msg: %d\n", read_u16(data, 0)); vnc_client_error(vs); break; } break; default: - printf("Msg: %d\n", data[0]); + VNC_DEBUG("Msg: %d\n", data[0]); vnc_client_error(vs); break; } @@ -2317,6 +2486,11 @@ static int protocol_client_init(VncState *vs, uint8_t *data, size_t len) } vnc_set_share_mode(vs, mode); + if (vs->vd->num_shared > vs->vd->connections_limit) { + vnc_disconnect_start(vs); + return 0; + } + vs->client_width = pixman_image_get_width(vs->vd->server); vs->client_height = pixman_image_get_height(vs->vd->server); vnc_write_u16(vs, vs->client_width); @@ -2783,7 +2957,7 @@ static void vnc_refresh(DisplayChangeListener *dcl) return; } - graphic_hw_update(NULL); + graphic_hw_update(vd->dcl.con); if (vnc_trylock_display(vd)) { update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE); @@ -2818,18 +2992,22 @@ static void vnc_connect(VncDisplay *vd, int csock, int i; vs->csock = csock; + vs->vd = vd; if (skipauth) { vs->auth = VNC_AUTH_NONE; -#ifdef CONFIG_VNC_TLS vs->subauth = VNC_AUTH_INVALID; -#endif } else { - vs->auth = vd->auth; -#ifdef CONFIG_VNC_TLS - vs->subauth = vd->subauth; -#endif + if (websocket) { + vs->auth = vd->ws_auth; + vs->subauth = VNC_AUTH_INVALID; + } else { + vs->auth = vd->auth; + vs->subauth = vd->subauth; + } } + VNC_DEBUG("Client sock=%d ws=%d auth=%d subauth=%d\n", + csock, websocket, vs->auth, vs->subauth); vs->lossy_rect = g_malloc0(VNC_STAT_ROWS * sizeof (*vs->lossy_rect)); for (i = 0; i < VNC_STAT_ROWS; ++i) { @@ -2843,8 +3021,8 @@ static void vnc_connect(VncDisplay *vd, int csock, if (websocket) { vs->websocket = 1; #ifdef CONFIG_VNC_TLS - if (vd->tls.x509cert) { - qemu_set_fd_handler2(vs->csock, NULL, vncws_tls_handshake_peek, + if (vd->ws_tls) { + qemu_set_fd_handler2(vs->csock, NULL, vncws_tls_handshake_io, NULL, vs); } else #endif /* CONFIG_VNC_TLS */ @@ -2862,14 +3040,21 @@ static void vnc_connect(VncDisplay *vd, int csock, vnc_qmp_event(vs, QAPI_EVENT_VNC_CONNECTED); vnc_set_share_mode(vs, VNC_SHARE_MODE_CONNECTING); - vs->vd = vd; - #ifdef CONFIG_VNC_WS if (!vs->websocket) #endif { vnc_init_state(vs); } + + if (vd->num_connecting > vd->connections_limit) { + QTAILQ_FOREACH(vs, &vd->clients, next) { + if (vs->share_mode == VNC_SHARE_MODE_CONNECTING) { + vnc_disconnect_start(vs); + return; + } + } + } } void vnc_init_state(VncState *vs) @@ -2888,9 +3073,9 @@ void vnc_init_state(VncState *vs) qemu_mutex_init(&vs->output_mutex); vs->bh = qemu_bh_new(vnc_jobs_bh, vs); - QTAILQ_INSERT_HEAD(&vd->clients, vs, next); + QTAILQ_INSERT_TAIL(&vd->clients, vs, next); - graphic_hw_update(NULL); + graphic_hw_update(vd->dcl.con); vnc_write(vs, "RFB 003.008\n", 12); vnc_flush(vs); @@ -2913,7 +3098,7 @@ static void vnc_listen_read(void *opaque, bool websocket) int csock; /* Catch-up */ - graphic_hw_update(NULL); + graphic_hw_update(vs->dcl.con); #ifdef CONFIG_VNC_WS if (websocket) { csock = qemu_accept(vs->lwebsock, (struct sockaddr *)&addr, &addrlen); @@ -2942,20 +3127,27 @@ static void vnc_listen_websocket_read(void *opaque) #endif /* CONFIG_VNC_WS */ static const DisplayChangeListenerOps dcl_ops = { - .dpy_name = "vnc", - .dpy_refresh = vnc_refresh, - .dpy_gfx_copy = vnc_dpy_copy, - .dpy_gfx_update = vnc_dpy_update, - .dpy_gfx_switch = vnc_dpy_switch, - .dpy_mouse_set = vnc_mouse_set, - .dpy_cursor_define = vnc_dpy_cursor_define, + .dpy_name = "vnc", + .dpy_refresh = vnc_refresh, + .dpy_gfx_copy = vnc_dpy_copy, + .dpy_gfx_update = vnc_dpy_update, + .dpy_gfx_switch = vnc_dpy_switch, + .dpy_gfx_check_format = qemu_pixman_check_format, + .dpy_mouse_set = vnc_mouse_set, + .dpy_cursor_define = vnc_dpy_cursor_define, }; -void vnc_display_init(DisplayState *ds) +void vnc_display_init(const char *id) { - VncDisplay *vs = g_malloc0(sizeof(*vs)); + VncDisplay *vs; + + if (vnc_display_find(id) != NULL) { + return; + } + vs = g_malloc0(sizeof(*vs)); - vnc_display = vs; + vs->id = strdup(id); + QTAILQ_INSERT_TAIL(&vnc_displays, vs, next); vs->lsock = -1; #ifdef CONFIG_VNC_WS @@ -2983,22 +3175,19 @@ void vnc_display_init(DisplayState *ds) } -static void vnc_display_close(DisplayState *ds) +static void vnc_display_close(VncDisplay *vs) { - VncDisplay *vs = vnc_display; - if (!vs) return; - g_free(vs->display); - vs->display = NULL; + vs->enabled = false; + vs->is_unix = false; if (vs->lsock != -1) { qemu_set_fd_handler2(vs->lsock, NULL, NULL, NULL, NULL); close(vs->lsock); vs->lsock = -1; } #ifdef CONFIG_VNC_WS - g_free(vs->ws_display); - vs->ws_display = NULL; + vs->ws_enabled = false; if (vs->lwebsock != -1) { qemu_set_fd_handler2(vs->lwebsock, NULL, NULL, NULL, NULL); close(vs->lwebsock); @@ -3006,15 +3195,15 @@ static void vnc_display_close(DisplayState *ds) } #endif /* CONFIG_VNC_WS */ vs->auth = VNC_AUTH_INVALID; -#ifdef CONFIG_VNC_TLS vs->subauth = VNC_AUTH_INVALID; +#ifdef CONFIG_VNC_TLS vs->tls.x509verify = 0; #endif } -int vnc_display_password(DisplayState *ds, const char *password) +int vnc_display_password(const char *id, const char *password) { - VncDisplay *vs = vnc_display; + VncDisplay *vs = vnc_display_find(id); if (!vs) { return -EINVAL; @@ -3031,9 +3220,9 @@ int vnc_display_password(DisplayState *ds, const char *password) return 0; } -int vnc_display_pw_expire(DisplayState *ds, time_t expires) +int vnc_display_pw_expire(const char *id, time_t expires) { - VncDisplay *vs = vnc_display; + VncDisplay *vs = vnc_display_find(id); if (!vs) { return -EINVAL; @@ -3043,189 +3232,143 @@ int vnc_display_pw_expire(DisplayState *ds, time_t expires) return 0; } -char *vnc_display_local_addr(DisplayState *ds) +char *vnc_display_local_addr(const char *id) { - VncDisplay *vs = vnc_display; - + VncDisplay *vs = vnc_display_find(id); + + assert(vs); return vnc_socket_local_addr("%s:%s", vs->lsock); } -void vnc_display_open(DisplayState *ds, const char *display, Error **errp) -{ - VncDisplay *vs = vnc_display; - const char *options; - int password = 0; - int reverse = 0; -#ifdef CONFIG_VNC_TLS - int tls = 0, x509 = 0; -#endif -#ifdef CONFIG_VNC_SASL - int sasl = 0; - int saslErr; -#endif -#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL) - int acl = 0; -#endif - int lock_key_sync = 1; - - if (!vnc_display) { - error_setg(errp, "VNC display not active"); - return; - } - vnc_display_close(ds); - if (strcmp(display, "none") == 0) - return; - - vs->display = g_strdup(display); - vs->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE; - - options = display; - while ((options = strchr(options, ','))) { - options++; - if (strncmp(options, "password", 8) == 0) { - if (fips_get_state()) { - error_setg(errp, - "VNC password auth disabled due to FIPS mode, " - "consider using the VeNCrypt or SASL authentication " - "methods as an alternative"); - goto fail; - } - password = 1; /* Require password auth */ - } else if (strncmp(options, "reverse", 7) == 0) { - reverse = 1; - } else if (strncmp(options, "no-lock-key-sync", 16) == 0) { - lock_key_sync = 0; -#ifdef CONFIG_VNC_SASL - } else if (strncmp(options, "sasl", 4) == 0) { - sasl = 1; /* Require SASL auth */ -#endif -#ifdef CONFIG_VNC_WS - } else if (strncmp(options, "websocket", 9) == 0) { - char *start, *end; - vs->websocket = 1; - - /* Check for 'websocket=' */ - start = strchr(options, '='); - end = strchr(options, ','); - if (start && (!end || (start < end))) { - int len = end ? end-(start+1) : strlen(start+1); - if (len < 6) { - /* extract the host specification from display */ - char *host = NULL, *port = NULL, *host_end = NULL; - port = g_strndup(start + 1, len); - - /* ipv6 hosts have colons */ - end = strchr(display, ','); - host_end = g_strrstr_len(display, end - display, ":"); - - if (host_end) { - host = g_strndup(display, host_end - display + 1); - } else { - host = g_strndup(":", 1); - } - vs->ws_display = g_strconcat(host, port, NULL); - g_free(host); - g_free(port); - } - } -#endif /* CONFIG_VNC_WS */ -#ifdef CONFIG_VNC_TLS - } else if (strncmp(options, "tls", 3) == 0) { - tls = 1; /* Require TLS */ - } else if (strncmp(options, "x509", 4) == 0) { - char *start, *end; - x509 = 1; /* Require x509 certificates */ - if (strncmp(options, "x509verify", 10) == 0) - vs->tls.x509verify = 1; /* ...and verify client certs */ - - /* Now check for 'x509=/some/path' postfix - * and use that to setup x509 certificate/key paths */ - start = strchr(options, '='); - end = strchr(options, ','); - if (start && (!end || (start < end))) { - int len = end ? end-(start+1) : strlen(start+1); - char *path = g_strndup(start + 1, len); - - VNC_DEBUG("Trying certificate path '%s'\n", path); - if (vnc_tls_set_x509_creds_dir(vs, path) < 0) { - error_setg(errp, "Failed to find x509 certificates/keys in %s", path); - g_free(path); - goto fail; - } - g_free(path); - } else { - error_setg(errp, "No certificate path provided"); - goto fail; - } -#endif -#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL) - } else if (strncmp(options, "acl", 3) == 0) { - acl = 1; -#endif - } else if (strncmp(options, "lossy", 5) == 0) { -#ifdef CONFIG_VNC_JPEG - vs->lossy = true; -#endif - } else if (strncmp(options, "non-adaptive", 12) == 0) { - vs->non_adaptive = true; - } else if (strncmp(options, "share=", 6) == 0) { - if (strncmp(options+6, "ignore", 6) == 0) { - vs->share_policy = VNC_SHARE_POLICY_IGNORE; - } else if (strncmp(options+6, "allow-exclusive", 15) == 0) { - vs->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE; - } else if (strncmp(options+6, "force-shared", 12) == 0) { - vs->share_policy = VNC_SHARE_POLICY_FORCE_SHARED; - } else { - error_setg(errp, "unknown vnc share= option"); - goto fail; - } - } - } - - /* adaptive updates are only used with tight encoding and - * if lossy updates are enabled so we can disable all the - * calculations otherwise */ - if (!vs->lossy) { - vs->non_adaptive = true; - } +static QemuOptsList qemu_vnc_opts = { + .name = "vnc", + .head = QTAILQ_HEAD_INITIALIZER(qemu_vnc_opts.head), + .implied_opt_name = "vnc", + .desc = { + { + .name = "vnc", + .type = QEMU_OPT_STRING, + },{ + .name = "websocket", + .type = QEMU_OPT_STRING, + },{ + .name = "x509", + .type = QEMU_OPT_STRING, + },{ + .name = "share", + .type = QEMU_OPT_STRING, + },{ + .name = "display", + .type = QEMU_OPT_STRING, + },{ + .name = "head", + .type = QEMU_OPT_NUMBER, + },{ + .name = "connections", + .type = QEMU_OPT_NUMBER, + },{ + .name = "to", + .type = QEMU_OPT_NUMBER, + },{ + .name = "ipv4", + .type = QEMU_OPT_BOOL, + },{ + .name = "ipv6", + .type = QEMU_OPT_BOOL, + },{ + .name = "password", + .type = QEMU_OPT_BOOL, + },{ + .name = "reverse", + .type = QEMU_OPT_BOOL, + },{ + .name = "lock-key-sync", + .type = QEMU_OPT_BOOL, + },{ + .name = "sasl", + .type = QEMU_OPT_BOOL, + },{ + .name = "tls", + .type = QEMU_OPT_BOOL, + },{ + .name = "x509verify", + .type = QEMU_OPT_STRING, + },{ + .name = "acl", + .type = QEMU_OPT_BOOL, + },{ + .name = "lossy", + .type = QEMU_OPT_BOOL, + },{ + .name = "non-adaptive", + .type = QEMU_OPT_BOOL, + }, + { /* end of list */ } + }, +}; -#ifdef CONFIG_VNC_TLS - if (acl && x509 && vs->tls.x509verify) { - if (!(vs->tls.acl = qemu_acl_init("vnc.x509dname"))) { - fprintf(stderr, "Failed to create x509 dname ACL\n"); - exit(1); - } - } -#endif -#ifdef CONFIG_VNC_SASL - if (acl && sasl) { - if (!(vs->sasl.acl = qemu_acl_init("vnc.username"))) { - fprintf(stderr, "Failed to create username ACL\n"); - exit(1); - } - } -#endif +static void +vnc_display_setup_auth(VncDisplay *vs, + bool password, + bool sasl, + bool tls, + bool x509, + bool websocket) +{ /* - * Combinations we support here: + * We have a choice of 3 authentication options + * + * 1. none + * 2. vnc + * 3. sasl + * + * The channel can be run in 2 modes + * + * 1. clear + * 2. tls + * + * And TLS can use 2 types of credentials * - * - no-auth (clear text, no auth) - * - password (clear text, weak auth) - * - sasl (encrypt, good auth *IF* using Kerberos via GSSAPI) - * - tls (encrypt, weak anonymous creds, no auth) - * - tls + password (encrypt, weak anonymous creds, weak auth) - * - tls + sasl (encrypt, weak anonymous creds, good auth) - * - tls + x509 (encrypt, good x509 creds, no auth) - * - tls + x509 + password (encrypt, good x509 creds, weak auth) - * - tls + x509 + sasl (encrypt, good x509 creds, good auth) + * 1. anon + * 2. x509 * - * NB1. TLS is a stackable auth scheme. - * NB2. the x509 schemes have option to validate a client cert dname + * We thus have 9 possible logical combinations + * + * 1. clear + none + * 2. clear + vnc + * 3. clear + sasl + * 4. tls + anon + none + * 5. tls + anon + vnc + * 6. tls + anon + sasl + * 7. tls + x509 + none + * 8. tls + x509 + vnc + * 9. tls + x509 + sasl + * + * These need to be mapped into the VNC auth schemes + * in an appropriate manner. In regular VNC, all the + * TLS options get mapped into VNC_AUTH_VENCRYPT + * sub-auth types. + * + * In websockets, the https:// protocol already provides + * TLS support, so there is no need to make use of the + * VeNCrypt extension. Furthermore, websockets browser + * clients could not use VeNCrypt even if they wanted to, + * as they cannot control when the TLS handshake takes + * place. Thus there is no option but to rely on https://, + * meaning combinations 4->6 and 7->9 will be mapped to + * VNC auth schemes in the same way as combos 1->3. + * + * Regardless of fact that we have a different mapping to + * VNC auth mechs for plain VNC vs websockets VNC, the end + * result has the same security characteristics. */ if (password) { -#ifdef CONFIG_VNC_TLS if (tls) { vs->auth = VNC_AUTH_VENCRYPT; + if (websocket) { + vs->ws_tls = true; + } if (x509) { VNC_DEBUG("Initializing VNC server with x509 password auth\n"); vs->subauth = VNC_AUTH_VENCRYPT_X509VNC; @@ -3234,18 +3377,21 @@ void vnc_display_open(DisplayState *ds, const char *display, Error **errp) vs->subauth = VNC_AUTH_VENCRYPT_TLSVNC; } } else { -#endif /* CONFIG_VNC_TLS */ VNC_DEBUG("Initializing VNC server with password auth\n"); vs->auth = VNC_AUTH_VNC; -#ifdef CONFIG_VNC_TLS vs->subauth = VNC_AUTH_INVALID; } -#endif /* CONFIG_VNC_TLS */ -#ifdef CONFIG_VNC_SASL + if (websocket) { + vs->ws_auth = VNC_AUTH_VNC; + } else { + vs->ws_auth = VNC_AUTH_INVALID; + } } else if (sasl) { -#ifdef CONFIG_VNC_TLS if (tls) { vs->auth = VNC_AUTH_VENCRYPT; + if (websocket) { + vs->ws_tls = true; + } if (x509) { VNC_DEBUG("Initializing VNC server with x509 SASL auth\n"); vs->subauth = VNC_AUTH_VENCRYPT_X509SASL; @@ -3254,18 +3400,21 @@ void vnc_display_open(DisplayState *ds, const char *display, Error **errp) vs->subauth = VNC_AUTH_VENCRYPT_TLSSASL; } } else { -#endif /* CONFIG_VNC_TLS */ VNC_DEBUG("Initializing VNC server with SASL auth\n"); vs->auth = VNC_AUTH_SASL; -#ifdef CONFIG_VNC_TLS vs->subauth = VNC_AUTH_INVALID; } -#endif /* CONFIG_VNC_TLS */ -#endif /* CONFIG_VNC_SASL */ + if (websocket) { + vs->ws_auth = VNC_AUTH_SASL; + } else { + vs->ws_auth = VNC_AUTH_INVALID; + } } else { -#ifdef CONFIG_VNC_TLS if (tls) { vs->auth = VNC_AUTH_VENCRYPT; + if (websocket) { + vs->ws_tls = true; + } if (x509) { VNC_DEBUG("Initializing VNC server with x509 no auth\n"); vs->subauth = VNC_AUTH_VENCRYPT_X509NONE; @@ -3274,14 +3423,218 @@ void vnc_display_open(DisplayState *ds, const char *display, Error **errp) vs->subauth = VNC_AUTH_VENCRYPT_TLSNONE; } } else { -#endif VNC_DEBUG("Initializing VNC server with no auth\n"); vs->auth = VNC_AUTH_NONE; -#ifdef CONFIG_VNC_TLS vs->subauth = VNC_AUTH_INVALID; } + if (websocket) { + vs->ws_auth = VNC_AUTH_NONE; + } else { + vs->ws_auth = VNC_AUTH_INVALID; + } + } +} + +void vnc_display_open(const char *id, Error **errp) +{ + VncDisplay *vs = vnc_display_find(id); + QemuOpts *opts = qemu_opts_find(&qemu_vnc_opts, id); + QemuOpts *sopts, *wsopts; + const char *share, *device_id; + QemuConsole *con; + bool password = false; + bool reverse = false; + const char *vnc; + const char *has_to; + char *h; + bool has_ipv4 = false; + bool has_ipv6 = false; + const char *websocket; + bool tls = false, x509 = false; +#ifdef CONFIG_VNC_TLS + const char *path; +#endif + bool sasl = false; +#ifdef CONFIG_VNC_SASL + int saslErr; +#endif +#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL) + int acl = 0; +#endif + int lock_key_sync = 1; + + if (!vs) { + error_setg(errp, "VNC display not active"); + return; + } + vnc_display_close(vs); + + if (!opts) { + return; + } + vnc = qemu_opt_get(opts, "vnc"); + if (!vnc || strcmp(vnc, "none") == 0) { + return; + } + + sopts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort); + wsopts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort); + + h = strrchr(vnc, ':'); + if (h) { + char *host; + size_t hlen = h - vnc; + + if (vnc[0] == '[' && vnc[hlen - 1] == ']') { + host = g_strndup(vnc + 1, hlen - 2); + } else { + host = g_strndup(vnc, hlen); + } + qemu_opt_set(sopts, "host", host, &error_abort); + qemu_opt_set(wsopts, "host", host, &error_abort); + qemu_opt_set(sopts, "port", h+1, &error_abort); + g_free(host); + } else { + error_setg(errp, "no vnc port specified"); + goto fail; + } + + has_to = qemu_opt_get(opts, "to"); + has_ipv4 = qemu_opt_get_bool(opts, "ipv4", false); + has_ipv6 = qemu_opt_get_bool(opts, "ipv6", false); + if (has_to) { + qemu_opt_set(sopts, "to", has_to, &error_abort); + qemu_opt_set(wsopts, "to", has_to, &error_abort); + } + if (has_ipv4) { + qemu_opt_set(sopts, "ipv4", "on", &error_abort); + qemu_opt_set(wsopts, "ipv4", "on", &error_abort); + } + if (has_ipv6) { + qemu_opt_set(sopts, "ipv6", "on", &error_abort); + qemu_opt_set(wsopts, "ipv6", "on", &error_abort); + } + + password = qemu_opt_get_bool(opts, "password", false); + if (password && fips_get_state()) { + error_setg(errp, + "VNC password auth disabled due to FIPS mode, " + "consider using the VeNCrypt or SASL authentication " + "methods as an alternative"); + goto fail; + } + + reverse = qemu_opt_get_bool(opts, "reverse", false); + lock_key_sync = qemu_opt_get_bool(opts, "lock-key-sync", true); + sasl = qemu_opt_get_bool(opts, "sasl", false); +#ifndef CONFIG_VNC_SASL + if (sasl) { + error_setg(errp, "VNC SASL auth requires cyrus-sasl support"); + goto fail; + } +#endif /* CONFIG_VNC_SASL */ + tls = qemu_opt_get_bool(opts, "tls", false); +#ifdef CONFIG_VNC_TLS + path = qemu_opt_get(opts, "x509"); + if (!path) { + path = qemu_opt_get(opts, "x509verify"); + if (path) { + vs->tls.x509verify = true; + } + } + if (path) { + x509 = true; + if (vnc_tls_set_x509_creds_dir(vs, path) < 0) { + error_setg(errp, "Failed to find x509 certificates/keys in %s", + path); + goto fail; + } + } +#else /* ! CONFIG_VNC_TLS */ + if (tls) { + error_setg(errp, "VNC TLS auth requires gnutls support"); + goto fail; + } +#endif /* ! CONFIG_VNC_TLS */ +#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL) + acl = qemu_opt_get_bool(opts, "acl", false); #endif + + share = qemu_opt_get(opts, "share"); + if (share) { + if (strcmp(share, "ignore") == 0) { + vs->share_policy = VNC_SHARE_POLICY_IGNORE; + } else if (strcmp(share, "allow-exclusive") == 0) { + vs->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE; + } else if (strcmp(share, "force-shared") == 0) { + vs->share_policy = VNC_SHARE_POLICY_FORCE_SHARED; + } else { + error_setg(errp, "unknown vnc share= option"); + goto fail; + } + } else { + vs->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE; + } + vs->connections_limit = qemu_opt_get_number(opts, "connections", 32); + + websocket = qemu_opt_get(opts, "websocket"); + if (websocket) { +#ifdef CONFIG_VNC_WS + vs->ws_enabled = true; + qemu_opt_set(wsopts, "port", websocket, &error_abort); +#else /* ! CONFIG_VNC_WS */ + error_setg(errp, "Websockets protocol requires gnutls support"); + goto fail; +#endif /* ! CONFIG_VNC_WS */ + } + +#ifdef CONFIG_VNC_JPEG + vs->lossy = qemu_opt_get_bool(opts, "lossy", false); +#endif + vs->non_adaptive = qemu_opt_get_bool(opts, "non-adaptive", false); + /* adaptive updates are only used with tight encoding and + * if lossy updates are enabled so we can disable all the + * calculations otherwise */ + if (!vs->lossy) { + vs->non_adaptive = true; + } + +#ifdef CONFIG_VNC_TLS + if (acl && x509 && vs->tls.x509verify) { + char *aclname; + + if (strcmp(vs->id, "default") == 0) { + aclname = g_strdup("vnc.x509dname"); + } else { + aclname = g_strdup_printf("vnc.%s.x509dname", vs->id); + } + vs->tls.acl = qemu_acl_init(aclname); + if (!vs->tls.acl) { + fprintf(stderr, "Failed to create x509 dname ACL\n"); + exit(1); + } + g_free(aclname); + } +#endif +#ifdef CONFIG_VNC_SASL + if (acl && sasl) { + char *aclname; + + if (strcmp(vs->id, "default") == 0) { + aclname = g_strdup("vnc.username"); + } else { + aclname = g_strdup_printf("vnc.%s.username", vs->id); + } + vs->sasl.acl = qemu_acl_init(aclname); + if (!vs->sasl.acl) { + fprintf(stderr, "Failed to create username ACL\n"); + exit(1); + } + g_free(aclname); } +#endif + + vnc_display_setup_auth(vs, password, sasl, tls, x509, websocket); #ifdef CONFIG_VNC_SASL if ((saslErr = sasl_server_init(NULL, "qemu")) != SASL_OK) { @@ -3292,6 +3645,33 @@ void vnc_display_open(DisplayState *ds, const char *display, Error **errp) #endif vs->lock_key_sync = lock_key_sync; + device_id = qemu_opt_get(opts, "display"); + if (device_id) { + DeviceState *dev; + int head = qemu_opt_get_number(opts, "head", 0); + + dev = qdev_find_recursive(sysbus_get_default(), device_id); + if (dev == NULL) { + error_setg(errp, "Device '%s' not found", device_id); + goto fail; + } + + con = qemu_console_lookup_by_device(dev, head); + if (con == NULL) { + error_setg(errp, "Device %s is not bound to a QemuConsole", + device_id); + goto fail; + } + } else { + con = NULL; + } + + if (con != vs->dcl.con) { + unregister_displaychangelistener(&vs->dcl); + vs->dcl.con = con; + register_displaychangelistener(&vs->dcl); + } + if (reverse) { /* connect to viewer */ int csock; @@ -3299,10 +3679,10 @@ void vnc_display_open(DisplayState *ds, const char *display, Error **errp) #ifdef CONFIG_VNC_WS vs->lwebsock = -1; #endif - if (strncmp(display, "unix:", 5) == 0) { - csock = unix_connect(display+5, errp); + if (strncmp(vnc, "unix:", 5) == 0) { + csock = unix_connect(vnc+5, errp); } else { - csock = inet_connect(display, errp); + csock = inet_connect(vnc, errp); } if (csock < 0) { goto fail; @@ -3310,64 +3690,111 @@ void vnc_display_open(DisplayState *ds, const char *display, Error **errp) vnc_connect(vs, csock, false, false); } else { /* listen for connects */ - char *dpy; - dpy = g_malloc(256); - if (strncmp(display, "unix:", 5) == 0) { - pstrcpy(dpy, 256, "unix:"); - vs->lsock = unix_listen(display+5, dpy+5, 256-5, errp); + if (strncmp(vnc, "unix:", 5) == 0) { + vs->lsock = unix_listen(vnc+5, NULL, 0, errp); + vs->is_unix = true; } else { - vs->lsock = inet_listen(display, dpy, 256, - SOCK_STREAM, 5900, errp); + vs->lsock = inet_listen_opts(sopts, 5900, errp); if (vs->lsock < 0) { - g_free(dpy); goto fail; } #ifdef CONFIG_VNC_WS - if (vs->websocket) { - if (vs->ws_display) { - vs->lwebsock = inet_listen(vs->ws_display, NULL, 256, - SOCK_STREAM, 0, errp); - } else { - vs->lwebsock = inet_listen(vs->display, NULL, 256, - SOCK_STREAM, 5700, errp); - } - + if (vs->ws_enabled) { + vs->lwebsock = inet_listen_opts(wsopts, 0, errp); if (vs->lwebsock < 0) { - if (vs->lsock) { + if (vs->lsock != -1) { close(vs->lsock); vs->lsock = -1; } - g_free(dpy); goto fail; } } #endif /* CONFIG_VNC_WS */ } - g_free(vs->display); - vs->display = dpy; + vs->enabled = true; qemu_set_fd_handler2(vs->lsock, NULL, vnc_listen_regular_read, NULL, vs); #ifdef CONFIG_VNC_WS - if (vs->websocket) { + if (vs->ws_enabled) { qemu_set_fd_handler2(vs->lwebsock, NULL, vnc_listen_websocket_read, NULL, vs); } #endif /* CONFIG_VNC_WS */ } + qemu_opts_del(sopts); + qemu_opts_del(wsopts); return; fail: - g_free(vs->display); - vs->display = NULL; + qemu_opts_del(sopts); + qemu_opts_del(wsopts); + vs->enabled = false; #ifdef CONFIG_VNC_WS - g_free(vs->ws_display); - vs->ws_display = NULL; + vs->ws_enabled = false; #endif /* CONFIG_VNC_WS */ } -void vnc_display_add_client(DisplayState *ds, int csock, bool skipauth) +void vnc_display_add_client(const char *id, int csock, bool skipauth) { - VncDisplay *vs = vnc_display; + VncDisplay *vs = vnc_display_find(id); + if (!vs) { + return; + } vnc_connect(vs, csock, skipauth, false); } + +static void vnc_auto_assign_id(QemuOptsList *olist, QemuOpts *opts) +{ + int i = 2; + char *id; + + id = g_strdup("default"); + while (qemu_opts_find(olist, id)) { + g_free(id); + id = g_strdup_printf("vnc%d", i++); + } + qemu_opts_set_id(opts, id); +} + +QemuOpts *vnc_parse_func(const char *str) +{ + QemuOptsList *olist = qemu_find_opts("vnc"); + QemuOpts *opts = qemu_opts_parse(olist, str, 1); + const char *id; + + if (!opts) { + return NULL; + } + + id = qemu_opts_id(opts); + if (!id) { + /* auto-assign id if not present */ + vnc_auto_assign_id(olist, opts); + } + return opts; +} + +int vnc_init_func(QemuOpts *opts, void *opaque) +{ + Error *local_err = NULL; + char *id = (char *)qemu_opts_id(opts); + + assert(id); + vnc_display_init(id); + vnc_display_open(id, &local_err); + if (local_err != NULL) { + error_report("Failed to start VNC server on `%s': %s", + qemu_opt_get(opts, "display"), + error_get_pretty(local_err)); + error_free(local_err); + exit(1); + } + return 0; +} + +static void vnc_register_config(void) +{ + qemu_add_opts(&qemu_vnc_opts); +} +machine_init(vnc_register_config); diff --git a/ui/vnc.h b/ui/vnc.h index 334de9d..3f7c6a9 100644 --- a/ui/vnc.h +++ b/ui/vnc.h @@ -150,13 +150,15 @@ typedef enum VncSharePolicy { struct VncDisplay { QTAILQ_HEAD(, VncState) clients; + int num_connecting; + int num_shared; int num_exclusive; + int connections_limit; VncSharePolicy share_policy; int lsock; #ifdef CONFIG_VNC_WS int lwebsock; - bool websocket; - char *ws_display; + bool ws_enabled; #endif DisplaySurface *ds; DisplayChangeListener dcl; @@ -171,14 +173,19 @@ struct VncDisplay struct VncSurface guest; /* guest visible surface (aka ds->surface) */ pixman_image_t *server; /* vnc server surface */ - char *display; + const char *id; + QTAILQ_ENTRY(VncDisplay) next; + bool enabled; + bool is_unix; char *password; time_t expires; int auth; + int subauth; /* Used by VeNCrypt */ + int ws_auth; /* Used by websockets */ + bool ws_tls; /* Used by websockets */ bool lossy; bool non_adaptive; #ifdef CONFIG_VNC_TLS - int subauth; /* Used by VeNCrypt */ VncDisplayTLS tls; #endif #ifdef CONFIG_VNC_SASL @@ -279,18 +286,15 @@ struct VncState int minor; int auth; + int subauth; /* Used by VeNCrypt */ char challenge[VNC_AUTH_CHALLENGE_SIZE]; #ifdef CONFIG_VNC_TLS - int subauth; /* Used by VeNCrypt */ VncStateTLS tls; #endif #ifdef CONFIG_VNC_SASL VncStateSASL sasl; #endif #ifdef CONFIG_VNC_WS -#ifdef CONFIG_VNC_TLS - VncStateTLS ws_tls; -#endif /* CONFIG_VNC_TLS */ bool encode_ws; bool websocket; #endif /* CONFIG_VNC_WS */ @@ -302,6 +306,8 @@ struct VncState #ifdef CONFIG_VNC_WS Buffer ws_input; Buffer ws_output; + size_t ws_payload_remain; + WsMask ws_payload_mask; #endif /* current output mode information */ VncWritePixels *write_pixels; diff --git a/ui/vnc_keysym.h b/ui/vnc_keysym.h index 1dc039f..7fa2bc1 100644 --- a/ui/vnc_keysym.h +++ b/ui/vnc_keysym.h @@ -404,6 +404,7 @@ static const name2keysym_t name2keysym[]={ {"breve", 0x01a2}, /* U+02D8 BREVE */ {"caron", 0x01b7}, /* U+02C7 CARON */ {"Ccaron", 0x01c8}, /* U+010C LATIN CAPITAL LETTER C WITH CARON */ +{"numerosign", 0x06b0}, /* U+2116 NUMERO SIGN */ {"Cyrillic_a", 0x06c1}, /* U+0430 CYRILLIC SMALL LETTER A */ {"Cyrillic_A", 0x06e1}, /* U+0410 CYRILLIC CAPITAL LETTER A */ {"Cyrillic_be", 0x06c2}, /* U+0431 CYRILLIC SMALL LETTER BE */ diff --git a/ui/x_keymap.c b/ui/x_keymap.c index b9b0944..1a77317 100644 --- a/ui/x_keymap.c +++ b/ui/x_keymap.c @@ -94,7 +94,7 @@ static const uint8_t x_keycode_to_pc_keycode[115] = { */ static const uint8_t evdev_keycode_to_pc_keycode[61] = { - 0, /* 97 EVDEV - RO ("Internet" Keyboards) */ + 0x73, /* 97 EVDEV - RO ("Internet" Keyboards) */ 0, /* 98 EVDEV - KATA (Katakana) */ 0, /* 99 EVDEV - HIRA (Hiragana) */ 0x79, /* 100 EVDEV - HENK (Henkan) */ @@ -126,7 +126,7 @@ static const uint8_t evdev_keycode_to_pc_keycode[61] = { 0, /* 126 EVDEV - I126 ("Internet" Keyboards) */ 0, /* 127 EVDEV - PAUS */ 0, /* 128 EVDEV - ???? */ - 0, /* 129 EVDEV - I129 ("Internet" Keyboards) */ + 0x7e, /* 129 EVDEV - KP_COMMA (brazilian) */ 0xf1, /* 130 EVDEV - HNGL (Korean Hangul Latin toggle) */ 0xf2, /* 131 EVDEV - HJCV (Korean Hangul Hanja toggle) */ 0x7d, /* 132 AE13 (Yen)*/ diff --git a/user-exec.c b/user-exec.c index 1ff8673..8f57e8a 100644 --- a/user-exec.c +++ b/user-exec.c @@ -404,6 +404,10 @@ int cpu_signal_handler(int host_signum, void *pinfo, struct sigcontext *uc = puc; unsigned long pc = uc->sc_pc; void *sigmask = (void *)(long)uc->sc_mask; +#elif defined(__NetBSD__) + ucontext_t *uc = puc; + unsigned long pc = _UC_MACHINE_PC(uc); + void *sigmask = (void *)&uc->uc_sigmask; #endif #endif @@ -441,15 +445,25 @@ int cpu_signal_handler(int host_signum, void *pinfo, #elif defined(__arm__) +#if defined(__NetBSD__) +#include +#endif + int cpu_signal_handler(int host_signum, void *pinfo, void *puc) { siginfo_t *info = pinfo; +#if defined(__NetBSD__) + ucontext_t *uc = puc; +#else struct ucontext *uc = puc; +#endif unsigned long pc; int is_write; -#if defined(__GLIBC__) && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3)) +#if defined(__NetBSD__) + pc = uc->uc_mcontext.__gregs[_REG_R15]; +#elif defined(__GLIBC__) && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3)) pc = uc->uc_mcontext.gregs[R15]; #else pc = uc->uc_mcontext.arm_pc; diff --git a/util/Makefile.objs b/util/Makefile.objs index 93007e2..ceaba30 100644 --- a/util/Makefile.objs +++ b/util/Makefile.objs @@ -17,3 +17,4 @@ util-obj-y += throttle.o util-obj-y += getauxval.o util-obj-y += readline.o util-obj-y += rfifolock.o +util-obj-y += rcu.o diff --git a/util/aes.c b/util/aes.c index 6058f19..3d7c4be 100644 --- a/util/aes.c +++ b/util/aes.c @@ -1161,7 +1161,7 @@ int AES_set_encrypt_key(const unsigned char *userKey, const int bits, rk += 8; } } - return 0; + abort(); } /** diff --git a/util/cutils.c b/util/cutils.c index dbe7412..144b25c 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -483,6 +483,20 @@ int64_t pow2floor(int64_t value) return value; } +/* round up to the nearest power of 2 (0 if overflow) */ +uint64_t pow2ceil(uint64_t value) +{ + uint8_t nlz = clz64(value); + + if (is_power_of_2(value)) { + return value; + } + if (!nlz) { + return 0; + } + return 1ULL << (64 - nlz); +} + /* * Implementation of ULEB128 (http://en.wikipedia.org/wiki/LEB128) * Input is limited to 14-bit numbers @@ -523,16 +537,17 @@ int parse_debug_env(const char *name, int max, int initial) { char *debug_env = getenv(name); char *inv = NULL; - int debug; + long debug; if (!debug_env) { return initial; } + errno = 0; debug = strtol(debug_env, &inv, 10); if (inv == debug_env) { return initial; } - if (debug < 0 || debug > max) { + if (debug < 0 || debug > max || errno != 0) { fprintf(stderr, "warning: %s not in [0, %d]", name, max); return initial; } diff --git a/util/envlist.c b/util/envlist.c index ebc06cf..099a544 100644 --- a/util/envlist.c +++ b/util/envlist.c @@ -94,30 +94,30 @@ envlist_parse(envlist_t *envlist, const char *env, { char *tmpenv, *envvar; char *envsave = NULL; - - assert(callback != NULL); + int ret = 0; + assert(callback != NULL); if ((envlist == NULL) || (env == NULL)) return (EINVAL); - /* - * We need to make temporary copy of the env string - * as strtok_r(3) modifies it while it tokenizes. - */ if ((tmpenv = strdup(env)) == NULL) return (errno); - - envvar = strtok_r(tmpenv, ",", &envsave); - while (envvar != NULL) { - if ((*callback)(envlist, envvar) != 0) { - free(tmpenv); - return (errno); + envsave = tmpenv; + + do { + envvar = strchr(tmpenv, ','); + if (envvar != NULL) { + *envvar = '\0'; + } + if ((*callback)(envlist, tmpenv) != 0) { + ret = errno; + break; } - envvar = strtok_r(NULL, ",", &envsave); - } + tmpenv = envvar + 1; + } while (envvar != NULL); - free(tmpenv); - return (0); + free(envsave); + return ret; } /* diff --git a/util/error.c b/util/error.c index 2ace0d8..14f4351 100644 --- a/util/error.c +++ b/util/error.c @@ -41,7 +41,7 @@ void error_set(Error **errp, ErrorClass err_class, const char *fmt, ...) err->err_class = err_class; if (errp == &error_abort) { - error_report("%s", error_get_pretty(err)); + error_report_err(err); abort(); } @@ -77,7 +77,7 @@ void error_set_errno(Error **errp, int os_errno, ErrorClass err_class, err->err_class = err_class; if (errp == &error_abort) { - error_report("%s", error_get_pretty(err)); + error_report_err(err); abort(); } @@ -122,7 +122,7 @@ void error_set_win32(Error **errp, int win32_err, ErrorClass err_class, err->err_class = err_class; if (errp == &error_abort) { - error_report("%s", error_get_pretty(err)); + error_report_err(err); abort(); } @@ -152,6 +152,12 @@ const char *error_get_pretty(Error *err) return err->msg; } +void error_report_err(Error *err) +{ + error_report("%s", error_get_pretty(err)); + error_free(err); +} + void error_free(Error *err) { if (err) { @@ -163,7 +169,7 @@ void error_free(Error *err) void error_propagate(Error **dst_errp, Error *local_err) { if (local_err && dst_errp == &error_abort) { - error_report("%s", error_get_pretty(local_err)); + error_report_err(local_err); abort(); } else if (dst_errp && !*dst_errp) { *dst_errp = local_err; diff --git a/util/hbitmap.c b/util/hbitmap.c index b3060e6..ab13971 100644 --- a/util/hbitmap.c +++ b/util/hbitmap.c @@ -373,7 +373,7 @@ void hbitmap_free(HBitmap *hb) HBitmap *hbitmap_alloc(uint64_t size, int granularity) { - HBitmap *hb = g_malloc0(sizeof (struct HBitmap)); + HBitmap *hb = g_new0(struct HBitmap, 1); unsigned i; assert(granularity >= 0 && granularity < 64); @@ -384,7 +384,7 @@ HBitmap *hbitmap_alloc(uint64_t size, int granularity) hb->granularity = granularity; for (i = HBITMAP_LEVELS; i-- > 0; ) { size = MAX((size + BITS_PER_LONG - 1) >> BITS_PER_LEVEL, 1); - hb->levels[i] = g_malloc0(size * sizeof(unsigned long)); + hb->levels[i] = g_new0(unsigned long, size); } /* We necessarily have free bits in level 0 due to the definition diff --git a/util/iov.c b/util/iov.c index 24566c8..2fb18e6 100644 --- a/util/iov.c +++ b/util/iov.c @@ -253,7 +253,7 @@ unsigned iov_copy(struct iovec *dst_iov, unsigned int dst_iov_cnt, void qemu_iovec_init(QEMUIOVector *qiov, int alloc_hint) { - qiov->iov = g_malloc(alloc_hint * sizeof(struct iovec)); + qiov->iov = g_new(struct iovec, alloc_hint); qiov->niov = 0; qiov->nalloc = alloc_hint; qiov->size = 0; @@ -277,7 +277,7 @@ void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len) if (qiov->niov == qiov->nalloc) { qiov->nalloc = 2 * qiov->nalloc + 1; - qiov->iov = g_realloc(qiov->iov, qiov->nalloc * sizeof(struct iovec)); + qiov->iov = g_renew(struct iovec, qiov->iov, qiov->nalloc); } qiov->iov[qiov->niov].iov_base = base; qiov->iov[qiov->niov].iov_len = len; diff --git a/util/oslib-posix.c b/util/oslib-posix.c index 16fcec2..37ffd96 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -399,10 +399,10 @@ void os_mem_prealloc(int fd, char *area, size_t memory) } else { int i; size_t hpagesize = fd_getpagesize(fd); + size_t numpages = DIV_ROUND_UP(memory, hpagesize); /* MAP_POPULATE silently ignores failures */ - memory = (memory + hpagesize - 1) & -hpagesize; - for (i = 0; i < (memory / hpagesize); i++) { + for (i = 0; i < numpages; i++) { memset(area + (hpagesize * i), 0, 1); } diff --git a/util/qemu-config.c b/util/qemu-config.c index ba375c0..2d32ce7 100644 --- a/util/qemu-config.c +++ b/util/qemu-config.c @@ -6,6 +6,7 @@ #include "hw/qdev.h" #include "qapi/error.h" #include "qmp-commands.h" +#include "hw/i386/pc.h" static QemuOptsList *vm_config_groups[32]; static QemuOptsList *drive_config_groups[4]; @@ -32,8 +33,7 @@ QemuOptsList *qemu_find_opts(const char *group) ret = find_list(vm_config_groups, group, &local_err); if (local_err) { - error_report("%s", error_get_pretty(local_err)); - error_free(local_err); + error_report_err(local_err); } return ret; @@ -149,6 +149,84 @@ static CommandLineParameterInfoList *get_drive_infolist(void) return head; } +/* restore machine options that are now machine's properties */ +static QemuOptsList machine_opts = { + .merge_lists = true, + .head = QTAILQ_HEAD_INITIALIZER(machine_opts.head), + .desc = { + { + .name = "type", + .type = QEMU_OPT_STRING, + .help = "emulated machine" + },{ + .name = "accel", + .type = QEMU_OPT_STRING, + .help = "accelerator list", + },{ + .name = "kernel_irqchip", + .type = QEMU_OPT_BOOL, + .help = "use KVM in-kernel irqchip", + },{ + .name = "kvm_shadow_mem", + .type = QEMU_OPT_SIZE, + .help = "KVM shadow MMU size", + },{ + .name = "kernel", + .type = QEMU_OPT_STRING, + .help = "Linux kernel image file", + },{ + .name = "initrd", + .type = QEMU_OPT_STRING, + .help = "Linux initial ramdisk file", + },{ + .name = "append", + .type = QEMU_OPT_STRING, + .help = "Linux kernel command line", + },{ + .name = "dtb", + .type = QEMU_OPT_STRING, + .help = "Linux kernel device tree file", + },{ + .name = "dumpdtb", + .type = QEMU_OPT_STRING, + .help = "Dump current dtb to a file and quit", + },{ + .name = "phandle_start", + .type = QEMU_OPT_NUMBER, + .help = "The first phandle ID we may generate dynamically", + },{ + .name = "dt_compatible", + .type = QEMU_OPT_STRING, + .help = "Overrides the \"compatible\" property of the dt root node", + },{ + .name = "dump-guest-core", + .type = QEMU_OPT_BOOL, + .help = "Include guest memory in a core dump", + },{ + .name = "mem-merge", + .type = QEMU_OPT_BOOL, + .help = "enable/disable memory merge support", + },{ + .name = "usb", + .type = QEMU_OPT_BOOL, + .help = "Set on/off to enable/disable usb", + },{ + .name = "firmware", + .type = QEMU_OPT_STRING, + .help = "firmware image", + },{ + .name = "iommu", + .type = QEMU_OPT_BOOL, + .help = "Set on/off to enable/disable Intel IOMMU (VT-d)", + },{ + .name = "suppress-vmdesc", + .type = QEMU_OPT_BOOL, + .help = "Set on to disable self-describing migration", + }, + { /* End of list */ } + } +}; + CommandLineOptionInfoList *qmp_query_command_line_options(bool has_option, const char *option, Error **errp) @@ -163,6 +241,8 @@ CommandLineOptionInfoList *qmp_query_command_line_options(bool has_option, info->option = g_strdup(vm_config_groups[i]->name); if (!strcmp("drive", vm_config_groups[i]->name)) { info->parameters = get_drive_infolist(); + } else if (!strcmp("machine", vm_config_groups[i]->name)) { + info->parameters = query_option_descs(machine_opts.desc); } else { info->parameters = query_option_descs(vm_config_groups[i]->desc); @@ -220,6 +300,7 @@ void qemu_add_opts(QemuOptsList *list) int qemu_set_option(const char *str) { + Error *local_err = NULL; char group[64], id[64], arg[64]; QemuOptsList *list; QemuOpts *opts; @@ -243,7 +324,9 @@ int qemu_set_option(const char *str) return -1; } - if (qemu_opt_set(opts, arg, str+offset+1) == -1) { + qemu_opt_set(opts, arg, str + offset + 1, &local_err); + if (local_err) { + error_report_err(local_err); return -1; } return 0; @@ -314,8 +397,7 @@ int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname) /* group with id */ list = find_list(lists, group, &local_err); if (local_err) { - error_report("%s", error_get_pretty(local_err)); - error_free(local_err); + error_report_err(local_err); goto out; } opts = qemu_opts_create(list, id, 1, NULL); @@ -325,8 +407,7 @@ int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname) /* group without id */ list = find_list(lists, group, &local_err); if (local_err) { - error_report("%s", error_get_pretty(local_err)); - error_free(local_err); + error_report_err(local_err); goto out; } opts = qemu_opts_create(list, NULL, 0, &error_abort); @@ -338,7 +419,9 @@ int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname) error_report("no group defined"); goto out; } - if (qemu_opt_set(opts, arg, value) != 0) { + qemu_opt_set(opts, arg, value, &local_err); + if (local_err) { + error_report_err(local_err); goto out; } continue; diff --git a/util/qemu-option.c b/util/qemu-option.c index 5d10695..fda4e5f 100644 --- a/util/qemu-option.c +++ b/util/qemu-option.c @@ -213,7 +213,7 @@ void parse_option_size(const char *name, const char *value, bool has_help_option(const char *param) { size_t buflen = strlen(param) + 1; - char *buf = g_malloc0(buflen); + char *buf = g_malloc(buflen); const char *p = param; bool result = false; @@ -230,14 +230,14 @@ bool has_help_option(const char *param) } out: - free(buf); + g_free(buf); return result; } bool is_valid_option_list(const char *param) { size_t buflen = strlen(param) + 1; - char *buf = g_malloc0(buflen); + char *buf = g_malloc(buflen); const char *p = param; bool result = true; @@ -255,7 +255,7 @@ bool is_valid_option_list(const char *param) } out: - free(buf); + g_free(buf); return result; } @@ -548,27 +548,14 @@ static void opt_set(QemuOpts *opts, const char *name, const char *value, } } -int qemu_opt_set(QemuOpts *opts, const char *name, const char *value) -{ - Error *local_err = NULL; - - opt_set(opts, name, value, false, &local_err); - if (local_err) { - qerror_report_err(local_err); - error_free(local_err); - return -1; - } - - return 0; -} - -void qemu_opt_set_err(QemuOpts *opts, const char *name, const char *value, - Error **errp) +void qemu_opt_set(QemuOpts *opts, const char *name, const char *value, + Error **errp) { opt_set(opts, name, value, false, errp); } -int qemu_opt_set_bool(QemuOpts *opts, const char *name, bool val) +void qemu_opt_set_bool(QemuOpts *opts, const char *name, bool val, + Error **errp) { QemuOpt *opt; const QemuOptDesc *desc = opts->list->desc; @@ -576,9 +563,9 @@ int qemu_opt_set_bool(QemuOpts *opts, const char *name, bool val) opt = g_malloc0(sizeof(*opt)); opt->desc = find_desc_by_name(desc, name); if (!opt->desc && !opts_accepts_any(opts)) { - qerror_report(QERR_INVALID_PARAMETER, name); + error_set(errp, QERR_INVALID_PARAMETER, name); g_free(opt); - return -1; + return; } opt->name = g_strdup(name); @@ -586,11 +573,10 @@ int qemu_opt_set_bool(QemuOpts *opts, const char *name, bool val) opt->value.boolean = !!val; opt->str = g_strdup(val ? "on" : "off"); QTAILQ_INSERT_TAIL(&opts->head, opt, next); - - return 0; } -int qemu_opt_set_number(QemuOpts *opts, const char *name, int64_t val) +void qemu_opt_set_number(QemuOpts *opts, const char *name, int64_t val, + Error **errp) { QemuOpt *opt; const QemuOptDesc *desc = opts->list->desc; @@ -598,9 +584,9 @@ int qemu_opt_set_number(QemuOpts *opts, const char *name, int64_t val) opt = g_malloc0(sizeof(*opt)); opt->desc = find_desc_by_name(desc, name); if (!opt->desc && !opts_accepts_any(opts)) { - qerror_report(QERR_INVALID_PARAMETER, name); + error_set(errp, QERR_INVALID_PARAMETER, name); g_free(opt); - return -1; + return; } opt->name = g_strdup(name); @@ -608,8 +594,6 @@ int qemu_opt_set_number(QemuOpts *opts, const char *name, int64_t val) opt->value.uint = val; opt->str = g_strdup_printf("%" PRId64, val); QTAILQ_INSERT_TAIL(&opts->head, opt, next); - - return 0; } int qemu_opt_foreach(QemuOpts *opts, qemu_opt_loopfunc func, void *opaque, @@ -692,19 +676,18 @@ void qemu_opts_loc_restore(QemuOpts *opts) loc_restore(&opts->loc); } -int qemu_opts_set(QemuOptsList *list, const char *id, - const char *name, const char *value) +void qemu_opts_set(QemuOptsList *list, const char *id, + const char *name, const char *value, Error **errp) { QemuOpts *opts; Error *local_err = NULL; opts = qemu_opts_create(list, id, 1, &local_err); if (local_err) { - qerror_report_err(local_err); - error_free(local_err); - return -1; + error_propagate(errp, local_err); + return; } - return qemu_opt_set(opts, name, value); + qemu_opt_set(opts, name, value, errp); } const char *qemu_opts_id(QemuOpts *opts) @@ -737,14 +720,14 @@ void qemu_opts_del(QemuOpts *opts) g_free(opts); } -void qemu_opts_print(QemuOpts *opts) +void qemu_opts_print(QemuOpts *opts, const char *sep) { QemuOpt *opt; QemuOptDesc *desc = opts->list->desc; if (desc[0].name == NULL) { QTAILQ_FOREACH(opt, &opts->head, next) { - printf("%s=\"%s\" ", opt->name, opt->str); + printf("%s%s=\"%s\"", sep, opt->name, opt->str); } return; } @@ -757,18 +740,18 @@ void qemu_opts_print(QemuOpts *opts) continue; } if (desc->type == QEMU_OPT_STRING) { - printf("%s='%s' ", desc->name, value); + printf("%s%s='%s'", sep, desc->name, value); } else if ((desc->type == QEMU_OPT_SIZE || desc->type == QEMU_OPT_NUMBER) && opt) { - printf("%s=%" PRId64 " ", desc->name, opt->value.uint); + printf("%s%s=%" PRId64, sep, desc->name, opt->value.uint); } else { - printf("%s=%s ", desc->name, value); + printf("%s%s=%s", sep, desc->name, value); } } } -static int opts_do_parse(QemuOpts *opts, const char *params, - const char *firstname, bool prepend) +static void opts_do_parse(QemuOpts *opts, const char *params, + const char *firstname, bool prepend, Error **errp) { char option[128], value[1024]; const char *p,*pe,*pc; @@ -806,25 +789,30 @@ static int opts_do_parse(QemuOpts *opts, const char *params, /* store and parse */ opt_set(opts, option, value, prepend, &local_err); if (local_err) { - qerror_report_err(local_err); - error_free(local_err); - return -1; + error_propagate(errp, local_err); + return; } } if (*p != ',') { break; } } - return 0; } -int qemu_opts_do_parse(QemuOpts *opts, const char *params, const char *firstname) +/** + * Store options parsed from @params into @opts. + * If @firstname is non-null, the first key=value in @params may omit + * key=, and is treated as if key was @firstname. + * On error, store an error object through @errp if non-null. + */ +void qemu_opts_do_parse(QemuOpts *opts, const char *params, + const char *firstname, Error **errp) { - return opts_do_parse(opts, params, firstname, false); + opts_do_parse(opts, params, firstname, false, errp); } static QemuOpts *opts_parse(QemuOptsList *list, const char *params, - int permit_abbrev, bool defaults) + int permit_abbrev, bool defaults, Error **errp) { const char *firstname; char value[1024], *id = NULL; @@ -853,14 +841,13 @@ static QemuOpts *opts_parse(QemuOptsList *list, const char *params, assert(!defaults || list->merge_lists); opts = qemu_opts_create(list, id, !defaults, &local_err); if (opts == NULL) { - if (local_err) { - qerror_report_err(local_err); - error_free(local_err); - } + error_propagate(errp, local_err); return NULL; } - if (opts_do_parse(opts, params, firstname, defaults) != 0) { + opts_do_parse(opts, params, firstname, defaults, &local_err); + if (local_err) { + error_propagate(errp, local_err); qemu_opts_del(opts); return NULL; } @@ -868,10 +855,25 @@ static QemuOpts *opts_parse(QemuOptsList *list, const char *params, return opts; } +/** + * Create a QemuOpts in @list and with options parsed from @params. + * If @permit_abbrev, the first key=value in @params may omit key=, + * and is treated as if key was @list->implied_opt_name. + * Report errors with qerror_report_err(). + * Return the new QemuOpts on success, null pointer on error. + */ QemuOpts *qemu_opts_parse(QemuOptsList *list, const char *params, int permit_abbrev) { - return opts_parse(list, params, permit_abbrev, false); + Error *err = NULL; + QemuOpts *opts; + + opts = opts_parse(list, params, permit_abbrev, false, &err); + if (!opts) { + qerror_report_err(err); + error_free(err); + } + return opts; } void qemu_opts_set_defaults(QemuOptsList *list, const char *params, @@ -879,7 +881,7 @@ void qemu_opts_set_defaults(QemuOptsList *list, const char *params, { QemuOpts *opts; - opts = opts_parse(list, params, permit_abbrev, true); + opts = opts_parse(list, params, permit_abbrev, true, NULL); assert(opts); } @@ -924,7 +926,7 @@ static void qemu_opts_from_qdict_1(const char *key, QObject *obj, void *opaque) return; } - qemu_opt_set_err(state->opts, key, value, state->errp); + qemu_opt_set(state->opts, key, value, state->errp); } /* diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index a76bb3c..87c9bc6 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -199,11 +199,13 @@ listen: freeaddrinfo(res); return -1; } - snprintf(uport, sizeof(uport), "%d", inet_getport(e) - port_offset); - qemu_opt_set(opts, "host", uaddr); - qemu_opt_set(opts, "port", uport); - qemu_opt_set(opts, "ipv6", (e->ai_family == PF_INET6) ? "on" : "off"); - qemu_opt_set(opts, "ipv4", (e->ai_family != PF_INET6) ? "on" : "off"); + qemu_opt_set(opts, "host", uaddr, &error_abort); + qemu_opt_set_number(opts, "port", inet_getport(e) - port_offset, + &error_abort); + qemu_opt_set_bool(opts, "ipv6", e->ai_family == PF_INET6, + &error_abort); + qemu_opt_set_bool(opts, "ipv4", e->ai_family != PF_INET6, + &error_abort); freeaddrinfo(res); return slisten; } @@ -512,7 +514,7 @@ InetSocketAddress *inet_parse(const char *str, Error **errp) { InetSocketAddress *addr; const char *optstr, *h; - char host[64]; + char host[65]; char port[33]; int to; int pos; @@ -580,16 +582,14 @@ static void inet_addr_to_opts(QemuOpts *opts, const InetSocketAddress *addr) bool ipv6 = addr->ipv6 || !addr->has_ipv6; if (!ipv4 || !ipv6) { - qemu_opt_set_bool(opts, "ipv4", ipv4); - qemu_opt_set_bool(opts, "ipv6", ipv6); + qemu_opt_set_bool(opts, "ipv4", ipv4, &error_abort); + qemu_opt_set_bool(opts, "ipv6", ipv6, &error_abort); } if (addr->has_to) { - char to[20]; - snprintf(to, sizeof(to), "%d", addr->to); - qemu_opt_set(opts, "to", to); + qemu_opt_set_number(opts, "to", addr->to, &error_abort); } - qemu_opt_set(opts, "host", addr->host); - qemu_opt_set(opts, "port", addr->port); + qemu_opt_set(opts, "host", addr->host, &error_abort); + qemu_opt_set(opts, "port", addr->port, &error_abort); } int inet_listen(const char *str, char *ostr, int olen, @@ -694,7 +694,7 @@ int unix_listen_opts(QemuOpts *opts, Error **errp) sock = qemu_socket(PF_UNIX, SOCK_STREAM, 0); if (sock < 0) { - error_setg_errno(errp, errno, "Failed to create socket"); + error_setg_errno(errp, errno, "Failed to create Unix socket"); return -1; } @@ -703,9 +703,15 @@ int unix_listen_opts(QemuOpts *opts, Error **errp) if (path && strlen(path)) { snprintf(un.sun_path, sizeof(un.sun_path), "%s", path); } else { - char *tmpdir = getenv("TMPDIR"); - snprintf(un.sun_path, sizeof(un.sun_path), "%s/qemu-socket-XXXXXX", - tmpdir ? tmpdir : "/tmp"); + const char *tmpdir = getenv("TMPDIR"); + tmpdir = tmpdir ? tmpdir : "/tmp"; + if (snprintf(un.sun_path, sizeof(un.sun_path), "%s/qemu-socket-XXXXXX", + tmpdir) >= sizeof(un.sun_path)) { + error_setg_errno(errp, errno, + "TMPDIR environment variable (%s) too large", tmpdir); + goto err; + } + /* * This dummy fd usage silences the mktemp() unsecure warning. * Using mkstemp() doesn't make things more secure here @@ -713,13 +719,19 @@ int unix_listen_opts(QemuOpts *opts, Error **errp) * to unlink first and thus re-open the race window. The * worst case possible is bind() failing, i.e. a DoS attack. */ - fd = mkstemp(un.sun_path); close(fd); - qemu_opt_set(opts, "path", un.sun_path); + fd = mkstemp(un.sun_path); + if (fd < 0) { + error_setg_errno(errp, errno, + "Failed to make a temporary socket name in %s", tmpdir); + goto err; + } + close(fd); + qemu_opt_set(opts, "path", un.sun_path, &error_abort); } unlink(un.sun_path); if (bind(sock, (struct sockaddr*) &un, sizeof(un)) < 0) { - error_setg_errno(errp, errno, "Failed to bind socket"); + error_setg_errno(errp, errno, "Failed to bind socket to %s", un.sun_path); goto err; } if (listen(sock, 1) < 0) { @@ -826,11 +838,11 @@ int unix_listen(const char *str, char *ostr, int olen, Error **errp) if (len) { path = g_malloc(len+1); snprintf(path, len+1, "%.*s", len, str); - qemu_opt_set(opts, "path", path); + qemu_opt_set(opts, "path", path, &error_abort); g_free(path); } } else { - qemu_opt_set(opts, "path", str); + qemu_opt_set(opts, "path", str, &error_abort); } sock = unix_listen_opts(opts, errp); @@ -847,7 +859,7 @@ int unix_connect(const char *path, Error **errp) int sock; opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort); - qemu_opt_set(opts, "path", path); + qemu_opt_set(opts, "path", path, &error_abort); sock = unix_connect_opts(opts, errp, NULL, NULL); qemu_opts_del(opts); return sock; @@ -864,7 +876,7 @@ int unix_nonblocking_connect(const char *path, g_assert(callback != NULL); opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort); - qemu_opt_set(opts, "path", path); + qemu_opt_set(opts, "path", path, &error_abort); sock = unix_connect_opts(opts, errp, callback, opaque); qemu_opts_del(opts); return sock; @@ -921,7 +933,7 @@ int socket_connect(SocketAddress *addr, Error **errp, break; case SOCKET_ADDRESS_KIND_UNIX: - qemu_opt_set(opts, "path", addr->q_unix->path); + qemu_opt_set(opts, "path", addr->q_unix->path, &error_abort); fd = unix_connect_opts(opts, errp, callback, opaque); break; @@ -953,7 +965,7 @@ int socket_listen(SocketAddress *addr, Error **errp) break; case SOCKET_ADDRESS_KIND_UNIX: - qemu_opt_set(opts, "path", addr->q_unix->path); + qemu_opt_set(opts, "path", addr->q_unix->path, &error_abort); fd = unix_listen_opts(opts, errp); break; @@ -978,8 +990,8 @@ int socket_dgram(SocketAddress *remote, SocketAddress *local, Error **errp) case SOCKET_ADDRESS_KIND_INET: inet_addr_to_opts(opts, remote->inet); if (local) { - qemu_opt_set(opts, "localaddr", local->inet->host); - qemu_opt_set(opts, "localport", local->inet->port); + qemu_opt_set(opts, "localaddr", local->inet->host, &error_abort); + qemu_opt_set(opts, "localport", local->inet->port, &error_abort); } fd = inet_dgram_opts(opts, errp); break; diff --git a/util/qemu-thread-posix.c b/util/qemu-thread-posix.c index d05a649..ba67cec 100644 --- a/util/qemu-thread-posix.c +++ b/util/qemu-thread-posix.c @@ -26,6 +26,7 @@ #endif #include "qemu/thread.h" #include "qemu/atomic.h" +#include "qemu/notify.h" static bool name_threads; @@ -50,12 +51,8 @@ static void error_exit(int err, const char *msg) void qemu_mutex_init(QemuMutex *mutex) { int err; - pthread_mutexattr_t mutexattr; - pthread_mutexattr_init(&mutexattr); - pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_ERRORCHECK); - err = pthread_mutex_init(&mutex->lock, &mutexattr); - pthread_mutexattr_destroy(&mutexattr); + err = pthread_mutex_init(&mutex->lock, NULL); if (err) error_exit(err, __func__); } @@ -306,11 +303,13 @@ static inline void futex_wait(QemuEvent *ev, unsigned val) #else static inline void futex_wake(QemuEvent *ev, int n) { + pthread_mutex_lock(&ev->lock); if (n == 1) { pthread_cond_signal(&ev->cond); } else { pthread_cond_broadcast(&ev->cond); } + pthread_mutex_unlock(&ev->lock); } static inline void futex_wait(QemuEvent *ev, unsigned val) @@ -401,6 +400,42 @@ void qemu_event_wait(QemuEvent *ev) } } +static pthread_key_t exit_key; + +union NotifierThreadData { + void *ptr; + NotifierList list; +}; +QEMU_BUILD_BUG_ON(sizeof(union NotifierThreadData) != sizeof(void *)); + +void qemu_thread_atexit_add(Notifier *notifier) +{ + union NotifierThreadData ntd; + ntd.ptr = pthread_getspecific(exit_key); + notifier_list_add(&ntd.list, notifier); + pthread_setspecific(exit_key, ntd.ptr); +} + +void qemu_thread_atexit_remove(Notifier *notifier) +{ + union NotifierThreadData ntd; + ntd.ptr = pthread_getspecific(exit_key); + notifier_remove(notifier); + pthread_setspecific(exit_key, ntd.ptr); +} + +static void qemu_thread_atexit_run(void *arg) +{ + union NotifierThreadData ntd = { .ptr = arg }; + notifier_list_notify(&ntd.list, NULL); +} + +static void __attribute__((constructor)) qemu_thread_atexit_init(void) +{ + pthread_key_create(&exit_key, qemu_thread_atexit_run); +} + + /* Attempt to set the threads name; note that this is for debug, so * we're not going to fail if we can't set it. */ diff --git a/util/qemu-thread-win32.c b/util/qemu-thread-win32.c index c405c9b..406b52f 100644 --- a/util/qemu-thread-win32.c +++ b/util/qemu-thread-win32.c @@ -12,6 +12,7 @@ */ #include "qemu-common.h" #include "qemu/thread.h" +#include "qemu/notify.h" #include #include #include @@ -268,6 +269,7 @@ struct QemuThreadData { void *(*start_routine)(void *); void *arg; short mode; + NotifierList exit; /* Only used for joinable threads. */ bool exited; @@ -275,18 +277,40 @@ struct QemuThreadData { CRITICAL_SECTION cs; }; +static bool atexit_registered; +static NotifierList main_thread_exit; + static __thread QemuThreadData *qemu_thread_data; +static void run_main_thread_exit(void) +{ + notifier_list_notify(&main_thread_exit, NULL); +} + +void qemu_thread_atexit_add(Notifier *notifier) +{ + if (!qemu_thread_data) { + if (!atexit_registered) { + atexit_registered = true; + atexit(run_main_thread_exit); + } + notifier_list_add(&main_thread_exit, notifier); + } else { + notifier_list_add(&qemu_thread_data->exit, notifier); + } +} + +void qemu_thread_atexit_remove(Notifier *notifier) +{ + notifier_remove(notifier); +} + static unsigned __stdcall win32_start_routine(void *arg) { QemuThreadData *data = (QemuThreadData *) arg; void *(*start_routine)(void *) = data->start_routine; void *thread_arg = data->arg; - if (data->mode == QEMU_THREAD_DETACHED) { - g_free(data); - data = NULL; - } qemu_thread_data = data; qemu_thread_exit(start_routine(thread_arg)); abort(); @@ -296,12 +320,14 @@ void qemu_thread_exit(void *arg) { QemuThreadData *data = qemu_thread_data; - if (data) { - assert(data->mode != QEMU_THREAD_DETACHED); + notifier_list_notify(&data->exit, NULL); + if (data->mode == QEMU_THREAD_JOINABLE) { data->ret = arg; EnterCriticalSection(&data->cs); data->exited = true; LeaveCriticalSection(&data->cs); + } else { + g_free(data); } _endthreadex(0); } @@ -313,9 +339,10 @@ void *qemu_thread_join(QemuThread *thread) HANDLE handle; data = thread->data; - if (!data) { + if (data->mode == QEMU_THREAD_DETACHED) { return NULL; } + /* * Because multiple copies of the QemuThread can exist via * qemu_thread_get_self, we need to store a value that cannot @@ -329,7 +356,6 @@ void *qemu_thread_join(QemuThread *thread) CloseHandle(handle); } ret = data->ret; - assert(data->mode != QEMU_THREAD_DETACHED); DeleteCriticalSection(&data->cs); g_free(data); return ret; @@ -347,6 +373,7 @@ void qemu_thread_create(QemuThread *thread, const char *name, data->arg = arg; data->mode = mode; data->exited = false; + notifier_list_init(&data->exit); if (data->mode != QEMU_THREAD_DETACHED) { InitializeCriticalSection(&data->cs); @@ -358,7 +385,7 @@ void qemu_thread_create(QemuThread *thread, const char *name, error_exit(GetLastError(), __func__); } CloseHandle(hThread); - thread->data = (mode == QEMU_THREAD_DETACHED) ? NULL : data; + thread->data = data; } void qemu_thread_get_self(QemuThread *thread) @@ -373,11 +400,10 @@ HANDLE qemu_thread_get_handle(QemuThread *thread) HANDLE handle; data = thread->data; - if (!data) { + if (data->mode == QEMU_THREAD_DETACHED) { return NULL; } - assert(data->mode != QEMU_THREAD_DETACHED); EnterCriticalSection(&data->cs); if (!data->exited) { handle = OpenThread(SYNCHRONIZE | THREAD_SUSPEND_RESUME, FALSE, diff --git a/util/rcu.c b/util/rcu.c new file mode 100644 index 0000000..7270151 --- /dev/null +++ b/util/rcu.c @@ -0,0 +1,328 @@ +/* + * urcu-mb.c + * + * Userspace RCU library with explicit memory barriers + * + * Copyright (c) 2009 Mathieu Desnoyers + * Copyright (c) 2009 Paul E. McKenney, IBM Corporation. + * Copyright 2015 Red Hat, Inc. + * + * Ported to QEMU by 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.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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * IBM's contributions to this file may be relicensed under LGPLv2 or later. + */ + +#include "qemu-common.h" +#include +#include +#include +#include +#include +#include "qemu/rcu.h" +#include "qemu/atomic.h" +#include "qemu/thread.h" +#include "qemu/main-loop.h" + +/* + * Global grace period counter. Bit 0 is always one in rcu_gp_ctr. + * Bits 1 and above are defined in synchronize_rcu. + */ +#define RCU_GP_LOCKED (1UL << 0) +#define RCU_GP_CTR (1UL << 1) + +unsigned long rcu_gp_ctr = RCU_GP_LOCKED; + +QemuEvent rcu_gp_event; +static QemuMutex rcu_gp_lock; + +/* + * Check whether a quiescent state was crossed between the beginning of + * update_counter_and_wait and now. + */ +static inline int rcu_gp_ongoing(unsigned long *ctr) +{ + unsigned long v; + + v = atomic_read(ctr); + return v && (v != rcu_gp_ctr); +} + +/* Written to only by each individual reader. Read by both the reader and the + * writers. + */ +__thread struct rcu_reader_data rcu_reader; + +/* Protected by rcu_gp_lock. */ +typedef QLIST_HEAD(, rcu_reader_data) ThreadList; +static ThreadList registry = QLIST_HEAD_INITIALIZER(registry); + +/* Wait for previous parity/grace period to be empty of readers. */ +static void wait_for_readers(void) +{ + ThreadList qsreaders = QLIST_HEAD_INITIALIZER(qsreaders); + struct rcu_reader_data *index, *tmp; + + for (;;) { + /* We want to be notified of changes made to rcu_gp_ongoing + * while we walk the list. + */ + qemu_event_reset(&rcu_gp_event); + + /* Instead of using atomic_mb_set for index->waiting, and + * atomic_mb_read for index->ctr, memory barriers are placed + * manually since writes to different threads are independent. + * atomic_mb_set has a smp_wmb before... + */ + smp_wmb(); + QLIST_FOREACH(index, ®istry, node) { + atomic_set(&index->waiting, true); + } + + /* ... and a smp_mb after. */ + smp_mb(); + + QLIST_FOREACH_SAFE(index, ®istry, node, tmp) { + if (!rcu_gp_ongoing(&index->ctr)) { + QLIST_REMOVE(index, node); + QLIST_INSERT_HEAD(&qsreaders, index, node); + + /* No need for mb_set here, worst of all we + * get some extra futex wakeups. + */ + atomic_set(&index->waiting, false); + } + } + + /* atomic_mb_read has smp_rmb after. */ + smp_rmb(); + + if (QLIST_EMPTY(®istry)) { + break; + } + + /* Wait for one thread to report a quiescent state and + * try again. + */ + qemu_event_wait(&rcu_gp_event); + } + + /* put back the reader list in the registry */ + QLIST_SWAP(®istry, &qsreaders, node); +} + +void synchronize_rcu(void) +{ + qemu_mutex_lock(&rcu_gp_lock); + + if (!QLIST_EMPTY(®istry)) { + /* In either case, the atomic_mb_set below blocks stores that free + * old RCU-protected pointers. + */ + if (sizeof(rcu_gp_ctr) < 8) { + /* For architectures with 32-bit longs, a two-subphases algorithm + * ensures we do not encounter overflow bugs. + * + * Switch parity: 0 -> 1, 1 -> 0. + */ + atomic_mb_set(&rcu_gp_ctr, rcu_gp_ctr ^ RCU_GP_CTR); + wait_for_readers(); + atomic_mb_set(&rcu_gp_ctr, rcu_gp_ctr ^ RCU_GP_CTR); + } else { + /* Increment current grace period. */ + atomic_mb_set(&rcu_gp_ctr, rcu_gp_ctr + RCU_GP_CTR); + } + + wait_for_readers(); + } + + qemu_mutex_unlock(&rcu_gp_lock); +} + + +#define RCU_CALL_MIN_SIZE 30 + +/* Multi-producer, single-consumer queue based on urcu/static/wfqueue.h + * from liburcu. Note that head is only used by the consumer. + */ +static struct rcu_head dummy; +static struct rcu_head *head = &dummy, **tail = &dummy.next; +static int rcu_call_count; +static QemuEvent rcu_call_ready_event; + +static void enqueue(struct rcu_head *node) +{ + struct rcu_head **old_tail; + + node->next = NULL; + old_tail = atomic_xchg(&tail, &node->next); + atomic_mb_set(old_tail, node); +} + +static struct rcu_head *try_dequeue(void) +{ + struct rcu_head *node, *next; + +retry: + /* Test for an empty list, which we do not expect. Note that for + * the consumer head and tail are always consistent. The head + * is consistent because only the consumer reads/writes it. + * The tail, because it is the first step in the enqueuing. + * It is only the next pointers that might be inconsistent. + */ + if (head == &dummy && atomic_mb_read(&tail) == &dummy.next) { + abort(); + } + + /* If the head node has NULL in its next pointer, the value is + * wrong and we need to wait until its enqueuer finishes the update. + */ + node = head; + next = atomic_mb_read(&head->next); + if (!next) { + return NULL; + } + + /* Since we are the sole consumer, and we excluded the empty case + * above, the queue will always have at least two nodes: the + * dummy node, and the one being removed. So we do not need to update + * the tail pointer. + */ + head = next; + + /* If we dequeued the dummy node, add it back at the end and retry. */ + if (node == &dummy) { + enqueue(node); + goto retry; + } + + return node; +} + +static void *call_rcu_thread(void *opaque) +{ + struct rcu_head *node; + + for (;;) { + int tries = 0; + int n = atomic_read(&rcu_call_count); + + /* Heuristically wait for a decent number of callbacks to pile up. + * Fetch rcu_call_count now, we only must process elements that were + * added before synchronize_rcu() starts. + */ + while (n == 0 || (n < RCU_CALL_MIN_SIZE && ++tries <= 5)) { + g_usleep(10000); + if (n == 0) { + qemu_event_reset(&rcu_call_ready_event); + n = atomic_read(&rcu_call_count); + if (n == 0) { + qemu_event_wait(&rcu_call_ready_event); + } + } + n = atomic_read(&rcu_call_count); + } + + atomic_sub(&rcu_call_count, n); + synchronize_rcu(); + qemu_mutex_lock_iothread(); + while (n > 0) { + node = try_dequeue(); + while (!node) { + qemu_mutex_unlock_iothread(); + qemu_event_reset(&rcu_call_ready_event); + node = try_dequeue(); + if (!node) { + qemu_event_wait(&rcu_call_ready_event); + node = try_dequeue(); + } + qemu_mutex_lock_iothread(); + } + + n--; + node->func(node); + } + qemu_mutex_unlock_iothread(); + } + abort(); +} + +void call_rcu1(struct rcu_head *node, void (*func)(struct rcu_head *node)) +{ + node->func = func; + enqueue(node); + atomic_inc(&rcu_call_count); + qemu_event_set(&rcu_call_ready_event); +} + +void rcu_register_thread(void) +{ + assert(rcu_reader.ctr == 0); + qemu_mutex_lock(&rcu_gp_lock); + QLIST_INSERT_HEAD(®istry, &rcu_reader, node); + qemu_mutex_unlock(&rcu_gp_lock); +} + +void rcu_unregister_thread(void) +{ + qemu_mutex_lock(&rcu_gp_lock); + QLIST_REMOVE(&rcu_reader, node); + qemu_mutex_unlock(&rcu_gp_lock); +} + +static void rcu_init_complete(void) +{ + QemuThread thread; + + qemu_mutex_init(&rcu_gp_lock); + qemu_event_init(&rcu_gp_event, true); + + qemu_event_init(&rcu_call_ready_event, false); + + /* The caller is assumed to have iothread lock, so the call_rcu thread + * must have been quiescent even after forking, just recreate it. + */ + qemu_thread_create(&thread, "call_rcu", call_rcu_thread, + NULL, QEMU_THREAD_DETACHED); + + rcu_register_thread(); +} + +#ifdef CONFIG_POSIX +static void rcu_init_lock(void) +{ + qemu_mutex_lock(&rcu_gp_lock); +} + +static void rcu_init_unlock(void) +{ + qemu_mutex_unlock(&rcu_gp_lock); +} +#endif + +void rcu_after_fork(void) +{ + memset(®istry, 0, sizeof(registry)); + rcu_init_complete(); +} + +static void __attribute__((__constructor__)) rcu_init(void) +{ +#ifdef CONFIG_POSIX + pthread_atfork(rcu_init_lock, rcu_init_unlock, rcu_init_unlock); +#endif + rcu_init_complete(); +} diff --git a/util/uri.c b/util/uri.c index e348c17..550b984 100644 --- a/util/uri.c +++ b/util/uri.c @@ -225,7 +225,7 @@ rfc3986_parse_scheme(URI *uri, const char **str) { while (ISA_ALPHA(cur) || ISA_DIGIT(cur) || (*cur == '+') || (*cur == '-') || (*cur == '.')) cur++; if (uri != NULL) { - if (uri->scheme != NULL) g_free(uri->scheme); + g_free(uri->scheme); uri->scheme = g_strndup(*str, cur - *str); } *str = cur; @@ -262,8 +262,7 @@ rfc3986_parse_fragment(URI *uri, const char **str) ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur)))) NEXT(cur); if (uri != NULL) { - if (uri->fragment != NULL) - g_free(uri->fragment); + g_free(uri->fragment); if (uri->cleanup & 2) uri->fragment = g_strndup(*str, cur - *str); else @@ -298,8 +297,7 @@ rfc3986_parse_query(URI *uri, const char **str) ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur)))) NEXT(cur); if (uri != NULL) { - if (uri->query != NULL) - g_free (uri->query); + g_free(uri->query); uri->query = g_strndup (*str, cur - *str); } *str = cur; @@ -322,19 +320,23 @@ static int rfc3986_parse_port(URI *uri, const char **str) { const char *cur = *str; + int port = 0; if (ISA_DIGIT(cur)) { - if (uri != NULL) - uri->port = 0; - while (ISA_DIGIT(cur)) { - if (uri != NULL) - uri->port = uri->port * 10 + (*cur - '0'); - cur++; - } - *str = cur; - return(0); + while (ISA_DIGIT(cur)) { + port = port * 10 + (*cur - '0'); + if (port > 65535) { + return 1; + } + cur++; + } + if (uri) { + uri->port = port; + } + *str = cur; + return 0; } - return(1); + return 1; } /** @@ -360,7 +362,7 @@ rfc3986_parse_user_info(URI *uri, const char **str) NEXT(cur); if (*cur == '@') { if (uri != NULL) { - if (uri->user != NULL) g_free(uri->user); + g_free(uri->user); if (uri->cleanup & 2) uri->user = g_strndup(*str, cur - *str); else @@ -473,9 +475,9 @@ not_ipv4: NEXT(cur); found: if (uri != NULL) { - if (uri->authority != NULL) g_free(uri->authority); + g_free(uri->authority); uri->authority = NULL; - if (uri->server != NULL) g_free(uri->server); + g_free(uri->server); if (cur != host) { if (uri->cleanup & 2) uri->server = g_strndup(host, cur - host); @@ -585,7 +587,7 @@ rfc3986_parse_path_ab_empty(URI *uri, const char **str) if (ret != 0) return(ret); } if (uri != NULL) { - if (uri->path != NULL) g_free(uri->path); + g_free(uri->path); if (*str != cur) { if (uri->cleanup & 2) uri->path = g_strndup(*str, cur - *str); @@ -631,7 +633,7 @@ rfc3986_parse_path_absolute(URI *uri, const char **str) } } if (uri != NULL) { - if (uri->path != NULL) g_free(uri->path); + g_free(uri->path); if (cur != *str) { if (uri->cleanup & 2) uri->path = g_strndup(*str, cur - *str); @@ -673,7 +675,7 @@ rfc3986_parse_path_rootless(URI *uri, const char **str) if (ret != 0) return(ret); } if (uri != NULL) { - if (uri->path != NULL) g_free(uri->path); + g_free(uri->path); if (cur != *str) { if (uri->cleanup & 2) uri->path = g_strndup(*str, cur - *str); @@ -715,7 +717,7 @@ rfc3986_parse_path_no_scheme(URI *uri, const char **str) if (ret != 0) return(ret); } if (uri != NULL) { - if (uri->path != NULL) g_free(uri->path); + g_free(uri->path); if (cur != *str) { if (uri->cleanup & 2) uri->path = g_strndup(*str, cur - *str); @@ -769,7 +771,7 @@ rfc3986_parse_hier_part(URI *uri, const char **str) } else { /* path-empty is effectively empty */ if (uri != NULL) { - if (uri->path != NULL) g_free(uri->path); + g_free(uri->path); uri->path = NULL; } } @@ -812,7 +814,7 @@ rfc3986_parse_relative_ref(URI *uri, const char *str) { } else { /* path-empty is effectively empty */ if (uri != NULL) { - if (uri->path != NULL) g_free(uri->path); + g_free(uri->path); uri->path = NULL; } } @@ -930,12 +932,10 @@ uri_parse(const char *str) { if (str == NULL) return(NULL); uri = uri_new(); - if (uri != NULL) { - ret = rfc3986_parse_uri_reference(uri, str); - if (ret) { - uri_free(uri); - return(NULL); - } + ret = rfc3986_parse_uri_reference(uri, str); + if (ret) { + uri_free(uri); + return(NULL); } return(uri); } @@ -976,15 +976,13 @@ uri_parse_raw(const char *str, int raw) { if (str == NULL) return(NULL); uri = uri_new(); - if (uri != NULL) { - if (raw) { - uri->cleanup |= 2; - } - ret = uri_parse_into(uri, str); - if (ret) { - uri_free(uri); - return(NULL); - } + if (raw) { + uri->cleanup |= 2; + } + ret = uri_parse_into(uri, str); + if (ret) { + uri_free(uri); + return(NULL); } return(uri); } @@ -1006,8 +1004,7 @@ URI * uri_new(void) { URI *ret; - ret = (URI *) g_malloc(sizeof(URI)); - memset(ret, 0, sizeof(URI)); + ret = g_new0(URI, 1); return(ret); } @@ -1056,14 +1053,12 @@ uri_to_string(URI *uri) { while (*p != 0) { if (len >= max) { temp = realloc2n(ret, &max); - if (temp == NULL) goto mem_error; ret = temp; } ret[len++] = *p++; } if (len >= max) { temp = realloc2n(ret, &max); - if (temp == NULL) goto mem_error; ret = temp; } ret[len++] = ':'; @@ -1073,7 +1068,6 @@ uri_to_string(URI *uri) { while (*p != 0) { if (len + 3 >= max) { temp = realloc2n(ret, &max); - if (temp == NULL) goto mem_error; ret = temp; } if (IS_RESERVED(*(p)) || IS_UNRESERVED(*(p))) @@ -1090,7 +1084,6 @@ uri_to_string(URI *uri) { if (uri->server != NULL) { if (len + 3 >= max) { temp = realloc2n(ret, &max); - if (temp == NULL) goto mem_error; ret = temp; } ret[len++] = '/'; @@ -1100,7 +1093,6 @@ uri_to_string(URI *uri) { while (*p != 0) { if (len + 3 >= max) { temp = realloc2n(ret, &max); - if (temp == NULL) goto mem_error; ret = temp; } if ((IS_UNRESERVED(*(p))) || @@ -1119,7 +1111,6 @@ uri_to_string(URI *uri) { } if (len + 3 >= max) { temp = realloc2n(ret, &max); - if (temp == NULL) goto mem_error; ret = temp; } ret[len++] = '@'; @@ -1128,7 +1119,6 @@ uri_to_string(URI *uri) { while (*p != 0) { if (len >= max) { temp = realloc2n(ret, &max); - if (temp == NULL) goto mem_error; ret = temp; } ret[len++] = *p++; @@ -1136,7 +1126,6 @@ uri_to_string(URI *uri) { if (uri->port > 0) { if (len + 10 >= max) { temp = realloc2n(ret, &max); - if (temp == NULL) goto mem_error; ret = temp; } len += snprintf(&ret[len], max - len, ":%d", uri->port); @@ -1144,7 +1133,6 @@ uri_to_string(URI *uri) { } else if (uri->authority != NULL) { if (len + 3 >= max) { temp = realloc2n(ret, &max); - if (temp == NULL) goto mem_error; ret = temp; } ret[len++] = '/'; @@ -1153,7 +1141,6 @@ uri_to_string(URI *uri) { while (*p != 0) { if (len + 3 >= max) { temp = realloc2n(ret, &max); - if (temp == NULL) goto mem_error; ret = temp; } if ((IS_UNRESERVED(*(p))) || @@ -1172,7 +1159,6 @@ uri_to_string(URI *uri) { } else if (uri->scheme != NULL) { if (len + 3 >= max) { temp = realloc2n(ret, &max); - if (temp == NULL) goto mem_error; ret = temp; } ret[len++] = '/'; @@ -1192,7 +1178,6 @@ uri_to_string(URI *uri) { (!strcmp(uri->scheme, "file"))) { if (len + 3 >= max) { temp = realloc2n(ret, &max); - if (temp == NULL) goto mem_error; ret = temp; } ret[len++] = *p++; @@ -1202,7 +1187,6 @@ uri_to_string(URI *uri) { while (*p != 0) { if (len + 3 >= max) { temp = realloc2n(ret, &max); - if (temp == NULL) goto mem_error; ret = temp; } if ((IS_UNRESERVED(*(p))) || ((*(p) == '/')) || @@ -1222,7 +1206,6 @@ uri_to_string(URI *uri) { if (uri->query != NULL) { if (len + 1 >= max) { temp = realloc2n(ret, &max); - if (temp == NULL) goto mem_error; ret = temp; } ret[len++] = '?'; @@ -1230,7 +1213,6 @@ uri_to_string(URI *uri) { while (*p != 0) { if (len + 1 >= max) { temp = realloc2n(ret, &max); - if (temp == NULL) goto mem_error; ret = temp; } ret[len++] = *p++; @@ -1240,7 +1222,6 @@ uri_to_string(URI *uri) { if (uri->fragment != NULL) { if (len + 3 >= max) { temp = realloc2n(ret, &max); - if (temp == NULL) goto mem_error; ret = temp; } ret[len++] = '#'; @@ -1248,7 +1229,6 @@ uri_to_string(URI *uri) { while (*p != 0) { if (len + 3 >= max) { temp = realloc2n(ret, &max); - if (temp == NULL) goto mem_error; ret = temp; } if ((IS_UNRESERVED(*(p))) || (IS_RESERVED(*(p)))) @@ -1264,15 +1244,10 @@ uri_to_string(URI *uri) { } if (len >= max) { temp = realloc2n(ret, &max); - if (temp == NULL) goto mem_error; ret = temp; } ret[len] = 0; return(ret); - -mem_error: - g_free(ret); - return(NULL); } /** @@ -1285,21 +1260,21 @@ static void uri_clean(URI *uri) { if (uri == NULL) return; - if (uri->scheme != NULL) g_free(uri->scheme); + g_free(uri->scheme); uri->scheme = NULL; - if (uri->server != NULL) g_free(uri->server); + g_free(uri->server); uri->server = NULL; - if (uri->user != NULL) g_free(uri->user); + g_free(uri->user); uri->user = NULL; - if (uri->path != NULL) g_free(uri->path); + g_free(uri->path); uri->path = NULL; - if (uri->fragment != NULL) g_free(uri->fragment); + g_free(uri->fragment); uri->fragment = NULL; - if (uri->opaque != NULL) g_free(uri->opaque); + g_free(uri->opaque); uri->opaque = NULL; - if (uri->authority != NULL) g_free(uri->authority); + g_free(uri->authority); uri->authority = NULL; - if (uri->query != NULL) g_free(uri->query); + g_free(uri->query); uri->query = NULL; } @@ -1678,8 +1653,6 @@ uri_resolve(const char *uri, const char *base) { else { if (*uri) { ref = uri_new(); - if (ref == NULL) - goto done; ret = uri_parse_into(ref, uri); } else @@ -1698,8 +1671,6 @@ uri_resolve(const char *uri, const char *base) { ret = -1; else { bas = uri_new(); - if (bas == NULL) - goto done; ret = uri_parse_into(bas, base); } if (ret != 0) { @@ -1711,10 +1682,8 @@ uri_resolve(const char *uri, const char *base) { /* * the base fragment must be ignored */ - if (bas->fragment != NULL) { - g_free(bas->fragment); - bas->fragment = NULL; - } + g_free(bas->fragment); + bas->fragment = NULL; val = uri_to_string(bas); goto done; } @@ -1732,28 +1701,23 @@ uri_resolve(const char *uri, const char *base) { * document. */ res = uri_new(); - if (res == NULL) - goto done; if ((ref->scheme == NULL) && (ref->path == NULL) && ((ref->authority == NULL) && (ref->server == NULL))) { - if (bas->scheme != NULL) - res->scheme = g_strdup(bas->scheme); + res->scheme = g_strdup(bas->scheme); if (bas->authority != NULL) res->authority = g_strdup(bas->authority); else if (bas->server != NULL) { - res->server = g_strdup(bas->server); - if (bas->user != NULL) - res->user = g_strdup(bas->user); - res->port = bas->port; + res->server = g_strdup(bas->server); + res->user = g_strdup(bas->user); + res->port = bas->port; } - if (bas->path != NULL) - res->path = g_strdup(bas->path); - if (ref->query != NULL) + res->path = g_strdup(bas->path); + if (ref->query != NULL) { res->query = g_strdup (ref->query); - else if (bas->query != NULL) - res->query = g_strdup(bas->query); - if (ref->fragment != NULL) - res->fragment = g_strdup(ref->fragment); + } else { + res->query = g_strdup(bas->query); + } + res->fragment = g_strdup(ref->fragment); goto step_7; } @@ -1767,13 +1731,10 @@ uri_resolve(const char *uri, const char *base) { val = uri_to_string(ref); goto done; } - if (bas->scheme != NULL) - res->scheme = g_strdup(bas->scheme); + res->scheme = g_strdup(bas->scheme); - if (ref->query != NULL) - res->query = g_strdup(ref->query); - if (ref->fragment != NULL) - res->fragment = g_strdup(ref->fragment); + res->query = g_strdup(ref->query); + res->fragment = g_strdup(ref->fragment); /* * 4) If the authority component is defined, then the reference is a @@ -1787,20 +1748,17 @@ uri_resolve(const char *uri, const char *base) { res->authority = g_strdup(ref->authority); else { res->server = g_strdup(ref->server); - if (ref->user != NULL) - res->user = g_strdup(ref->user); + res->user = g_strdup(ref->user); res->port = ref->port; } - if (ref->path != NULL) - res->path = g_strdup(ref->path); + res->path = g_strdup(ref->path); goto step_7; } if (bas->authority != NULL) res->authority = g_strdup(bas->authority); else if (bas->server != NULL) { - res->server = g_strdup(bas->server); - if (bas->user != NULL) - res->user = g_strdup(bas->user); + res->server = g_strdup(bas->server); + res->user = g_strdup(bas->user); res->port = bas->port; } @@ -1947,8 +1905,6 @@ uri_resolve_relative (const char *uri, const char * base) * First parse URI into a standard form */ ref = uri_new (); - if (ref == NULL) - return NULL; /* If URI not already in "relative" form */ if (uri[0] != '.') { ret = uri_parse_into (ref, uri); @@ -1965,8 +1921,6 @@ uri_resolve_relative (const char *uri, const char * base) goto done; } bas = uri_new (); - if (bas == NULL) - goto done; if (base[0] != '.') { ret = uri_parse_into (bas, base); if (ret != 0) @@ -1985,7 +1939,8 @@ uri_resolve_relative (const char *uri, const char * base) val = g_strdup (uri); goto done; } - if (!strcmp(bas->path, ref->path)) { + if (bas->path == ref->path || + (bas->path && ref->path && !strcmp(bas->path, ref->path))) { val = g_strdup(""); goto done; } diff --git a/vl.c b/vl.c index eb89d62..74c2681 100644 --- a/vl.c +++ b/vl.c @@ -78,6 +78,7 @@ int main(int argc, char **argv) #include "monitor/monitor.h" #include "ui/console.h" #include "sysemu/sysemu.h" +#include "sysemu/numa.h" #include "exec/gdbstub.h" #include "qemu/timer.h" #include "sysemu/char.h" @@ -129,6 +130,7 @@ static int data_dir_idx; const char *bios_name = NULL; enum vga_retrace_method vga_retrace_method = VGA_RETRACE_DUMB; DisplayType display_type = DT_DEFAULT; +int display_opengl; static int display_remote; const char* keyboard_layout = NULL; ram_addr_t ram_size; @@ -158,9 +160,6 @@ int smp_cpus = 1; int max_cpus = 0; int smp_cores = 1; int smp_threads = 1; -#ifdef CONFIG_VNC -const char *vnc_display; -#endif int acpi_enabled = 1; int no_hpet = 0; int fd_bootchk = 1; @@ -186,19 +185,12 @@ uint8_t qemu_extra_params_fw[2]; int icount_align_option; -int nb_numa_nodes; -int max_numa_nodeid; -NodeInfo numa_info[MAX_NODES]; - /* The bytes in qemu_uuid[] are in the order specified by RFC4122, _not_ in the * little-endian "wire format" described in the SMBIOS 2.6 specification. */ uint8_t qemu_uuid[16]; bool qemu_uuid_set; -static QEMUBootSetHandler *boot_set_handler; -static void *boot_set_opaque; - static NotifierList exit_notifiers = NOTIFIER_LIST_INITIALIZER(exit_notifiers); @@ -311,84 +303,12 @@ static QemuOptsList qemu_machine_opts = { .merge_lists = true, .head = QTAILQ_HEAD_INITIALIZER(qemu_machine_opts.head), .desc = { - { - .name = "type", - .type = QEMU_OPT_STRING, - .help = "emulated machine" - }, { - .name = "accel", - .type = QEMU_OPT_STRING, - .help = "accelerator list", - }, { - .name = "kernel_irqchip", - .type = QEMU_OPT_BOOL, - .help = "use KVM in-kernel irqchip", - }, { - .name = "kvm_shadow_mem", - .type = QEMU_OPT_SIZE, - .help = "KVM shadow MMU size", - }, { - .name = "kernel", - .type = QEMU_OPT_STRING, - .help = "Linux kernel image file", - }, { - .name = "initrd", - .type = QEMU_OPT_STRING, - .help = "Linux initial ramdisk file", - }, { - .name = "append", - .type = QEMU_OPT_STRING, - .help = "Linux kernel command line", - }, { - .name = "dtb", - .type = QEMU_OPT_STRING, - .help = "Linux kernel device tree file", - }, { - .name = "dumpdtb", - .type = QEMU_OPT_STRING, - .help = "Dump current dtb to a file and quit", - }, { - .name = "phandle_start", - .type = QEMU_OPT_NUMBER, - .help = "The first phandle ID we may generate dynamically", - }, { - .name = "dt_compatible", - .type = QEMU_OPT_STRING, - .help = "Overrides the \"compatible\" property of the dt root node", - }, { - .name = "dump-guest-core", - .type = QEMU_OPT_BOOL, - .help = "Include guest memory in a core dump", - }, { - .name = "mem-merge", - .type = QEMU_OPT_BOOL, - .help = "enable/disable memory merge support", - },{ - .name = "usb", - .type = QEMU_OPT_BOOL, - .help = "Set on/off to enable/disable usb", - },{ - .name = "firmware", - .type = QEMU_OPT_STRING, - .help = "firmware image", - },{ - .name = "kvm-type", - .type = QEMU_OPT_STRING, - .help = "Specifies the KVM virtualization mode (HV, PR)", - },{ - .name = PC_MACHINE_MAX_RAM_BELOW_4G, - .type = QEMU_OPT_SIZE, - .help = "maximum ram below the 4G boundary (32bit boundary)", - }, { - .name = PC_MACHINE_VMPORT, - .type = QEMU_OPT_STRING, - .help = "Enable vmport (pc & q35)", - },{ - .name = "iommu", - .type = QEMU_OPT_BOOL, - .help = "Set on/off to enable/disable Intel IOMMU (VT-d)", - }, - { /* End of list */ } + /* + * no elements => accept any + * sanity checking will happen later + * when setting machine properties + */ + { } }, }; @@ -554,6 +474,22 @@ static QemuOptsList qemu_icount_opts = { }, }; +static QemuOptsList qemu_semihosting_config_opts = { + .name = "semihosting-config", + .implied_opt_name = "enable", + .head = QTAILQ_HEAD_INITIALIZER(qemu_semihosting_config_opts.head), + .desc = { + { + .name = "enable", + .type = QEMU_OPT_BOOL, + }, { + .name = "target", + .type = QEMU_OPT_STRING, + }, + { /* end of list */ } + }, +}; + /** * Get machine options * @@ -772,13 +708,17 @@ void vm_start(void) /***********************************************************/ /* real time host monotonic timer */ +static time_t qemu_time(void) +{ + return qemu_clock_get_ms(QEMU_CLOCK_HOST) / 1000; +} + /***********************************************************/ /* host time/date access */ void qemu_get_timedate(struct tm *tm, int offset) { - time_t ti; + time_t ti = qemu_time(); - time(&ti); ti += offset; if (rtc_date_offset == -1) { if (rtc_utc) @@ -806,7 +746,7 @@ int qemu_timedate_diff(struct tm *tm) else seconds = mktimegm(tm) + rtc_date_offset; - return seconds - time(NULL); + return seconds - qemu_time(); } static void configure_rtc_date_offset(const char *startdate, int legacy) @@ -844,7 +784,7 @@ static void configure_rtc_date_offset(const char *startdate, int legacy) "'2006-06-17T16:01:21' or '2006-06-17'\n"); exit(1); } - rtc_date_offset = time(NULL) - rtc_start_date; + rtc_date_offset = qemu_time() - rtc_start_date; } } @@ -1056,10 +996,14 @@ static int parse_name(QemuOpts *opts, void *opaque) return 0; } -bool usb_enabled(bool default_usb) +bool defaults_enabled(void) +{ + return has_defaults; +} + +bool usb_enabled(void) { - return qemu_opt_get_bool(qemu_get_machine_opts(), "usb", - has_defaults && default_usb); + return machine_usb(current_machine); } #ifndef _WIN32 @@ -1068,6 +1012,7 @@ static int parse_add_fd(QemuOpts *opts, void *opaque) int fd, dupfd, flags; int64_t fdset_id; const char *fd_opaque = NULL; + AddfdInfo *fdinfo; fd = qemu_opt_get_number(opts, "fd", -1); fdset_id = qemu_opt_get_number(opts, "set", -1); @@ -1117,8 +1062,9 @@ static int parse_add_fd(QemuOpts *opts, void *opaque) } /* add the duplicate fd, and optionally the opaque string, to the fd set */ - monitor_fdset_add_fd(dupfd, true, fdset_id, fd_opaque ? true : false, - fd_opaque, NULL); + fdinfo = monitor_fdset_add_fd(dupfd, true, fdset_id, !!fd_opaque, fd_opaque, + &error_abort); + g_free(fdinfo); return 0; } @@ -1154,7 +1100,7 @@ static int drive_init_func(QemuOpts *opts, void *opaque) static int drive_enable_snapshot(QemuOpts *opts, void *opaque) { if (qemu_opt_get(opts, "snapshot") == NULL) { - qemu_opt_set(opts, "snapshot", "on"); + qemu_opt_set(opts, "snapshot", "on", &error_abort); } return 0; } @@ -1182,65 +1128,6 @@ static void default_drive(int enable, int snapshot, BlockInterfaceType type, } -void qemu_register_boot_set(QEMUBootSetHandler *func, void *opaque) -{ - boot_set_handler = func; - boot_set_opaque = opaque; -} - -int qemu_boot_set(const char *boot_order) -{ - if (!boot_set_handler) { - return -EINVAL; - } - return boot_set_handler(boot_set_opaque, boot_order); -} - -static void validate_bootdevices(const char *devices) -{ - /* We just do some generic consistency checks */ - const char *p; - int bitmap = 0; - - for (p = devices; *p != '\0'; p++) { - /* Allowed boot devices are: - * a-b: floppy disk drives - * c-f: IDE disk drives - * g-m: machine implementation dependent drives - * n-p: network devices - * It's up to each machine implementation to check if the given boot - * devices match the actual hardware implementation and firmware - * features. - */ - if (*p < 'a' || *p > 'p') { - fprintf(stderr, "Invalid boot device '%c'\n", *p); - exit(1); - } - if (bitmap & (1 << (*p - 'a'))) { - fprintf(stderr, "Boot device '%c' was given twice\n", *p); - exit(1); - } - bitmap |= 1 << (*p - 'a'); - } -} - -static void restore_boot_order(void *opaque) -{ - char *normal_boot_order = opaque; - static int first = 1; - - /* Restore boot order and remove ourselves after the first boot */ - if (first) { - first = 0; - return; - } - - qemu_boot_set(normal_boot_order); - - qemu_unregister_reset(restore_boot_order, normal_boot_order); - g_free(normal_boot_order); -} - static QemuOptsList qemu_smp_opts = { .name = "smp-opts", .implied_opt_name = "cpus", @@ -1284,13 +1171,17 @@ static void smp_parse(QemuOpts *opts) if (cpus == 0) { cpus = cores * threads * sockets; } - } else { - if (cores == 0) { - threads = threads > 0 ? threads : 1; - cores = cpus / (sockets * threads); - } else { - threads = cpus / (cores * sockets); - } + } else if (cores == 0) { + threads = threads > 0 ? threads : 1; + cores = cpus / (sockets * threads); + } else if (threads == 0) { + threads = cpus / (cores * sockets); + } else if (sockets * cores * threads < cpus) { + fprintf(stderr, "cpu topology: error: " + "sockets (%u) * cores (%u) * threads (%u) < " + "smp_cpus (%u)\n", + sockets, cores, threads, cpus); + exit(1); } max_cpus = qemu_opt_get_number(opts, "maxcpus", 0); @@ -1342,7 +1233,7 @@ static int usb_device_add(const char *devname) const char *p; #endif - if (!usb_enabled(false)) { + if (!usb_enabled()) { return -1; } @@ -1374,7 +1265,7 @@ static int usb_device_del(const char *devname) return -1; } - if (!usb_enabled(false)) { + if (!usb_enabled()) { return -1; } @@ -1397,7 +1288,7 @@ static int usb_parse(const char *cmdline) return r; } -void do_usb_add(Monitor *mon, const QDict *qdict) +void hmp_usb_add(Monitor *mon, const QDict *qdict) { const char *devname = qdict_get_str(qdict, "devname"); if (usb_device_add(devname) < 0) { @@ -1405,7 +1296,7 @@ void do_usb_add(Monitor *mon, const QDict *qdict) } } -void do_usb_del(Monitor *mon, const QDict *qdict) +void hmp_usb_del(Monitor *mon, const QDict *qdict) { const char *devname = qdict_get_str(qdict, "devname"); if (usb_device_del(devname) < 0) { @@ -1541,6 +1432,31 @@ MachineInfoList *qmp_query_machines(Error **errp) return mach_list; } +static int machine_help_func(QemuOpts *opts, MachineState *machine) +{ + ObjectProperty *prop; + + if (!qemu_opt_has_help_opt(opts)) { + return 0; + } + + QTAILQ_FOREACH(prop, &OBJECT(machine)->properties, node) { + if (!prop->set) { + continue; + } + + error_printf("%s.%s=%s", MACHINE_GET_CLASS(machine)->name, + prop->name, prop->type); + if (prop->description) { + error_printf(" (%s)\n", prop->description); + } else { + error_printf("\n"); + } + } + + return 1; +} + /***********************************************************/ /* main execution loop */ @@ -2087,16 +2003,11 @@ static DisplayType select_display(const char *p) #endif } else if (strstart(p, "vnc", &opts)) { #ifdef CONFIG_VNC - display_remote++; - - if (*opts) { - const char *nextopt; - - if (strstart(opts, "=", &nextopt)) { - vnc_display = nextopt; + if (*opts == '=') { + if (vnc_parse_func(opts+1) == NULL) { + exit(1); } - } - if (!vnc_display) { + } else { fprintf(stderr, "VNC requires a display argument vnc=\n"); exit(1); } @@ -2166,7 +2077,7 @@ static int balloon_parse(const char *arg) opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, &error_abort); } - qemu_opt_set(opts, "driver", "virtio-balloon"); + qemu_opt_set(opts, "driver", "virtio-balloon", &error_abort); return 0; } @@ -2229,8 +2140,7 @@ static int chardev_init_func(QemuOpts *opts, void *opaque) qemu_chr_new_from_opts(opts, NULL, &local_err); if (local_err) { - error_report("%s", error_get_pretty(local_err)); - error_free(local_err); + error_report_err(local_err); return -1; } return 0; @@ -2284,9 +2194,10 @@ static int mon_init_func(QemuOpts *opts, void *opaque) return 0; } -static void monitor_parse(const char *optarg, const char *mode) +static void monitor_parse(const char *optarg, const char *mode, bool pretty) { static int monitor_device_index = 0; + Error *local_err = NULL; QemuOpts *opts; const char *p; char label[32]; @@ -2307,15 +2218,16 @@ static void monitor_parse(const char *optarg, const char *mode) } } - opts = qemu_opts_create(qemu_find_opts("mon"), label, 1, NULL); + opts = qemu_opts_create(qemu_find_opts("mon"), label, 1, &local_err); if (!opts) { - fprintf(stderr, "duplicate chardev: %s\n", label); + error_report_err(local_err); exit(1); } - qemu_opt_set(opts, "mode", mode); - qemu_opt_set(opts, "chardev", label); + qemu_opt_set(opts, "mode", mode, &error_abort); + qemu_opt_set(opts, "chardev", label, &error_abort); + qemu_opt_set_bool(opts, "pretty", pretty, &error_abort); if (def) - qemu_opt_set(opts, "default", "on"); + qemu_opt_set(opts, "default", "on", &error_abort); monitor_device_index++; } @@ -2427,13 +2339,13 @@ static int virtcon_parse(const char *devname) bus_opts = qemu_opts_create(device, NULL, 0, &error_abort); if (arch_type == QEMU_ARCH_S390X) { - qemu_opt_set(bus_opts, "driver", "virtio-serial-s390"); + qemu_opt_set(bus_opts, "driver", "virtio-serial-s390", &error_abort); } else { - qemu_opt_set(bus_opts, "driver", "virtio-serial-pci"); + qemu_opt_set(bus_opts, "driver", "virtio-serial-pci", &error_abort); } dev_opts = qemu_opts_create(device, NULL, 0, &error_abort); - qemu_opt_set(dev_opts, "driver", "virtconsole"); + qemu_opt_set(dev_opts, "driver", "virtconsole", &error_abort); snprintf(label, sizeof(label), "virtcon%d", index); virtcon_hds[index] = qemu_chr_new(label, devname, NULL); @@ -2442,7 +2354,7 @@ static int virtcon_parse(const char *devname) " to character backend '%s'\n", devname); return -1; } - qemu_opt_set(dev_opts, "chardev", label); + qemu_opt_set(dev_opts, "chardev", label, &error_abort); index++; return 0; @@ -2466,7 +2378,7 @@ static int sclp_parse(const char *devname) assert(arch_type == QEMU_ARCH_S390X); dev_opts = qemu_opts_create(device, NULL, 0, NULL); - qemu_opt_set(dev_opts, "driver", "sclpconsole"); + qemu_opt_set(dev_opts, "driver", "sclpconsole", &error_abort); snprintf(label, sizeof(label), "sclpcon%d", index); sclp_hds[index] = qemu_chr_new(label, devname, NULL); @@ -2475,7 +2387,7 @@ static int sclp_parse(const char *devname) " to character backend '%s'\n", devname); return -1; } - qemu_opt_set(dev_opts, "chardev", label); + qemu_opt_set(dev_opts, "chardev", label, &error_abort); index++; return 0; @@ -2493,8 +2405,8 @@ static int debugcon_parse(const char *devname) fprintf(stderr, "qemu: already have a debugcon device\n"); exit(1); } - qemu_opt_set(opts, "driver", "isa-debugcon"); - qemu_opt_set(opts, "chardev", "debugcon"); + qemu_opt_set(opts, "driver", "isa-debugcon", &error_abort); + qemu_opt_set(opts, "chardev", "debugcon", &error_abort); return 0; } @@ -2541,6 +2453,7 @@ static gint machine_class_cmp(gconstpointer a, gconstpointer b) mc = find_machine(name); } if (mc) { + g_slist_free(machines); return mc; } if (name && !is_help_option(name)) { @@ -2652,7 +2565,6 @@ static int machine_set_property(const char *name, const char *value, void *opaque) { Object *obj = OBJECT(opaque); - StringInputVisitor *siv; Error *local_err = NULL; char *c, *qom_name; @@ -2668,14 +2580,11 @@ static int machine_set_property(const char *name, const char *value, } } - siv = string_input_visitor_new(value); - object_property_set(obj, string_input_get_visitor(siv), qom_name, &local_err); - string_input_visitor_cleanup(siv); + object_property_parse(obj, value, qom_name, &local_err); g_free(qom_name); if (local_err) { - qerror_report_err(local_err); - error_free(local_err); + error_report_err(local_err); return -1; } @@ -2728,20 +2637,104 @@ out: g_free(type); g_free(dummy); if (err) { - qerror_report_err(err); - error_free(err); + error_report_err(err); return -1; } return 0; } +static void set_memory_options(uint64_t *ram_slots, ram_addr_t *maxram_size) +{ + uint64_t sz; + const char *mem_str; + const char *maxmem_str, *slots_str; + const ram_addr_t default_ram_size = (ram_addr_t)DEFAULT_RAM_SIZE * + 1024 * 1024; + QemuOpts *opts = qemu_find_opts_singleton("memory"); + + sz = 0; + mem_str = qemu_opt_get(opts, "size"); + if (mem_str) { + if (!*mem_str) { + error_report("missing 'size' option value"); + exit(EXIT_FAILURE); + } + + sz = qemu_opt_get_size(opts, "size", ram_size); + + /* Fix up legacy suffix-less format */ + if (g_ascii_isdigit(mem_str[strlen(mem_str) - 1])) { + uint64_t overflow_check = sz; + + sz <<= 20; + if ((sz >> 20) != overflow_check) { + error_report("too large 'size' option value"); + exit(EXIT_FAILURE); + } + } + } + + /* backward compatibility behaviour for case "-m 0" */ + if (sz == 0) { + sz = default_ram_size; + } + + sz = QEMU_ALIGN_UP(sz, 8192); + ram_size = sz; + if (ram_size != sz) { + error_report("ram size too large"); + exit(EXIT_FAILURE); + } + + /* store value for the future use */ + qemu_opt_set_number(opts, "size", ram_size, &error_abort); + *maxram_size = ram_size; + + maxmem_str = qemu_opt_get(opts, "maxmem"); + slots_str = qemu_opt_get(opts, "slots"); + if (maxmem_str && slots_str) { + uint64_t slots; + + sz = qemu_opt_get_size(opts, "maxmem", 0); + slots = qemu_opt_get_number(opts, "slots", 0); + if (sz < ram_size) { + error_report("invalid value of -m option maxmem: " + "maximum memory size (0x%" PRIx64 ") must be at least " + "the initial memory size (0x" RAM_ADDR_FMT ")", + sz, ram_size); + exit(EXIT_FAILURE); + } else if (sz > ram_size) { + if (!slots) { + error_report("invalid value of -m option: maxmem was " + "specified, but no hotplug slots were specified"); + exit(EXIT_FAILURE); + } + } else if (slots) { + error_report("invalid value of -m option maxmem: " + "memory slots were specified but maximum memory size " + "(0x%" PRIx64 ") is equal to the initial memory size " + "(0x" RAM_ADDR_FMT ")", sz, ram_size); + exit(EXIT_FAILURE); + } + + *maxram_size = sz; + *ram_slots = slots; + } else if ((!maxmem_str && slots_str) || + (maxmem_str && !slots_str)) { + error_report("invalid -m option value: missing " + "'%s' option", slots_str ? "maxmem" : "slots"); + exit(EXIT_FAILURE); + } +} + int main(int argc, char **argv, char **envp) { int i; int snapshot, linux_boot; const char *initrd_filename; const char *kernel_filename, *kernel_cmdline; - const char *boot_order; + const char *boot_order = NULL; + const char *boot_once = NULL; DisplayState *ds; int cyls, heads, secs, translation; QemuOpts *hda_opts = NULL, *opts, *machine_opts, *icount_opts = NULL; @@ -2770,13 +2763,14 @@ int main(int argc, char **argv, char **envp) }; const char *trace_events = NULL; const char *trace_file = NULL; - const ram_addr_t default_ram_size = (ram_addr_t)DEFAULT_RAM_SIZE * - 1024 * 1024; - ram_addr_t maxram_size = default_ram_size; + ram_addr_t maxram_size; uint64_t ram_slots = 0; FILE *vmstate_dump_file = NULL; Error *main_loop_err = NULL; + qemu_init_cpu_loop(); + qemu_mutex_lock_iothread(); + atexit(qemu_run_exit_notifiers); error_set_progname(argv[0]); qemu_init_exec_dir(argv[0]); @@ -2811,6 +2805,7 @@ int main(int argc, char **argv, char **envp) qemu_add_opts(&qemu_name_opts); qemu_add_opts(&qemu_numa_opts); qemu_add_opts(&qemu_icount_opts); + qemu_add_opts(&qemu_semihosting_config_opts); runstate_init(); @@ -2822,19 +2817,10 @@ int main(int argc, char **argv, char **envp) module_call_init(MODULE_INIT_MACHINE); machine_class = find_default_machine(); cpu_model = NULL; - ram_size = default_ram_size; snapshot = 0; cyls = heads = secs = 0; translation = BIOS_ATA_TRANSLATION_AUTO; - for (i = 0; i < MAX_NODES; i++) { - numa_info[i].node_mem = 0; - numa_info[i].present = false; - bitmap_zero(numa_info[i].node_cpu, MAX_CPUMASK_BITS); - } - - nb_numa_nodes = 0; - max_numa_nodeid = 0; nb_nics = 0; bdrv_init_with_whitelist(); @@ -2876,7 +2862,7 @@ int main(int argc, char **argv, char **envp) if (optind >= argc) break; if (argv[optind][0] != '-') { - hda_opts = drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS); + hda_opts = drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS); } else { const QEMUOption *popt; @@ -2886,9 +2872,6 @@ int main(int argc, char **argv, char **envp) exit(1); } switch(popt->index) { - case QEMU_OPTION_M: - machine_class = machine_parse(optarg); - break; case QEMU_OPTION_no_kvm_irqchip: { olist = qemu_find_opts("machine"); qemu_opts_parse(olist, "kernel_irqchip=off", 0); @@ -2924,15 +2907,15 @@ int main(int argc, char **argv, char **envp) if (drive_def(optarg) == NULL) { exit(1); } - break; + break; case QEMU_OPTION_set: if (qemu_set_option(optarg) != 0) exit(1); - break; + break; case QEMU_OPTION_global: if (qemu_global_option(optarg) != 0) exit(1); - break; + break; case QEMU_OPTION_mtdblock: drive_add(IF_MTD, -1, optarg, MTD_OPTS); break; @@ -2984,22 +2967,25 @@ int main(int argc, char **argv, char **envp) fprintf(stderr, "qemu: invalid physical CHS format\n"); exit(1); } - if (hda_opts != NULL) { - char num[16]; - snprintf(num, sizeof(num), "%d", cyls); - qemu_opt_set(hda_opts, "cyls", num); - snprintf(num, sizeof(num), "%d", heads); - qemu_opt_set(hda_opts, "heads", num); - snprintf(num, sizeof(num), "%d", secs); - qemu_opt_set(hda_opts, "secs", num); + if (hda_opts != NULL) { + qemu_opt_set_number(hda_opts, "cyls", cyls, + &error_abort); + qemu_opt_set_number(hda_opts, "heads", heads, + &error_abort); + qemu_opt_set_number(hda_opts, "secs", secs, + &error_abort); if (translation == BIOS_ATA_TRANSLATION_LARGE) { - qemu_opt_set(hda_opts, "trans", "large"); + qemu_opt_set(hda_opts, "trans", "large", + &error_abort); } else if (translation == BIOS_ATA_TRANSLATION_RECHS) { - qemu_opt_set(hda_opts, "trans", "rechs"); + qemu_opt_set(hda_opts, "trans", "rechs", + &error_abort); } else if (translation == BIOS_ATA_TRANSLATION_LBA) { - qemu_opt_set(hda_opts, "trans", "lba"); + qemu_opt_set(hda_opts, "trans", "lba", + &error_abort); } else if (translation == BIOS_ATA_TRANSLATION_NONE) { - qemu_opt_set(hda_opts, "trans", "none"); + qemu_opt_set(hda_opts, "trans", "none", + &error_abort); } } } @@ -3037,16 +3023,20 @@ int main(int argc, char **argv, char **envp) } break; case QEMU_OPTION_kernel: - qemu_opts_set(qemu_find_opts("machine"), 0, "kernel", optarg); + qemu_opts_set(qemu_find_opts("machine"), 0, "kernel", optarg, + &error_abort); break; case QEMU_OPTION_initrd: - qemu_opts_set(qemu_find_opts("machine"), 0, "initrd", optarg); + qemu_opts_set(qemu_find_opts("machine"), 0, "initrd", optarg, + &error_abort); break; case QEMU_OPTION_append: - qemu_opts_set(qemu_find_opts("machine"), 0, "append", optarg); + qemu_opts_set(qemu_find_opts("machine"), 0, "append", optarg, + &error_abort); break; case QEMU_OPTION_dtb: - qemu_opts_set(qemu_find_opts("machine"), 0, "dtb", optarg); + qemu_opts_set(qemu_find_opts("machine"), 0, "dtb", optarg, + &error_abort); break; case QEMU_OPTION_cdrom: drive_add(IF_DEFAULT, 2, optarg, CDROM_OPTS); @@ -3112,92 +3102,13 @@ int main(int argc, char **argv, char **envp) version(); exit(0); break; - case QEMU_OPTION_m: { - uint64_t sz; - const char *mem_str; - const char *maxmem_str, *slots_str; - + case QEMU_OPTION_m: opts = qemu_opts_parse(qemu_find_opts("memory"), optarg, 1); if (!opts) { exit(EXIT_FAILURE); } - - mem_str = qemu_opt_get(opts, "size"); - if (!mem_str) { - error_report("invalid -m option, missing 'size' option"); - exit(EXIT_FAILURE); - } - if (!*mem_str) { - error_report("missing 'size' option value"); - exit(EXIT_FAILURE); - } - - sz = qemu_opt_get_size(opts, "size", ram_size); - - /* Fix up legacy suffix-less format */ - if (g_ascii_isdigit(mem_str[strlen(mem_str) - 1])) { - uint64_t overflow_check = sz; - - sz <<= 20; - if ((sz >> 20) != overflow_check) { - error_report("too large 'size' option value"); - exit(EXIT_FAILURE); - } - } - - /* backward compatibility behaviour for case "-m 0" */ - if (sz == 0) { - sz = default_ram_size; - } - - sz = QEMU_ALIGN_UP(sz, 8192); - ram_size = sz; - if (ram_size != sz) { - error_report("ram size too large"); - exit(EXIT_FAILURE); - } - maxram_size = ram_size; - - maxmem_str = qemu_opt_get(opts, "maxmem"); - slots_str = qemu_opt_get(opts, "slots"); - if (maxmem_str && slots_str) { - uint64_t slots; - - sz = qemu_opt_get_size(opts, "maxmem", 0); - if (sz < ram_size) { - error_report("invalid -m option value: maxmem " - "(0x%" PRIx64 ") <= initial memory (0x" - RAM_ADDR_FMT ")", sz, ram_size); - exit(EXIT_FAILURE); - } - - slots = qemu_opt_get_number(opts, "slots", 0); - if ((sz > ram_size) && !slots) { - error_report("invalid -m option value: maxmem " - "(0x%" PRIx64 ") more than initial memory (0x" - RAM_ADDR_FMT ") but no hotplug slots where " - "specified", sz, ram_size); - exit(EXIT_FAILURE); - } - - if ((sz <= ram_size) && slots) { - error_report("invalid -m option value: %" - PRIu64 " hotplug slots where specified but " - "maxmem (0x%" PRIx64 ") <= initial memory (0x" - RAM_ADDR_FMT ")", slots, sz, ram_size); - exit(EXIT_FAILURE); - } - maxram_size = sz; - ram_slots = slots; - } else if ((!maxmem_str && slots_str) || - (maxmem_str && !slots_str)) { - error_report("invalid -m option value: missing " - "'%s' option", slots_str ? "maxmem" : "slots"); - exit(EXIT_FAILURE); - } break; - } #ifdef CONFIG_TPM case QEMU_OPTION_tpmdev: if (tpm_config_parse(qemu_find_opts("tpmdev"), optarg) < 0) { @@ -3229,7 +3140,8 @@ int main(int argc, char **argv, char **envp) } break; case QEMU_OPTION_bios: - qemu_opts_set(qemu_find_opts("machine"), 0, "firmware", optarg); + qemu_opts_set(qemu_find_opts("machine"), 0, "firmware", optarg, + &error_abort); break; case QEMU_OPTION_singlestep: singlestep = 1; @@ -3237,9 +3149,9 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_S: autostart = 0; break; - case QEMU_OPTION_k: - keyboard_layout = optarg; - break; + case QEMU_OPTION_k: + keyboard_layout = optarg; + break; case QEMU_OPTION_localtime: rtc_utc = 0; break; @@ -3292,11 +3204,15 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_monitor: default_monitor = 0; if (strncmp(optarg, "none", 4)) { - monitor_parse(optarg, "readline"); + monitor_parse(optarg, "readline", false); } break; case QEMU_OPTION_qmp: - monitor_parse(optarg, "control"); + monitor_parse(optarg, "control", false); + default_monitor = 0; + break; + case QEMU_OPTION_qmp_pretty: + monitor_parse(optarg, "control", true); default_monitor = 0; break; case QEMU_OPTION_mon: @@ -3355,35 +3271,39 @@ int main(int argc, char **argv, char **envp) writeout = qemu_opt_get(opts, "writeout"); if (writeout) { #ifdef CONFIG_SYNC_FILE_RANGE - qemu_opt_set(fsdev, "writeout", writeout); + qemu_opt_set(fsdev, "writeout", writeout, &error_abort); #else fprintf(stderr, "writeout=immediate not supported on " "this platform\n"); exit(1); #endif } - qemu_opt_set(fsdev, "fsdriver", qemu_opt_get(opts, "fsdriver")); - qemu_opt_set(fsdev, "path", qemu_opt_get(opts, "path")); + qemu_opt_set(fsdev, "fsdriver", + qemu_opt_get(opts, "fsdriver"), &error_abort); + qemu_opt_set(fsdev, "path", qemu_opt_get(opts, "path"), + &error_abort); qemu_opt_set(fsdev, "security_model", - qemu_opt_get(opts, "security_model")); + qemu_opt_get(opts, "security_model"), + &error_abort); socket = qemu_opt_get(opts, "socket"); if (socket) { - qemu_opt_set(fsdev, "socket", socket); + qemu_opt_set(fsdev, "socket", socket, &error_abort); } sock_fd = qemu_opt_get(opts, "sock_fd"); if (sock_fd) { - qemu_opt_set(fsdev, "sock_fd", sock_fd); + qemu_opt_set(fsdev, "sock_fd", sock_fd, &error_abort); } qemu_opt_set_bool(fsdev, "readonly", - qemu_opt_get_bool(opts, "readonly", 0)); + qemu_opt_get_bool(opts, "readonly", 0), + &error_abort); device = qemu_opts_create(qemu_find_opts("device"), NULL, 0, &error_abort); - qemu_opt_set(device, "driver", "virtio-9p-pci"); + qemu_opt_set(device, "driver", "virtio-9p-pci", &error_abort); qemu_opt_set(device, "fsdev", - qemu_opt_get(opts, "mount_tag")); + qemu_opt_get(opts, "mount_tag"), &error_abort); qemu_opt_set(device, "mount_tag", - qemu_opt_get(opts, "mount_tag")); + qemu_opt_get(opts, "mount_tag"), &error_abort); break; } case QEMU_OPTION_virtfs_synth: { @@ -3396,13 +3316,13 @@ int main(int argc, char **argv, char **envp) fprintf(stderr, "duplicate option: %s\n", "virtfs_synth"); exit(1); } - qemu_opt_set(fsdev, "fsdriver", "synth"); + qemu_opt_set(fsdev, "fsdriver", "synth", &error_abort); device = qemu_opts_create(qemu_find_opts("device"), NULL, 0, &error_abort); - qemu_opt_set(device, "driver", "virtio-9p-pci"); - qemu_opt_set(device, "fsdev", "v_synth"); - qemu_opt_set(device, "mount_tag", "v_synth"); + qemu_opt_set(device, "driver", "virtio-9p-pci", &error_abort); + qemu_opt_set(device, "fsdev", "v_synth", &error_abort); + qemu_opt_set(device, "mount_tag", "v_synth", &error_abort); break; } case QEMU_OPTION_serial: @@ -3443,9 +3363,9 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_debugcon: add_device_config(DEV_DEBUGCON, optarg); break; - case QEMU_OPTION_loadvm: - loadvm = optarg; - break; + case QEMU_OPTION_loadvm: + loadvm = optarg; + break; case QEMU_OPTION_full_screen: full_screen = 1; break; @@ -3506,16 +3426,13 @@ int main(int argc, char **argv, char **envp) olist = qemu_find_opts("machine"); qemu_opts_parse(olist, "accel=kvm", 0); break; + case QEMU_OPTION_M: case QEMU_OPTION_machine: olist = qemu_find_opts("machine"); opts = qemu_opts_parse(olist, optarg, 1); if (!opts) { exit(1); } - optarg = qemu_opt_get(opts, "type"); - if (optarg) { - machine_class = machine_parse(optarg); - } break; case QEMU_OPTION_no_kvm: olist = qemu_find_opts("machine"); @@ -3560,10 +3477,11 @@ int main(int argc, char **argv, char **envp) exit(1); } break; - case QEMU_OPTION_vnc: + case QEMU_OPTION_vnc: #ifdef CONFIG_VNC - display_remote++; - vnc_display = optarg; + if (vnc_parse_func(optarg) == NULL) { + exit(1); + } #else fprintf(stderr, "VNC support is disabled\n"); exit(1); @@ -3598,11 +3516,11 @@ int main(int argc, char **argv, char **envp) } qemu_uuid_set = true; break; - case QEMU_OPTION_option_rom: - if (nb_option_roms >= MAX_OPTION_ROMS) { - fprintf(stderr, "Too many option ROMs\n"); - exit(1); - } + case QEMU_OPTION_option_rom: + if (nb_option_roms >= MAX_OPTION_ROMS) { + fprintf(stderr, "Too many option ROMs\n"); + exit(1); + } opts = qemu_opts_parse(qemu_find_opts("option-rom"), optarg, 1); if (!opts) { exit(1); @@ -3614,10 +3532,41 @@ int main(int argc, char **argv, char **envp) fprintf(stderr, "Option ROM file is not specified\n"); exit(1); } - nb_option_roms++; - break; + nb_option_roms++; + break; case QEMU_OPTION_semihosting: semihosting_enabled = 1; + semihosting_target = SEMIHOSTING_TARGET_AUTO; + break; + case QEMU_OPTION_semihosting_config: + semihosting_enabled = 1; + opts = qemu_opts_parse(qemu_find_opts("semihosting-config"), + optarg, 0); + if (opts != NULL) { + semihosting_enabled = qemu_opt_get_bool(opts, "enable", + true); + const char *target = qemu_opt_get(opts, "target"); + if (target != NULL) { + if (strcmp("native", target) == 0) { + semihosting_target = SEMIHOSTING_TARGET_NATIVE; + } else if (strcmp("gdb", target) == 0) { + semihosting_target = SEMIHOSTING_TARGET_GDB; + } else if (strcmp("auto", target) == 0) { + semihosting_target = SEMIHOSTING_TARGET_AUTO; + } else { + fprintf(stderr, "Unsupported semihosting-config" + " %s\n", + optarg); + exit(1); + } + } else { + semihosting_target = SEMIHOSTING_TARGET_AUTO; + } + } else { + fprintf(stderr, "Unsupported semihosting-config %s\n", + optarg); + exit(1); + } break; case QEMU_OPTION_tdf: fprintf(stderr, "Warning: user space PIT time drift fix " @@ -3669,8 +3618,10 @@ int main(int argc, char **argv, char **envp) } break; case QEMU_OPTION_incoming: + if (!incoming) { + runstate_set(RUN_STATE_INMIGRATE); + } incoming = optarg; - runstate_set(RUN_STATE_INMIGRATE); break; case QEMU_OPTION_nodefaults: has_defaults = 0; @@ -3807,12 +3758,21 @@ int main(int argc, char **argv, char **envp) } } } + + opts = qemu_get_machine_opts(); + optarg = qemu_opt_get(opts, "type"); + if (optarg) { + machine_class = machine_parse(optarg); + } + + set_memory_options(&ram_slots, &maxram_size); + loc_set_none(); os_daemonize(); if (qemu_init_main_loop(&main_loop_err)) { - error_report("%s", error_get_pretty(main_loop_err)); + error_report_err(main_loop_err); exit(1); } @@ -3842,6 +3802,9 @@ int main(int argc, char **argv, char **envp) current_machine = MACHINE(object_new(object_class_get_name( OBJECT_CLASS(machine_class)))); + if (machine_help_func(qemu_get_machine_opts(), current_machine)) { + exit(0); + } object_property_add_child(object_get_root(), "machine", OBJECT(current_machine), &error_abort); cpu_exec_init_all(); @@ -3903,9 +3866,9 @@ int main(int argc, char **argv, char **envp) smp_parse(qemu_opts_find(qemu_find_opts("smp-opts"), NULL)); machine_class->max_cpus = machine_class->max_cpus ?: 1; /* Default to UP */ - if (smp_cpus > machine_class->max_cpus) { + if (max_cpus > machine_class->max_cpus) { fprintf(stderr, "Number of SMP cpus requested (%d), exceeds max cpus " - "supported by machine `%s' (%d)\n", smp_cpus, + "supported by machine `%s' (%d)\n", max_cpus, machine_class->name, machine_class->max_cpus); exit(1); } @@ -3994,7 +3957,7 @@ int main(int argc, char **argv, char **envp) add_device_config(DEV_SCLP, "stdio"); } if (default_monitor) - monitor_parse("stdio", "readline"); + monitor_parse("stdio", "readline", false); } } else { if (default_serial) @@ -4002,7 +3965,7 @@ int main(int argc, char **argv, char **envp) if (default_parallel) add_device_config(DEV_PARALLEL, "vc:80Cx24C"); if (default_monitor) - monitor_parse("vc:80Cx24C", "readline"); + monitor_parse("vc:80Cx24C", "readline", false); if (default_virtcon) add_device_config(DEV_VIRTCON, "vc:80Cx24C"); if (default_sclp) { @@ -4010,13 +3973,18 @@ int main(int argc, char **argv, char **envp) } } +#if defined(CONFIG_VNC) + if (!QTAILQ_EMPTY(&(qemu_find_opts("vnc")->head))) { + display_remote++; + } +#endif if (display_type == DT_DEFAULT && !display_remote) { #if defined(CONFIG_GTK) display_type = DT_GTK; #elif defined(CONFIG_SDL) || defined(CONFIG_COCOA) display_type = DT_SDL; #elif defined(CONFIG_VNC) - vnc_display = "localhost:0,to=99"; + vnc_parse_func("localhost:0,to=99,id=default"); show_vnc_port = 1; #else display_type = DT_NONE; @@ -4053,9 +4021,6 @@ int main(int argc, char **argv, char **envp) exit(1); } - /* store value for the future use */ - qemu_opt_set_number(qemu_find_opts_singleton("memory"), "size", ram_size); - if (qemu_opts_foreach(qemu_find_opts("device"), device_help_func, NULL, 0) != 0) { exit(0); @@ -4079,8 +4044,7 @@ int main(int argc, char **argv, char **envp) Error *local_err = NULL; qtest_init(qtest_chrdev, qtest_log, &local_err); if (local_err) { - error_report("%s", error_get_pretty(local_err)); - error_free(local_err); + error_report_err(local_err); exit(1); } } @@ -4091,30 +4055,36 @@ int main(int argc, char **argv, char **envp) kernel_cmdline = qemu_opt_get(machine_opts, "append"); bios_name = qemu_opt_get(machine_opts, "firmware"); - boot_order = machine_class->default_boot_order; opts = qemu_opts_find(qemu_find_opts("boot-opts"), NULL); if (opts) { - char *normal_boot_order; - const char *order, *once; + Error *local_err = NULL; - order = qemu_opt_get(opts, "order"); - if (order) { - validate_bootdevices(order); - boot_order = order; + boot_order = qemu_opt_get(opts, "order"); + if (boot_order) { + validate_bootdevices(boot_order, &local_err); + if (local_err) { + error_report_err(local_err); + exit(1); + } } - once = qemu_opt_get(opts, "once"); - if (once) { - validate_bootdevices(once); - normal_boot_order = g_strdup(boot_order); - boot_order = once; - qemu_register_reset(restore_boot_order, normal_boot_order); + boot_once = qemu_opt_get(opts, "once"); + if (boot_once) { + validate_bootdevices(boot_once, &local_err); + if (local_err) { + error_report_err(local_err); + exit(1); + } } boot_menu = qemu_opt_get_bool(opts, "menu", boot_menu); boot_strict = qemu_opt_get_bool(opts, "strict", false); } + if (!boot_order) { + boot_order = machine_class->default_boot_order; + } + if (!kernel_cmdline) { kernel_cmdline = ""; current_machine->kernel_cmdline = (char *)kernel_cmdline; @@ -4139,9 +4109,6 @@ int main(int argc, char **argv, char **envp) os_set_line_buffering(); - qemu_init_cpu_loop(); - qemu_mutex_lock_iothread(); - #ifdef CONFIG_SPICE /* spice needs the timers to be initialized by this point */ qemu_spice_init(); @@ -4205,12 +4172,7 @@ int main(int argc, char **argv, char **envp) default_drive(default_floppy, snapshot, IF_FLOPPY, 0, FD_OPTS); default_drive(default_sdcard, snapshot, IF_SD, 0, SD_OPTS); - if (qemu_opts_foreach(qemu_find_opts("numa"), numa_init_func, - NULL, 1) != 0) { - exit(1); - } - - set_numa_nodes(); + parse_numa_opts(machine_class); if (qemu_opts_foreach(qemu_find_opts("mon"), mon_init_func, NULL, 1) != 0) { exit(1); @@ -4269,10 +4231,10 @@ int main(int argc, char **argv, char **envp) cpu_synchronize_all_post_init(); - set_numa_modes(); + numa_post_machine_init(); /* init USB devices */ - if (usb_enabled(false)) { + if (usb_enabled()) { if (foreach_device_config(DEV_USB, usb_parse) < 0) exit(1); } @@ -4286,6 +4248,16 @@ int main(int argc, char **argv, char **envp) net_check_clients(); + if (boot_once) { + Error *local_err = NULL; + qemu_boot_set(boot_once, &local_err); + if (local_err) { + error_report("%s", error_get_pretty(local_err)); + exit(1); + } + qemu_register_reset(restore_boot_order, g_strdup(boot_order)); + } + ds = init_displaystate(); /* init local displays */ @@ -4321,20 +4293,10 @@ int main(int argc, char **argv, char **envp) #ifdef CONFIG_VNC /* init remote displays */ - if (vnc_display) { - Error *local_err = NULL; - vnc_display_init(ds); - vnc_display_open(ds, vnc_display, &local_err); - if (local_err != NULL) { - error_report("Failed to start VNC server on `%s': %s", - vnc_display, error_get_pretty(local_err)); - error_free(local_err); - exit(1); - } - - if (show_vnc_port) { - printf("VNC server running on `%s'\n", vnc_display_local_addr(ds)); - } + qemu_opts_foreach(qemu_find_opts("vnc"), vnc_init_func, NULL, 0); + if (show_vnc_port) { + printf("VNC server running on `%s'\n", + vnc_display_local_addr("default")); } #endif #ifdef CONFIG_SPICE diff --git a/xen-hvm-stub.c b/xen-hvm-stub.c index 2d98696..46867d8 100644 --- a/xen-hvm-stub.c +++ b/xen-hvm-stub.c @@ -30,10 +30,6 @@ void xen_hvm_inject_msi(uint64_t addr, uint32_t data) { } -void xen_cmos_set_s3_resume(void *opaque, int irq, int level) -{ -} - void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, MemoryRegion *mr) { } diff --git a/xen-hvm.c b/xen-hvm.c index 7548794..315864c 100644 --- a/xen-hvm.c +++ b/xen-hvm.c @@ -85,11 +85,14 @@ static inline ioreq_t *xen_vcpu_ioreq(shared_iopage_t *shared_page, int vcpu) } # define FMT_ioreq_size "u" #endif -#ifndef HVM_PARAM_BUFIOREQ_EVTCHN -#define HVM_PARAM_BUFIOREQ_EVTCHN 26 -#endif #define BUFFER_IO_MAX_DELAY 100 +/* Leave some slack so that hvmloader does not complain about lack of + * memory at boot time ("Could not allocate order=0 extent"). + * Once hvmloader is modified to cope with that situation without + * printing warning messages, QEMU_SPARE_PAGES can be removed. + */ +#define QEMU_SPARE_PAGES 16 typedef struct XenPhysmap { hwaddr start_addr; @@ -101,6 +104,7 @@ typedef struct XenPhysmap { } XenPhysmap; typedef struct XenIOState { + ioservid_t ioservid; shared_iopage_t *shared_page; shared_vmport_iopage_t *shared_vmport_page; buffered_iopage_t *buffered_io_page; @@ -117,6 +121,8 @@ typedef struct XenIOState { struct xs_handle *xenstore; MemoryListener memory_listener; + MemoryListener io_listener; + DeviceListener device_listener; QLIST_HEAD(, XenPhysmap) physmap; hwaddr free_phys_offset; const XenPhysmap *log_for_dirtybit; @@ -244,6 +250,8 @@ void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, MemoryRegion *mr) unsigned long nr_pfn; xen_pfn_t *pfn_list; int i; + xc_domaininfo_t info; + unsigned long free_pages; if (runstate_check(RUN_STATE_INMIGRATE)) { /* RAM already populated in Xen */ @@ -266,6 +274,22 @@ void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, MemoryRegion *mr) pfn_list[i] = (ram_addr >> TARGET_PAGE_BITS) + i; } + if ((xc_domain_getinfolist(xen_xc, xen_domid, 1, &info) != 1) || + (info.domain != xen_domid)) { + hw_error("xc_domain_getinfolist failed"); + } + free_pages = info.max_pages - info.tot_pages; + if (free_pages > QEMU_SPARE_PAGES) { + free_pages -= QEMU_SPARE_PAGES; + } else { + free_pages = 0; + } + if ((free_pages < nr_pfn) && + (xc_domain_setmaxmem(xen_xc, xen_domid, + ((info.max_pages + nr_pfn - free_pages) + << (XC_PAGE_SHIFT - 10))) < 0)) { + hw_error("xc_domain_setmaxmem failed"); + } if (xc_domain_populate_physmap_exact(xen_xc, xen_domid, nr_pfn, 0, 0, pfn_list)) { hw_error("xen: failed to populate ram at " RAM_ADDR_FMT, ram_addr); } @@ -467,12 +491,23 @@ static void xen_set_memory(struct MemoryListener *listener, bool log_dirty = memory_region_is_logging(section->mr); hvmmem_type_t mem_type; + if (section->mr == &ram_memory) { + return; + } else { + if (add) { + xen_map_memory_section(xen_xc, xen_domid, state->ioservid, + section); + } else { + xen_unmap_memory_section(xen_xc, xen_domid, state->ioservid, + section); + } + } + if (!memory_region_is_ram(section->mr)) { return; } - if (!(section->mr != &ram_memory - && ( (log_dirty && add) || (!log_dirty && !add)))) { + if (log_dirty != add) { return; } @@ -515,6 +550,50 @@ static void xen_region_del(MemoryListener *listener, memory_region_unref(section->mr); } +static void xen_io_add(MemoryListener *listener, + MemoryRegionSection *section) +{ + XenIOState *state = container_of(listener, XenIOState, io_listener); + + memory_region_ref(section->mr); + + xen_map_io_section(xen_xc, xen_domid, state->ioservid, section); +} + +static void xen_io_del(MemoryListener *listener, + MemoryRegionSection *section) +{ + XenIOState *state = container_of(listener, XenIOState, io_listener); + + xen_unmap_io_section(xen_xc, xen_domid, state->ioservid, section); + + memory_region_unref(section->mr); +} + +static void xen_device_realize(DeviceListener *listener, + DeviceState *dev) +{ + XenIOState *state = container_of(listener, XenIOState, device_listener); + + if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { + PCIDevice *pci_dev = PCI_DEVICE(dev); + + xen_map_pcidev(xen_xc, xen_domid, state->ioservid, pci_dev); + } +} + +static void xen_device_unrealize(DeviceListener *listener, + DeviceState *dev) +{ + XenIOState *state = container_of(listener, XenIOState, device_listener); + + if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { + PCIDevice *pci_dev = PCI_DEVICE(dev); + + xen_unmap_pcidev(xen_xc, xen_domid, state->ioservid, pci_dev); + } +} + static void xen_sync_dirty_bitmap(XenIOState *state, hwaddr start_addr, ram_addr_t size) @@ -615,6 +694,17 @@ static MemoryListener xen_memory_listener = { .priority = 10, }; +static MemoryListener xen_io_listener = { + .region_add = xen_io_add, + .region_del = xen_io_del, + .priority = 10, +}; + +static DeviceListener xen_device_listener = { + .realize = xen_device_realize, + .unrealize = xen_device_unrealize, +}; + /* get the ioreq packets from share mem */ static ioreq_t *cpu_get_ioreq_from_shared_memory(XenIOState *state, int vcpu) { @@ -863,6 +953,27 @@ static void handle_ioreq(XenIOState *state, ioreq_t *req) case IOREQ_TYPE_INVALIDATE: xen_invalidate_map_cache(); break; + case IOREQ_TYPE_PCI_CONFIG: { + uint32_t sbdf = req->addr >> 32; + uint32_t val; + + /* Fake a write to port 0xCF8 so that + * the config space access will target the + * correct device model. + */ + val = (1u << 31) | + ((req->addr & 0x0f00) << 16) | + ((sbdf & 0xffff) << 8) | + (req->addr & 0xfc); + do_outp(0xcf8, 4, val); + + /* Now issue the config space access via + * port 0xCFC + */ + req->addr = 0xcfc | (req->addr & 0x03); + cpu_ioreq_pio(req); + break; + } default: hw_error("Invalid ioreq type 0x%x\n", req->type); } @@ -993,9 +1104,15 @@ static void xen_main_loop_prepare(XenIOState *state) static void xen_hvm_change_state_handler(void *opaque, int running, RunState rstate) { + XenIOState *state = opaque; + if (running) { - xen_main_loop_prepare((XenIOState *)opaque); + xen_main_loop_prepare(state); } + + xen_set_ioreq_server_state(xen_xc, xen_domid, + state->ioservid, + (rstate == RUN_STATE_RUNNING)); } static void xen_exit_notifier(Notifier *n, void *data) @@ -1064,8 +1181,9 @@ int xen_hvm_init(ram_addr_t *below_4g_mem_size, ram_addr_t *above_4g_mem_size, MemoryRegion **ram_memory) { int i, rc; - unsigned long ioreq_pfn; - unsigned long bufioreq_evtchn; + xen_pfn_t ioreq_pfn; + xen_pfn_t bufioreq_pfn; + evtchn_port_t bufioreq_evtchn; XenIOState *state; state = g_malloc0(sizeof (XenIOState)); @@ -1082,6 +1200,12 @@ int xen_hvm_init(ram_addr_t *below_4g_mem_size, ram_addr_t *above_4g_mem_size, return -1; } + rc = xen_create_ioreq_server(xen_xc, xen_domid, &state->ioservid); + if (rc < 0) { + perror("xen: ioreq server create"); + return -1; + } + state->exit.notify = xen_exit_notifier; qemu_add_exit_notifier(&state->exit); @@ -1091,8 +1215,18 @@ int xen_hvm_init(ram_addr_t *below_4g_mem_size, ram_addr_t *above_4g_mem_size, state->wakeup.notify = xen_wakeup_notifier; qemu_register_wakeup_notifier(&state->wakeup); - xc_get_hvm_param(xen_xc, xen_domid, HVM_PARAM_IOREQ_PFN, &ioreq_pfn); + rc = xen_get_ioreq_server_info(xen_xc, xen_domid, state->ioservid, + &ioreq_pfn, &bufioreq_pfn, + &bufioreq_evtchn); + if (rc < 0) { + hw_error("failed to get ioreq server info: error %d handle=" XC_INTERFACE_FMT, + errno, xen_xc); + } + DPRINTF("shared page at pfn %lx\n", ioreq_pfn); + DPRINTF("buffered io page at pfn %lx\n", bufioreq_pfn); + DPRINTF("buffered io evtchn is %x\n", bufioreq_evtchn); + state->shared_page = xc_map_foreign_range(xen_xc, xen_domid, XC_PAGE_SIZE, PROT_READ|PROT_WRITE, ioreq_pfn); if (state->shared_page == NULL) { @@ -1114,10 +1248,10 @@ int xen_hvm_init(ram_addr_t *below_4g_mem_size, ram_addr_t *above_4g_mem_size, hw_error("get vmport regs pfn returned error %d, rc=%d", errno, rc); } - xc_get_hvm_param(xen_xc, xen_domid, HVM_PARAM_BUFIOREQ_PFN, &ioreq_pfn); - DPRINTF("buffered io page at pfn %lx\n", ioreq_pfn); - state->buffered_io_page = xc_map_foreign_range(xen_xc, xen_domid, XC_PAGE_SIZE, - PROT_READ|PROT_WRITE, ioreq_pfn); + state->buffered_io_page = xc_map_foreign_range(xen_xc, xen_domid, + XC_PAGE_SIZE, + PROT_READ|PROT_WRITE, + bufioreq_pfn); if (state->buffered_io_page == NULL) { hw_error("map buffered IO page returned error %d", errno); } @@ -1125,6 +1259,12 @@ int xen_hvm_init(ram_addr_t *below_4g_mem_size, ram_addr_t *above_4g_mem_size, /* Note: cpus is empty at this point in init */ state->cpu_by_vcpu_id = g_malloc0(max_cpus * sizeof(CPUState *)); + rc = xen_set_ioreq_server_state(xen_xc, xen_domid, state->ioservid, true); + if (rc < 0) { + hw_error("failed to enable ioreq server info: error %d handle=" XC_INTERFACE_FMT, + errno, xen_xc); + } + state->ioreq_local_port = g_malloc0(max_cpus * sizeof (evtchn_port_t)); /* FIXME: how about if we overflow the page here? */ @@ -1132,22 +1272,16 @@ int xen_hvm_init(ram_addr_t *below_4g_mem_size, ram_addr_t *above_4g_mem_size, rc = xc_evtchn_bind_interdomain(state->xce_handle, xen_domid, xen_vcpu_eport(state->shared_page, i)); if (rc == -1) { - fprintf(stderr, "bind interdomain ioctl error %d\n", errno); + fprintf(stderr, "shared evtchn %d bind error %d\n", i, errno); return -1; } state->ioreq_local_port[i] = rc; } - rc = xc_get_hvm_param(xen_xc, xen_domid, HVM_PARAM_BUFIOREQ_EVTCHN, - &bufioreq_evtchn); - if (rc < 0) { - fprintf(stderr, "failed to get HVM_PARAM_BUFIOREQ_EVTCHN\n"); - return -1; - } rc = xc_evtchn_bind_interdomain(state->xce_handle, xen_domid, - (uint32_t)bufioreq_evtchn); + bufioreq_evtchn); if (rc == -1) { - fprintf(stderr, "bind interdomain ioctl error %d\n", errno); + fprintf(stderr, "buffered evtchn bind error %d\n", errno); return -1; } state->bufioreq_local_port = rc; @@ -1163,6 +1297,12 @@ int xen_hvm_init(ram_addr_t *below_4g_mem_size, ram_addr_t *above_4g_mem_size, memory_listener_register(&state->memory_listener, &address_space_memory); state->log_for_dirtybit = NULL; + state->io_listener = xen_io_listener; + memory_listener_register(&state->io_listener, &address_space_io); + + state->device_listener = xen_device_listener; + device_listener_register(&state->device_listener); + /* Initialize backend core & drivers */ if (xen_be_init() != 0) { fprintf(stderr, "%s: xen backend core setup failed\n", __FUNCTION__); diff --git a/xen-mapcache.c b/xen-mapcache.c index 66da1a6..8cefd0c 100644 --- a/xen-mapcache.c +++ b/xen-mapcache.c @@ -49,9 +49,6 @@ */ #define NON_MCACHE_MEMORY_SIZE (80 * 1024 * 1024) -#define mapcache_lock() ((void)0) -#define mapcache_unlock() ((void)0) - typedef struct MapCacheEntry { hwaddr paddr_index; uint8_t *vaddr_base; @@ -79,11 +76,22 @@ typedef struct MapCache { unsigned int mcache_bucket_shift; phys_offset_to_gaddr_t phys_offset_to_gaddr; + QemuMutex lock; void *opaque; } MapCache; static MapCache *mapcache; +static inline void mapcache_lock(void) +{ + qemu_mutex_lock(&mapcache->lock); +} + +static inline void mapcache_unlock(void) +{ + qemu_mutex_unlock(&mapcache->lock); +} + static inline int test_bits(int nr, int size, const unsigned long *addr) { unsigned long res = find_next_zero_bit(addr, size + nr, nr); @@ -102,6 +110,7 @@ void xen_map_cache_init(phys_offset_to_gaddr_t f, void *opaque) mapcache->phys_offset_to_gaddr = f; mapcache->opaque = opaque; + qemu_mutex_init(&mapcache->lock); QTAILQ_INIT(&mapcache->locked_entries); @@ -193,14 +202,14 @@ static void xen_remap_bucket(MapCacheEntry *entry, g_free(err); } -uint8_t *xen_map_cache(hwaddr phys_addr, hwaddr size, - uint8_t lock) +static uint8_t *xen_map_cache_unlocked(hwaddr phys_addr, hwaddr size, + uint8_t lock) { MapCacheEntry *entry, *pentry = NULL; hwaddr address_index; hwaddr address_offset; - hwaddr __size = size; - hwaddr __test_bit_size = size; + hwaddr cache_size = size; + hwaddr test_bit_size; bool translated = false; tryagain: @@ -209,22 +218,22 @@ tryagain: trace_xen_map_cache(phys_addr); - /* __test_bit_size is always a multiple of XC_PAGE_SIZE */ + /* test_bit_size is always a multiple of XC_PAGE_SIZE */ if (size) { - __test_bit_size = size + (phys_addr & (XC_PAGE_SIZE - 1)); + test_bit_size = size + (phys_addr & (XC_PAGE_SIZE - 1)); - if (__test_bit_size % XC_PAGE_SIZE) { - __test_bit_size += XC_PAGE_SIZE - (__test_bit_size % XC_PAGE_SIZE); + if (test_bit_size % XC_PAGE_SIZE) { + test_bit_size += XC_PAGE_SIZE - (test_bit_size % XC_PAGE_SIZE); } } else { - __test_bit_size = XC_PAGE_SIZE; + test_bit_size = XC_PAGE_SIZE; } if (mapcache->last_entry != NULL && mapcache->last_entry->paddr_index == address_index && - !lock && !__size && + !lock && !size && test_bits(address_offset >> XC_PAGE_SHIFT, - __test_bit_size >> XC_PAGE_SHIFT, + test_bit_size >> XC_PAGE_SHIFT, mapcache->last_entry->valid_mapping)) { trace_xen_map_cache_return(mapcache->last_entry->vaddr_base + address_offset); return mapcache->last_entry->vaddr_base + address_offset; @@ -232,20 +241,20 @@ tryagain: /* size is always a multiple of MCACHE_BUCKET_SIZE */ if (size) { - __size = size + address_offset; - if (__size % MCACHE_BUCKET_SIZE) { - __size += MCACHE_BUCKET_SIZE - (__size % MCACHE_BUCKET_SIZE); + cache_size = size + address_offset; + if (cache_size % MCACHE_BUCKET_SIZE) { + cache_size += MCACHE_BUCKET_SIZE - (cache_size % MCACHE_BUCKET_SIZE); } } else { - __size = MCACHE_BUCKET_SIZE; + cache_size = MCACHE_BUCKET_SIZE; } entry = &mapcache->entry[address_index % mapcache->nr_buckets]; while (entry && entry->lock && entry->vaddr_base && - (entry->paddr_index != address_index || entry->size != __size || + (entry->paddr_index != address_index || entry->size != cache_size || !test_bits(address_offset >> XC_PAGE_SHIFT, - __test_bit_size >> XC_PAGE_SHIFT, + test_bit_size >> XC_PAGE_SHIFT, entry->valid_mapping))) { pentry = entry; entry = entry->next; @@ -253,19 +262,19 @@ tryagain: if (!entry) { entry = g_malloc0(sizeof (MapCacheEntry)); pentry->next = entry; - xen_remap_bucket(entry, __size, address_index); + xen_remap_bucket(entry, cache_size, address_index); } else if (!entry->lock) { if (!entry->vaddr_base || entry->paddr_index != address_index || - entry->size != __size || + entry->size != cache_size || !test_bits(address_offset >> XC_PAGE_SHIFT, - __test_bit_size >> XC_PAGE_SHIFT, + test_bit_size >> XC_PAGE_SHIFT, entry->valid_mapping)) { - xen_remap_bucket(entry, __size, address_index); + xen_remap_bucket(entry, cache_size, address_index); } } if(!test_bits(address_offset >> XC_PAGE_SHIFT, - __test_bit_size >> XC_PAGE_SHIFT, + test_bit_size >> XC_PAGE_SHIFT, entry->valid_mapping)) { mapcache->last_entry = NULL; if (!translated && mapcache->phys_offset_to_gaddr) { @@ -291,14 +300,27 @@ tryagain: return mapcache->last_entry->vaddr_base + address_offset; } +uint8_t *xen_map_cache(hwaddr phys_addr, hwaddr size, + uint8_t lock) +{ + uint8_t *p; + + mapcache_lock(); + p = xen_map_cache_unlocked(phys_addr, size, lock); + mapcache_unlock(); + return p; +} + ram_addr_t xen_ram_addr_from_mapcache(void *ptr) { MapCacheEntry *entry = NULL; MapCacheRev *reventry; hwaddr paddr_index; hwaddr size; + ram_addr_t raddr; int found = 0; + mapcache_lock(); QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) { if (reventry->vaddr_req == ptr) { paddr_index = reventry->paddr_index; @@ -323,13 +345,16 @@ ram_addr_t xen_ram_addr_from_mapcache(void *ptr) } if (!entry) { DPRINTF("Trying to find address %p that is not in the mapcache!\n", ptr); - return 0; + raddr = 0; + } else { + raddr = (reventry->paddr_index << MCACHE_BUCKET_SHIFT) + + ((unsigned long) ptr - (unsigned long) entry->vaddr_base); } - return (reventry->paddr_index << MCACHE_BUCKET_SHIFT) + - ((unsigned long) ptr - (unsigned long) entry->vaddr_base); + mapcache_unlock(); + return raddr; } -void xen_invalidate_map_cache_entry(uint8_t *buffer) +static void xen_invalidate_map_cache_entry_unlocked(uint8_t *buffer) { MapCacheEntry *entry = NULL, *pentry = NULL; MapCacheRev *reventry; @@ -383,6 +408,13 @@ void xen_invalidate_map_cache_entry(uint8_t *buffer) g_free(entry); } +void xen_invalidate_map_cache_entry(uint8_t *buffer) +{ + mapcache_lock(); + xen_invalidate_map_cache_entry_unlocked(buffer); + mapcache_unlock(); +} + void xen_invalidate_map_cache(void) { unsigned long i; @@ -391,14 +423,14 @@ void xen_invalidate_map_cache(void) /* Flush pending AIO before destroying the mapcache */ bdrv_drain_all(); + mapcache_lock(); + QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) { DPRINTF("There should be no locked mappings at this time, " "but "TARGET_FMT_plx" -> %p is present\n", reventry->paddr_index, reventry->vaddr_req); } - mapcache_lock(); - for (i = 0; i < mapcache->nr_buckets; i++) { MapCacheEntry *entry = &mapcache->entry[i]; -- 2.7.4

    Sc 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�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%0DupI9U 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 Xk%rnNj$gk3p$CW;x-BHc|5^MGi1j^< diff --git a/pc-bios/keymaps/ru b/pc-bios/keymaps/ru index b3e7d24..8f652d5 100644 --- a/pc-bios/keymaps/ru +++ b/pc-bios/keymaps/ru @@ -4,7 +4,7 @@ map 0x419 exclam 0x02 shift at 0x03 shift quotedbl 0x03 shift altgr -numbersign 0x04 shift +numerosign 0x04 shift dollar 0x05 shift asterisk 0x05 shift altgr percent 0x06 shift diff --git a/pc-bios/linuxboot.bin b/pc-bios/linuxboot.bin index 130103fb739228a6869aaf1b174b9d20c13378fc..923d1796fbc58f1a836c160be533d1cf697b7511 100644 GIT binary patch delta 142 zcmZqRXyBNj#iGKj$vjbStpET3 delta 89 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 diff --git a/pc-bios/openbios-sparc32 b/pc-bios/openbios-sparc32 index 6d5a381a7f1e9da9527a4b0fcc5040775d352588..e2bc9aabd014751a1135360f7678adfc104c899f 100644 GIT binary patch delta 43357 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

    vR>`t-_MjJ zl+SM?a01CVOK4C+N%6ydFt_tU1Rf+DBjtP%@95~+GS;jZ1wV60r}o(!mG%gznIYg? zxe%A6q^(2N7L@|66DTFx^{jTinC*4mM)o%3`+TZg{>IDbbN+Ts*x52ImzTYb6a4J2 z^1UsT@i&J6HveyaoxaR8-aY(aa)t!`qT0dluFUUaT&IVND9sWa#8YD{V`&0X!#>TF z2C==6ZVxEIQBO~{-v5wmRL4c(7bTwF#^7HATXP?fjj5scAkSPaypByXJ-Pv})Sb8k zFynKTsyf7zb8=TgP}?*=a&NTZ4?kp*h?p%?0E4HM6ol-d32@;5fZ)*$!oQ7f4eo)xK-QJaL`oplmi{=5a|UgmR|GG}tRz-r&?}9?Jorw4c1~q=bZ(BTns4I^fXe zc0ZY!EP$F)9R*Ovq*3+2|Jwlg1v(B%Canjf`HPoc-ESOw$I?dUmF~%4(_LPU=-RdXqRMlh~L#}4<_RhAa+-9<-%ze%u zh6xS@`#999w8g(WIz!&Slar!TRZ}eZ{o`sSHZ?7WgNK?!HhZI(!HBjL)p<^(kM=`- zi)QO*0$@n+y{p@XlDNqHcx^j%?W6v*f{lEh`I{k;gRLC%%Z8n(Iqc%Raas{Bh^B^& z{QS4);XXepZ_$24p_(#$BrD$H1pF)7*d0VG+++~N9uYk4z(&nxd*_Y7jiN&w_);bY z)@C=6zS!_s!TUMIrurjrBjwc9W}8p#9c{CJ>TJg0!I!l(o&`;hlG)YlAE`}VW?ah9 zOZx%#a&UHQI0Kt%h{B3%SS?i;cK+Suy(Q}numqp`e0Ft*YszZvZg5I8rTmWoQ+`& zpa`5Uy8Qg09VO=IjJNt*FQicHU2j-)r&}t+qTw%Ad)(VdOJxU>vheLA9$N+=qS6Qq0Za*0IgHmA@rv%4X+^yXZ zU^F`l%EXksIU8pjj@@#s^#fh<>v)KA99;3oE)ALKF6r%XinTK4K&aMm3eQw5J4*hXkdJz|b~)NZ+rskOvMyr$BDZXozDPS5 zP!Y#O+Wu1ILDWqI*tljGcN^a1F1(1e(cRW*i@O3}6VoDH^b$mQ%pmoL34#jXy)p{w>(~7fk?~`7LYAbCc?h=k! zDgB|B+f-A)lrozQuy%w#{#6mdZg@t8z`ZtHK`oucnb#{hfmny}SY*)J1>535?Bn7H zmFl?&Mmj_6hFlJ!&k{~b^M2G~2SOq`b-RnSRIwE1KoMFIKC~jeXfpLPsi`>$e&bkG zR781Kq8`)p<9G;9We95E z*nFdKy{@?#rn=1L_nGlTM00@#6tgJN48OX!+2sdH{dG|(&&(M2^ka-|$s{inCm4@x z0a{6n7LH-Lm$tFmLAyp4(9X9I8@tkW*Y|op*f*lp-HPyI#4T12N3-kNV4%vUUB**m zX%k3MZ%yeiRyQJ$6}5){dM1xvDVMm9!O~!%U2jH?7W{nN&uzx7-Y<^;q9>LbIQ()| zVvH_CVN?5H9JAfK#mFY=zQ7$d>96r=Lq&9tTgWo1inExySrObPEwj%-bKEcEMza{b3p53}z%E5{> zOhIeuqDn#wVn@24xh7?NsZs$PSulB@EY7|-7x#F+)X?bJp_x(mgSjDPhw~-?Xm#Pw zR_kRs7tFBft{P5-7W$FZMO*3jik05&ge}`I8(=oj`>s^|q%XS%oWP;e=G@(XJTSD5 zm=r~x_-%ch*(%)|bf5Ke?Uv{9P|RXNZ2M@}yi`-a~miSQfJZjs-S8K!NWK*UT zXz7ta?<$4N;)^TWBnKF*Na&+$5w^Z)VrzvXmkkwHI@xU(6qx;lau!Ltq_ZyDieSQb z+0ej|W@gBx7buox}wzSs+LdDIs(?%N2yM<1_CN zZyqo3rlR-qMwme8x3x?{L1k z)o~loSVm@fhbGJ0y5~QJ(%ltnm1%IKKD<(hu*KpOvit?%Ud(vmS(l8EXdQ5Hue$~E z_UFWY(ZNRNGerZZ!VEkk@l1uf%k5Z?V%hmX`c(7zyHZXk;tjU%l^TIC&~xMUdcZQl zwTx6%-;^eAbd4}wwFDVlgg4kGapf#+ApCRofyQv)vpy@Z9dI|;qA@hjoo&3l7C~4N zt*kqIL`0k&dTviAL@hd7`j@KME)&b2>I;s1RjNO2Uz*ugI*RL*!tN-FB2aI{8?c%k;F^4~Ld_=hO0n{JD zHl1R#JkL~K$aS>x@J7q-DHWRd>@k>#66@NYXMQpRBQ8r<)35MBn*#0SGA7Zo;V?yG z&=~W6!D>52HZ$cxO&SB@zo^4ozAZ5j82Bh#HRIq4Gr5Cd!nIoS8oUvqgq@xH;qwnf z_j4$ECd)G-8q`zYdfc9XOJO)r*L4LAbDmlm$A7-3NxSJoW+@2@J@x#7NLj+Yk?Xk% z91Ap+EzY0zIedqhI%V1c|~Z(b~&d@%^5X^l+sbjXz|iY(aDTPx=J>+ zk#W*%^YnP>QfYWnbv0CNLvZ`4A@N4dO;Un|e6=xC;oB}r*fClXaF4NLK7rS%EXfwr zR=eOL<@D&lA{3C#T=e0mqKCW8=Os0n?Kd}o7Vf_OLNP+w4wzE=z*Z|wFfGY9(lMek!{>$nrYG7kv9{vWfT1(TF=hj0v;+`#x4q_yW`h|GK%>1*g+P|301d4 zaUH7L0^hL1INU_vu@Ob-xqEN$$oh2I{$j7cQO83Yuf=C9?`09R&uO`Q1U-osEVi_# z_%*Xgs(O>lN_~=9hYRb)nO^)D+{f1Z{xGFJ*V_Hk#ZJ!eQE>O`qPTm+1@ek;*m@@y z4}RBVhtGJrOmL%BbEA97c&!Yf-t$1-;sl~R??4FR73%O>iXL(B>TJlS@yMp#K;jaR zI9o-{N{(*=gFjZSx#{m;D_w*qrQipD+YbL*)rnnfNVOL8sV$)Z1WjW}6PYh3ThJ zUDLmQf3iU;r3t52HI?P8X4l9st1Y2d&6}Ijvt-0F{fJp8{gd=3(6^0C4whm3Hm{Lr z>TikW-)ZPU2o!~i?O282`>=KKeH08-Ii0Wj_=wv7BKmc=D2XfE!D{Hv1bYFLvB{CQ%`MPSX~JWY3CsRGMRqwo`p5*)uj!TYx(mCB7c`Kf+nSo z=@krW`b7_iS<0f6?iYaMqV*5KpqI=h!lXPIQDG?{RI&7s(Q4E)dUIVkg>nF@6+u!v z@0`1=3{{*XyA+~vY$AH=hKOg;6$f33G8D8<*|Hhf<)%4lrzGIa#kNRzsGfmV7O0je zquV}HSF~eOz*_7#JTOWH4xrSE4dw{UH&nWLs_y zQW3NXA)TqJ_$_$CzYfS#fO%^eF^X@WMnrCd8aA&FXe-L?3Ip*0FTe950$#7smP?S; z?jwJ2($#bFk+lV6LnI|0Oq!ni7OA_^y#>5lwmL3ZN~9LTm{d*IG(&Yag>GPs_u;b; z(IXD}LWo?sJl-fX>)k`!`*ahU5G+hvat5;7sh0@IvFd99==ZEhYkimCj>ihSifsU0pSJ~2xefy{rNwHZBoc+p{vv- z2NayuWLg%0m8(NmdKp3W;EV}|OvlarlH=vOB7`NKVd!P_ls$ASsVG}M+ebswN0+ej zWSPP12Mr|r{{<{M8nE-v)(4lJ;2>LNNff}~Vnc)p)1ENjrf z2$n^53hoPK^j3_KarZ$Kybror%!#4(974PBi@4%|aEra*@ocrSCeHSL@SfmR`4yW0 zNrFzpdcaXt7?9$rGtinIuSz{o)v`{l-uZR!AtEOzGP?@xH!I_hHdNt~x(m}cJ-q#( zl7&VnZ0}a_QDT7xSp0)3jyTZtT2=r_ase8~qZ(4GfBlOby1`WM*Dp85Ql(D$B4{g&;_b0bA9gu^Kl`u1H;Q zScPcDKKiK|MY^P#MYgD-udgVI*2u&phz8=0=@)1+8Zu?`UiU^x(ihg&_D{pK_4OK! zL9-jYNum3g+Tz;&3#8gvvFXt1iJ#t%WX+8aN|L*ZRYVEPY9l$?8j}5=&DvMBYWFPJWk8oo=N*tbJ<}i8`KLkE}{Kohn{6 zBk9_4DpTsm)mlEe58Z7uSj1Nh$BPw{daSs-Tz$Lvcwgz@j~VVbV+V=*z<~J<0poN!a49=bYlXJb+q)K(bn~QDsyfO*=S0juy?C(db9H6{-4xSOBaE zc>|(Ysf!V!KQJ44XkMvmdFQuH^s=L%!WR`=Oz1TJ)`bs=^T*J!Hj`fa!@472`PT*Xi9zB#1d@yLVRwS{Qt3vv9$;}Hilt{eMFoQv5Xz@3AE$0PhyGZ@0 z)l^uO5AG!3*MHg1E`>`pR25tF>;$}G>nBR)2r^&8z(P@aUIJr4YG_MN6?ahWczCBh z!v^teD#(MDSqQ~$hZi-?_y~8ejo^R5)Fm-Egl~_M65d#=q)FNR1)N`~) z*}oPGX6qeOID%K_g|DlX?t}AZBk-w~^Mu-Ometud+Q1MVn zktn3N0%PqI)#RMZm08k>fT9otY544hgQ`u3FZ>?be(<^6K<8un7t^$UWLC`k=Ub-DtI zf8`Wq!@IgpkF>(;>BxXgJSup?e)Jfg zmBx=3L48u23rV0w&TP|!QJfoLD_0nR*eMS906Qyg#7iC|mT=gwH#ak&L(h7m`kvd0 zu9v}ct-f!l6@Lww+TwH`;)Lx&2Vkt3kP4WzIlqFz8{4Z7`rhE!-oj4xlo9LD!&jwO@pbs+q=(`Jrp#~! zSgv6*$_@E$v>wMoT2};oI_zR5E8h2^_qUO~jf!G;S}9G34@!rW14g6aYI|-Y4kN?! zWxwd~(&fM8@cylDK=8ScnSH-R^FJnbrEx*(=pJo%{hn_tTA7!Ep2vxN-UHB4n&BhiS(G#_MOq?B#x4uHTZ?jtop$9JewTPgPhZC`J-5V7>(}0wow9t&%97fz ziHQ`yuf)3d(YNBPT?cT=@6h7Lkg^rcitL~KDVD6? z-(3UjNpUf{lDq2@W7V+M4c7TD#K3okp53AkK^kOl_ZwWTRQ;ha$6YTXNfHF%ePa86 z32D!nH}6$wl0Vu#puy9_Hy_n)ScF?|&b^Rqb}3NM6-H zu36f4@Ny5~<-yGMb&QhrtzRGrSB2yKM~&m!=^E0qk=vyhovV=R@6ju8gBoP?i7%%Kf$BK3%9oG_T6AcGZPPb|Oi4Vafl8@7D zXBYxC$&ca;uD?C$fk&`G2`3aqT5v;PdoSovbWv9n?>DUadzf4PbSq|u^T`BcH@hXR zO&*yLt>Q#0Zpq*+$3iIVW{gHxal2&6HQ(lvy1Cku99<7C_XuTaaVp}qVZU@hUMr-+ zwmR55kinG^b(qS*#G%MP5Far;Ddef4ep=x?y_s0Y@C6Jw2O8%TEFPcy# zMbT-~D>EQ&SRML+a}I}?4YDp0yA z?xp&*v$TqpoobN?MC~K1cpd<)a4!g1aWm3)5`A%(iAcW4%lq_-`C-O!gZZcL7S`@c zq#d|fDn@%IHBg$ij=C~m*Y)CAtSUcnONV(GJz|w&wkLG`4UFl5?G9jIdF)R6L6#UprE#K z6F|ev6HJQHCHSM2SJ~9DW!sy@k+v#Z;#xrCVxb!nLVq|(;^h(I#vz!543ev@@9vml zkf&o|i($^nQ7jqk-DSJN^#02R8lr4L%Yz@5M58?9F0_TUs3v-{`Xoe3Opg8FT*e0+ zWV^v|*n0WX}Hdg%@~&nH`C#r~ZY|Cr0680p&}P`lVK$wvLu!FOH()U z$Vplv<0&D*lIWzZyLkJKg!j+k>d1?+BD!kCS#cDx^HF2=piuOJEPC=3vClJ*C(36z zN~)Sm4S4;KF`0Rd8JXBFJ)7*hxn8=eXqGHEeQXgf}e$1^St&{aA z?umo&C7-ht1|YbOBGE-Zbk&)Y*XmHSTlKtOP9x%J9egC_%+>(0z9`AY6Uj%J z5@FBr7RQVs?L-8dEn_R-Ir~3XQpa=Czn;2Ej21yb{Dk99t$=w;AF!eNk*F>qOD*8S zNe7cs%W=<4ai1Wg9Bq>tTD^M0e@Am6s!mE&PnH$mGX(Tn5V2F~5;3u=iPse~5}@n%1s?GO91 zF+$^s+$K9i*;IgH4f*Asb4_=)jo?ZUFYVVAF}{}+DH6kL;kqJBIUIYjbKp0E)W@RK zioHS*iY;Q_M&3syqv>*7=D&Kk_2Z1M5$yUL^fUWm*p!P;+m)xDU(itGygXkaylr`H zV%Zjyh^aEpaZ4U55R8R?TELKbS7L<8g-KSv*rh8%l~9}7h`Rpecn`oSO7 zN0?ACDpK5U;>nkD54N1ML%S|m%rdCmQbVJ_QbW#38Wq#r${q*y>yfte>rwTvrbk1=UT=}! zAcHOIdZYkQNAD4CbrkbBOI>nErciFX#8XnsgU78+^^OwO=*rj^X19io-=2>BS`9po z3xv0*o&W1j|E%e6?71WWF18^(zG(nAE&M%(odE(j-U2jy^aS}*;MMcUMcB7T2D;Rj zNP)e1yh1m;rLMo(9A{l10pu48)T9K!Bn0&F9S{+cL_$t=zlt}0evKk00;c#x`M%zO z3-HvS1+D1-u{W#$zsVV!0tlALr;V;p8|U;m*nes;N$+3{Z_)#ED9YF&Ix9mDQ{>eM?2NvCb22uint()j?=6WJ*qD0zmkekWgAz09iklO7!^mN*B z^!-#MeJNEmetn%NsRDhdR+?lQ_r*~IVd^8!Y057Cg(%(i=~9oVs1Z0E4vh(;A!!hY zp7+Uos9vZ1DiOL&eynE042h>Hhi&{k>$&@qnw@pHH)I#n3l$0H5AlTSM?d54BtOkd z9HKZ%;HijH;kb~N%*7#tPA%z<8jwE9lMhhipuPW{0y5oS$swBf7|~G-#g?&#YlkbV zkPx!@<$!&hE3SC9d{Dc?Yrw{Wgrn`aaqty|o#CY>hf*m;%_2_Vre<#b8(mLUH5OMT zEKE7Pl=3#bdkynM?-3|oD-K3HivqCfQ3mmnRKfPEK%vN6#NV(I!RjeR$$7i%t|$o> zfxai|ie`uw*0tFdPZ@1$KKN^U8zgaL53TELx(Yeyr_8%Gp^rKufm!k^X)S>lo1?Z# zrjM>PKMQwNBCT5&EX82W+lZ1V$qgbAdytr7_B=#RtYg&! zEmRt)Qc1Pyj78K`vxe619*-h*Q)J?a@1D3Xuh0xZ*-6umEA-@Bjb3>;bL)&)eb!eZf;4nxD4?>cA3gBOOs4 znR8GHo3xE-8c{~}AWjMW)^Wh+JB|ZDehDjRX;Al$aFP%les=4a2OsGkwYN~*2|?~Y zc)La!4cuK3;qivl;S@a!ct{(93b8-5`is*~)_LYqEYA3!ZNi}u*~(Y;`g@N|!2F9` zPTNNkvkfdnqHlQbr z4PR8+A2tO{Zlp_KVy%ToMkE-Gg(}`@J7k9ziO1@*k`cH-a~3QL$>h-J=E^7;uWLun zM)3tC*3ggIm%|ryFFMC=7<|DJ|1k#D!<=b#x-mXsv)WWp-H!5(Z5VSjzaJQPA1Ll9 z`TVrBXjuPvs3z7>M?;eq92X)Kn`B4WpJr<&3@z~v`t!QtyzN6pL?kxIpH2ZPrkO=t zTMLwIx>y(4;y)uetH z18+IOq@VZdC7>(tz?$2@pR<1F7B*;fHu&1)`CNOu^a2{$M)8r@c%a0EiiC!|e#M!U zLNe{%HGYY`67a9>&T(Tv53s1q&CbgnUoF|XoZh}$xRQLgc_j9K+;dsskPrEfq_*#e z^(SuGxzXs(mxx@&pgLNf>HcGGreli!KKP`4Q*Qs7eCJgsJ%0%+)Y4Y%VRjRnxAXg` zQ}jLjE@`{v@;evH;-+5J@%h)TaaS+9_t*CHYvlGcYto8NM>|GD0Wq?DJ!uPTU7OR~ zZsT*$*s&ZjM&ECWUlS!4@HMmJM;BM}_Ivesgw&RL-|90B<_oO5fF~|oy4pax+CmrL zP`0PWiw;6q^;3~kF>?MtrFQ&VLXFSa4TIA^U#xFYEMsi!g0zB2w?#U9e^g`|j%-Hr zOi2Vi8|~C@>e2=XY1OuS(BkByIBT3{IeLXGBJa4VjKWGdnGmr|>Im7w8(sY}mh4Hb z#VE|oB3PS}X&lLlS5wx74Uak%F%d0?)^bHW6VoiRLtE0vn;9ZlNGPv`ZcKf(>f@U? zf~Ye}Au=>Q-qi3L*@AlpB?nMb*u%Y7X8dc56B3Ww<%TbYWHEA3An>$&8&s;Bm)Ke^ zkdq=m9o$PT* zbm;QikXdS+xQP57A#x1wrJ5}Rt}Oy;L*}0;E3PhJM^Y?nILnLz2pxVGG(KaY^N?*! zNJiKg((QRSjVKAIxU!4nA0V{a!swB{MVC@!jE@ab+lT`~+nlaO9!1fTrJGQ3&#H1{ z%YE?Zc~TCPsu(s8L+X~DuDm^^)#Jj`0y;)sy9=S6bhl$AM?djGoq?E)WmDWE*5aO^c|UCl!t_zm zftplpfW&=Ye<1PBt8-akzE=9)9@$%FQx5YSGTb~iRs%yiXhd=PFPXSFj`PSbF3l{-s9;N*_hvRo6|p0aw0&5#^L#?h@dlg z1mYUEQHv8&zg`vyr5;jicn)ZO=8M}gKMlN^VyACpkHSDH>48Lt9#k!n9V2>sX!``( zWH8n@_N#6G`#w2>6O9m}i+9%Mve_AkFP0POVx~dM3Pj?{=UhebLMuUA`pH&c&&mNC zs>*{y-N-g>Jt_@wGcMj1yLd$`&6)}g8$3-T?9I}ybabN612J;}eQ_c4_?D6o2JxFA ztf!Qp7gsfzmgyWPrDo-VENe_r8P7mohESOg9x^xc_c_NAc?7$wb#huQo98;crZs!m zgbU^r`~yplN%wI{baHSiW7=Nbn7o8%UG3SAkWMdjPCS@%Tksa&S~?8?i?X16;9*|J z%Xc-tsR=QJe-n)4aAje!QYMhYFHwBlpjZ-zugV{zV1<%ASvww8k-KHOxkX}rRI09# zvmrX~wJ9t&e1)!rA2x&w3`awEv0)R7GO77zFZKD&J;wOq{1!^$^ou5u7s+~>+@p@j zYcga<6O3pX{1xi3_nVsCL9zqpCf`lPtT{+C)F{gUCm_Ah-Hq!ue|Q*J1N$52zes{9 zYCw<@H#^JqZyHt(gv6d~;vMX@FBj=Gr2I_j^(W6tL%&z%|4qe-STy3nTwal=#~T|d z{I994V$Q<$xW-t&pmBPuAnhHXx&umqi)Q69gJk}eUIZCHRvyO4>>)YS<+-_RT{A~1 zyhn?vGmFYO#Avl_oL^Kt1U8hC`x-qI zf~vowHF@~aiRMa8-hPe`0xqQIcBgM>&6K7)=M!Zs7v~l)1k!RHa*NcroZZ?KuYVFM zHR-X*y@}+zJ_!vlt_3lJr2EeogTRfKoUnRDlw@l2&D%l-zh#FM@gUmK*b{lGFG$_% z+B<9W3w9i2K8Z)e!bHHRE;OS9ic$J3;#y&233Klh6Z=C-KFZyhrE=V=DeIh7rL2Rrfh)Bi)&mbl$IT`R1=Mdu z&J7_8pHXb{S{K{E9ERs>@VR!jc@BUK%^IF^!8fMxvc%^(jbRvTUPezX;}Jl?&&$!m z7V>Sh+BK=<5!QL#KL>FdXbprg`$Df@86ZHn!L^QUAg}MyZQ!enjwvQ)5$}5Dos>6^ zF}#^5%Os^o4l%RxY(5t+WNdu|SEC(e%$Z`2$(fRs;u)JI3#pmTkCRz=YvmBx&mx&uDo{ql=4+3WWAKF$8@-{w! zgq;&-GFwJ+!24|B0cd1NB=+!)n#N#=!Lyq#=9x!oxiC(d!MPXWM$d0~l(XL*G55mr zz4*I7ZVGko!|f@LzZ@KEEs_0I<*)JV%(KE9JkSG@K!Lmj4U{OLzHlz@a%z+P7njkl0^Rdyn)s`jFU#@QjWh0=@Zs6AKVc!9)lA8|m zP8-en#b%3c=U25UNuMv7lwoXjGx@7Wq zwf)YYgLfMDUO2O58Nq#778Ur06*<9;%^w8DXCKG2n#^mA5M?<*u80w0e_6ENEaln2 zSLi6nU43=dFWi!amkC|3Z46FtZcyAutBbeJWG!)i0^wa_ra}R=qOPNCn7_Xl5Z+-i8dM{^jdyRS;QDc;e)xcUg_euQ5?>S}JS>=L zXXhM@(y=lr@5?ZeP-CM=j|voulsnVy7^zo#8775F^{Byckv#vdYkm&mEz(6z+1+ym zO%dbeDOnXq*67Y^^DEUeVBjaO1z7YBvWPL!#3a&@mcN-ast+tOX-HJRK*#KeLl-0H z)DgY6N0|1nEoiBVAeYMPi=L75C7j@SGKbKm%?c4`^6=D!l#?iD5?Vh;l`mU;)0z zNtF#?6q4HR^FepOi=;{Blva^j=R`$)fy0l0NlyJK zrsnzr*iMvS$ikPjO!iH(wr~}~eX&G1R3qNYWFR(G1oEXKXcWC?Z&lh21>Wk&!fa7& z=u_OQxiM+-p!r@%#lIgk-L|&$*)2r#b!Wu*F;ClbDf}eU$etA$)0+JIw>Zgdbb?bp%qY<#-6eY7$Qxc)-V4pq(ZFGRl?U4Kw8V?B;NQKT92^&dI)2Og8_G4X}3%NJ%i^as*C6Ld@= zBBmG19_rafl-QX07J8$Cs$jJBkReEf1khdX!V=JD1)#?CSK3GI^%>;ytPNII1xzbM zPBWWOU)^U)-7-Kd;>RvZ`__*se>{08woaKv%n(<=5rwg894@SQTSm!}eq4aivH2&Y z_bzo-63w(S8V)72X<`*Zd5;R+FLWr=?73~@ICUw;S&uxkHB64926$*y_;Q-=>p{r& zBCc?$)Xbs|7~d;o=C`|!C`1G65h?dtqkwdJmvu4(iTpxhjPgCaIbO$#i&Z=f27fj5 zl|qQ>q7+4W_Sd4=lqAJT1U~Z_aq!%ZYYu-QdQi?awMM_a7a07=2JI^sLN{EH+DuwK z(c~lE+EKQUe!WzH@NXeUqpFT6GUUn@`bU(F?$OuHhJyEMJ%ZqY*SRrxp&im;Q!Ov-0ZCPQlyOji5 z^r}Rvm~9SBp$rntQGmHbcs;c8Ix*C%zHJ>i!nAW}>F`M?izbT9Nj09pNp#Lhxo25l zzRZ)oERiZb#=wLr-om}La<^myTwm)+M)Sy^{bYNYBCYEP(l7H^Qfzo(FC%dqfS;|{ zOj>`frj|kf{Qut@=6@A39xfC3P|Mg}$O9Sj_w?kIbWlb9NQI%2EX95$I~K(Pe(d@?VPs5v z6ru0lU?dDa3pCmo5T4uyt3{fPfD{zv-9fPNH}@(lj;CKCA?8Rqqxm9P$V9t$-fUp4 z6TLXZrTPg*?W^4tp)9hBi_XKmQD@AZ)u^*Y8B?86>%%9IMp~)=#AQ@P8-aiFX5dmlSGf1O{~f9J)#2hW zUj{@$MT9x;ee#hG3cfdb%!qUbIutnoMm(5=OV~bNH^PM1P#usN`~5}rKeVXFzFLM# zb87Z~X`G@{wcSi99_eh{lo*_rAzq;3Gqo_@L0dN{Uhu?$z44V)unRB5(;iq1GO)51 ze(o0h5&IgrOsz8;%dXArc?9&PZPYH9x8-_FF>d!B0cxcP%C;_*Ktqc8F}b)jCBTp; z?csDq*8Zs>V#9-0$55E0pqx&uK;-EbUO`mRi*-GOZ&e^Q_WeUwte!vNO;+F#E5}I@xSwfi^ORg^k0N zK6W;)U+$o0Efmadz(9;r&lF6@a2D>4O{A1;Tt+#;<8Q=`K)+kRiN=%8-$Pim5+Q@xji_7~I;-25-0V&h z$c#znZ~{y0dDSrz-}=Q={GQMj4I)6Mv0)p=Eg-JRR~kGiJfmv$j=ASER9NIB+Z>u# znzo0TUHyW&JI|R`wC{`26TaL;6~JV06OAzrcU*Ff%qI5z(;aj&bVF=q`XVx=^Sm)=Oq~iObma$aOsO)|BA4%D9VB5+7a;R_0_Ayrc=PPk z+nV_5!ibfPl8VdTg&}Kk4d2XnwlXIsG=P~)4(C=f2q6V6Gb|DESY9lGdC z3cqfINpOUAvi1Yt(dUL?JXwEHj*~aJVT%_6@D9CaOIWb#AlL1oITmZ~IQW>jib(HM zfX4MP0Izz90nnT{lpJ2XOb4wwflg=v`m7xzva*Rm?v+xS*!HP^g?f=JK%lSHk~u;L zUB#+fg-$#EowFuE8IquhNqwFbx#FT6NQsNY zql@tAPHnw$PylK`mA|96@rjCy6E#@KW`H#=k(lvLOd66XyDb)z#!smeojJiLkaggt z!7WF8V;`^LAJF637A(t-euOAQo~?}yAyIDx>vT3LvTcQsQgd;>iKI7(_-p3z6dyS< z$1+(Fj(Y+QyJJ+>9hmKBGeFYUGgLbi?N!?=(JtL$Y!(@bX>h6XEAO z@zdd{cj%|XUhcr~`f#j0TdS(Nvmi$c5`*>9gf5ckxVP3+_t)V)_2)IdwJmS0dFEquQp3PKT6m5K6XcPJdZ9j2#p(<% zi9zS}q@~)%l#(7AuaE#`+dx*+onj@da~TJdn^3kl{w1=uF)j2OAAGjpX~LF1{PL1^ zCBb3}jiGRK$?$a9@O26Bad6S3yL#tU*HJ)o zM`EN)T4B7AD-5P`a`0r24+s~ z!6t%JHYEiAStg$3G|_;!oa~kp{F7#vmBc^?kZUc4(sdic3E62(hkd zma=GaZ)fCzAo!l+5*u{( z)p7RLUI#C>oj_a3cQSLmYF*8<++X)x*b7wseYwyarj*Ieou?t*zq-6q>cJp*Lo+LZhCq ziWF(uBL8P83S=Fn3qa-AJB`FpRKsJ>wl25gbmFRyZg{yt$Hiwi)Q)G zuEFaWAXA(#NQTY~*vgGYG~i`M$&?3)zt9o|36hPJ-_ewOqDFQz$#DiP)Hz#T`^-;F zwz6f)?NOIOh$d*Ugyt`mG;ztJ!<8R1wEUsvSkx5fRN19)?vulTL6HZPg}TNg*E zOv+yg4H`xA&1z0z>;LyGoM}R}>prjZ?Q?+sB?k8u_HP}Z`*oEkr~f^-Dt(*oazCEj z<#uZne`ee6Lk4mJQU7H-gIJi@eq5Qku`b6@r`Gt|fP{5*W4+3JKO4Jr_-S3-r^EC+ zCN&%lE}OFhOv`p%88+RX?I+p;!d9X}+Cs$D-4te-HMC{TE%wU#>h6}(6rxL2{HLHG z2_OX1`K5PF_D}RP#ic<}o0AgkssOk$4)c;P?OPR54M{5fU}k5oj_b7nyjE~webUQy zuMZ8~D7|%qCmIqnl@5l~vi=^Sr`{JkHM|}*ydE{Y9yPokHM|}*ydE{Y9yPokCwLBr zE>&e2GmpN`i0WIIQr^m0{>nC28^nN^zvCWS7Wv7Q+!7uhN)zi^Ge9Vs>+#Iz_RELV zUr1?~fCfoCVyh);DMA{hK|z`OL|NA(W;CQ+*g zh_SSOtlFn(j9pA^(kH4Vl^@POtH@N$SB9A`<1+ZCaL@plC~1moI+S%&O%veevT4I+ z+ZwW(^1Hxh3QIPin{weI>G+EnXrUnT!4*3xin^L9oaC$8`m7zrlwV+{3SNvwqdT(a zfryo9a7v2S8pETBPF3}#qaUjj5_e7WFvhfn2G$wE%GR5KSFO`CJA}yqwNk|`DLHQf z3$Rkg;(AWy^PN@8&^L|0HK2RM_oC%w4C(h)wXHyGqWcZWLngIVEA`>i2tfMUCpxPY z7263A)vfcIg`rx~P6z8&kcn2cq?}|F`n9@})yh+qTso14+{Y3={OFxMgX$CFfla`S zjFiLGv2-?-vxNf4v}Q-%i)U$D(igstwrwCJ`F}vnB5V!gTG<08>b8{Mog4)_&r>Ej(< zjCz1=`PSoz^oQr{pPrQBZA5GFaf}Ca=hpBStaqm6m0k*=;5{;^UErIWGNxG4G*t)p z_U~|J1nbWUMQ!wu>dps4=O4J}IiyPdHd3;*fp`+VSDIM!hj-MSohBkuD{x2AcQ8oA zuTZU;%po%AR6tjW!_+@tyapfMfr*%=@}uHA2xMmh@e$O)Pd5t|6Ifk-1e4Kh-stg8 z#qc!cbK^mC!UGsFT>vqI758KyFZ*dz%fC-Nkeq2rLQHE_+)iLfb8UfzCIVH-{b4jH zMHlnLD86Ob7^gn&n7lw$bdx4%_^#0N#yhs~zLrX_jHjVzDGY$Eyb56|iKJ8hywZ?| z;mnb5bm*Su(tJgvIs$=rOV1i)_LB|Ffh|{lQ+&1EBRT7jL^zJ2lN~{|V@OT;i|wa% z{Wt?QCT8<--fb!ou&?avJerF&!?O8|0(a!y=G#6#UB@2)-t&lic6`f&vv?2rZ-j>} zurTGlwSLWS`LGAA@ts#MTc)jL%sa|l(KFx6(Iy!W5MSRbatD^MY7(wgn3)tr{i%!| zPp}Bz7w7p5dn+Lk9u{)`6rO=<2{2z8*e-tlLX5_RELhua885Fr8sjKn*|aNp&J;}$ zqpSh#{%Wxf_wt3N7e13?Gb|*BJPuGTs|d>%wRF2rfBulMgX&e{xz*i4vH2t1>b7w{ zIT)M#7?~PjOCO9GGrx2p-cetjNj@m_0VQ%xz>a8z4b73d?8lSAW$`38UiKW0wyOui zvW-6lZHh{n`a!t}{HZVz`nC`i{2|65+Q+)XAmE3go&$c4k8!Q1!SJ`_J!-p?CxG$5 zE%phyV}W2Ea`GHUDV0}AOy zT8rPV$q+1U#8A8GJU`j*!n z;D4Y9%G{-7I@z%|6e24iC|bKB9HV}b$~R$zqHf)j86bl~unDcvBk*`bLXjx*(SI); zhClhYK-eTj_<1YYJCPAp$vl^)&~ZKFW7z&+D>OE~LXlfHxcWtXY=%o?JTsfYKn_lF zWXzZHle2l=l1bSqT_(|Y)w|avmA z>e<4<@jcQ`CB`hOB78i-ijRvf`Y6PQc=yHGeTU^AYIY^u$i%FGm> zAN~Yyzo+Jq~fHerl( z&-XO*rY4?VlyBP1PH#zp-74l@6w0>EGSDUii5{GfOxlK3e%B?(x$+-~56&d-#JOpg zWej3mZGPpqk8`2h9!tCv=o!5UXg8_T8$pIBFg`8t&0FiQFsfFX45f6FJ;DZCdr~Hn zhb*P8hOL=JBYm|ZaoC3-{c2So;we<+K+~NEFZeZQKTtOJLYQ0Qi_?{em#XeF$FxeQ z>u+sV^8CNPqxghxQcFgqYaihTgt9M{No$WMLH?ol7d;{PUdrQ9gL5*9{4GwfAbCj@ zdq!5OWVSTfv9^TR+e&P0rZzT{7<($Q@!M$!#6KK-ReCTLUC_tC@hzal;^kq>KZ#)+ zCBMIp;EfbfVWBvJZS7sec%veOVwvNsb`>FT!Tc51#(8os?A3Tq)#XeKP6D;EL+Kf; zp(hdfsJ>~skt`i465iw?_et>zmFIm@jqYn_I3YU|j{I-JYJP}j0av7QFVoz=UX9cV z`s34{LgSV&>t!FUkaYom(&jh20a(LFD6!{GYao_e6JT3yzbOzQ^qUfoZ5eLx zv|R5;EC3&=NCVul|OzRGJgDQ z(tgXC9v3n^E@XLC=%~MCv>lw(bU}jP#BMILCTZCBAVl~w!hsCb1>q4lB4DxyIByyi z3QHnZ0XDWx;X~f&z9u6Ml)*YF=E}dCOHPqaTM>G8;uxGQEhP9Sc!IE`9dA*4Qn|xC z6C3YqX-}(epa~Q>86%q*K!q>RZ<&c!;5EC)a9V|^L5C>sWMv9{J z8IA&HDVRfikz!QM@u8}&)Ss1PPq|Udn#;~)!)sDnCBaK;-egP>nUcVlsu5O8>wVI! z_=hxf;w<^ishPsb@4x*slE}o%{sa)eWuKd0{4??&U^yaCJq_~9Ge7s#QZcVnauDHu zwbGOOs9g)$`xt!aF)Sjg8(o}Ru<^<<5Uw$?fI!DeFcF)C>Nl*TB7{bqQEyD~6`rW1 z)yk9c*~swURU`Y67B)bb&W?*9Rlwtx@haPYnf)du=0E@`^U4z+oPK#DcV+k^87&B& z)yJ8KlQ*g;PgDp+PfQv=>=m3WRP#WffJ)q9r5XTDcO4v(eCAm2+5{h+{x5i*4|t2= zp7$(ed4AYn1Qp^#c$Vle>6X7HkgmKri^t*S3Idho_7Z%}3LrTfB56Ayv-vw?#smq2 zNLYwo3I#ds16sQdEj&%@TEjyld8`SK>hA*rsm^79z+NzbG-dmFGT~!`gm6QT0}N9u zIM88;UP^jqJTsJmcNrNTUX~h5^2wf()5xDK$riLs*=3=BWS=XIF7IDngDuie2~cQ3 zKFwVAf)u-*RVbfrNb4Q^l%Ck5qi?y5@mVvZF7T%M!aRa6oKz#jS!FR8iLQ}t(5#*lo7n@D=)D7#E&jDL+EqUk39bG zB4q?hKUawNBS$%O%iaSxk6zQ-3pJb+>Tj~R#o#wnE*90+A3i1A4s z@D?hSipDpf9S#8}o;}lZpXYJ7jYj>Av+)^>DOfa1NMxwAau?{|DIio%LlC6|Hqse= z9EJin1UVT+u`b@Rx)P6B2-nr^jp2EbmzWw;+_4CPKYq%8sOG3w+GX}8;U#as%4;PS zk1oi21|;)nSb|<)?TR_e0VGIa;CLlhC;o=B3;Ow}=-R*aZdofl=J&5h-tU_>(Z0bp zQGklvhfwY^c|?^={a#5RchZupJ%pt{P!RiLG(r0?L5@O|N0mxUyQyy)K8|ojzr;r) ztS&XP5Gdmp%+N<7F?+YOG}kfc$7W$$JzzaFfGz>=u-9zSD72_(2TQ#rAlk!}9)Ej! zPzTtCBYnZ+NUzKuUjX8RM>uD}%9awMRDD?UhG&`D>DfNhGv_}O=LFjjj_fZye)&mB~bmk1BIIV0bTfrT)Ma49a|R`ZNQ!VNwg!rJ1i~WqVUs(A>9hAV%$LqZ==WGX$FUYM46`}4G$9RlAUrq7 zj>#1Kj&y!$xTX<9J`|NUV}&n$0biLK{IWvLAmr_oh90d>9(j`Y8JWY~$g zV6+fuOZF`c#FIAkN@pP(Z2@`+f>DsbC3r{3T$FhiXE3;tYvArk3WU1fmDTwcV@-g+ zkV_tMrgGL#(Pw}!WY;#$10Da6Vqe;~OcU4ERa(C9Y+iqjZ^g;tDU8>A+>R%Un8$9 z$KDdNBi`@K>CM86bcNodyyBKr^u$z!tN!K-|1f5K9$aC?CPPO~h{g@@i_Yr(vU;g6 z*C|P;dh<`;AKN)sLu`?v&z}m&&R9kIQzPz;HHgfeOfQI0cWlvi38MBXD}Dx)Ho=){ zGZ}B1f_J$qs$Yg<{!kgkWq^Uj=RTzm%n&!q3P z#$(0U;{2B-sLYkM@{jQzn4;-|%UL#Y?+pHtZpw#;sH>os^^cyP5e_asuT;QC72FTdL-#?YY!LaPY3;}LOmVAWcxV0I|cTVG_zRtL0< zyfy0sES2UF#!mWzt?BSVFdt-w$5v#1OKNV*K5?)gO4z+K?#0#RQe z&wkT69+B+&L~>^F7nBD?aA7l%=u+RBSkZ&Q_00d2=>NxiIzF8ARibF>Q%66`)5!cc zvci60qEgjs7bh(Dv$i`|Q|q2|Ecle(jcHp;${Wh=8oA-mvzjw)_T`qo_Pf ze*t_aq*F*M6!DThw$O9UB>&LnC+;+xqb+5&_Bl+S%5{f4K{9`-(HoyDJb`Hr&vRQ^ zV7uRh_4Bfqo0dvdJMKtQ{Tp%K0ykE7{H5i)iR^OqhqNi)lCr*7nRczfZkdbvKHV@+ z23Lwvu^kdn%%?BkQY-MIN)Q$`^_7a%^@pe{X~@Ab8l+x*lTtNOsYK5`JT;{D8r7Le zLf(*wy19P!G^*I67kp)p#mBJZ(SE)ZpHZZdD|}Bq)`rdaeuzpSwuHwhx>T;lX9iNA zu2HE#T8zu*bYoOzEsSc5CpxX^OsP0SR6sZ@3}~83l2lPH^uKq=CzB*+@=A#7*S*=a zrJaG2*)5bPNClw<0IE?fLa7j_Mwg|9l)k`~C{m?Ln3;f$QE?KgG7FRB=_H2wQEo(G(~Mf~WuhA7zLD0c}7D{r~^>*=W6c`_BH~JHXyH@OR(` zeD3$n-UjeDg}d-Ey~DMVNZUg5jVsFDVPXlu2y5|@b+B`gj7op;mXAm9@IymvYBh_; zsJQN_907N&20Yf_4Ua7Pco_p^1L`U#u~_BONaK9YiMPNHGm?~weG@+?09OCrdO3XC zUU{^k=Fl!PU^}HPX==`o+-kGVFQ^EdQVNrV2e1S0LrnTaXLE&34~_<|&On>U*rXD9 zTN9jOY`&Vitazyn@&TeJ{lNP~MeOmzT!8@#UO$n!lMWblm(cZHu}yavm{1CJnMBUA zX35Frx+yvD_bzTL@q+nsx_GAAyeX}>g(Q)6_%DRFGz#g+qdE-ozW})X;`1j%_b^^< zVp8*o;#de2Z^3c1c&|`5?Z+W_nA!Ol0zwz5IZT^LhcFQc)+}1{;bDK!jcidNY_f)g zD`ODqCg|kIkq)I&Yr>Vq=lFsZfTvh=#sncqi{16Bjul6i1(ulM-|%A252}~A>twD3 zMgAk}WL$)c{WMp{DXofDgY7HTb2Qd7jpNU^fw6FB0ICx^+hX>Z-$;}uQx?)DR$U1H zq(!fO9R}x-PuPXmiAKtDVvxkHeFiJ!EZca+y#W^d=7v8X(f>Pzg zI{$f0S1Rx|CVXo15a~R(2LF34fRazY?aWo3KgXD#;GVhR(}*3pQ7DH7zPyN)r6I$) z+>ql4QcwHtk4ocoR@@=4{B15*N-7LK>;l_oi?gU(|F2$r{rCRLDO)X>3oTK&eIWzJ zZyrogt%?hGORg}cq^(RhiL?4=-E<6S-Pu*Gp3I9k%e_enGRY9(5z4c9&^CzwVzMgE zbiGw(?C&i2y!H!+Fa96%F&`E97>-&u!#x`_{Q2jjea?aC+b(&uOT|3C?T+h`+Z&x4 zkmpx1kYP|}`=QM+ykYT{aS;0YsV<$ni{JeV3iV>7Z?zUu4}K{R`vVL;MX56=_rE9! zk_i=!Hcw_+?fM(YFo{K;DI}#6*O+riS;I(L6G&Xiq%L^Jrqjh9oA*VMU($+^dc>q% zQvHRNFt&TI{IdU}nMyE!y97TZSohN2Vs9lPfH75O{O06>^FPRaKL!KFC2_qdS_A5T z9&v-~KzDv+KmfM{d(to<4R8GT@@P&E!vd6&_ey~QmYr-)tn()qaP86tjk^Z59jV~q zfLpP4(wN08|G>VS*32R8#ph8-ucTxt@6 z9L*{aUO0&2)T29WxYEfQzywMDV^#j7180GDL9xNIS~k=ft|mqZG=2_}F^L0PSKj_S z5k#H{ZXa0M<1<%A1<&R^R?uY^B>RoXn z*2Y|XLdb+IqgSD>jw%w+hu4V2rU=*FcAZ8#b+R(ha%@$imd-0f8Jt#`lV6IlevmHk zNweJgFb1%M$d<5cq?b0BfNpv~wS1@H#)qsxaP=ik0^haJT@|Z=RMAE}$Z_f`w^9XQ zh@Hy^%%WDq;4AfPZsy{?6CqK2N9;*^Pqc-pL4ZJTelbs!H675g29#k*x7&$cX_b#yw*l>x1y*GZQ( zbGAL{G)I=08=qebOou?~Y#1;rt%T*~H#n^&>X-4i)qXbotT9oofCBE@JpNOjdCA;Q_1?Ntnjt;LTy#)IKYrmBYQ zKq_jeZsX}qRSQwI8y3{L%%fM#iHj(cUnIMJ7_r<4U@dV%!Lh;IB5xTmQog_~krPws zKt?>1MF~?4B(Aqmyr8 zJA905@cjCz*($U))lo@+RW(z$v~c@y^QDEzbkwFo4wDg&EJeGhGpupir+9|qjS&QIEFIl%tc%r&(Z(ps&F zKFbk5B0OYmtnQ`e(zeEZqZ}0Xe`7x)ue0-VtY_PGe$98AV?QPOGQG29EJ{hn1FRH+ z%J!Dq6AFyTaCXTCdf7bi9){7brnw-Ouz3=>*O2yBAw#_qss{@j>*IA#WXEgIFn{y7 zs#_||Fj}dWIb5zUrlbpc7yN(@gzwr@^ALz}D(x8)34540ZrRjIHUM>M27}^`Ksxih ztsRE?R{a_mGBuZ?*WB7M+l2OS1A^2c-S~M)5+TOB@<4O9ZcSiX5*Rz-k-ma)w>3;e zOT8(;g_2MFw-rv7l8<7(jT#^m#RDAV8_ za~2DPDZfX`O@3N5tPCJ05Du->XSSX85;h1{)c}Su|z=RPgX3xX+=JO@h0vV8&3Kcz)=j%Z?2ePomH) zctD~+e90U1*&k+Lpp6R6QSO`%?Ek**P&W8P{eDZuv6aZ|sbbiFX`w-(?->}3x@n*3 z3AIv&scAVhEm|4hh#yf~+^7goQGlP03q9K;pUR@@S)?F}1mKHMwndY)+#g@SbNbdVIRg-SEP72H4)QL<{e}u> z5Md42Ka(57(;Pnrg^Sg*b?g&P{>GIN>eC5WJk4}{BPcK%%9M$rL>DrCj&=ZP6edG$LXE>zi zEz3DbLC|F5i#Qx#enolrqx-IUPr{h>Yz9J`makOMc(dcbV#aA4HG0yy; z61bcM4Tm6ynh>M^VMBx10e=|&b?+>tU)};5$Oa&ypbCo%USjPO2xNbmaRf^;!NK#E zudqgm9A5=<8DCz95O$_k)Zf)sT?t?oC})^-^2a3^JnUOhB|9YAwrRCEs&=e2HW{Qh zgPbqL&I>Y?fTkUxXa?n=Fyw2Y=rc~vz+js$kvTX_(lDJ}TvY7Y&OQ-@&2$429DRMv9A&IBpT-|B!vW$K z(!qfm9$JtAOM)9k9HH48`bw2Xm^_^gB$Sb)bJ_-~8I zGF8O_qeNqT6!yUn4*zCx1I_A$g~;{Zuwhf5!1Ol-tGdnhB|!1`=~M%6(Xv&lIn;?w z5nTpgtGOY*ceJ?JU(WB3|69Jk=I`Zr5pMhLt;OASI@%#Bb%95an}yi21sjr(CEa{&9Sg-xm0qrhRok>pTOzAyiz+4R@k=I*ac{ZVn^9}u zV3n~67n}QusOClt+|5yH4E`l3z2M|i-05G~GH*!`6e%Y+{fc5iOU*lUcJk&u`E}9l z9JApkLUxO4TG|wOSVD7VTzLMK{9$9qfM8b@)>-1V$$|mWy)lYzNsR4&9CUzo^IRV+ zm>n)VpMs*j^-40CW7+kMYCTc|k!gm5ch5QEzR0}IZh;FnQ_d}DAmcY`D?zNCg7#!F z4}+!gvMXaPlv)0<6KBEC@1xSCMYOh2D4UOAKO~@&AQ9)32j;`E2~x*+QBdIeCU80i z*PgNl%<#7*bW#pkv)4Q#V7vO1;4#I3zu2g?9g>S>8z}0+bN z@+r@W~ESVcB$D^qY=sN||S9{kH75Y&xfHSHOy)Qk@$9hXH>VdKFoN{_qQHyg6 z4-od*Z}(LsXakL$EAnXZtJ%tK_)P^E8IDiRQ|uaZ?tjM@KE$YMYT0-kRIxC_K{_V`+fF+0J;jBILtX*atcoUNc)3`w z2&_ykSVn1uskB&Hz090j52Tw5HfIFew>Fwg0%0l;zK~LF0I@;qeV#Eb%ZG`0wvxG_ zuyr2hNjNL-njd(f!eFFR9S#_5&qW{xO9T&Om~+b-jV2SDT+@oL3|yx{FvW&e&EoaZ zT|L>)p9I{KK08RJ4&03{Z~w-PJX)c;DSL#OG9b$OK}iu2E9#~sX6B1t<;pDr+DXn| zH{-}L=*3Zf5kY2|@*{s9In+?E*(9QSW34@%%6SF#b4aB$WmCb&z_|##3YL&X;ACTa zj<)>4bMANK*0wJEn8&AJG)Fdo+z)fW!a#0nSCg*dgzT6#0K0B_heaM#U2d{ z94zpHcepNrZhR4~l#41oq9$>L!=?@F+XJ}rMaS-TyoYGmy%>(7yUptxnr|6-#3wv1 z@n(pfN`=?OS-eE5{Vq#4y+AWGmT!%&inD%ewcVlvbgfsa3zeJ2aSM{sSQ=@U9@4|w z6y@;UH@5;Phfs&~(7Rcle$0TUoND!307#gd7l6p!F~>)@jt{?+$_n*7(v|;)82`Rf zUa?KRqC6QRSHKrc&g=Y+SKAI5Xx* zI5+}WVO_Aos20tE9c#+}4`LtxpEAgg7Z5BIA#jBj?#~z+^Gj{N;L~8Z#Ug3=Cxiac>5Y zm!L=Zr=Aq>K1h_XsR9GQvWZJt*JW%e<~SswFtBBcT%?QO&(HoOx8Ra3KhY#sXVn4e zLD2P4MkPPlGcAlCVG(A0ZrTawLN}gpr)p8qV`ngO9wa2f2;wFKLb#@m?TbQ45h2H(>7*7{Hn? z#q^r}X!JNvr=~iwNt(@}d`mmoa)!#iC z0PmHi?)yW*#UmSd2#}hcXz{G?J_jl|Pjf6*7JqNZOh1rwUr#Ix>to59r)f`lAmw#E zBN`zXAoW;nAn9XKDbXGKUk_-JzDN`MVa|y7c?^%;MJ(% zoT4*G^(BvqvaeXTY@Nj#jm|~2tMBM$l-o@sB zX|Y)M?k!R6jZ>FDDqO@W@n<*Lbv8*SpdE%%dqM9dWf7U_q;^k9ibrG|K0Sg#qXst> zNn8LNnw)^}S)a+XB<<-Vu=|UP_6O_JMd-G9hQilV9Rc9`$I;ET=g|KFdbOUjO}o>%!`RG;hZu%(?65NBk=yZrnsu3?^;RzpGx?Q%pYe*l~fi)PW0FX|C`s*)`OY z{$Xy+xhu0>Pr!Z++UjS;vJo|UZ9(GuDW<^91;)%m zi=<1m%2{i)BSDWZ-HQv~u1O|XZ|Pu|60u|&<5K8~UYhBWl0DOo_D2|gj*v~I*5!TI zm^Ve}Ra9EF)Y0`$L2Nd-x~+|Zy#}gU1ZtLpkcj7W)^-z-$j&;c?#S`ghNnpFx>0;y z+eC>9z-N#)u_62>Yb-x-LJd@E)@(Z_9V2&ii{4v{boY>;MpEo+nukLGR<#_vp~YAC zo(rh^Tba|I`Mg9V=WWd{=hn^~Yu?nYnbP=08lNDK|k=>@Bsl1B5e`VCN9GPS@Gxg_Lz!jT&O}{1uqbMAfNXpW6#gVi=drsyetYhsL0(; zn>D;WVry;PT}@|E*+{T3K5zxE6^83k5UwxtW!N`K6IbY8kt=60roDx*d=>6N{vk&& z#ke}3?1!{9n|)SlwEG7F->-iq4(Kcoud~h~L-w6)v$zy<;rgON0g7RNctqbd>by9A zylE>v-OnOck{-zoE{%ZS^VJa@t8V`o^;=|o9dY;d*Opp6MQXweqFBRQe|`gLBg{>G zZz#4Ht+X1|q`5<1Tu9UZ{6M*3oNI^|#BbyN_P;nWqg=) zZkG;>qt#qLiDzp}{ysIlJ}=akeqWvm+6!4B592 zGd(xiCK;RxnX5IKqL$rMQSNlc&Y2ey=ZA5fv3al=)->j=sruDfgldMM!dy(%DZsAe+M zO`O(gW7zXmqHPbFe!e}@U*v&U%&0j>%;Fk;pR{n(%nzhO4QPR*Y*5snJ3MRL^U>*? zHQnu8SZkoWkLg@PS|y7{pQ#n<|J32P#MP85OvE<#fepA(wmg`X==23#J`!P|Twr*P zyQhlnP6)KeKL;_aAb13HeEOqnW2m1q$TQ!1Fu1A+zs~jmhfh_~>mDtVlpW1v@&$~e z*FSUa=|#L&#O8VF;ue`{>X4BRqE#tTxP93uuI<{h+ORQSqy??@(dei^CV_E1brzuX zovl5qe|Lu+14Syq^7ojNz+-6$ez9SfKtjgnaSGIMx;`6@z4*Fj;twcLA;MWny4=O= zpME&cMAigFvh&NnNwC1%_T(k6Y?e>|YuPkOnU4`S=N0JI5g;btnOsMw`uy{-=yxfG z-{XuwZvx{hoQ_MUdBxB{3&)liX}6$k2WxnH1bcPY3At_)aBze6szB1&0;CcB_0bIe z1$SyofwW78{%-8EkT>?QxPL_?8Q^x;O?G0$Yg79E}QiB1Q zYNR<6g5UU@7X5o#k3hStVV%J4dE*d-wZs@67Me*dW$q3O$NC|LRKv_B3tV}@YX81k zdZ}x+>~80B^-kcsW73C=8Yp27gEQgCujX(pJ6evUa4Xn7?4zLo>>|<*t*qU~&s9T@ zEN#`c@izE>IQw*cm=R7Lf`JrD?L4hmtrSizUb*^TOgmdADLfGso1g;YAVIb5NE#;0 z9U92GSD8H4d!szn-C-eq)!MWh0^(QX%vLrUEK7@2>2(&Ll%5ZebG&~rPCZ9nGKe+Gt{qyh_72?mdjTGJ(heP_ z4bScd)X>ri_S?Z`cK9id~`$DSU^sW$1xkaiP9JZHW8Pv8sHeSF+v&(&TbJ2Vc zLSYiJA1o08A;);Eb6roy5j7FP8r`eF{8-|m+Gfr-7hMBLMLQ)5yKxYHC5lPyNfm7V zl5L`-Dj^Y6%2gEjQm-<-|NIyyb=C|KK%%Q-R-uwxJl-~jMmIbP=bx5|z@t}myT99j zMx;^mmCb0;7I147j7l6j)3Q)hV>OY#E0L0aD^bXQe_D0DjOmjn5~^x;Pz8GY1?JqS zA%MK3moScTufu9wd*?(VeBBYMj{z2taLx(&Zm3%9aTA)GSJF;*ubVqo(3o=)wH5-~ z4~a;)q*Gc6N_jb~wfdd5Ox9pG;^>;OmK}g+R#YBhn1q-m}&%`LwiXSZ-6aIE%k`Zjp?<)5zgt z1uDGJ)qK522(U*uroRu^`>}!1Vy-AzbRM)_c_$LV4NBR+rG)fgVA2I3ShPmUApj=^ zZA9V!XgE}Dnaw{Lg5skz$|Ly;7($MsJzn5)q-aG3e^3-i5Dg&`b(M)f34lP#+7;o@=m0pujr65})N&n>M^4ONmb2C1W9dShaoV%8&eh1_m7T+nY+AFpxTo?Q zWyy=%x{4t=W$R?H?!!49DjT2G5p*`|K@7D1CE~nUdkKDRM&rl2hV{$YyK3`+4XnP_ z4|K)c8CKOBhi!YF1GRHlkseFcd6hlt;s{~{RRz%p6$O$#&>ZW`*UC{#tp3SKdm$A` zMMG;li(>c?`^p4NRQ6|khjmeo)T9@~5PBwb9n^1Z-q6P+lv;}n1Z`1m9HKaVCDpKc z1#rlDiJ4>dlZfstuGsWl%kYu$5F_7e?iUH>Tvm#}Z#xERFnML0qq^RU*aIURun7_e}2AO--IA6)$CVpUi4Barf8~o@h zIS;bNG3b6UxXRrVaa^%dDG~-t4$&8vmn9@49Gx2W<3{AhaSRMMH+=RDPF;3p1=rV6 z$ZM+pME~nr=*Qu5rhm5p)t?i6Guu_xQJL{KOino=>8Ik^BlpX0QX4%qDCVmkLQs-1^*vTFEsJMA3AxY zjoIiOF8EUI<&B7|ctYjb;tvXa*pAu26{`A}Q9#2MDAP<6syD-hw)nHT91g#i4o+fa zDq%K}%x6oHUX-AXBC^eXgrOFoHbjhrsQXMDWts5)0!WH(T99X(J#)Z)c_0`)c12g= zwo(Tnv_2^_S2W2clC`MqV-nA0SuQjB8AnB{y%dNa1cFEO227f%kU_U{KBc)EMNQ83 zO0Yr~!W|Wd2A#0?Zi%C&6cmxzRM0qx11cd1WDbE67qZ1)O3nl$^wTrPf9V{I&jQhF zCXIr5XubMJyNM)`5@<$r!O!phpWO*r6noz`!O8)O!6>5&gY**)KZo;{;`>`8*apr$bHk^;2QiBL zukB22m8}u`g-kvmqwbRk{+|YEuaWr-jTdF~=FATB0RlR!7uQ5a1I{6PgupIm+(>FD zybv%{En5qc<0rSr-kJNSo4wrWOrcg_WE8$@?|gX~Q=pkc2FCtjOP|1_y`g=JiBayR+B$*bZk%2Xr{=`G|iq z{bu5fvnG!*=~=^6Z^54*w{BY{{v>SL?>yY3P5;3NbqxB5UkJ834+v*eKtsX1dY)?M z+YNOFW7qT8MTyTcK)=A#N`S`a_zu?oo=MJ`veh5fgEsAN6Eg8-8x*xzaJ2}vpOg{1 zv;60pJ(ZSv)!mTdzFA9+ixA~KSf$t~U&flrdFd%sInJLFH5;{aa-tr`Vb6jc$I_xs ze+ZoyhLuM9u@MPt&zqt?y`$W-KB>w1@m0HH*peRW(=VLQaw*W)s(tu9y)R%=ks=bN?kUh;MvpQ&yk`#;tAF`}1--*&-rYrhfqS7Bx&?Rw z@b9ob4rZPXrmH><_iN9Cv*qfn#X0>meO5zz;){ZfQ3alt(I<#P_}rFgTLSIye~PD0<*M(I@jo!gZv!F}{XanQ@5tpm z*#d30_)0APMuaVY<;5m(ktWD#tjkl>WsP(zhNkfQbAaW*9D-5FDBHztNr$_llUy#e zu(gwnN;dx#wUV>)#my5vqVQ~m06##$zu3)7Ke}u57#@z+7xq3>oA!Rjg$4>gM{2vX zE27vBokvXjn8oU6m_Ls?sD1#_Uz)iws^;&j#VvXkzh~w?#kTW~t{a4u9FG?kn;ZNi z#k8yu++%vtiRe>u;d#+i+x6f>Aqve$Dwjr3N~F$NydkqTE1u}n9avc~vC>kg_vPZl zfTGe;q+V-B@%1OGjI@4XK4>Cf8w>Kx%8Q=P!?dF2;^UK&e{ zZlzo(&UdW?g&IA%-l@_-%0w!LA$YNu&Zj%o>DH*`+d$41&~!vEtikrTD?Ck}hU?@> zUH?>*O;nZMA1nvKvkIBb_SCi*8>p496J}P~=qxZ>8hc*b_L`cf{NO5+EA3Q^8r@#5 zkfoc{14LyQWjy^}U(a!8gD*PTJ!@l7(s~NJ&UdhlxwIkl+PlWSp!>TRr!9R&Xc#L- z$d7T;k4ntrjmD#*UXSFa%jq_9s-S4C9EeBTZQG5SkUBB+O@1x^=mFPr6t?aX zDFu{+*ev|Q&Ei#o+X#hFIOgr*yLV7HQSdnWXO(cH-fis^tK~+#iB|F@WDXM*JP3kv zxQ|%a+rwv^|19O=w}tmTS7#HBr}-&(t^e1@3)_0G-s2)1v%+V2z-jkvD6^Eg%0Wlg z_<^}#U+V8Itm$ic;hVY07|HeznWzg}=ajxP4EIThpNb+U$%8LxsFn6hAPI)HPYEX^`ai*g z4S9}kSSr8}-LqY(3l0lq37Z?a;fP2a*bFEe1zA$a$vF!Y`IL$pT;hZ@dwp?Mtk0dh zXu++LkUFf0B++^L3R4*reIh2*-~pFKpn_;^1>R7g>JfK6m2Mt9B`3fX8LbKoVK*$F zAIwaJK&aaUZ`+;vQkVX>|7Y*N`ZpKzEQKzE3~$71$&KW}v_R>TbZA(q0;#bZu>*n2 z0Y<1&@bM8QD+$z&Ha(*Ru}M-&z@D@-WPGpL_J@~*RZuh|S4&);waQ!9i7 z87Kn^pjN?n&)xuUjlvWJ;4EaR4`k8wZG#ZbS-nu|HynVsNK$BRihMeYNXmjT-Lnyt zsha5%sexbqSs0q{Y^1nnL)JWSV1v=SSk}X_`@o_zvd9*JC|oT9P4n`_>K1iAeR#_u zcc#gEZHR?Qge&8d4J%_XUYA7r2mqKLk2F7{)*Qn^k2FacQbyu|z>b^9I&DEyM)j$+ zno_3laLEE(dLr-_$e=YMTH~7RW2(`Cr~=9PfUs(yEYw9i3l%MZ4GvwaR3$~H?H&0S z=;y0m2T-(mSHfsGD``O?w@{R-LZ0gD7t(zlQ={MWwY8whcz+_cVd_}Wu6J@knR^%< zb}0R%J|^Hq7u?y3>N1|1zKJND+>s@sJTqw)hG9cQxH5VF-dnh$DtM?{FO~-aJ>^;f z@j+>wK;U3d?)H?~r8y7xPB#YTkR%a=0{DRwEMA8OOGZHRkf+*y;ec{akH6<|!O`Ul zeSu3GMBQ*qgI3!=?dXh5g41Upt3*lS$n}!a>%UlO{hplSrZnb-QYZ6I0ESO^iWAYN z)KO;TFfIW_f{oCK1?*H;CO)o+^)LoQ>Wg*o_q354e1HxK9lEwH6J2>!4al_YqKi!_ zV>;O}?PG?GY>W3^LtQ%WA$nw5OcsB`;wVO9DAm|00)G|@K`17pB5(r`Ygdn~$~Du3 zOno*s+_Ts@k)$%{6MUYG?0^uX8{~lr4oMjj+J%vM=cwsNH^XlaZoBf*u_)9Y`~f@G z*fZ_70xnwya|QH3ZHX@#;CBTR*aT|B)*qBxB1lb3@*AFnp&Mr?(3B4Dyf)`>iNZ;m#M3_M9FDEUs!-IsyCL)VRDgu0rWT0DXizzc&_a;RtQRBxlnZf zdZLEaxvuasHcUx?5(A8n+?Wq;goWVV3s_-6cfcWW2(NzeY-Fk@Eral&(=mAmNid=^ zH|VV)D6}{H?W{cTLdM$ZV`>i>>a^Ik6{egVf-ZZi z1D=gh9#yF}4%PMT;=}tM(dj7aQ0wXI(i7F$enH=(lh>B(O3U0c`PNvVjSJ^uO@rE} zdx8h{WVZxh*-u1j)Cf%+%O0hP4+_AW@||vwpiv!aQSrn_bRH5L)B}wKd1zaJ!j1h9 znXz8g$_>WQScY}SCUGJ||A-Qn@y&}^9&zwTROIYUaC|pZgMHq058CST1&p*mlwmOA zO3*jwfb@jhUy$M>OVAntY2a@MFqpW}H}GFrlHRXFj8F_EEG4BGuFk#heV|vE^QbM+yaN&2U0EE4du%Bp=*7u}u9iNgi6e`xK}U ziG4%G2^7giVI#Qergz%y4`}x^0SdNt6LxQIUS5QwI8S+@sR&jwU&dnSN3y`K#r3pU z2CvP&h~nv@7h&0zvJwg?I5%wNt<4j}*9d3HR3Uq>cdThSa9NUO6C@ya03u~jTvjV3 zRb*;gGK5^mG6!&SEEot-N}HfWSO`*PBoG1@Bni{EHnyv)YN+k2R;}8%R)8e}1SEhp z7y^KTkU<0g4uC)c5Q#It-@kXcAU3y`cii{F-RC{eeGL1Y_t2NVAGf%6w#g$%D`B&b zsyy+yf-cgVqYXr7tIzDj@1ZHW2-|F}WaD^6uFc{+CO!M&c`Gr5gC$@MQTn$1H0=iU zj*>MPk?b5ESdv4k`7O?x^I}F+?n^{rV|*W(voOKJjZPOm&(0(GuEcOc-iI7UHvA*l zBI}Jnkc+A}oe?IO*gSI{$|9B@Jh9NsT}8L$J^hHEER1-F3g4F-)6oVfg}ThpQP1cW z*|I}gyfc-pf*af%6ycOfQJClzjlv^)e=o3it-buys}?glBE?SS7Z?MUIy#YIQHbW< zIyH5CSxCH9n3R4#S^0#%hJdM(pP-I~f;_H;((jdoz?>M`49anMmgwzbtbqvQ%3WJ?V2>2gHF4Q=oXCgx8J$x=WZm-D2dCKG-npw z&%RvgYc^1g4fj?>b~z||bQD94eMzP0-}U=sxrKOj@Z)nI{P|}(cO=125=?XJ z$anizQbRdhM(uwdp@PMI@&TYMNL$bD6l#8JbExm)%Q!L6TS?Ps0z6vxD{a{4I9QVd zUJNMNwC<_D=Eehwh(dYH6IT-nC@AR8l?DnE3E`ESmSP?=;-(EP@?gB!!AlLXBg+;C zMusJQx8#;e=ffrL7}?zuk>*__E&;^lkZ-bL#c=LE&V#?RpzG`CV(!J7nMCwOByQdW z+mcw!qNlq}Pn8m;_6Ujrzm6HEC>sfzi?be$O_DQ5zD)GnlM|kRZB3mWrfRbNk-AU% zM(N4a#a#cqZ}H2?js6k@WZKc>wqVZ(XHkK_v$j)?QQ65c&%TZF$&O(J(TL_JB_3}C ztO}ACo_EH1V-2_)3`_wmP$Wq|vu7DZbNchxW`W63FQ*W8Kynd*jrZpOdZkf6GR5KMP8Fsl#bFigcu*O?Y{_AjuZDw_xtWW~~B@>_`%3 zjZPJbC?An(w5=5X9L;Q1_Die#4>nB|V3`W#$0eOKUI1&emYmJReZjm=0 zwfpx4q8SH(-=6@6d4bFeBq2cgz5 z?K~wif*sUO=-x=E?3mPoD7mKzYwIC?HJiVJ!eO_FF>GfR_{0>&1FZMTh@tzUh>3t0 zi$eqtJZ78R5#sd8d195#4X|4QjKWeQn47VBjsc-{3eD^57!zyu@Y`6wU+=JGzpiax z&!jJhD6KhwStFWpbft109SP-1X$a1jmKh2nd#imVo5Y=3Xh&r#Er80H(A;q~=b0LljTy3l|HvsG52vF;!;1{y**PWTG%ry#GRfZCByFk(q%<>>s&gq3 z-*sf=X8AbT@r)7Pj#(wSFLM)ZDAWXA%MO{wDO}TtDvVN2R-rgOo>-7~5}J*c!L)^h z%8JsVKOj!#rRhKhtMIV>G`6xOq>q7a%xB}!9kJW-lP_8dr;^-K^V5~iri;XJXxh`K zKgTV%HvwKJqS$RfhGCA!sEf7W`a0@H1$qx(_iuMmUziY%k+qMdJp(uBoK?VufQgjH z&oB|T@qaml3@*RXs;CL870>VWgN?-=UPBXclWaS(U2q6c*EQDJyCYpfzVl574C zRKSCM*@7_I*YicuAEAa=5$Q}xjp^iAN&?t0w7d5h_9qpD!@8&%mc}`ITinW5sn3s3 zeJ6fSG5suu+#D|~u;e=d4o(x~wEfK6+Ph?`^`S7=wNg6>ccOp~o?0FKW2FUsh>muZ zwB8r5h~6&FQE6-}^hXhPZbxBmB>l7GwXLS&qIxa7zHVZk+F*5>*sK?5nyvN1mxb(Q zVg;o(i(l}$V&K>Hl7iqEJ-fOSL%lt)}DoL(sz1 zM2IU*_w18ec0cn$Ca>F~pIvUl{(M+U{qnN(b&I7~_E7YG`$TT-kQ<}m3b@R~%`1M3 za8xy%U4WsVxl;U8F==3v1{89w3WgxlM!icF0KdZ*fR>j3e2cVpp1lpLacv@f5MyHc z#PUKkR%y=ER{i94>{z|A*uM3f7D*lAS+RG;o&V2-t?^4GHRWt#@g2qb9L3qYeOqo9&xIOl z2qeSm->LLpp&@5*^B5U~_54=QD{>7YZ=lA{v2ZREo#LkRm!EJvD%v)Sas7!wy;Q3B zg4DpRt}BZdWsR+(Yh&I>o{zR2TrZ0a#up})X0VzE7aCMtZ~DvmMY`Ym@Ul{Z-cPRh z8~j`$2aM(6K7Q@)p{ZS`+L?lYhv*iH4?f;5!gzPfA-L~qxPYsO4Z>{VOBO)AU$y^? znt)epjP|W~ciPRIJJ`ye)n@{l>g!-l^=WD1G#ibF+7d^ha;xA6THIP^%KhqMNoa3w zY<5e3h_(_62*64$^bMiM?s_DPP8rAbnkWb>f1IRd8)PY?hxC-%FbnewAO(wHzNnUB zEdHhvq!~f*Ci8X~Al=x&!5qvDUp*Hab}!9caV%dzNkY9Lj3*E8B|8Fcktx_yc@(E{ zQ+br9eHpSeGE=^+-;{rUx^L7!zj|-nKfmQSTya#Zo8O<=51m32z?*BaFSh}K_Ji(b zd7}vmI!AI+r_pRmz?$+l$)yBDdd5FgeUJZHtiMXlFlifcy6ov75*S!4VX-q$=57vc zPP%xL3?<$JKRy4;aS!ab4nMWH@A_8a=w?qn_6B9I)qg>mWv|ymGIRX~Ws5y6nzHmh z?ybW_Xs>5F=_I4J+O69O%(*!!NjtT*deY?9DJU36U@>C*^0!0GWs2*Nhs`b}9lXEU zNZ=S-g&Gy)lXpKVQy(WZhP=A{NeX85;c7};V3wOmPc~H;7n9L1uaeX_c$;XF;2U7> zHY9dx=POzrldp>)LB`G+QDjQP$1k-?vtmJooK|8lK%ix`i?0weq;jKzfskBQHXvKX z#1(sp{qV6r`|N2B<4kA6`{ryI<&c5`wp14$4kaEn^u@o} zkivjfeZSmMvkwwHvn1+jFJvR$sLXT#F7(o5&V*4v-uW3dm9iep;w))g0xz}Y`Lgklg=-SD;rpcU} zwf;<9uWGQa)pVP+0ZNHE7}wFdyV^R-tG(@d3|;7DX<##S!qofK*Rp?;VSSA3;iG@W zLXue|V(F4*sJ*dt^*1MU?XNMYN(>lx1yJg* z=Tm5AeZ~2n%j+ks9R_wSzCIk>YD+k`68X1l?Nf}~Q5L3UrgB=z?8$gE>CDVE+9^ph z7i7JBTYMx*rR-wJZ<}tWZNkekQiZq?sWYctC({vOv%pv^o!XOwth{G^Kjd zfy)JGY0IVU139Lq3&R|E`tUKx)HoP~hBSR&Jl2fr20AFcF1PL-_rTPpr@|xT8+S`j zoolV`M^hgj#J3HLQq)$x+w4#wmEdEuFg(Poj1gtncVKZBhl1sNkov-QUN>`KS1BK` zX-SFJ=It$Zs{s;9sZJ0HeH#?rqz9Y|fS!%zHS3eIZMHlj02IeEr#$-E~n+TmJqd?;vR{g`)7S$hR3 z=0h?$2`EHkQTaZp3?UJn7NzzuI&jAF#2a6KEQy~+H;<8}7@i|;5F+awlav$bld{D2 z_i^%*X_=O^ZF!`d^QK*!1dTnddov>TcEs??-{{&^afEF@K z=N%JEf^i+@?BS{Y1o#R&>zNGi`6|32nuebuEOwefBuB+%G>}llYWGFz;QxkchD6;l zXJPi!>(x}a9Kl&|AhX>h;VvpFA8cc8rrf_cvC;abWkBN_aW9JXVL! z6-`!oy7>rr8>}6oarRzX23NAnP0dkH{U)TgAy^%GvrYIzgwJ%O_~Tc#S3Q9>uQlCd z3eR*7c;aLONVA5jNY}Bp#8Wha)s>PVPVrRRL2L0=ZPKC%nb^Inw@7OTo~Ex5rBF$)t8DfE_Zw{&d-$2&%+IzXE0-8i^An6JMzF% zLOT*+fvtTNV(~i?r=2v3)1iNUVoeJcO$u5ui*qiOVUjbl(U1`=tiG#HQh8u)VxNF1ql9?iaR+*lByh z?8}!xT}7-1cKBNni(e{;%uuUpO80s~ntfHCY`EX$>$1!qcTjkR^`AiAJ~P8FFLp!Q zVQvf5$qE*vEn7r^GrHvCWi4^nrCz_!*G91net0$K`?xG4t)G`KOV4dnNu zuS&;A05VvL*qY775L|evx&PtgEwjoeiqrfSDeZ&bT)Bq#_pCb;CtZ>|k~UOm=ZE0wV4f zo`NCNxocFrE3{=&RmnA(Sz4+K&`roG17ddY#N22FeQhRvhCQAl<<1P2z>vgPdJP{V zW(%`8SqtR0V7on0yV^r19xniK;C{0#@yyFJhBa$xTc@L^q@p|1G6drWvhmw=jb(mj zrkMWps|K(2F3}jAV|TyY(w$3gk)<`#zwl{HWUbMqC+mXKzGZVCerEX1TF z)aYyJl_1X8!kfI30|q9lVGLpE_NnBX_1?>dj}Z~glYF=BaL}h>SKOar!q<{olPI+2 zmzfWI&Bh5;&3~_yS>SNTDq$o|F-TwqA+{7UYTw1?C)``=b5@r-b0U%ZG{0xFy|+JS zupZBxUdWq5y~sd&Jzs8*6iVuUZd2?EQROj3c!G+A zR8d>fxX8gB!HM4^ek3&kT$t+wujRAlwHo*-dw()4hhWw*-RAocU>OUe1b0Rq@f;ya z#kZ9MmZ!A2=B+M?OE9qy!Xfi?=D&gmNW|2QIiNogafGq#E!srofS{#T3vkkkv*{Ej zg3{-Lwp=IQ(DM!9O^2K|gx}Bh95gKlSvUOt!N5tRC!ETF+<)9DCI5owZ_);gfTt09 zR|3pXE!CQXJE0Q~Zz^J_5uLt=jI}x&OP#XQ6)qHS_=Om??M;V2c%;@)xaB%Wh*F0^ z>$quNtk!IOPuD!SOVX~;%vm?d`s`R1CHul)THy9Cu}lkF5K!RB2?=sz2|;=3#=qEg z1po-V0W3d_9b-mK&qcB`aop`=`Q#xrbNeLJ;fVDIrr&Fd_93BBHch@_t7rGBZs^tR zHY7orXTV9pufhjLG;c45;xH1w!65J+UKDQEP)pNcM{nV0w?lW^^2@dkUxW9V?e({P zy&-nn4V8Xi2E?k##>(MLUM#7nB-ftf%xk@sm-b%P% zjuMn!;%Z{`DC@q0fKD${30g053XQixw=ynGoE(*m{`QVE-H`KeFM-11tl<JBJN@21 zNJ_F5_gU8a`88qMJDON3uv7x6)HasI_KHyBK(3tr|lC zke!mym6f9+T>vcnG{&~*-NXEufNv<4MmL?DJzk_F1h zFjdvO1WNeUJeutU@-~HquJNIw~ClK4Mjk#&#pd5a~v+ zdXSzVg~Qk((c>s1R6n$jpP0l!(z0)c9=$!KzcxP9sIW}ol9WGaY*iWpHprcOwBAgF zFgJu4MW%I{P`!VRp_#TGVIDqgvNw4O zcHlfin()L;z~|#o=rU({`+gZ~{ty9zrF3=i<)O|5S>=ZB<52j6!%m*NCk0DJHGrQ# zl%GnL~&W8dH3Vfzy**!Q(tR11SeK`8ZSH8vSuLTMM-YJAJP06B43Bn_MP}2-lS#v#U`)Tn3{dzGR5iv;F z7PNUI-fx-4XL+L|F$@$I!&vv0XWz`X>{&`{g}9s zBtO;&zuX#F@KmnVIX&+I>kQ8V4c`nHU*s}tT?i}900`*_DU`!!5#%gXmUe*|b~qiQ zEQ8>B4(Z+D3t;)&YB8u~Gdwu)acnQ$&%~nQ-bK_Y^*Ox=#x#eWIF7YrF|6UIvTeuf zh>#>`6|Ri(_(L`!!=sKvpofDfU2!;tUT47}DczKL-J-RzYC_PPw2Eo!4oGt-u^vSF z%ckA0g{E*&Y$rx^E9@4X7xf;A;Ur8D#9V3T&!fYT!v6d=bONSB8zrU%Tjw-q zB>dKlu4y)+fGA*`JI%B^++2+H$ZJK(brz0PA9lq#)ETIv`7TRqWo4Fa7rma80@KB? zbMtOy61|4^(r{^Px#6>=%q(+W!V>=c^nnK;#%D6SW&aGh`q7f@+B7GDIGi@qN*=YD|veU17OHiZYuQA zdk9K>OKFrv-a{OhOI+_#)8^7w!*NWmGVrb)vZ-kIAO_RJt9yW!qiLhG7j-c()U_Mk z9>jKM2WIp;A;z?}W1ZONnICS4-Q<0_A9Dl-|3Sk=Ff(KbBEeK*zH`tk(omn!CQd=p z)~>H->5rOISJ<}8$>qplFJT0fpb1LZ;ogfu7s6HGC$vq1`q-uylyC3i z$r6Idtpm1^FSLs29U}$Uv}um4u9C+n&g*m3&%@I_Zd;oo?De<8oj7I7;_3U^h^d=c1T%5dxAYDT3p+K!EC4EkJI++97ho6>zg> z-A7fwWy{OqbkXWgSIWw{>BPBS?-M<`~cap_A?TUAI%jr|O_G#PE-ezh8Q1keTfF1Xhth=15{Hv+T zyEyHC{1DReF2M{ox0Un2wL$Jp`C*lNo3^c(lrn{-D#;RF7A65)Lk0+`c58b!z!Sd6 z_O*+%sW1uNAT3a6xP*rv)sV+v29K4sWVk}Ny;}Lgx2+_qpobyI4jYryI|;zRENrqZ zIA@79%M^S(;?b2)Ttmllo=VKI!%n*iY`HvZ{G1D{Yo5pd-ZXXnAorA7C+oFS7gvC7 zTbb<+N|Z8ro^v*;9*-acTQ5i3N;=x3p-YCVqlTr0KFph z#4t^`{22U%eYl*WKB1E9Z;>0ul9A49bB@P{oP-VM_o(-Uq7RqE?vd&jfJOlWI`qzT z)iY%I7Y&!lrRn#x5xgf>YkYz3Z3UY3`u+=syRf8>RBH(@EW3R_`GOS1?%G3ez3bT= zYo%Vv=|DsOZ|5+FfZ)c#Rt`K#1ShIYpdJDkHn}+%qql{?k_23(zI#i`TUUqqj)+yc$*>a1E zNe0l|&CrxRi>J()nK6$s+CPWP$mzX7=`%cnvSc_5HU|M3((T+0BMpl%yqgZ&c!GiH z+5SNeATE_SjxMN-KHpk;zVtOG^%S-Cip;6#XbXlZY;6$>o-=2xg3q!$n-|YIvMqBS z!#`Uh^ZJowvDZkImSvWKmBu+ct%nX&{~1{Y@8O?@zW&=tP#bF#yCMtFXGSxMS-)?c zD@G@6Q)*dgoNPIen*puPX|`L0bq5Kz<-O9;Lfial4n8w}hmuJ!w1?Ivtq(Z3;__U> zzT$Pcy*A6_N6;cxYq#Txn5i$+b5CDx$UchZ1iUmaVMaU}4Q^5;r=)(4+frF0oy?;z zGI$6V{?d6sIoAW`V3LWjiNOu+_fc(S;S+Fy%zA1)T2}4 zec;ilV$Sjg+LKdgTgNl9@>wN8HO4|gBq!0Z=-tSubm9W_?HKb>2Ify_p}(ftZC1&F zwgReD1yA#$%E%WxsYi#XV&?-;=)_UmNv?}YUu z7&Jy&pC?VS=zn24sqiBM(UTT@)|+LwDc}amx5%v-0v3F)S4mn~<*2JAQ>}c7 ziQ82VqO`@4{g>r*fj;(;d-rXW(s@S7Ax#GBa@`$uYf@kF7aQ3-k|mmS-0iG*4Nr3! z42flKJW6-a-Lbc6#;5gCefn{SFUEJbn$8nf`I$p*4jiHRME@`M+hFr$vxm!O$1Xxx z`CYMC_N;Q|Ni=!RKu00{NTG^dKKrx7Pyh~1k?$>|M#bfNj8lHY8{PLV;4D6h1c?7{ z;=+=jzW7Ou3{ci)blmOI*x><}^CngM{3CgtNhd)))iqdPjpgrg8+rKuggzVM1Jw83kG*WE~8q#;|kbzOnD&k?P(8g6$5Y5|bi zr))cpNT5%cqchfpxxV&!Tpo8e81Mt7EhKR?dm3RocF5fB&TPyE!S*pY+D}l@sNG#G z>n5z6gsz~wV_$dl%6L~wTG6Z6!omVdL(V~0$Q-z5aA$2{Jgv&oNVk%xAoe61aTNVA4`ir3ri` zR#!;y{yAtOvyP{nVH>NhJpyZsE>S8oEm!%F(jr=Yi|D^huRhK9Td{_dIgpzF3OQWz zY+eGC^)^rQ#e+Jg9f320daS8p;r74jaROf@nP*Vxyg#U&WwZBfVf~>|dktb=V(gl; zOep^{kIGcVbBW6Dyb^YtZbZJaLngm)NxRghLJkubp>*@I+97#MIio? z!`jd6=l1Bn$TgwGVwRtp`A%!@f5tA0=+sSLTWn=tB?7=|?f!ZtEOvs#VWr)f%fSPJ{4j3~9`F1lKEeA3)H z>$GXO;6QoHe3l<_^I(=9&MyO^^UExIsJtkDV#mrtRwJaU{@YTm@ZAvBGpE)#Zj7M# zxooTasMcHMFwz8wL`!K0e-`S+??tVP+nHMzvIqL|@RiTH)f5vBPB3F=d8w(Nbvw`Zx zJvxh4nz0@35S;9WX#0d_??sS3>)JTfWdciT)81tM-22@Ugs1SCC^*ELi;{QsERf5I zM5_bg%g3M}7~ilgLu-+}($nfQo05aptoFIq^O!WRnR3L2jY5ruK{_qiYRA!{jLL-Y zo^Rp86L{40qFnOIA8tVARnKSTmFdEHetS8VVKQQJi`YrtzGLVq-7+ALYDlA5P1Z11 zMR1Xj)g)f^+h5wyYgH=GjOcw?RiG(;;mDgZ*@+gmKHqN%=Mo+TnyyV!+^UforE={S zv>yMc$G_@b`ZJ%`(cEwD@LK)NUhCc5CmY&5`iye=xr|6Zm|t;>5J>X($LpTTwv#8b zY2u*urN%=OA&ruR$5R+`Y=PXrMA?rKD`jR8ajzN&RpqcEkvN*Vh6SAHzBl zm{>E8DG}8y(N&Gy7tluUuPOMDUpkv7xf8<2xib3J)9Ja~)G8ntw%Z?HWu$sEQhn_t z26#k~G+dIm@$!V|7?PE^h^w*PhU&@hl*Je39T(&2x!&x4?0B*K5pIhxkv}1J2ZsRe zXPxRA`)RyB2%Uq)RDpCC@pkohe|xdZQg``28GhbXr7 zHE&z|$*M>4kE&%~%4qf>Hz-k`TEUX3W9N@@Fw&z6mO(X33GiuJH7qo*?@*&-@9-6< zZ2?^9PhOVxM(&(^me#ej-4dL-IM6#jGvvN49_Li*OB(`JbA)`U;y(Qa8_N2Lm)cv* z1ho4)D$SfuqKueBF9|~Mfz4Enx=!2$`d&x?a+5FA7jp(FPD@^xW$x{*zM@D8(!c z1qmQpL6;WIi1p%X@B$#NhyDdBy+K0tEj({9qCuzgVE55RVJStKIE|3TJpdqB`ooe% z7HU!+0*eX!YR-Hba_PmRcG^Gg94~74@caUF9)YD2hv36*UD-m!iQ>eI?v?11jbTQf zUG;5YU<)){Nv&&EM-BW03Lj{w^DaiS=%aWoMj^I*t}inW8PC`hFX7@KDQ5e#tT_VV zD-K;4SJqTV3=#JZPUUS%jpf`5H(bXr5gp(k6n&#sEnn+}b{Qx6XC&G`+}p3gJ1>_m>T*%!_8cwKoL#v<0baHOoH^aiWwwV61zzX~t;7l51QmY>FK%o^EObQ?rOq}C zQ2^}x5LEA-JlW&0FI`evI$!zS zJRJN@yo;+cet5m2kFtN2XmBM1{!mnLo4+}Zg9MHVZl;0zcyj=xk~lH_qkwHY zgWBlO^mKlZ!TmOiJ9uc;9JKv(eJMqAPW`a=+n0$N8+M7c+@{vVv; z1PMVLD1a;4{3*tS-J&@>l61?Sey!tlYnQk-f-)T^2w``iK$BM^0>#e%2G|f(&Hc2(oQ}pg^qs3_LM0HO=S1&v};>Q zIwI|ieo5!uFrSb0my)GG>vY&@e8*?Mljo#eQkh>*&g5Nr*WXyR$465Mo7TXZ2YzZs zX@!H?NcVBbkjoVsX&YbZrC$p?VGGZf)5|)9sg+8Z;%);WWKOjo8Kc?ks z@$8Z>;x-A%&jJp!vc*S4#p9Drr4-D)4cQ7rWZCuY3h}x$-AS5PWE8o>V8B&EFE{^? z?+o!&_W^`L&8$i=E8Qn3*do2wjV!JpgeksIf>?<)RcY$~5dk^fl6{-^IO0e|iuq z^mnVS9n9n#p%9Y8J%Azf0339XZ#{tGIlCDVh`0zEgb^5nb5AYsw>4$mKt(ZV=d$A* z3o&Pl+m9mOB5DRT^`2}u$@_?AmI)ux6~};+CJ45SD)am9j4)}*zrNqD~({MTGQsU>4l6r&A>WC#&W{LL2wPX?4x=!rJPVh3SMrpH%s^7 zfU+TAJ4ZQX3Z^m0LiLUYE%EKe^(xVTH*gk?S&V{6cU&uR#wjzQaI3##sWc^O0t0UbRJ)Q3aviNW&MtnK0?sOg^esC?vz!8{5GT?C zk6H_#jWYJI@`|TOKX%cfnlmz=^Q+oI?Ze$NKZq1*q2k{3|0dL1POItYauUwvxMA+4dSfR8(**CqC41EFk^NHnAiT>)B3x zFfYggZX+PZPXog?7BPao5P>7)#ObT~u|?%ou_PF*-Lr$ciSvY{u4e=b5)G!+DFoop z{=&o`7&JOv;I9n`Y$F4{uccn7NHd*^JeakTLDhFQ8$Ab$%-dP;gICOOc9BaV+nAOB zv*X~|R|Dqxi3me{1FxMIU+%F_0g|>=4NExIL||8J^%4ND4Id~sI6*>s8=rF+#torh zT*SF4BBT>7Bt&L|)afg)p^Ju`yym9Q9-wjQaL0n_I>Z=Lod_{2bmHan2zhA0a9|to zV6~s`Ljov>ujWN7+2MtbOt=zlOwchU)Df^GC?RGSP)>Yxcs789aFez#A~1_dBs%A6 zw@dSmTL=WRg+LP{e|t$^fE>{$-R1^KTY+ZVbUR_7V@rZy@IlM><(*|y96Z;6k>Z5~ zio3fPFR-{4cXueR#bI%Z6)SFwySuv;cXx`r>+W;EzvIp1bCQ|NBxg=?t}A^x8t|zC zgFR^PPB;@t;#MVWI}G;^rYyNBNtROv{>NwazZazxy-3qur7VZwhL{v24QvG$Oni~E zKE%;U6@*Nc|AbLUGkin4By^nNIeX*EN_Fg>14!+HzW%20^^t92wa(Qk!_AsmOyV3N z*FWxVqVcqTnva@Oa+s2MfPI$&t4#OXkQ4GOzOL(cyCT6XapFdo?>|b^Rp!5`eQlb;q)&3X3jm8b?edeF?wS z$|A(CVv??gtZqt}d(@-%^ zHs3=K_98y%h)5pB5CguLcer<{J-H6=?ywOw(y*cTMUtd$_M|8$HS{LO2Lt_&+jX#P zPVs3ieLnq!V8flTgh!N%lhHQTa#_1F@jvY*6vbah{n6}3Jn#;1#LB8!m~z{wd}2MqBy%2>ohF;h zmw$UAvXtfE+z6tu912PUTC$lHAcgDY+`6x-F-u`CwKRNaiTKN*SU%t9G%_9e~xB@R%+MeSSY_=B@7Fj|Q& z&|SRSCO#LqcTJkQbxk1m*EJ|uZz3`)SY#CnmHw4TK#}frlApZ(cU@8`_3_*(mg~~Z z_*Q(jOj1UEl$OOcTK*}Zjf+KQS&Kxq<)lAd@9N8byTD-8GPSc?BwYQ4&X1RbYcA(X z1O^OixHH%^yJM{L;2F#pbk;&%|1Int3q0MJ!&Txy{s>Y`O0yqGb~{)9)}X(u469Rm z(zGmUQKs|9@C`{>g#LZDWR`4wCrc~G?){1$9GTF@ zbPXVZ?WaP6oxgPAk>`VvAT1%+sb6$Ir0*4&wOn+EXt(gn?B3qTt^11UYU{;|VD(u; zwH-b6C z6>UQ0Fk@1f$}HUtSsTCvL=66|$=p^)0}Oo@DuQo!62Z81s~LKCOcb;F=a^2ZH|rIE zQr3%(DBe+tKve2!V^d{io90*Vhv2C7%Y4DF%0zsmlJ}6D0FEEYvcyyOnkrn$F?Z7y zKQzEjQd+>8F6>H6snHxU6YR|Z5gBPCjCEfr^}OETOb?&0<2PIx(z1`Iml;iew10%x z7-;U4%4)=bFNq}LekS2s|M$YrOG00mG@Ekeo@Cl^fwlnG)T5qZHHCMgho{d>>3@eM zZnVGtdT@#xC9S>G)S>-(&&x6kTi;t;&Mxo^LlHIGw;5 z2ApRFW1C}AiEl+^+s~FJ%e3^8Q$$?ec^PPe{2mm$6Id;>?NN!kS2H8QM#+PXCyh7TTiw2>6^k4Pp;Y}1G%b6vr z&@pM7uWUs9^kE1Zi5b!qvo|`;Z!d__M9EVSrWOlxFvJ|s{U>CRD0kE#+NLw5XK??h zzW&fITDJS!8=x`CoLYRy;^E4eEJ>y>>rjiV{h?o<2E#w{WEYI|O!swhDYsZkuwlWf zgiZZd@K>umEXou?|w zj-(u9n1%^c<(U`{(;($mW(()9@s&Wh%h{x#c*7{R7hVa0jX$$x5Y4^Ec2@lAA}f=f zo*f+ZJ5pi1_I??eBjcMEi^k*~$jUZKko8nyuuS5ifJGLFQiRH<7r=FiFL71u5^GEy zQ47*FD?+e(AKmAX!%p>uLNF}Pxb*9SbRIbN+_FKc&qYRLpZI*Vzssd+h1OiM23jxQ z4+U{z*jN&(Xlsu$>CuG8<6Gu93lYf>y7V9j*_&{fD8$m%V%o7N|60|IK|`1)iPGl^ zKUGHP@fKf=Z3|8Rk!nELrDuwH^F5))KaNkQIUc)m?sS`Y#2jEhH z_&7)Z_dl+mzhjsQjVmq*OhEm?=mzorso;>>MkZs<{9)~%;jEPA%({|#;=`J{!I3^b z#i%TnvPirQA;rvuM%x)*Q$L5(Ip4Zd4lQ{nVh5w8AF=T1%$R$#!kni0+-xc7imjoZ z(^1Vxtf8?NnBxJ313eq`3D7^yh^+yHd(Xs#h-O?2Fi9iP-q7IIL#XBr;a8+W-U(cJ z&tU%=E)Ht7PYwJe8IZXfp?3KUqkWIzQILbORiP4AlgZ*_Oo?33bAAXD<(2YF{-Xc0 zIzLd*@G4X|VLYb70*NXnt&ep(ujz#vGh$6TPoUpF7?FZ|$TT^0N6_CltuAY0aWhSP z`ULhSa_b**GpER~op7<8@F1<1D$lVvo5~bkT450{<}YEjHU~AFsX=@a#Nxq^=`13g z1yrmJcZRPKd3c~U9BMB5ajsyni9{;#0RpVwpW<;Lcq5uDyrwa22Q(IMI#A{L`@6X& zduN?+Vn2+I$SdZC?|oUbaaw(Iq=_!CbHO_jHax~=Aydp8H-lIsG91i5!Rz>W_k&Bb zDjZISV!jyDx)|M4+-&~FG}x6nm~-Krv>I$qGB<)1aTg<%tP>^ zz}iM!<45BCp@%#^+b}L1@ZLo|kvV&g-mn&2)BZa_k2tSUR~%9Ws~m;MJl}V&(k$Xe zk2ymRWoh2)LhdiT#+!RgWb;;}6rMN2R4d$j7>$++SMv~t3k4mJy@u*jg4A&%6&l1JkK0rO`^GZ_^w06_>6N)33!B zF-h;pPVBW1ek7A((99r|W~GXrzjA56Ya#b!oXLdiEX{)dy(}9IX)QiP(DXkXP;ATwTN#)Xs zkl`avk({mwbetM3yApugW4@&$Z-^T{!p;WZ)ux#U@5FnO11llQJlNf=Ck( z1vMS-%75)i6;Uz9a==e+H=@Yv&@0?9(qK);XjJRu{47ndFUBIt3q2%m9WoigJ?4FQ+1Hk=SUovmyy|qxZ9( zH*C%Ut8tMilMT<$9r5$x{ecZ_)6)BxDXzn~!jJe4US!$rCt=zh@#ASzl4dQ$STRRf zQL7LN&5GnlL~0UaOWGU|FN<sEu!eb&`$1gMc;S!Es```X6HwvMG|IHiZ}hzJ{|NbFFWzRGN2!l%G;j z7-+AT(?9<7lZzH{D;S=D<>PyBjk6XQdL;N&A@dWw~0Teth| z94dzz?N8_4aMMlO{(VO!3|Td1m?SHds^86=d9ZfRKxOTQI$rmxF-)IQ6s3EvlHkRI zJa(v%v8ii{gsmT6juU6@#WB&wwHYY{j8RM-66T&1w*8x!xvTa?;`4v~StTqRF{}pL z#?Y@Ws>dryv<4%@DkALH7jl-T-QMorhN#rl*O%gjZdgO28GstKM8Qx3p4B4b=@0)H zTit`ZLrIxhj$Oygt%*RBvH*u4W^cukEDpV?H@Ur|v^fmC{Dkn7na-^=YB;ganfZNL zjGK#XL(RRF1=1VwnA>%6N zA?}#-$5%}x+)QtSmF_{4d#B`+luzM1Caa!3k)AvINMW5Cg9Dv!E4zO)rhSAhT7AR*Pm@Qko}%C$AEBmXwJbU#J}pOkp5nAc54(ig+T8oY zwB^!>p|t%qHOn+0D@Z>Z7BBFzDBRl{>y*+uwP=gLlf2@)XY*H~Jmu;GJi)|KSZ=;A zHxHKktP#J|H)b-b8=Is5W&SD{gKc8rw8K1QQ8~C~;ZIo7@*E%?tPe>31j@5vFH;OA2WL$^@LS{(pe9i6a&h|4Obr1RA`q zi{R!mlaKUhfth|C)|EVGaW`LW7i1n1V=H>I|ISv6HLSLO+n#B9A#zOFgl0fI)fFjQ z-TN3f(?nX-YB@9v?#;~W<#R66ydDv51F^#>A^(|^$xCkIpC%f9@J;8|_yul=BjWZ* za7gReijKqi0**r$eOY!x{XOwimmI>uSx1<#aL;T#$?+nLiRb8{V;mE!Pashn#>o}O z85=Jmk)6-IAH%<aKts$utXBWZW4uhN)8ny0>k3j3F2f@=GhF&`_p5aV4AO<445HT zl+0uLwf&SxLhWwnGC1h;EYVHOE3MH}zc{p?C!`j-uaCt$+fXrU(7^X+9)KkTzfo;5 ztA8j{q(=Gls@RX{oE!+`QYgPoi9^3+QW3q637aP!U-#|3 zGKk8+%p4Z(rUG-&*kb!pgX4`v%y+}*j)kDm@WuiM;OBdIu@w1uMa#YmMzr0zp^_;n zFKjzsSuryV7nLQI9TPvr<#-SLLy%9BkC}<^tr} z(kod;(aF8!J(e03u26U90;cFu)8d;tSP4CGX`i@3Ue-8c%fb^dIWc|LFA;#GuCm~+ zlrp7A|3{goh=$0F67`M7{y$dIzlqV+)tnsW9}Shm(<}J*md=aOOORG;sbI+^uA(hF z>Z7{!;y{yf+Z(;3a?=!?Rkl3x+AOesC(Yc5X&Q87$~Y^6ziO(OF(E9udLdkc$EWt& z+b{$BRLzg;c($S*P88eQ**i)dT? z()DGJ4!)kaj+r3Ns`4CxpUSXz8n(Ia2n(&=aFTy`#C%+r;2V|T)?)E7B5?n9oC z^H2T(#g`r`4x8Ap(A6&H?=^?p;mYJ>KdQdP2*pG_+{TS|r|79R2k7b8$OE(gi+EAF zxVRlj*h{7GTm8t;=hv1Am`WC&c^6r)UE`M9%Kf?~kVY2}X-;-D%WR{|OA={Hoi9$f z0Mhw4^W)q6Q=;xK0}mHQ?6$8tFperSByXs!ZYKP^tfi+iaap|OC0-|Q;b*6DcBH_7 zBcsd!og?X2yccYFg^8tIDElXiZ$jTB6JxLC-l@Obl77=L(Bi440F>E6dFAiGKUv6*$pF< zv7`Lc31@5b^YW=F9_3LUKaFwZQ`<%;%ZZ3Ef`M@L!=eFQnOLQlWp%a@_4mjj(rstK zp{%GWRgt3o{)(c)`I20^OjK4IaCU)zY&Q_aU=QuNbt3u_?!v22UHJBd%Dq;5ean58 z1z>)dE+>(U(?}9>n8~w*k;$fB4tdj`<_I~jxJ?MY8OWbeeUH`-{BCzrk>Cvd!kF#E zLN59h8F@id*ObYy&9e~xoK;baFth=VMihMyLY+Gn03 z9BM2ZdrtX|Jt^c#vqrWh$eo;@Q zl_d!LadiBbmx@h3m{8`K%c8E0iT0eaJeTTIvKrMV2RVBQA7uViqNJD zF|xR9VRrJ-gtHyaMmYt+Uvwe3X>bkIRBYu2nxc|_9%L7>f3E9UW86sc%w&7&Dn8mJL&65@~}sG+jPIY|JsU9o>C%;7Q{O7K33yFrX{!i;!;m3?l(aY%O-& zU`dgjG|~XW#S1c4N_gO@y{vAI=HeGpS)?ZM{-6YgptoA76uidpuD_9=ljSjxz6`hf ztLrQ_jz0> z=Qax&0{%^=AR3TMtY~y;_tSqmU|`lwW>fKH)ag`zl4<#4_{6iz4KlJ(z5E-ra+Yzn z4ZP@=ayZI;Y`K4gJSv;FEA07nE1z$_z8MQRg5p*jeX~53V^fzZp`V?;ZZ8~fdT*p~ zbfKzb!d`jQIxPp>@ogXR2u)oEbc~%@2=ltnWgAoZ&gIlyaJ^(g)7w{c5AjW&-95uO zlc7ot+ncw~#seqYe*a}W_g8g2`HVfUUatr}R3AZnsn1^TP9r-pO0;+Z| zgj0v_%kQ?1Ngx@{IDDH|zaG2&k;6Jj4dqqUxdWddsTO?l@)>`-ZUA2!`J&B!2|w9U z-+cfm`sDp9gimLm{d9+LwB~BT{6O zkfiK?_q@uuFi(nh+{GEXl z6}9~Kyksq_NJ6R^ZOUPQ^0( zyIb4gm*+KE7Z=M#NEg``Nr?jkARbIYdAZR!pZ;&qrzmQ%a1%+t4-F)T$zS1}&Ycs} zsGL&S`GS(9`VOj^8XtZ1N>gME5mvt{Vm`SM5?0L11epA}kmO=Z@Yun6-|W+&2a^cj zMcI}Y$|5bAyyx!$1#_wFIAmC|0jOj&xi1Z< z*1*XW5#S4aWm2k_R(lnTj}=F)>Kyc#HU1wR<+dCC;Rf&itr)AUVR%Jk|L!7S+tK38 zYIRufOp?G|Brg>aNM&U)T&d9A;x4Fug%0u}K7-%xSsnFA-_oVP`eX;#VGx_5)BDF6 z9=5u2aPJmlZlb#Zw%-BAXSJ94RfLpIRadzCDt5tPgOmAqCmMdmaceVCjI2yQ0es}h zY3HoCwN5yi$+-Gmu}j#%xv6d#>}kCMl~4FnrQ#*rgf%G$;=%olS^7`Z)>FjM$cl!! z7T?NC-0yNN?6h|I@Bn7Ke?HGtizAxaIk=we2e>%F(bds{Az-3Q`*Q*_y!e5RE#4-! zqWyl;e55)VV+Jz!>dL(;#fNA2Z*JKM33~R~AI_%FEO?&{ij3su&OED$1znMAhOqqV zZ>vVK&PuaheN`$yP3OOCG+;KS&3z&01Yr!P7->486>*KP1 zqS{TR+Wx6qVIfQ|XF^CJ5H(R1&9iuhpuZi0j>;}_RaRz8N^vni0;A}6S%U3qEZrY^ z@`aD;z(73&_!}nFyFW~3o~QE;@;wohodgbcP1hDcTj{M9Dn1}=hqNL8KK{>LTgm|2 zR;wqpUQ#22SPcE%X21)RrQFh4qE@oMX^f~dz)fb*p@G0 zVjRzIxG3sJEX^1%j!aiWFu@_tB#j_hH$ zRI#Hby;r|(WL21q*8GVfrIXcGzx#<D>4fI$5ZKlDz=hd+tNHQ?e$Yq?xfybmpUY=?p zxxdf^By|&MvE31eH;lB9G4iCm<`BQ>qlm^6HhXJG2VQZm_Tk_C%l!5!5G(4-y%fmb zyJ9w*$K)^?%snF)JztnDnTEv=J0x$Kb*pOn2|HC~6C=DJr&cVmN?Pa2J^3_g9>14( zQ6n0U2j-*A!3+XhR~wvzi284r;r1a3{n5}-WlsNnc_&f;#wFiz06@+=vII)W`y zpD+BPcE!jo_Zl_`rQt1YfOrerA~ZX%@W(ukoOfgv9S)V1;uOcQa-C%?EjlecOSHg^ zO0=o+dm-3;Nh90SK*u|76lw1}xnV@kP(=JQ+4LI*@Y<`pRgsei9oHjSg$!Q&<^6^I z>DCPSW2JEqEgSB_5sD)kuAfDjip!C4a45;qGQ%{F2PbbE*f9dLB)}*?+z*PasvG@! zesjUV;EH2dEFDKHm-N@tb=9MBGN@Y5C&IAXVN>C6N!!TuY>>9DzF~ILRvM{tS*QX=>mT`0N+6@ zF*@}eB0XirKQ$0%H9phFRuUExd}G7^gL)TaYZPH3BF-4gPpA@Zbc>+KVR{q@C&;!0 zBkMTYev8o#~#4hOCm(lLiKWyGB=(SM8FZj7!`_&cdU7=G25_*`Kd1|A4+SVrO&XnOJ zoo+b&1BlQ7CW5^eC0tA?OFQacmb ztmu=b#O)t|hlY^VPmVK902TP>--wex08b6!2hk6%ysJ~BlSM$Vitw(=vnBkNCO`=6 zJD2yy1HbjE+nJbm%Y<}_3rJM~s?Y5NR1_7wHc7Oo2!E(tE&!Ix$*M%35hRWmFedWm z*$f4P-gqRQqL4<)$<@FfQ4u#90MSwqqv%VrJ17eru&VOkpohr-3$Pbz#G?j4t26*R z=uA*jS+5kd0rvSP@qvmoRZboQ_Pfo4CI;P|M(E~z7YR~%qJ=;9(fYv7d;JOj-%=JC z$guMc766+}K7dJ&Lgq*)N^uzbc+i}Qb)RN7klqc)joSQ zaB4VlgsK;kFP_+fbX8uGSt0Ftr#OIfCeqWOb4`E&*uOsSEew8@iM(A!WK-Sv8VAs- zAyOv#HstJY7W9!Uai#%y!v!3dlKYi{bVc7DBrb6P{Tje?6<~WQ$VK$Oi6M*d@2C;4 z8i4C{rfgB@f7QW9TLv8yV7vQ5JC?9dU02pdk6AaqZr!o|M zx3e@ZC7V@wE`q;#HGqz4i0~-*j86+i-l8JKblzRONWl-6lXHQ+aV6SRglAPAn2_4b z$>4%6|3*wRklN;*<6yL@T>syu0t3Ll%L>M0OynBifF_mKCiv}Sz&{4^Y7Jlp*qcY< zYyrSf>YpEUbsDizN?!9HT>|V!mG?0Ozq<&yVIapW1to}H;M(I+1#Ox1jE&sCHsAU1 zwQgl??d5d!{cP%f0VIq}Qo!ayX zcny4$nqqsO9VofD=;%;AvyR+9fAQVVP4|i*%WWALLxE=kzT2L0TW~ zWonQg?qq6qaynVO7k>LZe+S8wd{D=YlV?6!*NmO$zg@mqKT7sUbj;TuzyFQ{p588P z8WW^L*VpF{A-h#gP~VgzUAmr`>1F->v(8L=f06Br^>yo6d4J{yRd?f|?_<{nwKgP@A)&p8K*R@kB zjt^e!Pr62}9YXGx5J#@)pJ+gy18=9(S5?5auW!BN+h!owM>G`Z)mSD_bp3*-=Y4vf z8KU|OY*0OMYYr&5I`YoN%Pc-(xLbZ}*dz(q1No7?uEKE%nrm;8Z&{&S-<1v+Ze(su z1zuV&=ZG|e&!NYYS55qH3^Fc|-mT}R4^r~M+a=~JhcXV|wvSyQw%AQ>!1zn{mzr)b zk5TeR<&am1Q)bj-w~&Oec1!ntfbiAz>6dK?Ug^NQgSJ#wuF#8!*Zw{I(eU~dt05$H-==!- zo4`G4GtUB6kL~X{aiqn63r21B{EdDy<4Y0lwmo@%C@*W%#ynJ<-WytnWruW|7QHYE zu-}s2^WL>bT}f-}a{3h&Lt%RMCP7!0kjhM@pT6OOXQ7K9ZK&j=OZ^r4m(wRd=ZV@F zp~_|8TNcMuW~lbw(bw!^e307%+;+S$WIJi^v2AO6NA6N$;n~El=pWzH@8)goM zU*ob%hjwU_uuoF>IH}r01jfcKdlKMBfpFL>3Kn6Mmj@FML;^{)ctv*-$OJUix)An(ml`VdxG6fgf zWR8IO^&cOE|K(vwPRKVvZon&{yB=ej|ID<$P7&9v*Y{AJOQlLLJ760u4Pu|9*QV2zXP53g^Dy_Dq*3A$08;)x1uTKD5!le9);wOPq#r zO>~IeQ}4e)Lif*~{8_h;Niq*utDsg_`MUvr`|ISOt)7+K`GJF$+^dF*s;8^BmPcXc zfSOs6?RJpMO^XB#+DOa6{m8K4=UAxt2B-#s+dQMkKH#aI-!tX9=t4D)5^@BByuG)5 zN-4O#dI+O@uXzPP#%hiL?Cf384k-MbN)oi?Or4&pSSb(s3VPlI72T6Qf(o~e02eO< z2g=5i!XOsNEmTGt6i_n{Ep`zjdwhiS8>|EGUE29SN=iQV$g7v{ojWpY^){B>z7icFC2e(|IPEosSGg>YI(nNE9!Q(aY2FHtMZHWx&Wb zIT@Se^+Y<}Q9v7#5ZDBP3#bq%Qpr)RiO*6IhB!33ic3<*h6~R#W=3$nvEl2xweIWC z{AejdRgogQ@t=*S!oB~bEqk6bN7n_l2kN_h<%hrZv|U|;yD0%e+=E214QSIO2z*mD zzw5zA3WOleb^21-|&3`^34egGL#S@BX{9kIk{yH4jSKxscsq z;4#AWrEd*ZHzw4YZ2wL5`R6-b$+_l>Hp}#bgYs6J6Y$2XQ}dE>=;0)F+M>jZvzYW)gXzJPi?jHk}n2KXD0a-Jb&b*R3VteSRS zoWuk)tND%rFIO1v05BZo~@2Mw^B&I9{8ZQ5)aqfPqvG#{#PLi_jx`T+$ z9)dvZ6QruUARUF<5IbNfpUh!~ahi=EOuMN+vT+aE-G$8kBzqy+=zEN7ZUfNV%NOmD zN+e*z!5Hmz7qo{zieC33s()+bdUhZ^><3F8h`)*O8sEcB`(a|7hW60s*J@mUxmxm8 z+wC-sv27;AzK!r4*mI3`7v$&c8!*V8i#B|Z#0qIt=zoPJ9}jz(>8R}y$KP*(Pv~S{ zc%>ZK7lm-HFz!yjIf{CG3xn(!ABElK`K1~Aj=|+_1!M5)__M9JN<4nfczP}RIE(0X zEbI0J*=Wu9En{$AlK35A@IPS)AgoEGj6uk4MZUK&KL2+k>^2U1d>eUonT!L~C%ikZ(P>jo`gmRSKKS^*sX`F* zw?`KHu+EJPWl|e9A&mOuMLV}demw(OVcDSz(5~6ccQ%@Q}wh^*O1c*{Nmi*d5M9!^;ZzSyY9m67v5?6pi}kov(g0l z^mdtE$x}YU`sb*|dM+<-h(M-mXFNnUV;NsvmbZtV0yBSC_=8VV>z6v9v-bw3zBAFv zHxa3aUnT1O3`sHL?i;@E`Z=qoEpAsI$=LBfI$W>h5ah0+bOTBFqlXKkgC5R(BN;P| zJXVMCJJ9WN;|ZaOd#gyV5HJub42EvzJw`Y~TZx?77n|C)zPS1M>U%<3`Zu!Y)!anr z4qmY6SJ829_^QTUIO#731CoO1Cs0I6W%_pH_E3PoSfPaPp`AA50hGJfh1acW0bOOl zvQ9_k;$69WKSq%sa_={TPHpX5Xxot@?q5E_ZQ4Yf4Ij;aaecZu^t5EZ%FNLRz6MFO0_3WTe8-Gn^vy?YUo!r zzdJS167@$Ly6Kw^`#$nVgGPWrIWT6q1~3xm5=LgH0cz!Wd(YnI^3zxIUE(o5C#-sQ zga<->iO+i3`IkJ+&jzyd_()e}!m4X*6^3)f*fNPm2HA2)&qSlvQ`m2ZYjR5a5;1QAXhtW}p{^b@syF?L! z5wVcYOYLR=l)XDbgXTSJ>LvP;?`#hVYKHORN&Il_oq79t4f1vp9hYbbqVaw8?_e3( zlWE@@xfS2Ol+J)8rCJ%kNdlJx#s!{lGH-0e;lGxtPx-!y*E=0HN2B6_K&W zxMuIf*gx(iT-oy8jOQhDxqpM0K9{QH{f%aL(WKQ(*3_DMR(ErMlW6qhGb#g6)8QApz>SER~RrX}C5g<|(W16F?$1Wk zI=4SAVkr01aw*kFHwh!{(7f#Tp-4KM1z`f6bUK+uWTF*0r$92DX1c3sl2XR0U0aP+ zcP5;@Ze8(6CY619$z%m@=eoMXWC!=)qtRSC@4NY2 zNB`AB72co)l9``kn@E4iX8c88QD(}-7t!3=RJbA=mGTdx%h}3A3L$?`Rmvobi2g*$ z%$3PEG2>LuwaEXYX3SKakKbRNEmj79{Qeu_e?#Y-{P(;S? zB8w`fhMn*yKJS#SNRye-R6*WA?KE7&*(yFLqw#N3te#d%sbjL|r-~-qq?Gc_WWD?H zr!y^%l>ZX#dZT(R-zzeT443t~8k;~K=_T30RA!hV8luJiDwMKfrM9U60U26b!&29L zR+(}k9qDe>=N68IUxk{K^1sBb#QPmozuK5Gxnjh7gj!N3ZzeWv`-M_{afy!)IzuHo zU~r&Rsp%SIW;B#YaE}dRNr73{hgrm}#R`p)3X42owx|9P2E51N{Pn$>_JYQuNiup4 z>*`-`Dx!fJwu=JOeu~x@OU?KCAr1z6g$A~bj>Ki0vnU;P%J>o!wUu;R%X*wAD>lY0 z%(5U}oE0}S%&IuvJXD`SCVR#D+NxIZ#ywniWVK!;Y?m)FS)tQ9+AB(XR!Y1sgRFLQ z?&AOMkM6*jU7_NTCWb*a9lT~OJZoiF@x`_NtxwOAtY^w~A`@?E;$06&_Y^82;(Jco57TrUuWOgE zOtT_wQZQFph`DPmPfY%{GSkFXx%S~Y^Zch}pbe~B;_*QJNRy7;bNc%_#C(`;Oib&i zm~|Zc8LpFg^Wl3`i{&ao8Z&|0(IRt&eRXCcyVJ2aa~wrAsV$ytSBBK$i9wpXIji$o z@<4~=UMqVFCFeNzX+lu5q~aK6%Yo=#{M+}4>_k$l_&y);ZbH1DlB|j^)|wfo*Y^Be zf<;wUHrYx{oUl8=s8Q&A9+C3Xv|2)r6Qj?{$_jJo4z0&d7$?U#9lL zf7x2+uV+ZOddh0P4{2|-1b^}?r`H?57nUl*f1?B`s2hX#M0&WNl5o5 z`r8WOwd{{{q17YIY0X+blwme47rTcLKe+_4hzhPcB;PMAJ8AQD8A<|f!4}(!0sob2 zprmgLkn+S59r}CHRv&nV_VYz_>4G@`A;HfNdchY2Xq;9TUvX*`z1 zXm*`8#r{>A`yM)}$FaoW(w7hxyuVh+brwIFATCF^7P#I8_F?XMlzS-J1ioJ{KFM7w vnJZ*;7G^n}7%P(lXRKcVXY4*VSIjA1cM5;z9x@#03e+L3LA@~0|1a%-o>Apf delta 193742 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≶KJoGX1)uFC(TEyEBKI{g!&~x^I*E@~ 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 diff --git a/pc-bios/efi-eepro100.rom b/pc-bios/efi-eepro100.rom index 677a8c373eea96e6221b925ce3f8da1eefa1ce8b..2a92d6f32ff303b330fc5ee0cea80260a76db45d 100644 GIT binary patch literal 197632 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