diff options
65 files changed, 3070 insertions, 332 deletions
diff --git a/.gitignore b/.gitignore index 824c0d24df..3ef77d0622 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ trace-dtrace.dtrace *-linux-user *-bsd-user libdis* +libhw libhw32 libhw64 libuser diff --git a/MAINTAINERS b/MAINTAINERS index 25733fc864..29aac4fce6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -474,6 +474,11 @@ M: Gerd Hoffmann <kraxel@redhat.com> S: Maintained F: hw/usb* +VFIO +M: Alex Williamson <alex.williamson@redhat.com> +S: Supported +F: hw/vfio* + vhost M: Michael S. Tsirkin <mst@redhat.com> S: Supported @@ -52,8 +52,13 @@ SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory) BUILD_DIR=$(BUILD_DIR) SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(TARGET_DIRS)) SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %/config-devices.mak.d, $(TARGET_DIRS)) +ifeq ($(SUBDIR_DEVICES_MAK),) +config-all-devices.mak: + $(call quiet-command,echo '# no devices' > $@," GEN $@") +else config-all-devices.mak: $(SUBDIR_DEVICES_MAK) $(call quiet-command,cat $(SUBDIR_DEVICES_MAK) | grep =y | sort -u > $@," GEN $@") +endif -include $(SUBDIR_DEVICES_MAK_DEP) @@ -209,7 +214,7 @@ $(qga-obj-y) qemu-ga.o: $(QGALIB_GEN) qemu-ga$(EXESUF): qemu-ga.o $(qga-obj-y) $(tools-obj-y) $(qapi-obj-y) $(qobject-obj-y) $(version-obj-y) -QEMULIBS=libhw32 libhw64 libuser libdis libdis-user +QEMULIBS=libhw libuser libdis libdis-user clean: # avoid old build problems by removing potentially incorrect old files diff --git a/Makefile.hw b/Makefile.hw index 59f5b48350..86f0bf40f4 100644 --- a/Makefile.hw +++ b/Makefile.hw @@ -2,7 +2,6 @@ include ../config-host.mak include ../config-all-devices.mak -include config.mak include $(SRC_PATH)/rules.mak .PHONY: all diff --git a/Makefile.target b/Makefile.target index d9d54b8dd0..4449444a0e 100644 --- a/Makefile.target +++ b/Makefile.target @@ -4,9 +4,6 @@ include ../config-host.mak include config-devices.mak include config-target.mak include $(SRC_PATH)/rules.mak -ifneq ($(HWDIR),) -include $(HWDIR)/config.mak -endif $(call set-vpath, $(SRC_PATH)) ifdef CONFIG_LINUX @@ -1286,10 +1286,6 @@ if test -z "$target_list" ; then else target_list=`echo "$target_list" | sed -e 's/,/ /g'` fi -if test -z "$target_list" ; then - echo "No targets enabled" - exit 1 -fi # see if system emulation was really requested case " $target_list " in *"-softmmu "*) softmmu=yes @@ -3698,7 +3694,6 @@ TARGET_ABI_DIR="" case "$target_arch2" in i386) - target_phys_bits=64 ;; x86_64) TARGET_BASE_ARCH=i386 @@ -3706,7 +3701,6 @@ case "$target_arch2" in target_long_alignment=8 ;; alpha) - target_phys_bits=64 target_long_alignment=8 target_nptl="yes" ;; @@ -3715,22 +3709,18 @@ case "$target_arch2" in bflt="yes" target_nptl="yes" gdb_xml_files="arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml" - target_phys_bits=64 target_llong_alignment=4 target_libs_softmmu="$fdt_libs" ;; cris) target_nptl="yes" - target_phys_bits=32 ;; lm32) - target_phys_bits=32 target_libs_softmmu="$opengl_libs" ;; m68k) bflt="yes" gdb_xml_files="cf-core.xml cf-fp.xml" - target_phys_bits=32 target_int_alignment=2 target_long_alignment=2 target_llong_alignment=2 @@ -3739,36 +3729,30 @@ case "$target_arch2" in TARGET_ARCH=microblaze bflt="yes" target_nptl="yes" - target_phys_bits=32 target_libs_softmmu="$fdt_libs" ;; mips|mipsel) TARGET_ARCH=mips echo "TARGET_ABI_MIPSO32=y" >> $config_target_mak target_nptl="yes" - target_phys_bits=64 ;; mipsn32|mipsn32el) TARGET_ARCH=mipsn32 TARGET_BASE_ARCH=mips echo "TARGET_ABI_MIPSN32=y" >> $config_target_mak - target_phys_bits=64 ;; mips64|mips64el) TARGET_ARCH=mips64 TARGET_BASE_ARCH=mips echo "TARGET_ABI_MIPSN64=y" >> $config_target_mak - target_phys_bits=64 target_long_alignment=8 ;; or32) TARGET_ARCH=openrisc TARGET_BASE_ARCH=openrisc - target_phys_bits=32 ;; ppc) gdb_xml_files="power-core.xml power-fpu.xml power-altivec.xml power-spe.xml" - target_phys_bits=64 target_nptl="yes" target_libs_softmmu="$fdt_libs" ;; @@ -3776,7 +3760,6 @@ case "$target_arch2" in TARGET_BASE_ARCH=ppc TARGET_ABI_DIR=ppc gdb_xml_files="power-core.xml power-fpu.xml power-altivec.xml power-spe.xml" - target_phys_bits=64 target_nptl="yes" target_libs_softmmu="$fdt_libs" ;; @@ -3784,7 +3767,6 @@ case "$target_arch2" in TARGET_BASE_ARCH=ppc TARGET_ABI_DIR=ppc gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml" - target_phys_bits=64 target_long_alignment=8 target_libs_softmmu="$fdt_libs" ;; @@ -3794,21 +3776,17 @@ case "$target_arch2" in TARGET_ABI_DIR=ppc echo "TARGET_ABI32=y" >> $config_target_mak gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml" - target_phys_bits=64 target_libs_softmmu="$fdt_libs" ;; sh4|sh4eb) TARGET_ARCH=sh4 bflt="yes" target_nptl="yes" - target_phys_bits=32 ;; sparc) - target_phys_bits=64 ;; sparc64) TARGET_BASE_ARCH=sparc - target_phys_bits=64 target_long_alignment=8 ;; sparc32plus) @@ -3816,11 +3794,9 @@ case "$target_arch2" in TARGET_BASE_ARCH=sparc TARGET_ABI_DIR=sparc echo "TARGET_ABI32=y" >> $config_target_mak - target_phys_bits=64 ;; s390x) target_nptl="yes" - target_phys_bits=64 target_long_alignment=8 ;; unicore32) @@ -3828,7 +3804,6 @@ case "$target_arch2" in ;; xtensa|xtensaeb) TARGET_ARCH=xtensa - target_phys_bits=32 ;; *) echo "Unsupported target CPU" @@ -3863,7 +3838,6 @@ echo "TARGET_ABI_DIR=$TARGET_ABI_DIR" >> $config_target_mak case "$target_arch2" in i386|x86_64) if test "$xen" = "yes" -a "$target_softmmu" = "yes" ; then - target_phys_bits=64 echo "CONFIG_XEN=y" >> $config_target_mak if test "$xen_pci_passthrough" = yes; then echo "CONFIG_XEN_PCI_PASSTHROUGH=y" >> "$config_target_mak" @@ -3903,11 +3877,10 @@ if test "$target_bigendian" = "yes" ; then echo "TARGET_WORDS_BIGENDIAN=y" >> $config_target_mak fi if test "$target_softmmu" = "yes" ; then - echo "TARGET_PHYS_ADDR_BITS=$target_phys_bits" >> $config_target_mak echo "CONFIG_SOFTMMU=y" >> $config_target_mak echo "LIBS+=$libs_softmmu $target_libs_softmmu" >> $config_target_mak - echo "HWDIR=../libhw$target_phys_bits" >> $config_target_mak - echo "subdir-$target: subdir-libhw$target_phys_bits" >> $config_host_mak + echo "HWDIR=../libhw" >> $config_target_mak + echo "subdir-$target: subdir-libhw" >> $config_host_mak if test "$smartcard_nss" = "yes" ; then echo "subdir-$target: subdir-libcacard" >> $config_host_mak fi @@ -4149,11 +4122,8 @@ for rom in seabios vgabios ; do echo "LD=$ld" >> $config_mak done -for hwlib in 32 64; do - d=libhw$hwlib - symlink "$source_path/Makefile.hw" "$d/Makefile" - echo "QEMU_CFLAGS+=-DTARGET_PHYS_ADDR_BITS=$hwlib" > $d/config.mak -done +d=libhw +symlink "$source_path/Makefile.hw" "$d/Makefile" d=libuser symlink "$source_path/Makefile.user" "$d/Makefile" diff --git a/cpu-common.h b/cpu-common.h index 85548de5ea..c0d27afd82 100644 --- a/cpu-common.h +++ b/cpu-common.h @@ -21,7 +21,7 @@ enum device_endian { }; /* address in the RAM (different from a physical address) */ -#if defined(CONFIG_XEN_BACKEND) && TARGET_PHYS_ADDR_BITS == 64 +#if defined(CONFIG_XEN_BACKEND) typedef uint64_t ram_addr_t; # define RAM_ADDR_MAX UINT64_MAX # define RAM_ADDR_FMT "%" PRIx64 @@ -31,7 +31,7 @@ struct QEMUSGList { DMAContext *dma; }; -#if defined(TARGET_PHYS_ADDR_BITS) +#ifndef CONFIG_USER_ONLY /* * When an IOMMU is present, bus addresses become distinct from diff --git a/fpu/softfloat.c b/fpu/softfloat.c index b29256a8eb..841314686b 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -1238,7 +1238,7 @@ float32 uint64_to_float32( uint64 a STATUS_PARAM ) if ( a == 0 ) return float32_zero; shiftCount = countLeadingZeros64( a ) - 40; if ( 0 <= shiftCount ) { - return packFloat32( 1 > 0, 0x95 - shiftCount, a<<shiftCount ); + return packFloat32(0, 0x95 - shiftCount, a<<shiftCount); } else { shiftCount += 7; @@ -1248,7 +1248,7 @@ float32 uint64_to_float32( uint64 a STATUS_PARAM ) else { a <<= shiftCount; } - return roundAndPackFloat32( 1 > 0, 0x9C - shiftCount, a STATUS_VAR ); + return roundAndPackFloat32(0, 0x9C - shiftCount, a STATUS_VAR); } } @@ -3007,7 +3007,7 @@ float32 float16_to_float32(float16 a, flag ieee STATUS_PARAM) if (aSig) { return commonNaNToFloat32(float16ToCommonNaN(a STATUS_VAR) STATUS_VAR); } - return packFloat32(aSign, 0xff, aSig << 13); + return packFloat32(aSign, 0xff, 0); } if (aExp == 0) { int8 shiftCount; diff --git a/hw/Makefile.objs b/hw/Makefile.objs index 6dfebd2978..ecdbe44ddd 100644 --- a/hw/Makefile.objs +++ b/hw/Makefile.objs @@ -198,7 +198,8 @@ obj-$(CONFIG_VGA) += vga.o obj-$(CONFIG_SOFTMMU) += device-hotplug.o obj-$(CONFIG_XEN) += xen_domainbuild.o xen_machine_pv.o -# Inter-VM PCI shared memory +# Inter-VM PCI shared memory & VFIO PCI device assignment ifeq ($(CONFIG_PCI), y) obj-$(CONFIG_KVM) += ivshmem.o +obj-$(CONFIG_LINUX) += vfio_pci.o endif diff --git a/hw/acpi_piix4.c b/hw/acpi_piix4.c index c56220b368..15275cf3e5 100644 --- a/hw/acpi_piix4.c +++ b/hw/acpi_piix4.c @@ -67,6 +67,7 @@ typedef struct PIIX4PMState { qemu_irq smi_irq; int kvm_enabled; Notifier machine_ready; + Notifier powerdown_notifier; /* for pci hotplug */ struct pci_status pci0_status; @@ -362,9 +363,9 @@ static void piix4_reset(void *opaque) piix4_update_hotplug(s); } -static void piix4_powerdown(void *opaque, int irq, int power_failing) +static void piix4_pm_powerdown_req(Notifier *n, void *opaque) { - PIIX4PMState *s = opaque; + PIIX4PMState *s = container_of(n, PIIX4PMState, powerdown_notifier); assert(s != NULL); acpi_pm1_evt_power_down(&s->ar); @@ -416,7 +417,8 @@ static int piix4_pm_initfn(PCIDevice *dev) acpi_pm_tmr_init(&s->ar, pm_tmr_timer); acpi_gpe_init(&s->ar, GPE_LEN); - qemu_system_powerdown = *qemu_allocate_irqs(piix4_powerdown, s, 1); + s->powerdown_notifier.notify = piix4_pm_powerdown_req; + qemu_register_powerdown_notifier(&s->powerdown_notifier); pm_smbus_init(&s->dev.qdev, &s->smb); s->machine_ready.notify = piix4_pm_machine_ready; diff --git a/hw/armv7m_nvic.c b/hw/armv7m_nvic.c index 6a0832eb3f..5c09116478 100644 --- a/hw/armv7m_nvic.c +++ b/hw/armv7m_nvic.c @@ -489,7 +489,8 @@ static int armv7m_nvic_init(SysBusDevice *dev) */ memory_region_init_alias(&s->gic_iomem_alias, "nvic-gic", &s->gic.iomem, 0x100, 0xc00); - memory_region_add_subregion_overlap(&s->container, 0x100, &s->gic.iomem, 1); + memory_region_add_subregion_overlap(&s->container, 0x100, + &s->gic_iomem_alias, 1); /* Map the whole thing into system memory at the location required * by the v7M architecture. */ diff --git a/hw/fw_cfg.c b/hw/fw_cfg.c index 7b3b5769a2..dcde1a943c 100644 --- a/hw/fw_cfg.c +++ b/hw/fw_cfg.c @@ -183,6 +183,30 @@ static void fw_cfg_bootsplash(FWCfgState *s) } } +static void fw_cfg_reboot(FWCfgState *s) +{ + int reboot_timeout = -1; + char *p; + const char *temp; + + /* get user configuration */ + QemuOptsList *plist = qemu_find_opts("boot-opts"); + QemuOpts *opts = QTAILQ_FIRST(&plist->head); + if (opts != NULL) { + temp = qemu_opt_get(opts, "reboot-timeout"); + if (temp != NULL) { + p = (char *)temp; + reboot_timeout = strtol(p, (char **)&p, 10); + } + } + /* validate the input */ + if (reboot_timeout > 0xffff) { + error_report("reboot timeout is larger than 65535, force it to 65535."); + reboot_timeout = 0xffff; + } + fw_cfg_add_file(s, "etc/boot-fail-wait", g_memdup(&reboot_timeout, 4), 4); +} + static void fw_cfg_write(FWCfgState *s, uint8_t value) { int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL); @@ -497,6 +521,7 @@ FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port, fw_cfg_add_i16(s, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); fw_cfg_add_i16(s, FW_CFG_BOOT_MENU, (uint16_t)boot_menu); fw_cfg_bootsplash(s); + fw_cfg_reboot(s); s->machine_ready.notify = fw_cfg_machine_ready; qemu_add_machine_init_done_notifier(&s->machine_ready); @@ -4,7 +4,7 @@ #include "qemu-common.h" -#if defined(TARGET_PHYS_ADDR_BITS) && !defined(NEED_CPU_H) +#if !defined(CONFIG_USER_ONLY) && !defined(NEED_CPU_H) #include "cpu-common.h" #endif diff --git a/hw/intel-hda.c b/hw/intel-hda.c index 127e81888b..d8e1b23a60 100644 --- a/hw/intel-hda.c +++ b/hw/intel-hda.c @@ -210,13 +210,7 @@ static target_phys_addr_t intel_hda_addr(uint32_t lbase, uint32_t ubase) { target_phys_addr_t addr; -#if TARGET_PHYS_ADDR_BITS == 32 - addr = lbase; -#else - addr = ubase; - addr <<= 32; - addr |= lbase; -#endif + addr = ((uint64_t)ubase << 32) | lbase; return addr; } diff --git a/hw/ivshmem.c b/hw/ivshmem.c index 62fe53ae2b..59f1aa4265 100644 --- a/hw/ivshmem.c +++ b/hw/ivshmem.c @@ -71,6 +71,8 @@ typedef struct IVShmemState { MemoryRegion bar; MemoryRegion ivshmem; uint64_t ivshmem_size; /* size of shared memory region */ + uint32_t ivshmem_attr; + uint32_t ivshmem_64bit; int shm_fd; /* shared memory file descriptor */ Peer *peers; @@ -339,7 +341,7 @@ static void create_shared_memory_BAR(IVShmemState *s, int fd) { memory_region_add_subregion(&s->bar, 0, &s->ivshmem); /* region for shared memory */ - pci_register_bar(&s->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar); + pci_register_bar(&s->dev, 2, s->ivshmem_attr, &s->bar); } static void ivshmem_add_eventfd(IVShmemState *s, int posn, int i) @@ -690,6 +692,11 @@ static int pci_ivshmem_init(PCIDevice *dev) &s->ivshmem_mmio); memory_region_init(&s->bar, "ivshmem-bar2-container", s->ivshmem_size); + s->ivshmem_attr = PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_PREFETCH; + if (s->ivshmem_64bit) { + s->ivshmem_attr |= PCI_BASE_ADDRESS_MEM_TYPE_64; + } if ((s->server_chr != NULL) && (strncmp(s->server_chr->filename, "unix:", 5) == 0)) { @@ -715,8 +722,7 @@ static int pci_ivshmem_init(PCIDevice *dev) /* allocate/initialize space for interrupt handling */ s->peers = g_malloc0(s->nb_peers * sizeof(Peer)); - pci_register_bar(&s->dev, 2, - PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar); + pci_register_bar(&s->dev, 2, s->ivshmem_attr, &s->bar); s->eventfd_chr = g_malloc0(s->vectors * sizeof(CharDriverState *)); @@ -786,6 +792,7 @@ static Property ivshmem_properties[] = { DEFINE_PROP_BIT("msi", IVShmemState, features, IVSHMEM_MSI, true), DEFINE_PROP_STRING("shm", IVShmemState, shmobj), DEFINE_PROP_STRING("role", IVShmemState, role), + DEFINE_PROP_UINT32("use64", IVShmemState, ivshmem_64bit, 1), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/nseries.c b/hw/nseries.c index 4df2670327..6df71ebb48 100644 --- a/hw/nseries.c +++ b/hw/nseries.c @@ -189,6 +189,17 @@ static void n8x0_nand_setup(struct n800_s *s) /* XXX: in theory should also update the OOB for both pages */ } +static qemu_irq n8x0_system_powerdown; + +static void n8x0_powerdown_req(Notifier *n, void *opaque) +{ + qemu_irq_raise(n8x0_system_powerdown); +} + +static Notifier n8x0_system_powerdown_notifier = { + .notify = n8x0_powerdown_req +}; + static void n8x0_i2c_setup(struct n800_s *s) { DeviceState *dev; @@ -201,7 +212,8 @@ static void n8x0_i2c_setup(struct n800_s *s) qdev_get_gpio_in(s->mpu->ih[0], OMAP_INT_24XX_SYS_NIRQ)); - qemu_system_powerdown = qdev_get_gpio_in(dev, 3); + n8x0_system_powerdown = qdev_get_gpio_in(dev, 3); + qemu_register_powerdown_notifier(&n8x0_system_powerdown_notifier); /* Attach a TMP105 PM chip (A0 wired to ground) */ dev = i2c_create_slave(i2c, "tmp105", N8X0_TMP105_ADDR); diff --git a/hw/pc_piix.c b/hw/pc_piix.c index afd8361dfb..fd5898fba6 100644 --- a/hw/pc_piix.c +++ b/hw/pc_piix.c @@ -367,6 +367,10 @@ static QEMUMachine pc_machine_v1_3 = { .driver = "nec-usb-xhci",\ .property = "msix",\ .value = "off",\ + },{\ + .driver = "ivshmem",\ + .property = "use64",\ + .value = "0",\ } static QEMUMachine pc_machine_v1_2 = { diff --git a/hw/pl190.c b/hw/pl190.c index cb50afb9f4..7332f4dbae 100644 --- a/hw/pl190.c +++ b/hw/pl190.c @@ -117,12 +117,18 @@ static uint64_t pl190_read(void *opaque, target_phys_addr_t offset, return s->protected; case 12: /* VECTADDR */ /* Read vector address at the start of an ISR. Increases the - current priority level to that of the current interrupt. */ - for (i = 0; i < s->priority; i++) - { - if ((s->level | s->soft_level) & s->prio_mask[i]) - break; - } + * current priority level to that of the current interrupt. + * + * Since an enabled interrupt X at priority P causes prio_mask[Y] + * to have bit X set for all Y > P, this loop will stop with + * i == the priority of the highest priority set interrupt. + */ + for (i = 0; i < s->priority; i++) { + if ((s->level | s->soft_level) & s->prio_mask[i + 1]) { + break; + } + } + /* Reading this value with no pending interrupts is undefined. We return the default address. */ if (i == PL190_NUM_PRIO) diff --git a/hw/rtl8139.c b/hw/rtl8139.c index 844f1b8c3f..b7c82ee027 100644 --- a/hw/rtl8139.c +++ b/hw/rtl8139.c @@ -774,11 +774,7 @@ static void rtl8139_write_buffer(RTL8139State *s, const void *buf, int size) #define MIN_BUF_SIZE 60 static inline dma_addr_t rtl8139_addr64(uint32_t low, uint32_t high) { -#if TARGET_PHYS_ADDR_BITS > 32 - return low | ((target_phys_addr_t)high << 32); -#else - return low; -#endif + return low | ((uint64_t)high << 32); } /* Workaround for buggy guest driver such as linux who allocates rx diff --git a/hw/sun4m.c b/hw/sun4m.c index 0f909b5f86..c98cd5ec3f 100644 --- a/hw/sun4m.c +++ b/hw/sun4m.c @@ -472,6 +472,17 @@ static void slavio_timer_init_all(target_phys_addr_t addr, qemu_irq master_irq, } } +static qemu_irq slavio_system_powerdown; + +static void slavio_powerdown_req(Notifier *n, void *opaque) +{ + qemu_irq_raise(slavio_system_powerdown); +} + +static Notifier slavio_system_powerdown_notifier = { + .notify = slavio_powerdown_req +}; + #define MISC_LEDS 0x01600000 #define MISC_CFG 0x01800000 #define MISC_DIAG 0x01a00000 @@ -514,7 +525,8 @@ static void slavio_misc_init(target_phys_addr_t base, } sysbus_connect_irq(s, 0, irq); sysbus_connect_irq(s, 1, fdc_tc); - qemu_system_powerdown = qdev_get_gpio_in(dev, 0); + slavio_system_powerdown = qdev_get_gpio_in(dev, 0); + qemu_register_powerdown_notifier(&slavio_system_powerdown_notifier); } static void ecc_init(target_phys_addr_t base, qemu_irq irq, uint32_t version) diff --git a/hw/vexpress.c b/hw/vexpress.c index b6158447d7..3596d1e33f 100644 --- a/hw/vexpress.c +++ b/hw/vexpress.c @@ -29,8 +29,12 @@ #include "sysemu.h" #include "boards.h" #include "exec-memory.h" +#include "blockdev.h" +#include "flash.h" #define VEXPRESS_BOARD_ID 0x8e0 +#define VEXPRESS_FLASH_SIZE (64 * 1024 * 1024) +#define VEXPRESS_FLASH_SECT_SIZE (256 * 1024) static struct arm_boot_info vexpress_binfo; @@ -62,7 +66,6 @@ enum { VE_COMPACTFLASH, VE_CLCD, VE_NORFLASH0, - VE_NORFLASH0ALIAS, VE_NORFLASH1, VE_SRAM, VE_VIDEORAM, @@ -104,9 +107,8 @@ static target_phys_addr_t motherboard_legacy_map[] = { }; static target_phys_addr_t motherboard_aseries_map[] = { - /* CS0: 0x00000000 .. 0x0c000000 */ - [VE_NORFLASH0] = 0x00000000, - [VE_NORFLASH0ALIAS] = 0x08000000, + /* CS0: 0x08000000 .. 0x0c000000 */ + [VE_NORFLASH0] = 0x08000000, /* CS4: 0x0c000000 .. 0x10000000 */ [VE_NORFLASH1] = 0x0c000000, /* CS5: 0x10000000 .. 0x14000000 */ @@ -357,6 +359,7 @@ static void vexpress_common_init(const VEDBoardInfo *daughterboard, qemu_irq pic[64]; uint32_t proc_id; uint32_t sys_id; + DriveInfo *dinfo; ram_addr_t vram_size, sram_size; MemoryRegion *sysmem = get_system_memory(); MemoryRegion *vram = g_new(MemoryRegion, 1); @@ -412,9 +415,25 @@ static void vexpress_common_init(const VEDBoardInfo *daughterboard, sysbus_create_simple("pl111", map[VE_CLCD], pic[14]); - /* VE_NORFLASH0: not modelled */ - /* VE_NORFLASH0ALIAS: not modelled */ - /* VE_NORFLASH1: not modelled */ + dinfo = drive_get_next(IF_PFLASH); + if (!pflash_cfi01_register(map[VE_NORFLASH0], NULL, "vexpress.flash0", + VEXPRESS_FLASH_SIZE, dinfo ? dinfo->bdrv : NULL, + VEXPRESS_FLASH_SECT_SIZE, + VEXPRESS_FLASH_SIZE / VEXPRESS_FLASH_SECT_SIZE, 4, + 0x00, 0x89, 0x00, 0x18, 0)) { + fprintf(stderr, "vexpress: error registering flash 0.\n"); + exit(1); + } + + dinfo = drive_get_next(IF_PFLASH); + if (!pflash_cfi01_register(map[VE_NORFLASH1], NULL, "vexpress.flash1", + VEXPRESS_FLASH_SIZE, dinfo ? dinfo->bdrv : NULL, + VEXPRESS_FLASH_SECT_SIZE, + VEXPRESS_FLASH_SIZE / VEXPRESS_FLASH_SECT_SIZE, 4, + 0x00, 0x89, 0x00, 0x18, 0)) { + fprintf(stderr, "vexpress: error registering flash 1.\n"); + exit(1); + } sram_size = 0x2000000; memory_region_init_ram(sram, "vexpress.sram", sram_size); diff --git a/hw/vfio_pci.c b/hw/vfio_pci.c new file mode 100644 index 0000000000..a1eeced8fd --- /dev/null +++ b/hw/vfio_pci.c @@ -0,0 +1,1864 @@ +/* + * vfio based device assignment support + * + * Copyright Red Hat, Inc. 2012 + * + * Authors: + * Alex Williamson <alex.williamson@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * 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 <dirent.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <linux/vfio.h> + +#include "config.h" +#include "event_notifier.h" +#include "exec-memory.h" +#include "kvm.h" +#include "memory.h" +#include "msi.h" +#include "msix.h" +#include "qemu-error.h" +#include "range.h" +#include "vfio_pci_int.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 + +#define MSIX_CAP_LENGTH 12 + +static QLIST_HEAD(, VFIOContainer) + container_list = QLIST_HEAD_INITIALIZER(container_list); + +static QLIST_HEAD(, VFIOGroup) + group_list = QLIST_HEAD_INITIALIZER(group_list); + +static void vfio_disable_interrupts(VFIODevice *vdev); +static uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, 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); + + vdev->interrupt = VFIO_INT_NONE; +} + +/* + * 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); +} + +static void vfio_intx_interrupt(void *opaque) +{ + VFIODevice *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); + + vdev->intx.pending = true; + qemu_set_irq(vdev->pdev.irq[vdev->intx.pin], 1); +} + +static void vfio_eoi(VFIODevice *vdev) +{ + 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); + + vdev->intx.pending = false; + qemu_set_irq(vdev->pdev.irq[vdev->intx.pin], 0); + vfio_unmask_intx(vdev); +} + +typedef struct QEMU_PACKED VFIOIRQSetFD { + struct vfio_irq_set irq_set; + int32_t fd; +} VFIOIRQSetFD; + +static int vfio_enable_intx(VFIODevice *vdev) +{ + VFIOIRQSetFD irq_set_fd = { + .irq_set = { + .argsz = sizeof(irq_set_fd), + .flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER, + .index = VFIO_PCI_INTX_IRQ_INDEX, + .start = 0, + .count = 1, + }, + }; + uint8_t pin = vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1); + int ret; + + if (vdev->intx.disabled || !pin) { + return 0; + } + + vfio_disable_interrupts(vdev); + + vdev->intx.pin = pin - 1; /* Pin A (1) -> irq[0] */ + ret = event_notifier_init(&vdev->intx.interrupt, 0); + if (ret) { + error_report("vfio: Error: event_notifier_init failed\n"); + return ret; + } + + irq_set_fd.fd = event_notifier_get_fd(&vdev->intx.interrupt); + qemu_set_fd_handler(irq_set_fd.fd, vfio_intx_interrupt, NULL, vdev); + + if (ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &irq_set_fd)) { + error_report("vfio: Error: Failed to setup INTx fd: %m\n"); + return -errno; + } + + /* + * Disable mmaps so we can trap on BAR accesses. We interpret any + * access as a response to an interrupt and unmask the physical + * device. The device will re-assert if the interrupt is still + * pending. We'll likely retrigger on the host multiple times per + * guest interrupt, but without EOI notification it's better than + * nothing. Acceleration paths through KVM will avoid this. + */ + vfio_mmap_set_enabled(vdev, false); + + 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); + + return 0; +} + +static void vfio_disable_intx(VFIODevice *vdev) +{ + int fd; + + vfio_disable_irqindex(vdev, VFIO_PCI_INTX_IRQ_INDEX); + vdev->intx.pending = false; + qemu_set_irq(vdev->pdev.irq[vdev->intx.pin], 0); + vfio_mmap_set_enabled(vdev, true); + + fd = event_notifier_get_fd(&vdev->intx.interrupt); + qemu_set_fd_handler(fd, NULL, NULL, vdev); + event_notifier_cleanup(&vdev->intx.interrupt); + + vdev->interrupt = VFIO_INT_NONE; + + DPRINTF("%s(%04x:%02x:%02x.%x)\n", __func__, vdev->host.domain, + vdev->host.bus, vdev->host.slot, vdev->host.function); +} + +/* + * MSI/X + */ +static void vfio_msi_interrupt(void *opaque) +{ + VFIOMSIVector *vector = opaque; + VFIODevice *vdev = vector->vdev; + int nr = vector - vdev->msi_vectors; + + if (!event_notifier_test_and_clear(&vector->interrupt)) { + return; + } + + DPRINTF("%s(%04x:%02x:%02x.%x) vector %d\n", __func__, + vdev->host.domain, vdev->host.bus, vdev->host.slot, + vdev->host.function, nr); + + if (vdev->interrupt == VFIO_INT_MSIX) { + msix_notify(&vdev->pdev, nr); + } else if (vdev->interrupt == VFIO_INT_MSI) { + msi_notify(&vdev->pdev, nr); + } else { + error_report("vfio: MSI interrupt receieved, but not enabled?\n"); + } +} + +static int vfio_enable_vectors(VFIODevice *vdev, bool msix) +{ + struct vfio_irq_set *irq_set; + int ret = 0, i, argsz; + int32_t *fds; + + argsz = sizeof(*irq_set) + (vdev->nr_vectors * sizeof(*fds)); + + irq_set = g_malloc0(argsz); + irq_set->argsz = argsz; + irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER; + irq_set->index = msix ? VFIO_PCI_MSIX_IRQ_INDEX : VFIO_PCI_MSI_IRQ_INDEX; + irq_set->start = 0; + irq_set->count = vdev->nr_vectors; + fds = (int32_t *)&irq_set->data; + + for (i = 0; i < vdev->nr_vectors; i++) { + if (!vdev->msi_vectors[i].use) { + fds[i] = -1; + continue; + } + + fds[i] = event_notifier_get_fd(&vdev->msi_vectors[i].interrupt); + } + + ret = ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, irq_set); + + g_free(irq_set); + + if (!ret) { + vdev->interrupt = msix ? VFIO_INT_MSIX : VFIO_INT_MSI; + } + + return ret; +} + +static int vfio_msix_vector_use(PCIDevice *pdev, + unsigned int nr, MSIMessage msg) +{ + VFIODevice *vdev = DO_UPCAST(VFIODevice, 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); + + if (vdev->interrupt != VFIO_INT_MSIX) { + vfio_disable_interrupts(vdev); + } + + if (!vdev->msi_vectors) { + vdev->msi_vectors = g_malloc0(vdev->msix->entries * + sizeof(VFIOMSIVector)); + } + + vector = &vdev->msi_vectors[nr]; + vector->vdev = vdev; + vector->use = true; + + msix_vector_use(pdev, nr); + + if (event_notifier_init(&vector->interrupt, 0)) { + error_report("vfio: Error: event_notifier_init failed\n"); + } + + /* + * Attempt to enable route through KVM irqchip, + * default to userspace handling if unavailable. + */ + vector->virq = kvm_irqchip_add_msi_route(kvm_state, msg); + if (vector->virq < 0 || + kvm_irqchip_add_irqfd_notifier(kvm_state, &vector->interrupt, + vector->virq) < 0) { + if (vector->virq >= 0) { + kvm_irqchip_release_virq(kvm_state, vector->virq); + vector->virq = -1; + } + qemu_set_fd_handler(event_notifier_get_fd(&vector->interrupt), + vfio_msi_interrupt, NULL, vector); + } + + /* + * We don't want to have the host allocate all possible MSI vectors + * for a device if they're not in use, so we shutdown and incrementally + * increase them as needed. + */ + if (vdev->nr_vectors < nr + 1) { + int i; + + vfio_disable_irqindex(vdev, VFIO_PCI_MSIX_IRQ_INDEX); + vdev->nr_vectors = nr + 1; + ret = vfio_enable_vectors(vdev, true); + if (ret) { + error_report("vfio: failed to enable vectors, %d\n", ret); + } + + /* We don't know if we've missed interrupts in the interim... */ + for (i = 0; i < vdev->msix->entries; i++) { + if (vdev->msi_vectors[i].use) { + msix_notify(&vdev->pdev, i); + } + } + } else { + VFIOIRQSetFD irq_set_fd = { + .irq_set = { + .argsz = sizeof(irq_set_fd), + .flags = VFIO_IRQ_SET_DATA_EVENTFD | + VFIO_IRQ_SET_ACTION_TRIGGER, + .index = VFIO_PCI_MSIX_IRQ_INDEX, + .start = nr, + .count = 1, + }, + .fd = event_notifier_get_fd(&vector->interrupt), + }; + ret = ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &irq_set_fd); + if (ret) { + error_report("vfio: failed to modify vector, %d\n", ret); + } + + /* + * If we were connected to the hardware PBA we could skip this, + * until then, a spurious interrupt is better than starvation. + */ + msix_notify(&vdev->pdev, nr); + } + + return 0; +} + +static void vfio_msix_vector_release(PCIDevice *pdev, unsigned int nr) +{ + VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev); + VFIOMSIVector *vector = &vdev->msi_vectors[nr]; + VFIOIRQSetFD irq_set_fd = { + .irq_set = { + .argsz = sizeof(irq_set_fd), + .flags = VFIO_IRQ_SET_DATA_EVENTFD | + VFIO_IRQ_SET_ACTION_TRIGGER, + .index = VFIO_PCI_MSIX_IRQ_INDEX, + .start = nr, + .count = 1, + }, + .fd = -1, + }; + + DPRINTF("%s(%04x:%02x:%02x.%x) vector %d released\n", __func__, + vdev->host.domain, vdev->host.bus, vdev->host.slot, + vdev->host.function, nr); + + /* + * XXX What's the right thing to do here? This turns off the interrupt + * completely, but do we really just want to switch the interrupt to + * bouncing through userspace and let msix.c drop it? Not sure. + */ + msix_vector_unuse(pdev, nr); + ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &irq_set_fd); + + if (vector->virq < 0) { + qemu_set_fd_handler(event_notifier_get_fd(&vector->interrupt), + NULL, NULL, NULL); + } else { + kvm_irqchip_remove_irqfd_notifier(kvm_state, &vector->interrupt, + vector->virq); + kvm_irqchip_release_virq(kvm_state, vector->virq); + vector->virq = -1; + } + + event_notifier_cleanup(&vector->interrupt); + vector->use = false; +} + +/* TODO This should move to msi.c */ +static MSIMessage msi_get_msg(PCIDevice *pdev, unsigned int vector) +{ + uint16_t flags = pci_get_word(pdev->config + pdev->msi_cap + PCI_MSI_FLAGS); + bool msi64bit = flags & PCI_MSI_FLAGS_64BIT; + MSIMessage msg; + + if (msi64bit) { + msg.address = pci_get_quad(pdev->config + + pdev->msi_cap + PCI_MSI_ADDRESS_LO); + } else { + msg.address = pci_get_long(pdev->config + + pdev->msi_cap + PCI_MSI_ADDRESS_LO); + } + + msg.data = pci_get_word(pdev->config + pdev->msi_cap + + (msi64bit ? PCI_MSI_DATA_64 : PCI_MSI_DATA_32)); + msg.data += vector; + + return msg; +} + +/* So should this */ +static void msi_set_qsize(PCIDevice *pdev, uint8_t size) +{ + uint8_t *config = pdev->config + pdev->msi_cap; + uint16_t flags; + + flags = pci_get_word(config + PCI_MSI_FLAGS); + flags = le16_to_cpu(flags); + flags &= ~PCI_MSI_FLAGS_QSIZE; + flags |= (size & 0x7) << 4; + flags = cpu_to_le16(flags); + pci_set_word(config + PCI_MSI_FLAGS, flags); +} + +static void vfio_enable_msi(VFIODevice *vdev) +{ + int ret, i; + + vfio_disable_interrupts(vdev); + + vdev->nr_vectors = msi_nr_vectors_allocated(&vdev->pdev); +retry: + vdev->msi_vectors = g_malloc0(vdev->nr_vectors * sizeof(VFIOMSIVector)); + + for (i = 0; i < vdev->nr_vectors; i++) { + MSIMessage msg; + VFIOMSIVector *vector = &vdev->msi_vectors[i]; + + vector->vdev = vdev; + vector->use = true; + + if (event_notifier_init(&vector->interrupt, 0)) { + error_report("vfio: Error: event_notifier_init failed\n"); + } + + msg = msi_get_msg(&vdev->pdev, i); + + /* + * Attempt to enable route through KVM irqchip, + * default to userspace handling if unavailable. + */ + vector->virq = kvm_irqchip_add_msi_route(kvm_state, msg); + if (vector->virq < 0 || + kvm_irqchip_add_irqfd_notifier(kvm_state, &vector->interrupt, + vector->virq) < 0) { + qemu_set_fd_handler(event_notifier_get_fd(&vector->interrupt), + vfio_msi_interrupt, NULL, vector); + } + } + + ret = vfio_enable_vectors(vdev, false); + if (ret) { + if (ret < 0) { + error_report("vfio: Error: Failed to setup MSI fds: %m\n"); + } else if (ret != vdev->nr_vectors) { + error_report("vfio: Error: Failed to enable %d " + "MSI vectors, retry with %d\n", vdev->nr_vectors, ret); + } + + for (i = 0; i < vdev->nr_vectors; i++) { + VFIOMSIVector *vector = &vdev->msi_vectors[i]; + if (vector->virq >= 0) { + kvm_irqchip_remove_irqfd_notifier(kvm_state, &vector->interrupt, + vector->virq); + kvm_irqchip_release_virq(kvm_state, vector->virq); + vector->virq = -1; + } else { + qemu_set_fd_handler(event_notifier_get_fd(&vector->interrupt), + NULL, NULL, NULL); + } + event_notifier_cleanup(&vector->interrupt); + } + + g_free(vdev->msi_vectors); + + if (ret > 0 && ret != vdev->nr_vectors) { + vdev->nr_vectors = ret; + goto retry; + } + vdev->nr_vectors = 0; + + return; + } + + msi_set_qsize(&vdev->pdev, vdev->nr_vectors); + + 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); +} + +static void vfio_disable_msi_x(VFIODevice *vdev, bool msix) +{ + int i; + + vfio_disable_irqindex(vdev, msix ? VFIO_PCI_MSIX_IRQ_INDEX : + VFIO_PCI_MSI_IRQ_INDEX); + + for (i = 0; i < vdev->nr_vectors; i++) { + VFIOMSIVector *vector = &vdev->msi_vectors[i]; + + if (!vector->use) { + continue; + } + + if (vector->virq >= 0) { + kvm_irqchip_remove_irqfd_notifier(kvm_state, + &vector->interrupt, vector->virq); + kvm_irqchip_release_virq(kvm_state, vector->virq); + vector->virq = -1; + } else { + qemu_set_fd_handler(event_notifier_get_fd(&vector->interrupt), + NULL, NULL, NULL); + } + + if (msix) { + msix_vector_unuse(&vdev->pdev, i); + } + + event_notifier_cleanup(&vector->interrupt); + } + + g_free(vdev->msi_vectors); + vdev->msi_vectors = NULL; + vdev->nr_vectors = 0; + + if (!msix) { + msi_set_qsize(&vdev->pdev, 0); /* Actually still means 1 vector */ + } + + DPRINTF("%s(%04x:%02x:%02x.%x, msi%s)\n", __func__, + vdev->host.domain, vdev->host.bus, vdev->host.slot, + vdev->host.function, msix ? "x" : ""); + + vfio_enable_intx(vdev); +} + +/* + * IO Port/MMIO - Beware of the endians, VFIO is always little endian + */ +static void vfio_bar_write(void *opaque, target_phys_addr_t 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\n", size); + break; + } + + if (pwrite(bar->fd, &buf, size, bar->fd_offset + addr) != size) { + error_report("%s(,0x%"TARGET_PRIxPHYS", 0x%"PRIx64", %d) failed: %m\n", + __func__, addr, data, size); + } + + DPRINTF("%s(BAR%d+0x%"TARGET_PRIxPHYS", 0x%"PRIx64", %d)\n", + __func__, bar->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. + */ + vfio_eoi(DO_UPCAST(VFIODevice, bars[bar->nr], bar)); +} + +static uint64_t vfio_bar_read(void *opaque, + target_phys_addr_t 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%"TARGET_PRIxPHYS", %d) failed: %m\n", + __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\n", size); + break; + } + + DPRINTF("%s(BAR%d+0x%"TARGET_PRIxPHYS", %d) = 0x%"PRIx64"\n", + __func__, bar->nr, addr, size, data); + + /* Same as write above */ + vfio_eoi(DO_UPCAST(VFIODevice, bars[bar->nr], bar)); + + return data; +} + +static const MemoryRegionOps vfio_bar_ops = { + .read = vfio_bar_read, + .write = vfio_bar_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +/* + * PCI config space + */ +static uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len) +{ + VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev); + uint32_t val = 0; + + /* + * We only need QEMU PCI config support for the ROM BAR, the MSI and MSIX + * capabilities, and the multifunction bit below. We let VFIO handle + * virtualizing everything else. Performance is not a concern here. + */ + if (ranges_overlap(addr, len, PCI_ROM_ADDRESS, 4) || + (pdev->cap_present & QEMU_PCI_CAP_MSIX && + ranges_overlap(addr, len, pdev->msix_cap, MSIX_CAP_LENGTH)) || + (pdev->cap_present & QEMU_PCI_CAP_MSI && + ranges_overlap(addr, len, pdev->msi_cap, vdev->msi_cap_size))) { + + val = pci_default_read_config(pdev, addr, len); + } else { + if (pread(vdev->fd, &val, len, vdev->config_offset + addr) != len) { + error_report("%s(%04x:%02x:%02x.%x, 0x%x, 0x%x) failed: %m\n", + __func__, vdev->host.domain, vdev->host.bus, + vdev->host.slot, vdev->host.function, addr, len); + return -errno; + } + val = le32_to_cpu(val); + } + + /* Multifunction bit is virualized in QEMU */ + if (unlikely(ranges_overlap(addr, len, PCI_HEADER_TYPE, 1))) { + uint32_t mask = PCI_HEADER_TYPE_MULTI_FUNCTION; + + if (len == 4) { + mask <<= 16; + } + + if (pdev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { + val |= mask; + } else { + val &= ~mask; + } + } + + 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); + + return val; +} + +static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr, + uint32_t val, int len) +{ + VFIODevice *vdev = DO_UPCAST(VFIODevice, 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); + + /* 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) { + error_report("%s(%04x:%02x:%02x.%x, 0x%x, 0x%x, 0x%x) failed: %m\n", + __func__, vdev->host.domain, vdev->host.bus, + vdev->host.slot, vdev->host.function, addr, val, len); + } + + /* Write standard header bits to emulation */ + if (addr < PCI_CONFIG_HEADER_SIZE) { + pci_default_write_config(pdev, addr, val, len); + return; + } + + /* MSI/MSI-X Enabling/Disabling */ + if (pdev->cap_present & QEMU_PCI_CAP_MSI && + ranges_overlap(addr, len, pdev->msi_cap, vdev->msi_cap_size)) { + int is_enabled, was_enabled = msi_enabled(pdev); + + pci_default_write_config(pdev, addr, val, len); + + is_enabled = msi_enabled(pdev); + + if (!was_enabled && is_enabled) { + vfio_enable_msi(vdev); + } else if (was_enabled && !is_enabled) { + vfio_disable_msi_x(vdev, false); + } + } + + if (pdev->cap_present & QEMU_PCI_CAP_MSIX && + ranges_overlap(addr, len, pdev->msix_cap, MSIX_CAP_LENGTH)) { + int is_enabled, was_enabled = msix_enabled(pdev); + + pci_default_write_config(pdev, addr, val, len); + + is_enabled = msix_enabled(pdev); + + if (!was_enabled && is_enabled) { + /* vfio_msix_vector_use handles this automatically */ + } else if (was_enabled && !is_enabled) { + vfio_disable_msi_x(vdev, true); + } + } +} + +/* + * DMA - Mapping and unmapping for the "type1" IOMMU interface used on x86 + */ +static int vfio_dma_map(VFIOContainer *container, target_phys_addr_t 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)(intptr_t)vaddr, + .iova = iova, + .size = size, + }; + + if (!readonly) { + map.flags |= VFIO_DMA_MAP_FLAG_WRITE; + } + + if (ioctl(container->fd, VFIO_IOMMU_MAP_DMA, &map)) { + DPRINTF("VFIO_MAP_DMA: %d\n", -errno); + return -errno; + } + + return 0; +} + +static int vfio_dma_unmap(VFIOContainer *container, + target_phys_addr_t 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 void vfio_listener_dummy1(MemoryListener *listener) +{ + /* We don't do batching (begin/commit) or care about logging */ +} + +static void vfio_listener_dummy2(MemoryListener *listener, + MemoryRegionSection *section) +{ + /* We don't do logging or care about nops */ +} + +static void vfio_listener_dummy3(MemoryListener *listener, + MemoryRegionSection *section, + bool match_data, uint64_t data, + EventNotifier *e) +{ + /* We don't care about eventfds */ +} + +static bool vfio_listener_skipped_section(MemoryRegionSection *section) +{ + return !memory_region_is_ram(section->mr); +} + +static void vfio_listener_region_add(MemoryListener *listener, + MemoryRegionSection *section) +{ + VFIOContainer *container = container_of(listener, VFIOContainer, + iommu_data.listener); + target_phys_addr_t iova, end; + void *vaddr; + int ret; + + if (vfio_listener_skipped_section(section)) { + DPRINTF("vfio: SKIPPING region_add %"TARGET_PRIxPHYS" - %"PRIx64"\n", + section->offset_within_address_space, + section->offset_within_address_space + section->size - 1); + return; + } + + if (unlikely((section->offset_within_address_space & ~TARGET_PAGE_MASK) != + (section->offset_within_region & ~TARGET_PAGE_MASK))) { + error_report("%s received unaligned region\n", __func__); + return; + } + + iova = TARGET_PAGE_ALIGN(section->offset_within_address_space); + end = (section->offset_within_address_space + section->size) & + TARGET_PAGE_MASK; + + if (iova >= end) { + return; + } + + vaddr = memory_region_get_ram_ptr(section->mr) + + section->offset_within_region + + (iova - section->offset_within_address_space); + + DPRINTF("vfio: region_add %"TARGET_PRIxPHYS" - %"TARGET_PRIxPHYS" [%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%"TARGET_PRIxPHYS", " + "0x%"TARGET_PRIxPHYS", %p) = %d (%m)\n", + container, iova, end - iova, vaddr, ret); + } +} + +static void vfio_listener_region_del(MemoryListener *listener, + MemoryRegionSection *section) +{ + VFIOContainer *container = container_of(listener, VFIOContainer, + iommu_data.listener); + target_phys_addr_t iova, end; + int ret; + + if (vfio_listener_skipped_section(section)) { + DPRINTF("vfio: SKIPPING region_del %"TARGET_PRIxPHYS" - %"PRIx64"\n", + section->offset_within_address_space, + section->offset_within_address_space + section->size - 1); + return; + } + + if (unlikely((section->offset_within_address_space & ~TARGET_PAGE_MASK) != + (section->offset_within_region & ~TARGET_PAGE_MASK))) { + error_report("%s received unaligned region\n", __func__); + return; + } + + iova = TARGET_PAGE_ALIGN(section->offset_within_address_space); + end = (section->offset_within_address_space + section->size) & + TARGET_PAGE_MASK; + + if (iova >= end) { + return; + } + + DPRINTF("vfio: region_del %"TARGET_PRIxPHYS" - %"TARGET_PRIxPHYS"\n", + iova, end - 1); + + ret = vfio_dma_unmap(container, iova, end - iova); + if (ret) { + error_report("vfio_dma_unmap(%p, 0x%"TARGET_PRIxPHYS", " + "0x%"TARGET_PRIxPHYS") = %d (%m)\n", + container, iova, end - iova, ret); + } +} + +static MemoryListener vfio_memory_listener = { + .begin = vfio_listener_dummy1, + .commit = vfio_listener_dummy1, + .region_add = vfio_listener_region_add, + .region_del = vfio_listener_region_del, + .region_nop = vfio_listener_dummy2, + .log_start = vfio_listener_dummy2, + .log_stop = vfio_listener_dummy2, + .log_sync = vfio_listener_dummy2, + .log_global_start = vfio_listener_dummy1, + .log_global_stop = vfio_listener_dummy1, + .eventfd_add = vfio_listener_dummy3, + .eventfd_del = vfio_listener_dummy3, +}; + +static void vfio_listener_release(VFIOContainer *container) +{ + memory_listener_unregister(&container->iommu_data.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_x(vdev, false); + break; + case VFIO_INT_MSIX: + vfio_disable_msi_x(vdev, true); + break; + } +} + +static int vfio_setup_msi(VFIODevice *vdev, int pos) +{ + uint16_t ctrl; + bool msi_64bit, msi_maskbit; + int ret, entries; + + /* + * TODO: don't peek into msi_supported, let msi_init fail and + * check for ENOTSUP + */ + if (!msi_supported) { + return 0; + } + + 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) { + error_report("vfio: msi_init failed\n"); + return ret; + } + vdev->msi_cap_size = 0xa + (msi_maskbit ? 0xa : 0) + (msi_64bit ? 0x4 : 0); + + 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) +{ + int ret; + + /* + * TODO: don't peek into msi_supported, let msix_init fail and + * check for ENOTSUP + */ + if (!msi_supported) { + return 0; + } + + ret = msix_init(&vdev->pdev, vdev->msix->entries, + &vdev->bars[vdev->msix->table_bar].mem, + vdev->msix->table_bar, vdev->msix->table_offset, + &vdev->bars[vdev->msix->pba_bar].mem, + vdev->msix->pba_bar, vdev->msix->pba_offset, pos); + if (ret < 0) { + error_report("vfio: msix_init failed\n"); + return ret; + } + + ret = msix_set_vector_notifiers(&vdev->pdev, vfio_msix_vector_use, + vfio_msix_vector_release); + if (ret) { + error_report("vfio: msix_set_vector_notifiers failed %d\n", ret); + msix_uninit(&vdev->pdev, &vdev->bars[vdev->msix->table_bar].mem, + &vdev->bars[vdev->msix->pba_bar].mem); + return ret; + } + + return 0; +} + +static void vfio_teardown_msi(VFIODevice *vdev) +{ + msi_uninit(&vdev->pdev); + + if (vdev->msix) { + /* FIXME: Why can't unset just silently do nothing?? */ + if (vdev->pdev.msix_vector_use_notifier && + vdev->pdev.msix_vector_release_notifier) { + msix_unset_vector_notifiers(&vdev->pdev); + } + + msix_uninit(&vdev->pdev, &vdev->bars[vdev->msix->table_bar].mem, + &vdev->bars[vdev->msix->pba_bar].mem); + } +} + +/* + * Resource setup + */ +static void vfio_mmap_set_enabled(VFIODevice *vdev, bool enabled) +{ + int i; + + for (i = 0; i < PCI_ROM_SLOT; i++) { + VFIOBAR *bar = &vdev->bars[i]; + + if (!bar->size) { + continue; + } + + memory_region_set_enabled(&bar->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) +{ + VFIOBAR *bar = &vdev->bars[nr]; + + if (!bar->size) { + return; + } + + memory_region_del_subregion(&bar->mem, &bar->mmap_mem); + munmap(bar->mmap, memory_region_size(&bar->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_destroy(&bar->mem); +} + +static int vfio_mmap_bar(VFIOBAR *bar, MemoryRegion *mem, MemoryRegion *submem, + void **map, size_t size, off_t offset, + const char *name) +{ + int ret = 0; + + if (size && bar->flags & VFIO_REGION_INFO_FLAG_MMAP) { + int prot = 0; + + if (bar->flags & VFIO_REGION_INFO_FLAG_READ) { + prot |= PROT_READ; + } + + if (bar->flags & VFIO_REGION_INFO_FLAG_WRITE) { + prot |= PROT_WRITE; + } + + *map = mmap(NULL, size, prot, MAP_SHARED, + bar->fd, bar->fd_offset + offset); + if (*map == MAP_FAILED) { + *map = NULL; + ret = -errno; + goto empty_region; + } + + memory_region_init_ram_ptr(submem, name, size, *map); + } else { +empty_region: + /* Create a zero sized sub-region to make cleanup easy. */ + memory_region_init(submem, name, 0); + } + + memory_region_add_subregion(mem, offset, submem); + + return ret; +} + +static void vfio_map_bar(VFIODevice *vdev, int nr) +{ + VFIOBAR *bar = &vdev->bars[nr]; + unsigned size = bar->size; + char name[64]; + uint32_t pci_bar; + uint8_t type; + int ret; + + /* Skip both unimplemented BARs and the upper half of 64bit BARS. */ + if (!size) { + return; + } + + snprintf(name, sizeof(name), "VFIO %04x:%02x:%02x.%x BAR %d", + vdev->host.domain, vdev->host.bus, vdev->host.slot, + vdev->host.function, nr); + + /* Determine what type of BAR this is for registration */ + ret = pread(vdev->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)\n", nr); + return; + } + + pci_bar = le32_to_cpu(pci_bar); + type = pci_bar & (pci_bar & PCI_BASE_ADDRESS_SPACE_IO ? + ~PCI_BASE_ADDRESS_IO_MASK : ~PCI_BASE_ADDRESS_MEM_MASK); + + /* A "slow" read/write mapping underlies all BARs */ + memory_region_init_io(&bar->mem, &vfio_bar_ops, bar, name, size); + pci_register_bar(&vdev->pdev, nr, type, &bar->mem); + + /* + * We can't mmap areas overlapping the MSIX vector table, so we + * potentially insert a direct-mapped subregion before and after it. + */ + if (vdev->msix && vdev->msix->table_bar == nr) { + size = vdev->msix->table_offset & TARGET_PAGE_MASK; + } + + strncat(name, " mmap", sizeof(name) - strlen(name) - 1); + if (vfio_mmap_bar(bar, &bar->mem, + &bar->mmap_mem, &bar->mmap, size, 0, name)) { + error_report("%s unsupported. Performance may be slow\n", name); + } + + if (vdev->msix && vdev->msix->table_bar == nr) { + unsigned start; + + start = TARGET_PAGE_ALIGN(vdev->msix->table_offset + + (vdev->msix->entries * PCI_MSIX_ENTRY_SIZE)); + + size = start < bar->size ? bar->size - start : 0; + strncat(name, " msix-hi", sizeof(name) - strlen(name) - 1); + /* VFIOMSIXInfo contains another MemoryRegion for this mapping */ + if (vfio_mmap_bar(bar, &bar->mem, &vdev->msix->mmap_mem, + &vdev->msix->mmap, size, start, name)) { + error_report("%s unsupported. Performance may be slow\n", name); + } + } +} + +static void vfio_map_bars(VFIODevice *vdev) +{ + int i; + + for (i = 0; i < PCI_ROM_SLOT; i++) { + vfio_map_bar(vdev, i); + } +} + +static void vfio_unmap_bars(VFIODevice *vdev) +{ + int i; + + for (i = 0; i < PCI_ROM_SLOT; i++) { + vfio_unmap_bar(vdev, i); + } +} + +/* + * General setup + */ +static uint8_t vfio_std_cap_max_size(PCIDevice *pdev, uint8_t pos) +{ + uint8_t tmp, next = 0xff; + + for (tmp = pdev->config[PCI_CAPABILITY_LIST]; tmp; + tmp = pdev->config[tmp + 1]) { + if (tmp > pos && tmp < next) { + next = tmp; + } + } + + return next - pos; +} + +static int vfio_add_std_cap(VFIODevice *vdev, uint8_t pos) +{ + PCIDevice *pdev = &vdev->pdev; + uint8_t cap_id, next, size; + int ret; + + cap_id = pdev->config[pos]; + next = pdev->config[pos + 1]; + + /* + * If it becomes important to configure capabilities to their actual + * size, use this as the default when it's something we don't recognize. + * Since QEMU doesn't actually handle many of the config accesses, + * exact size doesn't seem worthwhile. + */ + size = vfio_std_cap_max_size(pdev, pos); + + /* + * pci_add_capability always inserts the new capability at the head + * of the chain. Therefore to end up with a chain that matches the + * physical device, we insert from the end by making this recursive. + * This is also why we pre-caclulate size above as cached config space + * will be changed as we unwind the stack. + */ + if (next) { + ret = vfio_add_std_cap(vdev, next); + if (ret) { + return ret; + } + } else { + pdev->config[PCI_CAPABILITY_LIST] = 0; /* Begin the rebuild */ + } + + switch (cap_id) { + case PCI_CAP_ID_MSI: + ret = vfio_setup_msi(vdev, pos); + break; + case PCI_CAP_ID_MSIX: + ret = vfio_setup_msix(vdev, pos); + break; + default: + ret = pci_add_capability(pdev, cap_id, pos, size); + break; + } + + if (ret < 0) { + error_report("vfio: %04x:%02x:%02x.%x Error adding PCI capability " + "0x%x[0x%x]@0x%x: %d\n", vdev->host.domain, + vdev->host.bus, vdev->host.slot, vdev->host.function, + cap_id, size, pos, ret); + return ret; + } + + return 0; +} + +static int vfio_add_capabilities(VFIODevice *vdev) +{ + PCIDevice *pdev = &vdev->pdev; + + if (!(pdev->config[PCI_STATUS] & PCI_STATUS_CAP_LIST) || + !pdev->config[PCI_CAPABILITY_LIST]) { + return 0; /* Nothing to add */ + } + + return vfio_add_std_cap(vdev, pdev->config[PCI_CAPABILITY_LIST]); +} + +static int vfio_load_rom(VFIODevice *vdev) +{ + uint64_t size = vdev->rom_size; + char name[32]; + off_t off = 0, voff = vdev->rom_offset; + ssize_t bytes; + void *ptr; + + /* If loading ROM from file, pci handles it */ + if (vdev->pdev.romfile || !vdev->pdev.rom_bar || !size) { + return 0; + } + + DPRINTF("%s(%04x:%02x:%02x.%x)\n", __func__, vdev->host.domain, + vdev->host.bus, vdev->host.slot, vdev->host.function); + + snprintf(name, sizeof(name), "vfio[%04x:%02x:%02x.%x].rom", + vdev->host.domain, vdev->host.bus, vdev->host.slot, + vdev->host.function); + memory_region_init_ram(&vdev->pdev.rom, name, size); + ptr = memory_region_get_ram_ptr(&vdev->pdev.rom); + memset(ptr, 0xff, size); + + while (size) { + bytes = pread(vdev->fd, ptr + off, size, voff + off); + if (bytes == 0) { + break; /* expect that we could get back less than the ROM BAR */ + } else if (bytes > 0) { + off += bytes; + size -= bytes; + } else { + if (errno == EINTR || errno == EAGAIN) { + continue; + } + error_report("vfio: Error reading device ROM: %m\n"); + memory_region_destroy(&vdev->pdev.rom); + return -errno; + } + } + + pci_register_bar(&vdev->pdev, PCI_ROM_SLOT, 0, &vdev->pdev.rom); + vdev->pdev.has_rom = true; + return 0; +} + +static int vfio_connect_container(VFIOGroup *group) +{ + VFIOContainer *container; + int ret, fd; + + if (group->container) { + return 0; + } + + QLIST_FOREACH(container, &container_list, 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\n"); + return -errno; + } + + ret = ioctl(fd, VFIO_GET_API_VERSION); + if (ret != VFIO_API_VERSION) { + error_report("vfio: supported vfio version: %d, " + "reported version: %d\n", VFIO_API_VERSION, ret); + close(fd); + return -EINVAL; + } + + container = g_malloc0(sizeof(*container)); + 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\n"); + g_free(container); + close(fd); + return -errno; + } + + ret = ioctl(fd, VFIO_SET_IOMMU, VFIO_TYPE1_IOMMU); + if (ret) { + error_report("vfio: failed to set iommu for container: %m\n"); + g_free(container); + close(fd); + return -errno; + } + + container->iommu_data.listener = vfio_memory_listener; + container->iommu_data.release = vfio_listener_release; + + memory_listener_register(&container->iommu_data.listener, + get_system_memory()); + } else { + error_report("vfio: No available IOMMU models\n"); + g_free(container); + close(fd); + return -EINVAL; + } + + QLIST_INIT(&container->group_list); + QLIST_INSERT_HEAD(&container_list, container, next); + + group->container = container; + QLIST_INSERT_HEAD(&container->group_list, group, container_next); + + return 0; +} + +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\n", + group->groupid); + } + + QLIST_REMOVE(group, container_next); + group->container = NULL; + + if (QLIST_EMPTY(&container->group_list)) { + 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); + } +} + +static VFIOGroup *vfio_get_group(int groupid) +{ + VFIOGroup *group; + char path[32]; + struct vfio_group_status status = { .argsz = sizeof(status) }; + + QLIST_FOREACH(group, &group_list, next) { + if (group->groupid == groupid) { + return group; + } + } + + 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\n", path); + g_free(group); + return NULL; + } + + if (ioctl(group->fd, VFIO_GROUP_GET_STATUS, &status)) { + error_report("vfio: error getting group status: %m\n"); + close(group->fd); + g_free(group); + return NULL; + } + + 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.\n", groupid); + close(group->fd); + g_free(group); + return NULL; + } + + group->groupid = groupid; + QLIST_INIT(&group->device_list); + + if (vfio_connect_container(group)) { + error_report("vfio: failed to setup container for group %d\n", groupid); + close(group->fd); + g_free(group); + return NULL; + } + + QLIST_INSERT_HEAD(&group_list, group, next); + + return group; +} + +static void vfio_put_group(VFIOGroup *group) +{ + if (!QLIST_EMPTY(&group->device_list)) { + return; + } + + vfio_disconnect_container(group); + QLIST_REMOVE(group, next); + DPRINTF("vfio_put_group: close group->fd\n"); + close(group->fd); + g_free(group); +} + +static int vfio_get_device(VFIOGroup *group, const char *name, VFIODevice *vdev) +{ + struct vfio_device_info dev_info = { .argsz = sizeof(dev_info) }; + struct vfio_region_info reg_info = { .argsz = sizeof(reg_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\n", + name, group->groupid); + error_report("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); + + /* Sanity check device */ + ret = ioctl(vdev->fd, VFIO_DEVICE_GET_INFO, &dev_info); + if (ret) { + error_report("vfio: error getting device info: %m\n"); + 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)) { + error_report("vfio: Um, this isn't a PCI device\n"); + goto error; + } + + vdev->reset_works = !!(dev_info.flags & VFIO_DEVICE_FLAGS_RESET); + if (!vdev->reset_works) { + error_report("Warning, device %s does not support reset\n", name); + } + + if (dev_info.num_regions != VFIO_PCI_NUM_REGIONS) { + error_report("vfio: unexpected number of io regions %u\n", + dev_info.num_regions); + goto error; + } + + if (dev_info.num_irqs != VFIO_PCI_NUM_IRQS) { + error_report("vfio: unexpected number of irqs %u\n", dev_info.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); + if (ret) { + error_report("vfio: Error getting region %d info: %m\n", 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); + + 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; + } + + reg_info.index = VFIO_PCI_ROM_REGION_INDEX; + + ret = ioctl(vdev->fd, VFIO_DEVICE_GET_REGION_INFO, ®_info); + if (ret) { + error_report("vfio: Error getting ROM info: %m\n"); + goto error; + } + + DPRINTF("Device %s ROM:\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); + + vdev->rom_size = reg_info.size; + vdev->rom_offset = reg_info.offset; + + reg_info.index = VFIO_PCI_CONFIG_REGION_INDEX; + + ret = ioctl(vdev->fd, VFIO_DEVICE_GET_REGION_INFO, ®_info); + if (ret) { + error_report("vfio: Error getting config info: %m\n"); + 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); + + vdev->config_size = reg_info.size; + vdev->config_offset = reg_info.offset; + +error: + if (ret) { + QLIST_REMOVE(vdev, next); + vdev->group = NULL; + close(vdev->fd); + } + return ret; +} + +static void vfio_put_device(VFIODevice *vdev) +{ + QLIST_REMOVE(vdev, next); + vdev->group = NULL; + DPRINTF("vfio_put_device: close vdev->fd\n"); + close(vdev->fd); + if (vdev->msix) { + g_free(vdev->msix); + vdev->msix = NULL; + } +} + +static int vfio_initfn(PCIDevice *pdev) +{ + VFIODevice *pvdev, *vdev = DO_UPCAST(VFIODevice, pdev, pdev); + VFIOGroup *group; + char path[PATH_MAX], iommu_group_path[PATH_MAX], *group_name; + ssize_t len; + struct stat st; + int groupid; + int ret; + + /* Check that the host device exists */ + snprintf(path, sizeof(path), + "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/", + vdev->host.domain, vdev->host.bus, vdev->host.slot, + vdev->host.function); + if (stat(path, &st) < 0) { + error_report("vfio: error: no such host device: %s\n", path); + return -errno; + } + + strncat(path, "iommu_group", sizeof(path) - strlen(path) - 1); + + len = readlink(path, iommu_group_path, PATH_MAX); + if (len <= 0) { + error_report("vfio: error no iommu_group for device\n"); + return -errno; + } + + iommu_group_path[len] = 0; + group_name = basename(iommu_group_path); + + if (sscanf(group_name, "%d", &groupid) != 1) { + error_report("vfio: error reading %s: %m\n", path); + 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); + + group = vfio_get_group(groupid); + if (!group) { + error_report("vfio: failed to get group %d\n", groupid); + return -ENOENT; + } + + snprintf(path, sizeof(path), "%04x:%02x:%02x.%01x", + 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) { + + error_report("vfio: error: device %s is already attached\n", path); + vfio_put_group(group); + return -EBUSY; + } + } + + ret = vfio_get_device(group, path, vdev); + if (ret) { + error_report("vfio: failed to get device %s\n", path); + vfio_put_group(group); + return ret; + } + + /* Get a copy of config space */ + ret = pread(vdev->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\n"); + goto out_put; + } + + /* + * Clear host resource mapping info. If we choose not to register a + * BAR, such as might be the case with the option ROM, we can get + * confusing, unwritable, residual addresses from the host here. + */ + memset(&vdev->pdev.config[PCI_BASE_ADDRESS_0], 0, 24); + memset(&vdev->pdev.config[PCI_ROM_ADDRESS], 0, 4); + + vfio_load_rom(vdev); + + ret = vfio_early_setup_msix(vdev); + if (ret) { + goto out_put; + } + + vfio_map_bars(vdev); + + ret = vfio_add_capabilities(vdev); + if (ret) { + goto out_teardown; + } + + if (vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1)) { + if (vdev->intx.intx && strcmp(vdev->intx.intx, "off")) { + error_report("vfio: Unknown option x-intx=%s, " + "valid options: \"off\".\n", vdev->intx.intx); + ret = -EINVAL; + goto out_teardown; + } + + if (vdev->intx.intx && !strcmp(vdev->intx.intx, "off")) { + vdev->intx.disabled = true; + } + + ret = vfio_enable_intx(vdev); + if (ret) { + goto out_teardown; + } + } + + return 0; + +out_teardown: + pci_device_set_intx_routing_notifier(&vdev->pdev, NULL); + vfio_teardown_msi(vdev); + vfio_unmap_bars(vdev); +out_put: + 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; + + pci_device_set_intx_routing_notifier(&vdev->pdev, NULL); + vfio_disable_interrupts(vdev); + vfio_teardown_msi(vdev); + vfio_unmap_bars(vdev); + vfio_put_device(vdev); + vfio_put_group(group); +} + +static void vfio_pci_reset(DeviceState *dev) +{ + PCIDevice *pdev = DO_UPCAST(PCIDevice, qdev, dev); + VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev); + + if (!vdev->reset_works) { + return; + } + + if (ioctl(vdev->fd, VFIO_DEVICE_RESET)) { + error_report("vfio: Error unable to reset physical device " + "(%04x:%02x:%02x.%x): %m\n", vdev->host.domain, + vdev->host.bus, vdev->host.slot, vdev->host.function); + } +} + +static Property vfio_pci_dev_properties[] = { + DEFINE_PROP_PCI_HOST_DEVADDR("host", VFIODevice, host), + DEFINE_PROP_STRING("x-intx", VFIODevice, intx.intx), + /* + * TODO - support passed fds... is this necessary? + * DEFINE_PROP_STRING("vfiofd", VFIODevice, vfiofd_name), + * DEFINE_PROP_STRING("vfiogroupfd, VFIODevice, vfiogroupfd_name), + */ + DEFINE_PROP_END_OF_LIST(), +}; + + +static void vfio_pci_dev_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *pdc = PCI_DEVICE_CLASS(klass); + + dc->reset = vfio_pci_reset; + dc->props = vfio_pci_dev_properties; + pdc->init = vfio_initfn; + pdc->exit = vfio_exitfn; + pdc->config_read = vfio_pci_read_config; + pdc->config_write = vfio_pci_write_config; +} + +static const TypeInfo vfio_pci_dev_info = { + .name = "vfio-pci", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(VFIODevice), + .class_init = vfio_pci_dev_class_init, +}; + +static void register_vfio_pci_dev_type(void) +{ + type_register_static(&vfio_pci_dev_info); +} + +type_init(register_vfio_pci_dev_type) diff --git a/hw/vfio_pci_int.h b/hw/vfio_pci_int.h new file mode 100644 index 0000000000..3812d8d7f1 --- /dev/null +++ b/hw/vfio_pci_int.h @@ -0,0 +1,114 @@ +/* + * vfio based device assignment support + * + * Copyright Red Hat, Inc. 2012 + * + * Authors: + * Alex Williamson <alex.williamson@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#ifndef HW_VFIO_PCI_INT_H +#define HW_VFIO_PCI_INT_H + +#include "qemu-common.h" +#include "qemu-queue.h" +#include "pci.h" +#include "event_notifier.h" + +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 */ +} VFIOBAR; + +typedef struct VFIOINTx { + bool pending; /* interrupt pending */ + bool kvm_accel; /* set when QEMU bypass through KVM enabled */ + uint8_t pin; /* which pin to pull for qemu_set_irq */ + EventNotifier interrupt; /* eventfd triggered on interrupt */ + EventNotifier unmask; /* eventfd for unmask on QEMU bypass */ + PCIINTxRoute route; /* routing info for QEMU bypass */ + bool disabled; + char *intx; +} VFIOINTx; + +struct VFIODevice; + +typedef struct VFIOMSIVector { + EventNotifier interrupt; /* eventfd triggered on interrupt */ + struct VFIODevice *vdev; /* back pointer to device */ + int virq; /* KVM irqchip route for QEMU bypass */ + bool use; +} VFIOMSIVector; + +enum { + VFIO_INT_NONE = 0, + VFIO_INT_INTx = 1, + VFIO_INT_MSI = 2, + VFIO_INT_MSIX = 3, +}; + +struct VFIOGroup; + +typedef struct VFIOContainer { + int fd; /* /dev/vfio/vfio, empowered by the attached groups */ + struct { + /* enable abstraction to support various iommu backends */ + union { + MemoryListener listener; /* Used by type1 iommu */ + }; + void (*release)(struct VFIOContainer *); + } iommu_data; + QLIST_HEAD(, VFIOGroup) group_list; + QLIST_ENTRY(VFIOContainer) next; +} VFIOContainer; + +/* Cache of MSI-X setup plus extra mmap and memory region for split BAR map */ +typedef struct VFIOMSIXInfo { + uint8_t table_bar; + uint8_t pba_bar; + uint16_t entries; + uint32_t table_offset; + uint32_t pba_offset; + MemoryRegion mmap_mem; + void *mmap; +} VFIOMSIXInfo; + +typedef struct VFIODevice { + PCIDevice pdev; + int fd; + VFIOINTx intx; + unsigned int config_size; + off_t config_offset; /* Offset of config space region within device fd */ + unsigned int rom_size; + off_t rom_offset; /* Offset of ROM region within device fd */ + int msi_cap_size; + VFIOMSIVector *msi_vectors; + VFIOMSIXInfo *msix; + int nr_vectors; /* Number of MSI/MSIX vectors currently in use */ + int interrupt; /* Current interrupt type */ + VFIOBAR bars[PCI_NUM_REGIONS - 1]; /* No ROM */ + PCIHostDeviceAddress host; + QLIST_ENTRY(VFIODevice) next; + struct VFIOGroup *group; + bool reset_works; +} 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; + +#endif /* HW_VFIO_PCI_INT_H */ diff --git a/linux-headers/linux/vfio.h b/linux-headers/linux/vfio.h new file mode 100644 index 0000000000..f787b727a9 --- /dev/null +++ b/linux-headers/linux/vfio.h @@ -0,0 +1,368 @@ +/* + * VFIO API definition + * + * Copyright (C) 2012 Red Hat, Inc. All rights reserved. + * Author: Alex Williamson <alex.williamson@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef VFIO_H +#define VFIO_H + +#include <linux/types.h> +#include <linux/ioctl.h> + +#define VFIO_API_VERSION 0 + + +/* Kernel & User level defines for VFIO IOCTLs. */ + +/* Extensions */ + +#define VFIO_TYPE1_IOMMU 1 + +/* + * The IOCTL interface is designed for extensibility by embedding the + * structure length (argsz) and flags into structures passed between + * kernel and userspace. We therefore use the _IO() macro for these + * defines to avoid implicitly embedding a size into the ioctl request. + * As structure fields are added, argsz will increase to match and flag + * bits will be defined to indicate additional fields with valid data. + * It's *always* the caller's responsibility to indicate the size of + * the structure passed by setting argsz appropriately. + */ + +#define VFIO_TYPE (';') +#define VFIO_BASE 100 + +/* -------- IOCTLs for VFIO file descriptor (/dev/vfio/vfio) -------- */ + +/** + * VFIO_GET_API_VERSION - _IO(VFIO_TYPE, VFIO_BASE + 0) + * + * Report the version of the VFIO API. This allows us to bump the entire + * API version should we later need to add or change features in incompatible + * ways. + * Return: VFIO_API_VERSION + * Availability: Always + */ +#define VFIO_GET_API_VERSION _IO(VFIO_TYPE, VFIO_BASE + 0) + +/** + * VFIO_CHECK_EXTENSION - _IOW(VFIO_TYPE, VFIO_BASE + 1, __u32) + * + * Check whether an extension is supported. + * Return: 0 if not supported, 1 (or some other positive integer) if supported. + * Availability: Always + */ +#define VFIO_CHECK_EXTENSION _IO(VFIO_TYPE, VFIO_BASE + 1) + +/** + * VFIO_SET_IOMMU - _IOW(VFIO_TYPE, VFIO_BASE + 2, __s32) + * + * Set the iommu to the given type. The type must be supported by an + * iommu driver as verified by calling CHECK_EXTENSION using the same + * type. A group must be set to this file descriptor before this + * ioctl is available. The IOMMU interfaces enabled by this call are + * specific to the value set. + * Return: 0 on success, -errno on failure + * Availability: When VFIO group attached + */ +#define VFIO_SET_IOMMU _IO(VFIO_TYPE, VFIO_BASE + 2) + +/* -------- IOCTLs for GROUP file descriptors (/dev/vfio/$GROUP) -------- */ + +/** + * VFIO_GROUP_GET_STATUS - _IOR(VFIO_TYPE, VFIO_BASE + 3, + * struct vfio_group_status) + * + * Retrieve information about the group. Fills in provided + * struct vfio_group_info. Caller sets argsz. + * Return: 0 on succes, -errno on failure. + * Availability: Always + */ +struct vfio_group_status { + __u32 argsz; + __u32 flags; +#define VFIO_GROUP_FLAGS_VIABLE (1 << 0) +#define VFIO_GROUP_FLAGS_CONTAINER_SET (1 << 1) +}; +#define VFIO_GROUP_GET_STATUS _IO(VFIO_TYPE, VFIO_BASE + 3) + +/** + * VFIO_GROUP_SET_CONTAINER - _IOW(VFIO_TYPE, VFIO_BASE + 4, __s32) + * + * Set the container for the VFIO group to the open VFIO file + * descriptor provided. Groups may only belong to a single + * container. Containers may, at their discretion, support multiple + * groups. Only when a container is set are all of the interfaces + * of the VFIO file descriptor and the VFIO group file descriptor + * available to the user. + * Return: 0 on success, -errno on failure. + * Availability: Always + */ +#define VFIO_GROUP_SET_CONTAINER _IO(VFIO_TYPE, VFIO_BASE + 4) + +/** + * VFIO_GROUP_UNSET_CONTAINER - _IO(VFIO_TYPE, VFIO_BASE + 5) + * + * Remove the group from the attached container. This is the + * opposite of the SET_CONTAINER call and returns the group to + * an initial state. All device file descriptors must be released + * prior to calling this interface. When removing the last group + * from a container, the IOMMU will be disabled and all state lost, + * effectively also returning the VFIO file descriptor to an initial + * state. + * Return: 0 on success, -errno on failure. + * Availability: When attached to container + */ +#define VFIO_GROUP_UNSET_CONTAINER _IO(VFIO_TYPE, VFIO_BASE + 5) + +/** + * VFIO_GROUP_GET_DEVICE_FD - _IOW(VFIO_TYPE, VFIO_BASE + 6, char) + * + * Return a new file descriptor for the device object described by + * the provided string. The string should match a device listed in + * the devices subdirectory of the IOMMU group sysfs entry. The + * group containing the device must already be added to this context. + * Return: new file descriptor on success, -errno on failure. + * Availability: When attached to container + */ +#define VFIO_GROUP_GET_DEVICE_FD _IO(VFIO_TYPE, VFIO_BASE + 6) + +/* --------------- IOCTLs for DEVICE file descriptors --------------- */ + +/** + * VFIO_DEVICE_GET_INFO - _IOR(VFIO_TYPE, VFIO_BASE + 7, + * struct vfio_device_info) + * + * Retrieve information about the device. Fills in provided + * struct vfio_device_info. Caller sets argsz. + * Return: 0 on success, -errno on failure. + */ +struct vfio_device_info { + __u32 argsz; + __u32 flags; +#define VFIO_DEVICE_FLAGS_RESET (1 << 0) /* Device supports reset */ +#define VFIO_DEVICE_FLAGS_PCI (1 << 1) /* vfio-pci device */ + __u32 num_regions; /* Max region index + 1 */ + __u32 num_irqs; /* Max IRQ index + 1 */ +}; +#define VFIO_DEVICE_GET_INFO _IO(VFIO_TYPE, VFIO_BASE + 7) + +/** + * VFIO_DEVICE_GET_REGION_INFO - _IOWR(VFIO_TYPE, VFIO_BASE + 8, + * struct vfio_region_info) + * + * Retrieve information about a device region. Caller provides + * struct vfio_region_info with index value set. Caller sets argsz. + * Implementation of region mapping is bus driver specific. This is + * intended to describe MMIO, I/O port, as well as bus specific + * regions (ex. PCI config space). Zero sized regions may be used + * to describe unimplemented regions (ex. unimplemented PCI BARs). + * Return: 0 on success, -errno on failure. + */ +struct vfio_region_info { + __u32 argsz; + __u32 flags; +#define VFIO_REGION_INFO_FLAG_READ (1 << 0) /* Region supports read */ +#define VFIO_REGION_INFO_FLAG_WRITE (1 << 1) /* Region supports write */ +#define VFIO_REGION_INFO_FLAG_MMAP (1 << 2) /* Region supports mmap */ + __u32 index; /* Region index */ + __u32 resv; /* Reserved for alignment */ + __u64 size; /* Region size (bytes) */ + __u64 offset; /* Region offset from start of device fd */ +}; +#define VFIO_DEVICE_GET_REGION_INFO _IO(VFIO_TYPE, VFIO_BASE + 8) + +/** + * VFIO_DEVICE_GET_IRQ_INFO - _IOWR(VFIO_TYPE, VFIO_BASE + 9, + * struct vfio_irq_info) + * + * Retrieve information about a device IRQ. Caller provides + * struct vfio_irq_info with index value set. Caller sets argsz. + * Implementation of IRQ mapping is bus driver specific. Indexes + * using multiple IRQs are primarily intended to support MSI-like + * interrupt blocks. Zero count irq blocks may be used to describe + * unimplemented interrupt types. + * + * The EVENTFD flag indicates the interrupt index supports eventfd based + * signaling. + * + * The MASKABLE flags indicates the index supports MASK and UNMASK + * actions described below. + * + * AUTOMASKED indicates that after signaling, the interrupt line is + * automatically masked by VFIO and the user needs to unmask the line + * to receive new interrupts. This is primarily intended to distinguish + * level triggered interrupts. + * + * The NORESIZE flag indicates that the interrupt lines within the index + * are setup as a set and new subindexes cannot be enabled without first + * disabling the entire index. This is used for interrupts like PCI MSI + * and MSI-X where the driver may only use a subset of the available + * indexes, but VFIO needs to enable a specific number of vectors + * upfront. In the case of MSI-X, where the user can enable MSI-X and + * then add and unmask vectors, it's up to userspace to make the decision + * whether to allocate the maximum supported number of vectors or tear + * down setup and incrementally increase the vectors as each is enabled. + */ +struct vfio_irq_info { + __u32 argsz; + __u32 flags; +#define VFIO_IRQ_INFO_EVENTFD (1 << 0) +#define VFIO_IRQ_INFO_MASKABLE (1 << 1) +#define VFIO_IRQ_INFO_AUTOMASKED (1 << 2) +#define VFIO_IRQ_INFO_NORESIZE (1 << 3) + __u32 index; /* IRQ index */ + __u32 count; /* Number of IRQs within this index */ +}; +#define VFIO_DEVICE_GET_IRQ_INFO _IO(VFIO_TYPE, VFIO_BASE + 9) + +/** + * VFIO_DEVICE_SET_IRQS - _IOW(VFIO_TYPE, VFIO_BASE + 10, struct vfio_irq_set) + * + * Set signaling, masking, and unmasking of interrupts. Caller provides + * struct vfio_irq_set with all fields set. 'start' and 'count' indicate + * the range of subindexes being specified. + * + * The DATA flags specify the type of data provided. If DATA_NONE, the + * operation performs the specified action immediately on the specified + * interrupt(s). For example, to unmask AUTOMASKED interrupt [0,0]: + * flags = (DATA_NONE|ACTION_UNMASK), index = 0, start = 0, count = 1. + * + * DATA_BOOL allows sparse support for the same on arrays of interrupts. + * For example, to mask interrupts [0,1] and [0,3] (but not [0,2]): + * flags = (DATA_BOOL|ACTION_MASK), index = 0, start = 1, count = 3, + * data = {1,0,1} + * + * DATA_EVENTFD binds the specified ACTION to the provided __s32 eventfd. + * A value of -1 can be used to either de-assign interrupts if already + * assigned or skip un-assigned interrupts. For example, to set an eventfd + * to be trigger for interrupts [0,0] and [0,2]: + * flags = (DATA_EVENTFD|ACTION_TRIGGER), index = 0, start = 0, count = 3, + * data = {fd1, -1, fd2} + * If index [0,1] is previously set, two count = 1 ioctls calls would be + * required to set [0,0] and [0,2] without changing [0,1]. + * + * Once a signaling mechanism is set, DATA_BOOL or DATA_NONE can be used + * with ACTION_TRIGGER to perform kernel level interrupt loopback testing + * from userspace (ie. simulate hardware triggering). + * + * Setting of an event triggering mechanism to userspace for ACTION_TRIGGER + * enables the interrupt index for the device. Individual subindex interrupts + * can be disabled using the -1 value for DATA_EVENTFD or the index can be + * disabled as a whole with: flags = (DATA_NONE|ACTION_TRIGGER), count = 0. + * + * Note that ACTION_[UN]MASK specify user->kernel signaling (irqfds) while + * ACTION_TRIGGER specifies kernel->user signaling. + */ +struct vfio_irq_set { + __u32 argsz; + __u32 flags; +#define VFIO_IRQ_SET_DATA_NONE (1 << 0) /* Data not present */ +#define VFIO_IRQ_SET_DATA_BOOL (1 << 1) /* Data is bool (u8) */ +#define VFIO_IRQ_SET_DATA_EVENTFD (1 << 2) /* Data is eventfd (s32) */ +#define VFIO_IRQ_SET_ACTION_MASK (1 << 3) /* Mask interrupt */ +#define VFIO_IRQ_SET_ACTION_UNMASK (1 << 4) /* Unmask interrupt */ +#define VFIO_IRQ_SET_ACTION_TRIGGER (1 << 5) /* Trigger interrupt */ + __u32 index; + __u32 start; + __u32 count; + __u8 data[]; +}; +#define VFIO_DEVICE_SET_IRQS _IO(VFIO_TYPE, VFIO_BASE + 10) + +#define VFIO_IRQ_SET_DATA_TYPE_MASK (VFIO_IRQ_SET_DATA_NONE | \ + VFIO_IRQ_SET_DATA_BOOL | \ + VFIO_IRQ_SET_DATA_EVENTFD) +#define VFIO_IRQ_SET_ACTION_TYPE_MASK (VFIO_IRQ_SET_ACTION_MASK | \ + VFIO_IRQ_SET_ACTION_UNMASK | \ + VFIO_IRQ_SET_ACTION_TRIGGER) +/** + * VFIO_DEVICE_RESET - _IO(VFIO_TYPE, VFIO_BASE + 11) + * + * Reset a device. + */ +#define VFIO_DEVICE_RESET _IO(VFIO_TYPE, VFIO_BASE + 11) + +/* + * The VFIO-PCI bus driver makes use of the following fixed region and + * IRQ index mapping. Unimplemented regions return a size of zero. + * Unimplemented IRQ types return a count of zero. + */ + +enum { + VFIO_PCI_BAR0_REGION_INDEX, + VFIO_PCI_BAR1_REGION_INDEX, + VFIO_PCI_BAR2_REGION_INDEX, + VFIO_PCI_BAR3_REGION_INDEX, + VFIO_PCI_BAR4_REGION_INDEX, + VFIO_PCI_BAR5_REGION_INDEX, + VFIO_PCI_ROM_REGION_INDEX, + VFIO_PCI_CONFIG_REGION_INDEX, + VFIO_PCI_NUM_REGIONS +}; + +enum { + VFIO_PCI_INTX_IRQ_INDEX, + VFIO_PCI_MSI_IRQ_INDEX, + VFIO_PCI_MSIX_IRQ_INDEX, + VFIO_PCI_NUM_IRQS +}; + +/* -------- API for Type1 VFIO IOMMU -------- */ + +/** + * VFIO_IOMMU_GET_INFO - _IOR(VFIO_TYPE, VFIO_BASE + 12, struct vfio_iommu_info) + * + * Retrieve information about the IOMMU object. Fills in provided + * struct vfio_iommu_info. Caller sets argsz. + * + * XXX Should we do these by CHECK_EXTENSION too? + */ +struct vfio_iommu_type1_info { + __u32 argsz; + __u32 flags; +#define VFIO_IOMMU_INFO_PGSIZES (1 << 0) /* supported page sizes info */ + __u64 iova_pgsizes; /* Bitmap of supported page sizes */ +}; + +#define VFIO_IOMMU_GET_INFO _IO(VFIO_TYPE, VFIO_BASE + 12) + +/** + * VFIO_IOMMU_MAP_DMA - _IOW(VFIO_TYPE, VFIO_BASE + 13, struct vfio_dma_map) + * + * Map process virtual addresses to IO virtual addresses using the + * provided struct vfio_dma_map. Caller sets argsz. READ &/ WRITE required. + */ +struct vfio_iommu_type1_dma_map { + __u32 argsz; + __u32 flags; +#define VFIO_DMA_MAP_FLAG_READ (1 << 0) /* readable from device */ +#define VFIO_DMA_MAP_FLAG_WRITE (1 << 1) /* writable from device */ + __u64 vaddr; /* Process virtual address */ + __u64 iova; /* IO virtual address */ + __u64 size; /* Size of mapping (bytes) */ +}; + +#define VFIO_IOMMU_MAP_DMA _IO(VFIO_TYPE, VFIO_BASE + 13) + +/** + * VFIO_IOMMU_UNMAP_DMA - _IOW(VFIO_TYPE, VFIO_BASE + 14, struct vfio_dma_unmap) + * + * Unmap IO virtual addresses using the provided struct vfio_dma_unmap. + * Caller sets argsz. + */ +struct vfio_iommu_type1_dma_unmap { + __u32 argsz; + __u32 flags; + __u64 iova; /* IO virtual address */ + __u64 size; /* Size of mapping (bytes) */ +}; + +#define VFIO_IOMMU_UNMAP_DMA _IO(VFIO_TYPE, VFIO_BASE + 14) + +#endif /* VFIO_H */ diff --git a/migration-tcp.c b/migration-tcp.c index ac891c38a3..a15c2b87a1 100644 --- a/migration-tcp.c +++ b/migration-tcp.c @@ -53,54 +53,35 @@ static int tcp_close(MigrationState *s) return r; } -static void tcp_wait_for_connect(void *opaque) +static void tcp_wait_for_connect(int fd, void *opaque) { MigrationState *s = opaque; - int val, ret; - socklen_t valsize = sizeof(val); - DPRINTF("connect completed\n"); - do { - ret = getsockopt(s->fd, SOL_SOCKET, SO_ERROR, (void *) &val, &valsize); - } while (ret == -1 && (socket_error()) == EINTR); - - if (ret < 0) { + if (fd < 0) { + DPRINTF("migrate connect error\n"); + s->fd = -1; migrate_fd_error(s); - return; - } - - qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); - - if (val == 0) + } else { + DPRINTF("migrate connect success\n"); + s->fd = fd; migrate_fd_connect(s); - else { - DPRINTF("error connecting %d\n", val); - migrate_fd_error(s); } } int tcp_start_outgoing_migration(MigrationState *s, const char *host_port, Error **errp) { - bool in_progress; - s->get_error = socket_errno; s->write = socket_write; s->close = tcp_close; - s->fd = inet_connect(host_port, false, &in_progress, errp); + s->fd = inet_nonblocking_connect(host_port, tcp_wait_for_connect, s, + errp); if (error_is_set(errp)) { migrate_fd_error(s); return -1; } - if (in_progress) { - DPRINTF("connect in progress\n"); - qemu_set_fd_handler2(s->fd, NULL, NULL, tcp_wait_for_connect, s); - } else { - migrate_fd_connect(s); - } - return 0; } diff --git a/migration.c b/migration.c index 1edeec5ddd..22a05c437d 100644 --- a/migration.c +++ b/migration.c @@ -240,7 +240,9 @@ static int migrate_fd_cleanup(MigrationState *s) { int ret = 0; - qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); + if (s->fd != -1) { + qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); + } if (s->file) { DPRINTF("closing file\n"); @@ -3259,11 +3259,7 @@ static int64_t expr_unary(Monitor *mon) break; default: errno = 0; -#if TARGET_PHYS_ADDR_BITS > 32 n = strtoull(pch, &p, 0); -#else - n = strtoul(pch, &p, 0); -#endif if (errno == ERANGE) { expr_error(mon, "number too large"); } @@ -208,7 +208,7 @@ int tcp_socket_outgoing(const char *address, uint16_t port) int tcp_socket_outgoing_spec(const char *address_and_port) { - return inet_connect(address_and_port, true, NULL, NULL); + return inet_connect(address_and_port, NULL); } int tcp_socket_incoming(const char *address, uint16_t port) diff --git a/qemu-char.c b/qemu-char.c index 7f0f895157..b082bae11b 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2456,7 +2456,7 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) if (is_listen) { fd = inet_listen_opts(opts, 0, NULL); } else { - fd = inet_connect_opts(opts, NULL, NULL); + fd = inet_connect_opts(opts, NULL, NULL, NULL); } } if (fd < 0) { diff --git a/qemu-config.c b/qemu-config.c index 12eafbb4f5..cd1ec2165a 100644 --- a/qemu-config.c +++ b/qemu-config.c @@ -645,6 +645,9 @@ QemuOptsList qemu_boot_opts = { }, { .name = "splash-time", .type = QEMU_OPT_STRING, + }, { + .name = "reboot-timeout", + .type = QEMU_OPT_STRING, }, { /*End of list */ } }, diff --git a/qemu-options.hx b/qemu-options.hx index 09c86c4cb0..7d97f96928 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -344,13 +344,14 @@ ETEXI DEF("boot", HAS_ARG, QEMU_OPTION_boot, "-boot [order=drives][,once=drives][,menu=on|off]\n" - " [,splash=sp_name][,splash-time=sp_time]\n" + " [,splash=sp_name][,splash-time=sp_time][,reboot-timeout=rb_time]\n" " 'drives': floppy (a), hard disk (c), CD-ROM (d), network (n)\n" " 'sp_name': the file's name that would be passed to bios as logo picture, if menu=on\n" - " 'sp_time': the period that splash picture last if menu=on, unit is ms\n", + " 'sp_time': the period that splash picture last if menu=on, unit is ms\n" + " 'rb_timeout': the timeout before guest reboot when boot failed, unit is ms\n", QEMU_ARCH_ALL) STEXI -@item -boot [order=@var{drives}][,once=@var{drives}][,menu=on|off][,splash=@var{sp_name}][,splash-time=@var{sp_time}] +@item -boot [order=@var{drives}][,once=@var{drives}][,menu=on|off][,splash=@var{sp_name}][,splash-time=@var{sp_time}][,reboot-timeout=@var{rb_timeout}] @findex -boot Specify boot order @var{drives} as a string of drive letters. Valid drive letters depend on the target achitecture. The x86 PC uses: a, b @@ -369,6 +370,11 @@ limitation: The splash file could be a jpeg file or a BMP file in 24 BPP format(true color). The resolution should be supported by the SVGA mode, so the recommended is 320x240, 640x480, 800x640. +A timeout could be passed to bios, guest will pause for @var{rb_timeout} ms +when boot failed, then reboot. If @var{rb_timeout} is '-1', guest will not +reboot, qemu passes '-1' to bios by default. Currently Seabios for X86 +system support it. + @example # try to boot from network first, then from hard disk qemu-system-i386 -boot order=nc diff --git a/qemu-sockets.c b/qemu-sockets.c index 037775b86b..1f14e8bc63 100644 --- a/qemu-sockets.c +++ b/qemu-sockets.c @@ -24,6 +24,7 @@ #include "qemu_socket.h" #include "qemu-common.h" /* for qemu_isdigit */ +#include "main-loop.h" #ifndef AI_ADDRCONFIG # define AI_ADDRCONFIG 0 @@ -54,9 +55,6 @@ static QemuOptsList dummy_opts = { },{ .name = "ipv6", .type = QEMU_OPT_BOOL, - },{ - .name = "block", - .type = QEMU_OPT_BOOL, }, { /* end if list */ } }, @@ -209,95 +207,201 @@ listen: return slisten; } -int inet_connect_opts(QemuOpts *opts, bool *in_progress, Error **errp) +#ifdef _WIN32 +#define QEMU_SOCKET_RC_INPROGRESS(rc) \ + ((rc) == -EINPROGRESS || (rc) == -EWOULDBLOCK || (rc) == -WSAEALREADY) +#else +#define QEMU_SOCKET_RC_INPROGRESS(rc) \ + ((rc) == -EINPROGRESS) +#endif + +/* Struct to store connect state for non blocking connect */ +typedef struct ConnectState { + int fd; + struct addrinfo *addr_list; + struct addrinfo *current_addr; + NonBlockingConnectHandler *callback; + void *opaque; +} ConnectState; + +static int inet_connect_addr(struct addrinfo *addr, bool *in_progress, + ConnectState *connect_state); + +static void wait_for_connect(void *opaque) { - struct addrinfo ai,*res,*e; + ConnectState *s = opaque; + int val = 0, rc = 0; + socklen_t valsize = sizeof(val); + bool in_progress; + + qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); + + do { + rc = getsockopt(s->fd, SOL_SOCKET, SO_ERROR, (void *) &val, &valsize); + } while (rc == -1 && socket_error() == EINTR); + + /* update rc to contain error */ + if (!rc && val) { + rc = -1; + } + + /* connect error */ + if (rc < 0) { + closesocket(s->fd); + s->fd = rc; + } + + /* try to connect to the next address on the list */ + while (s->current_addr->ai_next != NULL && s->fd < 0) { + s->current_addr = s->current_addr->ai_next; + s->fd = inet_connect_addr(s->current_addr, &in_progress, s); + /* connect in progress */ + if (in_progress) { + return; + } + } + + freeaddrinfo(s->addr_list); + if (s->callback) { + s->callback(s->fd, s->opaque); + } + g_free(s); + return; +} + +static int inet_connect_addr(struct addrinfo *addr, bool *in_progress, + ConnectState *connect_state) +{ + int sock, rc; + + *in_progress = false; + + sock = qemu_socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); + if (sock < 0) { + fprintf(stderr, "%s: socket(%s): %s\n", __func__, + inet_strfamily(addr->ai_family), strerror(errno)); + return -1; + } + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + if (connect_state != NULL) { + socket_set_nonblock(sock); + } + /* connect to peer */ + do { + rc = 0; + if (connect(sock, addr->ai_addr, addr->ai_addrlen) < 0) { + rc = -socket_error(); + } + } while (rc == -EINTR); + + if (connect_state != NULL && QEMU_SOCKET_RC_INPROGRESS(rc)) { + connect_state->fd = sock; + qemu_set_fd_handler2(sock, NULL, NULL, wait_for_connect, + connect_state); + *in_progress = true; + } else if (rc < 0) { + closesocket(sock); + return -1; + } + return sock; +} + +static struct addrinfo *inet_parse_connect_opts(QemuOpts *opts, Error **errp) +{ + struct addrinfo ai, *res; + int rc; const char *addr; const char *port; - char uaddr[INET6_ADDRSTRLEN+1]; - char uport[33]; - int sock,rc; - bool block; - memset(&ai,0, sizeof(ai)); + memset(&ai, 0, sizeof(ai)); + ai.ai_flags = AI_CANONNAME | AI_ADDRCONFIG; ai.ai_family = PF_UNSPEC; ai.ai_socktype = SOCK_STREAM; - if (in_progress) { - *in_progress = false; - } - addr = qemu_opt_get(opts, "host"); port = qemu_opt_get(opts, "port"); - block = qemu_opt_get_bool(opts, "block", 0); if (addr == NULL || port == NULL) { - fprintf(stderr, "inet_connect: host and/or port not specified\n"); + fprintf(stderr, + "inet_parse_connect_opts: host and/or port not specified\n"); error_set(errp, QERR_SOCKET_CREATE_FAILED); - return -1; + return NULL; } - if (qemu_opt_get_bool(opts, "ipv4", 0)) + if (qemu_opt_get_bool(opts, "ipv4", 0)) { ai.ai_family = PF_INET; - if (qemu_opt_get_bool(opts, "ipv6", 0)) + } + if (qemu_opt_get_bool(opts, "ipv6", 0)) { ai.ai_family = PF_INET6; + } /* lookup */ - if (0 != (rc = getaddrinfo(addr, port, &ai, &res))) { - fprintf(stderr,"getaddrinfo(%s,%s): %s\n", addr, port, + rc = getaddrinfo(addr, port, &ai, &res); + if (rc != 0) { + fprintf(stderr, "getaddrinfo(%s,%s): %s\n", addr, port, gai_strerror(rc)); error_set(errp, QERR_SOCKET_CREATE_FAILED); - return -1; + return NULL; + } + return res; +} + +/** + * Create a socket and connect it to an address. + * + * @opts: QEMU options, recognized parameters strings "host" and "port", + * bools "ipv4" and "ipv6". + * @errp: set on error + * @callback: callback function for non-blocking connect + * @opaque: opaque for callback function + * + * Returns: -1 on error, file descriptor on success. + * + * If @callback is non-null, the connect is non-blocking. If this + * function succeeds, callback will be called when the connection + * completes, with the file descriptor on success, or -1 on error. + */ +int inet_connect_opts(QemuOpts *opts, Error **errp, + NonBlockingConnectHandler *callback, void *opaque) +{ + struct addrinfo *res, *e; + int sock = -1; + bool in_progress; + ConnectState *connect_state = NULL; + + res = inet_parse_connect_opts(opts, errp); + if (!res) { + return -1; + } + + if (callback != NULL) { + connect_state = g_malloc0(sizeof(*connect_state)); + connect_state->addr_list = res; + connect_state->callback = callback; + connect_state->opaque = opaque; } for (e = res; e != NULL; e = e->ai_next) { - if (getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen, - uaddr,INET6_ADDRSTRLEN,uport,32, - NI_NUMERICHOST | NI_NUMERICSERV) != 0) { - fprintf(stderr,"%s: getnameinfo: oops\n", __FUNCTION__); - continue; + if (connect_state != NULL) { + connect_state->current_addr = e; } - sock = qemu_socket(e->ai_family, e->ai_socktype, e->ai_protocol); - if (sock < 0) { - fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__, - inet_strfamily(e->ai_family), strerror(errno)); - continue; - } - setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(void*)&on,sizeof(on)); - if (!block) { - socket_set_nonblock(sock); - } - /* connect to peer */ - do { - rc = 0; - if (connect(sock, e->ai_addr, e->ai_addrlen) < 0) { - rc = -socket_error(); - } - } while (rc == -EINTR); - - #ifdef _WIN32 - if (!block && (rc == -EINPROGRESS || rc == -EWOULDBLOCK - || rc == -WSAEALREADY)) { - #else - if (!block && (rc == -EINPROGRESS)) { - #endif - if (in_progress) { - *in_progress = true; + sock = inet_connect_addr(e, &in_progress, connect_state); + if (in_progress) { + return sock; + } else if (sock >= 0) { + /* non blocking socket immediate success, call callback */ + if (callback != NULL) { + callback(sock, opaque); } - } else if (rc < 0) { - if (NULL == e->ai_next) - fprintf(stderr, "%s: connect(%s,%s,%s,%s): %s\n", __FUNCTION__, - inet_strfamily(e->ai_family), - e->ai_canonname, uaddr, uport, strerror(errno)); - closesocket(sock); - continue; + break; } - freeaddrinfo(res); - return sock; } - error_set(errp, QERR_SOCKET_CONNECT_FAILED); + if (sock < 0) { + error_set(errp, QERR_SOCKET_CONNECT_FAILED); + } + g_free(connect_state); freeaddrinfo(res); - return -1; + return sock; } int inet_dgram_opts(QemuOpts *opts) @@ -493,17 +597,54 @@ int inet_listen(const char *str, char *ostr, int olen, return sock; } -int inet_connect(const char *str, bool block, bool *in_progress, Error **errp) +/** + * Create a blocking socket and connect it to an address. + * + * @str: address string + * @errp: set in case of an error + * + * Returns -1 in case of error, file descriptor on success + **/ +int inet_connect(const char *str, Error **errp) { QemuOpts *opts; int sock = -1; opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL); if (inet_parse(opts, str) == 0) { - if (block) { - qemu_opt_set(opts, "block", "on"); - } - sock = inet_connect_opts(opts, in_progress, errp); + sock = inet_connect_opts(opts, errp, NULL, NULL); + } else { + error_set(errp, QERR_SOCKET_CREATE_FAILED); + } + qemu_opts_del(opts); + return sock; +} + +/** + * Create a non-blocking socket and connect it to an address. + * Calls the callback function with fd in case of success or -1 in case of + * error. + * + * @str: address string + * @callback: callback function that is called when connect completes, + * cannot be NULL. + * @opaque: opaque for callback function + * @errp: set in case of an error + * + * Returns: -1 on immediate error, file descriptor on success. + **/ +int inet_nonblocking_connect(const char *str, + NonBlockingConnectHandler *callback, + void *opaque, Error **errp) +{ + QemuOpts *opts; + int sock = -1; + + g_assert(callback != NULL); + + opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL); + if (inet_parse(opts, str) == 0) { + sock = inet_connect_opts(opts, errp, callback, opaque); } else { error_set(errp, QERR_SOCKET_CREATE_FAILED); } diff --git a/qemu_socket.h b/qemu_socket.h index 30ae6af8b8..3e8aee9cad 100644 --- a/qemu_socket.h +++ b/qemu_socket.h @@ -38,12 +38,21 @@ void socket_set_block(int fd); void socket_set_nonblock(int fd); int send_all(int fd, const void *buf, int len1); -/* New, ipv6-ready socket helper functions, see qemu-sockets.c */ +/* callback function for nonblocking connect + * valid fd on success, negative error code on failure + */ +typedef void NonBlockingConnectHandler(int fd, void *opaque); + int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp); int inet_listen(const char *str, char *ostr, int olen, int socktype, int port_offset, Error **errp); -int inet_connect_opts(QemuOpts *opts, bool *in_progress, Error **errp); -int inet_connect(const char *str, bool block, bool *in_progress, Error **errp); +int inet_connect_opts(QemuOpts *opts, Error **errp, + NonBlockingConnectHandler *callback, void *opaque); +int inet_connect(const char *str, Error **errp); +int inet_nonblocking_connect(const char *str, + NonBlockingConnectHandler *callback, + void *opaque, Error **errp); + int inet_dgram_opts(QemuOpts *opts); const char *inet_strfamily(int family); diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index 53a6f87650..67be2efecd 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -57,7 +57,7 @@ done rm -rf "$output/linux-headers/linux" mkdir -p "$output/linux-headers/linux" -for header in kvm.h kvm_para.h vhost.h virtio_config.h virtio_ring.h; do +for header in kvm.h kvm_para.h vfio.h vhost.h virtio_config.h virtio_ring.h; do cp "$tmpdir/include/linux/$header" "$output/linux-headers/linux" done rm -rf "$output/linux-headers/asm-generic" @@ -52,6 +52,7 @@ void qemu_system_wakeup_enable(WakeupReason reason, bool enabled); void qemu_register_wakeup_notifier(Notifier *notifier); void qemu_system_shutdown_request(void); void qemu_system_powerdown_request(void); +void qemu_register_powerdown_notifier(Notifier *notifier); void qemu_system_debug_request(void); void qemu_system_vmstop_request(RunState reason); int qemu_shutdown_requested_get(void); @@ -61,7 +62,6 @@ int qemu_reset_requested(void); int qemu_powerdown_requested(void); void qemu_system_killed(int signal, pid_t pid); void qemu_kill_report(void); -extern qemu_irq qemu_system_powerdown; void qemu_devices_reset(void); void qemu_system_reset(bool report); diff --git a/target-alpha/translate.c b/target-alpha/translate.c index 3f9aee12d4..f707d8deb9 100644 --- a/target-alpha/translate.c +++ b/target-alpha/translate.c @@ -3421,7 +3421,7 @@ static inline void gen_intermediate_code_internal(CPUAlphaState *env, insn = cpu_ldl_code(env, ctx.pc); num_insns++; - if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) { + if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { tcg_gen_debug_insn_start(ctx.pc); } diff --git a/target-arm/translate.c b/target-arm/translate.c index f4b447a49a..5fded491ec 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -9816,7 +9816,7 @@ static inline void gen_intermediate_code_internal(CPUARMState *env, if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) gen_io_start(); - if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) { + if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { tcg_gen_debug_insn_start(dc->pc); } diff --git a/target-cris/translate.c b/target-cris/translate.c index 19144b5e29..755de659df 100644 --- a/target-cris/translate.c +++ b/target-cris/translate.c @@ -3074,8 +3074,9 @@ static unsigned int crisv32_decoder(CPUCRISState *env, DisasContext *dc) int insn_len = 2; int i; - if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) + if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { tcg_gen_debug_insn_start(dc->pc); + } /* Load a halfword onto the instruction register. */ dc->ir = cris_fetch(env, dc, dc->pc, 2, 0); diff --git a/target-i386/cc_helper.c b/target-i386/cc_helper.c index 07892f9049..9422003f24 100644 --- a/target-i386/cc_helper.c +++ b/target-i386/cc_helper.c @@ -353,6 +353,16 @@ void helper_sti(CPUX86State *env) env->eflags |= IF_MASK; } +void helper_clac(CPUX86State *env) +{ + env->eflags &= ~AC_MASK; +} + +void helper_stac(CPUX86State *env) +{ + env->eflags |= AC_MASK; +} + #if 0 /* vm86plus instructions */ void helper_cli_vm(CPUX86State *env) diff --git a/target-i386/cpu.c b/target-i386/cpu.c index fd4fe2898b..bb1e44eb01 100644 --- a/target-i386/cpu.c +++ b/target-i386/cpu.c @@ -61,15 +61,19 @@ static const char *ext_feature_name[] = { "tsc-deadline", "aes", "xsave", "osxsave", "avx", NULL, NULL, "hypervisor", }; +/* Feature names that are already defined on feature_name[] but are set on + * CPUID[8000_0001].EDX on AMD CPUs don't have their names on + * ext2_feature_name[]. They are copied automatically to cpuid_ext2_features + * if and only if CPU vendor is AMD. + */ static const char *ext2_feature_name[] = { - "fpu", "vme", "de", "pse", - "tsc", "msr", "pae", "mce", - "cx8" /* AMD CMPXCHG8B */, "apic", NULL, "syscall", - "mtrr", "pge", "mca", "cmov", - "pat", "pse36", NULL, NULL /* Linux mp */, - "nx|xd", NULL, "mmxext", "mmx", - "fxsr", "fxsr_opt|ffxsr", "pdpe1gb" /* AMD Page1GB */, "rdtscp", - NULL, "lm|i64", "3dnowext", "3dnow", + NULL /* fpu */, NULL /* vme */, NULL /* de */, NULL /* pse */, + NULL /* tsc */, NULL /* msr */, NULL /* pae */, NULL /* mce */, + NULL /* cx8 */ /* AMD CMPXCHG8B */, NULL /* apic */, NULL, "syscall", + NULL /* mtrr */, NULL /* pge */, NULL /* mca */, NULL /* cmov */, + NULL /* pat */, NULL /* pse36 */, NULL, NULL /* Linux mp */, + "nx|xd", NULL, "mmxext", NULL /* mmx */, + NULL /* fxsr */, "fxsr_opt|ffxsr", "pdpe1gb" /* AMD Page1GB */, "rdtscp", }; static const char *ext3_feature_name[] = { "lahf_lm" /* AMD LahfSahf */, "cmp_legacy", "svm", "extapic" /* AMD ExtApicSpace */, @@ -100,6 +104,13 @@ static const char *svm_feature_name[] = { NULL, NULL, NULL, NULL, }; +static const char *cpuid_7_0_ebx_feature_name[] = { + NULL, NULL, NULL, NULL, NULL, NULL, NULL, "smep", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, "smap", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +}; + /* collects per-function cpuid data */ typedef struct model_features_t { @@ -215,14 +226,17 @@ static void add_flagname_to_bitmaps(const char *flagname, uint32_t *features, uint32_t *ext2_features, uint32_t *ext3_features, uint32_t *kvm_features, - uint32_t *svm_features) + uint32_t *svm_features, + uint32_t *cpuid_7_0_ebx_features) { if (!lookup_feature(features, flagname, NULL, feature_name) && !lookup_feature(ext_features, flagname, NULL, ext_feature_name) && !lookup_feature(ext2_features, flagname, NULL, ext2_feature_name) && !lookup_feature(ext3_features, flagname, NULL, ext3_feature_name) && !lookup_feature(kvm_features, flagname, NULL, kvm_feature_name) && - !lookup_feature(svm_features, flagname, NULL, svm_feature_name)) + !lookup_feature(svm_features, flagname, NULL, svm_feature_name) && + !lookup_feature(cpuid_7_0_ebx_features, flagname, NULL, + cpuid_7_0_ebx_feature_name)) fprintf(stderr, "CPU feature %s not found\n", flagname); } @@ -258,7 +272,6 @@ typedef struct x86_def_t { CPUID_MSR | CPUID_MCE | CPUID_CX8 | CPUID_PGE | CPUID_CMOV | \ CPUID_PAT | CPUID_FXSR | CPUID_MMX | CPUID_SSE | CPUID_SSE2 | \ CPUID_PAE | CPUID_SEP | CPUID_APIC) -#define EXT2_FEATURE_MASK 0x0183F3FF #define TCG_FEATURES (CPUID_FP87 | CPUID_PSE | CPUID_TSC | CPUID_MSR | \ CPUID_PAE | CPUID_MCE | CPUID_CX8 | CPUID_APIC | CPUID_SEP | \ @@ -276,7 +289,7 @@ typedef struct x86_def_t { /* missing: CPUID_EXT_DTES64, CPUID_EXT_DSCPL, CPUID_EXT_VMX, CPUID_EXT_EST, CPUID_EXT_TM2, CPUID_EXT_XTPR, CPUID_EXT_PDCM, CPUID_EXT_XSAVE */ -#define TCG_EXT2_FEATURES ((TCG_FEATURES & EXT2_FEATURE_MASK) | \ +#define TCG_EXT2_FEATURES ((TCG_FEATURES & CPUID_EXT2_AMD_ALIASES) | \ CPUID_EXT2_NX | CPUID_EXT2_MMXEXT | CPUID_EXT2_RDTSCP | \ CPUID_EXT2_3DNOW | CPUID_EXT2_3DNOWEXT) /* missing: @@ -284,6 +297,7 @@ typedef struct x86_def_t { #define TCG_EXT3_FEATURES (CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM | \ CPUID_EXT3_CR8LEG | CPUID_EXT3_ABM | CPUID_EXT3_SSE4A) #define TCG_SVM_FEATURES 0 +#define TCG_7_0_EBX_FEATURES (CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_SMAP) /* maintains list of cpu model definitions */ @@ -305,7 +319,7 @@ static x86_def_t builtin_x86_defs[] = { CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | CPUID_PSE36, .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_CX16 | CPUID_EXT_POPCNT, - .ext2_features = (PPRO_FEATURES & EXT2_FEATURE_MASK) | + .ext2_features = (PPRO_FEATURES & CPUID_EXT2_AMD_ALIASES) | CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX, .ext3_features = CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM | CPUID_EXT3_ABM | CPUID_EXT3_SSE4A, @@ -325,7 +339,7 @@ static x86_def_t builtin_x86_defs[] = { CPUID_PSE36 | CPUID_VME | CPUID_HT, .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_MONITOR | CPUID_EXT_CX16 | CPUID_EXT_POPCNT, - .ext2_features = (PPRO_FEATURES & EXT2_FEATURE_MASK) | + .ext2_features = (PPRO_FEATURES & CPUID_EXT2_AMD_ALIASES) | CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX | CPUID_EXT2_3DNOW | CPUID_EXT2_3DNOWEXT | CPUID_EXT2_MMXEXT | CPUID_EXT2_FFXSR | CPUID_EXT2_PDPE1GB | CPUID_EXT2_RDTSCP, @@ -373,7 +387,7 @@ static x86_def_t builtin_x86_defs[] = { /* Missing: CPUID_EXT_POPCNT, CPUID_EXT_MONITOR */ .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_CX16, /* Missing: CPUID_EXT2_PDPE1GB, CPUID_EXT2_RDTSCP */ - .ext2_features = (PPRO_FEATURES & EXT2_FEATURE_MASK) | + .ext2_features = (PPRO_FEATURES & CPUID_EXT2_AMD_ALIASES) | CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX, /* Missing: CPUID_EXT3_LAHF_LM, CPUID_EXT3_CMP_LEG, CPUID_EXT3_EXTAPIC, CPUID_EXT3_CR8LEG, CPUID_EXT3_ABM, CPUID_EXT3_SSE4A, @@ -402,7 +416,7 @@ static x86_def_t builtin_x86_defs[] = { .features = PPRO_FEATURES | CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | CPUID_PSE36, .ext_features = CPUID_EXT_SSE3, - .ext2_features = PPRO_FEATURES & EXT2_FEATURE_MASK, + .ext2_features = PPRO_FEATURES & CPUID_EXT2_AMD_ALIASES, .ext3_features = 0, .xlevel = 0x80000008, .model_id = "Common 32-bit KVM processor" @@ -467,8 +481,10 @@ static x86_def_t builtin_x86_defs[] = { .family = 6, .model = 2, .stepping = 3, - .features = PPRO_FEATURES | CPUID_PSE36 | CPUID_VME | CPUID_MTRR | CPUID_MCA, - .ext2_features = (PPRO_FEATURES & EXT2_FEATURE_MASK) | CPUID_EXT2_MMXEXT | CPUID_EXT2_3DNOW | CPUID_EXT2_3DNOWEXT, + .features = PPRO_FEATURES | CPUID_PSE36 | CPUID_VME | CPUID_MTRR | + CPUID_MCA, + .ext2_features = (PPRO_FEATURES & CPUID_EXT2_AMD_ALIASES) | + CPUID_EXT2_MMXEXT | CPUID_EXT2_3DNOW | CPUID_EXT2_3DNOWEXT, .xlevel = 0x80000008, }, { @@ -484,7 +500,8 @@ static x86_def_t builtin_x86_defs[] = { /* Some CPUs got no CPUID_SEP */ .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_MONITOR | CPUID_EXT_SSSE3 | CPUID_EXT_DSCPL | CPUID_EXT_EST | CPUID_EXT_TM2 | CPUID_EXT_XTPR, - .ext2_features = (PPRO_FEATURES & EXT2_FEATURE_MASK) | CPUID_EXT2_NX, + .ext2_features = (PPRO_FEATURES & CPUID_EXT2_AMD_ALIASES) | + CPUID_EXT2_NX, .ext3_features = CPUID_EXT3_LAHF_LM, .xlevel = 0x8000000A, .model_id = "Intel(R) Atom(TM) CPU N270 @ 1.60GHz", @@ -1091,10 +1108,12 @@ static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char *cpu_model) uint32_t plus_features = 0, plus_ext_features = 0; uint32_t plus_ext2_features = 0, plus_ext3_features = 0; uint32_t plus_kvm_features = 0, plus_svm_features = 0; + uint32_t plus_7_0_ebx_features = 0; /* Features to be removed */ uint32_t minus_features = 0, minus_ext_features = 0; uint32_t minus_ext2_features = 0, minus_ext3_features = 0; uint32_t minus_kvm_features = 0, minus_svm_features = 0; + uint32_t minus_7_0_ebx_features = 0; uint32_t numvalue; for (def = x86_defs; def; def = def->next) @@ -1121,8 +1140,8 @@ static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char *cpu_model) #endif add_flagname_to_bitmaps("hypervisor", &plus_features, - &plus_ext_features, &plus_ext2_features, &plus_ext3_features, - &plus_kvm_features, &plus_svm_features); + &plus_ext_features, &plus_ext2_features, &plus_ext3_features, + &plus_kvm_features, &plus_svm_features, &plus_7_0_ebx_features); featurestr = strtok(NULL, ","); @@ -1132,12 +1151,12 @@ static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char *cpu_model) add_flagname_to_bitmaps(featurestr + 1, &plus_features, &plus_ext_features, &plus_ext2_features, &plus_ext3_features, &plus_kvm_features, - &plus_svm_features); + &plus_svm_features, &plus_7_0_ebx_features); } else if (featurestr[0] == '-') { add_flagname_to_bitmaps(featurestr + 1, &minus_features, &minus_ext_features, &minus_ext2_features, &minus_ext3_features, &minus_kvm_features, - &minus_svm_features); + &minus_svm_features, &minus_7_0_ebx_features); } else if ((val = strchr(featurestr, '='))) { *val = 0; val++; if (!strcmp(featurestr, "family")) { @@ -1243,16 +1262,21 @@ static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char *cpu_model) x86_cpu_def->ext3_features |= plus_ext3_features; x86_cpu_def->kvm_features |= plus_kvm_features; x86_cpu_def->svm_features |= plus_svm_features; + x86_cpu_def->cpuid_7_0_ebx_features |= plus_7_0_ebx_features; x86_cpu_def->features &= ~minus_features; x86_cpu_def->ext_features &= ~minus_ext_features; x86_cpu_def->ext2_features &= ~minus_ext2_features; x86_cpu_def->ext3_features &= ~minus_ext3_features; x86_cpu_def->kvm_features &= ~minus_kvm_features; x86_cpu_def->svm_features &= ~minus_svm_features; + x86_cpu_def->cpuid_7_0_ebx_features &= ~minus_7_0_ebx_features; if (check_cpuid) { if (check_features_against_host(x86_cpu_def) && enforce_cpuid) goto error; } + if (x86_cpu_def->cpuid_7_0_ebx_features && x86_cpu_def->level < 7) { + x86_cpu_def->level = 7; + } g_free(s); return 0; @@ -1306,13 +1330,13 @@ void x86_cpu_list(FILE *f, fprintf_function cpu_fprintf) } (*cpu_fprintf)(f, "\nRecognized CPUID flags:\n"); listflags(buf, sizeof(buf), (uint32_t)~0, feature_name, 1); - (*cpu_fprintf)(f, " f_edx: %s\n", buf); + (*cpu_fprintf)(f, " %s\n", buf); listflags(buf, sizeof(buf), (uint32_t)~0, ext_feature_name, 1); - (*cpu_fprintf)(f, " f_ecx: %s\n", buf); + (*cpu_fprintf)(f, " %s\n", buf); listflags(buf, sizeof(buf), (uint32_t)~0, ext2_feature_name, 1); - (*cpu_fprintf)(f, " extf_edx: %s\n", buf); + (*cpu_fprintf)(f, " %s\n", buf); listflags(buf, sizeof(buf), (uint32_t)~0, ext3_feature_name, 1); - (*cpu_fprintf)(f, " extf_ecx: %s\n", buf); + (*cpu_fprintf)(f, " %s\n", buf); } CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp) @@ -1368,10 +1392,21 @@ int cpu_x86_register(X86CPU *cpu, const char *cpu_model) env->cpuid_kvm_features = def->kvm_features; env->cpuid_svm_features = def->svm_features; env->cpuid_ext4_features = def->ext4_features; - env->cpuid_7_0_ebx = def->cpuid_7_0_ebx_features; + env->cpuid_7_0_ebx_features = def->cpuid_7_0_ebx_features; env->cpuid_xlevel2 = def->xlevel2; object_property_set_int(OBJECT(cpu), (int64_t)def->tsc_khz * 1000, "tsc-frequency", &error); + + /* On AMD CPUs, some CPUID[8000_0001].EDX bits must match the bits on + * CPUID[1].EDX. + */ + if (env->cpuid_vendor1 == CPUID_VENDOR_AMD_1 && + env->cpuid_vendor2 == CPUID_VENDOR_AMD_2 && + env->cpuid_vendor3 == CPUID_VENDOR_AMD_3) { + env->cpuid_ext2_features &= ~CPUID_EXT2_AMD_ALIASES; + env->cpuid_ext2_features |= (def->features & CPUID_EXT2_AMD_ALIASES); + } + if (!kvm_enabled()) { env->cpuid_features &= TCG_FEATURES; env->cpuid_ext_features &= TCG_EXT_FEATURES; @@ -1545,7 +1580,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, /* Structured Extended Feature Flags Enumeration Leaf */ if (count == 0) { *eax = 0; /* Maximum ECX value for sub-leaves */ - *ebx = env->cpuid_7_0_ebx; /* Feature flags */ + *ebx = env->cpuid_7_0_ebx_features; /* Feature flags */ *ecx = 0; /* Reserved */ *edx = 0; /* Reserved */ } else { diff --git a/target-i386/cpu.h b/target-i386/cpu.h index d7ea2f92a8..e4a7d5b6e4 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -123,8 +123,8 @@ /* hidden flags - used internally by qemu to represent additional cpu states. Only the CPL, INHIBIT_IRQ, SMM and SVMI are not - redundant. We avoid using the IOPL_MASK, TF_MASK and VM_MASK bit - position to ease oring with eflags. */ + redundant. We avoid using the IOPL_MASK, TF_MASK, VM_MASK and AC_MASK + bit positions to ease oring with eflags. */ /* current cpl */ #define HF_CPL_SHIFT 0 /* true if soft mmu is being used */ @@ -147,10 +147,12 @@ #define HF_CS64_SHIFT 15 /* only used on x86_64: 64 bit code segment */ #define HF_RF_SHIFT 16 /* must be same as eflags */ #define HF_VM_SHIFT 17 /* must be same as eflags */ +#define HF_AC_SHIFT 18 /* must be same as eflags */ #define HF_SMM_SHIFT 19 /* CPU in SMM mode */ #define HF_SVME_SHIFT 20 /* SVME enabled (copy of EFER.SVME) */ #define HF_SVMI_SHIFT 21 /* SVM intercepts are active */ #define HF_OSFXSR_SHIFT 22 /* CR4.OSFXSR */ +#define HF_SMAP_SHIFT 23 /* CR4.SMAP */ #define HF_CPL_MASK (3 << HF_CPL_SHIFT) #define HF_SOFTMMU_MASK (1 << HF_SOFTMMU_SHIFT) @@ -168,10 +170,12 @@ #define HF_CS64_MASK (1 << HF_CS64_SHIFT) #define HF_RF_MASK (1 << HF_RF_SHIFT) #define HF_VM_MASK (1 << HF_VM_SHIFT) +#define HF_AC_MASK (1 << HF_AC_SHIFT) #define HF_SMM_MASK (1 << HF_SMM_SHIFT) #define HF_SVME_MASK (1 << HF_SVME_SHIFT) #define HF_SVMI_MASK (1 << HF_SVMI_SHIFT) #define HF_OSFXSR_MASK (1 << HF_OSFXSR_SHIFT) +#define HF_SMAP_MASK (1 << HF_SMAP_SHIFT) /* hflags2 */ @@ -210,6 +214,13 @@ #define CR4_OSFXSR_SHIFT 9 #define CR4_OSFXSR_MASK (1 << CR4_OSFXSR_SHIFT) #define CR4_OSXMMEXCPT_MASK (1 << 10) +#define CR4_VMXE_MASK (1 << 13) +#define CR4_SMXE_MASK (1 << 14) +#define CR4_FSGSBASE_MASK (1 << 16) +#define CR4_PCIDE_MASK (1 << 17) +#define CR4_OSXSAVE_MASK (1 << 18) +#define CR4_SMEP_MASK (1 << 20) +#define CR4_SMAP_MASK (1 << 21) #define DR6_BD (1 << 13) #define DR6_BS (1 << 14) @@ -409,6 +420,7 @@ #define CPUID_EXT_HYPERVISOR (1 << 31) #define CPUID_EXT2_FPU (1 << 0) +#define CPUID_EXT2_VME (1 << 1) #define CPUID_EXT2_DE (1 << 2) #define CPUID_EXT2_PSE (1 << 3) #define CPUID_EXT2_TSC (1 << 4) @@ -436,6 +448,17 @@ #define CPUID_EXT2_3DNOWEXT (1 << 30) #define CPUID_EXT2_3DNOW (1 << 31) +/* CPUID[8000_0001].EDX bits that are aliase of CPUID[1].EDX bits on AMD CPUs */ +#define CPUID_EXT2_AMD_ALIASES (CPUID_EXT2_FPU | CPUID_EXT2_VME | \ + CPUID_EXT2_DE | CPUID_EXT2_PSE | \ + CPUID_EXT2_TSC | CPUID_EXT2_MSR | \ + CPUID_EXT2_PAE | CPUID_EXT2_MCE | \ + CPUID_EXT2_CX8 | CPUID_EXT2_APIC | \ + CPUID_EXT2_MTRR | CPUID_EXT2_PGE | \ + CPUID_EXT2_MCA | CPUID_EXT2_CMOV | \ + CPUID_EXT2_PAT | CPUID_EXT2_PSE36 | \ + CPUID_EXT2_MMX | CPUID_EXT2_FXSR) + #define CPUID_EXT3_LAHF_LM (1 << 0) #define CPUID_EXT3_CMP_LEG (1 << 1) #define CPUID_EXT3_SVM (1 << 2) @@ -462,6 +485,9 @@ #define CPUID_SVM_PAUSEFILTER (1 << 10) #define CPUID_SVM_PFTHRESHOLD (1 << 12) +#define CPUID_7_0_EBX_SMEP (1 << 7) +#define CPUID_7_0_EBX_SMAP (1 << 20) + #define CPUID_VENDOR_INTEL_1 0x756e6547 /* "Genu" */ #define CPUID_VENDOR_INTEL_2 0x49656e69 /* "ineI" */ #define CPUID_VENDOR_INTEL_3 0x6c65746e /* "ntel" */ @@ -637,7 +663,7 @@ typedef struct { #define CPU_NB_REGS CPU_NB_REGS32 #endif -#define NB_MMU_MODES 2 +#define NB_MMU_MODES 3 typedef enum TPRAccess { TPR_ACCESS_READ, @@ -767,7 +793,7 @@ typedef struct CPUX86State { uint32_t cpuid_xlevel2; uint32_t cpuid_ext4_features; /* Flags from CPUID[EAX=7,ECX=0].EBX */ - uint32_t cpuid_7_0_ebx; + uint32_t cpuid_7_0_ebx_features; /* MTRRs */ uint64_t mtrr_fixed[11]; @@ -1006,10 +1032,15 @@ static inline CPUX86State *cpu_init(const char *cpu_model) /* MMU modes definitions */ #define MMU_MODE0_SUFFIX _kernel #define MMU_MODE1_SUFFIX _user -#define MMU_USER_IDX 1 +#define MMU_MODE2_SUFFIX _ksmap /* Kernel with SMAP override */ +#define MMU_KERNEL_IDX 0 +#define MMU_USER_IDX 1 +#define MMU_KSMAP_IDX 2 static inline int cpu_mmu_index (CPUX86State *env) { - return (env->hflags & HF_CPL_MASK) == 3 ? 1 : 0; + return (env->hflags & HF_CPL_MASK) == 3 ? MMU_USER_IDX : + ((env->hflags & HF_SMAP_MASK) && (env->eflags & AC_MASK)) + ? MMU_KSMAP_IDX : MMU_KERNEL_IDX; } #undef EAX @@ -1095,7 +1126,7 @@ static inline void cpu_get_tb_cpu_state(CPUX86State *env, target_ulong *pc, *cs_base = env->segs[R_CS].base; *pc = *cs_base + env->eip; *flags = env->hflags | - (env->eflags & (IOPL_MASK | TF_MASK | RF_MASK | VM_MASK)); + (env->eflags & (IOPL_MASK | TF_MASK | RF_MASK | VM_MASK | AC_MASK)); } void do_cpu_init(X86CPU *cpu); diff --git a/target-i386/helper.c b/target-i386/helper.c index 8a5da3d7c0..c635667d60 100644 --- a/target-i386/helper.c +++ b/target-i386/helper.c @@ -443,17 +443,27 @@ void cpu_x86_update_cr4(CPUX86State *env, uint32_t new_cr4) #if defined(DEBUG_MMU) printf("CR4 update: CR4=%08x\n", (uint32_t)env->cr[4]); #endif - if ((new_cr4 & (CR4_PGE_MASK | CR4_PAE_MASK | CR4_PSE_MASK)) != - (env->cr[4] & (CR4_PGE_MASK | CR4_PAE_MASK | CR4_PSE_MASK))) { + if ((new_cr4 ^ env->cr[4]) & + (CR4_PGE_MASK | CR4_PAE_MASK | CR4_PSE_MASK | + CR4_SMEP_MASK | CR4_SMAP_MASK)) { tlb_flush(env, 1); } /* SSE handling */ - if (!(env->cpuid_features & CPUID_SSE)) + if (!(env->cpuid_features & CPUID_SSE)) { new_cr4 &= ~CR4_OSFXSR_MASK; - if (new_cr4 & CR4_OSFXSR_MASK) + } + env->hflags &= ~HF_OSFXSR_MASK; + if (new_cr4 & CR4_OSFXSR_MASK) { env->hflags |= HF_OSFXSR_MASK; - else - env->hflags &= ~HF_OSFXSR_MASK; + } + + if (!(env->cpuid_7_0_ebx_features & CPUID_7_0_EBX_SMAP)) { + new_cr4 &= ~CR4_SMAP_MASK; + } + env->hflags &= ~HF_SMAP_MASK; + if (new_cr4 & CR4_SMAP_MASK) { + env->hflags |= HF_SMAP_MASK; + } env->cr[4] = new_cr4; } @@ -591,17 +601,38 @@ int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr, /* 2 MB page */ page_size = 2048 * 1024; ptep ^= PG_NX_MASK; - if ((ptep & PG_NX_MASK) && is_write1 == 2) + if ((ptep & PG_NX_MASK) && is_write1 == 2) { goto do_fault_protect; - if (is_user) { - if (!(ptep & PG_USER_MASK)) + } + switch (mmu_idx) { + case MMU_USER_IDX: + if (!(ptep & PG_USER_MASK)) { goto do_fault_protect; - if (is_write && !(ptep & PG_RW_MASK)) + } + if (is_write && !(ptep & PG_RW_MASK)) { goto do_fault_protect; - } else { + } + break; + + case MMU_KERNEL_IDX: + if (is_write1 != 2 && (env->cr[4] & CR4_SMAP_MASK) && + (ptep & PG_USER_MASK)) { + goto do_fault_protect; + } + /* fall through */ + case MMU_KSMAP_IDX: + if (is_write1 == 2 && (env->cr[4] & CR4_SMEP_MASK) && + (ptep & PG_USER_MASK)) { + goto do_fault_protect; + } if ((env->cr[0] & CR0_WP_MASK) && - is_write && !(ptep & PG_RW_MASK)) + is_write && !(ptep & PG_RW_MASK)) { goto do_fault_protect; + } + break; + + default: /* cannot happen */ + break; } is_dirty = is_write && !(pde & PG_DIRTY_MASK); if (!(pde & PG_ACCESSED_MASK) || is_dirty) { @@ -635,15 +666,35 @@ int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr, ptep ^= PG_NX_MASK; if ((ptep & PG_NX_MASK) && is_write1 == 2) goto do_fault_protect; - if (is_user) { - if (!(ptep & PG_USER_MASK)) + switch (mmu_idx) { + case MMU_USER_IDX: + if (!(ptep & PG_USER_MASK)) { goto do_fault_protect; - if (is_write && !(ptep & PG_RW_MASK)) + } + if (is_write && !(ptep & PG_RW_MASK)) { goto do_fault_protect; - } else { + } + break; + + case MMU_KERNEL_IDX: + if (is_write1 != 2 && (env->cr[4] & CR4_SMAP_MASK) && + (ptep & PG_USER_MASK)) { + goto do_fault_protect; + } + /* fall through */ + case MMU_KSMAP_IDX: + if (is_write1 == 2 && (env->cr[4] & CR4_SMEP_MASK) && + (ptep & PG_USER_MASK)) { + goto do_fault_protect; + } if ((env->cr[0] & CR0_WP_MASK) && - is_write && !(ptep & PG_RW_MASK)) + is_write && !(ptep & PG_RW_MASK)) { goto do_fault_protect; + } + break; + + default: /* cannot happen */ + break; } is_dirty = is_write && !(pte & PG_DIRTY_MASK); if (!(pte & PG_ACCESSED_MASK) || is_dirty) { @@ -670,15 +721,35 @@ int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr, /* if PSE bit is set, then we use a 4MB page */ if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) { page_size = 4096 * 1024; - if (is_user) { - if (!(pde & PG_USER_MASK)) + switch (mmu_idx) { + case MMU_USER_IDX: + if (!(pde & PG_USER_MASK)) { goto do_fault_protect; - if (is_write && !(pde & PG_RW_MASK)) + } + if (is_write && !(pde & PG_RW_MASK)) { goto do_fault_protect; - } else { + } + break; + + case MMU_KERNEL_IDX: + if (is_write1 != 2 && (env->cr[4] & CR4_SMAP_MASK) && + (pde & PG_USER_MASK)) { + goto do_fault_protect; + } + /* fall through */ + case MMU_KSMAP_IDX: + if (is_write1 == 2 && (env->cr[4] & CR4_SMEP_MASK) && + (pde & PG_USER_MASK)) { + goto do_fault_protect; + } if ((env->cr[0] & CR0_WP_MASK) && - is_write && !(pde & PG_RW_MASK)) + is_write && !(pde & PG_RW_MASK)) { goto do_fault_protect; + } + break; + + default: /* cannot happen */ + break; } is_dirty = is_write && !(pde & PG_DIRTY_MASK); if (!(pde & PG_ACCESSED_MASK) || is_dirty) { @@ -707,15 +778,35 @@ int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr, } /* combine pde and pte user and rw protections */ ptep = pte & pde; - if (is_user) { - if (!(ptep & PG_USER_MASK)) + switch (mmu_idx) { + case MMU_USER_IDX: + if (!(ptep & PG_USER_MASK)) { goto do_fault_protect; - if (is_write && !(ptep & PG_RW_MASK)) + } + if (is_write && !(ptep & PG_RW_MASK)) { goto do_fault_protect; - } else { + } + break; + + case MMU_KERNEL_IDX: + if (is_write1 != 2 && (env->cr[4] & CR4_SMAP_MASK) && + (ptep & PG_USER_MASK)) { + goto do_fault_protect; + } + /* fall through */ + case MMU_KSMAP_IDX: + if (is_write1 == 2 && (env->cr[4] & CR4_SMEP_MASK) && + (ptep & PG_USER_MASK)) { + goto do_fault_protect; + } if ((env->cr[0] & CR0_WP_MASK) && - is_write && !(ptep & PG_RW_MASK)) + is_write && !(ptep & PG_RW_MASK)) { goto do_fault_protect; + } + break; + + default: /* cannot happen */ + break; } is_dirty = is_write && !(pte & PG_DIRTY_MASK); if (!(pte & PG_ACCESSED_MASK) || is_dirty) { @@ -762,8 +853,9 @@ int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr, if (is_user) error_code |= PG_ERROR_U_MASK; if (is_write1 == 2 && - (env->efer & MSR_EFER_NXE) && - (env->cr[4] & CR4_PAE_MASK)) + (((env->efer & MSR_EFER_NXE) && + (env->cr[4] & CR4_PAE_MASK)) || + (env->cr[4] & CR4_SMEP_MASK))) error_code |= PG_ERROR_I_D_MASK; if (env->intercept_exceptions & (1 << EXCP0E_PAGE)) { /* cr2 is not modified in case of exceptions */ diff --git a/target-i386/helper.h b/target-i386/helper.h index ab6af638e6..93850ceecd 100644 --- a/target-i386/helper.h +++ b/target-i386/helper.h @@ -67,6 +67,8 @@ DEF_HELPER_3(raise_interrupt, void, env, int, int) DEF_HELPER_2(raise_exception, void, env, int) DEF_HELPER_1(cli, void, env) DEF_HELPER_1(sti, void, env) +DEF_HELPER_1(clac, void, env) +DEF_HELPER_1(stac, void, env) DEF_HELPER_1(set_inhibit_irq, void, env) DEF_HELPER_1(reset_inhibit_irq, void, env) DEF_HELPER_3(boundw, void, env, tl, int) diff --git a/target-i386/kvm.c b/target-i386/kvm.c index 6790180b0a..5b18383d8f 100644 --- a/target-i386/kvm.c +++ b/target-i386/kvm.c @@ -165,7 +165,7 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function, * so add missing bits according to the AMD spec: */ cpuid_1_edx = kvm_arch_get_supported_cpuid(s, 1, 0, R_EDX); - ret |= cpuid_1_edx & 0x183f7ff; + ret |= cpuid_1_edx & CPUID_EXT2_AMD_ALIASES; break; } break; diff --git a/target-i386/translate.c b/target-i386/translate.c index eb0cabcf19..0a7e4e3487 100644 --- a/target-i386/translate.c +++ b/target-i386/translate.c @@ -107,6 +107,7 @@ typedef struct DisasContext { int cpuid_ext_features; int cpuid_ext2_features; int cpuid_ext3_features; + int cpuid_7_0_ebx_features; } DisasContext; static void gen_eob(DisasContext *s); @@ -4202,8 +4203,9 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start) target_ulong next_eip, tval; int rex_w, rex_r; - if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) + if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { tcg_gen_debug_insn_start(pc_start); + } s->pc = pc_start; prefixes = 0; aflag = s->code32; @@ -6555,7 +6557,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start) } gen_pop_update(s); s->cc_op = CC_OP_EFLAGS; - /* abort translation because TF flag may change */ + /* abort translation because TF/AC flag may change */ gen_jmp_im(s->pc - s->cs_base); gen_eob(s); } @@ -7205,6 +7207,24 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start) gen_helper_mwait(cpu_env, tcg_const_i32(s->pc - pc_start)); gen_eob(s); break; + case 2: /* clac */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_SMAP) || + s->cpl != 0) { + goto illegal_op; + } + gen_helper_clac(cpu_env); + gen_jmp_im(s->pc - s->cs_base); + gen_eob(s); + break; + case 3: /* stac */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_SMAP) || + s->cpl != 0) { + goto illegal_op; + } + gen_helper_stac(cpu_env); + gen_jmp_im(s->pc - s->cs_base); + gen_eob(s); + break; default: goto illegal_op; } @@ -7900,15 +7920,13 @@ static inline void gen_intermediate_code_internal(CPUX86State *env, /* select memory access functions */ dc->mem_index = 0; if (flags & HF_SOFTMMU_MASK) { - if (dc->cpl == 3) - dc->mem_index = 2 * 4; - else - dc->mem_index = 1 * 4; + dc->mem_index = (cpu_mmu_index(env) + 1) << 2; } dc->cpuid_features = env->cpuid_features; dc->cpuid_ext_features = env->cpuid_ext_features; dc->cpuid_ext2_features = env->cpuid_ext2_features; dc->cpuid_ext3_features = env->cpuid_ext3_features; + dc->cpuid_7_0_ebx_features = env->cpuid_7_0_ebx_features; #ifdef TARGET_X86_64 dc->lma = (flags >> HF_LMA_SHIFT) & 1; dc->code64 = (flags >> HF_CS64_SHIFT) & 1; diff --git a/target-lm32/translate.c b/target-lm32/translate.c index 5f6dcbac86..77c2866611 100644 --- a/target-lm32/translate.c +++ b/target-lm32/translate.c @@ -942,7 +942,7 @@ static const DecoderInfo decinfo[] = { static inline void decode(DisasContext *dc, uint32_t ir) { - if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) { + if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { tcg_gen_debug_insn_start(dc->pc); } diff --git a/target-m68k/translate.c b/target-m68k/translate.c index fb707f21bc..451ef7410f 100644 --- a/target-m68k/translate.c +++ b/target-m68k/translate.c @@ -2953,6 +2953,10 @@ static void disas_m68k_insn(CPUM68KState * env, DisasContext *s) { uint16_t insn; + if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { + tcg_gen_debug_insn_start(s->pc); + } + insn = cpu_lduw_code(env, s->pc); s->pc += 2; diff --git a/target-microblaze/translate.c b/target-microblaze/translate.c index 9c7d77f57a..7d864b1dac 100644 --- a/target-microblaze/translate.c +++ b/target-microblaze/translate.c @@ -1664,8 +1664,9 @@ static inline void decode(DisasContext *dc, uint32_t ir) { int i; - if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) + if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { tcg_gen_debug_insn_start(dc->pc); + } dc->ir = ir; LOG_DIS("%8.8x\t", dc->ir); diff --git a/target-mips/translate.c b/target-mips/translate.c index fa79d4945b..454e5cc818 100644 --- a/target-mips/translate.c +++ b/target-mips/translate.c @@ -12124,8 +12124,9 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) gen_set_label(l1); } - if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) + if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { tcg_gen_debug_insn_start(ctx->pc); + } op = MASK_OP_MAJOR(ctx->opcode); rs = (ctx->opcode >> 21) & 0x1f; diff --git a/target-openrisc/translate.c b/target-openrisc/translate.c index 325ba09cb5..e2cad3ad42 100644 --- a/target-openrisc/translate.c +++ b/target-openrisc/translate.c @@ -1715,7 +1715,7 @@ static inline void gen_intermediate_code_internal(OpenRISCCPU *cpu, gen_opc_icount[k] = num_insns; } - if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) { + if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { tcg_gen_debug_insn_start(dc->pc); } diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c index d2664acef0..532b114aed 100644 --- a/target-ppc/mmu_helper.c +++ b/target-ppc/mmu_helper.c @@ -1032,12 +1032,10 @@ static int ppcemb_tlb_check(CPUPPCState *env, ppcemb_tlb_t *tlb, return -1; } *raddrp = (tlb->RPN & mask) | (address & ~mask); -#if (TARGET_PHYS_ADDR_BITS >= 36) if (ext) { /* Extend the physical address to 36 bits */ - *raddrp |= (target_phys_addr_t)(tlb->RPN & 0xF) << 32; + *raddrp |= (uint64_t)(tlb->RPN & 0xF) << 32; } -#endif return 0; } diff --git a/target-ppc/translate.c b/target-ppc/translate.c index ac915ccade..1042268ecf 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -9690,8 +9690,9 @@ static inline void gen_intermediate_code_internal(CPUPPCState *env, LOG_DISAS("translate opcode %08x (%02x %02x %02x) (%s)\n", ctx.opcode, opc1(ctx.opcode), opc2(ctx.opcode), opc3(ctx.opcode), little_endian ? "little" : "big"); - if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) + if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { tcg_gen_debug_insn_start(ctx.nip); + } ctx.nip += 4; table = env->opcodes; num_insns++; diff --git a/target-s390x/helper.c b/target-s390x/helper.c index a5741ecde1..22256b057d 100644 --- a/target-s390x/helper.c +++ b/target-s390x/helper.c @@ -511,7 +511,8 @@ static void do_program_interrupt(CPUS390XState *env) break; } - qemu_log("%s: code=0x%x ilc=%d\n", __func__, env->int_pgm_code, ilc); + qemu_log_mask(CPU_LOG_INT, "%s: code=0x%x ilc=%d\n", + __func__, env->int_pgm_code, ilc); lowcore = cpu_physical_memory_map(env->psa, &len, 1); @@ -575,8 +576,8 @@ static void do_ext_interrupt(CPUS390XState *env) void do_interrupt(CPUS390XState *env) { - qemu_log("%s: %d at pc=%" PRIx64 "\n", __func__, env->exception_index, - env->psw.addr); + qemu_log_mask(CPU_LOG_INT, "%s: %d at pc=%" PRIx64 "\n", + __func__, env->exception_index, env->psw.addr); s390_add_running_cpu(env); /* handle external interrupts */ diff --git a/target-s390x/misc_helper.c b/target-s390x/misc_helper.c index 2938ac9c76..e9b3caed49 100644 --- a/target-s390x/misc_helper.c +++ b/target-s390x/misc_helper.c @@ -53,7 +53,8 @@ void HELPER(exception)(CPUS390XState *env, uint32_t excp) #ifndef CONFIG_USER_ONLY void program_interrupt(CPUS390XState *env, uint32_t code, int ilc) { - qemu_log("program interrupt at %#" PRIx64 "\n", env->psw.addr); + qemu_log_mask(CPU_LOG_INT, "program interrupt at %#" PRIx64 "\n", + env->psw.addr); if (kvm_enabled()) { #ifdef CONFIG_KVM diff --git a/target-s390x/translate.c b/target-s390x/translate.c index 3214783aaa..db464cc073 100644 --- a/target-s390x/translate.c +++ b/target-s390x/translate.c @@ -79,6 +79,14 @@ void cpu_dump_state(CPUS390XState *env, FILE *f, fprintf_function cpu_fprintf, { int i; + if (env->cc_op > 3) { + cpu_fprintf(f, "PSW=mask %016" PRIx64 " addr %016" PRIx64 " cc %15s\n", + env->psw.mask, env->psw.addr, cc_name(env->cc_op)); + } else { + cpu_fprintf(f, "PSW=mask %016" PRIx64 " addr %016" PRIx64 " cc %02x\n", + env->psw.mask, env->psw.addr, env->cc_op); + } + for (i = 0; i < 16; i++) { cpu_fprintf(f, "R%02d=%016" PRIx64, i, env->regs[i]); if ((i % 4) == 3) { @@ -97,8 +105,6 @@ void cpu_dump_state(CPUS390XState *env, FILE *f, fprintf_function cpu_fprintf, } } - cpu_fprintf(f, "\n"); - #ifndef CONFIG_USER_ONLY for (i = 0; i < 16; i++) { cpu_fprintf(f, "C%02d=%016" PRIx64, i, env->cregs[i]); @@ -110,22 +116,14 @@ void cpu_dump_state(CPUS390XState *env, FILE *f, fprintf_function cpu_fprintf, } #endif - cpu_fprintf(f, "\n"); - - if (env->cc_op > 3) { - cpu_fprintf(f, "PSW=mask %016" PRIx64 " addr %016" PRIx64 " cc %15s\n", - env->psw.mask, env->psw.addr, cc_name(env->cc_op)); - } else { - cpu_fprintf(f, "PSW=mask %016" PRIx64 " addr %016" PRIx64 " cc %02x\n", - env->psw.mask, env->psw.addr, env->cc_op); - } - #ifdef DEBUG_INLINE_BRANCHES for (i = 0; i < CC_OP_MAX; i++) { cpu_fprintf(f, " %15s = %10ld\t%10ld\n", cc_name(i), inline_branch_miss[i], inline_branch_hit[i]); } #endif + + cpu_fprintf(f, "\n"); } static TCGv_i64 psw_addr; @@ -5173,10 +5171,11 @@ static inline void gen_intermediate_code_internal(CPUS390XState *env, if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) { gen_io_start(); } -#if defined(S390X_DEBUG_DISAS_VERBOSE) - LOG_DISAS("pc " TARGET_FMT_lx "\n", - dc.pc); -#endif + + if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { + tcg_gen_debug_insn_start(dc.pc); + } + disas_s390_insn(env, &dc); num_insns++; @@ -5219,7 +5218,6 @@ static inline void gen_intermediate_code_internal(CPUS390XState *env, tb->icount = num_insns; } #if defined(S390X_DEBUG_DISAS) - log_cpu_state_mask(CPU_LOG_TB_CPU, env, 0); if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { qemu_log("IN: %s\n", lookup_symbol(pc_start)); log_target_disas(pc_start, dc.pc - pc_start, 1); diff --git a/target-sh4/translate.c b/target-sh4/translate.c index 0fa83cab99..9d955eb204 100644 --- a/target-sh4/translate.c +++ b/target-sh4/translate.c @@ -1924,7 +1924,7 @@ static void decode_opc(DisasContext * ctx) { uint32_t old_flags = ctx->flags; - if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) { + if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { tcg_gen_debug_insn_start(ctx->pc); } diff --git a/target-sparc/translate.c b/target-sparc/translate.c index b95f91cd3b..e5ebedfa2c 100644 --- a/target-sparc/translate.c +++ b/target-sparc/translate.c @@ -2394,8 +2394,9 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) TCGv_i64 cpu_src1_64, cpu_src2_64, cpu_dst_64; target_long simm; - if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) + if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { tcg_gen_debug_insn_start(dc->pc); + } opc = GET_FIELD(insn, 0, 1); diff --git a/target-unicore32/translate.c b/target-unicore32/translate.c index b786a6b890..36f4f2f966 100644 --- a/target-unicore32/translate.c +++ b/target-unicore32/translate.c @@ -1861,6 +1861,10 @@ static void disas_uc32_insn(CPUUniCore32State *env, DisasContext *s) { unsigned int insn; + if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { + tcg_gen_debug_insn_start(s->pc); + } + insn = cpu_ldl_code(env, s->pc); s->pc += 4; diff --git a/target-xtensa/translate.c b/target-xtensa/translate.c index ba3ffcb7a4..b9acd706ce 100644 --- a/target-xtensa/translate.c +++ b/target-xtensa/translate.c @@ -2923,7 +2923,7 @@ static void gen_intermediate_code_internal( gen_opc_icount[lj] = insn_count; } - if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) { + if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { tcg_gen_debug_insn_start(dc.pc); } diff --git a/targphys.h b/targphys.h index bd4938fc02..08cade9096 100644 --- a/targphys.h +++ b/targphys.h @@ -3,25 +3,10 @@ #ifndef TARGPHYS_H #define TARGPHYS_H -#ifdef TARGET_PHYS_ADDR_BITS +#define TARGET_PHYS_ADDR_BITS 64 /* target_phys_addr_t is the type of a physical address (its size can be different from 'target_ulong'). */ -#if TARGET_PHYS_ADDR_BITS == 32 -typedef uint32_t target_phys_addr_t; -#define TARGET_PHYS_ADDR_MAX UINT32_MAX -#define TARGET_FMT_plx "%08x" -/* Format strings for printing target_phys_addr_t types. - * These are recommended over the less flexible TARGET_FMT_plx, - * which is retained for the benefit of existing code. - */ -#define TARGET_PRIdPHYS PRId32 -#define TARGET_PRIiPHYS PRIi32 -#define TARGET_PRIoPHYS PRIo32 -#define TARGET_PRIuPHYS PRIu32 -#define TARGET_PRIxPHYS PRIx32 -#define TARGET_PRIXPHYS PRIX32 -#elif TARGET_PHYS_ADDR_BITS == 64 typedef uint64_t target_phys_addr_t; #define TARGET_PHYS_ADDR_MAX UINT64_MAX #define TARGET_FMT_plx "%016" PRIx64 @@ -31,7 +16,5 @@ typedef uint64_t target_phys_addr_t; #define TARGET_PRIuPHYS PRIu64 #define TARGET_PRIxPHYS PRIx64 #define TARGET_PRIXPHYS PRIX64 -#endif -#endif #endif diff --git a/tcg/tci/tcg-target.c b/tcg/tci/tcg-target.c index 3f4a24bb8b..d272a906e8 100644 --- a/tcg/tci/tcg-target.c +++ b/tcg/tci/tcg-target.c @@ -300,7 +300,7 @@ static const int tcg_target_reg_alloc_order[] = { #endif }; -#if MAX_OPC_PARAM_IARGS != 4 +#if MAX_OPC_PARAM_IARGS != 5 # error Fix needed, number of supported input arguments changed! #endif @@ -309,16 +309,18 @@ static const int tcg_target_call_iarg_regs[] = { TCG_REG_R1, TCG_REG_R2, TCG_REG_R3, -#if TCG_TARGET_REG_BITS == 32 - /* 32 bit hosts need 2 * MAX_OPC_PARAM_IARGS registers. */ #if 0 /* used for TCG_REG_CALL_STACK */ TCG_REG_R4, #endif TCG_REG_R5, +#if TCG_TARGET_REG_BITS == 32 + /* 32 bit hosts need 2 * MAX_OPC_PARAM_IARGS registers. */ TCG_REG_R6, TCG_REG_R7, #if TCG_TARGET_NB_REGS >= 16 TCG_REG_R8, + TCG_REG_R9, + TCG_REG_R10, #else # error Too few input registers available #endif @@ -798,7 +800,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, case INDEX_op_qemu_st8: case INDEX_op_qemu_st16: case INDEX_op_qemu_st32: - tcg_out_r(s, TCG_AREG0); tcg_out_r(s, *args++); tcg_out_r(s, *args++); #if TARGET_LONG_BITS > TCG_TARGET_REG_BITS @@ -809,7 +810,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, #endif break; case INDEX_op_qemu_st64: - tcg_out_r(s, TCG_AREG0); tcg_out_r(s, *args++); #if TCG_TARGET_REG_BITS == 32 tcg_out_r(s, *args++); @@ -36,17 +36,19 @@ tcg_abort(); \ } while (0) -#if MAX_OPC_PARAM_IARGS != 4 +#if MAX_OPC_PARAM_IARGS != 5 # error Fix needed, number of supported input arguments changed! #endif #if TCG_TARGET_REG_BITS == 32 typedef uint64_t (*helper_function)(tcg_target_ulong, tcg_target_ulong, tcg_target_ulong, tcg_target_ulong, tcg_target_ulong, tcg_target_ulong, + tcg_target_ulong, tcg_target_ulong, tcg_target_ulong, tcg_target_ulong); #else typedef uint64_t (*helper_function)(tcg_target_ulong, tcg_target_ulong, - tcg_target_ulong, tcg_target_ulong); + tcg_target_ulong, tcg_target_ulong, + tcg_target_ulong); #endif /* TCI can optionally use a global register variable for env. */ @@ -489,14 +491,17 @@ tcg_target_ulong tcg_qemu_tb_exec(CPUArchState *cpustate, uint8_t *tb_ptr) tci_read_reg(TCG_REG_R5), tci_read_reg(TCG_REG_R6), tci_read_reg(TCG_REG_R7), - tci_read_reg(TCG_REG_R8)); + tci_read_reg(TCG_REG_R8), + tci_read_reg(TCG_REG_R9), + tci_read_reg(TCG_REG_R10)); tci_write_reg(TCG_REG_R0, tmp64); tci_write_reg(TCG_REG_R1, tmp64 >> 32); #else tmp64 = ((helper_function)t0)(tci_read_reg(TCG_REG_R0), tci_read_reg(TCG_REG_R1), tci_read_reg(TCG_REG_R2), - tci_read_reg(TCG_REG_R3)); + tci_read_reg(TCG_REG_R3), + tci_read_reg(TCG_REG_R5)); tci_write_reg(TCG_REG_R0, tmp64); #endif break; @@ -3061,7 +3061,7 @@ int vnc_display_open(DisplayState *ds, const char *display) if (strncmp(display, "unix:", 5) == 0) vs->lsock = unix_connect(display+5); else - vs->lsock = inet_connect(display, true, NULL, NULL); + vs->lsock = inet_connect(display, NULL); if (-1 == vs->lsock) { g_free(vs->display); vs->display = NULL; @@ -1355,6 +1355,8 @@ static int powerdown_requested; static int debug_requested; static int suspend_requested; static int wakeup_requested; +static NotifierList powerdown_notifiers = + NOTIFIER_LIST_INITIALIZER(powerdown_notifiers); static NotifierList suspend_notifiers = NOTIFIER_LIST_INITIALIZER(suspend_notifiers); static NotifierList wakeup_notifiers = @@ -1563,12 +1565,23 @@ void qemu_system_shutdown_request(void) qemu_notify_event(); } +static void qemu_system_powerdown(void) +{ + monitor_protocol_event(QEVENT_POWERDOWN, NULL); + notifier_list_notify(&powerdown_notifiers, NULL); +} + void qemu_system_powerdown_request(void) { powerdown_requested = 1; qemu_notify_event(); } +void qemu_register_powerdown_notifier(Notifier *notifier) +{ + notifier_list_add(&powerdown_notifiers, notifier); +} + void qemu_system_debug_request(void) { debug_requested = 1; @@ -1581,8 +1594,6 @@ void qemu_system_vmstop_request(RunState state) qemu_notify_event(); } -qemu_irq qemu_system_powerdown; - static bool main_loop_should_exit(void) { RunState r; @@ -1619,8 +1630,7 @@ static bool main_loop_should_exit(void) monitor_protocol_event(QEVENT_WAKEUP, NULL); } if (qemu_powerdown_requested()) { - monitor_protocol_event(QEVENT_POWERDOWN, NULL); - qemu_irq_raise(qemu_system_powerdown); + qemu_system_powerdown(); } if (qemu_vmstop_requested(&r)) { vm_stop(r); @@ -2622,7 +2632,8 @@ int main(int argc, char **argv, char **envp) { static const char * const params[] = { "order", "once", "menu", - "splash", "splash-time", NULL + "splash", "splash-time", + "reboot-timeout", NULL }; char buf[sizeof(boot_devices)]; char *standard_boot_devices; |